Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(122)

Unified Diff: src/core/SkDraw.cpp

Issue 1420973005: Refactor glyph selection and positioning (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix Ben's comment Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/core/SkDraw.cpp
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index d8d6fcf57fe123b7926eae12b668114c600e94dc..5da393f6b3ecbe1581cb8c9be240306ea51aad1e 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,474 @@ void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength,
}
}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Calculate a type with the same size and alignment as a list of types Ts.
mtklein 2015/11/04 17:54:11 You might just drop the AndAlignment on these name
herb_g 2015/11/04 21:58:42 Done.
+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;
+};
+
+template <typename... Ts> struct SameSizeAndAlignmentAs {
+private:
+ SkAlignedSStorage<MaxSizeOf<Ts...>::value> unused;
mtklein 2015/11/04 17:54:11 Even template <typename... Ts> using SameSizeAndA
herb_g 2015/11/04 21:58:41 I just embeded SkAlignedSStorage<MaxSizeOf<Ts...>:
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 {
mtklein 2015/11/04 17:54:12 I'm not sure I see why SameSizeAndAlignmentAs is s
herb_g 2015/11/04 21:58:42 I agree with the SameSizeAndAlignmentAs. This is f
+public:
+ UntaggedVariant() {}
+ ~UntaggedVariant() {}
+ UntaggedVariant(const UntaggedVariant&) = delete;
+ UntaggedVariant& operator=(const UntaggedVariant&) = delete;
+ UntaggedVariant(UntaggedVariant&&) = delete;
+ UntaggedVariant& operator=(UntaggedVariant&&) = delete;
+ template <typename Variant, typename... Args>
mtklein 2015/11/04 17:54:11 It'd be nice to add a newline before template.
herb_g 2015/11/04 21:58:42 Done.
+ void Init(Args&&... args) {
mtklein 2015/11/04 17:54:11 -> init
+ SkASSERT(sizeof(Variant) <= sizeof(fSpace));
+ #ifndef SK_BUILD_FOR_WIN32
mtklein 2015/11/04 17:54:11 Alternatively, I think we could do #if defined(_M
herb_g 2015/11/04 21:58:42 Done.
+ SkASSERT(alignof(Variant) <= alignof(SameSizeAndAlignmentAs<Ts...>));
+ #endif
+ new (&fSpace) Variant(skstd::forward<Args>(args)...);
+ }
+
+private:
+ SameSizeAndAlignmentAs<Ts...> fSpace;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// PolymorphicVariant holds supclasses of Base without slicing. Ts must be subclasses of Base.
mtklein 2015/11/04 17:54:12 sup -> sub
herb_g 2015/11/04 21:58:42 Done.
+template <typename Base, typename... Ts>
+class PolymorphicVariant {
+public:
+ typedef UntaggedVariant<Ts...> Variants;
+ template <typename Initializer>
+ PolymorphicVariant(Initializer&& initializer) {
+ skstd::forward<Initializer>(initializer)(&fVariants);
mtklein 2015/11/04 17:54:12 What's the deal with forward here? We don't seem
herb_g 2015/11/04 21:58:42 Forgotten experiment.
+ }
+ ~PolymorphicVariant() { get()->~Base(); }
+ Base* get() const { return (Base*) &fVariants; }
mtklein 2015/11/04 17:54:11 This seems like a spot where reinterpret_cast<> wo
herb_g 2015/11/04 21:58:41 Done.
+ Base* operator->() const { return get(); }
+ Base& operator*() const { return *get(); }
+
+private:
+ Variants fVariants;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// PositionReaderInterface read a point from the pos vector.
mtklein 2015/11/04 17:54:11 reads
herb_g 2015/11/04 21:58:42 Done.
+// * LinearPositions - assumes a common Y for many X values.
+// * ArbitraryPositions - a list of (X,Y) pairs.
+class PositionReaderInterface : SkNoncopyable {
+public:
+ virtual ~PositionReaderInterface() { }
+ virtual const SkPoint NextPoint() = 0;
mtklein 2015/11/04 17:54:11 nextPoint()
herb_g 2015/11/04 21:58:42 Done.
+};
+
+class LinearPositions final : public PositionReaderInterface {
mtklein 2015/11/04 17:54:12 Even, HorizontalPositions.
herb_g 2015/11/04 21:58:43 Done.
+public:
+ LinearPositions(const SkScalar* positions)
mtklein 2015/11/04 17:54:11 It seems surprising we don't pass the shared y her
herb_g 2015/11/04 21:58:42 Right. This is a confusion with the existing code.
+ : fPositions(positions) { }
+
+ const 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) { }
+ : fPositions(positions) { }
mtklein 2015/11/04 17:54:12 Typo?
herb_g 2015/11/04 21:58:43 Done.
+ const SkPoint NextPoint() override {
+ SkPoint to_return {fPositions[0], fPositions[1]};
+ fPositions += 2;
+ return to_return;
+ }
+
+private:
+ const SkScalar* fPositions;
+};
+
+typedef PolymorphicVariant<PositionReaderInterface, LinearPositions, 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;
mtklein 2015/11/04 17:54:11 map
herb_g 2015/11/04 21:58:42 Done.
+};
+
mtklein 2015/11/04 17:54:11 Let's write down a formula for Map explicitly in t
herb_g 2015/11/04 21:58:42 This code faithfully reproduces the existing code.
+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 {SkScalarMul(fXScale, position.fX) + fTranslate.fX, fTranslate.fY};
+ }
+
+private:
+ const SkPoint fTranslate;
+ const SkScalar fXScale;
+};
+
+class GeneralMapper final : public MapperInterface {
+public:
+ GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
mtklein 2015/11/04 17:54:11 // Caller must keep matrix alive.
herb_g 2015/11/04 21:58:42 Done.
+ : 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;
mtklein 2015/11/04 17:54:12 So, under this scheme, we're always going to occup
herb_g 2015/11/04 21:58:42 The maximum this stores is an SkPoint and two poin
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Text alignment handles shifting the glyph based on its width.
+template <SkPaint::Align textAlignment>
mtklein 2015/11/04 17:54:12 Reading this file is a serious cognitive load. Gi
herb_g 2015/11/04 21:58:41 Done.
+static SkPoint text_alignment_adjustment(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.
mtklein 2015/11/04 17:54:12 Might be nice to SkASSERT(false); too.
herb_g 2015/11/04 21:58:42 Done.
+ return {0.0f, 0.0f};
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Axis alignment are handled by NoAlignment, XAxisAlignment, and YAxisAlignment. They
+// handle aligning rounding to whole pixels of either the X axis or the Y axis. The XAlignment
+// and YAlignment functions produce a suitable position for the glyph cache to produce the
+// correct alignment. If a position is aligned with an axis a shortcut of 0 is used for the
+// sub-pixel position.
+
+static const SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubpixelRound);
mtklein 2015/11/04 17:54:12 Doesn't this require a pre-main call?
herb_g 2015/11/04 21:58:42 No. SkFixedToScalar is actually a macro. There are
+
+struct NoAlignment {
+ static SkPoint Rounding() { return {kSubpixelRounding, kSubpixelRounding}; }
mtklein 2015/11/04 17:54:12 rounding, xAlignment, yAlignment
herb_g 2015/11/04 21:58:42 Re-did all of this.
+ static SkFixed XAlignment(SkScalar x) { return SkScalarToFixed(x + kSubpixelRounding); }
+ static SkFixed YAlignment(SkScalar y) { return SkScalarToFixed(y + kSubpixelRounding); }
+};
mtklein 2015/11/04 17:54:11 Again, simplifying, think there's any harm in expr
herb_g 2015/11/04 21:58:43 Done.
+
+struct XAxisAlignment {
+ static SkPoint Rounding() { return {SkFixedToScalar(SkGlyph::kSubpixelRound), SK_ScalarHalf}; }
+ static SkFixed XAlignment(SkScalar x) { return SkScalarToFixed(x + kSubpixelRounding); }
+ static SkFixed YAlignment(SkScalar y) { return 0; }
+};
+
+struct YAxisAlignment {
+ static SkPoint Rounding() { return {SK_ScalarHalf, kSubpixelRounding}; }
+ static SkFixed XAlignment(SkScalar x) { return 0; }
+ static SkFixed YAlignment(SkScalar y) { return SkScalarToFixed(y + kSubpixelRounding); }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 GlyphProcessor>
+class GlyphFindAndPlaceInterface : SkNoncopyable {
+public:
+ struct Result {
+ const SkGlyph* fGlyph;
+ Sk48Dot16 fX;
+ Sk48Dot16 fY;
+ };
+ virtual ~GlyphFindAndPlaceInterface() { };
mtklein 2015/11/04 17:54:11 Mixing templates and virtuals is getting me kind o
herb_g 2015/11/04 21:58:42 Acknowledged.
+ // This should be a pure virtual, but older versions of clang have a bug that causes a
+ // compile error.
mtklein 2015/11/04 17:54:11 Can you tack on Clang <= vX.Y so we know when to t
herb_g 2015/11/04 21:58:42 Done.
+ virtual void FindAndPositionGlyph(const char **text, SkPoint position,
mtklein 2015/11/04 17:54:12 findAndPositionGlyph
herb_g 2015/11/04 21:58:42 Done.
+ GlyphProcessor&& processOneGlyph) {};
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// GlyphFindAndPlaceForSubpixel handles finding and placing glyphs when sub-pixel positioning is
+// requested. After it has found a placed the glyph it calls the templated function
+// GlyphProcessor in order to actually perform an action.
+template <typename GlyphProcessor, SkPaint::Align textAlignment, typename axisAlignment>
+class GlyphFindAndPlaceForSubpixel final : public GlyphFindAndPlaceInterface<GlyphProcessor> {
+public:
+ GlyphFindAndPlaceForSubpixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
+ : fCache(cache)
+ , fGlyphCacheProc(glyphCacheProc) {
+ }
+ void FindAndPositionGlyph(const char** text, SkPoint position,
+ GlyphProcessor&& processOneGlyph) override {
+ SkPoint finalPosition = position;
+ if (textAlignment != 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<textAlignment>(metricGlyph);
+ }
+
+ // Find the glyph.
+ const SkGlyph& renderGlyph = fGlyphCacheProc(
+ fCache, text,
+ axisAlignment::XAlignment(finalPosition.fX),
+ axisAlignment::YAlignment(finalPosition.fY));
+
+ // If the glyph has no width (no pixels) then don't bother processing it.
+ if (renderGlyph.fWidth > 0) {
+ processOneGlyph(renderGlyph, finalPosition, axisAlignment::Rounding());
+ }
+ }
+
+private:
+ SkGlyphCache* const fCache;
+ SkDrawCacheProc fGlyphCacheProc;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// GlyphFindAndPlaceForFullPixel handles finding and placing glyphs when no sub-pixel positioning
+// is requested.
+template <typename GlyphProcessor, SkPaint::Align textAlignment>
+class GlyphFindAndPlaceForFullPixel final : public GlyphFindAndPlaceInterface<GlyphProcessor> {
+public:
+ GlyphFindAndPlaceForFullPixel(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc)
+ : fCache(cache)
+ , fGlyphCacheProc(glyphCacheProc) { }
+ void FindAndPositionGlyph(const char** text, SkPoint position,
+ GlyphProcessor&& processOneGlyph) override {
+ SkPoint finalPosition = position;
+ const SkGlyph& glyph = fGlyphCacheProc(fCache, text, 0, 0);
+ if (glyph.fWidth <= 0) {
+ return;
+ }
+ finalPosition -= text_alignment_adjustment<textAlignment>(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.
mtklein 2015/11/04 17:54:12 What's the code-size impact of all these new class
herb_g 2015/11/04 21:58:42 According too objdump The sub-pixel variants take
+template <typename GlyphProcessor>
+using GlyphFindAndPlace =
+ PolymorphicVariant<
+ GlyphFindAndPlaceInterface<GlyphProcessor>,
+ // Subpixel
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kLeft_Align, NoAlignment >,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kLeft_Align, XAxisAlignment>,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kLeft_Align, YAxisAlignment>,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kCenter_Align, NoAlignment >,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kCenter_Align, XAxisAlignment>,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kCenter_Align, YAxisAlignment>,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kRight_Align, NoAlignment >,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kRight_Align, XAxisAlignment>,
+ GlyphFindAndPlaceForSubpixel< GlyphProcessor, SkPaint::kRight_Align, YAxisAlignment>,
+ // Full pixel
+ GlyphFindAndPlaceForFullPixel<GlyphProcessor, SkPaint::kLeft_Align >,
+ GlyphFindAndPlaceForFullPixel<GlyphProcessor, SkPaint::kCenter_Align>,
+ GlyphFindAndPlaceForFullPixel<GlyphProcessor, SkPaint::kRight_Align >
+ >;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// init_subpixel is a helper function for initializing all the variants of
+// GlyphFindAndPlaceForSubpixel.
+template <typename GlyphProcessor, SkPaint::Align textAlignment>
+static void init_subpixel(
+ typename GlyphFindAndPlace<GlyphProcessor>::Variants* to_init,
+ SkAxisAlignment axisAlignment,
+ SkGlyphCache* cache,
+ SkDrawCacheProc glyphCacheProc) {
+ switch (axisAlignment) {
+ case kX_SkAxisAlignment:
+ to_init->template Init<GlyphFindAndPlaceForSubpixel<
+ GlyphProcessor, textAlignment, XAxisAlignment>>(
+ cache, glyphCacheProc);
+ break;
+ case kNone_SkAxisAlignment:
+ to_init->template Init<GlyphFindAndPlaceForSubpixel<
+ GlyphProcessor, textAlignment, NoAlignment>>(
+ cache, glyphCacheProc);
+ break;
+ case kY_SkAxisAlignment:
+ to_init->template Init<GlyphFindAndPlaceForSubpixel<
+ GlyphProcessor, textAlignment, YAxisAlignment>>(
+ cache, glyphCacheProc);
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// SpecializedProcessPosText is a version of ProcessPosText that de-virtualizes the different
+// components used. This allows greater inlining freedom to the compiler. Currently, there is
+// only one variant that is used: sub-pixel position, left-aligned, x-axis-aligned, translation,
+// and one scalar per position entry.
+template <typename GlyphProcessor>
+static void SpecializedProcessPosText(const char *const text, size_t byteLength,
mtklein 2015/11/04 17:54:11 specialized_process_pos_text
herb_g 2015/11/04 21:58:43 Done.
+ const SkPoint &offset, const SkMatrix &matrix,
+ const SkScalar *const pos, SkDrawCacheProc &glyphCacheProc,
+ SkGlyphCache *cache, GlyphProcessor &&processOneGlyph) {
+ typedef GlyphFindAndPlaceForSubpixel<
+ GlyphProcessor, SkPaint::kLeft_Align, XAxisAlignment> Positioner;
+ LinearPositions 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.LinearPositions::NextPoint());
+ positioner.Positioner::FindAndPositionGlyph(
+ &cursor, mappedPoint, skstd::forward<GlyphProcessor>(processOneGlyph));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// ProcessPosText handles all cases for finding and positioning glyphs. It has a very large
+// multiplicity.
+// * 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 GlyphProcessor>
+static void ProcessPosText(const char text[], size_t byteLength,
+ const SkPoint& offset, const SkMatrix& matrix,
+ const SkScalar pos[], int scalarsPerPosition,
+ SkPaint::Align textAlignment, SkDrawCacheProc& glyphCacheProc,
+ SkGlyphCache* cache, GlyphProcessor&& processOneGlyph) {
+
+ PositionReader positionReader {
mtklein 2015/11/04 17:54:12 I think I'd still prefer these to be written with
herb_g 2015/11/04 21:58:42 Done.
+ [&](PositionReader::Variants* to_init) {
+ if (2 == scalarsPerPosition) {
+ to_init->Init<ArbitraryPositions>(pos);
+ } else {
+ to_init->Init<LinearPositions>(pos);
+ }
+ }
+ };
+
+ Mapper mapper {
+ [&] (Mapper::Variants* to_init) {
+ uint32_t mtype = matrix.getType();
+ if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)
+ || scalarsPerPosition == 2) {
mtklein 2015/11/04 17:54:12 What's scalarsPerPosition have to do with what map
herb_g 2015/11/04 21:58:43 Unfortunately, the original code uses that to dete
+ to_init->Init<GeneralMapper>(matrix, offset);
+ } else if (mtype & SkMatrix::kScale_Mask) {
mtklein 2015/11/04 17:54:12 Is y-scale impossible?
herb_g 2015/11/04 21:58:41 The Skia API does not have a drawVPosText, so it i
+ to_init->Init<XScaleMapper>(matrix, offset);
+ } else {
+ to_init->Init<TranslationMapper>(matrix, offset);
+ }
+ }
+ };
+
+ GlyphFindAndPlace<GlyphProcessor> findAndPosition{
+ [&](typename GlyphFindAndPlace<GlyphProcessor>::Variants* to_init) {
+ if (cache->isSubpixel()) {
+ SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
+ switch (textAlignment) {
+ case SkPaint::kLeft_Align:
+ init_subpixel<GlyphProcessor, SkPaint::kLeft_Align>(
+ to_init, axisAlignment, cache, glyphCacheProc);
+ break;
+ case SkPaint::kCenter_Align:
+ init_subpixel<GlyphProcessor, SkPaint::kCenter_Align>(
+ to_init, axisAlignment, cache, glyphCacheProc);
+ break;
+ case SkPaint::kRight_Align:
+ init_subpixel<GlyphProcessor, SkPaint::kRight_Align>(
+ to_init, axisAlignment, cache, glyphCacheProc);
+ break;
+ }
+ } else {
+ switch (textAlignment) {
+ case SkPaint::kLeft_Align:
+ to_init->template Init<GlyphFindAndPlaceForFullPixel<GlyphProcessor,
+ SkPaint::kLeft_Align>>(
+ cache, glyphCacheProc);
+ break;
+ case SkPaint::kCenter_Align:
+ to_init->template Init<GlyphFindAndPlaceForFullPixel<GlyphProcessor,
+ SkPaint::kCenter_Align>>(
+ cache, glyphCacheProc);
+ break;
+ case SkPaint::kRight_Align:
+ to_init->template Init<GlyphFindAndPlaceForFullPixel<GlyphProcessor,
+ 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<GlyphProcessor>(processOneGlyph));
+ }
+}
+
void SkDraw::drawPosText(const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkPaint& paint) const {
@@ -1760,108 +2231,36 @@ 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);
mtklein 2015/11/04 17:54:12 There's something to be said for this existing cod
herb_g 2015/11/04 21:58:43 Acknowledged.
- 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, &currentText,
- 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;
+ SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(*fMatrix);
+ uint32_t mtype = fMatrix->getType();
+ SkPaint::Align textAlignment = paint.getTextAlign();
+ if (scalarsPerPosition == 1
+ && textAlignment == SkPaint::kLeft_Align
+ && axisAlignment == kX_SkAxisAlignment
+ && cache->isSubpixel()
+ && (mtype
+ & (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask
+ | SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) <=
+ SkMatrix::kTranslate_Mask) {
+ SpecializedProcessPosText(
+ text, byteLength, offset, *fMatrix, pos, glyphCacheProc, cache,
+ [&](const SkGlyph &glyph, SkPoint position, SkPoint rounding) {
+ position += rounding;
+ proc(d1g, SkScalarToFixed(position.fX), SkScalarToFixed(position.fY), glyph);
}
- }
- } 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;
+ );
+ } else {
+ ProcessPosText(
+ text, byteLength, offset, *fMatrix, pos, scalarsPerPosition,
+ textAlignment, glyphCacheProc, cache,
+ [&](const SkGlyph &glyph, SkPoint position, SkPoint rounding) {
+ position += rounding;
+ proc(d1g, SkScalarToFixed(position.fX), SkScalarToFixed(position.fY), glyph);
}
- }
+ );
}
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698