blob: a9b9c99d9ece9e8ef35d27b3d1228d111bb4aeeb [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
reed@android.com149e2f62009-05-22 14:39:03 +000030static bool isPos32Bits(const Sk64& value) {
31 return !value.isNeg() && value.is32();
32}
33
reed@android.com8a1c16f2008-12-17 15:59:43 +000034struct MipLevel {
35 void* fPixels;
36 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000037 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000038};
39
40struct SkBitmap::MipMap : SkNoncopyable {
41 int32_t fRefCnt;
42 int fLevelCount;
43// MipLevel fLevel[fLevelCount];
44// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000045
reed@android.com8a1c16f2008-12-17 15:59:43 +000046 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000047 if (levelCount < 0) {
48 return NULL;
49 }
50 Sk64 size;
51 size.setMul(levelCount + 1, sizeof(MipLevel));
52 size.add(sizeof(MipMap));
53 size.add(pixelSize);
54 if (!isPos32Bits(size)) {
55 return NULL;
56 }
57 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 mm->fRefCnt = 1;
59 mm->fLevelCount = levelCount;
60 return mm;
61 }
62
63 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
64 MipLevel* levels() { return (MipLevel*)(this + 1); }
65
66 const void* pixels() const { return levels() + fLevelCount; }
67 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000068
reed@android.com149e2f62009-05-22 14:39:03 +000069 void ref() {
70 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
71 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 }
73 }
reed@android.com149e2f62009-05-22 14:39:03 +000074 void unref() {
75 SkASSERT(fRefCnt > 0);
76 if (sk_atomic_dec(&fRefCnt) == 1) {
77 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 }
79 }
80};
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
82///////////////////////////////////////////////////////////////////////////////
83///////////////////////////////////////////////////////////////////////////////
84
85SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000086 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000087}
88
89SkBitmap::SkBitmap(const SkBitmap& src) {
90 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000091 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 *this = src;
93 SkDEBUGCODE(this->validate();)
94}
95
96SkBitmap::~SkBitmap() {
97 SkDEBUGCODE(this->validate();)
98 this->freePixels();
99}
100
101SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
102 if (this != &src) {
103 this->freePixels();
104 memcpy(this, &src, sizeof(src));
105
106 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000107 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000108 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109
110 // we reset our locks if we get blown away
111 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000112
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 /* The src could be in 3 states
114 1. no pixelref, in which case we just copy/ref the pixels/ctable
115 2. unlocked pixelref, pixels/ctable should be null
116 3. locked pixelref, we should lock the ref again ourselves
117 */
118 if (NULL == fPixelRef) {
119 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000120 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 } else { // we have a pixelref, so pixels/ctable reflect it
122 // ignore the values from the memcpy
123 fPixels = NULL;
124 fColorTable = NULL;
125 }
126 }
127
128 SkDEBUGCODE(this->validate();)
129 return *this;
130}
131
132void SkBitmap::swap(SkBitmap& other) {
133 SkTSwap<SkColorTable*>(fColorTable, other.fColorTable);
134 SkTSwap<SkPixelRef*>(fPixelRef, other.fPixelRef);
135 SkTSwap<size_t>(fPixelRefOffset, other.fPixelRefOffset);
136 SkTSwap<int>(fPixelLockCount, other.fPixelLockCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 SkTSwap<MipMap*>(fMipMap, other.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 SkTSwap<void*>(fPixels, other.fPixels);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 SkTSwap<uint32_t>(fRowBytes, other.fRowBytes);
reed@android.comf459a492009-03-27 12:33:50 +0000140 SkTSwap<uint32_t>(fWidth, other.fWidth);
141 SkTSwap<uint32_t>(fHeight, other.fHeight);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 SkTSwap<uint8_t>(fConfig, other.fConfig);
143 SkTSwap<uint8_t>(fFlags, other.fFlags);
144 SkTSwap<uint8_t>(fBytesPerPixel, other.fBytesPerPixel);
145
146 SkDEBUGCODE(this->validate();)
147}
148
149void SkBitmap::reset() {
150 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000151 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152}
153
154int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
155 int bpp;
156 switch (config) {
157 case kNo_Config:
158 case kA1_Config:
159 bpp = 0; // not applicable
160 break;
161 case kRLE_Index8_Config:
162 case kA8_Config:
163 case kIndex8_Config:
164 bpp = 1;
165 break;
166 case kRGB_565_Config:
167 case kARGB_4444_Config:
168 bpp = 2;
169 break;
170 case kARGB_8888_Config:
171 bpp = 4;
172 break;
173 default:
174 SkASSERT(!"unknown config");
175 bpp = 0; // error
176 break;
177 }
178 return bpp;
179}
180
181int SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000182 if (width < 0) {
183 return 0;
184 }
185
186 Sk64 rowBytes;
187 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
189 switch (c) {
190 case kNo_Config:
191 case kRLE_Index8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 break;
193 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000194 rowBytes.set(width);
195 rowBytes.add(7);
196 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 break;
198 case kA8_Config:
199 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000200 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 break;
202 case kRGB_565_Config:
203 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000204 rowBytes.set(width);
205 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 break;
207 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000208 rowBytes.set(width);
209 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 break;
211 default:
212 SkASSERT(!"unknown config");
213 break;
214 }
reed@android.com149e2f62009-05-22 14:39:03 +0000215 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216}
217
218Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
219 Sk64 size;
220 size.setMul(SkBitmap::ComputeRowBytes(c, width), height);
221 return size;
222}
223
224size_t SkBitmap::ComputeSize(Config c, int width, int height) {
225 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000226 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227}
228
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000229Sk64 SkBitmap::ComputeSafeSize64(Config config,
230 uint32_t width,
231 uint32_t height,
232 uint32_t rowBytes) {
233 Sk64 safeSize;
234 safeSize.setZero();
235 if (height > 0) {
236 safeSize.set(ComputeRowBytes(config, width));
237 Sk64 sizeAllButLastRow;
238 sizeAllButLastRow.setMul(height - 1, rowBytes);
239 safeSize.add(sizeAllButLastRow);
240 }
241 SkASSERT(!safeSize.isNeg());
242 return safeSize;
243}
244
245size_t SkBitmap::ComputeSafeSize(Config config,
246 uint32_t width,
247 uint32_t height,
248 uint32_t rowBytes) {
249 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
250 return (safeSize.is32() ? safeSize.get32() : 0);
251}
252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
254 this->freePixels();
255
reed@android.com149e2f62009-05-22 14:39:03 +0000256 if ((width | height | rowBytes) < 0) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000257 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000258 }
259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 if (rowBytes == 0) {
261 rowBytes = SkBitmap::ComputeRowBytes(c, width);
reed@android.com149e2f62009-05-22 14:39:03 +0000262 if (0 == rowBytes && kNo_Config != c) {
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000263 goto err;
reed@android.com149e2f62009-05-22 14:39:03 +0000264 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 fConfig = SkToU8(c);
reed@android.comf459a492009-03-27 12:33:50 +0000268 fWidth = width;
269 fHeight = height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 fRowBytes = rowBytes;
271
272 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(c);
273
274 SkDEBUGCODE(this->validate();)
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000275 return;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000276
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000277 // if we got here, we had an error, so we reset the bitmap to empty
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000278err:
279 this->reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280}
281
282void SkBitmap::updatePixelsFromRef() const {
283 if (NULL != fPixelRef) {
284 if (fPixelLockCount > 0) {
285 SkASSERT(fPixelRef->getLockCount() > 0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 void* p = fPixelRef->pixels();
288 if (NULL != p) {
289 p = (char*)p + fPixelRefOffset;
290 }
291 fPixels = p;
292 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
293 } else {
294 SkASSERT(0 == fPixelLockCount);
295 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000296 if (fColorTable) {
297 fColorTable->unref();
298 fColorTable = NULL;
299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 }
301 }
302}
303
304SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
305 // do this first, we that we never have a non-zero offset with a null ref
306 if (NULL == pr) {
307 offset = 0;
308 }
309
310 if (fPixelRef != pr || fPixelRefOffset != offset) {
311 if (fPixelRef != pr) {
312 this->freePixels();
313 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000314
reed@google.com82065d62011-02-07 15:30:46 +0000315 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 fPixelRef = pr;
317 }
318 fPixelRefOffset = offset;
319 this->updatePixelsFromRef();
320 }
321
322 SkDEBUGCODE(this->validate();)
323 return pr;
324}
325
326void SkBitmap::lockPixels() const {
327 if (NULL != fPixelRef && 1 == ++fPixelLockCount) {
328 fPixelRef->lockPixels();
329 this->updatePixelsFromRef();
330 }
331 SkDEBUGCODE(this->validate();)
332}
333
334void SkBitmap::unlockPixels() const {
335 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
336
337 if (NULL != fPixelRef && 0 == --fPixelLockCount) {
338 fPixelRef->unlockPixels();
339 this->updatePixelsFromRef();
340 }
341 SkDEBUGCODE(this->validate();)
342}
343
344void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
345 this->freePixels();
346 fPixels = p;
347 SkRefCnt_SafeAssign(fColorTable, ctable);
348
349 SkDEBUGCODE(this->validate();)
350}
351
352bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
353 HeapAllocator stdalloc;
354
355 if (NULL == allocator) {
356 allocator = &stdalloc;
357 }
358 return allocator->allocPixelRef(this, ctable);
359}
360
361void SkBitmap::freePixels() {
362 // if we're gonna free the pixels, we certainly need to free the mipmap
363 this->freeMipMap();
364
reed@android.com149e2f62009-05-22 14:39:03 +0000365 if (fColorTable) {
366 fColorTable->unref();
367 fColorTable = NULL;
368 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369
370 if (NULL != fPixelRef) {
371 if (fPixelLockCount > 0) {
372 fPixelRef->unlockPixels();
373 }
374 fPixelRef->unref();
375 fPixelRef = NULL;
376 fPixelRefOffset = 0;
377 }
378 fPixelLockCount = 0;
379 fPixels = NULL;
380}
381
382void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000383 if (fMipMap) {
384 fMipMap->unref();
385 fMipMap = NULL;
386 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387}
388
389uint32_t SkBitmap::getGenerationID() const {
390 return fPixelRef ? fPixelRef->getGenerationID() : 0;
391}
392
393void SkBitmap::notifyPixelsChanged() const {
394 if (fPixelRef) {
395 fPixelRef->notifyPixelsChanged();
396 }
397}
398
reed@android.comce4e53a2010-09-09 16:01:26 +0000399SkGpuTexture* SkBitmap::getTexture() const {
400 return fPixelRef ? fPixelRef->getTexture() : NULL;
401}
402
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403///////////////////////////////////////////////////////////////////////////////
404
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405/** We explicitly use the same allocator for our pixels that SkMask does,
406 so that we can freely assign memory allocated by one class to the other.
407 */
408bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
409 SkColorTable* ctable) {
410 Sk64 size = dst->getSize64();
411 if (size.isNeg() || !size.is32()) {
412 return false;
413 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000414
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
416 if (NULL == addr) {
417 return false;
418 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
421 // since we're already allocated, we lockPixels right away
422 dst->lockPixels();
423 return true;
424}
425
426///////////////////////////////////////////////////////////////////////////////
427
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000428size_t SkBitmap::getSafeSize() const {
429 // This is intended to be a size_t version of ComputeSafeSize64(), just
430 // faster. The computation is meant to be identical.
431 return (fHeight ? ((fHeight - 1) * fRowBytes) +
432 ComputeRowBytes(getConfig(), fWidth): 0);
433}
434
435Sk64 SkBitmap::getSafeSize64() const {
436 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
437}
438
439bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes)
440 const {
441
442 if (dstRowBytes == -1)
443 dstRowBytes = fRowBytes;
444 SkASSERT(dstRowBytes >= 0);
445
446 if (getConfig() == kRLE_Index8_Config ||
447 dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
448 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
449 return false;
450
451 if (static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
452 size_t safeSize = getSafeSize();
453 if (safeSize > dstSize || safeSize == 0)
454 return false;
455 else {
456 SkAutoLockPixels lock(*this);
457 // This implementation will write bytes beyond the end of each row,
458 // excluding the last row, if the bitmap's stride is greater than
459 // strictly required by the current config.
460 memcpy(dst, getPixels(), safeSize);
461
462 return true;
463 }
464 } else {
465 // If destination has different stride than us, then copy line by line.
466 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
467 dstSize)
468 return false;
469 else {
470 // Just copy what we need on each line.
471 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
472 SkAutoLockPixels lock(*this);
473 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
474 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
475 for (uint32_t row = 0; row < fHeight;
476 row++, srcP += fRowBytes, dstP += dstRowBytes) {
477 memcpy(dstP, srcP, rowBytes);
478 }
479
480 return true;
481 }
482 }
483}
484
485bool SkBitmap::copyPixelsFrom(const void* const src, size_t srcSize,
486 int srcRowBytes) {
487
488 if (srcRowBytes == -1)
489 srcRowBytes = fRowBytes;
490 SkASSERT(srcRowBytes >= 0);
491
492 size_t safeSize = getSafeSize();
493 uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
494 if (getConfig() == kRLE_Index8_Config || src == NULL ||
495 static_cast<uint32_t>(srcRowBytes) < rowBytes ||
496 safeSize == 0 ||
497 srcSize < ComputeSafeSize(getConfig(), fWidth, fHeight, srcRowBytes)) {
498 return false;
499 }
500
501 SkAutoLockPixels lock(*this);
502 if (static_cast<uint32_t>(srcRowBytes) == fRowBytes) {
503 // This implementation will write bytes beyond the end of each row,
504 // excluding the last row, if the bitmap's stride is greater than
505 // strictly required by the current config.
506 memcpy(getPixels(), src, safeSize);
507 } else {
508 // Just copy the bytes we need on each line.
509 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src);
510 uint8_t* dstP = reinterpret_cast<uint8_t*>(getPixels());
511 for (uint32_t row = 0; row < fHeight;
512 row++, srcP += srcRowBytes, dstP += fRowBytes) {
513 memcpy(dstP, srcP, rowBytes);
514 }
515 }
516
517 return true;
518}
519
520///////////////////////////////////////////////////////////////////////////////
521
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522bool SkBitmap::isOpaque() const {
523 switch (fConfig) {
524 case kNo_Config:
525 return true;
526
527 case kA1_Config:
528 case kA8_Config:
529 case kARGB_4444_Config:
530 case kARGB_8888_Config:
531 return (fFlags & kImageIsOpaque_Flag) != 0;
532
533 case kIndex8_Config:
534 case kRLE_Index8_Config: {
535 uint32_t flags = 0;
536
537 this->lockPixels();
538 // if lockPixels failed, we may not have a ctable ptr
539 if (fColorTable) {
540 flags = fColorTable->getFlags();
541 }
542 this->unlockPixels();
543
544 return (flags & SkColorTable::kColorsAreOpaque_Flag) != 0;
545 }
546
547 case kRGB_565_Config:
548 return true;
549
550 default:
551 SkASSERT(!"unknown bitmap config pased to isOpaque");
552 return false;
553 }
554}
555
556void SkBitmap::setIsOpaque(bool isOpaque) {
557 /* we record this regardless of fConfig, though it is ignored in
558 isOpaque() for configs that can't support per-pixel alpha.
559 */
560 if (isOpaque) {
561 fFlags |= kImageIsOpaque_Flag;
562 } else {
563 fFlags &= ~kImageIsOpaque_Flag;
564 }
565}
566
567void* SkBitmap::getAddr(int x, int y) const {
568 SkASSERT((unsigned)x < (unsigned)this->width());
569 SkASSERT((unsigned)y < (unsigned)this->height());
570
571 char* base = (char*)this->getPixels();
572 if (base) {
573 base += y * this->rowBytes();
574 switch (this->config()) {
575 case SkBitmap::kARGB_8888_Config:
576 base += x << 2;
577 break;
578 case SkBitmap::kARGB_4444_Config:
579 case SkBitmap::kRGB_565_Config:
580 base += x << 1;
581 break;
582 case SkBitmap::kA8_Config:
583 case SkBitmap::kIndex8_Config:
584 base += x;
585 break;
586 case SkBitmap::kA1_Config:
587 base += x >> 3;
588 break;
589 case kRLE_Index8_Config:
590 SkASSERT(!"Can't return addr for kRLE_Index8_Config");
591 base = NULL;
592 break;
593 default:
594 SkASSERT(!"Can't return addr for config");
595 base = NULL;
596 break;
597 }
598 }
599 return base;
600}
601
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000602SkColor SkBitmap::getColor(int x, int y) const {
603 SkASSERT((unsigned)x < (unsigned)this->width());
604 SkASSERT((unsigned)y < (unsigned)this->height());
605
606 switch (this->config()) {
607 case SkBitmap::kA1_Config: {
608 uint8_t* addr = getAddr1(x, y);
609 uint8_t mask = 1 << (7 - (x % 8));
610 if (addr[0] & mask) {
611 return SK_ColorBLACK;
612 } else {
613 return 0;
614 }
615 }
616 case SkBitmap::kA8_Config: {
617 uint8_t* addr = getAddr8(x, y);
618 return SkColorSetA(0, addr[0]);
619 }
620 case SkBitmap::kIndex8_Config: {
621 SkPMColor c = getIndex8Color(x, y);
622 return SkUnPreMultiply::PMColorToColor(c);
623 }
624 case SkBitmap::kRGB_565_Config: {
625 uint16_t* addr = getAddr16(x, y);
626 return SkPixel16ToColor(addr[0]);
627 }
628 case SkBitmap::kARGB_4444_Config: {
629 uint16_t* addr = getAddr16(x, y);
630 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
631 return SkUnPreMultiply::PMColorToColor(c);
632 }
633 case SkBitmap::kARGB_8888_Config: {
634 uint32_t* addr = getAddr32(x, y);
635 return SkUnPreMultiply::PMColorToColor(addr[0]);
636 }
637 case kRLE_Index8_Config: {
638 uint8_t dst;
639 const SkBitmap::RLEPixels* rle =
640 (const SkBitmap::RLEPixels*) getPixels();
641 SkPackBits::Unpack8(&dst, x, 1, rle->packedAtY(y));
642 return SkUnPreMultiply::PMColorToColor((*fColorTable)[dst]);
643 }
644 case kNo_Config:
645 case kConfigCount:
646 SkASSERT(false);
647 return 0;
648 }
649 SkASSERT(false); // Not reached.
650 return 0;
651}
652
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653///////////////////////////////////////////////////////////////////////////////
654///////////////////////////////////////////////////////////////////////////////
655
656void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
657 SkDEBUGCODE(this->validate();)
658
659 if (0 == fWidth || 0 == fHeight ||
660 kNo_Config == fConfig || kIndex8_Config == fConfig) {
661 return;
662 }
663
664 SkAutoLockPixels alp(*this);
665 // perform this check after the lock call
666 if (!this->readyToDraw()) {
667 return;
668 }
669
670 int height = fHeight;
671 const int width = fWidth;
672 const int rowBytes = fRowBytes;
673
674 // make rgb premultiplied
675 if (255 != a) {
676 r = SkAlphaMul(r, a);
677 g = SkAlphaMul(g, a);
678 b = SkAlphaMul(b, a);
679 }
680
681 switch (fConfig) {
682 case kA1_Config: {
683 uint8_t* p = (uint8_t*)fPixels;
684 const int count = (width + 7) >> 3;
685 a = (a >> 7) ? 0xFF : 0;
686 SkASSERT(count <= rowBytes);
687 while (--height >= 0) {
688 memset(p, a, count);
689 p += rowBytes;
690 }
691 break;
692 }
693 case kA8_Config: {
694 uint8_t* p = (uint8_t*)fPixels;
695 while (--height >= 0) {
696 memset(p, a, width);
697 p += rowBytes;
698 }
699 break;
700 }
701 case kARGB_4444_Config:
702 case kRGB_565_Config: {
703 uint16_t* p = (uint16_t*)fPixels;
704 uint16_t v;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000705
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 if (kARGB_4444_Config == fConfig) {
707 v = SkPackARGB4444(a >> 4, r >> 4, g >> 4, b >> 4);
708 } else { // kRGB_565_Config
709 v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS),
710 b >> (8 - SK_B16_BITS));
711 }
712 while (--height >= 0) {
713 sk_memset16(p, v, width);
714 p = (uint16_t*)((char*)p + rowBytes);
715 }
716 break;
717 }
718 case kARGB_8888_Config: {
719 uint32_t* p = (uint32_t*)fPixels;
720 uint32_t v = SkPackARGB32(a, r, g, b);
721
722 while (--height >= 0) {
723 sk_memset32(p, v, width);
724 p = (uint32_t*)((char*)p + rowBytes);
725 }
726 break;
727 }
728 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000729
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 this->notifyPixelsChanged();
731}
732
733//////////////////////////////////////////////////////////////////////////////////////
734//////////////////////////////////////////////////////////////////////////////////////
735
736#define SUB_OFFSET_FAILURE ((size_t)-1)
737
738static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
739 SkASSERT((unsigned)x < (unsigned)bm.width());
740 SkASSERT((unsigned)y < (unsigned)bm.height());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000741
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 switch (bm.getConfig()) {
743 case SkBitmap::kA8_Config:
744 case SkBitmap:: kIndex8_Config:
745 // x is fine as is for the calculation
746 break;
747
748 case SkBitmap::kRGB_565_Config:
749 case SkBitmap::kARGB_4444_Config:
750 x <<= 1;
751 break;
752
753 case SkBitmap::kARGB_8888_Config:
754 x <<= 2;
755 break;
756
757 case SkBitmap::kNo_Config:
758 case SkBitmap::kA1_Config:
759 default:
760 return SUB_OFFSET_FAILURE;
761 }
762 return y * bm.rowBytes() + x;
763}
764
765bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
766 SkDEBUGCODE(this->validate();)
767
768 if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
769 return false; // no src pixels
770 }
771
772 SkIRect srcRect, r;
773 srcRect.set(0, 0, this->width(), this->height());
774 if (!r.intersect(srcRect, subset)) {
775 return false; // r is empty (i.e. no intersection)
776 }
777
778 if (kRLE_Index8_Config == fConfig) {
779 SkAutoLockPixels alp(*this);
780 // don't call readyToDraw(), since we can operate w/o a colortable
781 // at this stage
782 if (this->getPixels() == NULL) {
783 return false;
784 }
785 SkBitmap bm;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 bm.setConfig(kIndex8_Config, r.width(), r.height());
788 bm.allocPixels(this->getColorTable());
789 if (NULL == bm.getPixels()) {
790 return false;
791 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000792
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 const RLEPixels* rle = (const RLEPixels*)this->getPixels();
794 uint8_t* dst = bm.getAddr8(0, 0);
795 const int width = bm.width();
796 const int rowBytes = bm.rowBytes();
weita@google.comf9ab99a2009-05-03 18:23:30 +0000797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 for (int y = r.fTop; y < r.fBottom; y++) {
799 SkPackBits::Unpack8(dst, r.fLeft, width, rle->packedAtY(y));
800 dst += rowBytes;
801 }
802 result->swap(bm);
803 return true;
804 }
805
806 size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
807 if (SUB_OFFSET_FAILURE == offset) {
808 return false; // config not supported
809 }
810
811 SkBitmap dst;
812 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
813
814 if (fPixelRef) {
815 // share the pixelref with a custom offset
816 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
817 } else {
818 // share the pixels (owned by the caller)
819 dst.setPixels((char*)fPixels + offset, this->getColorTable());
820 }
821 SkDEBUGCODE(dst.validate();)
822
823 // we know we're good, so commit to result
824 result->swap(dst);
825 return true;
826}
827
828///////////////////////////////////////////////////////////////////////////////
829
830#include "SkCanvas.h"
831#include "SkPaint.h"
832
reed@android.comfbaa88d2009-05-06 17:44:34 +0000833bool SkBitmap::canCopyTo(Config dstConfig) const {
834 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 return false;
836 }
837
reed@android.comfbaa88d2009-05-06 17:44:34 +0000838 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 switch (dstConfig) {
840 case kA8_Config:
841 case kARGB_4444_Config:
842 case kRGB_565_Config:
843 case kARGB_8888_Config:
844 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000845 case kA1_Config:
846 case kIndex8_Config:
847 if (!sameConfigs) {
848 return false;
849 }
850 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 default:
852 return false;
853 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000854
855 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
856 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 return false;
858 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000859
reed@android.comfbaa88d2009-05-06 17:44:34 +0000860 return true;
861}
862
863bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
864 if (!this->canCopyTo(dstConfig)) {
865 return false;
866 }
867
reed@google.com50dfa012011-04-01 19:05:36 +0000868 // if we have a texture, first get those pixels
869 SkBitmap tmpSrc;
870 const SkBitmap* src = this;
871
872 if (this->getTexture()) {
873 if (!fPixelRef->readPixels(&tmpSrc)) {
874 return false;
875 }
876 SkASSERT(tmpSrc.width() == this->width());
877 SkASSERT(tmpSrc.height() == this->height());
878
879 // did we get lucky and we can just return tmpSrc?
880 if (tmpSrc.config() == dstConfig && NULL == alloc) {
881 dst->swap(tmpSrc);
882 return true;
883 }
884
885 // fall through to the raster case
886 src = &tmpSrc;
reed@android.comfbaa88d2009-05-06 17:44:34 +0000887 }
reed@android.com311c82d2009-05-05 23:13:23 +0000888
reed@google.com50dfa012011-04-01 19:05:36 +0000889 // we lock this now, since we may need its colortable
890 SkAutoLockPixels srclock(*src);
891 if (!src->readyToDraw()) {
892 return false;
893 }
894
895 SkBitmap tmpDst;
896 tmpDst.setConfig(dstConfig, src->width(), src->height());
897
weita@google.comf9ab99a2009-05-03 18:23:30 +0000898 // allocate colortable if srcConfig == kIndex8_Config
899 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +0000900 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000901 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +0000902 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000903 return false;
904 }
reed@google.com50dfa012011-04-01 19:05:36 +0000905
906 SkAutoLockPixels dstlock(tmpDst);
907 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 // allocator/lock failed
909 return false;
910 }
reed@google.com50dfa012011-04-01 19:05:36 +0000911
reed@android.comfbaa88d2009-05-06 17:44:34 +0000912 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000913 */
reed@google.com50dfa012011-04-01 19:05:36 +0000914 if (src->config() == dstConfig) {
915 if (tmpDst.getSize() == src->getSize()) {
916 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
reed@android.com311c82d2009-05-05 23:13:23 +0000917 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000918 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
919 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000920 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000921 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
922 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000923 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000924 srcP += src->rowBytes();
925 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000926 }
927 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000928 } else {
929 // if the src has alpha, we have to clear the dst first
reed@google.com50dfa012011-04-01 19:05:36 +0000930 if (!src->isOpaque()) {
931 tmpDst.eraseColor(0);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000932 }
933
reed@google.com50dfa012011-04-01 19:05:36 +0000934 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000935 SkPaint paint;
936
937 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +0000938 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 }
940
reed@google.com50dfa012011-04-01 19:05:36 +0000941 tmpDst.setIsOpaque(src->isOpaque());
reed@android.comcafc9f92009-08-22 03:44:57 +0000942
reed@google.com50dfa012011-04-01 19:05:36 +0000943 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000944 return true;
945}
946
947///////////////////////////////////////////////////////////////////////////////
948///////////////////////////////////////////////////////////////////////////////
949
950static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
951 const SkBitmap& src) {
952 x <<= 1;
953 y <<= 1;
954 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000955 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 SkPMColor c, ag, rb;
957
958 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
959 if (x < src.width() - 1) {
960 p += 1;
961 }
962 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
963
reed@android.com829c83c2009-06-08 12:05:31 +0000964 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +0000966 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 }
968 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
969 if (x < src.width() - 1) {
970 p += 1;
971 }
972 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
973
974 *dst->getAddr32(x >> 1, y >> 1) =
975 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
976}
977
978static inline uint32_t expand16(U16CPU c) {
979 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
980}
981
982// returns dirt in the top 16bits, but we don't care, since we only
983// store the low 16bits.
984static inline U16CPU pack16(uint32_t c) {
985 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
986}
987
988static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
989 const SkBitmap& src) {
990 x <<= 1;
991 y <<= 1;
992 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +0000993 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000995
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +0000997 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 p += 1;
999 }
1000 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001001
reed@android.com829c83c2009-06-08 12:05:31 +00001002 p = baseP;
1003 if (y < src.height() - 1) {
1004 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 }
1006 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001007 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 p += 1;
1009 }
1010 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001011
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1013}
1014
1015static uint32_t expand4444(U16CPU c) {
1016 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1017}
1018
1019static U16CPU collaps4444(uint32_t c) {
1020 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1021}
1022
1023static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1024 const SkBitmap& src) {
1025 x <<= 1;
1026 y <<= 1;
1027 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001028 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001030
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 c = expand4444(*p);
1032 if (x < src.width() - 1) {
1033 p += 1;
1034 }
1035 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001036
reed@android.com829c83c2009-06-08 12:05:31 +00001037 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001039 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 }
1041 c += expand4444(*p);
1042 if (x < src.width() - 1) {
1043 p += 1;
1044 }
1045 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001046
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1048}
1049
1050void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 if (forceRebuild)
1052 this->freeMipMap();
1053 else if (fMipMap)
1054 return; // we're already built
1055
1056 SkASSERT(NULL == fMipMap);
1057
1058 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1059
1060 const SkBitmap::Config config = this->getConfig();
1061
1062 switch (config) {
1063 case kARGB_8888_Config:
1064 proc = downsampleby2_proc32;
1065 break;
1066 case kRGB_565_Config:
1067 proc = downsampleby2_proc16;
1068 break;
1069 case kARGB_4444_Config:
1070 proc = downsampleby2_proc4444;
1071 break;
1072 case kIndex8_Config:
1073 case kA8_Config:
1074 default:
1075 return; // don't build mipmaps for these configs
1076 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001077
reed@android.com149e2f62009-05-22 14:39:03 +00001078 SkAutoLockPixels alp(*this);
1079 if (!this->readyToDraw()) {
1080 return;
1081 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082
1083 // whip through our loop to compute the exact size needed
1084 size_t size = 0;
1085 int maxLevels = 0;
1086 {
reed@android.com149e2f62009-05-22 14:39:03 +00001087 int width = this->width();
1088 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 for (;;) {
1090 width >>= 1;
1091 height >>= 1;
1092 if (0 == width || 0 == height) {
1093 break;
1094 }
1095 size += ComputeRowBytes(config, width) * height;
1096 maxLevels += 1;
1097 }
1098 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001099
reed@android.com149e2f62009-05-22 14:39:03 +00001100 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 if (0 == maxLevels) {
1102 return;
1103 }
1104
reed@android.com149e2f62009-05-22 14:39:03 +00001105 SkBitmap srcBM(*this);
1106 srcBM.lockPixels();
1107 if (!srcBM.readyToDraw()) {
1108 return;
1109 }
1110
1111 MipMap* mm = MipMap::Alloc(maxLevels, size);
1112 if (NULL == mm) {
1113 return;
1114 }
1115
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116 MipLevel* level = mm->levels();
1117 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001118 int width = this->width();
1119 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 unsigned rowBytes = this->rowBytes();
reed@android.com149e2f62009-05-22 14:39:03 +00001121 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122
1123 for (int i = 0; i < maxLevels; i++) {
1124 width >>= 1;
1125 height >>= 1;
1126 rowBytes = ComputeRowBytes(config, width);
1127
1128 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001129 level[i].fWidth = width;
1130 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001131 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132
1133 dstBM.setConfig(config, width, height, rowBytes);
1134 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001135
reed@android.com149e2f62009-05-22 14:39:03 +00001136 for (int y = 0; y < height; y++) {
1137 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 proc(&dstBM, x, y, srcBM);
1139 }
1140 }
1141
1142 srcBM = dstBM;
1143 addr += height * rowBytes;
1144 }
1145 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1146 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147}
1148
1149bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151}
1152
1153int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001154 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001156 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001157
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 int level = ComputeMipLevel(sx, sy) >> 16;
1159 SkASSERT(level >= 0);
1160 if (level <= 0) {
1161 return 0;
1162 }
1163
1164 if (level >= fMipMap->fLevelCount) {
1165 level = fMipMap->fLevelCount - 1;
1166 }
1167 if (dst) {
1168 const MipLevel& mip = fMipMap->levels()[level - 1];
1169 dst->setConfig((SkBitmap::Config)this->config(),
1170 mip.fWidth, mip.fHeight, mip.fRowBytes);
1171 dst->setPixels(mip.fPixels);
1172 }
1173 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174}
1175
1176SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 sx = SkAbs32(sx);
1178 sy = SkAbs32(sy);
1179 if (sx < sy) {
1180 sx = sy;
1181 }
1182 if (sx < SK_Fixed1) {
1183 return 0;
1184 }
1185 int clz = SkCLZ(sx);
1186 SkASSERT(clz >= 1 && clz <= 15);
1187 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188}
1189
1190///////////////////////////////////////////////////////////////////////////////
1191
reed@android.com1cdcb512009-08-24 19:11:00 +00001192static bool GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[],
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 int alphaRowBytes) {
1194 SkASSERT(alpha != NULL);
1195 SkASSERT(alphaRowBytes >= src.width());
1196
1197 SkBitmap::Config config = src.getConfig();
1198 int w = src.width();
1199 int h = src.height();
1200 int rb = src.rowBytes();
1201
reed@android.com1cdcb512009-08-24 19:11:00 +00001202 SkAutoLockPixels alp(src);
1203 if (!src.readyToDraw()) {
1204 // zero out the alpha buffer and return
1205 while (--h >= 0) {
1206 memset(alpha, 0, w);
1207 alpha += alphaRowBytes;
1208 }
1209 return false;
1210 }
reed@google.com82065d62011-02-07 15:30:46 +00001211
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1213 const uint8_t* s = src.getAddr8(0, 0);
1214 while (--h >= 0) {
1215 memcpy(alpha, s, w);
1216 s += rb;
1217 alpha += alphaRowBytes;
1218 }
1219 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1220 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1221 while (--h >= 0) {
1222 for (int x = 0; x < w; x++) {
1223 alpha[x] = SkGetPackedA32(s[x]);
1224 }
1225 s = (const SkPMColor*)((const char*)s + rb);
1226 alpha += alphaRowBytes;
1227 }
1228 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1229 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1230 while (--h >= 0) {
1231 for (int x = 0; x < w; x++) {
1232 alpha[x] = SkPacked4444ToA32(s[x]);
1233 }
1234 s = (const SkPMColor16*)((const char*)s + rb);
1235 alpha += alphaRowBytes;
1236 }
1237 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1238 SkColorTable* ct = src.getColorTable();
1239 if (ct) {
1240 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1241 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1242 while (--h >= 0) {
1243 for (int x = 0; x < w; x++) {
1244 alpha[x] = SkGetPackedA32(table[s[x]]);
1245 }
1246 s += rb;
1247 alpha += alphaRowBytes;
1248 }
1249 ct->unlockColors(false);
1250 }
1251 } else { // src is opaque, so just fill alpha[] with 0xFF
1252 memset(alpha, 0xFF, h * alphaRowBytes);
1253 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001254 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255}
1256
1257#include "SkPaint.h"
1258#include "SkMaskFilter.h"
1259#include "SkMatrix.h"
1260
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001261bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001262 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 SkDEBUGCODE(this->validate();)
1264
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001265 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 SkMatrix identity;
1267 SkMask srcM, dstM;
1268
1269 srcM.fBounds.set(0, 0, this->width(), this->height());
1270 srcM.fRowBytes = SkAlign4(this->width());
1271 srcM.fFormat = SkMask::kA8_Format;
1272
1273 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1274
1275 // compute our (larger?) dst bounds if we have a filter
1276 if (NULL != filter) {
1277 identity.reset();
1278 srcM.fImage = NULL;
1279 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1280 goto NO_FILTER_CASE;
1281 }
1282 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1283 } else {
1284 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001285 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001287 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1288 // Allocation of pixels for alpha bitmap failed.
1289 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1290 tmpBitmap.width(), tmpBitmap.height());
1291 return false;
1292 }
1293 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 if (offset) {
1295 offset->set(0, 0);
1296 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001297 tmpBitmap.swap(*dst);
1298 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 }
1300
1301 SkAutoMaskImage srcCleanup(&srcM, true);
1302
1303 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1304 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1305 goto NO_FILTER_CASE;
1306 }
1307
1308 SkAutoMaskImage dstCleanup(&dstM, false);
1309
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001310 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001312 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1313 // Allocation of pixels for alpha bitmap failed.
1314 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1315 tmpBitmap.width(), tmpBitmap.height());
1316 return false;
1317 }
1318 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 if (offset) {
1320 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1321 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001322 SkDEBUGCODE(tmpBitmap.validate();)
1323
1324 tmpBitmap.swap(*dst);
1325 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326}
1327
1328///////////////////////////////////////////////////////////////////////////////
1329
1330enum {
1331 SERIALIZE_PIXELTYPE_NONE,
1332 SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
1333 SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
1334 SERIALIZE_PIXELTYPE_REF_DATA,
1335 SERIALIZE_PIXELTYPE_REF_PTR,
1336};
1337
1338static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
1339 size_t len = strlen(str);
1340 buffer.write32(len);
1341 buffer.writePad(str, len);
1342}
1343
1344static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
1345 size_t len = buffer.readInt();
1346 SkAutoSMalloc<256> storage(len + 1);
1347 char* str = (char*)storage.get();
1348 buffer.read(str, len);
1349 str[len] = 0;
1350 return SkPixelRef::NameToFactory(str);
1351}
1352
1353/*
1354 It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
1355 we just have pixels, then we can only flatten the pixels, or write out an
1356 empty bitmap.
weita@google.comf9ab99a2009-05-03 18:23:30 +00001357
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 With a pixelref, we still have the question of recognizing when two sitings
1359 of the same pixelref are the same, and when they are different. Perhaps we
1360 should look at the generationID and keep a record of that in some dictionary
1361 associated with the buffer. SkGLTextureCache does this sort of thing to know
1362 when to create a new texture.
1363*/
1364void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
1365 buffer.write32(fWidth);
1366 buffer.write32(fHeight);
1367 buffer.write32(fRowBytes);
1368 buffer.write8(fConfig);
1369 buffer.writeBool(this->isOpaque());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001370
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 /* If we are called in this mode, then it is up to the caller to manage
1372 the owner-counts on the pixelref, as we just record the ptr itself.
1373 */
1374 if (!buffer.persistBitmapPixels()) {
1375 if (fPixelRef) {
1376 buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
1377 buffer.write32(fPixelRefOffset);
1378 buffer.writeRefCnt(fPixelRef);
1379 return;
1380 } else {
1381 // we ignore the non-persist request, since we don't have a ref
1382 // ... or we could just write an empty bitmap...
1383 // (true) will write an empty bitmap, (false) will flatten the pix
1384 if (true) {
1385 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1386 return;
1387 }
1388 }
1389 }
1390
1391 if (fPixelRef) {
1392 SkPixelRef::Factory fact = fPixelRef->getFactory();
1393 if (fact) {
1394 const char* name = SkPixelRef::FactoryToName(fact);
1395 if (name && *name) {
1396 buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
1397 buffer.write32(fPixelRefOffset);
1398 writeString(buffer, name);
1399 fPixelRef->flatten(buffer);
1400 return;
1401 }
1402 }
1403 // if we get here, we can't record the pixels
1404 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1405 } else if (fPixels) {
1406 if (fColorTable) {
1407 buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
1408 fColorTable->flatten(buffer);
1409 } else {
1410 buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
1411 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001412 buffer.writePad(fPixels, this->getSafeSize());
1413 // There is no writeZeroPad() fcn, so write individual bytes.
1414 if (this->getSize() > this->getSafeSize()) {
1415 size_t deltaSize = this->getSize() - this->getSafeSize();
1416 // Need aligned pointer to write into due to internal implementa-
1417 // tion of SkWriter32.
1418 memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
1419 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 } else {
1421 buffer.write8(SERIALIZE_PIXELTYPE_NONE);
1422 }
1423}
1424
1425void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1426 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 int width = buffer.readInt();
1429 int height = buffer.readInt();
1430 int rowBytes = buffer.readInt();
1431 int config = buffer.readU8();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001432
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 this->setConfig((Config)config, width, height, rowBytes);
1434 this->setIsOpaque(buffer.readBool());
weita@google.comf9ab99a2009-05-03 18:23:30 +00001435
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 int reftype = buffer.readU8();
1437 switch (reftype) {
1438 case SERIALIZE_PIXELTYPE_REF_PTR: {
1439 size_t offset = buffer.readU32();
1440 SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
1441 this->setPixelRef(pr, offset);
1442 break;
1443 }
1444 case SERIALIZE_PIXELTYPE_REF_DATA: {
1445 size_t offset = buffer.readU32();
1446 SkPixelRef::Factory fact = deserialize_factory(buffer);
1447 SkPixelRef* pr = fact(buffer);
reed@google.com82065d62011-02-07 15:30:46 +00001448 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 break;
1450 }
1451 case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
1452 case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
1453 SkColorTable* ctable = NULL;
1454 if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
1455 ctable = SkNEW_ARGS(SkColorTable, (buffer));
1456 }
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001457 size_t size = this->getSize();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 if (this->allocPixels(ctable)) {
1459 this->lockPixels();
wjmaclean@chromium.org3f0260d2011-02-22 22:32:14 +00001460 // Just read what we need.
1461 buffer.read(this->getPixels(), this->getSafeSize());
1462 // Keep aligned for subsequent reads.
1463 buffer.skip(size - this->getSafeSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 this->unlockPixels();
1465 } else {
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +00001466 buffer.skip(size); // Still skip the full-sized buffer though.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 }
reed@android.com149e2f62009-05-22 14:39:03 +00001468 SkSafeUnref(ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 break;
1470 }
1471 case SERIALIZE_PIXELTYPE_NONE:
1472 break;
1473 default:
1474 SkASSERT(!"unrecognized pixeltype in serialized data");
1475 sk_throw();
1476 }
1477}
1478
1479///////////////////////////////////////////////////////////////////////////////
1480
1481SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1482 fHeight = height;
1483 fYPtrs = (uint8_t**)sk_malloc_throw(height * sizeof(uint8_t*));
reed@android.com4516f472009-06-29 16:25:36 +00001484 sk_bzero(fYPtrs, height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485}
1486
1487SkBitmap::RLEPixels::~RLEPixels() {
1488 sk_free(fYPtrs);
1489}
1490
1491///////////////////////////////////////////////////////////////////////////////
1492
1493#ifdef SK_DEBUG
1494void SkBitmap::validate() const {
1495 SkASSERT(fConfig < kConfigCount);
1496 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
1497 SkASSERT(fFlags <= kImageIsOpaque_Flag);
1498 SkASSERT(fPixelLockCount >= 0);
1499 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1500 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1501
1502#if 0 // these asserts are not thread-correct, so disable for now
1503 if (fPixelRef) {
1504 if (fPixelLockCount > 0) {
1505 SkASSERT(fPixelRef->getLockCount() > 0);
1506 } else {
1507 SkASSERT(NULL == fPixels);
1508 SkASSERT(NULL == fColorTable);
1509 }
1510 }
1511#endif
1512}
1513#endif
1514