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 size_t offset = bitmap.pixelRefOffset(); 69 int16_t width = static_cast<int16_t>(bitmap.width()); 70 int16_t height = static_cast<int16_t>(bitmap.height()); 71 72 GrCacheID::Key key; 73 memcpy(key.fData8, &genID, 4); 74 memcpy(key.fData8 + 4, &width, 2); 75 memcpy(key.fData8 + 6, &height, 2); 76 memcpy(key.fData8 + 8, &offset, sizeof(size_t)); 77 static const size_t kKeyDataSize = 8 + sizeof(size_t); 78 memset(key.fData8 + kKeyDataSize, 0, sizeof(key) - kKeyDataSize); 79 GR_STATIC_ASSERT(sizeof(key) >= 8 + sizeof(size_t)); 80 static const GrCacheID::Domain gBitmapTextureDomain = GrCacheID::GenerateDomain(); 81 id->reset(gBitmapTextureDomain, key); 82 } 83 84 static void generate_bitmap_texture_desc(const SkBitmap& bitmap, GrTextureDesc* desc) { 85 desc->fFlags = kNone_GrTextureFlags; 86 desc->fWidth = bitmap.width(); 87 desc->fHeight = bitmap.height(); 88 desc->fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config()); 89 desc->fSampleCnt = 0; 90 } 91 92 namespace { 93 94 // When the SkPixelRef genID changes, invalidate a corresponding GrResource described by key. 95 class GrResourceInvalidator : public SkPixelRef::GenIDChangeListener { 96 public: 97 explicit GrResourceInvalidator(GrResourceKey key) : fKey(key) {} 98 private: 99 GrResourceKey fKey; 100 101 virtual void onChange() SK_OVERRIDE { 102 const GrResourceInvalidatedMessage message = { fKey }; 103 SkMessageBus<GrResourceInvalidatedMessage>::Post(message); 104 } 105 }; 106 107 } // namespace 108 109 static void add_genID_listener(GrResourceKey key, SkPixelRef* pixelRef) { 110 SkASSERT(NULL != pixelRef); 111 pixelRef->addGenIDChangeListener(SkNEW_ARGS(GrResourceInvalidator, (key))); 112 } 113 114 static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, 115 bool cache, 116 const GrTextureParams* params, 117 const SkBitmap& origBitmap) { 118 SkBitmap tmpBitmap; 119 120 const SkBitmap* bitmap = &origBitmap; 121 122 GrTextureDesc desc; 123 generate_bitmap_texture_desc(*bitmap, &desc); 124 125 if (SkBitmap::kIndex8_Config == bitmap->config()) { 126 // build_compressed_data doesn't do npot->pot expansion 127 // and paletted textures can't be sub-updated 128 if (ctx->supportsIndex8PixelConfig(params, bitmap->width(), bitmap->height())) { 129 size_t imagesize = bitmap->width() * bitmap->height() + kGrColorTableSize; 130 SkAutoMalloc storage(imagesize); 131 132 build_compressed_data(storage.get(), origBitmap); 133 134 // our compressed data will be trimmed, so pass width() for its 135 // "rowBytes", since they are the same now. 136 137 if (cache) { 138 GrCacheID cacheID; 139 generate_bitmap_cache_id(origBitmap, &cacheID); 140 141 GrResourceKey key; 142 GrTexture* result = ctx->createTexture(params, desc, cacheID, 143 storage.get(), bitmap->width(), &key); 144 if (NULL != result) { 145 add_genID_listener(key, origBitmap.pixelRef()); 146 } 147 return result; 148 } else { 149 GrTexture* result = ctx->lockAndRefScratchTexture(desc, 150 GrContext::kExact_ScratchTexMatch); 151 result->writePixels(0, 0, bitmap->width(), 152 bitmap->height(), desc.fConfig, 153 storage.get()); 154 return result; 155 } 156 } else { 157 origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config); 158 // now bitmap points to our temp, which has been promoted to 32bits 159 bitmap = &tmpBitmap; 160 desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap->config()); 161 } 162 } 163 164 SkAutoLockPixels alp(*bitmap); 165 if (!bitmap->readyToDraw()) { 166 return NULL; 167 } 168 if (cache) { 169 // This texture is likely to be used again so leave it in the cache 170 GrCacheID cacheID; 171 generate_bitmap_cache_id(origBitmap, &cacheID); 172 173 GrResourceKey key; 174 GrTexture* result = ctx->createTexture(params, desc, cacheID, 175 bitmap->getPixels(), bitmap->rowBytes(), &key); 176 if (NULL != result) { 177 add_genID_listener(key, origBitmap.pixelRef()); 178 } 179 return result; 180 } else { 181 // This texture is unlikely to be used again (in its present form) so 182 // just use a scratch texture. This will remove the texture from the 183 // cache so no one else can find it. Additionally, once unlocked, the 184 // scratch texture will go to the end of the list for purging so will 185 // likely be available for this volatile bitmap the next time around. 186 GrTexture* result = ctx->lockAndRefScratchTexture(desc, GrContext::kExact_ScratchTexMatch); 187 result->writePixels(0, 0, 188 bitmap->width(), bitmap->height(), 189 desc.fConfig, 190 bitmap->getPixels(), 191 bitmap->rowBytes()); 192 return result; 193 } 194 } 195 196 bool GrIsBitmapInCache(const GrContext* ctx, 197 const SkBitmap& bitmap, 198 const GrTextureParams* params) { 199 GrCacheID cacheID; 200 generate_bitmap_cache_id(bitmap, &cacheID); 201 202 GrTextureDesc desc; 203 generate_bitmap_texture_desc(bitmap, &desc); 204 return ctx->isTextureInCache(desc, cacheID, params); 205 } 206 207 GrTexture* GrLockAndRefCachedBitmapTexture(GrContext* ctx, 208 const SkBitmap& bitmap, 209 const GrTextureParams* params) { 210 GrTexture* result = NULL; 211 212 bool cache = !bitmap.isVolatile(); 213 214 if (cache) { 215 // If the bitmap isn't changing try to find a cached copy first. 216 217 GrCacheID cacheID; 218 generate_bitmap_cache_id(bitmap, &cacheID); 219 220 GrTextureDesc desc; 221 generate_bitmap_texture_desc(bitmap, &desc); 222 223 result = ctx->findAndRefTexture(desc, cacheID, params); 224 } 225 if (NULL == result) { 226 result = sk_gr_create_bitmap_texture(ctx, cache, params, bitmap); 227 } 228 if (NULL == result) { 229 GrPrintf("---- failed to create texture for cache [%d %d]\n", 230 bitmap.width(), bitmap.height()); 231 } 232 return result; 233 } 234 235 void GrUnlockAndUnrefCachedBitmapTexture(GrTexture* texture) { 236 SkASSERT(NULL != texture->getContext()); 237 238 texture->getContext()->unlockScratchTexture(texture); 239 texture->unref(); 240 } 241 242 /////////////////////////////////////////////////////////////////////////////// 243 244 GrPixelConfig SkBitmapConfig2GrPixelConfig(SkBitmap::Config config) { 245 switch (config) { 246 case SkBitmap::kA8_Config: 247 return kAlpha_8_GrPixelConfig; 248 case SkBitmap::kIndex8_Config: 249 return kIndex_8_GrPixelConfig; 250 case SkBitmap::kRGB_565_Config: 251 return kRGB_565_GrPixelConfig; 252 case SkBitmap::kARGB_4444_Config: 253 return kRGBA_4444_GrPixelConfig; 254 case SkBitmap::kARGB_8888_Config: 255 return kSkia8888_GrPixelConfig; 256 default: 257 // kNo_Config, kA1_Config missing 258 return kUnknown_GrPixelConfig; 259 } 260 } 261 262 bool GrPixelConfig2ColorType(GrPixelConfig config, SkColorType* ctOut) { 263 SkColorType ct; 264 switch (config) { 265 case kAlpha_8_GrPixelConfig: 266 ct = kAlpha_8_SkColorType; 267 break; 268 case kIndex_8_GrPixelConfig: 269 ct = kIndex_8_SkColorType; 270 break; 271 case kRGB_565_GrPixelConfig: 272 ct = kRGB_565_SkColorType; 273 break; 274 case kRGBA_4444_GrPixelConfig: 275 ct = kARGB_4444_SkColorType; 276 break; 277 case kRGBA_8888_GrPixelConfig: 278 ct = kRGBA_8888_SkColorType; 279 break; 280 case kBGRA_8888_GrPixelConfig: 281 ct = kBGRA_8888_SkColorType; 282 break; 283 default: 284 return false; 285 } 286 if (ctOut) { 287 *ctOut = ct; 288 } 289 return true; 290 } 291