blob: b86d733fed3bf3f123421cc750793e48d715fe3b [file] [log] [blame]
/* libs/graphics/sgl/SkBitmap.cpp
**
** Copyright 2006, Google Inc.
**
** 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
**
** 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.
*/
#include "SkBitmap.h"
#include "SkColorPriv.h"
#include "SkUtils.h"
SkBitmap::SkBitmap()
{
memset(this, 0, sizeof(*this));
}
SkBitmap::SkBitmap(const SkBitmap& src)
{
src.fColorTable->safeRef();
memcpy(this, &src, sizeof(src));
fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag);
}
SkBitmap::~SkBitmap()
{
fColorTable->safeUnref();
this->freePixels();
}
SkBitmap& SkBitmap::operator=(const SkBitmap& src)
{
src.fColorTable->safeRef();
fColorTable->safeUnref();
this->freePixels();
memcpy(this, &src, sizeof(src));
fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag);
return *this;
}
void SkBitmap::swap(SkBitmap& other)
{
SkTSwap<SkColorTable*>(fColorTable, other.fColorTable);
#ifdef SK_SUPPORT_MIPMAP
SkTSwap<MipMap*>(fMipMap, other.fMipMap);
#endif
SkTSwap<void*>(fPixels, other.fPixels);
SkTSwap<U16>(fWidth, other.fWidth);
SkTSwap<U16>(fHeight, other.fHeight);
SkTSwap<U16>(fRowBytes, other.fRowBytes);
SkTSwap<U8>(fConfig, other.fConfig);
SkTSwap<U8>(fFlags, other.fFlags);
}
void SkBitmap::reset()
{
fColorTable->safeUnref();
this->freePixels();
memset(this, 0, sizeof(*this));
}
void SkBitmap::setConfig(Config c, U16CPU width, U16CPU height, U16CPU rowBytes)
{
this->freePixels();
if (rowBytes == 0)
{
switch (c) {
case kA1_Config:
rowBytes = (width + 7) >> 3;
break;
case kA8_Config:
case kIndex8_Config:
rowBytes = width;
break;
case kRGB_565_Config:
rowBytes = SkAlign4(width << 1);
break;
case kARGB_8888_Config:
rowBytes = width << 2;
break;
default:
SkASSERT(!"unknown config");
break;
}
}
fConfig = SkToU8(c);
fWidth = SkToU16(width);
fHeight = SkToU16(height);
fRowBytes = SkToU16(rowBytes);
}
void SkBitmap::setPixels(void* p)
{
this->freePixels();
fPixels = p;
fFlags &= ~(kWeOwnThePixels_Flag | kWeOwnTheMipMap_Flag);
}
void SkBitmap::allocPixels()
{
this->freePixels();
fPixels = (U32*)sk_malloc_throw(fHeight * fRowBytes);
fFlags |= kWeOwnThePixels_Flag;
}
void SkBitmap::freePixels()
{
if (fFlags & kWeOwnThePixels_Flag)
{
SkASSERT(fPixels);
sk_free(fPixels);
fPixels = NULL;
fFlags &= ~kWeOwnThePixels_Flag;
}
#ifdef SK_SUPPORT_MIPMAP
if (fFlags & kWeOwnTheMipMap_Flag)
{
sk_free(fMipMap);
fMipMap = NULL;
}
#endif
}
bool SkBitmap::getOwnsPixels() const
{
return SkToBool(fFlags & kWeOwnThePixels_Flag);
}
void SkBitmap::setOwnsPixels(bool ownsPixels)
{
if (ownsPixels)
fFlags |= kWeOwnThePixels_Flag;
else
fFlags &= ~kWeOwnThePixels_Flag;
}
SkColorTable* SkBitmap::setColorTable(SkColorTable* ct)
{
SkRefCnt_SafeAssign(fColorTable, ct);
return ct;
}
bool SkBitmap::isOpaque() const
{
switch (fConfig) {
case kNo_Config:
return true;
case kA1_Config:
case kA8_Config:
case kARGB_8888_Config:
return (fFlags & kImageIsOpaque_Flag) != 0;
case kIndex8_Config:
return (fColorTable->getFlags() & SkColorTable::kColorsAreOpaque_Flag) != 0;
case kRGB_565_Config:
return true;
default:
SkASSERT(!"unknown bitmap config");
return false;
}
}
void SkBitmap::setIsOpaque(bool isOpaque)
{
/* we record this regardless of fConfig, though it is ignored in isOpaque() for
configs that can't support per-pixel alpha.
*/
if (isOpaque)
fFlags |= kImageIsOpaque_Flag;
else
fFlags &= ~kImageIsOpaque_Flag;
}
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
{
if (fPixels == NULL || fConfig == kNo_Config)
return;
int height = fHeight;
this->setIsOpaque(a == 255);
// make rgb premultiplied
if (a != 255)
{
r = SkAlphaMul(r, a);
g = SkAlphaMul(g, a);
b = SkAlphaMul(b, a);
}
switch (fConfig) {
case kA1_Config:
{
U8* p = (uint8_t*)fPixels;
size_t count = (fWidth + 7) >> 3;
a = (a >> 7) ? 0xFF : 0;
SkASSERT(count <= fRowBytes);
while (--height >= 0)
{
memset(p, a, count);
p += fRowBytes;
}
}
break;
case kA8_Config:
memset(fPixels, a, fRowBytes * fWidth);
break;
case kIndex8_Config:
SkASSERT(!"Don't support writing to Index8 bitmaps");
break;
case kRGB_565_Config:
// now erase the color-plane
{
U16* p = (uint16_t*)fPixels;
U16 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
g >> (8 - SK_G16_BITS),
b >> (8 - SK_B16_BITS));
while (--height >= 0)
{
sk_memset16(p, v, fWidth);
p = (uint16_t*)((char*)p + fRowBytes);
}
}
break;
case kARGB_8888_Config:
{
uint32_t* p = (uint32_t*)fPixels;
uint32_t v = SkPackARGB32(a, r, g, b);
while (--height >= 0)
{
sk_memset32(p, v, fWidth);
p = (uint32_t*)((char*)p + fRowBytes);
}
}
break;
}
}
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
static void downsampleby2_proc32(SkBitmap* dst, int x, int y, const SkBitmap& src)
{
x <<= 1;
y <<= 1;
const U32* p = src.getAddr32(x, y);
U32 c, ag, rb;
c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
if (x < (int)src.width() - 1)
p += 1;
c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
if (y < (int)src.height() - 1)
p = src.getAddr32(x, y + 1);
c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
if (x < (int)src.width() - 1)
p += 1;
c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
*dst->getAddr32(x >> 1, y >> 1) = ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
}
static inline uint32_t expand16(U16CPU c)
{
return (c & SK_R16B16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
}
static inline U16CPU pack16(uint32_t c)
{
return (c & SK_R16B16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
}
static void downsampleby2_proc16(SkBitmap* dst, int x, int y, const SkBitmap& src)
{
x <<= 1;
y <<= 1;
const U16* p = src.getAddr16(x, y);
U32 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);
c += expand16(*p);
if (x < (int)src.width() - 1)
p += 1;
c += expand16(*p);
*dst->getAddr16(x >> 1, y >> 1) = SkToU16(pack16(c >> 2));
}
bool SkBitmap::quarterSizeFiltered(SkBitmap* dst) const
{
int shift;
void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
switch (getConfig()) {
case SkBitmap::kARGB_8888_Config:
shift = 2;
proc = downsampleby2_proc32;
break;
case SkBitmap::kRGB_565_Config:
shift = 1;
proc = downsampleby2_proc16;
break;
default:
//other formats not supported
return false;
}
unsigned width = (this->width() + 1) >> 1;
unsigned height = (this->height() + 1) >> 1;
unsigned rowBytes = width << shift;
dst->setConfig(getConfig(), width, height, rowBytes);
dst->allocPixels();
for (unsigned j = 0; j < height; j++)
for (unsigned i = 0; i < width; i++)
proc(dst, i, j, *this);
return true;
}
void SkBitmap::buildMipMap(bool forceRebuild)
{
#ifdef SK_SUPPORT_MIPMAP
if (!forceRebuild && fMipMap)
return;
if (fFlags & kWeOwnTheMipMap_Flag)
{
SkASSERT(fMipMap);
sk_free(fMipMap);
fMipMap = NULL;
fFlags &= ~kWeOwnTheMipMap_Flag;
}
int shift;
void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
switch (this->getConfig()) {
case kARGB_8888_Config:
shift = 2;
proc = downsampleby2_proc32;
break;
case kRGB_565_Config:
shift = 1;
proc = downsampleby2_proc16;
break;
case kIndex8_Config:
case kA8_Config:
// shift = 0; break;
default:
return; // don't build mipmaps for these configs
}
// whip through our loop to compute the exact size needed
size_t size;
{
unsigned width = this->width();
unsigned height = this->height();
size = 0;
for (int i = 1; i < kMaxMipLevels; i++)
{
width = (width + 1) >> 1;
height = (height + 1) >> 1;
size += width * height << shift;
}
}
MipMap* mm = (MipMap*)sk_malloc_throw(sizeof(MipMap) + size);
U8* addr = (U8*)(mm + 1);
unsigned width = this->width();
unsigned height = this->height();
unsigned rowBytes = this->rowBytes();
SkBitmap srcBM(*this), dstBM;
mm->fLevel[0].fPixels = this->getPixels();
mm->fLevel[0].fWidth = SkToU16(width);
mm->fLevel[0].fHeight = SkToU16(height);
mm->fLevel[0].fRowBytes = SkToU16(rowBytes);
mm->fLevel[0].fConfig = SkToU8(this->getConfig());
mm->fLevel[0].fShift = SkToU8(shift);
for (int i = 1; i < kMaxMipLevels; i++)
{
width = (width + 1) >> 1;
height = (height + 1) >> 1;
rowBytes = width << shift;
mm->fLevel[i].fPixels = addr;
mm->fLevel[i].fWidth = SkToU16(width);
mm->fLevel[i].fHeight = SkToU16(height);
mm->fLevel[i].fRowBytes = SkToU16(rowBytes);
mm->fLevel[i].fConfig = SkToU8(this->getConfig());
mm->fLevel[i].fShift = SkToU8(shift);
dstBM.setConfig(this->getConfig(), 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);
srcBM = dstBM;
addr += height * rowBytes;
}
SkASSERT(addr == (U8*)mm->fLevel[1].fPixels + size);
fMipMap = mm;
fFlags |= kWeOwnTheMipMap_Flag;
#endif
}
unsigned SkBitmap::countMipLevels() const
{
#ifdef SK_SUPPORT_MIPMAP
return fMipMap ? kMaxMipLevels : 0;
#else
return 0;
#endif
}
#ifdef SK_SUPPORT_MIPMAP
const SkBitmap::MipLevel* SkBitmap::getMipLevel(unsigned level) const
{
SkASSERT(level < this->countMipLevels());
return &fMipMap->fLevel[level];
}
#endif