diff options
-rw-r--r-- | gyp/core.gyp | 1 | ||||
-rw-r--r-- | gyp/tests.gyp | 1 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 31 | ||||
-rw-r--r-- | include/core/SkDevice.h | 13 | ||||
-rw-r--r-- | include/gpu/SkGpuDevice.h | 3 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 5 | ||||
-rw-r--r-- | src/core/SkConfig8888.h | 286 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 207 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 38 | ||||
-rw-r--r-- | tests/WritePixelsTest.cpp | 419 |
10 files changed, 843 insertions, 161 deletions
diff --git a/gyp/core.gyp b/gyp/core.gyp index ca07529bbb..341eb4f476 100644 --- a/gyp/core.gyp +++ b/gyp/core.gyp @@ -52,6 +52,7 @@ '../src/core/SkComposeShader.cpp', '../src/core/SkConcaveToTriangles.cpp', '../src/core/SkConcaveToTriangles.h', + '../src/core/SkConfig8888.h', '../src/core/SkCordic.cpp', '../src/core/SkCordic.h', '../src/core/SkCoreBlitters.h', diff --git a/gyp/tests.gyp b/gyp/tests.gyp index 92aba93a03..872f035f4f 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -61,6 +61,7 @@ '../tests/TestSize.cpp', '../tests/ToUnicode.cpp', '../tests/UtilsTest.cpp', + '../tests/WritePixelsTest.cpp', '../tests/Writer32Test.cpp', '../tests/XfermodeTest.cpp', ], diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index d22fbd6d3d..08bd0f027d 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -106,12 +106,16 @@ public: /////////////////////////////////////////////////////////////////////////// /** - * This enum can be used with readPixels to perform a readback into an 8888 - * format other than Skia's native format (SkPMColor). There are three byte - * orders supported: native, BGRA, and RGBA. Each has a premultiplied and - * unpremultiplied variant. Unlike the other 8888 configs, the native 8888 - * config is defined by shift values rather than byte order. The conversion - * from the native config to a byte order must consider endianness. + * This enum can be used with read/writePixels to perform a pixel ops to or + * from an 8888 config other than Skia's native config (SkPMColor). There + * are three byte orders supported: native, BGRA, and RGBA. Each has a + * premultiplied and unpremultiplied variant. + * + * Components of a 8888 pixel can be packed/unpacked from a 32bit word using + * either byte offsets or shift values. Byte offsets are endian-invariant + * while shifts are not. BGRA and RGBA configs are defined by byte + * orderings. The native config is defined by shift values (SK_A32_SHIFT, + * ..., SK_B32_SHIFT). */ enum Config8888 { /** @@ -186,13 +190,22 @@ public: /** * Similar to draw sprite, this method will copy the pixels in bitmap onto - * the device, with the top/left corner specified by (x, y). The pixel - * values in the device are completely replaced: there is no blending. + * the canvas, with the top/left corner specified by (x, y). The canvas' + * pixel values are completely replaced: there is no blending. + * + * Currently if bitmap is backed by a texture this is a no-op. This may be + * relaxed in the future. + * + * If the bitmap has config kARGB_8888_Config then the config8888 param + * will determines how the pixel valuess are intepreted. If the bitmap is + * not kARGB_8888_Config then this parameter is ignored. * * Note: If you are recording drawing commands on this canvas to * SkPicture, writePixels() is ignored! */ - void writePixels(const SkBitmap& bitmap, int x, int y); + void writePixels(const SkBitmap& bitmap, + int x, int y, + Config8888 config8888 = kNative_Premul_Config8888); /////////////////////////////////////////////////////////////////////////// diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index 6fe1c19340..215ceeef4a 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -107,13 +107,22 @@ public: const SkBitmap& accessBitmap(bool changePixels); /** - * DEPRECATED: This will be made protected once WebKit stops using it. + * DEPRECATED: This will be made protected once WebKit stops using it.
* Instead use Canvas' writePixels method. + * * Similar to draw sprite, this method will copy the pixels in bitmap onto * the device, with the top/left corner specified by (x, y). The pixel * values in the device are completely replaced: there is no blending. + * + * Currently if bitmap is backed by a texture this is a no-op. This may be + * relaxed in the future. + * + * If the bitmap has config kARGB_8888_Config then the config8888 param + * will determines how the pixel valuess are intepreted. If the bitmap is + * not kARGB_8888_Config then this parameter is ignored. */ - virtual void writePixels(const SkBitmap& bitmap, int x, int y); + virtual void writePixels(const SkBitmap& bitmap, int x, int y, + SkCanvas::Config8888 config8888); /** * Return the device's associated gpu render target, or NULL. diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index 631cc065d0..c34cd43489 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -68,7 +68,8 @@ public: // overrides from SkDevice virtual void clear(SkColor color); - virtual void writePixels(const SkBitmap& bitmap, int x, int y); + virtual void writePixels(const SkBitmap& bitmap, int x, int y, + SkCanvas::Config8888 config8888) SK_OVERRIDE; virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip, const SkClipStack&); diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index e063112e81..cd835ace89 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -580,10 +580,11 @@ bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) { } } -void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) { +void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y, + Config8888 config8888) { SkDevice* device = this->getDevice(); if (device) { - device->writePixels(bitmap, x, y); + device->writePixels(bitmap, x, y, config8888); } } diff --git a/src/core/SkConfig8888.h b/src/core/SkConfig8888.h new file mode 100644 index 0000000000..8a666e73f9 --- /dev/null +++ b/src/core/SkConfig8888.h @@ -0,0 +1,286 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "SkCanvas.h" +#include "SkColorPriv.h" + +namespace { + +/** + Copies all pixels from a bitmap to a dst ptr with a given rowBytes and + Config8888. The bitmap must have kARGB_8888_Config. + */ +inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels, + size_t dstRowBytes, + SkCanvas::Config8888 dstConfig8888, + const SkBitmap& srcBmp); + +/** + * Copies all pixels in a bitmap to a dst ptr with row bytes. The src bitmap + * is assumed to have pixels and be kARGB_8888_Config. No conversion is applied + */ +inline void SkCopyARGB8888BitmapTo(uint32_t* dstPixels, + size_t dstRowBytes, + const SkBitmap& srcBmp); + +/** + Copies over all pixels in a bitmap from a src ptr with a given rowBytes and + Config8888. The bitmap must have pixels and be kARGB_8888_Config. + */ +inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp, + const uint32_t* srcPixels, + size_t srcRowBytes, + SkCanvas::Config8888 srcConfig8888); + +} + +/////////////////////////////////////////////////////////////////////////////// +// Implementation + +namespace { + +template <int A_IDX, int R_IDX, int G_IDX, int B_IDX> +inline uint32_t pack_config8888(uint32_t a, uint32_t r, + uint32_t g, uint32_t b) { +#ifdef SK_CPU_LENDIAN + return (a << (A_IDX * 8)) | (r << (R_IDX * 8)) | + (g << (G_IDX * 8)) | (b << (B_IDX * 8)); +#else + return (a << ((3-A_IDX) * 8)) | (r << ((3-R_IDX) * 8)) | + (g << ((3-G_IDX) * 8)) | (b << ((3-B_IDX) * 8)); +#endif +} + +template <int A_IDX, int R_IDX, int G_IDX, int B_IDX> +inline void unpack_config8888(uint32_t color, + uint32_t* a, uint32_t* r, + uint32_t* g, uint32_t* b) { +#ifdef SK_CPU_LENDIAN + *a = (color >> (A_IDX * 8)) & 0xff; + *r = (color >> (R_IDX * 8)) & 0xff; + *g = (color >> (G_IDX * 8)) & 0xff; + *b = (color >> (B_IDX * 8)) & 0xff; +#else + *a = (color >> ((3 - A_IDX) * 8)) & 0xff; + *r = (color >> ((3 - R_IDX) * 8)) & 0xff; + *g = (color >> ((3 - G_IDX) * 8)) & 0xff; + *b = (color >> ((3 - B_IDX) * 8)) & 0xff; +#endif +} + +template <bool UNPM, int A_IDX, int R_IDX, int G_IDX, int B_IDX> +inline void bitmap_copy_to_config8888(uint32_t* dstPixels, + size_t dstRowBytes, + const SkBitmap& srcBmp) { + SkASSERT(SkBitmap::kARGB_8888_Config == srcBmp.config()); + SkAutoLockPixels alp(srcBmp); + int w = srcBmp.width(); + int h = srcBmp.height(); + size_t srcRowBytes = srcBmp.rowBytes(); + + intptr_t src = reinterpret_cast<intptr_t>(srcBmp.getPixels()); + intptr_t dst = reinterpret_cast<intptr_t>(dstPixels); + + for (int y = 0; y < h; ++y) { + const SkPMColor* srcRow = reinterpret_cast<SkPMColor*>(src); + uint32_t* dstRow = reinterpret_cast<uint32_t*>(dst); + for (int x = 0; x < w; ++x) { + SkPMColor pmcolor = srcRow[x]; + if (UNPM) { + U8CPU a, r, g, b; + a = SkGetPackedA32(pmcolor); + if (a) { + // We're doing the explicit divide to match WebKit layout + // test expectations. We can modify and rebaseline if there + // it can be shown that there is a more performant way to + // unpremul. + r = SkGetPackedR32(pmcolor) * 0xff / a; + g = SkGetPackedG32(pmcolor) * 0xff / a; + b = SkGetPackedB32(pmcolor) * 0xff / a; + dstRow[x] = pack_config8888<A_IDX, R_IDX, + G_IDX, B_IDX>(a, r, g, b); + } else { + dstRow[x] = 0; + } + } else { + dstRow[x] = pack_config8888<A_IDX, R_IDX, + G_IDX, B_IDX>( + SkGetPackedA32(pmcolor), + SkGetPackedR32(pmcolor), + SkGetPackedG32(pmcolor), + SkGetPackedB32(pmcolor)); + } + } + dst += dstRowBytes; + src += srcRowBytes; + } +} + +template <bool PM, int A_IDX, int R_IDX, int G_IDX, int B_IDX> +inline void config8888_copy_to_bitmap(const SkBitmap& dstBmp, + const uint32_t* srcPixels, + size_t srcRowBytes) { + SkASSERT(SkBitmap::kARGB_8888_Config == dstBmp.config()); + SkAutoLockPixels alp(dstBmp); + int w = dstBmp.width(); + int h = dstBmp.height(); + size_t dstRowBytes = dstBmp.rowBytes(); + + intptr_t src = reinterpret_cast<intptr_t>(srcPixels); + intptr_t dst = reinterpret_cast<intptr_t>(dstBmp.getPixels()); + + for (int y = 0; y < h; ++y) { + const uint32_t* srcRow = reinterpret_cast<uint32_t*>(src); + SkPMColor* dstRow = reinterpret_cast<SkPMColor*>(dst); + for (int x = 0; x < w; ++x) { + uint32_t c8888 = srcRow[x]; + uint32_t a, r, g, b; + unpack_config8888<A_IDX, R_IDX, G_IDX, B_IDX>(c8888, &a, &r, + &g, &b); + if (PM) { + // This matches WebKit's conversion which we are replacing. + // We can consider alternative rounding rules for performance. + r = SkMulDiv255Ceiling(r, a); + g = SkMulDiv255Ceiling(g, a); + b = SkMulDiv255Ceiling(b, a); + } + dstRow[x] = SkPackARGB32(a, r, g, b); + } + src += srcRowBytes; + dst += dstRowBytes; + } +} + +#ifdef SK_CPU_LENDIAN + static const int SK_NATIVE_A_IDX = SK_A32_SHIFT / 8; + static const int SK_NATIVE_R_IDX = SK_R32_SHIFT / 8; + static const int SK_NATIVE_G_IDX = SK_G32_SHIFT / 8; + static const int SK_NATIVE_B_IDX = SK_B32_SHIFT / 8; +#else + static const int SK_NATIVE_A_IDX = 3 - (SK_A32_SHIFT / 8); + static const int SK_NATIVE_R_IDX = 3 - (SK_R32_SHIFT / 8); + static const int SK_NATIVE_G_IDX = 3 - (SK_G32_SHIFT / 8); + static const int SK_NATIVE_B_IDX = 3 - (SK_B32_SHIFT / 8); +#endif + +inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels, + size_t dstRowBytes, + SkCanvas::Config8888 dstConfig8888, + const SkBitmap& srcBmp) { + switch (dstConfig8888) { + case SkCanvas::kNative_Premul_Config8888: + bitmap_copy_to_config8888<false, + SK_NATIVE_A_IDX, SK_NATIVE_R_IDX, + SK_NATIVE_G_IDX, SK_NATIVE_B_IDX>( + dstPixels, + dstRowBytes, + srcBmp); + break; + case SkCanvas::kNative_Unpremul_Config8888: + bitmap_copy_to_config8888<true, + SK_NATIVE_A_IDX, SK_NATIVE_R_IDX, + SK_NATIVE_G_IDX, SK_NATIVE_B_IDX>( + dstPixels, + dstRowBytes, + srcBmp); + break; + case SkCanvas::kBGRA_Premul_Config8888: + bitmap_copy_to_config8888<false, 3, 2, 1, 0> ( + dstPixels, dstRowBytes, srcBmp); + break; + case SkCanvas::kBGRA_Unpremul_Config8888: + bitmap_copy_to_config8888<true, 3, 2, 1, 0> ( + dstPixels, dstRowBytes, srcBmp); + break; + case SkCanvas::kRGBA_Premul_Config8888: + bitmap_copy_to_config8888<false, 3, 0, 1, 2> ( + dstPixels, dstRowBytes, srcBmp); + break; + case SkCanvas::kRGBA_Unpremul_Config8888: + bitmap_copy_to_config8888<true, 3, 0, 1, 2> ( + dstPixels, dstRowBytes, srcBmp); + break; + default: + SkASSERT(false && "unexpected Config8888"); + break; + } +} + +inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp, + const uint32_t* srcPixels, + size_t srcRowBytes, + SkCanvas::Config8888 srcConfig8888) { + switch (srcConfig8888) { + case SkCanvas::kNative_Premul_Config8888: + config8888_copy_to_bitmap<false, + SK_NATIVE_A_IDX, SK_NATIVE_R_IDX, + SK_NATIVE_G_IDX, SK_NATIVE_B_IDX>( + dstBmp, + srcPixels, + srcRowBytes); + break; + case SkCanvas::kNative_Unpremul_Config8888: + config8888_copy_to_bitmap<true, + SK_NATIVE_A_IDX, SK_NATIVE_R_IDX, + SK_NATIVE_G_IDX, SK_NATIVE_B_IDX>( + dstBmp, + srcPixels, + srcRowBytes); + break; + case SkCanvas::kBGRA_Premul_Config8888: + config8888_copy_to_bitmap<false, 3, 2, 1, 0> ( + dstBmp, srcPixels, srcRowBytes); + break; + case SkCanvas::kBGRA_Unpremul_Config8888: + config8888_copy_to_bitmap<true, 3, 2, 1, 0> ( + dstBmp, srcPixels, srcRowBytes); + break; + case SkCanvas::kRGBA_Premul_Config8888: + config8888_copy_to_bitmap<false, 3, 0, 1, 2> ( + dstBmp, srcPixels, srcRowBytes); + break; + case SkCanvas::kRGBA_Unpremul_Config8888: + config8888_copy_to_bitmap<true, 3, 0, 1, 2> ( + dstBmp, srcPixels, srcRowBytes); + break; + default: + SkASSERT(false && "unexpected Config8888"); + break; + } +} + +inline void SkCopyARGB8888BitmapTo(uint32_t* dstPixels, + size_t dstRowBytes, + const SkBitmap& srcBmp) { + SkASSERT(SkBitmap::kARGB_8888_Config == srcBmp.config()); + + SkAutoLockPixels alp(srcBmp); + + int w = srcBmp.width(); + int h = srcBmp.height(); + size_t srcRowBytes = srcBmp.rowBytes(); + + size_t tightRowBytes = w * 4; + + char* src = reinterpret_cast<char*>(srcBmp.getPixels()); + char* dst = reinterpret_cast<char*>(dstPixels); + + if (tightRowBytes == srcRowBytes && + tightRowBytes == dstRowBytes) { + memcpy(dst, src, tightRowBytes * h); + } else { + for (int y = 0; y < h; ++y) { + memcpy(dst, src, tightRowBytes); + dst += dstRowBytes; + src += srcRowBytes; + } + } +} + +} diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index a66f2a9872..dd2301d578 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -159,10 +159,6 @@ bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y, const SkCanvas::Config8888 SkDevice::kPMColorAlias = (SkCanvas::Config8888) -1; #endif - static const int NATIVE_A_IDX = SK_A32_SHIFT / 8; - static const int NATIVE_R_IDX = SK_R32_SHIFT / 8; - static const int NATIVE_G_IDX = SK_G32_SHIFT / 8; - static const int NATIVE_B_IDX = SK_B32_SHIFT / 8; #else #if 0 == SK_A32_SHIFT && 8 == SK_R32_SHIFT && \ 16 == SK_G32_SHIFT && 24 == SK_B32_SHIFT @@ -176,105 +172,9 @@ bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y, const SkCanvas::Config8888 SkDevice::kPMColorAlias = (SkCanvas::Config8888) -1; #endif - static const int NATIVE_A_IDX = 3 - (SK_A32_SHIFT / 8); - static const int NATIVE_R_IDX = 3 - (SK_R32_SHIFT / 8); - static const int NATIVE_G_IDX = 3 - (SK_G32_SHIFT / 8); - static const int NATIVE_B_IDX = 3 - (SK_B32_SHIFT / 8); #endif -#include <SkColorPriv.h> - -namespace { - -template <int A_IDX, int R_IDX, int G_IDX, int B_IDX> -inline uint32_t pack_config8888(uint32_t a, uint32_t r, - uint32_t g, uint32_t b) { -#ifdef SK_CPU_LENDIAN - return (a << (A_IDX * 8)) | (r << (R_IDX * 8)) | - (g << (G_IDX * 8)) | (b << (B_IDX * 8)); -#else - return (a << ((3-A_IDX) * 8)) | (r << ((3-R_IDX) * 8)) | - (g << ((3-G_IDX) * 8)) | (b << ((3-B_IDX) * 8)); -#endif -} - -template <bool UNPM, int A_IDX, int R_IDX, int G_IDX, int B_IDX> -inline void bitmap_copy_to_config8888(const SkBitmap& srcBmp, - uint32_t* dstPixels, - size_t dstRowBytes) { - SkASSERT(SkBitmap::kARGB_8888_Config == srcBmp.config()); - SkAutoLockPixels alp(srcBmp); - int w = srcBmp.width(); - int h = srcBmp.height(); - size_t srcRowBytes = srcBmp.rowBytes(); - - intptr_t src = reinterpret_cast<intptr_t>(srcBmp.getPixels()); - intptr_t dst = reinterpret_cast<intptr_t>(dstPixels); - - for (int y = 0; y < h; ++y) { - const SkPMColor* srcRow = reinterpret_cast<SkPMColor*>(src); - uint32_t* dstRow = reinterpret_cast<uint32_t*>(dst); - for (int x = 0; x < w; ++x) { - SkPMColor pmcolor = srcRow[x]; - if (UNPM) { - U8CPU a, r, g, b; - a = SkGetPackedA32(pmcolor); - if (a) { - // We're doing the explicit divide to match WebKit layout - // test expectations. We can modify and rebaseline if there - // it can be shown that there is a more performant way to - // unpremul. - r = SkGetPackedR32(pmcolor) * 0xff / a; - g = SkGetPackedG32(pmcolor) * 0xff / a; - b = SkGetPackedB32(pmcolor) * 0xff / a; - dstRow[x] = pack_config8888<A_IDX, R_IDX, - G_IDX, B_IDX>(a, r, g, b); - } else { - dstRow[x] = 0; - } - } else { - dstRow[x] = pack_config8888<A_IDX, R_IDX, - G_IDX, B_IDX>( - SkGetPackedA32(pmcolor), - SkGetPackedR32(pmcolor), - SkGetPackedG32(pmcolor), - SkGetPackedB32(pmcolor)); - } - } - dst += dstRowBytes; - src += srcRowBytes; - } -} - -inline void bitmap_copy_to_native(const SkBitmap& srcBmp, - uint32_t* dstPixels, - size_t dstRowBytes) { - SkASSERT(SkBitmap::kARGB_8888_Config == srcBmp.config()); - - SkAutoLockPixels alp(srcBmp); - - int w = srcBmp.width(); - int h = srcBmp.height(); - size_t srcRowBytes = srcBmp.rowBytes(); - - size_t tightRowBytes = w * 4; - - char* src = reinterpret_cast<char*>(srcBmp.getPixels()); - char* dst = reinterpret_cast<char*>(dstPixels); - - if (tightRowBytes == srcRowBytes && - tightRowBytes == dstRowBytes) { - memcpy(dst, src, tightRowBytes * h); - } else { - for (int y = 0; y < h; ++y) { - memcpy(dst, src, tightRowBytes); - dst += dstRowBytes; - src += srcRowBytes; - } - } -} - -} +#include <SkConfig8888.h> bool SkDevice::onReadPixels(const SkBitmap& bitmap, int x, int y, @@ -299,55 +199,78 @@ bool SkDevice::onReadPixels(const SkBitmap& bitmap, uint32_t* bmpPixels = reinterpret_cast<uint32_t*>(bitmap.getPixels()); if ((SkCanvas::kNative_Premul_Config8888 == config8888 || kPMColorAlias == config8888)) { - bitmap_copy_to_native(subset, bmpPixels, bitmap.rowBytes()); + SkCopyARGB8888BitmapTo(bmpPixels, bitmap.rowBytes(), subset); } else { - switch (config8888) { - case SkCanvas::kNative_Premul_Config8888: - bitmap_copy_to_config8888<false, - NATIVE_A_IDX, NATIVE_R_IDX, - NATIVE_G_IDX, NATIVE_B_IDX>( - subset, - bmpPixels, - bitmap.rowBytes()); - break; - case SkCanvas::kNative_Unpremul_Config8888: - bitmap_copy_to_config8888<true, - NATIVE_A_IDX, NATIVE_R_IDX, - NATIVE_G_IDX, NATIVE_B_IDX>( - subset, - bmpPixels, - bitmap.rowBytes()); - break; - case SkCanvas::kBGRA_Premul_Config8888: - bitmap_copy_to_config8888<false, 3, 2, 1, 0> ( - subset, bmpPixels, bitmap.rowBytes()); - break; - case SkCanvas::kBGRA_Unpremul_Config8888: - bitmap_copy_to_config8888<true, 3, 2, 1, 0> ( - subset, bmpPixels, bitmap.rowBytes()); - break; - case SkCanvas::kRGBA_Premul_Config8888: - bitmap_copy_to_config8888<false, 3, 0, 1, 2> ( - subset, bmpPixels, bitmap.rowBytes()); - break; - case SkCanvas::kRGBA_Unpremul_Config8888: - bitmap_copy_to_config8888<true, 3, 0, 1, 2> ( - subset, bmpPixels, bitmap.rowBytes()); - break; - default: - SkASSERT(false && "unexpected Config8888"); - break; - } + SkCopyBitmapToConfig8888(bmpPixels, + bitmap.rowBytes(), + config8888, + subset); } return true; } -void SkDevice::writePixels(const SkBitmap& bitmap, int x, int y) { +void SkDevice::writePixels(const SkBitmap& bitmap, + int x, int y, + SkCanvas::Config8888 config8888) { + if (bitmap.isNull() || bitmap.getTexture()) { + return; + } + const SkBitmap* sprite = &bitmap; + // check whether we have to handle a config8888 that doesn't match SkPMColor + if (SkBitmap::kARGB_8888_Config == bitmap.config() && + SkCanvas::kNative_Premul_Config8888 != config8888 && + kPMColorAlias != config8888) { + + // We're going to have to convert from a config8888 to the native config + // First we clip to the device bounds. + SkBitmap dstBmp = this->accessBitmap(true); + SkIRect spriteRect = SkIRect::MakeXYWH(x, y, + bitmap.width(), bitmap.height()); + SkIRect devRect = SkIRect::MakeWH(dstBmp.width(), dstBmp.height()); + if (!spriteRect.intersect(devRect)) { + return; + } + + // write directly to the device if it has pixels and is SkPMColor + bool drawSprite; + if (SkBitmap::kARGB_8888_Config == dstBmp.config() && !dstBmp.isNull()) { + // we can write directly to the dst when doing the conversion + dstBmp.extractSubset(&dstBmp, spriteRect); + drawSprite = false; + } else { + // we convert to a temporary bitmap and draw that as a sprite + dstBmp.setConfig(SkBitmap::kARGB_8888_Config, + spriteRect.width(), + spriteRect.height()); + if (!dstBmp.allocPixels()) { + return; + } + drawSprite = true; + } + + // copy pixels to dstBmp and convert from config8888 to native config. + SkAutoLockPixels alp(bitmap); + uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x, + spriteRect.fTop - y); + SkCopyConfig8888ToBitmap(dstBmp, + srcPixels, + bitmap.rowBytes(), + config8888); + + if (drawSprite) { + // we've clipped the sprite when we made a copy + x = spriteRect.fLeft; + y = spriteRect.fTop; + sprite = &dstBmp; + } else { + return; + } + } + SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); - SkCanvas canvas(this); - canvas.drawSprite(bitmap, x, y, &paint); + canvas.drawSprite(*sprite, x, y, &paint); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 64d74ee8ab..7230516316 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -299,20 +299,48 @@ bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap, bitmap.rowBytes()); } -void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) { +// This can be removed when temporary code in writePixels is removed +#include "SkConfig8888.h" + +void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y, + SkCanvas::Config8888 config8888) { SkAutoLockPixels alp(bitmap); if (!bitmap.readyToDraw()) { return; } - GrPixelConfig config = SkGr::BitmapConfig2PixelConfig(bitmap.config(), - bitmap.isOpaque()); + + GrPixelConfig config; + if (SkBitmap::kARGB_8888_Config == bitmap.config()) { + config = config8888_to_gr_config(config8888); + } else { + config= SkGr::BitmapConfig2PixelConfig(bitmap.config(), + bitmap.isOpaque()); + } + + // Temporary until we add support for drawing from an unpremul config in + // GrContext + const SkBitmap* src = &bitmap; + SkBitmap tmp; + if (GrPixelConfigIsUnpremultiplied(config)) { + config = kSkia8888_PM_GrPixelConfig; + tmp.setConfig(SkBitmap::kARGB_8888_Config, + bitmap.width(), bitmap.height()); + if (!tmp.allocPixels()) { + return; + } + SkAutoLockPixels alp(bitmap); + uint32_t* pixels = reinterpret_cast<uint32_t*>(bitmap.getPixels()); + SkCopyConfig8888ToBitmap(tmp, pixels, bitmap.rowBytes(), config8888); + src = &tmp; + } + fContext->setRenderTarget(fRenderTarget); // we aren't setting the clip or matrix, so mark as dirty // we don't need to set them for this call and don't have them anyway fNeedPrepareRenderTarget = true; - fContext->writePixels(x, y, bitmap.width(), bitmap.height(), - config, bitmap.getPixels(), bitmap.rowBytes()); + fContext->writePixels(x, y, src->width(), src->height(), + config, src->getPixels(), src->rowBytes()); } /////////////////////////////////////////////////////////////////////////////// diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp new file mode 100644 index 0000000000..8244fb17bf --- /dev/null +++ b/tests/WritePixelsTest.cpp @@ -0,0 +1,419 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkCanvas.h" +#include "SkRegion.h" +#include "SkGpuDevice.h" + +static const int DEV_W = 100, DEV_H = 100; +static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); +static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, + DEV_H * SK_Scalar1); +static const U8CPU DEV_PAD = 0xee; + +namespace { +SkPMColor getCanvasColor(int x, int y) { + SkASSERT(x >= 0 && x < DEV_W); + SkASSERT(y >= 0 && y < DEV_H); + + U8CPU r = x; + U8CPU g = y; + U8CPU b = 0xc; + + U8CPU a = 0xff; + switch ((x+y) % 5) { + case 0: + a = 0xff; + break; + case 1: + a = 0x80; + break; + case 2: + a = 0xCC; + break; + case 4: + a = 0x01; + break; + case 3: + a = 0x00; + break; + } + return SkPremultiplyARGBInline(a, r, g, b); +} + +bool config8888IsPremul(SkCanvas::Config8888 config8888) { + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + case SkCanvas::kBGRA_Premul_Config8888: + case SkCanvas::kRGBA_Premul_Config8888: + return true; + case SkCanvas::kNative_Unpremul_Config8888: + case SkCanvas::kBGRA_Unpremul_Config8888: + case SkCanvas::kRGBA_Unpremul_Config8888: + return false; + default: + SkASSERT(0); + return false; + } +} + +// assumes any premu/.unpremul has been applied +uint32_t packConfig8888(SkCanvas::Config8888 config8888, + U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + uint32_t r32; + uint8_t* result = reinterpret_cast<uint8_t*>(&r32); + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + case SkCanvas::kNative_Unpremul_Config8888: + r32 = SkPackARGB32NoCheck(a, r, g, b); + break; + case SkCanvas::kBGRA_Premul_Config8888: + case SkCanvas::kBGRA_Unpremul_Config8888: + result[0] = b; + result[1] = g; + result[2] = r; + result[3] = a; + break; + case SkCanvas::kRGBA_Premul_Config8888: + case SkCanvas::kRGBA_Unpremul_Config8888: + result[0] = r; + result[1] = g; + result[2] = b; + result[3] = a; + break; + default: + SkASSERT(0); + return 0; + } + return r32; +} + +uint32_t getBitmapColor(int x, int y, int w, int h, SkCanvas::Config8888 config8888) { + int n = y * w + x; + U8CPU b = n & 0xff; + U8CPU g = (n >> 8) & 0xff; + U8CPU r = (n >> 16) & 0xff; + U8CPU a; + switch ((x+y) % 5) { + case 4: + a = 0xff; + break; + case 3: + a = 0x80; + break; + case 2: + a = 0xCC; + break; + case 1: + a = 0x01; + break; + case 0: + a = 0x00; + break; + } + if (config8888IsPremul(config8888)) { + r = SkMulDiv255Ceiling(r, a); + g = SkMulDiv255Ceiling(g, a); + b = SkMulDiv255Ceiling(b, a); + } + return packConfig8888(config8888, a, r, g , b); +} + +void fillCanvas(SkCanvas* canvas) { + static SkBitmap bmp; + if (bmp.isNull()) { + bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H); + bool alloc = bmp.allocPixels(); + SkASSERT(alloc); + SkAutoLockPixels alp(bmp); + intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels()); + for (int y = 0; y < DEV_H; ++y) { + for (int x = 0; x < DEV_W; ++x) { + SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel()); + *pixel = getCanvasColor(x, y); + } + } + } + canvas->save(); + canvas->setMatrix(SkMatrix::I()); + canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op); + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + canvas->drawBitmap(bmp, 0, 0, &paint); + canvas->restore(); +} + +SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888, + uint32_t color, + bool* premul) { + const uint8_t* c = reinterpret_cast<uint8_t*>(&color); + U8CPU a,r,g,b; + *premul = false; + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + return color; + case SkCanvas::kNative_Unpremul_Config8888: + *premul = true; + a = SkGetPackedA32(color); + r = SkGetPackedR32(color); + g = SkGetPackedG32(color); + b = SkGetPackedB32(color); + break; + case SkCanvas::kBGRA_Unpremul_Config8888: + *premul = true; // fallthru + case SkCanvas::kBGRA_Premul_Config8888: + a = static_cast<U8CPU>(c[3]); + r = static_cast<U8CPU>(c[2]); + g = static_cast<U8CPU>(c[1]); + b = static_cast<U8CPU>(c[0]); + break; + case SkCanvas::kRGBA_Unpremul_Config8888: + *premul = true; // fallthru + case SkCanvas::kRGBA_Premul_Config8888: + a = static_cast<U8CPU>(c[3]); + r = static_cast<U8CPU>(c[0]); + g = static_cast<U8CPU>(c[1]); + b = static_cast<U8CPU>(c[2]); + break; + default: + GrCrash("Unexpected Config8888"); + } + if (*premul) { + r = SkMulDiv255Ceiling(r, a); + g = SkMulDiv255Ceiling(g, a); + b = SkMulDiv255Ceiling(b, a); + } + return SkPackARGB32(a, r, g, b); +} + +bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { + if (!didPremulConversion) { + return a == b; + } + int32_t aA = static_cast<int32_t>(SkGetPackedA32(a)); + int32_t aR = static_cast<int32_t>(SkGetPackedR32(a)); + int32_t aG = static_cast<int32_t>(SkGetPackedG32(a)); + int32_t aB = SkGetPackedB32(a); + + int32_t bA = static_cast<int32_t>(SkGetPackedA32(b)); + int32_t bR = static_cast<int32_t>(SkGetPackedR32(b)); + int32_t bG = static_cast<int32_t>(SkGetPackedG32(b)); + int32_t bB = static_cast<int32_t>(SkGetPackedB32(b)); + + return aA == bA && + SkAbs32(aR - bR) <= 1 && + SkAbs32(aG - bG) <= 1 && + SkAbs32(aB - bB) <= 1; +} + +bool checkWrite(skiatest::Reporter* reporter, + SkCanvas* canvas, + const SkBitmap& bitmap, + int writeX, int writeY, + SkCanvas::Config8888 config8888) { + SkDevice* dev = canvas->getDevice(); + if (!dev) { + return false; + } + SkBitmap devBmp = dev->accessBitmap(false); + if (devBmp.width() != DEV_W || + devBmp.height() != DEV_H || + devBmp.config() != SkBitmap::kARGB_8888_Config || + devBmp.isNull()) { + return false; + } + SkAutoLockPixels alp(devBmp); + + intptr_t canvasPixels = reinterpret_cast<intptr_t>(devBmp.getPixels()); + size_t canvasRowBytes = devBmp.rowBytes(); + SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height()); + bool success = true; + for (int cy = 0; cy < DEV_H; ++cy) { + const SkPMColor* canvasRow = reinterpret_cast<const SkPMColor*>(canvasPixels); + for (int cx = 0; cx < DEV_W; ++cx) { + SkPMColor canvasPixel = canvasRow[cx]; + if (writeRect.contains(cx, cy)) { + int bx = cx - writeX; + int by = cy - writeY; + uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(), bitmap.height(), config8888); + bool mul; + SkPMColor bmpPMColor = convertConfig8888ToPMColor(config8888, bmpColor8888, &mul); + bool check; + // do we need fuzzy test? + REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul)); + if (!check) { + success = false; + } + } else { + bool check; + SkPMColor testColor = getCanvasColor(cx, cy); + REPORTER_ASSERT(reporter, check = (canvasPixel == testColor)); + if (!check) { + success = false; + } + } + } + if (cy != DEV_H -1) { + const char* pad = reinterpret_cast<const char*>(canvasPixels + 4 * DEV_W); + for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) { + bool check; + REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD))); + if (!check) { + success = false; + } + } + } + canvasPixels += canvasRowBytes; + } + + return success; +} + +enum DevType { + kRaster_DevType, + kGpu_DevType, +}; + +struct CanvasConfig { + DevType fDevType; + bool fTightRowBytes; +}; + +static const CanvasConfig gCanvasConfigs[] = { + {kRaster_DevType, true}, + {kRaster_DevType, false}, + {kGpu_DevType, true}, // row bytes has no meaning on gpu devices +}; + +bool setupCanvas(SkCanvas* canvas, const CanvasConfig& c, GrContext* grCtx) { + switch (c.fDevType) { + case kRaster_DevType: { + SkBitmap bmp; + size_t rowBytes = c.fTightRowBytes ? 0 : 4 * DEV_W + 100; + bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H, rowBytes); + if (!bmp.allocPixels()) { + return false; + } + // if rowBytes isn't tight then set the padding to a known value + if (rowBytes) { + SkAutoLockPixels alp(bmp); + memset(bmp.getPixels(), DEV_PAD, bmp.getSafeSize()); + } + canvas->setDevice(new SkDevice(bmp))->unref(); + } break; + case kGpu_DevType: + canvas->setDevice(new SkGpuDevice(grCtx, + SkBitmap::kARGB_8888_Config, + DEV_W, DEV_H))->unref(); + break; + } + return true; +} + +bool setupBitmap(SkBitmap* bitmap, + SkCanvas::Config8888 config8888, + int w, int h, + bool tightRowBytes) { + size_t rowBytes = tightRowBytes ? 0 : 4 * w + 60; + bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes); + if (!bitmap->allocPixels()) { + return false; + } + SkAutoLockPixels alp(*bitmap); + intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels()); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + uint32_t* pixel = reinterpret_cast<uint32_t*>(pixels + y * bitmap->rowBytes() + x * 4); + *pixel = getBitmapColor(x, y, w, h, config8888); + } + } + return true; +} + +void WritePixelsTest(skiatest::Reporter* reporter, GrContext* context) { + SkCanvas canvas; + + const SkIRect testRects[] = { + // entire thing + DEV_RECT, + // larger on all sides + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10), + // fully contained + SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4), + // outside top left + SkIRect::MakeLTRB(-10, -10, -1, -1), + // touching top left corner + SkIRect::MakeLTRB(-10, -10, 0, 0), + // overlapping top left corner + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4), + // overlapping top left and top right corners + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4), + // touching entire top edge + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0), + // overlapping top right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4), + // contained in x, overlapping top edge + SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4), + // outside top right corner + SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1), + // touching top right corner + SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0), + // overlapping top left and bottom left corners + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10), + // overlapping bottom left corner + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10), + // contained in y, overlapping left edge + SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4), + // outside bottom left corner + SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10), + // touching bottom left corner + SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10), + // overlapping bottom left and bottom right corners + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10), + // overlapping bottom right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // overlapping top right and bottom right corners + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) { + REPORTER_ASSERT(reporter, setupCanvas(&canvas, gCanvasConfigs[i], context)); + + static const SkCanvas::Config8888 gReadConfigs[] = { + SkCanvas::kNative_Premul_Config8888, + SkCanvas::kNative_Unpremul_Config8888, + SkCanvas::kBGRA_Premul_Config8888, + SkCanvas::kBGRA_Unpremul_Config8888, + SkCanvas::kRGBA_Premul_Config8888, + SkCanvas::kRGBA_Unpremul_Config8888, + }; + for (int r = 0; r < SK_ARRAY_COUNT(testRects); ++r) { + const SkIRect& rect = testRects[r]; + for (int tightBmp = 0; tightBmp < 2; ++tightBmp) { + for (int c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) { + fillCanvas(&canvas); + SkCanvas::Config8888 config8888 = gReadConfigs[c]; + SkBitmap bmp; + REPORTER_ASSERT(reporter, setupBitmap(&bmp, config8888, rect.width(), rect.height(), SkToBool(tightBmp))); + canvas.writePixels(bmp, rect.fLeft, rect.fTop, config8888); + REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop, config8888)); + } + } + } + } +} +} + +#include "TestClassDef.h" +DEFINE_GPUTESTCLASS("WritePixels", WritePixelsTestClass, WritePixelsTest) + |