Index: src/core/SkFindAndPlaceGlyph.h |
diff --git a/src/core/SkFindAndPlaceGlyph.h b/src/core/SkFindAndPlaceGlyph.h |
index 3de4c61c42529d77b1c88216b4e70a9e68b40343..15000b3d58395ab6e028b3a0555052c5b710d46d 100644 |
--- a/src/core/SkFindAndPlaceGlyph.h |
+++ b/src/core/SkFindAndPlaceGlyph.h |
@@ -8,6 +8,7 @@ |
#ifndef SkFindAndPositionGlyph_DEFINED |
#define SkFindAndPositionGlyph_DEFINED |
+#include "SkAutoKern.h" |
#include "SkGlyph.h" |
#include "SkGlyphCache.h" |
#include "SkPaint.h" |
@@ -30,6 +31,10 @@ struct SkMaxSizeOf<H, Ts...> { |
class SkFindAndPlaceGlyph { |
public: |
+ template<typename ProcessOneGlyph> |
+ static void ProcessText(const char text[], size_t byteLength, SkPoint offset, const |
+ SkMatrix& matrix, SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc, |
+ SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph); |
// ProcessPosText handles all cases for finding and positioning glyphs. It has a very large |
// multiplicity. It figures out the glyph, position and rounding and pass those parameters to |
// processOneGlyph. |
@@ -48,7 +53,7 @@ public: |
// This routine handles all of them using inline polymorphic variable (no heap allocation). |
template<typename ProcessOneGlyph> |
static void ProcessPosText(const char text[], size_t byteLength, |
- const SkPoint& offset, const SkMatrix& matrix, |
+ SkPoint offset, const SkMatrix& matrix, |
const SkScalar pos[], int scalarsPerPosition, |
SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc, |
SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph); |
@@ -142,7 +147,8 @@ private: |
typedef PolymorphicVariant<PositionReaderInterface, HorizontalPositions, ArbitraryPositions> |
PositionReader; |
- // MapperInterface given a point map it through the matrix. There are several shortcut variants. |
+ // MapperInterface given a point map it through the matrix. There are several shortcut |
+ // variants. |
// * TranslationMapper - assumes a translation only matrix. |
// * XScaleMapper - assumes an X scaling and a translation. |
// * GeneralMapper - Does all other matricies. |
@@ -201,7 +207,7 @@ private: |
typedef PolymorphicVariant< |
MapperInterface, TranslationMapper, XScaleMapper, GeneralMapper> Mapper; |
- // Text alignment handles shifting the glyph based on its width. |
+ // TextAlignmentAdjustment handles shifting the glyph based on its width. |
static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) { |
switch (textAlignment) { |
case SkPaint::kLeft_Align: |
@@ -222,8 +228,7 @@ private: |
// Needs to be a macro because you can't have a const float unless you make it constexpr. |
#define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound)) |
- // Functions for handling sub-pixel aligned positions. |
- // The subpixel_position_rounding function returns a point suitable for rounding a sub-pixel |
+ // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel |
// positioned glyph. |
static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) { |
switch (axisAlignment) { |
@@ -238,7 +243,7 @@ private: |
return {0.0f, 0.0f}; |
} |
- // The subpixel_position_alignment function produces a suitable position for the glyph cache to |
+ // The SubpixelAlignment function produces a suitable position for the glyph cache to |
// produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut |
// of 0 is used for the sub-pixel position. |
static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) { |
@@ -266,36 +271,47 @@ private: |
public: |
virtual ~GlyphFindAndPlaceInterface() { }; |
+ // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and |
+ // returns the position of where the next glyph will be using the glyph's advance and |
+ // possibly kerning. The returned position is used by drawText, but ignored by drawPosText. |
+ // The compiler should prune all this calculation if the return value is not used. |
+ // |
// This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a |
// compile error. |
// See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277 |
- virtual void findAndPositionGlyph(const char** text, SkPoint position, |
- ProcessOneGlyph&& processOneGlyph) { }; |
+ virtual SkPoint findAndPositionGlyph( |
+ const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) { |
+ SkFAIL("Should never get here."); |
+ return {0.0f, 0.0f}; |
+ }; |
}; |
// GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is |
// requested. After it has found and placed the glyph it calls the templated function |
// ProcessOneGlyph in order to actually perform an action. |
- template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SkAxisAlignment kAxisAlignment> |
+ template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, |
+ SkAxisAlignment kAxisAlignment> |
class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { |
public: |
GlyphFindAndPlaceSubpixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc) |
- : fCache(cache), fGlyphCacheProc(glyphCacheProc) { |
- } |
+ : fCache(cache) |
+ , fGlyphCacheProc(glyphCacheProc) { } |
- void findAndPositionGlyph(const char** text, SkPoint position, |
- ProcessOneGlyph&& processOneGlyph) override { |
+ SkPoint findAndPositionGlyph( |
+ const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { |
SkPoint finalPosition = position; |
if (kTextAlignment != SkPaint::kLeft_Align) { |
- // Get the width of an un-sub-pixel positioned glyph for calculating the alignment. |
- // This is not needed for kLeftAlign because its adjustment is always {0, 0}. |
+ // Get the width of an un-sub-pixel positioned glyph for calculating the |
+ // alignment. This is not needed for kLeftAlign because its adjustment is |
+ // always {0, 0}. |
const char* tempText = *text; |
const SkGlyph &metricGlyph = fGlyphCacheProc(fCache, &tempText, 0, 0); |
if (metricGlyph.fWidth <= 0) { |
// Exiting early, be sure to update text pointer. |
*text = tempText; |
- return; |
+ return finalPosition + SkPoint{SkFixedToScalar(metricGlyph.fAdvanceX), |
+ SkFixedToScalar(metricGlyph.fAdvanceY)}; |
} |
// Adjust the final position by the alignment adjustment. |
@@ -304,14 +320,16 @@ private: |
// Find the glyph. |
SkIPoint lookupPosition = SubpixelAlignment(kAxisAlignment, finalPosition); |
- const SkGlyph& renderGlyph = fGlyphCacheProc( |
- fCache, text, lookupPosition.fX, lookupPosition.fY); |
+ const SkGlyph& renderGlyph = |
+ fGlyphCacheProc(fCache, text, lookupPosition.fX, lookupPosition.fY); |
// If the glyph has no width (no pixels) then don't bother processing it. |
if (renderGlyph.fWidth > 0) { |
processOneGlyph(renderGlyph, finalPosition, |
SubpixelPositionRounding(kAxisAlignment)); |
} |
+ return finalPosition + SkPoint{SkFixedToScalar(renderGlyph.fAdvanceX), |
+ SkFixedToScalar(renderGlyph.fAdvanceY)}; |
} |
private: |
@@ -319,28 +337,43 @@ private: |
SkDrawCacheProc fGlyphCacheProc; |
}; |
+ enum SelectKerning { |
+ kNoKerning = false, |
+ kUseKerning = true |
+ }; |
+ |
// GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel |
- // positioning is requested. |
- template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment> |
+ // positioning is requested. The kUseKerning argument should be true for drawText, and false |
+ // for drawPosText. |
+ template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning> |
class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { |
public: |
GlyphFindAndPlaceFullPixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc) |
- : fCache(cache), fGlyphCacheProc(glyphCacheProc) { } |
+ : fCache(cache), fGlyphCacheProc(glyphCacheProc) { |
+ // Kerning can only be used with SkPaint::kLeft_Align |
+ static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment, |
+ "Kerning can only be used with left aligned text."); |
+ } |
- void findAndPositionGlyph(const char** text, SkPoint position, |
- ProcessOneGlyph&& processOneGlyph) override { |
+ SkPoint findAndPositionGlyph( |
+ const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { |
SkPoint finalPosition = position; |
const SkGlyph& glyph = fGlyphCacheProc(fCache, text, 0, 0); |
- if (glyph.fWidth <= 0) { |
- return; |
+ if (kUseKerning) { |
+ finalPosition += {SkFixedToScalar(fAutoKern.adjust(glyph)), 0.0f}; |
} |
- finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph); |
- processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf}); |
+ if (glyph.fWidth > 0) { |
+ finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph); |
+ processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf}); |
+ } |
+ return finalPosition + SkPoint{SkFixedToScalar(glyph.fAdvanceX), |
+ SkFixedToScalar(glyph.fAdvanceY)}; |
} |
private: |
SkGlyphCache* const fCache; |
SkDrawCacheProc fGlyphCacheProc; |
+ SkAutoKern fAutoKern; |
}; |
// GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and |
@@ -365,9 +398,9 @@ private: |
GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kX_SkAxisAlignment >, |
GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kY_SkAxisAlignment >, |
// Full pixel |
- GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align >, |
- GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align>, |
- GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align > |
+ GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNoKerning>, |
+ GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNoKerning>, |
+ GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align, kNoKerning> |
>; |
// InitSubpixel is a helper function for initializing all the variants of |
@@ -396,11 +429,30 @@ private: |
break; |
} |
} |
+ |
+ static SkPoint MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc, |
+ const char text[], size_t byteLength) { |
+ SkFixed x = 0, y = 0; |
+ const char* stop = text + byteLength; |
+ |
+ SkAutoKern autokern; |
+ |
+ while (text < stop) { |
+ // don't need x, y here, since all subpixel variants will have the |
+ // same advance |
+ const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); |
+ |
+ x += autokern.adjust(glyph) + glyph.fAdvanceX; |
+ y += glyph.fAdvanceY; |
+ } |
+ SkASSERT(text == stop); |
+ return {SkFixedToScalar(x), SkFixedToScalar(y)}; |
+ } |
}; |
template<typename ProcessOneGlyph> |
inline void SkFindAndPlaceGlyph::ProcessPosText( |
- const char text[], size_t byteLength, const SkPoint& offset, const SkMatrix& matrix, |
+ const char text[], size_t byteLength, SkPoint offset, const SkMatrix& matrix, |
const SkScalar pos[], int scalarsPerPosition, SkPaint::Align textAlignment, |
SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { |
@@ -475,17 +527,17 @@ inline void SkFindAndPlaceGlyph::ProcessPosText( |
case SkPaint::kLeft_Align: |
to_init->template initialize< |
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, |
- SkPaint::kLeft_Align>>(cache, glyphCacheProc); |
+ SkPaint::kLeft_Align, kNoKerning>>(cache, glyphCacheProc); |
break; |
case SkPaint::kCenter_Align: |
to_init->template initialize< |
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, |
- SkPaint::kCenter_Align>>(cache, glyphCacheProc); |
+ SkPaint::kCenter_Align, kNoKerning>>(cache, glyphCacheProc); |
break; |
case SkPaint::kRight_Align: |
to_init->template initialize< |
GlyphFindAndPlaceFullPixel<ProcessOneGlyph, |
- SkPaint::kRight_Align>>(cache, glyphCacheProc); |
+ SkPaint::kRight_Align, kNoKerning>>(cache, glyphCacheProc); |
break; |
} |
} |
@@ -500,4 +552,48 @@ inline void SkFindAndPlaceGlyph::ProcessPosText( |
} |
} |
+template<typename ProcessOneGlyph> |
+inline void SkFindAndPlaceGlyph::ProcessText( |
+ const char text[], size_t byteLength, SkPoint offset, const SkMatrix& matrix, |
+ SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache, |
+ ProcessOneGlyph&& processOneGlyph) { |
+ |
+ // transform the starting point |
+ matrix.mapPoints(&offset, 1); |
+ |
+ // need to measure first |
+ if (textAlignment != SkPaint::kLeft_Align) { |
+ SkVector stop = MeasureText(cache, glyphCacheProc, text, byteLength); |
+ |
+ if (textAlignment == SkPaint::kCenter_Align) { |
+ stop *= SK_ScalarHalf; |
+ } |
+ offset -= stop; |
+ } |
+ |
+ GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{ |
+ [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) { |
+ if (cache->isSubpixel()) { |
+ SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix); |
+ InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>( |
+ to_init, axisAlignment, cache, glyphCacheProc); |
+ } else { |
+ to_init->template initialize< |
+ GlyphFindAndPlaceFullPixel< |
+ ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>>( |
+ cache, glyphCacheProc); |
+ } |
+ } |
+ }; |
+ |
+ const char* stop = text + byteLength; |
+ SkPoint current = offset; |
+ while (text < stop) { |
+ current = |
+ findAndPosition->findAndPositionGlyph( |
+ &text, current, skstd::forward<ProcessOneGlyph>(processOneGlyph)); |
+ |
+ } |
+} |
+ |
#endif // SkFindAndPositionGlyph_DEFINED |