static sk_sp<SkShader> make_shader0(SkIPoint* size) { SkBitmap bm; size->set(2, 2); SkPMColor color0 = SkPreMultiplyARGB(0x80, 0x80, 0xff, 0x80); SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff); bm.allocN32Pixels(size->fX, size->fY); bm.eraseColor(color0); uint32_t* pixels = (uint32_t*) bm.getPixels(); pixels[0] = pixels[2] = color0; pixels[1] = pixels[3] = color1; return SkShader::MakeBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); }
/** * Interprets c as an unpremultiplied color, and returns the * premultiplied equivalent. */ static SkPMColor premultiply_unpmcolor(SkPMColor c) { U8CPU a = SkGetPackedA32(c); U8CPU r = SkGetPackedR32(c); U8CPU g = SkGetPackedG32(c); U8CPU b = SkGetPackedB32(c); return SkPreMultiplyARGB(a, r, g, b); }
void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace) { const uint8_t* lookUpTable = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace); if (!lookUpTable) return; // FIXME: Disable color space conversions on accelerated canvases (for now). if (context()->isAccelerated() || !isSurfaceValid()) return; const SkBitmap& bitmap = m_surface->bitmap(); if (bitmap.isNull()) return; ASSERT(bitmap.colorType() == kN32_SkColorType); IntSize size = m_surface->size(); SkAutoLockPixels bitmapLock(bitmap); for (int y = 0; y < size.height(); ++y) { uint32_t* srcRow = bitmap.getAddr32(0, y); for (int x = 0; x < size.width(); ++x) { SkColor color = SkPMColorToColor(srcRow[x]); srcRow[x] = SkPreMultiplyARGB( SkColorGetA(color), lookUpTable[SkColorGetR(color)], lookUpTable[SkColorGetG(color)], lookUpTable[SkColorGetB(color)]); } } }
// Set a bitmap shader that mimics dashing by width-on, width-off. // Returns false if it could not succeed (e.g. there was an existing shader) static bool setBitmapDash(SkPaint* paint, int width) { if (width <= 0 || paint->getShader()) return false; SkColor c = paint->getColor(); SkBitmap bm; bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1); bm.allocPixels(); bm.lockPixels(); // set the ON pixel *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); // set the OFF pixel *bm.getAddr32(1, 0) = 0; bm.unlockPixels(); SkMatrix matrix; matrix.setScale(SkIntToScalar(width), SK_Scalar1); SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kClamp_TileMode); s->setLocalMatrix(matrix); paint->setShader(s)->unref(); return true; }
void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) { for (int y = rect.fTop; y < rect.fBottom; ++y) { SkPMColor* dptr = result->getAddr32(rect.fLeft, y); for (int x = rect.fLeft; x < rect.fRight; ++x) { SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0; for (int cy = 0; cy < fKernelSize.fHeight; cy++) { for (int cx = 0; cx < fKernelSize.fWidth; cx++) { SkPMColor s = PixelFetcher::fetch(src, x + cx - fTarget.fX, y + cy - fTarget.fY); SkScalar k = fKernel[cy * fKernelSize.fWidth + cx]; if (convolveAlpha) { sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k); } sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k); sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k); sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k); } } int a = convolveAlpha ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255) : 255; int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a); int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a); int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a); if (!convolveAlpha) { a = SkGetPackedA32(PixelFetcher::fetch(src, x, y)); *dptr++ = SkPreMultiplyARGB(a, r, g, b); } else { *dptr++ = SkPackARGB32(a, r, g, b); } } } }
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 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++); } } }
void skia_set_palette(const uint8_t *palette) { LOGI("skia_set_palette"); if(!g_color_table) { g_color_table.reset(new SkColorTable(256)); } SkPMColor *color_table = g_color_table->lockColors(); color_table[0] = SkPreMultiplyARGB(0, 0, 0, 0); for (int i = 1; i < 256; i++) { color_table[i] = SkPreMultiplyARGB(255, palette[i*3], palette[i*3+1], palette[i*3+2]); } g_color_table->unlockColors(true); }
static SkShader* make_shader0(SkIPoint* size) { SkBitmap bm; size->set(2, 2); bm.setConfig(SkBitmap::kARGB_8888_Config, size->fX, size->fY); SkPMColor color0 = SkPreMultiplyARGB(0x80, 0x80, 0xff, 0x80); SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff); bm.allocPixels(); bm.eraseColor(color0); bm.lockPixels(); uint32_t* pixels = (uint32_t*) bm.getPixels(); pixels[0] = pixels[2] = color0; pixels[1] = pixels[3] = color1; bm.unlockPixels(); return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); }
SkPMColor SkPreMultiplyColor(SkColor c) { unsigned a = SkColorGetA(c); unsigned r = SkColorGetR(c); unsigned g = SkColorGetG(c); unsigned b = SkColorGetB(c); return SkPreMultiplyARGB(a, r, g, b); }
/** * First, make sure that writing an 8-bit RGBA KTX file and then * reading it produces the same bitmap. */ DEF_TEST(KtxReadWrite, reporter) { // Random number generator with explicit seed for reproducibility SkRandom rand(0x1005cbad); SkBitmap bm8888; bool pixelsAllocated = bm8888.allocN32Pixels(128, 128); REPORTER_ASSERT(reporter, pixelsAllocated); uint8_t *pixels = reinterpret_cast<uint8_t*>(bm8888.getPixels()); REPORTER_ASSERT(reporter, NULL != pixels); if (NULL == pixels) { return; } uint8_t *row = pixels; for (int y = 0; y < bm8888.height(); ++y) { for (int x = 0; x < bm8888.width(); ++x) { uint8_t a = rand.nextRangeU(0, 255); uint8_t r = rand.nextRangeU(0, 255); uint8_t g = rand.nextRangeU(0, 255); uint8_t b = rand.nextRangeU(0, 255); SkPMColor &pixel = *(reinterpret_cast<SkPMColor*>(row + x*sizeof(SkPMColor))); pixel = SkPreMultiplyARGB(a, r, g, b); } row += bm8888.rowBytes(); } REPORTER_ASSERT(reporter, !(bm8888.empty())); SkAutoDataUnref encodedData(SkImageEncoder::EncodeData(bm8888, SkImageEncoder::kKTX_Type, 0)); REPORTER_ASSERT(reporter, NULL != encodedData); SkAutoTUnref<SkMemoryStream> stream(SkNEW_ARGS(SkMemoryStream, (encodedData))); REPORTER_ASSERT(reporter, NULL != stream); SkBitmap decodedBitmap; bool imageDecodeSuccess = SkImageDecoder::DecodeStream(stream, &decodedBitmap); REPORTER_ASSERT(reporter, imageDecodeSuccess); REPORTER_ASSERT(reporter, decodedBitmap.colorType() == bm8888.colorType()); REPORTER_ASSERT(reporter, decodedBitmap.alphaType() == bm8888.alphaType()); REPORTER_ASSERT(reporter, decodedBitmap.width() == bm8888.width()); REPORTER_ASSERT(reporter, decodedBitmap.height() == bm8888.height()); REPORTER_ASSERT(reporter, !(decodedBitmap.empty())); uint8_t *decodedPixels = reinterpret_cast<uint8_t*>(decodedBitmap.getPixels()); REPORTER_ASSERT(reporter, NULL != decodedPixels); REPORTER_ASSERT(reporter, decodedBitmap.getSize() == bm8888.getSize()); if (NULL == decodedPixels) { return; } REPORTER_ASSERT(reporter, memcmp(decodedPixels, pixels, decodedBitmap.getSize()) == 0); }
bool SkPopplerRasterizePDF(SkStream* pdf, SkBitmap* output) { size_t size = pdf->getLength(); SkAutoFree buffer(sk_malloc_throw(size)); pdf->read(buffer.get(), size); SkAutoTDelete<poppler::document> doc( poppler::document::load_from_raw_data((const char*)buffer.get(), size)); if (!doc.get() || doc->is_locked()) { return false; } SkAutoTDelete<poppler::page> page(doc->create_page(0)); poppler::page_renderer renderer; poppler::image image = renderer.render_page(page.get()); if (!image.is_valid() || image.format() != poppler::image::format_argb32) { return false; } int width = image.width(), height = image.height(); size_t rowSize = image.bytes_per_row(); char *imgData = image.data(); SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); if (!bitmap.allocPixels()) { return false; } bitmap.eraseColor(SK_ColorWHITE); SkPMColor* bitmapPixels = (SkPMColor*)bitmap.getPixels(); // do pixel-by-pixel copy to deal with RGBA ordering conversions for (int y = 0; y < height; y++) { char *rowData = imgData; for (int x = 0; x < width; x++) { uint8_t a = rowData[3]; uint8_t r = rowData[2]; uint8_t g = rowData[1]; uint8_t b = rowData[0]; *bitmapPixels = SkPreMultiplyARGB(a, r, g, b); bitmapPixels++; rowData += 4; } imgData += rowSize; } output->swap(bitmap); return true; }
/** * Next test is to see whether or not reading an unpremultiplied KTX file accurately * creates a premultiplied buffer... */ DEF_TEST(KtxReadUnpremul, reporter) { static const uint8_t kHalfWhiteKTX[] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, // First twelve bytes is magic 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A, // KTX identifier string 0x01, 0x02, 0x03, 0x04, // Then magic endian specifier 0x01, 0x14, 0x00, 0x00, // uint32_t fGLType; 0x01, 0x00, 0x00, 0x00, // uint32_t fGLTypeSize; 0x08, 0x19, 0x00, 0x00, // uint32_t fGLFormat; 0x58, 0x80, 0x00, 0x00, // uint32_t fGLInternalFormat; 0x08, 0x19, 0x00, 0x00, // uint32_t fGLBaseInternalFormat; 0x02, 0x00, 0x00, 0x00, // uint32_t fPixelWidth; 0x02, 0x00, 0x00, 0x00, // uint32_t fPixelHeight; 0x00, 0x00, 0x00, 0x00, // uint32_t fPixelDepth; 0x00, 0x00, 0x00, 0x00, // uint32_t fNumberOfArrayElements; 0x01, 0x00, 0x00, 0x00, // uint32_t fNumberOfFaces; 0x01, 0x00, 0x00, 0x00, // uint32_t fNumberOfMipmapLevels; 0x00, 0x00, 0x00, 0x00, // uint32_t fBytesOfKeyValueData; 0x10, 0x00, 0x00, 0x00, // image size: 2x2 image of RGBA = 4 * 4 = 16 bytes 0xFF, 0xFF, 0xFF, 0x80, // Pixel 1 0xFF, 0xFF, 0xFF, 0x80, // Pixel 2 0xFF, 0xFF, 0xFF, 0x80, // Pixel 3 0xFF, 0xFF, 0xFF, 0x80};// Pixel 4 SkAutoTDelete<SkMemoryStream> stream(new SkMemoryStream(kHalfWhiteKTX, sizeof(kHalfWhiteKTX))); REPORTER_ASSERT(reporter, stream); SkBitmap decodedBitmap; bool imageDecodeSuccess = SkImageDecoder::DecodeStream(stream, &decodedBitmap); if (!imageDecodeSuccess) { ERRORF(reporter, "failed to decode the KTX stream"); return; } REPORTER_ASSERT(reporter, decodedBitmap.colorType() == kN32_SkColorType); REPORTER_ASSERT(reporter, decodedBitmap.alphaType() == kPremul_SkAlphaType); REPORTER_ASSERT(reporter, decodedBitmap.width() == 2); REPORTER_ASSERT(reporter, decodedBitmap.height() == 2); REPORTER_ASSERT(reporter, !(decodedBitmap.empty())); uint8_t *decodedPixels = reinterpret_cast<uint8_t*>(decodedBitmap.getPixels()); REPORTER_ASSERT(reporter, decodedPixels); uint8_t *row = decodedPixels; for (int j = 0; j < decodedBitmap.height(); ++j) { for (int i = 0; i < decodedBitmap.width(); ++i) { SkPMColor pixel = *(reinterpret_cast<SkPMColor*>(row + i*sizeof(SkPMColor))); REPORTER_ASSERT(reporter, SkPreMultiplyARGB(0x80, 0xFF, 0xFF, 0xFF) == pixel); } row += decodedBitmap.rowBytes(); } }
SkPMColor SkPerlinNoiseShader::PerlinNoiseShaderContext::shade( const SkPoint& point, StitchData& stitchData) const { SkPoint newPoint; fMatrix.mapPoints(&newPoint, &point, 1); newPoint.fX = SkScalarRoundToScalar(newPoint.fX); newPoint.fY = SkScalarRoundToScalar(newPoint.fY); U8CPU rgba[4]; for (int channel = 3; channel >= 0; --channel) { rgba[channel] = SkScalarFloorToInt(255 * calculateTurbulenceValueForPoint(channel, stitchData, newPoint)); } return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]); }
void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, const SkBitmap& bitmap, const IntSize& size) { ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); int originX = sourceRect.x(); int destX = destPoint.x() + sourceRect.x(); ASSERT(destX >= 0); ASSERT(destX < size.width()); ASSERT(originX >= 0); ASSERT(originX < sourceRect.right()); int endX = destPoint.x() + sourceRect.right(); ASSERT(endX <= size.width()); int numColumns = endX - destX; int originY = sourceRect.y(); int destY = destPoint.y() + sourceRect.y(); ASSERT(destY >= 0); ASSERT(destY < size.height()); ASSERT(originY >= 0); ASSERT(originY < sourceRect.bottom()); int endY = destPoint.y() + sourceRect.bottom(); ASSERT(endY <= size.height()); int numRows = endY - destY; ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); unsigned srcBytesPerRow = 4 * source->width(); const unsigned char* srcRow = source->data()->data()->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { uint32_t* destRow = bitmap.getAddr32(destX, destY + y); for (int x = 0; x < numColumns; ++x) { const unsigned char* srcPixel = &srcRow[x * 4]; if (multiplied == Unmultiplied) destRow[x] = SkPreMultiplyARGB(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); else destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); } srcRow += srcBytesPerRow; } }
void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) { ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); int originx = sourceRect.x(); int destx = destPoint.x() + sourceRect.x(); ASSERT(destx >= 0); ASSERT(destx < m_size.width()); ASSERT(originx >= 0); ASSERT(originx < sourceRect.right()); int endx = destPoint.x() + sourceRect.right(); ASSERT(endx <= m_size.width()); int numColumns = endx - destx; int originy = sourceRect.y(); int desty = destPoint.y() + sourceRect.y(); ASSERT(desty >= 0); ASSERT(desty < m_size.height()); ASSERT(originy >= 0); ASSERT(originy < sourceRect.bottom()); int endy = destPoint.y() + sourceRect.bottom(); ASSERT(endy <= m_size.height()); int numRows = endy - desty; const SkBitmap& bitmap = *context()->platformContext()->bitmap(); ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); unsigned srcBytesPerRow = 4 * source->width(); const unsigned char* srcRow = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; for (int y = 0; y < numRows; ++y) { uint32_t* destRow = bitmap.getAddr32(destx, desty + y); for (int x = 0; x < numColumns; ++x) { const unsigned char* srcPixel = &srcRow[x * 4]; destRow[x] = SkPreMultiplyARGB(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); } srcRow += srcBytesPerRow; } }
void DragImage::dissolveToFraction(float fraction) { m_bitmap.setAlphaType(kPremul_SkAlphaType); SkAutoLockPixels lock(m_bitmap); for (int row = 0; row < m_bitmap.height(); ++row) { for (int column = 0; column < m_bitmap.width(); ++column) { uint32_t* pixel = m_bitmap.getAddr32(column, row); *pixel = SkPreMultiplyARGB( SkColorGetA(*pixel) * fraction, SkColorGetR(*pixel), SkColorGetG(*pixel), SkColorGetB(*pixel)); } } }
static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_premul( void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) { // Use the masks to decode to the destination SkPMColor* dstPtr = (SkPMColor*) dstRow; INIT_RESULT_ALPHA; for (int i = 0; i < 3*width; i += 3) { uint32_t p = srcRow[i] | (srcRow[i + 1] << 8) | srcRow[i + 2] << 16; uint8_t red = masks->getRed(p); uint8_t green = masks->getGreen(p); uint8_t blue = masks->getBlue(p); uint8_t alpha = masks->getAlpha(p); UPDATE_RESULT_ALPHA(alpha); dstPtr[i/3] = SkPreMultiplyARGB(alpha, red, green, blue); } return COMPUTE_RESULT_ALPHA; }
void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) { const SkBitmap& bitmap = *context()->platformContext()->bitmap(); if (bitmap.isNull()) return; ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); for (int y = 0; y < m_size.height(); ++y) { uint32_t* srcRow = bitmap.getAddr32(0, y); for (int x = 0; x < m_size.width(); ++x) { SkColor color = SkPMColorToColor(srcRow[x]); srcRow[x] = SkPreMultiplyARGB(SkColorGetA(color), lookUpTable[SkColorGetR(color)], lookUpTable[SkColorGetG(color)], lookUpTable[SkColorGetB(color)]); } } }
static void setBitmapDash(SkPaint* paint, int width) { SkColor c = paint->getColor(); SkBitmap bm; bm.allocN32Pixels(2, 1); bm.lockPixels(); *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); *bm.getAddr32(1, 0) = 0; bm.unlockPixels(); SkMatrix matrix; matrix.setScale(SkIntToScalar(width), SK_Scalar1); SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kClamp_TileMode, &matrix); paint->setShader(s)->unref(); }
template <bool doSwapRB, AlphaVerb doAlpha> uint32_t convert32(uint32_t c) { if (doSwapRB) { c = SkSwizzle_RB(c); } // Lucky for us, in both RGBA and BGRA, the alpha component is always in the same place, so // we can perform premul or unpremul the same way without knowing the swizzles for RGB. switch (doAlpha) { case kNothing_AlphaVerb: // no change break; case kPremul_AlphaVerb: c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); break; case kUnpremul_AlphaVerb: c = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(c); break; } return c; }
DragImageRef dissolveDragImageToFraction(DragImageRef image, float fraction) { if (!image) return 0; image->bitmap->setIsOpaque(false); image->bitmap->lockPixels(); for (int row = 0; row < image->bitmap->height(); ++row) { for (int column = 0; column < image->bitmap->width(); ++column) { uint32_t* pixel = image->bitmap->getAddr32(column, row); *pixel = SkPreMultiplyARGB(SkColorGetA(*pixel) * fraction, SkColorGetR(*pixel), SkColorGetG(*pixel), SkColorGetB(*pixel)); } } image->bitmap->unlockPixels(); return image; }
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; }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); static const int kW = 256; static const int kH = 256; static const size_t kRowBytes = sizeof(uint32_t) * kW; GrSurfaceDesc baseDesc; baseDesc.fConfig = kRGBA_8888_GrPixelConfig; baseDesc.fWidth = kW; baseDesc.fHeight = kH; const SkImageInfo ii = SkImageInfo::MakeN32Premul(kW, kH); SkAutoTMalloc<uint32_t> srcPixels(kW * kH); for (int y = 0; y < kH; ++y) { for (int x = 0; x < kW; ++x) { srcPixels.get()[y*kW+x] = SkPreMultiplyARGB(x, y, x, 0xFF); } } SkBitmap bm; bm.installPixels(ii, srcPixels.get(), kRowBytes); SkAutoTMalloc<uint32_t> read(kW * kH); // We allow more error on GPUs with lower precision shader variables. float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f; for (auto toSRGB : { false, true }) { sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii)); if (!dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } SkCanvas* dstCanvas = dst->getCanvas(); dstCanvas->clear(SK_ColorRED); dstCanvas->flush(); SkPaint gammaPaint; gammaPaint.setBlendMode(SkBlendMode::kSrc); gammaPaint.setColorFilter(toSRGB ? SkColorFilter::MakeLinearToSRGBGamma() : SkColorFilter::MakeSRGBToLinearGamma()); dstCanvas->drawBitmap(bm, 0, 0, &gammaPaint); dstCanvas->flush(); sk_memset32(read.get(), 0, kW * kH); if (!dstCanvas->readPixels(ii, read.get(), kRowBytes, 0, 0)) { ERRORF(reporter, "Error calling readPixels"); continue; } bool abort = false; // Validate that pixels were copied/transformed correctly. for (int y = 0; y < kH && !abort; ++y) { for (int x = 0; x < kW && !abort; ++x) { uint32_t r = read.get()[y * kW + x]; uint32_t s = srcPixels.get()[y * kW + x]; uint32_t expected; if (!check_gamma(s, r, toSRGB, error, &expected)) { ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x " "from src 0x%08x and mode %s. Got %08x", x, y, expected, s, toSRGB ? "ToSRGB" : "ToLinear", r); abort = true; break; } } } } }