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

Unified Diff: src/core/SkDraw.cpp

Issue 1409123010: Extract glyph find and position code. (Closed) Base URL: https://skia.googlesource.com/skia.git@extract-glyph-position-code
Patch Set: undef the constant 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 | « gyp/core.gypi ('k') | src/core/SkFindAndPlaceGlyph.h » ('j') | 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 e83bdb74b9ad5cd4f73740fcdee861997ee9fbd0..0b650bea441fdc25fa87a78f190232fb4a9d97bc 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -12,6 +12,7 @@
#include "SkColorPriv.h"
#include "SkDevice.h"
#include "SkDeviceLooper.h"
+#include "SkFindAndPlaceGlyph.h"
#include "SkFixed.h"
#include "SkMaskFilter.h"
#include "SkMatrix.h"
@@ -1729,491 +1730,6 @@ 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 {
-public:
- virtual ~PositionReaderInterface() { }
- virtual SkPoint nextPoint() = 0;
-};
-
-class HorizontalPositions final : public PositionReaderInterface {
-public:
- explicit 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:
- explicit 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 {
-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 {
@@ -2258,9 +1774,11 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
};
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,
+ if (!SkFindAndPlaceGlyph::SpecializedProcessPosText(
+ text, byteLength, offset, *fMatrix, pos, scalarsPerPosition,
+ textAlignment, glyphCacheProc, cache, processOneGlyph)) {
+ SkFindAndPlaceGlyph::ProcessPosText(
+ text, byteLength, offset, *fMatrix, pos, scalarsPerPosition,
textAlignment, glyphCacheProc, cache, processOneGlyph);
}
}
« no previous file with comments | « gyp/core.gypi ('k') | src/core/SkFindAndPlaceGlyph.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698