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