@@ -2407,3 +2407,309 @@ D2D1_RECT_F GetBlackBox(const DWRITE_OVERHANG_METRICS& overhangMetrics, const DW
24072407 /* bottom*/ overhangMetrics.bottom + textMetrics.layoutHeight
24082408 };
24092409}
2410+
2411+
2412+ HRESULT GetGlyphMetrics (
2413+ IDWriteFontFace* fontFace,
2414+ float fontEmSize,
2415+ float pixelsPerDip,
2416+ const DWRITE_MATRIX* transform,
2417+ DWRITE_MEASURING_MODE measuringMode,
2418+ bool isSideways,
2419+ uint32_t glyphCount,
2420+ _In_reads_ (glyphCount) const uint16_t* glyphIndices,
2421+ _Out_writes_(glyphCount) DWRITE_GLYPH_METRICS* glyphMetrics
2422+ )
2423+ {
2424+ switch (measuringMode)
2425+ {
2426+ case DWRITE_MEASURING_MODE_GDI_CLASSIC:
2427+ case DWRITE_MEASURING_MODE_GDI_NATURAL:
2428+ IFR (fontFace->GetGdiCompatibleGlyphMetrics (
2429+ fontEmSize,
2430+ pixelsPerDip,
2431+ transform,
2432+ (measuringMode == DWRITE_MEASURING_MODE_GDI_NATURAL),
2433+ glyphIndices,
2434+ glyphCount,
2435+ OUT glyphMetrics,
2436+ isSideways
2437+ ));
2438+ break ;
2439+
2440+ default :
2441+ DEBUG_ASSERT (" A new measuring mode has been added. Handle it here." );
2442+ __fallthrough;
2443+
2444+ case DWRITE_MEASURING_MODE_NATURAL:
2445+ IFR (fontFace->GetDesignGlyphMetrics (
2446+ glyphIndices,
2447+ glyphCount,
2448+ OUT glyphMetrics,
2449+ isSideways
2450+ ));
2451+ break ;
2452+ }
2453+
2454+ return S_OK;
2455+ }
2456+
2457+
2458+ DWRITE_GLYPH_ORIENTATION_ANGLE GetRelativeOrientation (bool isSideways, bool isFlippedOrientation) throw()
2459+ {
2460+ // Determine a relative angle from the flags.
2461+ //
2462+ // isSideways isFlipped -> Angle
2463+ // F F 0
2464+ // T T 90
2465+ // F T 180
2466+ // T F 270
2467+
2468+ DWRITE_GLYPH_ORIENTATION_ANGLE rotationAmount = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2469+ if (isSideways) rotationAmount = DWRITE_GLYPH_ORIENTATION_ANGLE (rotationAmount + 1 );
2470+ if (isSideways != isFlippedOrientation) rotationAmount = DWRITE_GLYPH_ORIENTATION_ANGLE (rotationAmount + 2 );
2471+
2472+ return rotationAmount;
2473+ }
2474+
2475+
2476+ void AccumulateRect (_Inout_ D2D1_RECT_F& modifiedRect, D2D1_RECT_F const & otherRect)
2477+ {
2478+ if (otherRect.left < modifiedRect.left ) modifiedRect.left = otherRect.left ;
2479+ if (otherRect.right > modifiedRect.right ) modifiedRect.right = otherRect.right ;
2480+ if (otherRect.top < modifiedRect.top ) modifiedRect.top = otherRect.top ;
2481+ if (otherRect.bottom > modifiedRect.bottom ) modifiedRect.bottom = otherRect.bottom ;
2482+ }
2483+
2484+
2485+ inline void OffsetRect (_Inout_ D2D1_RECT_F& modifiedRect, float x, float y)
2486+ {
2487+ modifiedRect.left += x;
2488+ modifiedRect.top += y;
2489+ modifiedRect.right += x;
2490+ modifiedRect.bottom += y;
2491+ }
2492+
2493+
2494+ template <typename RectType>
2495+ void RotateRect (
2496+ _Inout_ RectType& modifiedRect,
2497+ DWRITE_GLYPH_ORIENTATION_ANGLE rotationAmount
2498+ )
2499+ {
2500+ // Rotate the corners around the zero point <0,0>, whether it be a glyph
2501+ // or inline object.
2502+ switch (rotationAmount)
2503+ {
2504+ case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2505+ break ; // do nothing
2506+
2507+ case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2508+ {
2509+ auto oldLeft = modifiedRect.left ;
2510+ modifiedRect.left = modifiedRect.bottom ;
2511+ modifiedRect.bottom = -modifiedRect.right ;
2512+ modifiedRect.right = modifiedRect.top ;
2513+ modifiedRect.top = -oldLeft;
2514+ }
2515+ break ;
2516+
2517+ case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES: // for stacked Arabic
2518+ {
2519+ std::swap (modifiedRect.left , modifiedRect.right );
2520+ std::swap (modifiedRect.top , modifiedRect.bottom );
2521+ modifiedRect.left = -modifiedRect.left ;
2522+ modifiedRect.right = -modifiedRect.right ;
2523+ modifiedRect.top = -modifiedRect.top ;
2524+ modifiedRect.bottom = -modifiedRect.bottom ;
2525+ }
2526+ break ;
2527+
2528+ case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES: // for ideographs (isSideways = true)
2529+ {
2530+ auto oldLeft = modifiedRect.left ;
2531+ modifiedRect.left = modifiedRect.top ;
2532+ modifiedRect.top = -modifiedRect.right ;
2533+ modifiedRect.right = modifiedRect.bottom ;
2534+ modifiedRect.bottom = -oldLeft;
2535+ }
2536+ break ;
2537+
2538+ default :
2539+ DEBUG_ASSERT (" Update the code to handle the new orientation." );
2540+ }
2541+ }
2542+
2543+
2544+ // Checks whether a glyph has a non-empty black box.
2545+ inline bool GlyphHasSize (DWRITE_GLYPH_METRICS const & glyphMetrics) throw()
2546+ {
2547+ return static_cast <int64_t >(glyphMetrics.advanceWidth ) - glyphMetrics.leftSideBearing - glyphMetrics.rightSideBearing > 0
2548+ && static_cast <int64_t >(glyphMetrics.advanceHeight ) - glyphMetrics.topSideBearing - glyphMetrics.bottomSideBearing > 0 ;
2549+ }
2550+
2551+
2552+ HRESULT GetGlyphRunBlackBox (
2553+ float baselineOriginX,
2554+ float baselineOriginY,
2555+ IDWriteFontFace* fontFace,
2556+ float fontEmSize,
2557+ float pixelsPerDip,
2558+ const DWRITE_MATRIX* transform,
2559+ DWRITE_MEASURING_MODE measuringMode,
2560+ bool isRtlContent,
2561+ bool isRtlSpan,
2562+ bool isSideways,
2563+ uint32_t glyphCount,
2564+ _In_reads_ (glyphCount) const uint16_t* glyphIndices,
2565+ _In_reads_(glyphCount) const float* glyphAdvances,
2566+ _In_reads_opt_(glyphCount) const DWRITE_GLYPH_OFFSET* glyphOffsets,
2567+ _Inout_ D2D1_RECT_F& blackBox
2568+ )
2569+ {
2570+ if (glyphCount <= 0 )
2571+ {
2572+ return S_OK;
2573+ }
2574+
2575+ DWRITE_FONT_METRICS fontMetrics = {};
2576+ fontFace->GetMetrics (&fontMetrics);
2577+
2578+ DWRITE_GLYPH_METRICS smallBuffer[120 ];
2579+ std::vector<DWRITE_GLYPH_METRICS> bigBuffer;
2580+ DWRITE_GLYPH_METRICS* glyphRunMetrics = smallBuffer;
2581+
2582+ if (glyphCount > std::size (smallBuffer))
2583+ {
2584+ bigBuffer.resize (glyphCount);
2585+ glyphRunMetrics = &bigBuffer[0 ];
2586+ }
2587+
2588+ IFR (GetGlyphMetrics (
2589+ fontFace,
2590+ fontEmSize,
2591+ pixelsPerDip,
2592+ transform,
2593+ measuringMode,
2594+ isSideways,
2595+ glyphCount,
2596+ glyphIndices,
2597+ OUT glyphRunMetrics
2598+ ));
2599+
2600+ D2D1_RECT_F accumulatedBounds;
2601+ float baselineX = baselineOriginX;
2602+ float const baselineY = baselineOriginY;
2603+ float const fontScale = fontEmSize / fontMetrics.designUnitsPerEm ;
2604+
2605+ // Determine how to rotate the blackbox of each glyph from the flags.
2606+
2607+ bool const isFlippedOrientation = (isRtlContent != isRtlSpan);
2608+ DWRITE_GLYPH_ORIENTATION_ANGLE const rotationAmount = GetRelativeOrientation (isSideways, isFlippedOrientation);
2609+
2610+ for (uint32_t j = 0 ; j < glyphCount; ++j)
2611+ {
2612+ DWRITE_GLYPH_METRICS const & glyphMetrics = glyphRunMetrics[j];
2613+ float const nominalGlyphAdvance = (isSideways ? glyphMetrics.advanceHeight : glyphMetrics.advanceWidth ) * fontScale;
2614+ float const glyphAdvance = (glyphAdvances != nullptr ) ? glyphAdvances[j] : nominalGlyphAdvance;
2615+
2616+ float glyphX = baselineX, glyphY = baselineY;
2617+
2618+ // When glyphs are drawn RTL, the glyph origin is on the opposite
2619+ // side of the pen. So add the nominal advance to get the glyph origin
2620+ // (the nominal advance, not the user supplied advance). The sign
2621+ // depends on whether the glyphs are increasing/decreasing in
2622+ // coordinate space which depends on the direction of the span
2623+ // rather than the content (since upside-down RTL text is effectively
2624+ // LTR).
2625+ if (isRtlContent)
2626+ {
2627+ glyphX += isRtlSpan ? -nominalGlyphAdvance : nominalGlyphAdvance;
2628+ }
2629+ // Advance the pen.
2630+ baselineX += isRtlSpan ? -glyphAdvance : glyphAdvance;
2631+
2632+ // Skip any blank glyphs like the space character, which has no black box.
2633+ if (!GlyphHasSize (glyphMetrics))
2634+ continue ;
2635+
2636+ // For sideways glyphs like ideographs in vertical, move from the pen
2637+ // position (which is relative to the vertical origin) to the glyph
2638+ // origin at the bottom left (0,0 in font design space). The delta
2639+ // from the vertical origin to the horizontal origin is the vertical
2640+ // origin distance and half the advance width.
2641+ if (isSideways)
2642+ {
2643+ // If the glyph run is flipped relative to the line, then reverse
2644+ // the signs, since the glyph is turned 180 degrees relative to
2645+ // the coordinate space.
2646+ int32_t originY = glyphMetrics.verticalOriginY ;
2647+ int32_t advance = glyphMetrics.advanceWidth ;
2648+ glyphX += (isFlippedOrientation ? -originY : originY) * fontScale;
2649+ glyphY += (isFlippedOrientation ? -advance : advance) * fontScale * 0 .5f ;
2650+ }
2651+
2652+ // Determine ink edges of glyph (in font design units but Cartesian coordinates).
2653+ // The four corners are relative to the horizontal glyph origin now.
2654+ D2D_RECT_L intBounds = {
2655+ int32_t (glyphMetrics.leftSideBearing ),
2656+ int32_t (glyphMetrics.topSideBearing - glyphMetrics.verticalOriginY ),
2657+ int32_t (glyphMetrics.advanceWidth - glyphMetrics.rightSideBearing ),
2658+ int32_t (glyphMetrics.advanceHeight - glyphMetrics.bottomSideBearing - glyphMetrics.verticalOriginY )
2659+ };
2660+
2661+ RotateRect (IN OUT intBounds, rotationAmount);
2662+
2663+ // Add the glyph offset from shaping/character spacing/justification.
2664+ if (glyphOffsets != nullptr )
2665+ {
2666+ float advanceOffset = glyphOffsets[j].advanceOffset ;
2667+ float ascenderOffset = glyphOffsets[j].ascenderOffset ;
2668+ glyphX += isRtlSpan ? -advanceOffset : advanceOffset;
2669+ glyphY += isFlippedOrientation ? ascenderOffset : -ascenderOffset;
2670+ }
2671+
2672+ // Scale the font design units by the font size to get DIPs
2673+ // and add the glyph position to move from glyph space to layout space.
2674+ D2D1_RECT_F glyphBounds = {
2675+ float (intBounds.left ) * fontScale,
2676+ float (intBounds.top ) * fontScale,
2677+ float (intBounds.right ) * fontScale,
2678+ float (intBounds.bottom ) * fontScale
2679+ };
2680+ OffsetRect (IN OUT glyphBounds, glyphX, glyphY);
2681+
2682+ AccumulateRect (IN OUT accumulatedBounds, glyphBounds);
2683+ }
2684+
2685+ AccumulateRect (IN OUT blackBox, accumulatedBounds);
2686+
2687+ return S_OK;
2688+ }
2689+
2690+
2691+ HRESULT GetGlyphRunBlackBox (
2692+ DWRITE_GLYPH_RUN const & glyphRun,
2693+ float baselineOriginX,
2694+ float baselineOriginY,
2695+ _Inout_ D2D1_RECT_F& blackBox
2696+ )
2697+ {
2698+ return GetGlyphRunBlackBox (
2699+ baselineOriginX,
2700+ baselineOriginY,
2701+ glyphRun.fontFace ,
2702+ glyphRun.fontEmSize ,
2703+ 1 , // pixelsPerDip
2704+ nullptr , // transform
2705+ DWRITE_MEASURING_MODE_NATURAL,
2706+ glyphRun.bidiLevel & 1 ,
2707+ glyphRun.bidiLevel & 1 ,
2708+ glyphRun.isSideways ,
2709+ glyphRun.glyphCount ,
2710+ glyphRun.glyphIndices ,
2711+ glyphRun.glyphAdvances ,
2712+ glyphRun.glyphOffsets ,
2713+ OUT blackBox
2714+ );
2715+ }
0 commit comments