Allow copying an Index8 bitmap when srcConfig and dstConfig are both

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
This commit is contained in:
weita@google.com 2009-05-03 18:23:30 +00:00
parent 4226396806
commit f9ab99aaad
4 changed files with 130 additions and 90 deletions

View 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.

View 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;
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)) {
// 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());
// 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) {

View 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;)
}

View 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());