From 60d3235ab42bd1d32a070695ee30d6e3ec2fa222 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Fri, 28 Jun 2013 19:40:50 +0000 Subject: [PATCH] add bitmap::eraseArea BUG= R=scroggo@google.com Review URL: https://codereview.chromium.org/18029021 git-svn-id: http://skia.googlecode.com/svn/trunk@9815 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkBitmap.h | 46 ++++++++++++++++--------- src/core/SkBitmap.cpp | 69 ++++++++++++++++++++++++++++++------- tests/BitmapGetColorTest.cpp | 82 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 162 insertions(+), 35 deletions(-) diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h index 86e267c..887169c 100644 --- a/include/core/SkBitmap.h +++ b/include/core/SkBitmap.h @@ -386,28 +386,38 @@ public: */ void notifyPixelsChanged() const; - /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format - for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is ignored. - If the config is kA8_Config, then the r,g,b parameters are ignored. - */ - void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const; - /** Initialize the bitmap's pixels with the specified color+alpha, automatically converting into the correct format - for the bitmap's config. If the config is kRGB_565_Config, then the alpha value is presumed - to be 0xFF. If the config is kA8_Config, then the r,g,b parameters are ignored and the - pixels are all set to 0xFF. - */ - void eraseRGB(U8CPU r, U8CPU g, U8CPU b) const { - this->eraseARGB(0xFF, r, g, b); - } - /** Initialize the bitmap's pixels with the specified color, automatically converting into the correct format - for the bitmap's config. If the config is kRGB_565_Config, then the color's alpha value is presumed - to be 0xFF. If the config is kA8_Config, then only the color's alpha value is used. - */ + /** + * Fill the entire bitmap with the specified color. + * If the bitmap's config does not support alpha (e.g. 565) then the alpha + * of the color is ignored (treated as opaque). If the config only supports + * alpha (e.g. A1 or A8) then the color's r,g,b components are ignored. + */ void eraseColor(SkColor c) const { this->eraseARGB(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); } + /** + * Fill the entire bitmap with the specified color. + * If the bitmap's config does not support alpha (e.g. 565) then the alpha + * of the color is ignored (treated as opaque). If the config only supports + * alpha (e.g. A1 or A8) then the color's r,g,b components are ignored. + */ + void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const; + + // DEPRECATED -- call eraseColor or eraseARGB + void eraseRGB(U8CPU r, U8CPU g, U8CPU b) const { + this->eraseARGB(0xFF, r, g, b); + } + + /** + * Fill the specified area of this bitmap with the specified color. + * If the bitmap's config does not support alpha (e.g. 565) then the alpha + * of the color is ignored (treated as opaque). If the config only supports + * alpha (e.g. A1 or A8) then the color's r,g,b components are ignored. + */ + void eraseArea(const SkIRect& area, SkColor c) const; + /** Scroll (a subset of) the contents of this bitmap by dx/dy. If there are no pixels allocated (i.e. getPixels() returns null) the method will still update the inval region (if present). If the bitmap is immutable, @@ -665,6 +675,8 @@ private: uint8_t fFlags; uint8_t fBytesPerPixel; // based on config + void internalErase(const SkIRect&, U8CPU a, U8CPU r, U8CPU g, U8CPU b)const; + /* Internal computations for safe size. */ static Sk64 ComputeSafeSize64(Config config, diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp index f164ca5..07fbe12 100644 --- a/src/core/SkBitmap.cpp +++ b/src/core/SkBitmap.cpp @@ -739,11 +739,18 @@ static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b return SkToU16(pixel); } -void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { +void SkBitmap::internalErase(const SkIRect& area, + U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { +#ifdef SK_DEBUG SkDEBUGCODE(this->validate();) + SkASSERT(!area.isEmpty()); + { + SkIRect total = { 0, 0, fWidth, fHeight }; + SkASSERT(total.contains(area)); + } +#endif - if (0 == fWidth || 0 == fHeight || - kNo_Config == fConfig || kIndex8_Config == fConfig) { + if (kNo_Config == fConfig || kIndex8_Config == fConfig) { return; } @@ -753,8 +760,8 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { return; } - int height = fHeight; - const int width = fWidth; + int height = area.height(); + const int width = area.width(); const int rowBytes = fRowBytes; // make rgb premultiplied @@ -766,18 +773,39 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { switch (fConfig) { case kA1_Config: { - uint8_t* p = (uint8_t*)fPixels; - const int count = (width + 7) >> 3; + uint8_t* p = this->getAddr1(area.fLeft, area.fTop); + const int left = area.fLeft >> 3; + const int right = area.fRight >> 3; + + int middle = right - left - 1; + + uint8_t leftMask = 0xFF >> (area.fLeft & 7); + uint8_t rightMask = ~(0xFF >> (area.fRight & 7)); + if (left == right) { + leftMask &= rightMask; + rightMask = 0; + } + a = (a >> 7) ? 0xFF : 0; - SkASSERT(count <= rowBytes); while (--height >= 0) { - memset(p, a, count); - p += rowBytes; + uint8_t* startP = p; + + *p = (*p & ~leftMask) | (a & leftMask); + p++; + if (middle > 0) { + memset(p, a, middle); + p += middle; + } + if (rightMask) { + *p = (*p & ~rightMask) | (a & rightMask); + } + + p = startP + rowBytes; } break; } case kA8_Config: { - uint8_t* p = (uint8_t*)fPixels; + uint8_t* p = this->getAddr8(area.fLeft, area.fTop); while (--height >= 0) { memset(p, a, width); p += rowBytes; @@ -786,7 +814,7 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { } case kARGB_4444_Config: case kRGB_565_Config: { - uint16_t* p = (uint16_t*)fPixels; + uint16_t* p = this->getAddr16(area.fLeft, area.fTop);; uint16_t v; if (kARGB_4444_Config == fConfig) { @@ -803,7 +831,7 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { break; } case kARGB_8888_Config: { - uint32_t* p = (uint32_t*)fPixels; + uint32_t* p = this->getAddr32(area.fLeft, area.fTop); uint32_t v = SkPackARGB32(a, r, g, b); while (--height >= 0) { @@ -817,6 +845,21 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { this->notifyPixelsChanged(); } +void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const { + SkIRect area = { 0, 0, fWidth, fHeight }; + if (!area.isEmpty()) { + this->internalErase(area, a, r, g, b); + } +} + +void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const { + SkIRect area = { 0, 0, fWidth, fHeight }; + if (area.intersect(rect)) { + this->internalErase(area, SkColorGetA(c), SkColorGetR(c), + SkColorGetG(c), SkColorGetB(c)); + } +} + ////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// diff --git a/tests/BitmapGetColorTest.cpp b/tests/BitmapGetColorTest.cpp index 8ada98c..3363777 100644 --- a/tests/BitmapGetColorTest.cpp +++ b/tests/BitmapGetColorTest.cpp @@ -1,12 +1,75 @@ - /* * 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 "SkBitmap.h" +#include "SkRect.h" +#include "SkRandom.h" + +static int nextRand(SkRandom& rand, int min, int max) { + return min + (int)rand.nextRangeU(0, max - min); +} + +static void rand_irect(SkIRect* rect, int W, int H, SkRandom& rand) { + const int DX = W / 2; + const int DY = H / 2; + + rect->fLeft = nextRand(rand, -DX, W + DX); + rect->fTop = nextRand(rand, -DY, H + DY); + rect->fRight = nextRand(rand, -DX, W + DX); + rect->fBottom = nextRand(rand, -DY, H + DY); + rect->sort(); +} + +static void test_equal_A1_A8(skiatest::Reporter* reporter, + const SkBitmap& bm1, const SkBitmap& bm8) { + SkASSERT(SkBitmap::kA1_Config == bm1.config()); + SkASSERT(SkBitmap::kA8_Config == bm8.config()); + + REPORTER_ASSERT(reporter, bm1.width() == bm8.width()); + REPORTER_ASSERT(reporter, bm1.height() == bm8.height()); + for (int y = 0; y < bm1.height(); ++y) { + for (int x = 0; x < bm1.width(); ++x) { + int p1 = *bm1.getAddr1(x, y) & (1 << (7 - (x & 7))); + SkASSERT(SkIsPow2(p1)); + p1 = p1 ? 0xFF : 0; + + int p8 = *bm8.getAddr8(x, y); + SkASSERT(0 == p8 || 0xFF == p8); + + REPORTER_ASSERT(reporter, p1 == p8); + } + } +} + +static void test_eraserect_A1(skiatest::Reporter* reporter) { + const int W = 43; + const int H = 13; + + SkBitmap bm1, bm8; + + bm1.setConfig(SkBitmap::kA1_Config, W, H); + bm1.allocPixels(); + bm8.setConfig(SkBitmap::kA8_Config, W, H); + bm8.allocPixels(); + + SkRandom rand; + for (int i = 0; i < 10000; ++i) { + SkIRect area; + rand_irect(&area, W, H, rand); + + bm1.eraseColor(0); + bm8.eraseColor(0); + + bm1.eraseArea(area, SK_ColorWHITE); + bm8.eraseArea(area, SK_ColorWHITE); + test_equal_A1_A8(reporter, bm1, bm8); + } +} static void TestGetColor(skiatest::Reporter* reporter) { static const struct Rec { @@ -25,16 +88,25 @@ static void TestGetColor(skiatest::Reporter* reporter) { { SkBitmap::kARGB_8888_Config, 0xFF224466, 0xFF224466 }, }; + // specify an area that doesn't touch (0,0) and may extend beyond the + // bitmap bounds (to test that we catch that in eraseArea + const SkColor initColor = 0xFF0000FF; + const SkIRect area = { 1, 1, 3, 3 }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { SkBitmap bm; - uint32_t storage[1]; - bm.setConfig(gRec[i].fConfig, 1, 1); + uint32_t storage[4]; + bm.setConfig(gRec[i].fConfig, 2, 2); bm.setPixels(storage); - bm.eraseColor(gRec[i].fInColor); - SkColor c = bm.getColor(0, 0); + bm.eraseColor(initColor); + bm.eraseArea(area, gRec[i].fInColor); + + SkColor c = bm.getColor(1, 1); REPORTER_ASSERT(reporter, c == gRec[i].fOutColor); } + + test_eraserect_A1(reporter); } #include "TestClassDef.h" -- 2.7.4