blob: 92ff88ae6660e7ab281826650d1258d64bba3d00 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
2 * Copyright (C) 2006-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkBitmap.h"
18#include "SkColorPriv.h"
19#include "SkDither.h"
20#include "SkFlattenable.h"
21#include "SkMallocPixelRef.h"
22#include "SkMask.h"
23#include "SkPixelRef.h"
24#include "SkThread.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000025#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkUtils.h"
27#include "SkPackBits.h"
28#include <new>
29
bsalomon@google.com586f48c2011-04-14 15:07:22 +000030extern int32_t SkNextPixelRefGenerationID();
31
reed@android.com149e2f62009-05-22 14:39:03 +000032static bool isPos32Bits(const Sk64& value) {
33 return !value.isNeg() && value.is32();
34}
35
reed@android.com8a1c16f2008-12-17 15:59:43 +000036struct MipLevel {
37 void* fPixels;
38 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000039 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000040};
41
42struct SkBitmap::MipMap : SkNoncopyable {
43 int32_t fRefCnt;
44 int fLevelCount;
45// MipLevel fLevel[fLevelCount];
46// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000047
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000049 if (levelCount < 0) {
50 return NULL;
51 }
52 Sk64 size;
53 size.setMul(levelCount + 1, sizeof(MipLevel));
54 size.add(sizeof(MipMap));
55 size.add(pixelSize);
56 if (!isPos32Bits(size)) {
57 return NULL;
58 }
59 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 mm->fRefCnt = 1;
61 mm->fLevelCount = levelCount;
62 return mm;
63 }
64
65 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
66 MipLevel* levels() { return (MipLevel*)(this + 1); }
67
68 const void* pixels() const { return levels() + fLevelCount; }
69 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000070
reed@android.com149e2f62009-05-22 14:39:03 +000071 void ref() {
72 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
73 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 }
75 }
reed@android.com149e2f62009-05-22 14:39:03 +000076 void unref() {
77 SkASSERT(fRefCnt > 0);
78 if (sk_atomic_dec(&fRefCnt) == 1) {
79 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 }
81 }
82};
reed@android.com8a1c16f2008-12-17 15:59:43 +000083
84///////////////////////////////////////////////////////////////////////////////
85///////////////////////////////////////////////////////////////////////////////
86
87SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000088 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000089}
90
91SkBitmap::SkBitmap(const SkBitmap& src) {
92 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000093 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 *this = src;
95 SkDEBUGCODE(this->validate();)
96}
97
98SkBitmap::~SkBitmap() {
99 SkDEBUGCODE(this->validate();)
100 this->freePixels();
101}
102
103SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
104 if (this != &src) {
105 this->freePixels();
106 memcpy(this, &src, sizeof(src));
107
108 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000109 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000110 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111
112 // we reset our locks if we get blown away
113 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 /* The src could be in 3 states
116 1. no pixelref, in which case we just copy/ref the pixels/ctable
117 2. unlocked pixelref, pixels/ctable should be null
118 3. locked pixelref, we should lock the ref again ourselves
119 */
120 if (NULL == fPixelRef) {
121 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000122 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 } else { // we have a pixelref, so pixels/ctable reflect it
124 // ignore the values from the memcpy
125 fPixels = NULL;
126 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000127 // Note that what to for genID is somewhat arbitrary. We have no
128 // way to track changes to raw pixels across multiple SkBitmaps.
129 // Would benefit from an SkRawPixelRef type created by
130 // setPixels.
131 // Just leave the memcpy'ed one but they'll get out of sync
132 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 }
134 }
135
136 SkDEBUGCODE(this->validate();)
137 return *this;
138}
139
140void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000141 SkTSwap(fColorTable, other.fColorTable);
142 SkTSwap(fPixelRef, other.fPixelRef);
143 SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
144 SkTSwap(fPixelLockCount, other.fPixelLockCount);
145 SkTSwap(fMipMap, other.fMipMap);
146 SkTSwap(fPixels, other.fPixels);
147 SkTSwap(fRawPixelGenerationID, other.fRawPixelGenerationID);
148 SkTSwap(fRowBytes, other.fRowBytes);
149 SkTSwap(fWidth, other.fWidth);
150 SkTSwap(fHeight, other.fHeight);
151 SkTSwap(fConfig, other.fConfig);
152 SkTSwap(fFlags, other.fFlags);
153 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154
155 SkDEBUGCODE(this->validate();)
156}
157
158void SkBitmap::reset() {
159 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000160 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161}
162
163int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
164 int bpp;
165 switch (config) {
166 case kNo_Config:
167 case kA1_Config:
168 bpp = 0; // not applicable
169 break;
170 case kRLE_Index8_Config:
171 case kA8_Config:
172 case kIndex8_Config:
173 bpp = 1;
174 break;
175 case kRGB_565_Config:
176 case kARGB_4444_Config:
177 bpp = 2;
178 break;
179 case kARGB_8888_Config:
180 bpp = 4;
181 break;
182 default:
183 SkASSERT(!"unknown config");
184 bpp = 0; // error
185 break;
186 }
187 return bpp;
188}
189
190int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000191 if (width < 0) {
192 return 0;
193 }
194
195 Sk64 rowBytes;
196 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197
198 switch (c) {
199 case kNo_Config:
200 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 break;
202 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000203 rowBytes.set(width);
204 rowBytes.add(7);
205 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 break;
207 case kA8_Config:
208 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000209 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 break;
211 case kRGB_565_Config:
212 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000213 rowBytes.set(width);
214 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 break;
216 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000217 rowBytes.set(width);
218 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 break;
220 default:
221 SkASSERT(!"unknown config");
222 break;
223 }
reed@android.com149e2f62009-05-22 14:39:03 +0000224 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225}
226
227Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
228 Sk64 size;
229 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
230 return size;
231}
232
233size_t SkBitmap::ComputeSize(Config c, int width, int height) {
234 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000235 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236}
237
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000238Sk64 SkBitmap::ComputeSafeSize64(Config config,
239 uint32_t width,
240 uint32_t height,
241 uint32_t rowBytes) {
242 Sk64 safeSize;
243 safeSize.setZero();
244 if (height > 0) {
245 safeSize.set(ComputeRowBytes(config, width));
246 Sk64 sizeAllButLastRow;
247 sizeAllButLastRow.setMul(height - 1, rowBytes);
248 safeSize.add(sizeAllButLastRow);
249 }
250 SkASSERT(!safeSize.isNeg());
251 return safeSize;
252}
253
254size_t SkBitmap::ComputeSafeSize(Config config,
255 uint32_t width,
256 uint32_t height,
257 uint32_t rowBytes) {
258 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
259 return (safeSize.is32() ? safeSize.get32() : 0);
260}
261
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
263 this->freePixels();
264
reed@android.com149e2f62009-05-22 14:39:03 +0000265 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000266 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000267 }
268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 if (rowBytes == 0) {
270 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000271 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000272 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000275
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000277 fWidth = width;
278 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 fRowBytes = rowBytes;
280
281 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
282
283 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000284 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000285
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000286 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000287err:
288 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289}
290
291void SkBitmap::updatePixelsFromRef() const {
292 if (NULL != fPixelRef) {
293 if (fPixelLockCount > 0) {
294 SkASSERT(fPixelRef->getLockCount() > 0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000295
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 void* p = fPixelRef->pixels();
297 if (NULL != p) {
298 p = (char*)p + fPixelRefOffset;
299 }
300 fPixels = p;
301 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
302 } else {
303 SkASSERT(0 == fPixelLockCount);
304 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000305 if (fColorTable) {
306 fColorTable->unref();
307 fColorTable = NULL;
308 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 }
310 }
311}
312
313SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
314 // do this first, we that we never have a non-zero offset with a null ref
315 if (NULL == pr) {
316 offset = 0;
317 }
318
319 if (fPixelRef != pr || fPixelRefOffset != offset) {
320 if (fPixelRef != pr) {
321 this->freePixels();
322 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000323
reed@google.com82065d62011-02-07 15:30:46 +0000324 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 fPixelRef = pr;
326 }
327 fPixelRefOffset = offset;
328 this->updatePixelsFromRef();
329 }
330
331 SkDEBUGCODE(this->validate();)
332 return pr;
333}
334
335void SkBitmap::lockPixels() const {
336 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
337 fPixelRef->lockPixels();
338 this->updatePixelsFromRef();
339 }
340 SkDEBUGCODE(this->validate();)
341}
342
343void SkBitmap::unlockPixels() const {
344 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
345
346 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
347 fPixelRef->unlockPixels();
348 this->updatePixelsFromRef();
349 }
350 SkDEBUGCODE(this->validate();)
351}
352
reed@google.com9c49bc32011-07-07 13:42:37 +0000353bool SkBitmap::lockPixelsAreWritable() const {
354 if (fPixelRef) {
355 return fPixelRef->lockPixelsAreWritable();
356 } else {
357 return fPixels != NULL;
358 }
359}
360
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
362 this->freePixels();
363 fPixels = p;
364 SkRefCnt_SafeAssign(fColorTable, ctable);
365
366 SkDEBUGCODE(this->validate();)
367}
368
369bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
370 HeapAllocator stdalloc;
371
372 if (NULL == allocator) {
373 allocator = &stdalloc;
374 }
375 return allocator->allocPixelRef(this, ctable);
376}
377
378void SkBitmap::freePixels() {
379 // if we're gonna free the pixels, we certainly need to free the mipmap
380 this->freeMipMap();
381
reed@android.com149e2f62009-05-22 14:39:03 +0000382 if (fColorTable) {
383 fColorTable->unref();
384 fColorTable = NULL;
385 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386
387 if (NULL != fPixelRef) {
388 if (fPixelLockCount > 0) {
389 fPixelRef->unlockPixels();
390 }
391 fPixelRef->unref();
392 fPixelRef = NULL;
393 fPixelRefOffset = 0;
394 }
395 fPixelLockCount = 0;
396 fPixels = NULL;
397}
398
399void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000400 if (fMipMap) {
401 fMipMap->unref();
402 fMipMap = NULL;
403 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404}
405
406uint32_t SkBitmap::getGenerationID() const {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000407 if (fPixelRef) {
408 return fPixelRef->getGenerationID();
409 } else {
410 SkASSERT(fPixels || !fRawPixelGenerationID);
411 if (fPixels && !fRawPixelGenerationID) {
412 fRawPixelGenerationID = SkNextPixelRefGenerationID();
413 }
414 return fRawPixelGenerationID;
415 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416}
417
418void SkBitmap::notifyPixelsChanged() const {
419 if (fPixelRef) {
420 fPixelRef->notifyPixelsChanged();
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000421 } else {
422 fRawPixelGenerationID = 0; // will grab next ID in getGenerationID
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 }
424}
425
reed@android.comce4e53a2010-09-09 16:01:26 +0000426SkGpuTexture* SkBitmap::getTexture() const {
427 return fPixelRef ? fPixelRef->getTexture() : NULL;
428}
429
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430///////////////////////////////////////////////////////////////////////////////
431
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432/** We explicitly use the same allocator for our pixels that SkMask does,
433 so that we can freely assign memory allocated by one class to the other.
434 */
435bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
436 SkColorTable* ctable) {
437 Sk64 size = dst->getSize64();
438 if (size.isNeg() || !size.is32()) {
439 return false;
440 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000441
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
443 if (NULL == addr) {
444 return false;
445 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
448 // since we're already allocated, we lockPixels right away
449 dst->lockPixels();
450 return true;
451}
452
453///////////////////////////////////////////////////////////////////////////////
454
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000455size_t SkBitmap::getSafeSize() const {
456 // This is intended to be a size_t version of ComputeSafeSize64(), just
457 // faster. The computation is meant to be identical.
458 return (fHeight ? ((fHeight - 1) * fRowBytes) +
459 ComputeRowBytes(getConfig(), fWidth): 0);
460}
461
462Sk64 SkBitmap::getSafeSize64() const {
463 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
464}
465
466bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes)
467 const {
468
469 if (dstRowBytes == -1)
470 dstRowBytes = fRowBytes;
471 SkASSERT(dstRowBytes >= 0);
472
473 if (getConfig() == kRLE_Index8_Config ||
474 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
475 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
476 return false;
477
478 if (static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
479 size_t safeSize = getSafeSize();
480 if (safeSize > dstSize || safeSize == 0)
481 return false;
482 else {
483 SkAutoLockPixels lock(*this);
484 // This implementation will write bytes beyond the end of each row,
485 // excluding the last row, if the bitmap's stride is greater than
486 // strictly required by the current config.
487 memcpy(dst, getPixels(), safeSize);
488
489 return true;
490 }
491 } else {
492 // If destination has different stride than us, then copy line by line.
493 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
494 dstSize)
495 return false;
496 else {
497 // Just copy what we need on each line.
498 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
499 SkAutoLockPixels lock(*this);
500 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
501 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
502 for (uint32_t row = 0; row < fHeight;
503 row++, srcP += fRowBytes, dstP += dstRowBytes) {
504 memcpy(dstP, srcP, rowBytes);
505 }
506
507 return true;
508 }
509 }
510}
511
512bool SkBitmap::copyPixelsFrom(const void* const src, size_t srcSize,
513 int srcRowBytes) {
514
515 if (srcRowBytes == -1)
516 srcRowBytes = fRowBytes;
517 SkASSERT(srcRowBytes >= 0);
518
519 size_t safeSize = getSafeSize();
520 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
521 if (getConfig() == kRLE_Index8_Config || src == NULL ||
522 static_cast<uint32_t>(srcRowBytes) < rowBytes ||
523 safeSize == 0 ||
524 srcSize < ComputeSafeSize(getConfig(), fWidth, fHeight, srcRowBytes)) {
525 return false;
526 }
527
528 SkAutoLockPixels lock(*this);
529 if (static_cast<uint32_t>(srcRowBytes) == fRowBytes) {
530 // This implementation will write bytes beyond the end of each row,
531 // excluding the last row, if the bitmap's stride is greater than
532 // strictly required by the current config.
533 memcpy(getPixels(), src, safeSize);
534 } else {
535 // Just copy the bytes we need on each line.
536 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src);
537 uint8_t* dstP = reinterpret_cast<uint8_t*>(getPixels());
538 for (uint32_t row = 0; row < fHeight;
539 row++, srcP += srcRowBytes, dstP += fRowBytes) {
540 memcpy(dstP, srcP, rowBytes);
541 }
542 }
543
544 return true;
545}
546
547///////////////////////////////////////////////////////////////////////////////
548
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549bool SkBitmap::isOpaque() const {
550 switch (fConfig) {
551 case kNo_Config:
552 return true;
553
554 case kA1_Config:
555 case kA8_Config:
556 case kARGB_4444_Config:
557 case kARGB_8888_Config:
558 return (fFlags & kImageIsOpaque_Flag) != 0;
559
560 case kIndex8_Config:
561 case kRLE_Index8_Config: {
562 uint32_t flags = 0;
563
564 this->lockPixels();
565 // if lockPixels failed, we may not have a ctable ptr
566 if (fColorTable) {
567 flags = fColorTable->getFlags();
568 }
569 this->unlockPixels();
570
571 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
572 }
573
574 case kRGB_565_Config:
575 return true;
576
577 default:
578 SkASSERT(!"unknown bitmap config pased to isOpaque");
579 return false;
580 }
581}
582
583void SkBitmap::setIsOpaque(bool isOpaque) {
584 /* we record this regardless of fConfig, though it is ignored in
585 isOpaque() for configs that can't support per-pixel alpha.
586 */
587 if (isOpaque) {
588 fFlags |= kImageIsOpaque_Flag;
589 } else {
590 fFlags &= ~kImageIsOpaque_Flag;
591 }
592}
593
junov@google.com4ee7ae52011-06-30 17:30:49 +0000594bool SkBitmap::isVolatile() const {
595 return (fFlags & kImageIsVolatile_Flag) != 0;
596}
597
598void SkBitmap::setIsVolatile(bool isVolatile) {
599 if (isVolatile) {
600 fFlags |= kImageIsVolatile_Flag;
601 } else {
602 fFlags &= ~kImageIsVolatile_Flag;
603 }
604}
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606void* SkBitmap::getAddr(int x, int y) const {
607 SkASSERT((unsigned)x < (unsigned)this->width());
608 SkASSERT((unsigned)y < (unsigned)this->height());
609
610 char* base = (char*)this->getPixels();
611 if (base) {
612 base += y * this->rowBytes();
613 switch (this->config()) {
614 case SkBitmap::kARGB_8888_Config:
615 base += x << 2;
616 break;
617 case SkBitmap::kARGB_4444_Config:
618 case SkBitmap::kRGB_565_Config:
619 base += x << 1;
620 break;
621 case SkBitmap::kA8_Config:
622 case SkBitmap::kIndex8_Config:
623 base += x;
624 break;
625 case SkBitmap::kA1_Config:
626 base += x >> 3;
627 break;
628 case kRLE_Index8_Config:
629 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
630 base = NULL;
631 break;
632 default:
633 SkASSERT(!"Can't return addr for config");
634 base = NULL;
635 break;
636 }
637 }
638 return base;
639}
640
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000641SkColor SkBitmap::getColor(int x, int y) const {
642 SkASSERT((unsigned)x < (unsigned)this->width());
643 SkASSERT((unsigned)y < (unsigned)this->height());
644
645 switch (this->config()) {
646 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000647 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000648 uint8_t mask = 1 << (7 - (x % 8));
649 if (addr[0] & mask) {
650 return SK_ColorBLACK;
651 } else {
652 return 0;
653 }
654 }
655 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000656 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000657 return SkColorSetA(0, addr[0]);
658 }
659 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000660 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000661 return SkUnPreMultiply::PMColorToColor(c);
662 }
663 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000664 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000665 return SkPixel16ToColor(addr[0]);
666 }
667 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000668 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000669 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
670 return SkUnPreMultiply::PMColorToColor(c);
671 }
672 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000673 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000674 return SkUnPreMultiply::PMColorToColor(addr[0]);
675 }
676 case kRLE_Index8_Config: {
677 uint8_t dst;
678 const SkBitmap::RLEPixels* rle =
reed@google.com3b521d02011-04-29 11:53:41 +0000679 (const SkBitmap::RLEPixels*)this->getPixels();
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000680 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
681 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
682 }
683 case kNo_Config:
684 case kConfigCount:
685 SkASSERT(false);
686 return 0;
687 }
688 SkASSERT(false); // Not reached.
689 return 0;
690}
691
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692///////////////////////////////////////////////////////////////////////////////
693///////////////////////////////////////////////////////////////////////////////
694
695void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
696 SkDEBUGCODE(this->validate();)
697
698 if (0 == fWidth || 0 == fHeight ||
699 kNo_Config == fConfig || kIndex8_Config == fConfig) {
700 return;
701 }
702
703 SkAutoLockPixels alp(*this);
704 // perform this check after the lock call
705 if (!this->readyToDraw()) {
706 return;
707 }
708
709 int height = fHeight;
710 const int width = fWidth;
711 const int rowBytes = fRowBytes;
712
713 // make rgb premultiplied
714 if (255 != a) {
715 r = SkAlphaMul(r, a);
716 g = SkAlphaMul(g, a);
717 b = SkAlphaMul(b, a);
718 }
719
720 switch (fConfig) {
721 case kA1_Config: {
722 uint8_t* p = (uint8_t*)fPixels;
723 const int count = (width + 7) >> 3;
724 a = (a >> 7) ? 0xFF : 0;
725 SkASSERT(count <= rowBytes);
726 while (--height >= 0) {
727 memset(p, a, count);
728 p += rowBytes;
729 }
730 break;
731 }
732 case kA8_Config: {
733 uint8_t* p = (uint8_t*)fPixels;
734 while (--height >= 0) {
735 memset(p, a, width);
736 p += rowBytes;
737 }
738 break;
739 }
740 case kARGB_4444_Config:
741 case kRGB_565_Config: {
742 uint16_t* p = (uint16_t*)fPixels;
743 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000744
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 if (kARGB_4444_Config == fConfig) {
746 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
747 } else { // kRGB_565_Config
748 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
749 b >> (8 - SK_B16_BITS));
750 }
751 while (--height >= 0) {
752 sk_memset16(p, v, width);
753 p = (uint16_t*)((char*)p + rowBytes);
754 }
755 break;
756 }
757 case kARGB_8888_Config: {
758 uint32_t* p = (uint32_t*)fPixels;
759 uint32_t v = SkPackARGB32(a, r, g, b);
760
761 while (--height >= 0) {
762 sk_memset32(p, v, width);
763 p = (uint32_t*)((char*)p + rowBytes);
764 }
765 break;
766 }
767 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 this->notifyPixelsChanged();
770}
771
772//////////////////////////////////////////////////////////////////////////////////////
773//////////////////////////////////////////////////////////////////////////////////////
774
775#define SUB_OFFSET_FAILURE ((size_t)-1)
776
777static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
778 SkASSERT((unsigned)x < (unsigned)bm.width());
779 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000780
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 switch (bm.getConfig()) {
782 case SkBitmap::kA8_Config:
783 case SkBitmap:: kIndex8_Config:
784 // x is fine as is for the calculation
785 break;
786
787 case SkBitmap::kRGB_565_Config:
788 case SkBitmap::kARGB_4444_Config:
789 x <<= 1;
790 break;
791
792 case SkBitmap::kARGB_8888_Config:
793 x <<= 2;
794 break;
795
796 case SkBitmap::kNo_Config:
797 case SkBitmap::kA1_Config:
798 default:
799 return SUB_OFFSET_FAILURE;
800 }
801 return y * bm.rowBytes() + x;
802}
803
804bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
805 SkDEBUGCODE(this->validate();)
806
807 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
808 return false; // no src pixels
809 }
810
811 SkIRect srcRect, r;
812 srcRect.set(0, 0, this->width(), this->height());
813 if (!r.intersect(srcRect, subset)) {
814 return false; // r is empty (i.e. no intersection)
815 }
816
817 if (kRLE_Index8_Config == fConfig) {
818 SkAutoLockPixels alp(*this);
819 // don't call readyToDraw(), since we can operate w/o a colortable
820 // at this stage
821 if (this->getPixels() == NULL) {
822 return false;
823 }
824 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 bm.setConfig(kIndex8_Config, r.width(), r.height());
827 bm.allocPixels(this->getColorTable());
828 if (NULL == bm.getPixels()) {
829 return false;
830 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000831
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
833 uint8_t* dst = bm.getAddr8(0, 0);
834 const int width = bm.width();
835 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000836
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 for (int y = r.fTop; y < r.fBottom; y++) {
838 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
839 dst += rowBytes;
840 }
841 result->swap(bm);
842 return true;
843 }
844
845 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
846 if (SUB_OFFSET_FAILURE == offset) {
847 return false; // config not supported
848 }
849
850 SkBitmap dst;
851 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
852
853 if (fPixelRef) {
854 // share the pixelref with a custom offset
855 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
856 } else {
857 // share the pixels (owned by the caller)
858 dst.setPixels((char*)fPixels + offset, this->getColorTable());
859 }
860 SkDEBUGCODE(dst.validate();)
861
862 // we know we're good, so commit to result
863 result->swap(dst);
864 return true;
865}
866
867///////////////////////////////////////////////////////////////////////////////
868
869#include "SkCanvas.h"
870#include "SkPaint.h"
871
reed@android.comfbaa88d2009-05-06 17:44:34 +0000872bool SkBitmap::canCopyTo(Config dstConfig) const {
873 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 return false;
875 }
876
reed@android.comfbaa88d2009-05-06 17:44:34 +0000877 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 switch (dstConfig) {
879 case kA8_Config:
880 case kARGB_4444_Config:
881 case kRGB_565_Config:
882 case kARGB_8888_Config:
883 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000884 case kA1_Config:
885 case kIndex8_Config:
886 if (!sameConfigs) {
887 return false;
888 }
889 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 default:
891 return false;
892 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000893
894 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
895 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 return false;
897 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000898
reed@android.comfbaa88d2009-05-06 17:44:34 +0000899 return true;
900}
901
902bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
903 if (!this->canCopyTo(dstConfig)) {
904 return false;
905 }
906
reed@google.com50dfa012011-04-01 19:05:36 +0000907 // if we have a texture, first get those pixels
908 SkBitmap tmpSrc;
909 const SkBitmap* src = this;
910
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000911 if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000912 SkASSERT(tmpSrc.width() == this->width());
913 SkASSERT(tmpSrc.height() == this->height());
914
915 // did we get lucky and we can just return tmpSrc?
916 if (tmpSrc.config() == dstConfig && NULL == alloc) {
917 dst->swap(tmpSrc);
918 return true;
919 }
920
921 // fall through to the raster case
922 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000923 }
reed@android.com311c82d2009-05-05 23:13:23 +0000924
reed@google.com50dfa012011-04-01 19:05:36 +0000925 // we lock this now, since we may need its colortable
926 SkAutoLockPixels srclock(*src);
927 if (!src->readyToDraw()) {
928 return false;
929 }
930
931 SkBitmap tmpDst;
932 tmpDst.setConfig(dstConfig, src->width(), src->height());
933
weita@google.comf9ab99a2009-05-03 18:23:30 +0000934 // allocate colortable if srcConfig == kIndex8_Config
935 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000936 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000937 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000938 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000939 return false;
940 }
reed@google.com50dfa012011-04-01 19:05:36 +0000941
942 SkAutoLockPixels dstlock(tmpDst);
943 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 // allocator/lock failed
945 return false;
946 }
reed@google.com50dfa012011-04-01 19:05:36 +0000947
reed@android.comfbaa88d2009-05-06 17:44:34 +0000948 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000949 */
reed@google.com50dfa012011-04-01 19:05:36 +0000950 if (src->config() == dstConfig) {
951 if (tmpDst.getSize() == src->getSize()) {
952 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000953 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000954 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
955 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000956 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000957 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
958 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000959 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000960 srcP += src->rowBytes();
961 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000962 }
963 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000964 } else {
965 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000966 if (!src->isOpaque()) {
967 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000968 }
969
reed@google.com50dfa012011-04-01 19:05:36 +0000970 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000971 SkPaint paint;
972
973 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000974 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 }
976
reed@google.com50dfa012011-04-01 19:05:36 +0000977 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000978
reed@google.com50dfa012011-04-01 19:05:36 +0000979 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 return true;
981}
982
983///////////////////////////////////////////////////////////////////////////////
984///////////////////////////////////////////////////////////////////////////////
985
986static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
987 const SkBitmap& src) {
988 x <<= 1;
989 y <<= 1;
990 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000991 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 SkPMColor c, ag, rb;
993
994 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
995 if (x < src.width() - 1) {
996 p += 1;
997 }
998 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
999
reed@android.com829c83c2009-06-08 12:05:31 +00001000 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001002 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 }
1004 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1005 if (x < src.width() - 1) {
1006 p += 1;
1007 }
1008 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1009
1010 *dst->getAddr32(x >> 1, y >> 1) =
1011 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1012}
1013
1014static inline uint32_t expand16(U16CPU c) {
1015 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1016}
1017
1018// returns dirt in the top 16bits, but we don't care, since we only
1019// store the low 16bits.
1020static inline U16CPU pack16(uint32_t c) {
1021 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1022}
1023
1024static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1025 const SkBitmap& src) {
1026 x <<= 1;
1027 y <<= 1;
1028 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001029 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001031
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001033 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 p += 1;
1035 }
1036 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001037
reed@android.com829c83c2009-06-08 12:05:31 +00001038 p = baseP;
1039 if (y < src.height() - 1) {
1040 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 }
1042 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001043 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 p += 1;
1045 }
1046 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001047
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1049}
1050
1051static uint32_t expand4444(U16CPU c) {
1052 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1053}
1054
1055static U16CPU collaps4444(uint32_t c) {
1056 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1057}
1058
1059static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1060 const SkBitmap& src) {
1061 x <<= 1;
1062 y <<= 1;
1063 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001064 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001066
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 c = expand4444(*p);
1068 if (x < src.width() - 1) {
1069 p += 1;
1070 }
1071 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001072
reed@android.com829c83c2009-06-08 12:05:31 +00001073 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001075 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 }
1077 c += expand4444(*p);
1078 if (x < src.width() - 1) {
1079 p += 1;
1080 }
1081 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001082
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1084}
1085
1086void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 if (forceRebuild)
1088 this->freeMipMap();
1089 else if (fMipMap)
1090 return; // we're already built
1091
1092 SkASSERT(NULL == fMipMap);
1093
1094 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1095
1096 const SkBitmap::Config config = this->getConfig();
1097
1098 switch (config) {
1099 case kARGB_8888_Config:
1100 proc = downsampleby2_proc32;
1101 break;
1102 case kRGB_565_Config:
1103 proc = downsampleby2_proc16;
1104 break;
1105 case kARGB_4444_Config:
1106 proc = downsampleby2_proc4444;
1107 break;
1108 case kIndex8_Config:
1109 case kA8_Config:
1110 default:
1111 return; // don't build mipmaps for these configs
1112 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001113
reed@android.com149e2f62009-05-22 14:39:03 +00001114 SkAutoLockPixels alp(*this);
1115 if (!this->readyToDraw()) {
1116 return;
1117 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118
1119 // whip through our loop to compute the exact size needed
1120 size_t size = 0;
1121 int maxLevels = 0;
1122 {
reed@android.com149e2f62009-05-22 14:39:03 +00001123 int width = this->width();
1124 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 for (;;) {
1126 width >>= 1;
1127 height >>= 1;
1128 if (0 == width || 0 == height) {
1129 break;
1130 }
1131 size += ComputeRowBytes(config, width) * height;
1132 maxLevels += 1;
1133 }
1134 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001135
reed@android.com149e2f62009-05-22 14:39:03 +00001136 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 if (0 == maxLevels) {
1138 return;
1139 }
1140
reed@android.com149e2f62009-05-22 14:39:03 +00001141 SkBitmap srcBM(*this);
1142 srcBM.lockPixels();
1143 if (!srcBM.readyToDraw()) {
1144 return;
1145 }
1146
1147 MipMap* mm = MipMap::Alloc(maxLevels, size);
1148 if (NULL == mm) {
1149 return;
1150 }
1151
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152 MipLevel* level = mm->levels();
1153 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001154 int width = this->width();
1155 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001157 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158
1159 for (int i = 0; i < maxLevels; i++) {
1160 width >>= 1;
1161 height >>= 1;
1162 rowBytes = ComputeRowBytes(config, width);
1163
1164 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001165 level[i].fWidth = width;
1166 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001167 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168
1169 dstBM.setConfig(config, width, height, rowBytes);
1170 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001171
reed@android.com149e2f62009-05-22 14:39:03 +00001172 for (int y = 0; y < height; y++) {
1173 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 proc(&dstBM, x, y, srcBM);
1175 }
1176 }
1177
1178 srcBM = dstBM;
1179 addr += height * rowBytes;
1180 }
1181 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1182 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183}
1184
1185bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187}
1188
1189int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001190 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001192 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001193
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 int level = ComputeMipLevel(sx, sy) >> 16;
1195 SkASSERT(level >= 0);
1196 if (level <= 0) {
1197 return 0;
1198 }
1199
1200 if (level >= fMipMap->fLevelCount) {
1201 level = fMipMap->fLevelCount - 1;
1202 }
1203 if (dst) {
1204 const MipLevel& mip = fMipMap->levels()[level - 1];
1205 dst->setConfig((SkBitmap::Config)this->config(),
1206 mip.fWidth, mip.fHeight, mip.fRowBytes);
1207 dst->setPixels(mip.fPixels);
1208 }
1209 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210}
1211
1212SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 sx = SkAbs32(sx);
1214 sy = SkAbs32(sy);
1215 if (sx < sy) {
1216 sx = sy;
1217 }
1218 if (sx < SK_Fixed1) {
1219 return 0;
1220 }
1221 int clz = SkCLZ(sx);
1222 SkASSERT(clz >= 1 && clz <= 15);
1223 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224}
1225
1226///////////////////////////////////////////////////////////////////////////////
1227
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001228static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 int alphaRowBytes) {
1230 SkASSERT(alpha != NULL);
1231 SkASSERT(alphaRowBytes >= src.width());
1232
1233 SkBitmap::Config config = src.getConfig();
1234 int w = src.width();
1235 int h = src.height();
1236 int rb = src.rowBytes();
1237
reed@android.com1cdcb512009-08-24 19:11:00 +00001238 SkAutoLockPixels alp(src);
1239 if (!src.readyToDraw()) {
1240 // zero out the alpha buffer and return
1241 while (--h >= 0) {
1242 memset(alpha, 0, w);
1243 alpha += alphaRowBytes;
1244 }
1245 return false;
1246 }
reed@google.com82065d62011-02-07 15:30:46 +00001247
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1249 const uint8_t* s = src.getAddr8(0, 0);
1250 while (--h >= 0) {
1251 memcpy(alpha, s, w);
1252 s += rb;
1253 alpha += alphaRowBytes;
1254 }
1255 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1256 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1257 while (--h >= 0) {
1258 for (int x = 0; x < w; x++) {
1259 alpha[x] = SkGetPackedA32(s[x]);
1260 }
1261 s = (const SkPMColor*)((const char*)s + rb);
1262 alpha += alphaRowBytes;
1263 }
1264 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1265 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1266 while (--h >= 0) {
1267 for (int x = 0; x < w; x++) {
1268 alpha[x] = SkPacked4444ToA32(s[x]);
1269 }
1270 s = (const SkPMColor16*)((const char*)s + rb);
1271 alpha += alphaRowBytes;
1272 }
1273 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1274 SkColorTable* ct = src.getColorTable();
1275 if (ct) {
1276 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1277 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1278 while (--h >= 0) {
1279 for (int x = 0; x < w; x++) {
1280 alpha[x] = SkGetPackedA32(table[s[x]]);
1281 }
1282 s += rb;
1283 alpha += alphaRowBytes;
1284 }
1285 ct->unlockColors(false);
1286 }
1287 } else { // src is opaque, so just fill alpha[] with 0xFF
1288 memset(alpha, 0xFF, h * alphaRowBytes);
1289 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001290 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291}
1292
1293#include "SkPaint.h"
1294#include "SkMaskFilter.h"
1295#include "SkMatrix.h"
1296
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001297bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001298 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 SkDEBUGCODE(this->validate();)
1300
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001301 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 SkMatrix identity;
1303 SkMask srcM, dstM;
1304
1305 srcM.fBounds.set(0, 0, this->width(), this->height());
1306 srcM.fRowBytes = SkAlign4(this->width());
1307 srcM.fFormat = SkMask::kA8_Format;
1308
1309 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1310
1311 // compute our (larger?) dst bounds if we have a filter
1312 if (NULL != filter) {
1313 identity.reset();
1314 srcM.fImage = NULL;
1315 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1316 goto NO_FILTER_CASE;
1317 }
1318 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1319 } else {
1320 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001321 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001323 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1324 // Allocation of pixels for alpha bitmap failed.
1325 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1326 tmpBitmap.width(), tmpBitmap.height());
1327 return false;
1328 }
1329 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 if (offset) {
1331 offset->set(0, 0);
1332 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001333 tmpBitmap.swap(*dst);
1334 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 }
1336
1337 SkAutoMaskImage srcCleanup(&srcM, true);
1338
1339 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1340 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1341 goto NO_FILTER_CASE;
1342 }
1343
1344 SkAutoMaskImage dstCleanup(&dstM, false);
1345
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001346 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001348 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1349 // Allocation of pixels for alpha bitmap failed.
1350 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1351 tmpBitmap.width(), tmpBitmap.height());
1352 return false;
1353 }
1354 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 if (offset) {
1356 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1357 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001358 SkDEBUGCODE(tmpBitmap.validate();)
1359
1360 tmpBitmap.swap(*dst);
1361 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362}
1363
1364///////////////////////////////////////////////////////////////////////////////
1365
1366enum {
1367 SERIALIZE_PIXELTYPE_NONE,
1368 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1369 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1370 SERIALIZE_PIXELTYPE_REF_DATA,
1371 SERIALIZE_PIXELTYPE_REF_PTR,
1372};
1373
1374static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1375 size_t len = strlen(str);
1376 buffer.write32(len);
1377 buffer.writePad(str, len);
1378}
1379
1380static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1381 size_t len = buffer.readInt();
1382 SkAutoSMalloc<256> storage(len + 1);
1383 char* str = (char*)storage.get();
1384 buffer.read(str, len);
1385 str[len] = 0;
1386 return SkPixelRef::NameToFactory(str);
1387}
1388
1389/*
1390 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1391 we just have pixels, then we can only flatten the pixels, or write out an
1392 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001393
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 With a pixelref, we still have the question of recognizing when two sitings
1395 of the same pixelref are the same, and when they are different. Perhaps we
1396 should look at the generationID and keep a record of that in some dictionary
1397 associated with the buffer. SkGLTextureCache does this sort of thing to know
1398 when to create a new texture.
1399*/
1400void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1401 buffer.write32(fWidth);
1402 buffer.write32(fHeight);
1403 buffer.write32(fRowBytes);
1404 buffer.write8(fConfig);
1405 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 /* If we are called in this mode, then it is up to the caller to manage
1408 the owner-counts on the pixelref, as we just record the ptr itself.
1409 */
1410 if (!buffer.persistBitmapPixels()) {
1411 if (fPixelRef) {
1412 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1413 buffer.write32(fPixelRefOffset);
1414 buffer.writeRefCnt(fPixelRef);
1415 return;
1416 } else {
1417 // we ignore the non-persist request, since we don't have a ref
1418 // ... or we could just write an empty bitmap...
1419 // (true) will write an empty bitmap, (false) will flatten the pix
1420 if (true) {
1421 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1422 return;
1423 }
1424 }
1425 }
1426
1427 if (fPixelRef) {
1428 SkPixelRef::Factory fact = fPixelRef->getFactory();
1429 if (fact) {
1430 const char* name = SkPixelRef::FactoryToName(fact);
1431 if (name && *name) {
1432 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1433 buffer.write32(fPixelRefOffset);
1434 writeString(buffer, name);
1435 fPixelRef->flatten(buffer);
1436 return;
1437 }
1438 }
1439 // if we get here, we can't record the pixels
1440 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1441 } else if (fPixels) {
1442 if (fColorTable) {
1443 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1444 fColorTable->flatten(buffer);
1445 } else {
1446 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1447 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001448 buffer.writePad(fPixels, this->getSafeSize());
1449 // There is no writeZeroPad() fcn, so write individual bytes.
1450 if (this->getSize() > this->getSafeSize()) {
1451 size_t deltaSize = this->getSize() - this->getSafeSize();
1452 // Need aligned pointer to write into due to internal implementa-
1453 // tion of SkWriter32.
1454 memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
1455 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 } else {
1457 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1458 }
1459}
1460
1461void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1462 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001463
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 int width = buffer.readInt();
1465 int height = buffer.readInt();
1466 int rowBytes = buffer.readInt();
1467 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001468
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 this->setConfig((Config)config, width, height, rowBytes);
1470 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001471
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472 int reftype = buffer.readU8();
1473 switch (reftype) {
1474 case SERIALIZE_PIXELTYPE_REF_PTR: {
1475 size_t offset = buffer.readU32();
1476 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1477 this->setPixelRef(pr, offset);
1478 break;
1479 }
1480 case SERIALIZE_PIXELTYPE_REF_DATA: {
1481 size_t offset = buffer.readU32();
1482 SkPixelRef::Factory fact = deserialize_factory(buffer);
1483 SkPixelRef* pr = fact(buffer);
reed@google.com82065d62011-02-07 15:30:46 +00001484 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 break;
1486 }
1487 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1488 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1489 SkColorTable* ctable = NULL;
1490 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1491 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1492 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001493 size_t size = this->getSize();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 if (this->allocPixels(ctable)) {
1495 this->lockPixels();
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001496 // Just read what we need.
1497 buffer.read(this->getPixels(), this->getSafeSize());
1498 // Keep aligned for subsequent reads.
1499 buffer.skip(size - this->getSafeSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 this->unlockPixels();
1501 } else {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001502 buffer.skip(size); // Still skip the full-sized buffer though.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503 }
reed@android.com149e2f62009-05-22 14:39:03 +00001504 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 break;
1506 }
1507 case SERIALIZE_PIXELTYPE_NONE:
1508 break;
1509 default:
1510 SkASSERT(!"unrecognized pixeltype in serialized data");
1511 sk_throw();
1512 }
1513}
1514
1515///////////////////////////////////////////////////////////////////////////////
1516
1517SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1518 fHeight = height;
1519 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001520 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521}
1522
1523SkBitmap::RLEPixels::~RLEPixels() {
1524 sk_free(fYPtrs);
1525}
1526
1527///////////////////////////////////////////////////////////////////////////////
1528
1529#ifdef SK_DEBUG
1530void SkBitmap::validate() const {
1531 SkASSERT(fConfig < kConfigCount);
1532 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
junov@google.com4ee7ae52011-06-30 17:30:49 +00001533 SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534 SkASSERT(fPixelLockCount >= 0);
1535 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1536 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1537
1538#if 0 // these asserts are not thread-correct, so disable for now
1539 if (fPixelRef) {
1540 if (fPixelLockCount > 0) {
1541 SkASSERT(fPixelRef->getLockCount() > 0);
1542 } else {
1543 SkASSERT(NULL == fPixels);
1544 SkASSERT(NULL == fColorTable);
1545 }
1546 }
1547#endif
1548}
1549#endif