static void convertToIndex666(const SkBitmap& src, SkBitmap* dst) { SkColorTable* ctable = new SkColorTable(216); SkPMColor* colors = ctable->lockColors(); // rrr ggg bbb for (int r = 0; r < 6; r++) { int rr = conv6ToByte(r); for (int g = 0; g < 6; g++) { int gg = conv6ToByte(g); for (int b = 0; b < 6; b++) { int bb = conv6ToByte(b); *colors++ = SkPreMultiplyARGB(0xFF, rr, gg, bb); } } } ctable->unlockColors(true); dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height()); dst->allocPixels(ctable); ctable->unref(); SkAutoLockPixels alps(src); SkAutoLockPixels alpd(*dst); for (int y = 0; y < src.height(); y++) { const SkPMColor* srcP = src.getAddr32(0, y); uint8_t* dstP = dst->getAddr8(0, y); for (int x = src.width() - 1; x >= 0; --x) { *dstP++ = compute666Index(*srcP++); } } }
static SkBitmap make_bitmap() { SkBitmap bm; SkColorTable* ctable = new SkColorTable(256); SkPMColor* c = ctable->lockColors(); for (int i = 0; i < 256; i++) { c[i] = SkPackARGB32(0xFF, i, 0, 0); } ctable->unlockColors(true); bm.setConfig(SkBitmap::kIndex8_Config, 256, 32); bm.allocPixels(ctable); ctable->unref(); bm.lockPixels(); for (int y = 0; y < bm.height(); y++) { uint8_t* p = bm.getAddr8(0, y); for (int x = 0; x < 256; x++) { p[x] = x; } } bm.unlockPixels(); return bm; }
static SkBitmap make_bitmap() { SkBitmap bm; SkColorTable* ctable = new SkColorTable(256); SkPMColor* c = ctable->lockColors(); for (int i = 0; i < 256; i++) { c[i] = SkPackARGB32(255 - i, 0, 0, 0); } ctable->unlockColors(true); bm.setConfig(SkBitmap::kIndex8_Config, 256, 256); bm.allocPixels(ctable); ctable->unref(); bm.lockPixels(); const float cx = bm.width() * 0.5f; const float cy = bm.height() * 0.5f; for (int y = 0; y < bm.height(); y++) { float dy = y - cy; dy *= dy; uint8_t* p = bm.getAddr8(0, y); for (int x = 0; x < 256; x++) { float dx = x - cx; dx *= dx; float d = (dx + dy) / (cx/2); int id = (int)d; if (id > 255) { id = 255; } p[x] = id; } } bm.unlockPixels(); return bm; }
static SkBitmap make_bitmap() { SkPMColor c[256]; for (int i = 0; i < 256; i++) { c[i] = SkPackARGB32(255 - i, 0, 0, 0); } SkBitmap bm; SkColorTable* ctable = new SkColorTable(c, 256); bm.allocPixels(SkImageInfo::Make(256, 256, kIndex_8_SkColorType, kPremul_SkAlphaType), nullptr, ctable); ctable->unref(); bm.lockPixels(); const float cx = bm.width() * 0.5f; const float cy = bm.height() * 0.5f; for (int y = 0; y < bm.height(); y++) { float dy = y - cy; dy *= dy; uint8_t* p = bm.getAddr8(0, y); for (int x = 0; x < 256; x++) { float dx = x - cx; dx *= dx; float d = (dx + dy) / (cx/2); int id = (int)d; if (id > 255) { id = 255; } p[x] = id; } } bm.unlockPixels(); return bm; }
static void convert_to_index666(const SkBitmap& src, SkBitmap* dst) { SkPMColor storage[216]; SkPMColor* colors = storage; // rrr ggg bbb for (int r = 0; r < 6; r++) { int rr = conv_6_to_byte(r); for (int g = 0; g < 6; g++) { int gg = conv_6_to_byte(g); for (int b = 0; b < 6; b++) { int bb = conv_6_to_byte(b); *colors++ = SkPreMultiplyARGB(0xFF, rr, gg, bb); } } } SkColorTable* ctable = new SkColorTable(storage, 216); dst->allocPixels(SkImageInfo::Make(src.width(), src.height(), kIndex_8_SkColorType, kOpaque_SkAlphaType), NULL, ctable); ctable->unref(); SkAutoLockPixels alps(src); SkAutoLockPixels alpd(*dst); for (int y = 0; y < src.height(); y++) { const SkPMColor* srcP = src.getAddr32(0, y); uint8_t* dstP = dst->getAddr8(0, y); for (int x = src.width() - 1; x >= 0; --x) { *dstP++ = compute_666_index(*srcP++); } } }
/* Fill out buffer with the compressed format Ganesh expects from a colortable based bitmap. [palette (colortable) + indices]. At the moment Ganesh only supports 8bit version. If Ganesh allowed we others we could detect that the colortable.count is <= 16, and then repack the indices as nibbles to save RAM, but it would take more time (i.e. a lot slower than memcpy), so skipping that for now. Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big as the colortable.count says it is. */ static void build_compressed_data(void* buffer, const SkBitmap& bitmap) { SkASSERT(SkBitmap::kIndex8_Config == bitmap.config()); SkAutoLockPixels apl(bitmap); if (!bitmap.readyToDraw()) { SkASSERT(!"bitmap not ready to draw!"); return; } SkColorTable* ctable = bitmap.getColorTable(); char* dst = (char*)buffer; memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); ctable->unlockColors(false); // always skip a full 256 number of entries, even if we memcpy'd fewer dst += kGrColorTableSize; if (bitmap.width() == bitmap.rowBytes()) { memcpy(dst, bitmap.getPixels(), bitmap.getSize()); } else { // need to trim off the extra bytes per row size_t width = bitmap.width(); size_t rowBytes = bitmap.rowBytes(); const char* src = (const char*)bitmap.getPixels(); for (int y = 0; y < bitmap.height(); y++) { memcpy(dst, src, width); src += rowBytes; dst += width; } } }
virtual Result onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options&, SkPMColor ctableEntries[], int* ctableCount) override { SkMemoryStream stream(fData->data(), fData->size(), false); SkAutoTUnref<BareMemoryAllocator> allocator(SkNEW_ARGS(BareMemoryAllocator, (info, pixels, rowBytes))); fDecoder->setAllocator(allocator); fDecoder->setRequireUnpremultipliedColors(kUnpremul_SkAlphaType == info.alphaType()); SkBitmap bm; const SkImageDecoder::Result result = fDecoder->decode(&stream, &bm, info.colorType(), SkImageDecoder::kDecodePixels_Mode); if (SkImageDecoder::kFailure == result) { return kInvalidInput; } SkASSERT(info.colorType() == bm.info().colorType()); if (kIndex_8_SkColorType == info.colorType()) { SkASSERT(ctableEntries); SkColorTable* ctable = bm.getColorTable(); if (NULL == ctable) { return kInvalidConversion; } const int count = ctable->count(); memcpy(ctableEntries, ctable->readColors(), count * sizeof(SkPMColor)); *ctableCount = count; } if (SkImageDecoder::kPartialSuccess == result) { return kIncompleteInput; } return kSuccess; }
static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) { SkAutoLockPixels alp(*bm); // needed for ctable bm->setIsOpaque(isOpaque); SkColorTable* ctable = bm->getColorTable(); if (ctable) { ctable->setIsOpaque(isOpaque); } }
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jboolean isMutable, jint density, jobject parcel) { const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); if (parcel == NULL) { SkDebugf("------- writeToParcel null parcel\n"); return JNI_FALSE; } android::Parcel* p = android::parcelForJavaObject(env, parcel); p->writeInt32(isMutable); p->writeInt32(bitmap->colorType()); p->writeInt32(bitmap->alphaType()); p->writeInt32(bitmap->width()); p->writeInt32(bitmap->height()); p->writeInt32(bitmap->rowBytes()); p->writeInt32(density); if (bitmap->colorType() == kIndex_8_SkColorType) { SkColorTable* ctable = bitmap->getColorTable(); if (ctable != NULL) { int count = ctable->count(); p->writeInt32(count); memcpy(p->writeInplace(count * sizeof(SkPMColor)), ctable->lockColors(), count * sizeof(SkPMColor)); ctable->unlockColors(); } else { p->writeInt32(0); // indicate no ctable } } size_t size = bitmap->getSize(); android::Parcel::WritableBlob blob; android::status_t status = p->writeBlob(size, &blob); if (status) { doThrowRE(env, "Could not write bitmap to parcel blob."); return JNI_FALSE; } bitmap->lockPixels(); const void* pSrc = bitmap->getPixels(); if (pSrc == NULL) { memset(blob.data(), 0, size); } else { memcpy(blob.data(), pSrc, size); } bitmap->unlockPixels(); blob.release(); return JNI_TRUE; }
static void make_image(SkBitmap* bm, SkColorType ct, int configIndex) { const int width = 98; const int height = 100; const SkImageInfo info = SkImageInfo::Make(width, height, ct, kPremul_SkAlphaType); SkBitmap device; device.allocN32Pixels(width, height); SkCanvas canvas(device); SkPaint paint; paint.setAntiAlias(true); canvas.drawColor(SK_ColorRED); paint.setColor(SK_ColorBLUE); canvas.drawCircle(SkIntToScalar(width)/2, SkIntToScalar(height)/2, SkIntToScalar(width)/2, paint); switch (ct) { case kPMColor_SkColorType: bm->swap(device); break; case kRGB_565_SkColorType: { bm->allocPixels(info); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { *bm->getAddr16(x, y) = SkPixel32ToPixel16(*device.getAddr32(x, y)); } } break; } case kIndex_8_SkColorType: { SkPMColor colors[256]; for (int i = 0; i < 256; i++) { if (configIndex & 1) { colors[i] = SkPackARGB32(255-i, 0, 0, 255-i); } else { colors[i] = SkPackARGB32(0xFF, i, 0, 255-i); } } SkColorTable* ctable = new SkColorTable(colors, 256); bm->allocPixels(info, NULL, ctable); ctable->unref(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { *bm->getAddr8(x, y) = SkGetPackedR32(*device.getAddr32(x, y)); } } break; } default: SkASSERT(0); } }
/* Fill out buffer with the compressed format GL expects from a colortable based bitmap. [palette (colortable) + indices]. At the moment I always take the 8bit version, since that's what my data is. I could detect that the colortable.count is <= 16, and then repack the indices as nibbles to save RAM, but it would take more time (i.e. a lot slower than memcpy), so I'm skipping that for now. GL wants a full 256 palette entry, even though my ctable is only as big as the colortable.count says it is. I presume it is OK to leave any trailing entries uninitialized, since none of my indices should exceed ctable->count(). */ static void build_compressed_data(void* buffer, const SkBitmap& bitmap) { SkASSERT(SkBitmap::kIndex8_Config == bitmap.config()); SkColorTable* ctable = bitmap.getColorTable(); uint8_t* dst = (uint8_t*)buffer; memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); ctable->unlockColors(false); // always skip a full 256 number of entries, even if we memcpy'd fewer dst += SK_GL_SIZE_OF_PALETTE; memcpy(dst, bitmap.getPixels(), bitmap.getSize()); }
static void make_bm(SkBitmap* bm) { const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE }; SkColorTable* ctable = new SkColorTable(colors, 4); bm->setConfig(SkBitmap::kIndex8_Config, 2, 2); bm->allocPixels(ctable); ctable->unref(); *bm->getAddr8(0, 0) = 0; *bm->getAddr8(1, 0) = 1; *bm->getAddr8(0, 1) = 2; *bm->getAddr8(1, 1) = 3; }
static SkBitmap make_bitmap() { const SkPMColor c[] = { SkPackARGB32(0x80, 0x80, 0, 0) }; SkColorTable* ctable = new SkColorTable(c, SK_ARRAY_COUNT(c)); SkBitmap bm; bm.allocPixels(SkImageInfo::Make(1, 1, kIndex_8_SkColorType, kPremul_SkAlphaType), NULL, ctable); ctable->unref(); bm.lockPixels(); *bm.getAddr8(0, 0) = 0; bm.unlockPixels(); return bm; }
static void make_bm(SkBitmap* bm) { const SkPMColor colors[] = { SkPreMultiplyColor(SK_ColorRED), SkPreMultiplyColor(SK_ColorGREEN), SkPreMultiplyColor(SK_ColorBLUE), SkPreMultiplyColor(SK_ColorWHITE) }; SkColorTable* ctable = new SkColorTable(colors, 4); bm->allocPixels(SkImageInfo::Make(2, 2, kIndex_8_SkColorType, kOpaque_SkAlphaType), nullptr, ctable); ctable->unref(); *bm->getAddr8(0, 0) = 0; *bm->getAddr8(1, 0) = 1; *bm->getAddr8(0, 1) = 2; *bm->getAddr8(1, 1) = 3; }
/* Fill out buffer with the compressed format Ganesh expects from a colortable based bitmap. [palette (colortable) + indices]. At the moment Ganesh only supports 8bit version. If Ganesh allowed we others we could detect that the colortable.count is <= 16, and then repack the indices as nibbles to save RAM, but it would take more time (i.e. a lot slower than memcpy), so skipping that for now. Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big as the colortable.count says it is. */ static void build_compressed_data(void* buffer, const SkBitmap& bitmap) { SkASSERT(SkBitmap::kIndex8_Config == bitmap.config()); SkAutoLockPixels alp(bitmap); if (!bitmap.readyToDraw()) { SkDEBUGFAIL("bitmap not ready to draw!"); return; } SkColorTable* ctable = bitmap.getColorTable(); char* dst = (char*)buffer; const int count = ctable->count(); SkDstPixelInfo dstPI; dstPI.fColorType = kRGBA_8888_SkColorType; dstPI.fAlphaType = kPremul_SkAlphaType; dstPI.fPixels = buffer; dstPI.fRowBytes = count * sizeof(SkPMColor); SkSrcPixelInfo srcPI; srcPI.fColorType = kPMColor_SkColorType; srcPI.fAlphaType = kPremul_SkAlphaType; srcPI.fPixels = ctable->lockColors(); srcPI.fRowBytes = count * sizeof(SkPMColor); srcPI.convertPixelsTo(&dstPI, count, 1); ctable->unlockColors(); // always skip a full 256 number of entries, even if we memcpy'd fewer dst += kGrColorTableSize; if ((unsigned)bitmap.width() == bitmap.rowBytes()) { memcpy(dst, bitmap.getPixels(), bitmap.getSize()); } else { // need to trim off the extra bytes per row size_t width = bitmap.width(); size_t rowBytes = bitmap.rowBytes(); const char* src = (const char*)bitmap.getPixels(); for (int y = 0; y < bitmap.height(); y++) { memcpy(dst, src, width); src += rowBytes; dst += width; } } }
static SkBitmap make_bitmap() { SkBitmap bm; SkColorTable* ctable = new SkColorTable(1); SkPMColor* c = ctable->lockColors(); c[0] = SkPackARGB32(0x80, 0x80, 0, 0); ctable->unlockColors(true); bm.setConfig(SkBitmap::kIndex8_Config, 1, 1); bm.allocPixels(ctable); ctable->unref(); bm.lockPixels(); *bm.getAddr8(0, 0) = 0; bm.unlockPixels(); return bm; }
// As copy constructor is hidden in the class hierarchy, we need to call // default constructor explicitly to suppress a compiler warning. SkColorTable::SkColorTable(const SkColorTable& src) : INHERITED() { f16BitCache = NULL; fAlphaType = src.fAlphaType; int count = src.count(); fCount = SkToU16(count); fColors = reinterpret_cast<SkPMColor*>( sk_malloc_throw(count * sizeof(SkPMColor))); memcpy(fColors, src.fColors, count * sizeof(SkPMColor)); SkDEBUGCODE(fColorLockCount = 0;)
static void make_bm(SkBitmap* bm) { const SkColor colors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE }; SkPMColor colorsPM[4]; for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) { colorsPM[i] = SkPreMultiplyColor(colors[i]); } SkColorTable* ctable = new SkColorTable(colorsPM, 4); bm->setConfig(SkBitmap::kIndex8_Config, 2, 2); bm->allocPixels(ctable); ctable->unref(); *bm->getAddr8(0, 0) = 0; *bm->getAddr8(1, 0) = 1; *bm->getAddr8(0, 1) = 2; *bm->getAddr8(1, 1) = 3; }
static void make_bm(SkBitmap* bm) { const SkColor colors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE }; SkPMColor colorsPM[4]; for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) { colorsPM[i] = SkPreMultiplyColor(colors[i]); } SkColorTable* ctable = new SkColorTable(colorsPM, 4); bm->allocPixels(SkImageInfo::Make(2, 2, kIndex_8_SkColorType, kPremul_SkAlphaType), NULL, ctable); ctable->unref(); *bm->getAddr8(0, 0) = 0; *bm->getAddr8(1, 0) = 1; *bm->getAddr8(0, 1) = 2; *bm->getAddr8(1, 1) = 3; }
void drawBG(SkCanvas* canvas) { canvas->drawColor(0xFFDDDDDD); return; #if 0 SkColorTable ct; SkPMColor colors[] = { SK_ColorRED, SK_ColorBLUE }; ct.setColors(colors, 2); ct.setFlags(ct.getFlags() | SkColorTable::kColorsAreOpaque_Flag); SkBitmap bm; bm.setConfig(SkBitmap::kIndex8_Config, 20, 20, 21); bm.setColorTable(&ct); bm.allocPixels(); sk_memset16((uint16_t*)bm.getAddr8(0, 0), 0x0001, bm.rowBytes() * bm.height() / 2); #endif #if 0 SkBitmap bm; bm.setConfig(SkBitmap::kRGB_565_Config, 20, 20, 42); bm.allocPixels(); sk_memset32((uint32_t*)bm.getAddr16(0, 0), 0x0000FFFF, bm.rowBytes() * bm.height() / 4); #endif #if 1 SkBitmap bm; bm.setConfig(SkBitmap::kARGB_8888_Config, 20, 20); bm.allocPixels(); sk_memset32((uint32_t*)bm.getAddr32(0, 0), 0xFFDDDDDD, bm.rowBytes() * bm.height() / 4); #endif SkPaint paint; // SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kBilinear_FilterType, SkShader::kRepeat_TileMode); SkPoint pts[] = { 0, 0, SkIntToScalar(100), SkIntToScalar(0) }; SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; SkShader* shader = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kMirror_TileMode); paint.setShader(shader)->unref(); canvas->drawPaint(paint); }
static SkBitmap make_bitmap() { SkPMColor c[256]; for (int i = 0; i < 256; i++) { c[i] = SkPackARGB32(0xFF, i, 0, 0); } SkColorTable* ctable = new SkColorTable(c, 256); SkBitmap bm; bm.allocPixels(SkImageInfo::Make(256, 32, kIndex_8_SkColorType, kPremul_SkAlphaType), nullptr, ctable); ctable->unref(); bm.lockPixels(); for (int y = 0; y < bm.height(); y++) { uint8_t* p = bm.getAddr8(0, y); for (int x = 0; x < 256; x++) { p[x] = x; } } bm.unlockPixels(); return bm; }
/* SampleFilter = 滤镜样子 : 滤镜的作用与效果 见 百度百科 流程 : FilterView() -> make_bm() -> onDrawContent() -> draw_row() -> draw_set() -> draw_bm() */ static void make_bm(SkBitmap* bm) { const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE }; //颜色表 存储4个颜色 SkColorTable* ctable = new SkColorTable(colors, 4); //bitmap设置格式 为一个像素8位, 共2*2个像素 bm->setConfig(SkBitmap::kIndex8_Config, 2, 2); //bitmap分配实际内存, 按照以上格式 bm->allocPixels(ctable); //颜色表的引用值-1 :见到0时释放, ctable->unref(); /*获取kIndex8_Config的bitmap的 (0,0)像素内存位置, 赋值0, 为索引0的颜色.. 依次将红, 绿, 蓝, 白赋值给4个像素 : 见结果图样式. */ *bm->getAddr8(0, 0) = 0; *bm->getAddr8(1, 0) = 1; *bm->getAddr8(0, 1) = 2; *bm->getAddr8(1, 1) = 3; }
/* Fill out buffer with the compressed format Ganesh expects from a colortable based bitmap. [palette (colortable) + indices]. At the moment Ganesh only supports 8bit version. If Ganesh allowed we others we could detect that the colortable.count is <= 16, and then repack the indices as nibbles to save RAM, but it would take more time (i.e. a lot slower than memcpy), so skipping that for now. Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big as the colortable.count says it is. */ static void build_compressed_data(void* buffer, const SkBitmap& bitmap) { SkASSERT(SkBitmap::kIndex8_Config == bitmap.config()); SkAutoLockPixels alp(bitmap); if (!bitmap.readyToDraw()) { SkDEBUGFAIL("bitmap not ready to draw!"); return; } SkColorTable* ctable = bitmap.getColorTable(); char* dst = (char*)buffer; uint32_t* colorTableDst = reinterpret_cast<uint32_t*>(dst); const uint32_t* colorTableSrc = reinterpret_cast<const uint32_t*>(ctable->lockColors()); SkConvertConfig8888Pixels(colorTableDst, 0, SkCanvas::kRGBA_Premul_Config8888, colorTableSrc, 0, SkCanvas::kNative_Premul_Config8888, ctable->count(), 1); ctable->unlockColors(); // always skip a full 256 number of entries, even if we memcpy'd fewer dst += kGrColorTableSize; if ((unsigned)bitmap.width() == bitmap.rowBytes()) { memcpy(dst, bitmap.getPixels(), bitmap.getSize()); } else { // need to trim off the extra bytes per row size_t width = bitmap.width(); size_t rowBytes = bitmap.rowBytes(); const char* src = (const char*)bitmap.getPixels(); for (int y = 0; y < bitmap.height(); y++) { memcpy(dst, src, width); src += rowBytes; dst += width; } } }
static SkBitmap make_bitmap() { const int N = 1; SkPMColor c[N]; for (int i = 0; i < N; i++) { c[i] = SkPackARGB32(0x80, 0x80, 0, 0); } SkColorTable* ctable = new SkColorTable(c, N); SkBitmap bm; bm.setConfig(SkBitmap::kIndex8_Config, 1, 1); bm.allocPixels(ctable); ctable->unref(); bm.lockPixels(); for (int y = 0; y < bm.height(); y++) { uint8_t* p = bm.getAddr8(0, y); for (int x = 0; x < bm.width(); x++) { p[x] = 0; } } bm.unlockPixels(); return bm; }
// copyTo() should preserve isOpaque when it makes sense static void test_isOpaque(skiatest::Reporter* reporter, const SkBitmap& src, SkBitmap::Config dstConfig) { SkBitmap bitmap(src); SkBitmap dst; // we need the lock so that we get a valid colorTable (when available) SkAutoLockPixels alp(bitmap); SkColorTable* ctable = bitmap.getColorTable(); unsigned ctableFlags = ctable ? ctable->getFlags() : 0; if (canHaveAlpha(bitmap.config()) && canHaveAlpha(dstConfig)) { bitmap.setIsOpaque(false); if (ctable) { ctable->setFlags(ctableFlags & ~SkColorTable::kColorsAreOpaque_Flag); } REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig)); REPORTER_ASSERT(reporter, dst.config() == dstConfig); if (bitmap.isOpaque() != dst.isOpaque()) { report_opaqueness(reporter, bitmap, dst); } } bitmap.setIsOpaque(true); if (ctable) { ctable->setFlags(ctableFlags | SkColorTable::kColorsAreOpaque_Flag); } REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig)); REPORTER_ASSERT(reporter, dst.config() == dstConfig); if (bitmap.isOpaque() != dst.isOpaque()) { report_opaqueness(reporter, bitmap, dst); } if (ctable) { ctable->setFlags(ctableFlags); } }
QImage toQImage(const SkBitmap &bitmap) { QImage image; switch (bitmap.colorType()) { case kUnknown_SkColorType: break; case kAlpha_8_SkColorType: image = toQImage(bitmap, QImage::Format_Alpha8); break; case kRGB_565_SkColorType: image = toQImage(bitmap, QImage::Format_RGB16); break; case kARGB_4444_SkColorType: switch (bitmap.alphaType()) { case kUnknown_SkAlphaType: break; case kUnpremul_SkAlphaType: // not supported - treat as opaque case kOpaque_SkAlphaType: image = toQImage(bitmap, QImage::Format_RGB444); break; case kPremul_SkAlphaType: image = toQImage(bitmap, QImage::Format_ARGB4444_Premultiplied); break; } break; case kRGBA_8888_SkColorType: switch (bitmap.alphaType()) { case kUnknown_SkAlphaType: break; case kOpaque_SkAlphaType: image = toQImage(bitmap, QImage::Format_RGBX8888); break; case kPremul_SkAlphaType: image = toQImage(bitmap, QImage::Format_RGBA8888_Premultiplied); break; case kUnpremul_SkAlphaType: image = toQImage(bitmap, QImage::Format_RGBA8888); break; } break; case kBGRA_8888_SkColorType: // we are assuming little-endian arch here. switch (bitmap.alphaType()) { case kUnknown_SkAlphaType: break; case kOpaque_SkAlphaType: image = toQImage(bitmap, QImage::Format_RGB32); break; case kPremul_SkAlphaType: image = toQImage(bitmap, QImage::Format_ARGB32_Premultiplied); break; case kUnpremul_SkAlphaType: image = toQImage(bitmap, QImage::Format_ARGB32); break; } break; case kIndex_8_SkColorType: { image = toQImage(bitmap, QImage::Format_Indexed8); SkColorTable *skTable = bitmap.getColorTable(); if (skTable) { QVector<QRgb> qTable(skTable->count()); for (int i = 0; i < skTable->count(); ++i) qTable[i] = (*skTable)[i]; image.setColorTable(qTable); } break; } case kGray_8_SkColorType: image = toQImage(bitmap, QImage::Format_Grayscale8); break; } return image; }
static void TestBitmapCopy(skiatest::Reporter* reporter) { static const Pair gPairs[] = { { SkBitmap::kNo_Config, "00000000" }, { SkBitmap::kA1_Config, "01000000" }, { SkBitmap::kA8_Config, "00101110" }, { SkBitmap::kIndex8_Config, "00111110" }, { SkBitmap::kRGB_565_Config, "00101110" }, { SkBitmap::kARGB_4444_Config, "00101110" }, { SkBitmap::kARGB_8888_Config, "00101110" }, // TODO: create valid RLE bitmap to test with // { SkBitmap::kRLE_Index8_Config, "00101111" } }; const int W = 20; const int H = 33; for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) { SkBitmap src, dst; SkColorTable* ct = NULL; src.setConfig(gPairs[i].fConfig, W, H); if (SkBitmap::kIndex8_Config == src.config() || SkBitmap::kRLE_Index8_Config == src.config()) { ct = init_ctable(); } src.allocPixels(ct); ct->safeRef(); init_src(src); bool success = src.copyTo(&dst, gPairs[j].fConfig); bool expected = gPairs[i].fValid[j] != '0'; if (success != expected) { SkString str; str.printf("SkBitmap::copyTo from %s to %s. expected %s returned %s", gConfigName[i], gConfigName[j], boolStr(expected), boolStr(success)); reporter->reportFailed(str); } bool canSucceed = src.canCopyTo(gPairs[j].fConfig); if (success != canSucceed) { SkString str; str.printf("SkBitmap::copyTo from %s to %s. returned %s canCopyTo %s", gConfigName[i], gConfigName[j], boolStr(success), boolStr(canSucceed)); reporter->reportFailed(str); } if (success) { REPORTER_ASSERT(reporter, src.width() == dst.width()); REPORTER_ASSERT(reporter, src.height() == dst.height()); REPORTER_ASSERT(reporter, dst.config() == gPairs[j].fConfig); test_isOpaque(reporter, src, dst.config()); if (src.config() == dst.config()) { SkAutoLockPixels srcLock(src); SkAutoLockPixels dstLock(dst); REPORTER_ASSERT(reporter, src.readyToDraw()); REPORTER_ASSERT(reporter, dst.readyToDraw()); const char* srcP = (const char*)src.getAddr(0, 0); const char* dstP = (const char*)dst.getAddr(0, 0); REPORTER_ASSERT(reporter, srcP != dstP); REPORTER_ASSERT(reporter, !memcmp(srcP, dstP, src.getSize())); } // test extractSubset { SkBitmap subset; SkIRect r; r.set(1, 1, 2, 2); if (src.extractSubset(&subset, r)) { REPORTER_ASSERT(reporter, subset.width() == 1); REPORTER_ASSERT(reporter, subset.height() == 1); SkBitmap copy; REPORTER_ASSERT(reporter, subset.copyTo(©, subset.config())); REPORTER_ASSERT(reporter, copy.width() == 1); REPORTER_ASSERT(reporter, copy.height() == 1); REPORTER_ASSERT(reporter, copy.rowBytes() <= 4); SkAutoLockPixels alp0(subset); SkAutoLockPixels alp1(copy); // they should both have, or both not-have, a colortable bool hasCT = subset.getColorTable() != NULL; REPORTER_ASSERT(reporter, (copy.getColorTable() != NULL) == hasCT); } } } else { // dst should be unchanged from its initial state REPORTER_ASSERT(reporter, dst.config() == SkBitmap::kNo_Config); REPORTER_ASSERT(reporter, dst.width() == 0); REPORTER_ASSERT(reporter, dst.height() == 0); } } } }
bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, SkBitmap::Config prefConfig, Mode mode) { // SkAutoTrace apr("SkPNGImageDecoder::onDecode"); /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also supply the * the compiler header file version, so that we know if the application * was compiled with a compatible version of the library. */ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, NULL); // png_voidp user_error_ptr, user_error_fn, user_warning_fn); if (png_ptr == NULL) { return false; } /* Allocate/initialize the memory for image information. */ png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); return false; } PNGAutoClean autoClean(png_ptr, info_ptr); /* Set error handling if you are using the setjmp/longjmp method (this is * the normal method of doing things with libpng). REQUIRED unless you * set up your own error handlers in the png_create_read_struct() earlier. */ if (setjmp(png_jmpbuf(png_ptr))) { return false; } /* If you are using replacement read functions, instead of calling * png_init_io() here you would call: */ png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); /* where user_io_ptr is a structure you want available to the callbacks */ /* If we have already read some of the signature */ // png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); // hookup our peeker so we can see any user-chunks the caller may be interested in png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); if (this->getPeeker()) { png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk); } /* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). */ png_read_info(png_ptr, info_ptr); png_uint_32 origWidth, origHeight; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL); SkBitmap::Config config; bool hasAlpha = false; bool doDither = this->getDitherImage(); // check for sBIT chunk data, in case we should disable dithering because // our data is not truely 8bits per component if (doDither) { #if 0 SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red, info_ptr->sig_bit.green, info_ptr->sig_bit.blue, info_ptr->sig_bit.alpha); #endif // 0 seems to indicate no information available if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) && pos_le(info_ptr->sig_bit.green, SK_G16_BITS) && pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) { doDither = false; } } if (color_type == PNG_COLOR_TYPE_PALETTE) { config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha } else { png_color_16p transColor; png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &transColor); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || PNG_COLOR_TYPE_RGB_ALPHA == color_type || PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { hasAlpha = true; config = SkBitmap::kARGB_8888_Config; } else { // we get to choose the config config = prefConfig; if (config == SkBitmap::kNo_Config) { config = SkImageDecoder::GetDeviceConfig(); } if (config != SkBitmap::kRGB_565_Config && config != SkBitmap::kARGB_4444_Config) { config = SkBitmap::kARGB_8888_Config; } } } if (!this->chooseFromOneChoice(config, origWidth, origHeight)) { return false; } const int sampleSize = this->getSampleSize(); SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); decodedBitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0); if (SkImageDecoder::kDecodeBounds_Mode == mode) { return true; } // from here down we are concerned with colortables and pixels /* tell libpng to strip 16 bit/color files down to 8 bits/color */ if (bit_depth == 16) { png_set_strip_16(png_ptr); } /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ if (bit_depth < 8) { png_set_packing(png_ptr); } /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_gray_1_2_4_to_8(png_ptr); } /* Make a grayscale image into RGB. */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we // draw lots faster if we can flag the bitmap has being opaque bool reallyHasAlpha = false; SkColorTable* colorTable = NULL; if (color_type == PNG_COLOR_TYPE_PALETTE) { int num_palette; png_colorp palette; png_bytep trans; int num_trans; png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); /* BUGGY IMAGE WORKAROUND We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count which is a problem since we use the byte as an index. To work around this we grow the colortable by 1 (if its < 256) and duplicate the last color into that slot. */ int colorCount = num_palette + (num_palette < 256); colorTable = SkNEW_ARGS(SkColorTable, (colorCount)); SkPMColor* colorPtr = colorTable->lockColors(); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); hasAlpha = (num_trans > 0); } else { num_trans = 0; colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); } // check for bad images that might make us crash if (num_trans > num_palette) { num_trans = num_palette; } int index = 0; int transLessThanFF = 0; for (; index < num_trans; index++) { transLessThanFF |= (int)*trans - 0xFF; *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue); palette++; } reallyHasAlpha |= (transLessThanFF < 0); for (; index < num_palette; index++) { *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); palette++; } // see BUGGY IMAGE WORKAROUND comment above if (num_palette < 256) { *colorPtr = colorPtr[-1]; } colorTable->unlockColors(true); } SkAutoUnref aur(colorTable); if (!this->allocPixelRef(decodedBitmap, colorTable)) { delete colorTable; return false; } SkAutoLockPixels alp(*decodedBitmap); /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ // if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) // ; // png_set_swap_alpha(png_ptr); /* swap bytes of 16 bit files to least significant byte first */ // png_set_swap(png_ptr); /* Add filler (or alpha) byte (before/after each RGB triplet) */ if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) { png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); } /* Turn on interlace handling. REQUIRED if you are not using * png_read_image(). To see how to handle interlacing passes, * see the png_read_row() method below: */ const int number_passes = interlace_type != PNG_INTERLACE_NONE ? png_set_interlace_handling(png_ptr) : 1; /* Optional call to gamma correct and add the background to the palette * and update info structure. REQUIRED if you are expecting libpng to * update the palette for you (ie you selected such a transform above). */ png_read_update_info(png_ptr, info_ptr); if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { for (int i = 0; i < number_passes; i++) { for (png_uint_32 y = 0; y < origHeight; y++) { uint8_t* bmRow = decodedBitmap->getAddr8(0, y); png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); } } } else { SkScaledBitmapSampler::SrcConfig sc; int srcBytesPerPixel = 4; if (SkBitmap::kIndex8_Config == config) { sc = SkScaledBitmapSampler::kIndex; srcBytesPerPixel = 1; } else if (hasAlpha) { sc = SkScaledBitmapSampler::kRGBA; } else { sc = SkScaledBitmapSampler::kRGBX; } SkAutoMalloc storage(origWidth * srcBytesPerPixel); const int height = decodedBitmap->height(); for (int i = 0; i < number_passes; i++) { if (!sampler.begin(decodedBitmap, sc, doDither)) { return false; } uint8_t* srcRow = (uint8_t*)storage.get(); skip_src_rows(png_ptr, srcRow, sampler.srcY0()); for (int y = 0; y < height; y++) { uint8_t* tmp = srcRow; png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); reallyHasAlpha |= sampler.next(srcRow); if (y < height - 1) { skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1); } } // skip the rest of the rows (if any) png_uint_32 read = (height - 1) * sampler.srcDY() + sampler.srcY0() + 1; SkASSERT(read <= origHeight); skip_src_rows(png_ptr, srcRow, origHeight - read); } if (hasAlpha && !reallyHasAlpha) { SkDEBUGF(("Image doesn't really have alpha [%d %d]\n", origWidth, origHeight)); } } /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); decodedBitmap->setIsOpaque(!reallyHasAlpha); return true; }