1/*
2 * Copyright 2016 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#include "GrAtlasTextBlob.h"
9
10#include "GrOpFlushState.h"
11#include "GrTextUtils.h"
12
13#include "SkDistanceFieldGen.h"
14#include "SkGlyphCache.h"
15
16#include "ops/GrAtlasTextOp.h"
17
18////////////////////////////////////////////////////////////////////////////////////////////////////
19// A large template to handle regenerating the vertices of a textblob with as few branches as
20// possible
21template <bool regenPos, bool regenCol, bool regenTexCoords>
22inline void regen_vertices(intptr_t vertex, const GrGlyph* glyph, size_t vertexStride,
23                           bool useDistanceFields, SkScalar transX, SkScalar transY,
24                           int32_t log2Width, int32_t log2Height,
25                           GrColor color) {
26    int u0, v0, u1, v1;
27    if (regenTexCoords) {
28        SkASSERT(glyph);
29        int width = glyph->fBounds.width();
30        int height = glyph->fBounds.height();
31
32        if (useDistanceFields) {
33            u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
34            v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
35            u1 = u0 + width - 2 * SK_DistanceFieldInset;
36            v1 = v0 + height - 2 * SK_DistanceFieldInset;
37        } else {
38            u0 = glyph->fAtlasLocation.fX;
39            v0 = glyph->fAtlasLocation.fY;
40            u1 = u0 + width;
41            v1 = v0 + height;
42        }
43
44        // normalize
45        u0 *= 65535;
46        u0 >>= log2Width;
47        u1 *= 65535;
48        u1 >>= log2Width;
49        v0 *= 65535;
50        v0 >>= log2Height;
51        v1 *= 65535;
52        v1 >>= log2Height;
53        SkASSERT(u0 >= 0 && u0 <= 65535);
54        SkASSERT(u1 >= 0 && u1 <= 65535);
55        SkASSERT(v0 >= 0 && v0 <= 65535);
56        SkASSERT(v1 >= 0 && v1 <= 65535);
57    }
58
59    // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color
60    // vertices, hence vertexStride - sizeof(SkIPoint16)
61    intptr_t colorOffset = sizeof(SkPoint);
62    intptr_t texCoordOffset = vertexStride - sizeof(SkIPoint16);
63
64    // V0
65    if (regenPos) {
66        SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
67        point->fX += transX;
68        point->fY += transY;
69    }
70
71    if (regenCol) {
72        SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
73        *vcolor = color;
74    }
75
76    if (regenTexCoords) {
77        uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
78        textureCoords[0] = (uint16_t) u0;
79        textureCoords[1] = (uint16_t) v0;
80    }
81    vertex += vertexStride;
82
83    // V1
84    if (regenPos) {
85        SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
86        point->fX += transX;
87        point->fY += transY;
88    }
89
90    if (regenCol) {
91        SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
92        *vcolor = color;
93    }
94
95    if (regenTexCoords) {
96        uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
97        textureCoords[0] = (uint16_t)u0;
98        textureCoords[1] = (uint16_t)v1;
99    }
100    vertex += vertexStride;
101
102    // V2
103    if (regenPos) {
104        SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
105        point->fX += transX;
106        point->fY += transY;
107    }
108
109    if (regenCol) {
110        SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
111        *vcolor = color;
112    }
113
114    if (regenTexCoords) {
115        uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
116        textureCoords[0] = (uint16_t)u1;
117        textureCoords[1] = (uint16_t)v1;
118    }
119    vertex += vertexStride;
120
121    // V3
122    if (regenPos) {
123        SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
124        point->fX += transX;
125        point->fY += transY;
126    }
127
128    if (regenCol) {
129        SkColor* vcolor = reinterpret_cast<SkColor*>(vertex + colorOffset);
130        *vcolor = color;
131    }
132
133    if (regenTexCoords) {
134        uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
135        textureCoords[0] = (uint16_t)u1;
136        textureCoords[1] = (uint16_t)v0;
137    }
138}
139
140template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
141void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target, GrAtlasGlyphCache* fontCache,
142                                GrBlobRegenHelper* helper, Run* run, Run::SubRunInfo* info,
143                                SkAutoGlyphCache* lazyCache, int glyphCount, size_t vertexStride,
144                                GrColor color, SkScalar transX, SkScalar transY) const {
145    SkASSERT(lazyCache);
146    static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
147    GrAtlasTextStrike* strike = nullptr;
148    if (regenTexCoords) {
149        info->resetBulkUseToken();
150
151        const SkDescriptor* desc = (run->fOverrideDescriptor && !info->drawAsDistanceFields())
152                                      ? run->fOverrideDescriptor->getDesc()
153                                      : run->fDescriptor.getDesc();
154
155        if (!*lazyCache || (*lazyCache)->getDescriptor() != *desc) {
156            SkScalerContextEffects effects;
157            effects.fPathEffect = run->fPathEffect.get();
158            effects.fRasterizer = run->fRasterizer.get();
159            effects.fMaskFilter = run->fMaskFilter.get();
160            lazyCache->reset(SkGlyphCache::DetachCache(run->fTypeface.get(), effects, desc));
161        }
162
163        if (regenGlyphs) {
164            strike = fontCache->getStrike(lazyCache->get());
165        } else {
166            strike = info->strike();
167        }
168    }
169
170    bool brokenRun = false;
171    for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
172        GrGlyph* glyph = nullptr;
173        int log2Width = 0, log2Height = 0;
174        if (regenTexCoords) {
175            size_t glyphOffset = glyphIdx + info->glyphStartIndex();
176
177            if (regenGlyphs) {
178                // Get the id from the old glyph, and use the new strike to lookup
179                // the glyph.
180                GrGlyph::PackedID id = fGlyphs[glyphOffset]->fPackedID;
181                fGlyphs[glyphOffset] = strike->getGlyph(id, info->maskFormat(), lazyCache->get());
182                SkASSERT(id == fGlyphs[glyphOffset]->fPackedID);
183            }
184            glyph = fGlyphs[glyphOffset];
185            SkASSERT(glyph && glyph->fMaskFormat == info->maskFormat());
186
187            if (!fontCache->hasGlyph(glyph) &&
188                !strike->addGlyphToAtlas(target, glyph, lazyCache->get(), info->maskFormat())) {
189                helper->flush();
190                brokenRun = glyphIdx > 0;
191
192                SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(target,
193                                                                    glyph,
194                                                                    lazyCache->get(),
195                                                                    info->maskFormat());
196                SkASSERT(success);
197            }
198            fontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph,
199                                                    target->nextDrawToken());
200            log2Width = fontCache->log2Width(info->maskFormat());
201            log2Height = fontCache->log2Height(info->maskFormat());
202        }
203
204        intptr_t vertex = reinterpret_cast<intptr_t>(fVertices);
205        vertex += info->vertexStartIndex();
206        vertex += vertexStride * glyphIdx * GrAtlasTextOp::kVerticesPerGlyph;
207        regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
208                                                           info->drawAsDistanceFields(), transX,
209                                                           transY, log2Width, log2Height, color);
210        helper->incGlyphCount();
211    }
212
213    // We may have changed the color so update it here
214    info->setColor(color);
215    if (regenTexCoords) {
216        if (regenGlyphs) {
217            info->setStrike(strike);
218        }
219        info->setAtlasGeneration(brokenRun ? GrDrawOpAtlas::kInvalidAtlasGeneration
220                                           : fontCache->atlasGeneration(info->maskFormat()));
221    }
222}
223
224enum RegenMask {
225    kNoRegen    = 0x0,
226    kRegenPos   = 0x1,
227    kRegenCol   = 0x2,
228    kRegenTex   = 0x4,
229    kRegenGlyph = 0x8 | kRegenTex, // we have to regenerate the texture coords when we regen glyphs
230
231    // combinations
232    kRegenPosCol = kRegenPos | kRegenCol,
233    kRegenPosTex = kRegenPos | kRegenTex,
234    kRegenPosTexGlyph = kRegenPos | kRegenGlyph,
235    kRegenPosColTex = kRegenPos | kRegenCol | kRegenTex,
236    kRegenPosColTexGlyph = kRegenPos | kRegenCol | kRegenGlyph,
237    kRegenColTex = kRegenCol | kRegenTex,
238    kRegenColTexGlyph = kRegenCol | kRegenGlyph,
239};
240
241#define REGEN_ARGS target, fontCache, helper, &run, &info, lazyCache, \
242                   *glyphCount, vertexStride, color, transX, transY
243
244void GrAtlasTextBlob::regenInOp(GrDrawOp::Target* target,
245                                GrAtlasGlyphCache* fontCache,
246                                GrBlobRegenHelper* helper,
247                                int runIndex, int subRunIndex, SkAutoGlyphCache* lazyCache,
248                                size_t vertexStride, const SkMatrix& viewMatrix,
249                                SkScalar x, SkScalar y, GrColor color,
250                                void** vertices, size_t* byteCount, int* glyphCount) {
251    Run& run = fRuns[runIndex];
252    Run::SubRunInfo& info = run.fSubRunInfo[subRunIndex];
253
254    uint64_t currentAtlasGen = fontCache->atlasGeneration(info.maskFormat());
255
256    // Compute translation if any
257    SkScalar transX, transY;
258    info.computeTranslation(viewMatrix, x, y, &transX, &transY);
259
260    // Because the GrAtlasGlyphCache may evict the strike a blob depends on using for
261    // generating its texture coords, we have to track whether or not the strike has
262    // been abandoned.  If it hasn't been abandoned, then we can use the GrGlyph*s as is
263    // otherwise we have to get the new strike, and use that to get the correct glyphs.
264    // Because we do not have the packed ids, and thus can't look up our glyphs in the
265    // new strike, we instead keep our ref to the old strike and use the packed ids from
266    // it.  These ids will still be valid as long as we hold the ref.  When we are done
267    // updating our cache of the GrGlyph*s, we drop our ref on the old strike
268    bool regenerateGlyphs = info.strike()->isAbandoned();
269    bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen ||
270                                   regenerateGlyphs;
271    bool regenerateColors = kARGB_GrMaskFormat != info.maskFormat() &&
272                            info.color() != color;
273    bool regeneratePositions = transX != 0.f || transY != 0.f;
274    *glyphCount = info.glyphCount();
275
276    uint32_t regenMaskBits = kNoRegen;
277    regenMaskBits |= regeneratePositions ? kRegenPos : 0;
278    regenMaskBits |= regenerateColors ? kRegenCol : 0;
279    regenMaskBits |= regenerateTextureCoords ? kRegenTex : 0;
280    regenMaskBits |= regenerateGlyphs ? kRegenGlyph : 0;
281    RegenMask regenMask = (RegenMask)regenMaskBits;
282
283    switch (regenMask) {
284        case kRegenPos:
285            this->regenInOp<true, false, false, false>(REGEN_ARGS);
286            break;
287        case kRegenCol:
288            this->regenInOp<false, true, false, false>(REGEN_ARGS);
289            break;
290        case kRegenTex:
291            this->regenInOp<false, false, true, false>(REGEN_ARGS);
292            break;
293        case kRegenGlyph:
294            this->regenInOp<false, false, true, true>(REGEN_ARGS);
295            break;
296
297        // combinations
298        case kRegenPosCol:
299            this->regenInOp<true, true, false, false>(REGEN_ARGS);
300            break;
301        case kRegenPosTex:
302            this->regenInOp<true, false, true, false>(REGEN_ARGS);
303            break;
304        case kRegenPosTexGlyph:
305            this->regenInOp<true, false, true, true>(REGEN_ARGS);
306            break;
307        case kRegenPosColTex:
308            this->regenInOp<true, true, true, false>(REGEN_ARGS);
309            break;
310        case kRegenPosColTexGlyph:
311            this->regenInOp<true, true, true, true>(REGEN_ARGS);
312            break;
313        case kRegenColTex:
314            this->regenInOp<false, true, true, false>(REGEN_ARGS);
315            break;
316        case kRegenColTexGlyph:
317            this->regenInOp<false, true, true, true>(REGEN_ARGS);
318            break;
319        case kNoRegen:
320            helper->incGlyphCount(*glyphCount);
321
322            // set use tokens for all of the glyphs in our subrun.  This is only valid if we
323            // have a valid atlas generation
324            fontCache->setUseTokenBulk(*info.bulkUseToken(), target->nextDrawToken(),
325                                        info.maskFormat());
326            break;
327    }
328
329    *byteCount = info.byteCount();
330    *vertices = fVertices + info.vertexStartIndex();
331}
332