| Index: src/core/SkDraw.cpp
|
| diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
|
| index d8d6fcf57fe123b7926eae12b668114c600e94dc..f884d3bf3e5c4e8df8cd730f8ca3ce6444201b56 100644
|
| --- a/src/core/SkDraw.cpp
|
| +++ b/src/core/SkDraw.cpp
|
| @@ -14,6 +14,7 @@
|
| #include "SkDeviceLooper.h"
|
| #include "SkFixed.h"
|
| #include "SkMaskFilter.h"
|
| +#include "SkMatrix.h"
|
| #include "SkPaint.h"
|
| #include "SkPathEffect.h"
|
| #include "SkRasterClip.h"
|
| @@ -25,8 +26,10 @@
|
| #include "SkString.h"
|
| #include "SkStroke.h"
|
| #include "SkStrokeRec.h"
|
| +#include "SkTemplates.h"
|
| #include "SkTextMapStateProc.h"
|
| #include "SkTLazy.h"
|
| +#include "SkUtility.h"
|
| #include "SkUtils.h"
|
| #include "SkVertState.h"
|
|
|
| @@ -1439,7 +1442,7 @@ void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
|
| #pragma warning ( disable : 4701 )
|
| #endif
|
|
|
| -//////////////////////////////////////////////////////////////////////////////
|
| +////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
| static void D1G_RectClip(const SkDraw1Glyph& state, Sk48Dot16 fx, Sk48Dot16 fy, const SkGlyph& glyph) {
|
| // Prevent glyphs from being drawn outside of or straddling the edge of device space.
|
| @@ -1726,6 +1729,491 @@ void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength,
|
| }
|
| }
|
|
|
| +////////////////////////////////////////////////////////////////////////////////////////////////////
|
| +// Calculate a type with the same size as the max of all the Ts.
|
| +template <typename... Ts> struct MaxSizeOf;
|
| +
|
| +template <> struct MaxSizeOf<> { static const size_t value = 0; };
|
| +
|
| +template <typename H, typename... Ts> struct MaxSizeOf<H, Ts...> {
|
| + static const size_t value =
|
| + sizeof(H) >= MaxSizeOf<Ts...>::value ? sizeof(H) : MaxSizeOf<Ts...>::value;
|
| +};
|
| +
|
| +// UntaggedVariant is a pile of memory that can hold one of the Ts. It provides a way
|
| +// to initialize that memory in a typesafe way.
|
| +template <typename... Ts>
|
| +class UntaggedVariant {
|
| +public:
|
| + UntaggedVariant() {}
|
| + ~UntaggedVariant() {}
|
| + UntaggedVariant(const UntaggedVariant&) = delete;
|
| + UntaggedVariant& operator=(const UntaggedVariant&) = delete;
|
| + UntaggedVariant(UntaggedVariant&&) = delete;
|
| + UntaggedVariant& operator=(UntaggedVariant&&) = delete;
|
| +
|
| + template <typename Variant, typename... Args>
|
| + void initialize(Args &&... args) {
|
| + SkASSERT(sizeof(Variant) <= sizeof(fSpace));
|
| + #if defined(_MSC_VER) && _MSC_VER < 1900
|
| + #define alignof __alignof
|
| + #endif
|
| + SkASSERT(alignof(Variant) <= alignof(Space));
|
| + new (&fSpace) Variant(skstd::forward<Args>(args)...);
|
| + }
|
| +
|
| +private:
|
| + typedef SkAlignedSStorage<MaxSizeOf<Ts...>::value> Space;
|
| + Space fSpace;
|
| +};
|
| +
|
| +// PolymorphicVariant holds subclasses of Base without slicing. Ts must be subclasses of Base.
|
| +template <typename Base, typename... Ts>
|
| +class PolymorphicVariant {
|
| +public:
|
| + typedef UntaggedVariant<Ts...> Variants;
|
| + template <typename Initializer>
|
| + PolymorphicVariant(Initializer&& initializer) {
|
| + initializer(&fVariants);
|
| + }
|
| + ~PolymorphicVariant() { get()->~Base(); }
|
| + Base* get() const { return reinterpret_cast<Base*>(&fVariants); }
|
| + Base* operator->() const { return get(); }
|
| + Base& operator*() const { return *get(); }
|
| +
|
| +private:
|
| + mutable Variants fVariants;
|
| +};
|
| +
|
| +// PositionReaderInterface reads a point from the pos vector.
|
| +// * HorizontalPositions - assumes a common Y for many X values.
|
| +// * ArbitraryPositions - a list of (X,Y) pairs.
|
| +class PositionReaderInterface : SkNoncopyable {
|
| +public:
|
| + virtual ~PositionReaderInterface() { }
|
| + virtual SkPoint nextPoint() = 0;
|
| +};
|
| +
|
| +class HorizontalPositions final : public PositionReaderInterface {
|
| +public:
|
| + HorizontalPositions(const SkScalar* positions)
|
| + : fPositions(positions) { }
|
| +
|
| + SkPoint nextPoint() override {
|
| + SkScalar x = *fPositions++;
|
| + return {x, 0};
|
| + }
|
| +private:
|
| + const SkScalar* fPositions;
|
| +};
|
| +
|
| +class ArbitraryPositions final : public PositionReaderInterface {
|
| +public:
|
| + ArbitraryPositions(const SkScalar* positions)
|
| + : fPositions(positions) { }
|
| + SkPoint nextPoint() override {
|
| + SkPoint to_return {fPositions[0], fPositions[1]};
|
| + fPositions += 2;
|
| + return to_return;
|
| + }
|
| +
|
| +private:
|
| + const SkScalar* fPositions;
|
| +};
|
| +
|
| +typedef PolymorphicVariant<PositionReaderInterface, HorizontalPositions, ArbitraryPositions>
|
| + PositionReader;
|
| +
|
| +// 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.
|
| +class MapperInterface : SkNoncopyable {
|
| +public:
|
| + virtual ~MapperInterface() {}
|
| + virtual SkPoint map(SkPoint position) const = 0;
|
| +};
|
| +
|
| +class TranslationMapper final : public MapperInterface {
|
| +public:
|
| + TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
|
| + : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }
|
| + SkPoint map(SkPoint position) const override {
|
| + return position + fTranslate;
|
| + }
|
| +
|
| +private:
|
| + const SkPoint fTranslate;
|
| +};
|
| +
|
| +class XScaleMapper final : public MapperInterface {
|
| +public:
|
| + XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
|
| + : fTranslate(matrix.mapXY(origin.fX, origin.fY))
|
| + , fXScale(matrix.getScaleX()) { }
|
| + SkPoint map(SkPoint position) const override {
|
| + return {fXScale * position.fX + fTranslate.fX, fTranslate.fY};
|
| + }
|
| +
|
| +private:
|
| + const SkPoint fTranslate;
|
| + const SkScalar fXScale;
|
| +};
|
| +
|
| +// The caller must keep matrix alive while this class is used.
|
| +class GeneralMapper final : public MapperInterface {
|
| +public:
|
| + GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
|
| + : fOrigin(origin)
|
| + , fMatrix(matrix)
|
| + , fMapProc(matrix.getMapXYProc()) { }
|
| + SkPoint map(SkPoint position) const override {
|
| + SkPoint result;
|
| + fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY, &result);
|
| + return result;
|
| + }
|
| +
|
| +private:
|
| + const SkPoint fOrigin;
|
| + const SkMatrix& fMatrix;
|
| + const SkMatrix::MapXYProc fMapProc;
|
| +};
|
| +
|
| +typedef PolymorphicVariant<MapperInterface, TranslationMapper, XScaleMapper, GeneralMapper> Mapper;
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////////////////////////
|
| +// Text alignment handles shifting the glyph based on its width.
|
| +static SkPoint text_alignment_adjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) {
|
| + switch (textAlignment) {
|
| + case SkPaint::kLeft_Align:
|
| + return {0.0f, 0.0f};
|
| + break;
|
| + case SkPaint::kCenter_Align:
|
| + return {SkFixedToScalar(glyph.fAdvanceX >> 1),
|
| + SkFixedToScalar(glyph.fAdvanceY >> 1)};
|
| + break;
|
| + case SkPaint::kRight_Align:
|
| + return {SkFixedToScalar(glyph.fAdvanceX),
|
| + SkFixedToScalar(glyph.fAdvanceY)};
|
| + break;
|
| + }
|
| + // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy.
|
| + SkFAIL("Should never get here.");
|
| + return {0.0f, 0.0f};
|
| +}
|
| +
|
| +// The "call" to SkFixedToScalar is actually a macro. It's macros all the way down.
|
| +static const SkScalar 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
|
| +// positioned glyph.
|
| +static SkPoint subpixel_position_rounding(SkAxisAlignment axisAlignment) {
|
| + switch (axisAlignment) {
|
| + case kX_SkAxisAlignment:
|
| + return {SkFixedToScalar(SkGlyph::kSubpixelRound), SK_ScalarHalf};
|
| + case kY_SkAxisAlignment:
|
| + return {SK_ScalarHalf, kSubpixelRounding};
|
| + case kNone_SkAxisAlignment:
|
| + return {kSubpixelRounding, kSubpixelRounding};
|
| + }
|
| + SkFAIL("Should not get here.");
|
| + return {0.0f, 0.0f};
|
| +}
|
| +
|
| +// The subpixel_position_alignment 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 subpixel_position_alignment(SkAxisAlignment axisAlignment, SkPoint position) {
|
| + switch (axisAlignment) {
|
| + case kX_SkAxisAlignment:
|
| + return {SkScalarToFixed(position.fX + kSubpixelRounding), 0};
|
| + case kY_SkAxisAlignment:
|
| + return {0, SkScalarToFixed(position.fY + kSubpixelRounding)};
|
| + case kNone_SkAxisAlignment:
|
| + return {SkScalarToFixed(position.fX + kSubpixelRounding),
|
| + SkScalarToFixed(position.fY + kSubpixelRounding)};
|
| + }
|
| + SkFAIL("Should not get here.");
|
| + return {0, 0};
|
| +}
|
| +
|
| +// GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does glyph
|
| +// specific position adjustment. The findAndPositionGlyph method takes text and position and calls
|
| +// processOneGlyph with the correct glyph, final position and rounding terms. The final position
|
| +// is not rounded yet and is the responsibility of processOneGlyph.
|
| +template <typename ProcessOneGlyph>
|
| +class GlyphFindAndPlaceInterface : SkNoncopyable {
|
| +public:
|
| + struct Result {
|
| + const SkGlyph* fGlyph;
|
| + Sk48Dot16 fX;
|
| + Sk48Dot16 fY;
|
| + };
|
| + virtual ~GlyphFindAndPlaceInterface() { };
|
| + // 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) {};
|
| +};
|
| +
|
| +// GlyphFindAndPlaceForSubpixel 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>
|
| +class GlyphFindAndPlaceForSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
|
| +public:
|
| + GlyphFindAndPlaceForSubpixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
|
| + : fCache(cache)
|
| + , fGlyphCacheProc(glyphCacheProc) {
|
| + }
|
| + void 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}.
|
| + const char* tempText = *text;
|
| + const SkGlyph& metricGlyph = fGlyphCacheProc(fCache, &tempText, 0, 0);
|
| +
|
| + if (metricGlyph.fWidth <= 0) {
|
| + return;
|
| + }
|
| +
|
| + // Adjust the final position by the alignment adjustment.
|
| + finalPosition -= text_alignment_adjustment(kTextAlignment, metricGlyph);
|
| + }
|
| +
|
| + // Find the glyph.
|
| + SkIPoint lookupPosition = subpixel_position_alignment(kAxisAlignment, finalPosition);
|
| + 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, subpixel_position_rounding(kAxisAlignment));
|
| + }
|
| + }
|
| +
|
| +private:
|
| + SkGlyphCache* const fCache;
|
| + SkDrawCacheProc fGlyphCacheProc;
|
| +};
|
| +
|
| +// GlyphFindAndPlaceForFullPixel handles finding and placing glyphs when no sub-pixel positioning
|
| +// is requested.
|
| +template <typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
|
| +class GlyphFindAndPlaceForFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
|
| +public:
|
| + GlyphFindAndPlaceForFullPixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
|
| + : fCache(cache)
|
| + , fGlyphCacheProc(glyphCacheProc) { }
|
| + void 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;
|
| + }
|
| + finalPosition -= text_alignment_adjustment(kTextAlignment, glyph);
|
| + processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
|
| + }
|
| +
|
| +private:
|
| + SkGlyphCache* const fCache;
|
| + SkDrawCacheProc fGlyphCacheProc;
|
| +};
|
| +
|
| +// GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and
|
| +// placing a glyph. There are three factors that go into the different factors.
|
| +// * Is sub-pixel positioned - a boolean that says whether to use sub-pixel positioning.
|
| +// * Text alignment - indicates if the glyph should be placed to the right, centered or left of a
|
| +// given position.
|
| +// * Axis alignment - indicates if the glyphs final sub-pixel position should be rounded to a
|
| +// whole pixel if the glyph is aligned with an axis. This is only used for sub-pixel positioning
|
| +// and allows the baseline to look crisp.
|
| +template <typename ProcessOneGlyph>
|
| +using GlyphFindAndPlace =
|
| + PolymorphicVariant<
|
| + GlyphFindAndPlaceInterface<ProcessOneGlyph>,
|
| + // Subpixel
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNone_SkAxisAlignment>,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment >,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kY_SkAxisAlignment >,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNone_SkAxisAlignment>,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kX_SkAxisAlignment >,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kY_SkAxisAlignment >,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kNone_SkAxisAlignment>,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kX_SkAxisAlignment >,
|
| + GlyphFindAndPlaceForSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kY_SkAxisAlignment >,
|
| + // Full pixel
|
| + GlyphFindAndPlaceForFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align >,
|
| + GlyphFindAndPlaceForFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align>,
|
| + GlyphFindAndPlaceForFullPixel<ProcessOneGlyph, SkPaint::kRight_Align >
|
| + >;
|
| +
|
| +// init_subpixel is a helper function for initializing all the variants of
|
| +// GlyphFindAndPlaceForSubpixel.
|
| +template <typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
|
| +static void init_subpixel(
|
| + typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init,
|
| + SkAxisAlignment axisAlignment,
|
| + SkGlyphCache* cache,
|
| + SkDrawCacheProc glyphCacheProc) {
|
| + switch (axisAlignment) {
|
| + case kX_SkAxisAlignment:
|
| + to_init->template initialize<GlyphFindAndPlaceForSubpixel<
|
| + ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(
|
| + cache, glyphCacheProc);
|
| + break;
|
| + case kNone_SkAxisAlignment:
|
| + to_init->template initialize<GlyphFindAndPlaceForSubpixel<
|
| + ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(
|
| + cache, glyphCacheProc);
|
| + break;
|
| + case kY_SkAxisAlignment:
|
| + to_init->template initialize<GlyphFindAndPlaceForSubpixel<
|
| + ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(
|
| + cache, glyphCacheProc);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +// specialized_process_pos_text is a version of ProcessPosText that de-virtualizes the different
|
| +// components used. It returns true if it can handle the situation, otherwise it returns false.
|
| +// This allows greater inlining freedom to the compiler. Currently, there is only one specialized
|
| +// variant: sub-pixel position, left-aligned, x-axis-aligned, translation, and one scalar per
|
| +// position entry.
|
| +// * This is by far the most common type of text Blink draws.
|
| +template <typename ProcessOneGlyph>
|
| +static bool specialized_process_pos_text(const char* const text, size_t byteLength,
|
| + const SkPoint& offset, const SkMatrix& matrix,
|
| + const SkScalar pos[], int scalarsPerPosition,
|
| + SkPaint::Align textAlignment,
|
| + SkDrawCacheProc& glyphCacheProc,
|
| + SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
|
| + SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
|
| + uint32_t mtype = matrix.getType();
|
| + if (scalarsPerPosition == 1
|
| + && textAlignment == SkPaint::kLeft_Align
|
| + && axisAlignment == kX_SkAxisAlignment
|
| + && cache->isSubpixel()
|
| + && mtype <= SkMatrix::kTranslate_Mask) {
|
| + typedef GlyphFindAndPlaceForSubpixel<
|
| + ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment> Positioner;
|
| + HorizontalPositions positions{pos};
|
| + TranslationMapper mapper{matrix, offset};
|
| + Positioner positioner(cache, glyphCacheProc);
|
| + const char *cursor = text;
|
| + const char *stop = text + byteLength;
|
| + while (cursor < stop) {
|
| + SkPoint mappedPoint = mapper.TranslationMapper::map(
|
| + positions.HorizontalPositions::nextPoint());
|
| + positioner.Positioner::findAndPositionGlyph(
|
| + &cursor, mappedPoint, skstd::forward<ProcessOneGlyph>(processOneGlyph));
|
| + }
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// 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.
|
| +//
|
| +// The routine processOneGlyph passed in by the client has the following signature:
|
| +// void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding);
|
| +//
|
| +// * Sub-pixel positioning (2) - use sub-pixel positioning.
|
| +// * Text alignment (3) - text alignment with respect to the glyph's width.
|
| +// * Matrix type (3) - special cases for translation and X-coordinate scaling.
|
| +// * Components per position (2) - the positions vector can have a common Y with different Xs, or
|
| +// XY-pairs.
|
| +// * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel positioning, round to
|
| +// a whole coordinate instead of using sub-pixel positioning.
|
| +// The number of variations is 108 for sub-pixel and 36 for full-pixel.
|
| +// This routine handles all of them using inline polymorphic variable (no heap allocation).
|
| +template <typename ProcessOneGlyph>
|
| +static void process_pos_text(const char text[], size_t byteLength,
|
| + const SkPoint& offset, const SkMatrix& matrix,
|
| + const SkScalar pos[], int scalarsPerPosition,
|
| + SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc,
|
| + SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
|
| +
|
| + PositionReader positionReader {
|
| + [&](PositionReader::Variants* to_init) {
|
| + if (2 == scalarsPerPosition) {
|
| + to_init->initialize<ArbitraryPositions>(pos);
|
| + } else {
|
| + to_init->initialize<HorizontalPositions>(pos);
|
| + }
|
| + }
|
| + };
|
| +
|
| + Mapper mapper {
|
| + [&] (Mapper::Variants* to_init) {
|
| + uint32_t mtype = matrix.getType();
|
| + if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)
|
| + || scalarsPerPosition == 2) {
|
| + to_init->initialize<GeneralMapper>(matrix, offset);
|
| + } else if (mtype & SkMatrix::kScale_Mask) {
|
| + to_init->initialize<XScaleMapper>(matrix, offset);
|
| + } else {
|
| + to_init->initialize<TranslationMapper>(matrix, offset);
|
| + }
|
| + }
|
| + };
|
| +
|
| + GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{
|
| + [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
|
| + if (cache->isSubpixel()) {
|
| + SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
|
| + switch (textAlignment) {
|
| + case SkPaint::kLeft_Align:
|
| + init_subpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
|
| + to_init, axisAlignment, cache, glyphCacheProc);
|
| + break;
|
| + case SkPaint::kCenter_Align:
|
| + init_subpixel<ProcessOneGlyph, SkPaint::kCenter_Align>(
|
| + to_init, axisAlignment, cache, glyphCacheProc);
|
| + break;
|
| + case SkPaint::kRight_Align:
|
| + init_subpixel<ProcessOneGlyph, SkPaint::kRight_Align>(
|
| + to_init, axisAlignment, cache, glyphCacheProc);
|
| + break;
|
| + }
|
| + } else {
|
| + switch (textAlignment) {
|
| + case SkPaint::kLeft_Align:
|
| + to_init->template initialize<GlyphFindAndPlaceForFullPixel<ProcessOneGlyph,
|
| + SkPaint::kLeft_Align>>(
|
| + cache, glyphCacheProc);
|
| + break;
|
| + case SkPaint::kCenter_Align:
|
| + to_init->template initialize<GlyphFindAndPlaceForFullPixel<ProcessOneGlyph,
|
| + SkPaint::kCenter_Align>>(
|
| + cache, glyphCacheProc);
|
| + break;
|
| + case SkPaint::kRight_Align:
|
| + to_init->template initialize<GlyphFindAndPlaceForFullPixel<ProcessOneGlyph,
|
| + SkPaint::kRight_Align>>(
|
| + cache, glyphCacheProc);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + };
|
| +
|
| + const char* stop = text + byteLength;
|
| + while (text < stop) {
|
| + SkPoint mappedPoint = mapper->map(positionReader->nextPoint());
|
| + findAndPosition->findAndPositionGlyph(
|
| + &text, mappedPoint, skstd::forward<ProcessOneGlyph>(processOneGlyph));
|
| + }
|
| +}
|
| +
|
| void SkDraw::drawPosText(const char text[], size_t byteLength,
|
| const SkScalar pos[], int scalarsPerPosition,
|
| const SkPoint& offset, const SkPaint& paint) const {
|
| @@ -1760,108 +2248,20 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
|
| }
|
| }
|
|
|
| - const char* stop = text + byteLength;
|
| - SkTextAlignProc alignProc(paint.getTextAlign());
|
| SkDraw1Glyph d1g;
|
| SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache, paint);
|
| - SkTextMapStateProc tmsProc(*fMatrix, offset, scalarsPerPosition);
|
| -
|
| - if (cache->isSubpixel()) {
|
| - // maybe we should skip the rounding if linearText is set
|
| - SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
|
| -
|
| - SkFixed fxMask = ~0;
|
| - SkFixed fyMask = ~0;
|
| - if (kX_SkAxisAlignment == baseline) {
|
| - fyMask = 0;
|
| - d1g.fHalfSampleY = SK_ScalarHalf;
|
| - } else if (kY_SkAxisAlignment == baseline) {
|
| - fxMask = 0;
|
| - d1g.fHalfSampleX = SK_ScalarHalf;
|
| - }
|
|
|
| - if (SkPaint::kLeft_Align == paint.getTextAlign()) {
|
| - while (text < stop) {
|
| - SkPoint tmsLoc;
|
| - tmsProc(pos, &tmsLoc);
|
| -
|
| - Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + d1g.fHalfSampleX);
|
| - Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + d1g.fHalfSampleY);
|
| -
|
| - const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
|
| -
|
| - if (glyph.fWidth) {
|
| - proc(d1g, fx, fy, glyph);
|
| - }
|
| - pos += scalarsPerPosition;
|
| - }
|
| - } else {
|
| - while (text < stop) {
|
| - const char* currentText = text;
|
| - const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
|
| -
|
| - if (metricGlyph.fWidth) {
|
| - SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
|
| - SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
|
| - SkPoint tmsLoc;
|
| - tmsProc(pos, &tmsLoc);
|
| -
|
| - SkPoint alignLoc;
|
| - alignProc(tmsLoc, metricGlyph, &alignLoc);
|
| -
|
| - Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + d1g.fHalfSampleX);
|
| - Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + d1g.fHalfSampleY);
|
| -
|
| - // have to call again, now that we've been "aligned"
|
| - const SkGlyph& glyph = glyphCacheProc(cache, ¤tText,
|
| - fx & fxMask, fy & fyMask);
|
| - // the assumption is that the metrics haven't changed
|
| - SkASSERT(prevAdvX == glyph.fAdvanceX);
|
| - SkASSERT(prevAdvY == glyph.fAdvanceY);
|
| - SkASSERT(glyph.fWidth);
|
| -
|
| - proc(d1g, fx, fy, glyph);
|
| - }
|
| - pos += scalarsPerPosition;
|
| - }
|
| - }
|
| - } else { // not subpixel
|
| - if (SkPaint::kLeft_Align == paint.getTextAlign()) {
|
| - while (text < stop) {
|
| - // the last 2 parameters are ignored
|
| - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
|
| -
|
| - if (glyph.fWidth) {
|
| - SkPoint tmsLoc;
|
| - tmsProc(pos, &tmsLoc);
|
| -
|
| - proc(d1g,
|
| - SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf), //d1g.fHalfSampleX,
|
| - SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf), //d1g.fHalfSampleY,
|
| - glyph);
|
| - }
|
| - pos += scalarsPerPosition;
|
| - }
|
| - } else {
|
| - while (text < stop) {
|
| - // the last 2 parameters are ignored
|
| - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
|
| -
|
| - if (glyph.fWidth) {
|
| - SkPoint tmsLoc;
|
| - tmsProc(pos, &tmsLoc);
|
| -
|
| - SkPoint alignLoc;
|
| - alignProc(tmsLoc, glyph, &alignLoc);
|
| -
|
| - proc(d1g,
|
| - SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf), //d1g.fHalfSampleX,
|
| - SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf), //d1g.fHalfSampleY,
|
| - glyph);
|
| - }
|
| - pos += scalarsPerPosition;
|
| - }
|
| - }
|
| + auto processOneGlyph =
|
| + [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
| + position += rounding;
|
| + proc(d1g, SkScalarToFixed(position.fX), SkScalarToFixed(position.fY), glyph);
|
| + };
|
| +
|
| + SkPaint::Align textAlignment = paint.getTextAlign();
|
| + if (!specialized_process_pos_text(text, byteLength, offset, *fMatrix, pos, scalarsPerPosition,
|
| + textAlignment, glyphCacheProc, cache, processOneGlyph)) {
|
| + process_pos_text(text, byteLength, offset, *fMatrix, pos, scalarsPerPosition,
|
| + textAlignment, glyphCacheProc, cache, processOneGlyph);
|
| }
|
| }
|
|
|
|
|