1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef GrAtlasTextContext_DEFINED
9#define GrAtlasTextContext_DEFINED
10
11#include "GrTextContext.h"
12
13#include "GrBatchAtlas.h"
14#include "GrBatchFontCache.h"
15#include "GrGeometryProcessor.h"
16#include "SkDescriptor.h"
17#include "GrMemoryPool.h"
18#include "SkMaskFilter.h"
19#include "SkTextBlob.h"
20#include "SkTInternalLList.h"
21
22#ifdef GR_TEST_UTILS
23#include "GrBatchTest.h"
24#endif
25
26class BitmapTextBatch;
27class GrPipelineBuilder;
28class GrTextBlobCache;
29
30/*
31 * This class implements GrTextContext using standard bitmap fonts, and can also process textblobs.
32 * TODO replace GrBitmapTextContext
33 */
34class GrAtlasTextContext : public GrTextContext {
35public:
36    static GrAtlasTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&,
37                                      bool enableDistanceFields);
38
39private:
40    GrAtlasTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&,
41                       bool enableDistanceFields);
42    ~GrAtlasTextContext() override {}
43
44    bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
45                 const SkPaint&, const SkMatrix& viewMatrix) override;
46
47    void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
48                    const SkMatrix& viewMatrix, const char text[], size_t byteLength,
49                    SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override;
50    void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
51                       const SkMatrix& viewMatrix,
52                       const char text[], size_t byteLength,
53                       const SkScalar pos[], int scalarsPerPosition,
54                       const SkPoint& offset, const SkIRect& regionClipBounds) override;
55    void drawTextBlob(GrRenderTarget*, const GrClip&, const SkPaint&,
56                      const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y,
57                      SkDrawFilter*, const SkIRect& clipBounds) override;
58
59    /*
60     * A BitmapTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
61     * on the GPU.  These are initially created with valid positions and colors, but invalid
62     * texture coordinates.  The BitmapTextBlob itself has a few Blob-wide properties, and also
63     * consists of a number of runs.  Runs inside a blob are flushed individually so they can be
64     * reordered.
65     *
66     * The only thing(aside from a memcopy) required to flush a BitmapTextBlob is to ensure that
67     * the GrAtlas will not evict anything the Blob needs.
68     */
69    struct BitmapTextBlob : public SkRefCnt {
70        SK_DECLARE_INTERNAL_LLIST_INTERFACE(BitmapTextBlob);
71
72        /*
73         * Each Run inside of the blob can have its texture coordinates regenerated if required.
74         * To determine if regeneration is necessary, fAtlasGeneration is used.  If there have been
75         * any evictions inside of the atlas, then we will simply regenerate Runs.  We could track
76         * this at a more fine grained level, but its not clear if this is worth it, as evictions
77         * should be fairly rare.
78         *
79         * One additional point, each run can contain glyphs with any of the three mask formats.
80         * We call these SubRuns.  Because a subrun must be a contiguous range, we have to create
81         * a new subrun each time the mask format changes in a run.  In theory, a run can have as
82         * many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8.  In
83         * practice, the vast majority of runs have only a single subrun.
84         *
85         * Finally, for runs where the entire thing is too large for the GrAtlasTextContext to
86         * handle, we have a bit to mark the run as flusahable via rendering as paths.  It is worth
87         * pointing. It would be a bit expensive to figure out ahead of time whether or not a run
88         * can flush in this manner, so we always allocate vertices for the run, regardless of
89         * whether or not it is too large.  The benefit of this strategy is that we can always reuse
90         * a blob allocation regardless of viewmatrix changes.  We could store positions for these
91         * glyphs.  However, its not clear if this is a win because we'd still have to either go the
92         * glyph cache to get the path at flush time, or hold onto the path in the cache, which
93         * would greatly increase the memory of these cached items.
94         */
95        struct Run {
96            Run()
97                : fColor(GrColor_ILLEGAL)
98                , fInitialized(false)
99                , fDrawAsPaths(false) {
100                fVertexBounds.setLargestInverted();
101                // To ensure we always have one subrun, we push back a fresh run here
102                fSubRunInfo.push_back();
103            }
104            struct SubRunInfo {
105                SubRunInfo()
106                    : fAtlasGeneration(GrBatchAtlas::kInvalidAtlasGeneration)
107                    , fVertexStartIndex(0)
108                    , fVertexEndIndex(0)
109                    , fGlyphStartIndex(0)
110                    , fGlyphEndIndex(0)
111                    , fDrawAsDistanceFields(false) {}
112                // Distance field text cannot draw coloremoji, and so has to fall back.  However,
113                // though the distance field text and the coloremoji may share the same run, they
114                // will have different descriptors.  If fOverrideDescriptor is non-NULL, then it
115                // will be used in place of the run's descriptor to regen texture coords
116                // TODO we could have a descriptor cache, it would reduce the size of these blobs
117                // significantly, and then the subrun could just have a refed pointer to the
118                // correct descriptor.
119                GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken;
120                uint64_t fAtlasGeneration;
121                size_t fVertexStartIndex;
122                size_t fVertexEndIndex;
123                uint32_t fGlyphStartIndex;
124                uint32_t fGlyphEndIndex;
125                SkScalar fTextRatio; // df property
126                GrMaskFormat fMaskFormat;
127                bool fDrawAsDistanceFields; // df property
128                bool fUseLCDText; // df property
129            };
130
131            SubRunInfo& push_back() {
132                // Forward glyph / vertex information to seed the new sub run
133                SubRunInfo& prevSubRun = fSubRunInfo.back();
134                SubRunInfo& newSubRun = fSubRunInfo.push_back();
135                newSubRun.fGlyphStartIndex = prevSubRun.fGlyphEndIndex;
136                newSubRun.fGlyphEndIndex = prevSubRun.fGlyphEndIndex;
137
138                newSubRun.fVertexStartIndex = prevSubRun.fVertexEndIndex;
139                newSubRun.fVertexEndIndex = prevSubRun.fVertexEndIndex;
140                return newSubRun;
141            }
142            static const int kMinSubRuns = 1;
143            SkAutoTUnref<GrBatchTextStrike> fStrike;
144            SkAutoTUnref<SkTypeface> fTypeface;
145            SkRect fVertexBounds;
146            SkSTArray<kMinSubRuns, SubRunInfo> fSubRunInfo;
147            SkAutoDescriptor fDescriptor;
148            SkAutoTDelete<SkAutoDescriptor> fOverrideDescriptor; // df properties
149            GrColor fColor;
150            bool fInitialized;
151            bool fDrawAsPaths;
152        };
153
154        struct BigGlyph {
155            BigGlyph(const SkPath& path, SkScalar vx, SkScalar vy)
156                : fPath(path)
157                , fVx(vx)
158                , fVy(vy) {}
159            SkPath fPath;
160            SkScalar fVx;
161            SkScalar fVy;
162        };
163
164        struct Key {
165            Key() {
166                sk_bzero(this, sizeof(Key));
167            }
168            uint32_t fUniqueID;
169            // Color may affect the gamma of the mask we generate, but in a fairly limited way.
170            // Each color is assigned to on of a fixed number of buckets based on its
171            // luminance. For each luminance bucket there is a "canonical color" that
172            // represents the bucket.  This functionality is currently only supported for A8
173            SkColor fCanonicalColor;
174            SkPaint::Style fStyle;
175            SkPixelGeometry fPixelGeometry;
176            bool fHasBlur;
177
178            bool operator==(const Key& other) const {
179                return 0 == memcmp(this, &other, sizeof(Key));
180            }
181        };
182
183        struct StrokeInfo {
184            SkScalar fFrameWidth;
185            SkScalar fMiterLimit;
186            SkPaint::Join fJoin;
187        };
188
189        enum TextType {
190            kHasDistanceField_TextType = 0x1,
191            kHasBitmap_TextType = 0x2,
192        };
193
194        // all glyph / vertex offsets are into these pools.
195        unsigned char* fVertices;
196        GrGlyph** fGlyphs;
197        Run* fRuns;
198        GrMemoryPool* fPool;
199        SkMaskFilter::BlurRec fBlurRec;
200        StrokeInfo fStrokeInfo;
201        SkTArray<BigGlyph> fBigGlyphs;
202        Key fKey;
203        SkMatrix fViewMatrix;
204        SkColor fPaintColor;
205        SkScalar fX;
206        SkScalar fY;
207
208        // We can reuse distance field text, but only if the new viewmatrix would not result in
209        // a mip change.  Because there can be multiple runs in a blob, we track the overall
210        // maximum minimum scale, and minimum maximum scale, we can support before we need to regen
211        SkScalar fMaxMinScale;
212        SkScalar fMinMaxScale;
213        int fRunCount;
214        uint8_t fTextType;
215
216        BitmapTextBlob()
217            : fMaxMinScale(-SK_ScalarMax)
218            , fMinMaxScale(SK_ScalarMax)
219            , fTextType(0) {}
220
221        ~BitmapTextBlob() override {
222            for (int i = 0; i < fRunCount; i++) {
223                fRuns[i].~Run();
224            }
225        }
226
227        static const Key& GetKey(const BitmapTextBlob& blob) {
228            return blob.fKey;
229        }
230
231        static uint32_t Hash(const Key& key) {
232            return SkChecksum::Murmur3(&key, sizeof(Key));
233        }
234
235        void operator delete(void* p) {
236            BitmapTextBlob* blob = reinterpret_cast<BitmapTextBlob*>(p);
237            blob->fPool->release(p);
238        }
239        void* operator new(size_t) {
240            SkFAIL("All blobs are created by placement new.");
241            return sk_malloc_throw(0);
242        }
243
244        void* operator new(size_t, void* p) { return p; }
245        void operator delete(void* target, void* placement) {
246            ::operator delete(target, placement);
247        }
248
249        bool hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); }
250        bool hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
251        void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
252        void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
253    };
254
255    typedef BitmapTextBlob::Run Run;
256    typedef Run::SubRunInfo PerSubRunInfo;
257
258    inline bool canDrawAsDistanceFields(const SkPaint&, const SkMatrix& viewMatrix);
259    BitmapTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint,
260                                const SkMatrix& viewMatrix, SkGlyphCache** cache,
261                                SkPaint* dfPaint, SkScalar* textRatio);
262    void bmpAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
263                        GrColor color, GrFontScaler*, const SkIRect& clipRect);
264    bool dfAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, SkScalar sx, SkScalar sy,
265                       GrColor color, GrFontScaler*, const SkIRect& clipRect, SkScalar textRatio,
266                       const SkMatrix& viewMatrix);
267    inline void appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
268                                GrFontScaler* scaler, SkScalar x, SkScalar y);
269    inline void appendGlyphCommon(BitmapTextBlob*, Run*, Run::SubRunInfo*,
270                                  const SkRect& positions, GrColor color,
271                                  size_t vertexStride, bool useVertexColor,
272                                  GrGlyph*);
273
274    inline void flushRunAsPaths(const SkTextBlob::RunIterator&, const SkPaint&, SkDrawFilter*,
275                                const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x,
276                                SkScalar y);
277    inline BitmapTextBatch* createBatch(BitmapTextBlob*, const PerSubRunInfo&,
278                                        int glyphCount, int run, int subRun,
279                                        GrColor, SkScalar transX, SkScalar transY,
280                                        const SkPaint&);
281    inline void flushRun(GrDrawTarget*, GrPipelineBuilder*, BitmapTextBlob*, int run, GrColor,
282                         SkScalar transX, SkScalar transY, const SkPaint&);
283    inline void flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
284                               const SkPaint& skPaint,
285                               SkScalar transX, SkScalar transY, const SkIRect& clipBounds);
286
287    // We have to flush SkTextBlobs differently from drawText / drawPosText
288    void flush(GrDrawTarget*, const SkTextBlob*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&,
289               const GrPaint&, SkDrawFilter*, const GrClip&, const SkMatrix& viewMatrix,
290               const SkIRect& clipBounds, SkScalar x, SkScalar y, SkScalar transX, SkScalar transY);
291    void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&,
292               const GrPaint&, const GrClip&, const SkIRect& clipBounds);
293
294    // A helper for drawing BitmapText in a run of distance fields
295    inline void fallbackDrawPosText(BitmapTextBlob*, int runIndex,
296                                    GrRenderTarget*, const GrClip&,
297                                    const GrPaint&,
298                                    const SkPaint&, const SkMatrix& viewMatrix,
299                                    const SkTDArray<char>& fallbackTxt,
300                                    const SkTDArray<SkScalar>& fallbackPos,
301                                    int scalarsPerPosition,
302                                    const SkPoint& offset,
303                                    const SkIRect& clipRect);
304
305    void internalDrawBMPText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
306                             GrColor color, const SkMatrix& viewMatrix,
307                             const char text[], size_t byteLength,
308                             SkScalar x, SkScalar y, const SkIRect& clipRect);
309    void internalDrawBMPPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
310                                GrColor color, const SkMatrix& viewMatrix,
311                                const char text[], size_t byteLength,
312                                const SkScalar pos[], int scalarsPerPosition,
313                                const SkPoint& offset, const SkIRect& clipRect);
314
315    void internalDrawDFText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
316                            GrColor color, const SkMatrix& viewMatrix,
317                            const char text[], size_t byteLength,
318                            SkScalar x, SkScalar y, const SkIRect& clipRect,
319                            SkScalar textRatio,
320                            SkTDArray<char>* fallbackTxt,
321                            SkTDArray<SkScalar>* fallbackPos,
322                            SkPoint* offset, const SkPaint& origPaint);
323    void internalDrawDFPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
324                               GrColor color, const SkMatrix& viewMatrix,
325                               const char text[], size_t byteLength,
326                               const SkScalar pos[], int scalarsPerPosition,
327                               const SkPoint& offset, const SkIRect& clipRect,
328                               SkScalar textRatio,
329                               SkTDArray<char>* fallbackTxt,
330                               SkTDArray<SkScalar>* fallbackPos);
331
332    // sets up the descriptor on the blob and returns a detached cache.  Client must attach
333    inline static GrColor ComputeCanonicalColor(const SkPaint&, bool lcd);
334    inline SkGlyphCache* setupCache(Run*, const SkPaint&, const SkMatrix* viewMatrix, bool noGamma);
335    static inline bool MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
336                                          const BitmapTextBlob&, const SkPaint&,
337                                          const SkMaskFilter::BlurRec&,
338                                          const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
339    void regenerateTextBlob(BitmapTextBlob* bmp, const SkPaint& skPaint, GrColor,
340                            const SkMatrix& viewMatrix,
341                            const SkTextBlob* blob, SkScalar x, SkScalar y,
342                            SkDrawFilter* drawFilter, const SkIRect& clipRect, GrRenderTarget*,
343                            const GrClip&, const GrPaint&);
344    inline static bool HasLCD(const SkTextBlob*);
345    inline void initDistanceFieldPaint(BitmapTextBlob*, SkPaint*, SkScalar* textRatio,
346                                       const SkMatrix&);
347
348    // Test methods
349    // TODO this is really ugly.  It'd be much nicer if positioning could be moved to batch
350    inline BitmapTextBlob* createDrawTextBlob(GrRenderTarget*, const GrClip&, const GrPaint&,
351                                              const SkPaint&, const SkMatrix& viewMatrix,
352                                              const char text[], size_t byteLength,
353                                              SkScalar x, SkScalar y,
354                                              const SkIRect& regionClipBounds);
355    inline BitmapTextBlob* createDrawPosTextBlob(GrRenderTarget*, const GrClip&, const GrPaint&,
356                                                 const SkPaint&, const SkMatrix& viewMatrix,
357                                                 const char text[], size_t byteLength,
358                                                 const SkScalar pos[], int scalarsPerPosition,
359                                                 const SkPoint& offset,
360                                                 const SkIRect& regionClipBounds);
361
362    // Distance field text needs this table to compute a value for use in the fragment shader.
363    // Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be
364    // refcnted and malloced
365    struct DistanceAdjustTable : public SkNVRefCnt<DistanceAdjustTable> {
366        DistanceAdjustTable(float gamma) { this->buildDistanceAdjustTable(gamma); }
367        ~DistanceAdjustTable() { SkDELETE_ARRAY(fTable); }
368
369        void buildDistanceAdjustTable(float gamma);
370
371        SkScalar& operator[] (int i) {
372            return fTable[i];
373        }
374
375        const SkScalar& operator[] (int i) const {
376            return fTable[i];
377        }
378
379        SkScalar* fTable;
380    };
381
382    GrBatchTextStrike* fCurrStrike;
383    GrTextBlobCache* fCache;
384    bool fEnableDFRendering;
385    SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
386
387    friend class GrTextBlobCache;
388    friend class BitmapTextBatch;
389
390#ifdef GR_TEST_UTILS
391    BATCH_TEST_FRIEND(TextBlobBatch);
392#endif
393
394    typedef GrTextContext INHERITED;
395};
396
397#endif
398