• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "GrAtlasGlyphCache.h"
9 #include "GrContext.h"
10 #include "GrGpu.h"
11 #include "GrRectanizer.h"
12 #include "GrSurfacePriv.h"
13 #include "SkAutoMalloc.h"
14 #include "SkString.h"
15 
16 #include "SkDistanceFieldGen.h"
17 #include "GrDistanceFieldGenFromVector.h"
18 
initAtlas(GrMaskFormat format)19 bool GrAtlasGlyphCache::initAtlas(GrMaskFormat format) {
20     int index = MaskFormatToAtlasIndex(format);
21     if (!fAtlases[index]) {
22         GrPixelConfig config = MaskFormatToPixelConfig(format, *fContext->caps());
23         int width = fAtlasConfigs[index].fWidth;
24         int height = fAtlasConfigs[index].fHeight;
25         int numPlotsX = fAtlasConfigs[index].numPlotsX();
26         int numPlotsY = fAtlasConfigs[index].numPlotsY();
27 
28         fAtlases[index] = GrDrawOpAtlas::Make(
29                 fContext, config, width, height, numPlotsX, numPlotsY,
30                 &GrAtlasGlyphCache::HandleEviction, (void*)this);
31         if (!fAtlases[index]) {
32             return false;
33         }
34     }
35     return true;
36 }
37 
GrAtlasGlyphCache(GrContext * context,float maxTextureBytes)38 GrAtlasGlyphCache::GrAtlasGlyphCache(GrContext* context, float maxTextureBytes)
39         : fContext(context), fPreserveStrike(nullptr) {
40     // Calculate RGBA size. Must be between 1024 x 512 and MaxTextureSize x MaxTextureSize / 2
41     int log2MaxTextureSize = log2(context->caps()->maxTextureSize());
42     int log2MaxDim = 10;
43     for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) {
44         int maxDim = 1 << log2MaxDim;
45         int minDim = 1 << (log2MaxDim - 1);
46 
47         if (maxDim * minDim * 4 >= maxTextureBytes) break;
48     }
49 
50     int log2MinDim = log2MaxDim - 1;
51     int maxDim = 1 << log2MaxDim;
52     int minDim = 1 << log2MinDim;
53     // Plots are either 256 or 512.
54     int maxPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 2)));
55     int minPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 3)));
56 
57     // Setup default atlas configs. The A8 atlas uses maxDim for both width and height, as the A8
58     // format is already very compact.
59     fAtlasConfigs[kA8_GrMaskFormat].fWidth = maxDim;
60     fAtlasConfigs[kA8_GrMaskFormat].fHeight = maxDim;
61     fAtlasConfigs[kA8_GrMaskFormat].fLog2Width = log2MaxDim;
62     fAtlasConfigs[kA8_GrMaskFormat].fLog2Height = log2MaxDim;
63     fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = maxPlot;
64     fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = minPlot;
65 
66     // A565 and ARGB use maxDim x minDim.
67     fAtlasConfigs[kA565_GrMaskFormat].fWidth = minDim;
68     fAtlasConfigs[kA565_GrMaskFormat].fHeight = maxDim;
69     fAtlasConfigs[kA565_GrMaskFormat].fLog2Width = log2MinDim;
70     fAtlasConfigs[kA565_GrMaskFormat].fLog2Height = log2MaxDim;
71     fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = minPlot;
72     fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = minPlot;
73 
74     fAtlasConfigs[kARGB_GrMaskFormat].fWidth = minDim;
75     fAtlasConfigs[kARGB_GrMaskFormat].fHeight = maxDim;
76     fAtlasConfigs[kARGB_GrMaskFormat].fLog2Width = log2MinDim;
77     fAtlasConfigs[kARGB_GrMaskFormat].fLog2Height = log2MaxDim;
78     fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = minPlot;
79     fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = minPlot;
80 }
81 
~GrAtlasGlyphCache()82 GrAtlasGlyphCache::~GrAtlasGlyphCache() {
83     StrikeHash::Iter iter(&fCache);
84     while (!iter.done()) {
85         (*iter).fIsAbandoned = true;
86         (*iter).unref();
87         ++iter;
88     }
89 }
90 
freeAll()91 void GrAtlasGlyphCache::freeAll() {
92     StrikeHash::Iter iter(&fCache);
93     while (!iter.done()) {
94         (*iter).fIsAbandoned = true;
95         (*iter).unref();
96         ++iter;
97     }
98     fCache.rewind();
99     for (int i = 0; i < kMaskFormatCount; ++i) {
100         fAtlases[i] = nullptr;
101     }
102 }
103 
HandleEviction(GrDrawOpAtlas::AtlasID id,void * ptr)104 void GrAtlasGlyphCache::HandleEviction(GrDrawOpAtlas::AtlasID id, void* ptr) {
105     GrAtlasGlyphCache* fontCache = reinterpret_cast<GrAtlasGlyphCache*>(ptr);
106 
107     StrikeHash::Iter iter(&fontCache->fCache);
108     for (; !iter.done(); ++iter) {
109         GrAtlasTextStrike* strike = &*iter;
110         strike->removeID(id);
111 
112         // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
113         // triggered the eviction
114         if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
115             fontCache->fCache.remove(GrAtlasTextStrike::GetKey(*strike));
116             strike->fIsAbandoned = true;
117             strike->unref();
118         }
119     }
120 }
121 
122 #ifdef SK_DEBUG
123 #include "GrContextPriv.h"
124 #include "GrSurfaceProxy.h"
125 #include "GrSurfaceContext.h"
126 #include "GrTextureProxy.h"
127 
128 #include "SkBitmap.h"
129 #include "SkImageEncoder.h"
130 #include "SkStream.h"
131 #include <stdio.h>
132 
133 /**
134   * Write the contents of the surface proxy to a PNG. Returns true if successful.
135   * @param filename      Full path to desired file
136   */
save_pixels(GrContext * context,GrSurfaceProxy * sProxy,const char * filename)137 static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, const char* filename) {
138     if (!sProxy) {
139         return false;
140     }
141 
142     SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(),
143                                        kRGBA_8888_SkColorType, kPremul_SkAlphaType);
144     SkBitmap bm;
145     if (!bm.tryAllocPixels(ii)) {
146         return false;
147     }
148 
149     sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
150                                                                             sk_ref_sp(sProxy),
151                                                                             nullptr));
152     if (!sContext || !sContext->asTextureProxy()) {
153         return false;
154     }
155 
156     bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), 0, 0);
157     if (!result) {
158         SkDebugf("------ failed to read pixels for %s\n", filename);
159         return false;
160     }
161 
162     // remove any previous version of this file
163     remove(filename);
164 
165     SkFILEWStream file(filename);
166     if (!file.isValid()) {
167         SkDebugf("------ failed to create file: %s\n", filename);
168         remove(filename);   // remove any partial file
169         return false;
170     }
171 
172     if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
173         SkDebugf("------ failed to encode %s\n", filename);
174         remove(filename);   // remove any partial file
175         return false;
176     }
177 
178     return true;
179 }
180 
dump() const181 void GrAtlasGlyphCache::dump() const {
182     static int gDumpCount = 0;
183     for (int i = 0; i < kMaskFormatCount; ++i) {
184         if (fAtlases[i]) {
185             sk_sp<GrTextureProxy> proxy = fAtlases[i]->getProxy();
186             if (proxy) {
187                 SkString filename;
188 #ifdef SK_BUILD_FOR_ANDROID
189                 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
190 #else
191                 filename.printf("fontcache_%d%d.png", gDumpCount, i);
192 #endif
193 
194                 save_pixels(fContext, proxy.get(), filename.c_str());
195             }
196         }
197     }
198     ++gDumpCount;
199 }
200 #endif
201 
setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3])202 void GrAtlasGlyphCache::setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]) {
203     // Delete any old atlases.
204     // This should be safe to do as long as we are not in the middle of a flush.
205     for (int i = 0; i < kMaskFormatCount; i++) {
206         fAtlases[i] = nullptr;
207     }
208     memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs));
209 }
210 
211 ///////////////////////////////////////////////////////////////////////////////
212 
get_packed_glyph_mask_format(const SkGlyph & glyph)213 static inline GrMaskFormat get_packed_glyph_mask_format(const SkGlyph& glyph) {
214     SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
215     switch (format) {
216         case SkMask::kBW_Format:
217             // fall through to kA8 -- we store BW glyphs in our 8-bit cache
218         case SkMask::kA8_Format:
219             return kA8_GrMaskFormat;
220         case SkMask::kLCD16_Format:
221             return kA565_GrMaskFormat;
222         case SkMask::kARGB32_Format:
223             return kARGB_GrMaskFormat;
224         default:
225             SkDEBUGFAIL("unsupported SkMask::Format");
226             return kA8_GrMaskFormat;
227     }
228 }
229 
get_packed_glyph_bounds(SkGlyphCache * cache,const SkGlyph & glyph,SkIRect * bounds)230 static inline bool get_packed_glyph_bounds(SkGlyphCache* cache, const SkGlyph& glyph,
231                                            SkIRect* bounds) {
232 #if 1
233     // crbug:510931
234     // Retrieving the image from the cache can actually change the mask format.
235     cache->findImage(glyph);
236 #endif
237     bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
238 
239     return true;
240 }
241 
get_packed_glyph_df_bounds(SkGlyphCache * cache,const SkGlyph & glyph,SkIRect * bounds)242 static inline bool get_packed_glyph_df_bounds(SkGlyphCache* cache, const SkGlyph& glyph,
243                                               SkIRect* bounds) {
244 #if 1
245     // crbug:510931
246     // Retrieving the image from the cache can actually change the mask format.
247     cache->findImage(glyph);
248 #endif
249     bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
250     bounds->outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
251 
252     return true;
253 }
254 
255 // expands each bit in a bitmask to 0 or ~0 of type INT_TYPE. Used to expand a BW glyph mask to
256 // A8, RGB565, or RGBA8888.
257 template <typename INT_TYPE>
expand_bits(INT_TYPE * dst,const uint8_t * src,int width,int height,int dstRowBytes,int srcRowBytes)258 static void expand_bits(INT_TYPE* dst,
259                         const uint8_t* src,
260                         int width,
261                         int height,
262                         int dstRowBytes,
263                         int srcRowBytes) {
264     for (int i = 0; i < height; ++i) {
265         int rowWritesLeft = width;
266         const uint8_t* s = src;
267         INT_TYPE* d = dst;
268         while (rowWritesLeft > 0) {
269             unsigned mask = *s++;
270             for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
271                 *d++ = (mask & (1 << i)) ? (INT_TYPE)(~0UL) : 0;
272             }
273         }
274         dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
275         src += srcRowBytes;
276     }
277 }
278 
get_packed_glyph_image(SkGlyphCache * cache,const SkGlyph & glyph,int width,int height,int dstRB,GrMaskFormat expectedMaskFormat,void * dst)279 static bool get_packed_glyph_image(SkGlyphCache* cache, const SkGlyph& glyph, int width,
280                                    int height, int dstRB, GrMaskFormat expectedMaskFormat,
281                                    void* dst) {
282     SkASSERT(glyph.fWidth == width);
283     SkASSERT(glyph.fHeight == height);
284     const void* src = cache->findImage(glyph);
285     if (nullptr == src) {
286         return false;
287     }
288 
289     // crbug:510931
290     // Retrieving the image from the cache can actually change the mask format.  This case is very
291     // uncommon so for now we just draw a clear box for these glyphs.
292     if (get_packed_glyph_mask_format(glyph) != expectedMaskFormat) {
293         const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
294         for (int y = 0; y < height; y++) {
295             sk_bzero(dst, width * bpp);
296             dst = (char*)dst + dstRB;
297         }
298         return true;
299     }
300 
301     int srcRB = glyph.rowBytes();
302     // The windows font host sometimes has BW glyphs in a non-BW strike. So it is important here to
303     // check the glyph's format, not the strike's format, and to be able to convert to any of the
304     // GrMaskFormats.
305     if (SkMask::kBW_Format == glyph.fMaskFormat) {
306         // expand bits to our mask type
307         const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
308         switch (expectedMaskFormat) {
309             case kA8_GrMaskFormat:{
310                 uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
311                 expand_bits(bytes, bits, width, height, dstRB, srcRB);
312                 break;
313             }
314             case kA565_GrMaskFormat: {
315                 uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
316                 expand_bits(rgb565, bits, width, height, dstRB, srcRB);
317                 break;
318             }
319             default:
320                 SkFAIL("Invalid GrMaskFormat");
321         }
322     } else if (srcRB == dstRB) {
323         memcpy(dst, src, dstRB * height);
324     } else {
325         const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
326         for (int y = 0; y < height; y++) {
327             memcpy(dst, src, width * bbp);
328             src = (const char*)src + srcRB;
329             dst = (char*)dst + dstRB;
330         }
331     }
332     return true;
333 }
334 
get_packed_glyph_df_image(SkGlyphCache * cache,const SkGlyph & glyph,int width,int height,void * dst)335 static bool get_packed_glyph_df_image(SkGlyphCache* cache, const SkGlyph& glyph,
336                                       int width, int height, void* dst) {
337     SkASSERT(glyph.fWidth + 2*SK_DistanceFieldPad == width);
338     SkASSERT(glyph.fHeight + 2*SK_DistanceFieldPad == height);
339 
340 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
341     const SkPath* path = cache->findPath(glyph);
342     if (nullptr == path) {
343         return false;
344     }
345 
346     SkDEBUGCODE(SkRect glyphBounds = SkRect::MakeXYWH(glyph.fLeft,
347                                                       glyph.fTop,
348                                                       glyph.fWidth,
349                                                       glyph.fHeight));
350     SkASSERT(glyphBounds.contains(path->getBounds()));
351 
352     // now generate the distance field
353     SkASSERT(dst);
354     SkMatrix drawMatrix;
355     drawMatrix.setTranslate((SkScalar)-glyph.fLeft, (SkScalar)-glyph.fTop);
356 
357     // Generate signed distance field directly from SkPath
358     bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dst,
359                                            *path, drawMatrix,
360                                            width, height, width * sizeof(unsigned char));
361 
362     if (!succeed) {
363 #endif
364         const void* image = cache->findImage(glyph);
365         if (nullptr == image) {
366             return false;
367         }
368 
369         // now generate the distance field
370         SkASSERT(dst);
371         SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
372         if (SkMask::kA8_Format == maskFormat) {
373             // make the distance field from the image
374             SkGenerateDistanceFieldFromA8Image((unsigned char*)dst,
375                                                (unsigned char*)image,
376                                                glyph.fWidth, glyph.fHeight,
377                                                glyph.rowBytes());
378         } else if (SkMask::kBW_Format == maskFormat) {
379             // make the distance field from the image
380             SkGenerateDistanceFieldFromBWImage((unsigned char*)dst,
381                                                (unsigned char*)image,
382                                                glyph.fWidth, glyph.fHeight,
383                                                glyph.rowBytes());
384         } else {
385             return false;
386         }
387 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
388     }
389 #endif
390     return true;
391 }
392 
393 ///////////////////////////////////////////////////////////////////////////////
394 
395 /*
396     The text strike is specific to a given font/style/matrix setup, which is
397     represented by the GrHostFontScaler object we are given in getGlyph().
398 
399     We map a 32bit glyphID to a GrGlyph record, which in turn points to a
400     atlas and a position within that texture.
401  */
402 
GrAtlasTextStrike(GrAtlasGlyphCache * owner,const SkDescriptor & key)403 GrAtlasTextStrike::GrAtlasTextStrike(GrAtlasGlyphCache* owner, const SkDescriptor& key)
404     : fFontScalerKey(key)
405     , fPool(9/*start allocations at 512 bytes*/)
406     , fAtlasGlyphCache(owner) // no need to ref, it won't go away before we do
407     , fAtlasedGlyphs(0)
408     , fIsAbandoned(false) {}
409 
~GrAtlasTextStrike()410 GrAtlasTextStrike::~GrAtlasTextStrike() {
411     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
412     while (!iter.done()) {
413         (*iter).reset();
414         ++iter;
415     }
416 }
417 
generateGlyph(const SkGlyph & skGlyph,GrGlyph::PackedID packed,SkGlyphCache * cache)418 GrGlyph* GrAtlasTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
419                                           SkGlyphCache* cache) {
420     SkIRect bounds;
421     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
422         if (!get_packed_glyph_df_bounds(cache, skGlyph, &bounds)) {
423             return nullptr;
424         }
425     } else {
426         if (!get_packed_glyph_bounds(cache, skGlyph, &bounds)) {
427             return nullptr;
428         }
429     }
430     GrMaskFormat format = get_packed_glyph_mask_format(skGlyph);
431 
432     GrGlyph* glyph = fPool.make<GrGlyph>();
433     glyph->init(packed, bounds, format);
434     fCache.add(glyph);
435     return glyph;
436 }
437 
removeID(GrDrawOpAtlas::AtlasID id)438 void GrAtlasTextStrike::removeID(GrDrawOpAtlas::AtlasID id) {
439     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
440     while (!iter.done()) {
441         if (id == (*iter).fID) {
442             (*iter).fID = GrDrawOpAtlas::kInvalidAtlasID;
443             fAtlasedGlyphs--;
444             SkASSERT(fAtlasedGlyphs >= 0);
445         }
446         ++iter;
447     }
448 }
449 
addGlyphToAtlas(GrDrawOp::Target * target,GrGlyph * glyph,SkGlyphCache * cache,GrMaskFormat expectedMaskFormat)450 bool GrAtlasTextStrike::addGlyphToAtlas(GrDrawOp::Target* target,
451                                         GrGlyph* glyph,
452                                         SkGlyphCache* cache,
453                                         GrMaskFormat expectedMaskFormat) {
454     SkASSERT(glyph);
455     SkASSERT(cache);
456     SkASSERT(fCache.find(glyph->fPackedID));
457 
458     int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
459 
460     size_t size = glyph->fBounds.area() * bytesPerPixel;
461     SkAutoSMalloc<1024> storage(size);
462 
463     const SkGlyph& skGlyph = GrToSkGlyph(cache, glyph->fPackedID);
464     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
465         if (!get_packed_glyph_df_image(cache, skGlyph, glyph->width(), glyph->height(),
466                                        storage.get())) {
467             return false;
468         }
469     } else {
470         if (!get_packed_glyph_image(cache, skGlyph, glyph->width(), glyph->height(),
471                                     glyph->width() * bytesPerPixel, expectedMaskFormat,
472                                     storage.get())) {
473             return false;
474         }
475     }
476 
477     bool success = fAtlasGlyphCache->addToAtlas(this, &glyph->fID, target, expectedMaskFormat,
478                                                glyph->width(), glyph->height(),
479                                                storage.get(), &glyph->fAtlasLocation);
480     if (success) {
481         SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != glyph->fID);
482         fAtlasedGlyphs++;
483     }
484     return success;
485 }
486