1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/* 2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2015 Google Inc. 3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * 4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be 5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file. 6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */ 7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#ifndef SkFindAndPositionGlyph_DEFINED 9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#define SkFindAndPositionGlyph_DEFINED 10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkArenaAlloc.h" 12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkAutoKern.h" 13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGlyph.h" 14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGlyphCache.h" 15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkMatrixPriv.h" 16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPaint.h" 17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTemplates.h" 18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkUtils.h" 19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include <utility> 20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass SkFindAndPlaceGlyph { 22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic: 23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot template<typename ProcessOneGlyph> 24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static void ProcessText( 25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::TextEncoding, const char text[], size_t byteLength, 26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment, 27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph); 28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // ProcessPosText handles all cases for finding and positioning glyphs. It has a very large 29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // multiplicity. It figures out the glyph, position and rounding and pass those parameters to 30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // processOneGlyph. 31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // 32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // The routine processOneGlyph passed in by the client has the following signature: 33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding); 34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // 35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * Sub-pixel positioning (2) - use sub-pixel positioning. 36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * Text alignment (3) - text alignment with respect to the glyph's width. 37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * Matrix type (3) - special cases for translation and X-coordinate scaling. 38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * Components per position (2) - the positions vector can have a common Y with different 39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Xs, or XY-pairs. 40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel positioning, round 41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // to a whole coordinate instead of using sub-pixel positioning. 42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // The number of variations is 108 for sub-pixel and 36 for full-pixel. 43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // This routine handles all of them using inline polymorphic variable (no heap allocation). 44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot template<typename ProcessOneGlyph> 45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static void ProcessPosText( 46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::TextEncoding, const char text[], size_t byteLength, 47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition, 48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::Align textAlignment, 49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph); 50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate: 52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // GlyphFinderInterface is the polymorphic base for classes that parse a stream of chars into 53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // the right UniChar (or GlyphID) and lookup up the glyph on the cache. The concrete 54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // implementations are: Utf8GlyphFinder, Utf16GlyphFinder, Utf32GlyphFinder, 55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // and GlyphIdGlyphFinder. 56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class GlyphFinderInterface { 57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual ~GlyphFinderInterface() {} 59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual const SkGlyph& lookupGlyph(const char** text) = 0; 60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) = 0; 61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class UtfNGlyphFinder : public GlyphFinderInterface { 64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit UtfNGlyphFinder(SkGlyphCache* cache) 66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fCache(cache) { 67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkASSERT(cache != nullptr); 68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph& lookupGlyph(const char** text) override { 71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkASSERT(text != nullptr); 72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return fCache->getUnicharMetrics(nextUnichar(text)); 73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override { 75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkASSERT(text != nullptr); 76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return fCache->getUnicharMetrics(nextUnichar(text), x, y); 77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual SkUnichar nextUnichar(const char** text) = 0; 81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkGlyphCache* fCache; 82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class Utf8GlyphFinder final : public UtfNGlyphFinder { 85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { } 87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); } 90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class Utf16GlyphFinder final : public UtfNGlyphFinder { 93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { } 95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkUnichar nextUnichar(const char** text) override { 98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return SkUTF16_NextUnichar((const uint16_t**)text); 99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class Utf32GlyphFinder final : public UtfNGlyphFinder { 103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { } 105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkUnichar nextUnichar(const char** text) override { 108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const int32_t* ptr = *(const int32_t**)text; 109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkUnichar uni = *ptr++; 110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *text = (const char*)ptr; 111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return uni; 112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class GlyphIdGlyphFinder final : public GlyphFinderInterface { 116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit GlyphIdGlyphFinder(SkGlyphCache* cache) 118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fCache(cache) { 119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkASSERT(cache != nullptr); 120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph& lookupGlyph(const char** text) override { 123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return fCache->getGlyphIDMetrics(nextGlyphId(text)); 124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override { 126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y); 127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot uint16_t nextGlyphId(const char** text) { 131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkASSERT(text != nullptr); 132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const uint16_t* ptr = *(const uint16_t**)text; 134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot uint16_t glyphID = *ptr; 135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ptr += 1; 136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *text = (const char*)ptr; 137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return glyphID; 138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkGlyphCache* fCache; 140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static GlyphFinderInterface* getGlyphFinder( 143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkArenaAlloc* arena, SkPaint::TextEncoding encoding, SkGlyphCache* cache) { 144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot switch(encoding) { 145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kUTF8_TextEncoding: 146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return arena->make<Utf8GlyphFinder>(cache); 147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kUTF16_TextEncoding: 148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return arena->make<Utf16GlyphFinder>(cache); 149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kUTF32_TextEncoding: 150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return arena->make<Utf32GlyphFinder>(cache); 151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kGlyphID_TextEncoding: 152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return arena->make<GlyphIdGlyphFinder>(cache); 153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_ABORT("Should not get here."); 155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return nullptr; 156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // PositionReaderInterface reads a point from the pos vector. 159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * HorizontalPositions - assumes a common Y for many X values. 160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * ArbitraryPositions - a list of (X,Y) pairs. 161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class PositionReaderInterface { 162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual ~PositionReaderInterface() { } 164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual SkPoint nextPoint() = 0; 165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class HorizontalPositions final : public PositionReaderInterface { 168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit HorizontalPositions(const SkScalar* positions) 170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fPositions(positions) { } 171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint nextPoint() override { 173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkScalar x = *fPositions++; 174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {x, 0}; 175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkScalar* fPositions; 179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class ArbitraryPositions final : public PositionReaderInterface { 182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit ArbitraryPositions(const SkScalar* positions) 184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fPositions(positions) { } 185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint nextPoint() override { 187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint to_return{fPositions[0], fPositions[1]}; 188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fPositions += 2; 189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return to_return; 190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkScalar* fPositions; 194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // MapperInterface given a point map it through the matrix. There are several shortcut 197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // variants. 198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * TranslationMapper - assumes a translation only matrix. 199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * XScaleMapper - assumes an X scaling and a translation. 200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // * GeneralMapper - Does all other matricies. 201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class MapperInterface { 202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual ~MapperInterface() { } 204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual SkPoint map(SkPoint position) const = 0; 206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class TranslationMapper final : public MapperInterface { 209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot TranslationMapper(const SkMatrix& matrix, const SkPoint origin) 211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { } 212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint map(SkPoint position) const override { 214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return position + fTranslate; 215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkPoint fTranslate; 219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class XScaleMapper final : public MapperInterface { 222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot XScaleMapper(const SkMatrix& matrix, const SkPoint origin) 224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { } 225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint map(SkPoint position) const override { 227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {fXScale * position.fX + fTranslate.fX, fTranslate.fY}; 228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkPoint fTranslate; 232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkScalar fXScale; 233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // The caller must keep matrix alive while this class is used. 236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class GeneralMapper final : public MapperInterface { 237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GeneralMapper(const SkMatrix& matrix, const SkPoint origin) 239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fOrigin(origin), fMatrix(matrix), fMapProc(SkMatrixPriv::GetMapXYProc(matrix)) { } 240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint map(SkPoint position) const override { 242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint result; 243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY, &result); 244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return result; 245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkPoint fOrigin; 249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkMatrix& fMatrix; 250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkMatrixPriv::MapXYProc fMapProc; 251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // TextAlignmentAdjustment handles shifting the glyph based on its width. 254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) { 255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot switch (textAlignment) { 256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kLeft_Align: 257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {0.0f, 0.0f}; 258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kCenter_Align: 259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {SkFloatToScalar(glyph.fAdvanceX) / 2, 260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkFloatToScalar(glyph.fAdvanceY) / 2}; 261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kRight_Align: 262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {SkFloatToScalar(glyph.fAdvanceX), 263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkFloatToScalar(glyph.fAdvanceY)}; 264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy. 266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_ABORT("Should never get here."); 267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {0.0f, 0.0f}; 268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // The "call" to SkFixedToScalar is actually a macro. It's macros all the way down. 271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Needs to be a macro because you can't have a const float unless you make it constexpr. 272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot #define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound)) 273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel 275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // positioned glyph. 276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) { 277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot switch (axisAlignment) { 278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kX_SkAxisAlignment: 279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {kSubpixelRounding, SK_ScalarHalf}; 280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kY_SkAxisAlignment: 281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {SK_ScalarHalf, kSubpixelRounding}; 282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kNone_SkAxisAlignment: 283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {kSubpixelRounding, kSubpixelRounding}; 284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_ABORT("Should not get here."); 286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {0.0f, 0.0f}; 287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // The SubpixelAlignment function produces a suitable position for the glyph cache to 290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut 291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // of 0 is used for the sub-pixel position. 292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) { 293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Only the fractional part of position.fX and position.fY matter, because the result of 294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // this function will just be passed to FixedToSub. 295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot switch (axisAlignment) { 296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kX_SkAxisAlignment: 297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), 0}; 298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kY_SkAxisAlignment: 299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {0, SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)}; 300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kNone_SkAxisAlignment: 301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), 302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)}; 303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_ABORT("Should not get here."); 305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {0, 0}; 306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot #undef kSubpixelRounding 309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does 311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // glyph specific position adjustment. The findAndPositionGlyph method takes text and 312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // position and calls processOneGlyph with the correct glyph, final position and rounding 313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // terms. The final position is not rounded yet and is the responsibility of processOneGlyph. 314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot template<typename ProcessOneGlyph> 315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class GlyphFindAndPlaceInterface : SkNoncopyable { 316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual ~GlyphFindAndPlaceInterface() { } 318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and 320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // returns the position of where the next glyph will be using the glyph's advance and 321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // possibly kerning. The returned position is used by drawText, but ignored by drawPosText. 322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // The compiler should prune all this calculation if the return value is not used. 323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // 324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a 325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // compile error. 326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277 327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual SkPoint findAndPositionGlyph( 328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) { 329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_ABORT("Should never get here."); 330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {0.0f, 0.0f}; 331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is 335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // requested. After it has found and placed the glyph it calls the templated function 336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // ProcessOneGlyph in order to actually perform an action. 337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, 338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkAxisAlignment kAxisAlignment> 339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { 340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit GlyphFindAndPlaceSubpixel(GlyphFinderInterface* glyphFinder) 342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fGlyphFinder(glyphFinder) { } 343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint findAndPositionGlyph( 345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { 346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (kTextAlignment != SkPaint::kLeft_Align) { 348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Get the width of an un-sub-pixel positioned glyph for calculating the 349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // alignment. This is not needed for kLeftAlign because its adjustment is 350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // always {0, 0}. 351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* tempText = *text; 352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph &metricGlyph = fGlyphFinder->lookupGlyph(&tempText); 353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (metricGlyph.fWidth <= 0) { 355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Exiting early, be sure to update text pointer. 356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *text = tempText; 357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return position + SkPoint{SkFloatToScalar(metricGlyph.fAdvanceX), 358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkFloatToScalar(metricGlyph.fAdvanceY)}; 359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Adjust the final position by the alignment adjustment. 362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot position -= TextAlignmentAdjustment(kTextAlignment, metricGlyph); 363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Find the glyph. 366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkIPoint lookupPosition = SkScalarsAreFinite(position.fX, position.fY) 367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ? SubpixelAlignment(kAxisAlignment, position) 368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : SkIPoint{0, 0}; 369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph& renderGlyph = 370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fGlyphFinder->lookupGlyphXY(text, lookupPosition.fX, lookupPosition.fY); 371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // If the glyph has no width (no pixels) then don't bother processing it. 373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (renderGlyph.fWidth > 0) { 374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot processOneGlyph(renderGlyph, position, 375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SubpixelPositionRounding(kAxisAlignment)); 376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return position + SkPoint{SkFloatToScalar(renderGlyph.fAdvanceX), 378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkFloatToScalar(renderGlyph.fAdvanceY)}; 379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFinderInterface* fGlyphFinder; 383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot enum SelectKerning { 386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot kNoKerning = false, 387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot kUseKerning = true 388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel 391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // positioning is requested. The kUseKerning argument should be true for drawText, and false 392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // for drawPosText. 393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning> 394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> { 395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot public: 396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot explicit GlyphFindAndPlaceFullPixel(GlyphFinderInterface* glyphFinder) 397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : fGlyphFinder(glyphFinder) { 398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Kerning can only be used with SkPaint::kLeft_Align 399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment, 400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "Kerning can only be used with left aligned text."); 401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint findAndPositionGlyph( 404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override { 405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint finalPosition = position; 406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text); 407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (kUseKerning) { 408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot finalPosition += {fAutoKern.adjust(glyph), 0.0f}; 409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (glyph.fWidth > 0) { 411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph); 412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf}); 413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return finalPosition + SkPoint{SkFloatToScalar(glyph.fAdvanceX), 415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkFloatToScalar(glyph.fAdvanceY)}; 416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot private: 419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFinderInterface* fGlyphFinder; 420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkAutoKern fAutoKern; 422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot template <typename ProcessOneGlyph, SkPaint::Align kTextAlignment> 425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static GlyphFindAndPlaceInterface<ProcessOneGlyph>* getSubpixel( 426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkArenaAlloc* arena, SkAxisAlignment axisAlignment, GlyphFinderInterface* glyphFinder) 427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot { 428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot switch (axisAlignment) { 429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kX_SkAxisAlignment: 430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return arena->make<GlyphFindAndPlaceSubpixel< 431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(glyphFinder); 432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kNone_SkAxisAlignment: 433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return arena->make<GlyphFindAndPlaceSubpixel< 434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(glyphFinder); 435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case kY_SkAxisAlignment: 436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return arena->make<GlyphFindAndPlaceSubpixel< 437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(glyphFinder); 438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_ABORT("Should never get here."); 440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return nullptr; 441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static SkPoint MeasureText( 444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFinderInterface* glyphFinder, const char text[], size_t byteLength) { 445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkScalar x = 0, y = 0; 446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* stop = text + byteLength; 447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkAutoKern autokern; 449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot while (text < stop) { 451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // don't need x, y here, since all subpixel variants will have the 452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // same advance 453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkGlyph& glyph = glyphFinder->lookupGlyph(&text); 454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x += autokern.adjust(glyph) + SkFloatToScalar(glyph.fAdvanceX); 456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot y += SkFloatToScalar(glyph.fAdvanceY); 457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkASSERT(text == stop); 459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return {x, y}; 460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}; 462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robottemplate<typename ProcessOneGlyph> 464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotinline void SkFindAndPlaceGlyph::ProcessPosText( 465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength, 466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition, 467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::Align textAlignment, 468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { 469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); 471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot uint32_t mtype = matrix.getType(); 472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Specialized code for handling the most common case for blink. 474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (textEncoding == SkPaint::kGlyphID_TextEncoding 475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot && textAlignment == SkPaint::kLeft_Align 476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot && axisAlignment == kX_SkAxisAlignment 477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot && cache->isSubpixel() 478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot && mtype <= SkMatrix::kTranslate_Mask) 479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot { 480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphIdGlyphFinder glyphFinder(cache); 481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot using Positioner = 482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFindAndPlaceSubpixel < 483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment>; 484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot HorizontalPositions hPositions{pos}; 485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ArbitraryPositions aPositions{pos}; 486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot PositionReaderInterface* positions = nullptr; 487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (scalarsPerPosition == 2) { 488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot positions = &aPositions; 489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } else { 490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot positions = &hPositions; 491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot TranslationMapper mapper{matrix, offset}; 493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot Positioner positioner(&glyphFinder); 494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* cursor = text; 495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* stop = text + byteLength; 496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot while (cursor < stop) { 497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint mappedPoint = mapper.TranslationMapper::map(positions->nextPoint()); 498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot positioner.Positioner::findAndPositionGlyph( 499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &cursor, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph)); 500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return; 502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkSTArenaAlloc<120> arena; 505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache); 507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot PositionReaderInterface* positionReader = nullptr; 509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (2 == scalarsPerPosition) { 510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot positionReader = arena.make<ArbitraryPositions>(pos); 511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } else { 512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot positionReader = arena.make<HorizontalPositions>(pos); 513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot MapperInterface* mapper = nullptr; 516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask) 517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot || scalarsPerPosition == 2) { 518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot mapper = arena.make<GeneralMapper>(matrix, offset); 519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } else if (mtype & SkMatrix::kScale_Mask) { 520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot mapper = arena.make<XScaleMapper>(matrix, offset); 521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } else { 522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot mapper = arena.make<TranslationMapper>(matrix, offset); 523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr; 526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (cache->isSubpixel()) { 527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot switch (textAlignment) { 528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kLeft_Align: 529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>( 530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &arena, axisAlignment, glyphFinder); 531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break; 532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kCenter_Align: 533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>( 534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &arena, axisAlignment, glyphFinder); 535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break; 536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kRight_Align: 537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>( 538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &arena, axisAlignment, glyphFinder); 539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break; 540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } else { 542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot switch (textAlignment) { 543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kLeft_Align: 544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = arena.make< 545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::kLeft_Align, kNoKerning>>(glyphFinder); 547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break; 548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kCenter_Align: 549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = arena.make< 550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::kCenter_Align, kNoKerning>>(glyphFinder); 552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break; 553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot case SkPaint::kRight_Align: 554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = arena.make< 555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFindAndPlaceFullPixel<ProcessOneGlyph, 556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::kRight_Align, kNoKerning>>(glyphFinder); 557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break; 558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* stop = text + byteLength; 562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot while (text < stop) { 563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint mappedPoint = mapper->map(positionReader->nextPoint()); 564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition->findAndPositionGlyph( 565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &text, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph)); 566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robottemplate<typename ProcessOneGlyph> 570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotinline void SkFindAndPlaceGlyph::ProcessText( 571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength, 572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment, 573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { 574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkSTArenaAlloc<64> arena; 575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // transform the starting point 577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix.mapPoints(&offset, 1); 578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache); 580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // need to measure first 582fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (textAlignment != SkPaint::kLeft_Align) { 583fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkVector stop = MeasureText(glyphFinder, text, byteLength); 584fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 585fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (textAlignment == SkPaint::kCenter_Align) { 586fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot stop *= SK_ScalarHalf; 587fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 588fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot offset -= stop; 589fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 590fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 591fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr; 592fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (cache->isSubpixel()) { 593fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); 594fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>( 595fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &arena, axisAlignment, glyphFinder); 596fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } else { 597fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot using FullPixel = 598fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>; 599fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition = arena.make<FullPixel>(glyphFinder); 600fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 601fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 602fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* stop = text + byteLength; 603fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkPoint current = offset; 604fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot while (text < stop) { 605fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot current = 606fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot findAndPosition->findAndPositionGlyph( 607fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &text, current, std::forward<ProcessOneGlyph>(processOneGlyph)); 608fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 609fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 610fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 611fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 612fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif // SkFindAndPositionGlyph_DEFINED 613