|
1 /* |
|
2 * Copyright 2010 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 "SkGr.h" |
|
9 #include "SkConfig8888.h" |
|
10 #include "SkMessageBus.h" |
|
11 #include "SkPixelRef.h" |
|
12 #include "GrResourceCache.h" |
|
13 |
|
14 /* Fill out buffer with the compressed format Ganesh expects from a colortable |
|
15 based bitmap. [palette (colortable) + indices]. |
|
16 |
|
17 At the moment Ganesh only supports 8bit version. If Ganesh allowed we others |
|
18 we could detect that the colortable.count is <= 16, and then repack the |
|
19 indices as nibbles to save RAM, but it would take more time (i.e. a lot |
|
20 slower than memcpy), so skipping that for now. |
|
21 |
|
22 Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big |
|
23 as the colortable.count says it is. |
|
24 */ |
|
25 static void build_compressed_data(void* buffer, const SkBitmap& bitmap) { |
|
26 SkASSERT(SkBitmap::kIndex8_Config == bitmap.config()); |
|
27 |
|
28 SkAutoLockPixels alp(bitmap); |
|
29 if (!bitmap.readyToDraw()) { |
|
30 SkDEBUGFAIL("bitmap not ready to draw!"); |
|
31 return; |
|
32 } |
|
33 |
|
34 SkColorTable* ctable = bitmap.getColorTable(); |
|
35 char* dst = (char*)buffer; |
|
36 |
|
37 uint32_t* colorTableDst = reinterpret_cast<uint32_t*>(dst); |
|
38 const uint32_t* colorTableSrc = reinterpret_cast<const uint32_t*>(ctable->lockColors()); |
|
39 SkConvertConfig8888Pixels(colorTableDst, 0, SkCanvas::kRGBA_Premul_Config8888, |
|
40 colorTableSrc, 0, SkCanvas::kNative_Premul_Config8888, |
|
41 ctable->count(), 1); |
|
42 ctable->unlockColors(); |
|
43 |
|
44 // always skip a full 256 number of entries, even if we memcpy'd fewer |
|
45 dst += kGrColorTableSize; |
|
46 |
|
47 if ((unsigned)bitmap.width() == bitmap.rowBytes()) { |
|
48 memcpy(dst, bitmap.getPixels(), bitmap.getSize()); |
|
49 } else { |
|
50 // need to trim off the extra bytes per row |
|
51 size_t width = bitmap.width(); |
|
52 size_t rowBytes = bitmap.rowBytes(); |
|
53 const char* src = (const char*)bitmap.getPixels(); |
|
54 for (int y = 0; y < bitmap.height(); y++) { |
|
55 memcpy(dst, src, width); |
|
56 src += rowBytes; |
|
57 dst += width; |
|
58 } |
|
59 } |
|
60 } |
|
61 |
|
62 //////////////////////////////////////////////////////////////////////////////// |
|
63 |
|
64 static void generate_bitmap_cache_id(const SkBitmap& bitmap, GrCacheID* id) { |
|
65 // Our id includes the offset, width, and height so that bitmaps created by extractSubset() |
|
66 // are unique. |
|
67 uint32_t genID = bitmap.getGenerationID(); |
|
68 SkIPoint origin = bitmap.pixelRefOrigin(); |
|
69 int16_t width = SkToS16(bitmap.width()); |
|
70 int16_t height = SkToS16(bitmap.height()); |
|
71 |
|
72 GrCacheID::Key key; |
|
73 memcpy(key.fData8 + 0, &genID, 4); |
|
74 memcpy(key.fData8 + 4, &origin.fX, 4); |
|
75 memcpy(key.fData8 + 8, &origin.fY, 4); |
|
76 memcpy(key.fData8 + 12, &width, 2); |
|
77 memcpy(key.fData8 + 14, &height, 2); |
|
78 static const size_t kKeyDataSize = 16; |
|
79 memset(key.fData8 + kKeyDataSize, 0, sizeof(key) - kKeyDataSize); |
|
80 GR_STATIC_ASSERT(sizeof(key) >= kKeyDataSize); |
|
81 static const GrCacheID::Domain gBitmapTextureDomain = GrCacheID::GenerateDomain(); |
|
82 id->reset(gBitmapTextureDomain, key); |
|
83 } |
|
84 |
|
85 static void generate_bitmap_texture_desc(const SkBitmap& bitmap, GrTextureDesc* desc) { |
|
86 desc->fFlags = kNone_GrTextureFlags; |
|
87 desc->fWidth = bitmap.width(); |
|
88 desc->fHeight = bitmap.height(); |
|
89 desc->fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config()); |
|
90 desc->fSampleCnt = 0; |
|
91 } |
|
92 |
|
93 namespace { |
|
94 |
|
95 // When the SkPixelRef genID changes, invalidate a corresponding GrResource described by key. |
|
96 class GrResourceInvalidator : public SkPixelRef::GenIDChangeListener { |
|
97 public: |
|
98 explicit GrResourceInvalidator(GrResourceKey key) : fKey(key) {} |
|
99 private: |
|
100 GrResourceKey fKey; |
|
101 |
|
102 virtual void onChange() SK_OVERRIDE { |
|
103 const GrResourceInvalidatedMessage message = { fKey }; |
|
104 SkMessageBus<GrResourceInvalidatedMessage>::Post(message); |
|
105 } |
|
106 }; |
|
107 |
|
108 } // namespace |
|
109 |
|
110 static void add_genID_listener(GrResourceKey key, SkPixelRef* pixelRef) { |
|
111 SkASSERT(NULL != pixelRef); |
|
112 pixelRef->addGenIDChangeListener(SkNEW_ARGS(GrResourceInvalidator, (key))); |
|
113 } |
|
114 |
|
115 static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, |
|
116 bool cache, |
|
117 const GrTextureParams* params, |
|
118 const SkBitmap& origBitmap) { |
|
119 SkBitmap tmpBitmap; |
|
120 |
|
121 const SkBitmap* bitmap = &origBitmap; |
|
122 |
|
123 GrTextureDesc desc; |
|
124 generate_bitmap_texture_desc(*bitmap, &desc); |
|
125 |
|
126 if (SkBitmap::kIndex8_Config == bitmap->config()) { |
|
127 // build_compressed_data doesn't do npot->pot expansion |
|
128 // and paletted textures can't be sub-updated |
|
129 if (ctx->supportsIndex8PixelConfig(params, bitmap->width(), bitmap->height())) { |
|
130 size_t imagesize = bitmap->width() * bitmap->height() + kGrColorTableSize; |
|
131 SkAutoMalloc storage(imagesize); |
|
132 |
|
133 build_compressed_data(storage.get(), origBitmap); |
|
134 |
|
135 // our compressed data will be trimmed, so pass width() for its |
|
136 // "rowBytes", since they are the same now. |
|
137 |
|
138 if (cache) { |
|
139 GrCacheID cacheID; |
|
140 generate_bitmap_cache_id(origBitmap, &cacheID); |
|
141 |
|
142 GrResourceKey key; |
|
143 GrTexture* result = ctx->createTexture(params, desc, cacheID, |
|
144 storage.get(), bitmap->width(), &key); |
|
145 if (NULL != result) { |
|
146 add_genID_listener(key, origBitmap.pixelRef()); |
|
147 } |
|
148 return result; |
|
149 } else { |
|
150 GrTexture* result = ctx->lockAndRefScratchTexture(desc, |
|
151 GrContext::kExact_ScratchTexMatch); |
|
152 result->writePixels(0, 0, bitmap->width(), |
|
153 bitmap->height(), desc.fConfig, |
|
154 storage.get()); |
|
155 return result; |
|
156 } |
|
157 } else { |
|
158 origBitmap.copyTo(&tmpBitmap, kPMColor_SkColorType); |
|
159 // now bitmap points to our temp, which has been promoted to 32bits |
|
160 bitmap = &tmpBitmap; |
|
161 desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap->config()); |
|
162 } |
|
163 } |
|
164 |
|
165 SkAutoLockPixels alp(*bitmap); |
|
166 if (!bitmap->readyToDraw()) { |
|
167 return NULL; |
|
168 } |
|
169 if (cache) { |
|
170 // This texture is likely to be used again so leave it in the cache |
|
171 GrCacheID cacheID; |
|
172 generate_bitmap_cache_id(origBitmap, &cacheID); |
|
173 |
|
174 GrResourceKey key; |
|
175 GrTexture* result = ctx->createTexture(params, desc, cacheID, |
|
176 bitmap->getPixels(), bitmap->rowBytes(), &key); |
|
177 if (NULL != result) { |
|
178 add_genID_listener(key, origBitmap.pixelRef()); |
|
179 } |
|
180 return result; |
|
181 } else { |
|
182 // This texture is unlikely to be used again (in its present form) so |
|
183 // just use a scratch texture. This will remove the texture from the |
|
184 // cache so no one else can find it. Additionally, once unlocked, the |
|
185 // scratch texture will go to the end of the list for purging so will |
|
186 // likely be available for this volatile bitmap the next time around. |
|
187 GrTexture* result = ctx->lockAndRefScratchTexture(desc, GrContext::kExact_ScratchTexMatch); |
|
188 result->writePixels(0, 0, |
|
189 bitmap->width(), bitmap->height(), |
|
190 desc.fConfig, |
|
191 bitmap->getPixels(), |
|
192 bitmap->rowBytes()); |
|
193 return result; |
|
194 } |
|
195 } |
|
196 |
|
197 bool GrIsBitmapInCache(const GrContext* ctx, |
|
198 const SkBitmap& bitmap, |
|
199 const GrTextureParams* params) { |
|
200 GrCacheID cacheID; |
|
201 generate_bitmap_cache_id(bitmap, &cacheID); |
|
202 |
|
203 GrTextureDesc desc; |
|
204 generate_bitmap_texture_desc(bitmap, &desc); |
|
205 return ctx->isTextureInCache(desc, cacheID, params); |
|
206 } |
|
207 |
|
208 GrTexture* GrLockAndRefCachedBitmapTexture(GrContext* ctx, |
|
209 const SkBitmap& bitmap, |
|
210 const GrTextureParams* params) { |
|
211 GrTexture* result = NULL; |
|
212 |
|
213 bool cache = !bitmap.isVolatile(); |
|
214 |
|
215 if (cache) { |
|
216 // If the bitmap isn't changing try to find a cached copy first. |
|
217 |
|
218 GrCacheID cacheID; |
|
219 generate_bitmap_cache_id(bitmap, &cacheID); |
|
220 |
|
221 GrTextureDesc desc; |
|
222 generate_bitmap_texture_desc(bitmap, &desc); |
|
223 |
|
224 result = ctx->findAndRefTexture(desc, cacheID, params); |
|
225 } |
|
226 if (NULL == result) { |
|
227 result = sk_gr_create_bitmap_texture(ctx, cache, params, bitmap); |
|
228 } |
|
229 if (NULL == result) { |
|
230 GrPrintf("---- failed to create texture for cache [%d %d]\n", |
|
231 bitmap.width(), bitmap.height()); |
|
232 } |
|
233 return result; |
|
234 } |
|
235 |
|
236 void GrUnlockAndUnrefCachedBitmapTexture(GrTexture* texture) { |
|
237 SkASSERT(NULL != texture->getContext()); |
|
238 |
|
239 texture->getContext()->unlockScratchTexture(texture); |
|
240 texture->unref(); |
|
241 } |
|
242 |
|
243 /////////////////////////////////////////////////////////////////////////////// |
|
244 |
|
245 GrPixelConfig SkBitmapConfig2GrPixelConfig(SkBitmap::Config config) { |
|
246 switch (config) { |
|
247 case SkBitmap::kA8_Config: |
|
248 return kAlpha_8_GrPixelConfig; |
|
249 case SkBitmap::kIndex8_Config: |
|
250 return kIndex_8_GrPixelConfig; |
|
251 case SkBitmap::kRGB_565_Config: |
|
252 return kRGB_565_GrPixelConfig; |
|
253 case SkBitmap::kARGB_4444_Config: |
|
254 return kRGBA_4444_GrPixelConfig; |
|
255 case SkBitmap::kARGB_8888_Config: |
|
256 return kSkia8888_GrPixelConfig; |
|
257 default: |
|
258 // kNo_Config, kA1_Config missing |
|
259 return kUnknown_GrPixelConfig; |
|
260 } |
|
261 } |
|
262 |
|
263 // alphatype is ignore for now, but if GrPixelConfig is expanded to encompass |
|
264 // alpha info, that will be considered. |
|
265 GrPixelConfig SkImageInfo2GrPixelConfig(SkColorType ct, SkAlphaType) { |
|
266 switch (ct) { |
|
267 case kUnknown_SkColorType: |
|
268 return kUnknown_GrPixelConfig; |
|
269 case kAlpha_8_SkColorType: |
|
270 return kAlpha_8_GrPixelConfig; |
|
271 case kRGB_565_SkColorType: |
|
272 return kRGB_565_GrPixelConfig; |
|
273 case kARGB_4444_SkColorType: |
|
274 return kRGBA_4444_GrPixelConfig; |
|
275 case kRGBA_8888_SkColorType: |
|
276 return kRGBA_8888_GrPixelConfig; |
|
277 case kBGRA_8888_SkColorType: |
|
278 return kBGRA_8888_GrPixelConfig; |
|
279 case kIndex_8_SkColorType: |
|
280 return kIndex_8_GrPixelConfig; |
|
281 } |
|
282 SkASSERT(0); // shouldn't get here |
|
283 return kUnknown_GrPixelConfig; |
|
284 } |
|
285 |
|
286 bool GrPixelConfig2ColorType(GrPixelConfig config, SkColorType* ctOut) { |
|
287 SkColorType ct; |
|
288 switch (config) { |
|
289 case kAlpha_8_GrPixelConfig: |
|
290 ct = kAlpha_8_SkColorType; |
|
291 break; |
|
292 case kIndex_8_GrPixelConfig: |
|
293 ct = kIndex_8_SkColorType; |
|
294 break; |
|
295 case kRGB_565_GrPixelConfig: |
|
296 ct = kRGB_565_SkColorType; |
|
297 break; |
|
298 case kRGBA_4444_GrPixelConfig: |
|
299 ct = kARGB_4444_SkColorType; |
|
300 break; |
|
301 case kRGBA_8888_GrPixelConfig: |
|
302 ct = kRGBA_8888_SkColorType; |
|
303 break; |
|
304 case kBGRA_8888_GrPixelConfig: |
|
305 ct = kBGRA_8888_SkColorType; |
|
306 break; |
|
307 default: |
|
308 return false; |
|
309 } |
|
310 if (ctOut) { |
|
311 *ctOut = ct; |
|
312 } |
|
313 return true; |
|
314 } |