Allow copying an Index8 bitmap when srcConfig and dstConfig are both
authorweita@google.com <weita@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Sun, 3 May 2009 18:23:30 +0000 (18:23 +0000)
committerweita@google.com <weita@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Sun, 3 May 2009 18:23:30 +0000 (18:23 +0000)
Index8.

Also, change the logic of SkBitmap.copyTo() to do memcpy() if srcConfig
and dstConfig are the same.

git-svn-id: http://skia.googlecode.com/svn/trunk@164 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkBitmap.h
src/core/SkBitmap.cpp
src/core/SkColorTable.cpp
tests/BitmapCopyTest.cpp

index 5e7c44e..48055d0 100644 (file)
@@ -74,11 +74,11 @@ public:
     */
     //  This method is not exported to java.
     void swap(SkBitmap& other);
-    
+
     /** Return true iff the bitmap has empty dimensions.
     */
     bool empty() const { return 0 == fWidth || 0 == fHeight; }
-    
+
     /** Return true iff the bitmap has no pixels nor a pixelref. Note: this can
         return true even if the dimensions of the bitmap are > 0 (see empty()).
     */
@@ -99,7 +99,7 @@ public:
     /** Return the number of bytes between subsequent rows of the bitmap.
     */
     int rowBytes() const { return fRowBytes; }
-    
+
     /** Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for
         2-bytes per pixel configs, 2 for 4-bytes per pixel configs). Return 0
         for configs that are not at least 1-byte per pixel (e.g. kA1_Config
@@ -128,7 +128,7 @@ public:
         if the real size exceeds 32bits.
     */
     size_t getSize() const { return fHeight * fRowBytes; }
-    
+
     /** Return the byte size of the pixels, based on the height and rowBytes.
         This routine is slightly slower than getSize(), but does not truncate
         the answer to 32bits.
@@ -138,7 +138,7 @@ public:
         size.setMul(fHeight, fRowBytes);
         return size;
     }
-    
+
     /** Returns true if the bitmap is opaque (has no translucent/transparent pixels).
     */
     bool isOpaque() const;
@@ -168,7 +168,7 @@ public:
     static int ComputeShiftPerPixel(Config c) {
         return ComputeBytesPerPixel(c) >> 1;
     }
-    
+
     static Sk64 ComputeSize64(Config, int width, int height);
     static size_t ComputeSize(Config, int width, int height);
 
@@ -180,7 +180,7 @@ public:
     /** Use this to assign a new pixel address for an existing bitmap. This
         will automatically release any pixelref previously installed. Only call
         this if you are handling ownership/lifetime of the pixel memory.
-     
+
         If the bitmap retains a reference to the colortable (assuming it is
         not null) it will take care of incrementing the reference count.
 
@@ -193,7 +193,7 @@ public:
         pixel memory. It will be sized based on the current width/height/config.
         If this is called multiple times, a new pixelref object will be created
         each time.
-        
+
         If the bitmap retains a reference to the colortable (assuming it is
         not null) it will take care of incrementing the reference count.
 
@@ -205,15 +205,15 @@ public:
     bool allocPixels(SkColorTable* ctable = NULL) {
         return this->allocPixels(NULL, ctable);
     }
-    
+
     /** Use the specified Allocator to create the pixelref that manages the
         pixel memory. It will be sized based on the current width/height/config.
         If this is called multiple times, a new pixelref object will be created
         each time.
-        
+
         If the bitmap retains a reference to the colortable (assuming it is
         not null) it will take care of incrementing the reference count.
-     
+
         @param allocator The Allocator to use to create a pixelref that can
                          manage the pixel memory for the current
                          width/height/config. If allocator is NULL, the standard
@@ -226,7 +226,7 @@ public:
                      the bitmap will be unchanged.
     */
     bool allocPixels(Allocator* allocator, SkColorTable* ctable);
-    
+
     /** Return the current pixelref object, of any
     */
     SkPixelRef* pixelRef() const { return fPixelRef; }
@@ -239,7 +239,7 @@ public:
         ref'd.
     */
     SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset = 0);
-    
+
     /** Call this to ensure that the bitmap points to the current pixel address
         in the pixelref. Balance it with a call to unlockPixels(). These calls
         are harmless if there is no pixelref.
@@ -251,7 +251,7 @@ public:
         a given image.
     */
     void unlockPixels() const;
-    
+
     /** Call this to be sure that the bitmap is valid enough to be drawn (i.e.
         it has non-null pixels, and if required by its config, it has a
         non-null colortable. Returns true if all of the above are met.
@@ -273,7 +273,7 @@ public:
         will be returned.
     */
     uint32_t getGenerationID() const;
-    
+
     /** Call this if you have changed the contents of the pixels. This will in-
         turn cause a different generation ID value to be returned from
         getGenerationID().
@@ -301,7 +301,7 @@ public:
         this->eraseARGB(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c),
                         SkColorGetB(c));
     }
-    
+
     /** 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).
@@ -397,7 +397,7 @@ public:
 
     void extractAlpha(SkBitmap* dst, const SkPaint* paint,
                       SkIPoint* offset) const;
-    
+
     void flatten(SkFlattenableWriteBuffer&) const;
     void unflatten(SkFlattenableReadBuffer&);
 
@@ -428,23 +428,23 @@ public:
     public:
         RLEPixels(int width, int height);
         virtual ~RLEPixels();
-        
+
         uint8_t* packedAtY(int y) const {
             SkASSERT((unsigned)y < (unsigned)fHeight);
             return fYPtrs[y];
         }
-        
+
         // called by subclasses during creation
         void setPackedAtY(int y, uint8_t* addr) {
             SkASSERT((unsigned)y < (unsigned)fHeight);
             fYPtrs[y] = addr;
         }
-            
+
     private:
         uint8_t** fYPtrs;
         int       fHeight;
     };
-        
+
 private:
 #ifdef SK_SUPPORT_MIPMAP
     struct MipMap;
@@ -474,7 +474,7 @@ private:
     */
     void freePixels();
     void updatePixelsFromRef() const;
-    
+
     static SkFixed ComputeMipLevel(SkFixed sx, SkFixed dy);
 };
 
@@ -485,7 +485,11 @@ private:
 */
 class SkColorTable : public SkRefCnt {
 public:
-    /** Constructs an empty color table (zero colors).
+    /** Makes a deep copy of colors.
+     */
+    SkColorTable(const SkColorTable& src);
+    /** Preallocates the colortable to have 'count' colors, which
+     *  are initially set to 0.
     */
     explicit SkColorTable(int count);
     explicit SkColorTable(SkFlattenableReadBuffer&);
@@ -599,12 +603,12 @@ public:
             fCTable->unlockColors(false);
         }
     }
-    
+
     /** Return the currently locked colors, or NULL if no bitmap's colortable
         is currently locked.
     */
     const SkPMColor* colors() const { return fColors; }
-    
+
     /** If a previous bitmap has been locked by this object, unlock its colors
         first. If the specified bitmap has a colortable, lock its colors and
         return them.
index c5d0eda..3debd81 100644 (file)
@@ -38,7 +38,7 @@ struct SkBitmap::MipMap : SkNoncopyable {
     int     fLevelCount;
 //  MipLevel    fLevel[fLevelCount];
 //  Pixels[]
-    
+
     static MipMap* Alloc(int levelCount, size_t pixelSize) {
         MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) +
                                               levelCount * sizeof(MipLevel) +
@@ -53,7 +53,7 @@ struct SkBitmap::MipMap : SkNoncopyable {
 
     const void* pixels() const { return levels() + fLevelCount; }
     void* pixels() { return levels() + fLevelCount; }
-    
+
     void safeRef() {
         if (this) {
             SkASSERT(fRefCnt > 0);
@@ -103,7 +103,7 @@ SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
 
         // we reset our locks if we get blown away
         fPixelLockCount = 0;
-        
+
         /*  The src could be in 3 states
             1. no pixelref, in which case we just copy/ref the pixels/ctable
             2. unlocked pixelref, pixels/ctable should be null
@@ -238,7 +238,7 @@ void SkBitmap::updatePixelsFromRef() const {
     if (NULL != fPixelRef) {
         if (fPixelLockCount > 0) {
             SkASSERT(fPixelRef->getLockCount() > 0);
-            
+
             void* p = fPixelRef->pixels();
             if (NULL != p) {
                 p = (char*)p + fPixelRefOffset;
@@ -264,7 +264,7 @@ SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
         if (fPixelRef != pr) {
             this->freePixels();
             SkASSERT(NULL == fPixelRef);
-            
+
             pr->safeRef();
             fPixelRef = pr;
         }
@@ -374,7 +374,7 @@ void SkMallocPixelRef::onUnlockPixels() {
 
 void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    
+
     buffer.write32(fSize);
     buffer.writePad(fStorage, fSize);
     if (fCTable) {
@@ -408,12 +408,12 @@ bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
     if (size.isNeg() || !size.is32()) {
         return false;
     }
-    
+
     void* addr = sk_malloc_flags(size.get32(), 0);  // returns NULL on failure
     if (NULL == addr) {
         return false;
     }
-    
+
     dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
     // since we're already allocated, we lockPixels right away
     dst->lockPixels();
@@ -554,7 +554,7 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
         case kRGB_565_Config: {
             uint16_t* p = (uint16_t*)fPixels;
             uint16_t v;
-            
+
             if (kARGB_4444_Config == fConfig) {
                 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
             } else {    // kRGB_565_Config
@@ -578,7 +578,7 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
             break;
         }
     }
-    
+
     this->notifyPixelsChanged();
 }
 
@@ -590,7 +590,7 @@ void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
 static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
     SkASSERT((unsigned)x < (unsigned)bm.width());
     SkASSERT((unsigned)y < (unsigned)bm.height());
-    
+
     switch (bm.getConfig()) {
         case SkBitmap::kA8_Config:
         case SkBitmap:: kIndex8_Config:
@@ -635,18 +635,18 @@ bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
             return false;
         }
         SkBitmap bm;
-        
+
         bm.setConfig(kIndex8_Config, r.width(), r.height());
         bm.allocPixels(this->getColorTable());
         if (NULL == bm.getPixels()) {
             return false;
         }
-        
+
         const RLEPixels* rle = (const RLEPixels*)this->getPixels();
         uint8_t* dst = bm.getAddr8(0, 0);
         const int width = bm.width();
         const int rowBytes = bm.rowBytes();
-            
+
         for (int y = r.fTop; y < r.fBottom; y++) {
             SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
             dst += rowBytes;
@@ -683,47 +683,70 @@ bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
 #include "SkPaint.h"
 
 bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
-    if (NULL == dst || this->width() == 0 || this->height() == 0) {
+    if (NULL == dst || this->getConfig() == kNo_Config
+            || this->width() == 0 || this->height() == 0) {
         return false;
     }
 
+    bool sameConfigs = (dstConfig == this->config());
     switch (dstConfig) {
         case kA8_Config:
         case kARGB_4444_Config:
         case kRGB_565_Config:
         case kARGB_8888_Config:
             break;
+        case kA1_Config:
+        case kIndex8_Config:
+            if (!sameConfigs) {
+                return false;
+            }
+            break;
         default:
             return false;
     }
-    
-    SkBitmap    tmp;
-    
+
+    // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
+    if (this->getConfig() == kA1_Config && !sameConfigs) {
+        return false;
+    }
+
+    SkBitmap tmp;
     tmp.setConfig(dstConfig, this->width(), this->height());
-    // pass null for colortable, since we don't support Index8 config for dst
-    if (!tmp.allocPixels(alloc, NULL)) {
+
+    // allocate colortable if srcConfig == kIndex8_Config
+    SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
+        new SkColorTable(*this->getColorTable()) : NULL;
+    SkAutoUnref au(ctable);
+    if (!tmp.allocPixels(alloc, ctable)) {
         return false;
     }
-    
+
     SkAutoLockPixels srclock(*this);
     SkAutoLockPixels dstlock(tmp);
-    
+
     if (!this->readyToDraw() || !tmp.readyToDraw()) {
         // allocator/lock failed
         return false;
     }
 
-    // if the src has alpha, we have to clear the dst first
-    if (!this->isOpaque()) {
-        tmp.eraseColor(0);
+    /* do memcpy for the sameConfigs cases and
+       re-draw for the !sameConfigs cases
+    */
+    if (sameConfigs) {
+       memcpy(tmp.getPixels(), this->getPixels(), this->getSize());
+    } else {
+        // if the src has alpha, we have to clear the dst first
+        if (!this->isOpaque()) {
+            tmp.eraseColor(0);
+        }
+
+        SkCanvas canvas(tmp);
+        SkPaint  paint;
+
+        paint.setDither(true);
+        canvas.drawBitmap(*this, 0, 0, &paint);
     }
 
-    SkCanvas canvas(tmp);
-    SkPaint  paint;
-    
-    paint.setDither(true);
-    canvas.drawBitmap(*this, 0, 0, &paint);
-    
     dst->swap(tmp);
     return true;
 }
@@ -773,13 +796,13 @@ static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
     y <<= 1;
     const uint16_t* p = src.getAddr16(x, y);
     SkPMColor       c;
-    
+
     c = expand16(*p);
     if (x < (int)src.width() - 1) {
         p += 1;
     }
     c += expand16(*p);
-    
+
     if (y < (int)src.height() - 1) {
         p = src.getAddr16(x, y + 1);
     }
@@ -788,7 +811,7 @@ static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
         p += 1;
     }
     c += expand16(*p);
-    
+
     *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
 }
 
@@ -806,13 +829,13 @@ static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
     y <<= 1;
     const uint16_t* p = src.getAddr16(x, y);
     uint32_t        c;
-    
+
     c = expand4444(*p);
     if (x < src.width() - 1) {
         p += 1;
     }
     c += expand4444(*p);
-    
+
     if (y < src.height() - 1) {
         p = src.getAddr16(x, y + 1);
     }
@@ -821,7 +844,7 @@ static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
         p += 1;
     }
     c += expand4444(*p);
-    
+
     *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
 }
 
@@ -897,7 +920,7 @@ void SkBitmap::buildMipMap(bool forceRebuild) {
 
         dstBM.setConfig(config, width, height, rowBytes);
         dstBM.setPixels(addr);
-    
+
         for (unsigned y = 0; y < height; y++) {
             for (unsigned x = 0; x < width; x++) {
                 proc(&dstBM, x, y, srcBM);
@@ -924,7 +947,7 @@ int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
 #ifdef SK_SUPPORT_MIPMAP
     if (NULL == fMipMap)
         return 0;
-    
+
     int level = ComputeMipLevel(sx, sy) >> 16;
     SkASSERT(level >= 0);
     if (level <= 0) {
@@ -1049,7 +1072,7 @@ void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
     NO_FILTER_CASE:
         dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
                        srcM.fRowBytes);
-        dst->allocPixels();        
+        dst->allocPixels();
         GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
         if (offset) {
             offset->set(0, 0);
@@ -1105,7 +1128,7 @@ static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer)
     It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
     we just have pixels, then we can only flatten the pixels, or write out an
     empty bitmap.
+
     With a pixelref, we still have the question of recognizing when two sitings
     of the same pixelref are the same, and when they are different. Perhaps we
     should look at the generationID and keep a record of that in some dictionary
@@ -1118,7 +1141,7 @@ void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
     buffer.write32(fRowBytes);
     buffer.write8(fConfig);
     buffer.writeBool(this->isOpaque());
-    
+
     /*  If we are called in this mode, then it is up to the caller to manage
         the owner-counts on the pixelref, as we just record the ptr itself.
     */
@@ -1168,15 +1191,15 @@ void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
 
 void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
     this->reset();
-    
+
     int width = buffer.readInt();
     int height = buffer.readInt();
     int rowBytes = buffer.readInt();
     int config = buffer.readU8();
-    
+
     this->setConfig((Config)config, width, height, rowBytes);
     this->setIsOpaque(buffer.readBool());
-    
+
     size_t size = this->getSize();
     int reftype = buffer.readU8();
     switch (reftype) {
index f991da3..1f92caf 100644 (file)
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
@@ -31,7 +31,19 @@ SkColorTable::SkColorTable(int count)
     fCount = SkToU16(count);
     fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
     memset(fColors, 0, count * sizeof(SkPMColor));
-    
+
+    SkDEBUGCODE(fColorLockCount = 0;)
+    SkDEBUGCODE(f16BitCacheLockCount = 0;)
+}
+
+SkColorTable::SkColorTable(const SkColorTable& src) {
+    f16BitCache = NULL;
+    fFlags = src.fFlags;
+    int count = src.count();
+    fCount = SkToU16(count);
+    fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
+    memcpy(fColors, src.fColors, count * sizeof(SkPMColor));
+
     SkDEBUGCODE(fColorLockCount = 0;)
     SkDEBUGCODE(f16BitCacheLockCount = 0;)
 }
@@ -43,13 +55,13 @@ SkColorTable::SkColorTable(const SkPMColor colors[], int count)
         count = 0;
     else if (count > 256)
         count = 256;
-    
+
     fCount = SkToU16(count);
     fColors = (SkPMColor*)sk_malloc_throw(count * sizeof(SkPMColor));
-    
+
     if (colors)
         memcpy(fColors, colors, count * sizeof(SkPMColor));
-    
+
     SkDEBUGCODE(fColorLockCount = 0;)
     SkDEBUGCODE(f16BitCacheLockCount = 0;)
 }
index 5ec64cf..f904859 100644 (file)
@@ -11,7 +11,7 @@ static const char* gConfigName[] = {
 };
 
 static void init_src(const SkBitmap& bitmap) {
-    SkAutoLockPixels lock(bitmap);    
+    SkAutoLockPixels lock(bitmap);
     if (bitmap.getPixels()) {
         memset(bitmap.getPixels(), 4, bitmap.getSize());
     }
@@ -32,7 +32,7 @@ struct Pair {
 static void TestBitmapCopy(skiatest::Reporter* reporter) {
     static const Pair gPairs[] = {
         { SkBitmap::kNo_Config,         "00000000"  },
-        { SkBitmap::kA1_Config,         "01101110"  },
+        { SkBitmap::kA1_Config,         "01000000"  },
         { SkBitmap::kA8_Config,         "00101110"  },
         { SkBitmap::kIndex8_Config,     "00111110"  },
         { SkBitmap::kRGB_565_Config,    "00101110"  },
@@ -40,15 +40,15 @@ static void TestBitmapCopy(skiatest::Reporter* reporter) {
         { SkBitmap::kARGB_8888_Config,  "00101110"  },
         { SkBitmap::kRLE_Index8_Config, "00000000"  }
     };
-    
+
     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()) {
                 ct = init_ctable();
@@ -66,12 +66,13 @@ static void TestBitmapCopy(skiatest::Reporter* reporter) {
                            boolStr(success));
                 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);
                 if (src.config() == dst.config()) {
-                    SkAutoLockPixels srcLock(src);    
+                    SkAutoLockPixels srcLock(src);
                     SkAutoLockPixels dstLock(dst);
                     REPORTER_ASSERT(reporter, src.readyToDraw());
                     REPORTER_ASSERT(reporter, dst.readyToDraw());