blob: 38594f8f3c638f6691cb70db44e56f56f20a65e6 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
13#include "SkFlattenable.h"
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +000014#include "SkImagePriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkMallocPixelRef.h"
16#include "SkMask.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000017#include "SkOrderedReadBuffer.h"
18#include "SkOrderedWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPixelRef.h"
20#include "SkThread.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000021#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkUtils.h"
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000023#include "SkValidationUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPackBits.h"
25#include <new>
26
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000027SK_DEFINE_INST_COUNT(SkBitmap::Allocator)
28
reed@android.com149e2f62009-05-22 14:39:03 +000029static bool isPos32Bits(const Sk64& value) {
30 return !value.isNeg() && value.is32();
31}
32
reed@android.com8a1c16f2008-12-17 15:59:43 +000033struct MipLevel {
34 void* fPixels;
35 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000036 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000037};
38
39struct SkBitmap::MipMap : SkNoncopyable {
40 int32_t fRefCnt;
41 int fLevelCount;
42// MipLevel fLevel[fLevelCount];
43// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000044
reed@android.com8a1c16f2008-12-17 15:59:43 +000045 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000046 if (levelCount < 0) {
47 return NULL;
48 }
49 Sk64 size;
50 size.setMul(levelCount + 1, sizeof(MipLevel));
51 size.add(sizeof(MipMap));
scroggo@google.come5f48242013-02-25 21:47:41 +000052 size.add(SkToS32(pixelSize));
reed@android.com149e2f62009-05-22 14:39:03 +000053 if (!isPos32Bits(size)) {
54 return NULL;
55 }
56 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000057 mm->fRefCnt = 1;
58 mm->fLevelCount = levelCount;
59 return mm;
60 }
61
62 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
63 MipLevel* levels() { return (MipLevel*)(this + 1); }
64
65 const void* pixels() const { return levels() + fLevelCount; }
66 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000067
reed@android.com149e2f62009-05-22 14:39:03 +000068 void ref() {
69 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
70 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 }
72 }
reed@android.com149e2f62009-05-22 14:39:03 +000073 void unref() {
74 SkASSERT(fRefCnt > 0);
75 if (sk_atomic_dec(&fRefCnt) == 1) {
76 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 }
78 }
79};
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81///////////////////////////////////////////////////////////////////////////////
82///////////////////////////////////////////////////////////////////////////////
83
84SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000085 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000086}
87
88SkBitmap::SkBitmap(const SkBitmap& src) {
89 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000090 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 *this = src;
92 SkDEBUGCODE(this->validate();)
93}
94
95SkBitmap::~SkBitmap() {
96 SkDEBUGCODE(this->validate();)
97 this->freePixels();
98}
99
100SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
101 if (this != &src) {
102 this->freePixels();
103 memcpy(this, &src, sizeof(src));
104
105 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000106 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000107 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
109 // we reset our locks if we get blown away
110 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000111
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 /* The src could be in 3 states
113 1. no pixelref, in which case we just copy/ref the pixels/ctable
114 2. unlocked pixelref, pixels/ctable should be null
115 3. locked pixelref, we should lock the ref again ourselves
116 */
117 if (NULL == fPixelRef) {
118 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000119 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 } else { // we have a pixelref, so pixels/ctable reflect it
121 // ignore the values from the memcpy
122 fPixels = NULL;
123 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000124 // Note that what to for genID is somewhat arbitrary. We have no
125 // way to track changes to raw pixels across multiple SkBitmaps.
126 // Would benefit from an SkRawPixelRef type created by
127 // setPixels.
128 // Just leave the memcpy'ed one but they'll get out of sync
129 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130 }
131 }
132
133 SkDEBUGCODE(this->validate();)
134 return *this;
135}
136
137void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000138 SkTSwap(fColorTable, other.fColorTable);
139 SkTSwap(fPixelRef, other.fPixelRef);
140 SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
141 SkTSwap(fPixelLockCount, other.fPixelLockCount);
142 SkTSwap(fMipMap, other.fMipMap);
143 SkTSwap(fPixels, other.fPixels);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000144 SkTSwap(fRowBytes, other.fRowBytes);
145 SkTSwap(fWidth, other.fWidth);
146 SkTSwap(fHeight, other.fHeight);
147 SkTSwap(fConfig, other.fConfig);
reed@google.com383a6972013-10-21 14:00:07 +0000148 SkTSwap(fAlphaType, other.fAlphaType);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000149 SkTSwap(fFlags, other.fFlags);
150 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151
152 SkDEBUGCODE(this->validate();)
153}
154
155void SkBitmap::reset() {
156 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000157 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158}
159
160int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
161 int bpp;
162 switch (config) {
163 case kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 bpp = 0; // not applicable
165 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 case kA8_Config:
167 case kIndex8_Config:
168 bpp = 1;
169 break;
170 case kRGB_565_Config:
171 case kARGB_4444_Config:
172 bpp = 2;
173 break;
174 case kARGB_8888_Config:
175 bpp = 4;
176 break;
177 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000178 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 bpp = 0; // error
180 break;
181 }
182 return bpp;
183}
184
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000185size_t SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000186 if (width < 0) {
187 return 0;
188 }
189
190 Sk64 rowBytes;
191 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192
193 switch (c) {
194 case kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 case kA8_Config:
197 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000198 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 break;
200 case kRGB_565_Config:
201 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000202 rowBytes.set(width);
203 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 break;
205 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000206 rowBytes.set(width);
207 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 break;
209 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000210 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 break;
212 }
reed@android.com149e2f62009-05-22 14:39:03 +0000213 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214}
215
216Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
217 Sk64 size;
scroggo@google.come5f48242013-02-25 21:47:41 +0000218 size.setMul(SkToS32(SkBitmap::ComputeRowBytes(c, width)), height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 return size;
220}
221
222size_t SkBitmap::ComputeSize(Config c, int width, int height) {
223 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000224 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225}
226
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000227Sk64 SkBitmap::ComputeSafeSize64(Config config,
228 uint32_t width,
229 uint32_t height,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000230 size_t rowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000231 Sk64 safeSize;
232 safeSize.setZero();
233 if (height > 0) {
scroggo@google.come5f48242013-02-25 21:47:41 +0000234 // TODO: Handle the case where the return value from
235 // ComputeRowBytes is more than 31 bits.
236 safeSize.set(SkToS32(ComputeRowBytes(config, width)));
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000237 Sk64 sizeAllButLastRow;
scroggo@google.come5f48242013-02-25 21:47:41 +0000238 sizeAllButLastRow.setMul(height - 1, SkToS32(rowBytes));
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000239 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,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000248 size_t rowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000249 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
250 return (safeSize.is32() ? safeSize.get32() : 0);
251}
252
reed@google.com86b2e432012-03-15 21:17:03 +0000253void SkBitmap::getBounds(SkRect* bounds) const {
254 SkASSERT(bounds);
255 bounds->set(0, 0,
256 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
257}
258
reed@google.com80e14592012-03-16 14:58:07 +0000259void SkBitmap::getBounds(SkIRect* bounds) const {
260 SkASSERT(bounds);
261 bounds->set(0, 0, fWidth, fHeight);
262}
263
reed@google.com86b2e432012-03-15 21:17:03 +0000264///////////////////////////////////////////////////////////////////////////////
265
reed@google.com383a6972013-10-21 14:00:07 +0000266static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType,
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000267 SkAlphaType* canonical = NULL) {
reed@google.com383a6972013-10-21 14:00:07 +0000268 switch (config) {
269 case SkBitmap::kNo_Config:
270 alphaType = kIgnore_SkAlphaType;
271 break;
reed@google.com383a6972013-10-21 14:00:07 +0000272 case SkBitmap::kA8_Config:
273 if (kUnpremul_SkAlphaType == alphaType) {
274 alphaType = kPremul_SkAlphaType;
275 }
276 // fall-through
277 case SkBitmap::kIndex8_Config:
278 case SkBitmap::kARGB_4444_Config:
279 case SkBitmap::kARGB_8888_Config:
280 if (kIgnore_SkAlphaType == alphaType) {
281 return false;
282 }
283 break;
284 case SkBitmap::kRGB_565_Config:
285 alphaType = kOpaque_SkAlphaType;
286 break;
reed@google.com2a1f4462013-11-25 18:58:29 +0000287 default:
288 return false;
reed@android.com149e2f62009-05-22 14:39:03 +0000289 }
reed@google.com383a6972013-10-21 14:00:07 +0000290 if (canonical) {
291 *canonical = alphaType;
292 }
293 return true;
294}
reed@android.com149e2f62009-05-22 14:39:03 +0000295
reed@google.com383a6972013-10-21 14:00:07 +0000296bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
297 SkAlphaType alphaType) {
298 if ((width | height) < 0) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000299 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000300 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 if (rowBytes == 0) {
reed@google.com383a6972013-10-21 14:00:07 +0000302 rowBytes = SkBitmap::ComputeRowBytes(config, width);
303 if (0 == rowBytes && kNo_Config != config) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000304 goto BAD_CONFIG;
reed@android.com149e2f62009-05-22 14:39:03 +0000305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000307
reed@google.com383a6972013-10-21 14:00:07 +0000308 if (!validate_alphaType(config, alphaType, &alphaType)) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000309 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000310 }
311
312 this->freePixels();
313
314 fConfig = SkToU8(config);
315 fAlphaType = SkToU8(alphaType);
reed@android.comf459a492009-03-27 12:33:50 +0000316 fWidth = width;
317 fHeight = height;
scroggo@google.come5f48242013-02-25 21:47:41 +0000318 fRowBytes = SkToU32(rowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319
reed@google.com383a6972013-10-21 14:00:07 +0000320 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(config);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321
322 SkDEBUGCODE(this->validate();)
reed@google.com383a6972013-10-21 14:00:07 +0000323 return true;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000324
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000325 // if we got here, we had an error, so we reset the bitmap to empty
reed@google.com9cd697c2013-10-21 14:12:13 +0000326BAD_CONFIG:
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000327 this->reset();
reed@google.com383a6972013-10-21 14:00:07 +0000328 return false;
329}
330
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +0000331bool SkBitmap::setConfig(const SkImageInfo& info, size_t rowBytes) {
332 return this->setConfig(SkImageInfoToBitmapConfig(info), info.fWidth,
333 info.fHeight, rowBytes, info.fAlphaType);
334}
335
reed@google.com383a6972013-10-21 14:00:07 +0000336bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
337 if (!validate_alphaType(this->config(), alphaType, &alphaType)) {
338 return false;
339 }
340 fAlphaType = SkToU8(alphaType);
341 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342}
343
344void SkBitmap::updatePixelsFromRef() const {
345 if (NULL != fPixelRef) {
346 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000347 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000348
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 void* p = fPixelRef->pixels();
350 if (NULL != p) {
351 p = (char*)p + fPixelRefOffset;
352 }
353 fPixels = p;
354 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
355 } else {
356 SkASSERT(0 == fPixelLockCount);
357 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000358 if (fColorTable) {
359 fColorTable->unref();
360 fColorTable = NULL;
361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 }
363 }
364}
365
366SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
367 // do this first, we that we never have a non-zero offset with a null ref
368 if (NULL == pr) {
369 offset = 0;
370 }
371
372 if (fPixelRef != pr || fPixelRefOffset != offset) {
373 if (fPixelRef != pr) {
374 this->freePixels();
375 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000376
reed@google.com82065d62011-02-07 15:30:46 +0000377 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 fPixelRef = pr;
379 }
380 fPixelRefOffset = offset;
381 this->updatePixelsFromRef();
382 }
383
384 SkDEBUGCODE(this->validate();)
385 return pr;
386}
387
388void SkBitmap::lockPixels() const {
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000389 if (NULL != fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 fPixelRef->lockPixels();
391 this->updatePixelsFromRef();
392 }
393 SkDEBUGCODE(this->validate();)
394}
395
396void SkBitmap::unlockPixels() const {
397 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
398
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000399 if (NULL != fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 fPixelRef->unlockPixels();
401 this->updatePixelsFromRef();
402 }
403 SkDEBUGCODE(this->validate();)
404}
405
reed@google.com9c49bc32011-07-07 13:42:37 +0000406bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000407 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000408}
409
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000411 if (NULL == p) {
412 this->setPixelRef(NULL, 0);
413 return;
414 }
415
djsollen@google.comc84b8332012-07-27 13:41:44 +0000416 Sk64 size = this->getSize64();
417 SkASSERT(!size.isNeg() && size.is32());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418
djsollen@google.comc84b8332012-07-27 13:41:44 +0000419 this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref();
420 // since we're already allocated, we lockPixels right away
421 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422 SkDEBUGCODE(this->validate();)
423}
424
425bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
426 HeapAllocator stdalloc;
427
428 if (NULL == allocator) {
429 allocator = &stdalloc;
430 }
431 return allocator->allocPixelRef(this, ctable);
432}
433
434void SkBitmap::freePixels() {
435 // if we're gonna free the pixels, we certainly need to free the mipmap
436 this->freeMipMap();
437
reed@android.com149e2f62009-05-22 14:39:03 +0000438 if (fColorTable) {
439 fColorTable->unref();
440 fColorTable = NULL;
441 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442
443 if (NULL != fPixelRef) {
444 if (fPixelLockCount > 0) {
445 fPixelRef->unlockPixels();
446 }
447 fPixelRef->unref();
448 fPixelRef = NULL;
449 fPixelRefOffset = 0;
450 }
451 fPixelLockCount = 0;
452 fPixels = NULL;
453}
454
455void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000456 if (fMipMap) {
457 fMipMap->unref();
458 fMipMap = NULL;
459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460}
461
462uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000463 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464}
465
466void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000467 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468 if (fPixelRef) {
469 fPixelRef->notifyPixelsChanged();
470 }
471}
472
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000473GrTexture* SkBitmap::getTexture() const {
reed@android.comce4e53a2010-09-09 16:01:26 +0000474 return fPixelRef ? fPixelRef->getTexture() : NULL;
475}
476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477///////////////////////////////////////////////////////////////////////////////
478
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479/** We explicitly use the same allocator for our pixels that SkMask does,
480 so that we can freely assign memory allocated by one class to the other.
481 */
482bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
483 SkColorTable* ctable) {
484 Sk64 size = dst->getSize64();
485 if (size.isNeg() || !size.is32()) {
486 return false;
487 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000488
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
490 if (NULL == addr) {
491 return false;
492 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000493
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
495 // since we're already allocated, we lockPixels right away
496 dst->lockPixels();
497 return true;
498}
499
500///////////////////////////////////////////////////////////////////////////////
501
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000502size_t SkBitmap::getSafeSize() const {
503 // This is intended to be a size_t version of ComputeSafeSize64(), just
504 // faster. The computation is meant to be identical.
505 return (fHeight ? ((fHeight - 1) * fRowBytes) +
reed@google.com44699382013-10-31 17:28:30 +0000506 ComputeRowBytes(this->config(), fWidth): 0);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000507}
508
509Sk64 SkBitmap::getSafeSize64() const {
reed@google.com44699382013-10-31 17:28:30 +0000510 return ComputeSafeSize64(this->config(), fWidth, fHeight, fRowBytes);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000511}
512
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000513bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000514 size_t dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000515
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000516 if (0 == dstRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000517 dstRowBytes = fRowBytes;
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000518 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000519
reed@google.com44699382013-10-31 17:28:30 +0000520 if (dstRowBytes < ComputeRowBytes(this->config(), fWidth) ||
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000521 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
522 return false;
523
bsalomon@google.comc6980972011-11-02 19:57:21 +0000524 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
reed@google.com44699382013-10-31 17:28:30 +0000525 size_t safeSize = this->getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000526 if (safeSize > dstSize || safeSize == 0)
527 return false;
528 else {
529 SkAutoLockPixels lock(*this);
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(dst, getPixels(), safeSize);
534
535 return true;
536 }
537 } else {
538 // If destination has different stride than us, then copy line by line.
reed@google.com44699382013-10-31 17:28:30 +0000539 if (ComputeSafeSize(this->config(), fWidth, fHeight, dstRowBytes) >
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000540 dstSize)
541 return false;
542 else {
543 // Just copy what we need on each line.
reed@google.com44699382013-10-31 17:28:30 +0000544 size_t rowBytes = ComputeRowBytes(this->config(), fWidth);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000545 SkAutoLockPixels lock(*this);
546 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
547 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
548 for (uint32_t row = 0; row < fHeight;
549 row++, srcP += fRowBytes, dstP += dstRowBytes) {
550 memcpy(dstP, srcP, rowBytes);
551 }
552
553 return true;
554 }
555 }
556}
557
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000558///////////////////////////////////////////////////////////////////////////////
559
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000560bool SkBitmap::isImmutable() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000561 return fPixelRef ? fPixelRef->isImmutable() :
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000562 fFlags & kImageIsImmutable_Flag;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000563}
564
565void SkBitmap::setImmutable() {
566 if (fPixelRef) {
567 fPixelRef->setImmutable();
568 } else {
569 fFlags |= kImageIsImmutable_Flag;
570 }
571}
572
junov@google.com4ee7ae52011-06-30 17:30:49 +0000573bool SkBitmap::isVolatile() const {
574 return (fFlags & kImageIsVolatile_Flag) != 0;
575}
576
577void SkBitmap::setIsVolatile(bool isVolatile) {
578 if (isVolatile) {
579 fFlags |= kImageIsVolatile_Flag;
580 } else {
581 fFlags &= ~kImageIsVolatile_Flag;
582 }
583}
584
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585void* SkBitmap::getAddr(int x, int y) const {
586 SkASSERT((unsigned)x < (unsigned)this->width());
587 SkASSERT((unsigned)y < (unsigned)this->height());
588
589 char* base = (char*)this->getPixels();
590 if (base) {
591 base += y * this->rowBytes();
592 switch (this->config()) {
593 case SkBitmap::kARGB_8888_Config:
594 base += x << 2;
595 break;
596 case SkBitmap::kARGB_4444_Config:
597 case SkBitmap::kRGB_565_Config:
598 base += x << 1;
599 break;
600 case SkBitmap::kA8_Config:
601 case SkBitmap::kIndex8_Config:
602 base += x;
603 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000606 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 base = NULL;
608 break;
609 }
610 }
611 return base;
612}
613
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000614SkColor SkBitmap::getColor(int x, int y) const {
615 SkASSERT((unsigned)x < (unsigned)this->width());
616 SkASSERT((unsigned)y < (unsigned)this->height());
617
618 switch (this->config()) {
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000619 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000620 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000621 return SkColorSetA(0, addr[0]);
622 }
623 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000624 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000625 return SkUnPreMultiply::PMColorToColor(c);
626 }
627 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000628 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000629 return SkPixel16ToColor(addr[0]);
630 }
631 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000632 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000633 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
634 return SkUnPreMultiply::PMColorToColor(c);
635 }
636 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000637 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000638 return SkUnPreMultiply::PMColorToColor(addr[0]);
639 }
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000640 case kNo_Config:
reed@google.com2a1f4462013-11-25 18:58:29 +0000641 default:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000642 SkASSERT(false);
643 return 0;
644 }
645 SkASSERT(false); // Not reached.
646 return 0;
647}
648
reed@google.com2a7579d2012-11-07 18:30:18 +0000649bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
650 SkAutoLockPixels alp(bm);
651 if (!bm.getPixels()) {
652 return false;
653 }
654
655 const int height = bm.height();
656 const int width = bm.width();
657
658 switch (bm.config()) {
reed@google.com2a7579d2012-11-07 18:30:18 +0000659 case SkBitmap::kA8_Config: {
660 unsigned a = 0xFF;
661 for (int y = 0; y < height; ++y) {
662 const uint8_t* row = bm.getAddr8(0, y);
663 for (int x = 0; x < width; ++x) {
664 a &= row[x];
665 }
666 if (0xFF != a) {
667 return false;
668 }
669 }
670 return true;
671 } break;
reed@google.com2a7579d2012-11-07 18:30:18 +0000672 case SkBitmap::kIndex8_Config: {
673 SkAutoLockColors alc(bm);
674 const SkPMColor* table = alc.colors();
675 if (!table) {
676 return false;
677 }
reed@google.com140d7282013-01-07 20:25:04 +0000678 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000679 for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
680 c &= table[i];
681 }
682 return 0xFF == SkGetPackedA32(c);
683 } break;
684 case SkBitmap::kRGB_565_Config:
685 return true;
686 break;
687 case SkBitmap::kARGB_4444_Config: {
688 unsigned c = 0xFFFF;
689 for (int y = 0; y < height; ++y) {
690 const SkPMColor16* row = bm.getAddr16(0, y);
691 for (int x = 0; x < width; ++x) {
692 c &= row[x];
693 }
694 if (0xF != SkGetPackedA4444(c)) {
695 return false;
696 }
697 }
698 return true;
699 } break;
700 case SkBitmap::kARGB_8888_Config: {
reed@google.com140d7282013-01-07 20:25:04 +0000701 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000702 for (int y = 0; y < height; ++y) {
703 const SkPMColor* row = bm.getAddr32(0, y);
704 for (int x = 0; x < width; ++x) {
705 c &= row[x];
706 }
707 if (0xFF != SkGetPackedA32(c)) {
708 return false;
709 }
710 }
711 return true;
712 }
713 default:
714 break;
715 }
716 return false;
717}
718
719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720///////////////////////////////////////////////////////////////////////////////
721///////////////////////////////////////////////////////////////////////////////
722
reed@google.com45f746f2013-06-21 19:51:31 +0000723static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
724 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
725 (SkR32To4444(r) << SK_R4444_SHIFT) |
726 (SkG32To4444(g) << SK_G4444_SHIFT) |
727 (SkB32To4444(b) << SK_B4444_SHIFT);
728 return SkToU16(pixel);
729}
730
reed@google.com60d32352013-06-28 19:40:50 +0000731void SkBitmap::internalErase(const SkIRect& area,
732 U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
733#ifdef SK_DEBUG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 SkDEBUGCODE(this->validate();)
reed@google.com60d32352013-06-28 19:40:50 +0000735 SkASSERT(!area.isEmpty());
736 {
reed@google.com92833f92013-06-28 19:47:43 +0000737 SkIRect total = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000738 SkASSERT(total.contains(area));
739 }
740#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741
reed@google.com60d32352013-06-28 19:40:50 +0000742 if (kNo_Config == fConfig || kIndex8_Config == fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 return;
744 }
745
746 SkAutoLockPixels alp(*this);
747 // perform this check after the lock call
748 if (!this->readyToDraw()) {
749 return;
750 }
751
reed@google.com60d32352013-06-28 19:40:50 +0000752 int height = area.height();
753 const int width = area.width();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 const int rowBytes = fRowBytes;
755
756 // make rgb premultiplied
757 if (255 != a) {
758 r = SkAlphaMul(r, a);
759 g = SkAlphaMul(g, a);
760 b = SkAlphaMul(b, a);
761 }
762
763 switch (fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 case kA8_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000765 uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 while (--height >= 0) {
767 memset(p, a, width);
768 p += rowBytes;
769 }
770 break;
771 }
reed@google.com45f746f2013-06-21 19:51:31 +0000772 case kARGB_4444_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 case kRGB_565_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000774 uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
reed@google.com45f746f2013-06-21 19:51:31 +0000775 uint16_t v;
skia.committer@gmail.com020b25b2013-06-22 07:00:58 +0000776
reed@google.com45f746f2013-06-21 19:51:31 +0000777 if (kARGB_4444_Config == fConfig) {
778 v = pack_8888_to_4444(a, r, g, b);
779 } else {
780 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
781 g >> (8 - SK_G16_BITS),
782 b >> (8 - SK_B16_BITS));
783 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 while (--height >= 0) {
785 sk_memset16(p, v, width);
786 p = (uint16_t*)((char*)p + rowBytes);
787 }
788 break;
789 }
790 case kARGB_8888_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000791 uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 uint32_t v = SkPackARGB32(a, r, g, b);
793
794 while (--height >= 0) {
795 sk_memset32(p, v, width);
796 p = (uint32_t*)((char*)p + rowBytes);
797 }
798 break;
799 }
800 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000801
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 this->notifyPixelsChanged();
803}
804
reed@google.com60d32352013-06-28 19:40:50 +0000805void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
reed@google.com92833f92013-06-28 19:47:43 +0000806 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000807 if (!area.isEmpty()) {
808 this->internalErase(area, a, r, g, b);
809 }
810}
811
812void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
reed@google.com92833f92013-06-28 19:47:43 +0000813 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000814 if (area.intersect(rect)) {
815 this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
816 SkColorGetG(c), SkColorGetB(c));
817 }
818}
819
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820//////////////////////////////////////////////////////////////////////////////////////
821//////////////////////////////////////////////////////////////////////////////////////
822
823#define SUB_OFFSET_FAILURE ((size_t)-1)
824
scroggo@google.coma2a31922012-12-07 19:14:45 +0000825/**
826 * Based on the Config and rowBytes() of bm, return the offset into an SkPixelRef of the pixel at
827 * (x, y).
828 * Note that the SkPixelRef does not need to be set yet. deepCopyTo takes advantage of this fact.
829 * Also note that (x, y) may be outside the range of (0 - width(), 0 - height()), so long as it is
830 * within the bounds of the SkPixelRef being used.
831 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000832static size_t get_sub_offset(const SkBitmap& bm, int x, int y) {
reed@google.com44699382013-10-31 17:28:30 +0000833 switch (bm.config()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 case SkBitmap::kA8_Config:
835 case SkBitmap:: kIndex8_Config:
836 // x is fine as is for the calculation
837 break;
838
839 case SkBitmap::kRGB_565_Config:
840 case SkBitmap::kARGB_4444_Config:
841 x <<= 1;
842 break;
843
844 case SkBitmap::kARGB_8888_Config:
845 x <<= 2;
846 break;
847
848 case SkBitmap::kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 default:
850 return SUB_OFFSET_FAILURE;
851 }
852 return y * bm.rowBytes() + x;
853}
854
scroggo@google.coma2a31922012-12-07 19:14:45 +0000855/**
856 * Using the pixelRefOffset(), rowBytes(), and Config of bm, determine the (x, y) coordinate of the
857 * upper left corner of bm relative to its SkPixelRef.
858 * x and y must be non-NULL.
859 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000860bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com61d6c9e2013-05-21 20:38:40 +0000861 int32_t* x, int32_t* y);
862bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000863 int32_t* x, int32_t* y) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000864 SkASSERT(x != NULL && y != NULL);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000865 if (0 == offset) {
866 *x = *y = 0;
867 return true;
868 }
869 // Use integer division to find the correct y position.
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000870 // The remainder will be the x position, after we reverse get_sub_offset.
commit-bot@chromium.org2c86fbb2013-09-26 19:22:54 +0000871 SkTDivMod(offset, rowBytes, y, x);
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000872 switch (config) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000873 case SkBitmap::kA8_Config:
874 // Fall through.
875 case SkBitmap::kIndex8_Config:
876 // x is unmodified
877 break;
878
879 case SkBitmap::kRGB_565_Config:
880 // Fall through.
881 case SkBitmap::kARGB_4444_Config:
882 *x >>= 1;
883 break;
884
885 case SkBitmap::kARGB_8888_Config:
886 *x >>= 2;
887 break;
888
889 case SkBitmap::kNo_Config:
890 // Fall through.
scroggo@google.coma2a31922012-12-07 19:14:45 +0000891 default:
892 return false;
893 }
894 return true;
895}
896
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000897static bool get_upper_left_from_offset(const SkBitmap& bm, int32_t* x, int32_t* y) {
898 return get_upper_left_from_offset(bm.config(), bm.pixelRefOffset(), bm.rowBytes(), x, y);
899}
900
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
902 SkDEBUGCODE(this->validate();)
903
djsollen@google.comc84b8332012-07-27 13:41:44 +0000904 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 return false; // no src pixels
906 }
907
908 SkIRect srcRect, r;
909 srcRect.set(0, 0, this->width(), this->height());
910 if (!r.intersect(srcRect, subset)) {
911 return false; // r is empty (i.e. no intersection)
912 }
913
scroggo@google.coma2a31922012-12-07 19:14:45 +0000914 if (fPixelRef->getTexture() != NULL) {
915 // Do a deep copy
916 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->config(), &subset);
917 if (pixelRef != NULL) {
918 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000919 dst.setConfig(this->config(), subset.width(), subset.height(), 0,
920 this->alphaType());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000921 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000922 dst.setPixelRef(pixelRef)->unref();
923 SkDEBUGCODE(dst.validate());
924 result->swap(dst);
925 return true;
926 }
927 }
928
scroggo@google.coma2a31922012-12-07 19:14:45 +0000929 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
930 // exited above.
931 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
932 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
933
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000934 size_t offset = get_sub_offset(*this, r.fLeft, r.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 if (SUB_OFFSET_FAILURE == offset) {
936 return false; // config not supported
937 }
938
939 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000940 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes(),
941 this->alphaType());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000942 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943
944 if (fPixelRef) {
945 // share the pixelref with a custom offset
946 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 }
948 SkDEBUGCODE(dst.validate();)
949
950 // we know we're good, so commit to result
951 result->swap(dst);
952 return true;
953}
954
955///////////////////////////////////////////////////////////////////////////////
956
957#include "SkCanvas.h"
958#include "SkPaint.h"
959
reed@android.comfbaa88d2009-05-06 17:44:34 +0000960bool SkBitmap::canCopyTo(Config dstConfig) const {
reed@google.com44699382013-10-31 17:28:30 +0000961 if (this->config() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 return false;
963 }
964
reed@android.comfbaa88d2009-05-06 17:44:34 +0000965 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 switch (dstConfig) {
967 case kA8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 case kRGB_565_Config:
969 case kARGB_8888_Config:
970 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000971 case kIndex8_Config:
972 if (!sameConfigs) {
973 return false;
974 }
975 break;
scroggo@google.com8dc8bc52013-08-07 19:16:05 +0000976 case kARGB_4444_Config:
977 return sameConfigs || kARGB_8888_Config == this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 default:
979 return false;
980 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000981 return true;
982}
983
984bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
985 if (!this->canCopyTo(dstConfig)) {
986 return false;
987 }
988
reed@google.com50dfa012011-04-01 19:05:36 +0000989 // if we have a texture, first get those pixels
990 SkBitmap tmpSrc;
991 const SkBitmap* src = this;
992
scroggo@google.coma2a31922012-12-07 19:14:45 +0000993 if (fPixelRef) {
994 SkIRect subset;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000995 if (get_upper_left_from_offset(*this, &subset.fLeft, &subset.fTop)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000996 subset.fRight = subset.fLeft + fWidth;
997 subset.fBottom = subset.fTop + fHeight;
998 if (fPixelRef->readPixels(&tmpSrc, &subset)) {
999 SkASSERT(tmpSrc.width() == this->width());
1000 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +00001001
scroggo@google.coma2a31922012-12-07 19:14:45 +00001002 // did we get lucky and we can just return tmpSrc?
1003 if (tmpSrc.config() == dstConfig && NULL == alloc) {
1004 dst->swap(tmpSrc);
1005 if (dst->pixelRef() && this->config() == dstConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001006 // TODO(scroggo): fix issue 1742
1007 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001008 }
1009 return true;
1010 }
1011
1012 // fall through to the raster case
1013 src = &tmpSrc;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001014 }
reed@google.com50dfa012011-04-01 19:05:36 +00001015 }
reed@android.comfbaa88d2009-05-06 17:44:34 +00001016 }
reed@android.com311c82d2009-05-05 23:13:23 +00001017
reed@google.com50dfa012011-04-01 19:05:36 +00001018 // we lock this now, since we may need its colortable
1019 SkAutoLockPixels srclock(*src);
1020 if (!src->readyToDraw()) {
1021 return false;
1022 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001023
reed@google.com50dfa012011-04-01 19:05:36 +00001024 SkBitmap tmpDst;
reed@google.com383a6972013-10-21 14:00:07 +00001025 tmpDst.setConfig(dstConfig, src->width(), src->height(), 0,
1026 src->alphaType());
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001027
weita@google.comf9ab99a2009-05-03 18:23:30 +00001028 // allocate colortable if srcConfig == kIndex8_Config
1029 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +00001030 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001031 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +00001032 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +00001033 return false;
1034 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001035
reed@google.com50dfa012011-04-01 19:05:36 +00001036 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 // allocator/lock failed
1038 return false;
1039 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001040
reed@android.comfbaa88d2009-05-06 17:44:34 +00001041 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +00001042 */
reed@google.com50dfa012011-04-01 19:05:36 +00001043 if (src->config() == dstConfig) {
1044 if (tmpDst.getSize() == src->getSize()) {
1045 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
scroggo@google.comd5764e82012-08-22 15:00:05 +00001046 SkPixelRef* pixelRef = tmpDst.pixelRef();
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001047 if (NULL != pixelRef && NULL != fPixelRef) {
1048 // TODO(scroggo): fix issue 1742
1049 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.comd5764e82012-08-22 15:00:05 +00001050 }
reed@android.com311c82d2009-05-05 23:13:23 +00001051 } else {
reed@google.com50dfa012011-04-01 19:05:36 +00001052 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
1053 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +00001054 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +00001055 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
1056 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +00001057 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +00001058 srcP += src->rowBytes();
1059 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +00001060 }
1061 }
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001062 } else if (SkBitmap::kARGB_4444_Config == dstConfig
1063 && SkBitmap::kARGB_8888_Config == src->config()) {
1064 SkASSERT(src->height() == tmpDst.height());
1065 SkASSERT(src->width() == tmpDst.width());
1066 for (int y = 0; y < src->height(); ++y) {
1067 SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*) tmpDst.getAddr16(0, y);
1068 SkPMColor* SK_RESTRICT srcRow = (SkPMColor*) src->getAddr32(0, y);
1069 DITHER_4444_SCAN(y);
1070 for (int x = 0; x < src->width(); ++x) {
1071 dstRow[x] = SkDitherARGB32To4444(srcRow[x],
1072 DITHER_VALUE(x));
1073 }
1074 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001075 } else {
robertphillips@google.com0197b322013-10-10 15:48:16 +00001076 // Always clear the dest in case one of the blitters accesses it
1077 // TODO: switch the allocation of tmpDst to call sk_calloc_throw
1078 tmpDst.eraseColor(SK_ColorTRANSPARENT);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001079
reed@google.com50dfa012011-04-01 19:05:36 +00001080 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001081 SkPaint paint;
1082
1083 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +00001084 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 }
1086
reed@google.com50dfa012011-04-01 19:05:36 +00001087 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 return true;
1089}
1090
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001091bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
1092 if (!this->canCopyTo(dstConfig)) {
1093 return false;
1094 }
1095
1096 // If we have a PixelRef, and it supports deep copy, use it.
1097 // Currently supported only by texture-backed bitmaps.
1098 if (fPixelRef) {
1099 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
1100 if (pixelRef) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001101 uint32_t rowBytes;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001102 if (dstConfig == fConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001103 // TODO(scroggo): fix issue 1742
1104 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001105 // Use the same rowBytes as the original.
1106 rowBytes = fRowBytes;
1107 } else {
1108 // With the new config, an appropriate fRowBytes will be computed by setConfig.
1109 rowBytes = 0;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001110 }
scroggo@google.coma2a31922012-12-07 19:14:45 +00001111 dst->setConfig(dstConfig, fWidth, fHeight, rowBytes);
1112
1113 size_t pixelRefOffset;
1114 if (0 == fPixelRefOffset || dstConfig == fConfig) {
1115 // Use the same offset as the original.
1116 pixelRefOffset = fPixelRefOffset;
1117 } else {
1118 // Find the correct offset in the new config. This needs to be done after calling
1119 // setConfig so dst's fConfig and fRowBytes have been set properly.
scroggo@google.come5f48242013-02-25 21:47:41 +00001120 int32_t x, y;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001121 if (!get_upper_left_from_offset(*this, &x, &y)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001122 return false;
1123 }
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001124 pixelRefOffset = get_sub_offset(*dst, x, y);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001125 if (SUB_OFFSET_FAILURE == pixelRefOffset) {
1126 return false;
1127 }
1128 }
1129 dst->setPixelRef(pixelRef, pixelRefOffset)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001130 return true;
1131 }
1132 }
1133
1134 if (this->getTexture()) {
1135 return false;
1136 } else {
1137 return this->copyTo(dst, dstConfig, NULL);
1138 }
1139}
1140
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141///////////////////////////////////////////////////////////////////////////////
1142///////////////////////////////////////////////////////////////////////////////
1143
1144static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
1145 const SkBitmap& src) {
1146 x <<= 1;
1147 y <<= 1;
1148 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001149 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 SkPMColor c, ag, rb;
1151
1152 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
1153 if (x < src.width() - 1) {
1154 p += 1;
1155 }
1156 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1157
reed@android.com829c83c2009-06-08 12:05:31 +00001158 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001160 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 }
1162 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1163 if (x < src.width() - 1) {
1164 p += 1;
1165 }
1166 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1167
1168 *dst->getAddr32(x >> 1, y >> 1) =
1169 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1170}
1171
1172static inline uint32_t expand16(U16CPU c) {
1173 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1174}
1175
1176// returns dirt in the top 16bits, but we don't care, since we only
1177// store the low 16bits.
1178static inline U16CPU pack16(uint32_t c) {
1179 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1180}
1181
1182static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1183 const SkBitmap& src) {
1184 x <<= 1;
1185 y <<= 1;
1186 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001187 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001189
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001191 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 p += 1;
1193 }
1194 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001195
reed@android.com829c83c2009-06-08 12:05:31 +00001196 p = baseP;
1197 if (y < src.height() - 1) {
1198 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 }
1200 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001201 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 p += 1;
1203 }
1204 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1207}
1208
1209static uint32_t expand4444(U16CPU c) {
1210 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1211}
1212
1213static U16CPU collaps4444(uint32_t c) {
1214 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1215}
1216
1217static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1218 const SkBitmap& src) {
1219 x <<= 1;
1220 y <<= 1;
1221 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001222 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001224
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 c = expand4444(*p);
1226 if (x < src.width() - 1) {
1227 p += 1;
1228 }
1229 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001230
reed@android.com829c83c2009-06-08 12:05:31 +00001231 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001233 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234 }
1235 c += expand4444(*p);
1236 if (x < src.width() - 1) {
1237 p += 1;
1238 }
1239 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1242}
1243
1244void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 if (forceRebuild)
1246 this->freeMipMap();
1247 else if (fMipMap)
1248 return; // we're already built
1249
1250 SkASSERT(NULL == fMipMap);
1251
1252 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1253
reed@google.com44699382013-10-31 17:28:30 +00001254 const SkBitmap::Config config = this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255
1256 switch (config) {
1257 case kARGB_8888_Config:
1258 proc = downsampleby2_proc32;
1259 break;
1260 case kRGB_565_Config:
1261 proc = downsampleby2_proc16;
1262 break;
1263 case kARGB_4444_Config:
1264 proc = downsampleby2_proc4444;
1265 break;
1266 case kIndex8_Config:
1267 case kA8_Config:
1268 default:
1269 return; // don't build mipmaps for these configs
1270 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001271
reed@android.com149e2f62009-05-22 14:39:03 +00001272 SkAutoLockPixels alp(*this);
1273 if (!this->readyToDraw()) {
1274 return;
1275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276
1277 // whip through our loop to compute the exact size needed
1278 size_t size = 0;
1279 int maxLevels = 0;
1280 {
reed@android.com149e2f62009-05-22 14:39:03 +00001281 int width = this->width();
1282 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 for (;;) {
1284 width >>= 1;
1285 height >>= 1;
1286 if (0 == width || 0 == height) {
1287 break;
1288 }
1289 size += ComputeRowBytes(config, width) * height;
1290 maxLevels += 1;
1291 }
1292 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001293
reed@android.com149e2f62009-05-22 14:39:03 +00001294 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 if (0 == maxLevels) {
1296 return;
1297 }
1298
reed@android.com149e2f62009-05-22 14:39:03 +00001299 SkBitmap srcBM(*this);
1300 srcBM.lockPixels();
1301 if (!srcBM.readyToDraw()) {
1302 return;
1303 }
1304
1305 MipMap* mm = MipMap::Alloc(maxLevels, size);
1306 if (NULL == mm) {
1307 return;
1308 }
1309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 MipLevel* level = mm->levels();
1311 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001312 int width = this->width();
1313 int height = this->height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001314 uint32_t rowBytes;
reed@android.com149e2f62009-05-22 14:39:03 +00001315 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316
1317 for (int i = 0; i < maxLevels; i++) {
1318 width >>= 1;
1319 height >>= 1;
scroggo@google.come5f48242013-02-25 21:47:41 +00001320 rowBytes = SkToU32(ComputeRowBytes(config, width));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321
1322 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001323 level[i].fWidth = width;
1324 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001325 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326
1327 dstBM.setConfig(config, width, height, rowBytes);
1328 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001329
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001330 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001331 for (int y = 0; y < height; y++) {
1332 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 proc(&dstBM, x, y, srcBM);
1334 }
1335 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001336 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337
1338 srcBM = dstBM;
1339 addr += height * rowBytes;
1340 }
1341 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1342 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343}
1344
1345bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347}
1348
1349int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001350 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001352 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001353
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 int level = ComputeMipLevel(sx, sy) >> 16;
1355 SkASSERT(level >= 0);
1356 if (level <= 0) {
1357 return 0;
1358 }
1359
1360 if (level >= fMipMap->fLevelCount) {
1361 level = fMipMap->fLevelCount - 1;
1362 }
1363 if (dst) {
1364 const MipLevel& mip = fMipMap->levels()[level - 1];
1365 dst->setConfig((SkBitmap::Config)this->config(),
1366 mip.fWidth, mip.fHeight, mip.fRowBytes);
1367 dst->setPixels(mip.fPixels);
1368 }
1369 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370}
1371
1372SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 sx = SkAbs32(sx);
1374 sy = SkAbs32(sy);
1375 if (sx < sy) {
1376 sx = sy;
1377 }
1378 if (sx < SK_Fixed1) {
1379 return 0;
1380 }
1381 int clz = SkCLZ(sx);
1382 SkASSERT(clz >= 1 && clz <= 15);
1383 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384}
1385
1386///////////////////////////////////////////////////////////////////////////////
1387
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001388static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 int alphaRowBytes) {
1390 SkASSERT(alpha != NULL);
1391 SkASSERT(alphaRowBytes >= src.width());
1392
reed@google.com44699382013-10-31 17:28:30 +00001393 SkBitmap::Config config = src.config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 int w = src.width();
1395 int h = src.height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001396 size_t rb = src.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397
reed@android.com1cdcb512009-08-24 19:11:00 +00001398 SkAutoLockPixels alp(src);
1399 if (!src.readyToDraw()) {
1400 // zero out the alpha buffer and return
1401 while (--h >= 0) {
1402 memset(alpha, 0, w);
1403 alpha += alphaRowBytes;
1404 }
1405 return false;
1406 }
reed@google.com82065d62011-02-07 15:30:46 +00001407
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1409 const uint8_t* s = src.getAddr8(0, 0);
1410 while (--h >= 0) {
1411 memcpy(alpha, s, w);
1412 s += rb;
1413 alpha += alphaRowBytes;
1414 }
1415 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1416 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1417 while (--h >= 0) {
1418 for (int x = 0; x < w; x++) {
1419 alpha[x] = SkGetPackedA32(s[x]);
1420 }
1421 s = (const SkPMColor*)((const char*)s + rb);
1422 alpha += alphaRowBytes;
1423 }
1424 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1425 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1426 while (--h >= 0) {
1427 for (int x = 0; x < w; x++) {
1428 alpha[x] = SkPacked4444ToA32(s[x]);
1429 }
1430 s = (const SkPMColor16*)((const char*)s + rb);
1431 alpha += alphaRowBytes;
1432 }
1433 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1434 SkColorTable* ct = src.getColorTable();
1435 if (ct) {
1436 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1437 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1438 while (--h >= 0) {
1439 for (int x = 0; x < w; x++) {
1440 alpha[x] = SkGetPackedA32(table[s[x]]);
1441 }
1442 s += rb;
1443 alpha += alphaRowBytes;
1444 }
reed@google.com0a6151d2013-10-10 14:44:56 +00001445 ct->unlockColors();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 }
1447 } else { // src is opaque, so just fill alpha[] with 0xFF
1448 memset(alpha, 0xFF, h * alphaRowBytes);
1449 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001450 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
1453#include "SkPaint.h"
1454#include "SkMaskFilter.h"
1455#include "SkMatrix.h"
1456
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001457bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001458 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 SkDEBUGCODE(this->validate();)
1460
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001461 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 SkMatrix identity;
1463 SkMask srcM, dstM;
1464
1465 srcM.fBounds.set(0, 0, this->width(), this->height());
1466 srcM.fRowBytes = SkAlign4(this->width());
1467 srcM.fFormat = SkMask::kA8_Format;
1468
1469 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1470
1471 // compute our (larger?) dst bounds if we have a filter
1472 if (NULL != filter) {
1473 identity.reset();
1474 srcM.fImage = NULL;
1475 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1476 goto NO_FILTER_CASE;
1477 }
1478 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1479 } else {
1480 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001481 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001483 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1484 // Allocation of pixels for alpha bitmap failed.
1485 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1486 tmpBitmap.width(), tmpBitmap.height());
1487 return false;
1488 }
1489 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 if (offset) {
1491 offset->set(0, 0);
1492 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001493 tmpBitmap.swap(*dst);
1494 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001496 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1497 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498
1499 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1500 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1501 goto NO_FILTER_CASE;
1502 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001503 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001505 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001507 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1508 // Allocation of pixels for alpha bitmap failed.
1509 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1510 tmpBitmap.width(), tmpBitmap.height());
1511 return false;
1512 }
1513 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 if (offset) {
1515 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1516 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001517 SkDEBUGCODE(tmpBitmap.validate();)
1518
1519 tmpBitmap.swap(*dst);
1520 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521}
1522
1523///////////////////////////////////////////////////////////////////////////////
1524
1525enum {
1526 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001527 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528};
1529
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001531 buffer.writeInt(fWidth);
1532 buffer.writeInt(fHeight);
1533 buffer.writeInt(fRowBytes);
1534 buffer.writeInt(fConfig);
reed@google.com383a6972013-10-21 14:00:07 +00001535 buffer.writeInt(fAlphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001536
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001538 if (fPixelRef->getFactory()) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001539 buffer.writeInt(SERIALIZE_PIXELTYPE_REF_DATA);
scroggo@google.come5f48242013-02-25 21:47:41 +00001540 buffer.writeUInt(SkToU32(fPixelRefOffset));
djsollen@google.com5370cd92012-03-28 20:47:01 +00001541 buffer.writeFlattenable(fPixelRef);
1542 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543 }
1544 // if we get here, we can't record the pixels
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001545 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546 } else {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001547 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 }
1549}
1550
1551void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1552 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 int width = buffer.readInt();
1555 int height = buffer.readInt();
1556 int rowBytes = buffer.readInt();
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001557 Config config = (Config)buffer.readInt();
1558 SkAlphaType alphaType = (SkAlphaType)buffer.readInt();
1559 buffer.validate((width >= 0) && (height >= 0) && (rowBytes >= 0) &&
1560 SkIsValidConfig(config) && validate_alphaType(config, alphaType));
weita@google.comf9ab99a2009-05-03 18:23:30 +00001561
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001562 this->setConfig(config, width, height, rowBytes, alphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001563
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001564 int reftype = buffer.readInt();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565 switch (reftype) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 case SERIALIZE_PIXELTYPE_REF_DATA: {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001567 size_t offset = buffer.readUInt();
reed@google.com35348222013-10-16 13:05:06 +00001568 SkPixelRef* pr = buffer.readPixelRef();
reed@google.com82065d62011-02-07 15:30:46 +00001569 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 break;
1571 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001572 case SERIALIZE_PIXELTYPE_NONE:
1573 break;
1574 default:
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001575 buffer.validate(false);
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001576 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577 sk_throw();
1578 }
1579}
1580
1581///////////////////////////////////////////////////////////////////////////////
1582
1583SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1584 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001585 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586}
1587
1588SkBitmap::RLEPixels::~RLEPixels() {
1589 sk_free(fYPtrs);
1590}
1591
1592///////////////////////////////////////////////////////////////////////////////
1593
1594#ifdef SK_DEBUG
1595void SkBitmap::validate() const {
1596 SkASSERT(fConfig < kConfigCount);
1597 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001598 uint8_t allFlags = kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag;
1599#ifdef SK_BUILD_FOR_ANDROID
1600 allFlags |= kHasHardwareMipMap_Flag;
1601#endif
1602 SkASSERT(fFlags <= allFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 SkASSERT(fPixelLockCount >= 0);
1604 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1605 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1606
1607#if 0 // these asserts are not thread-correct, so disable for now
1608 if (fPixelRef) {
1609 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001610 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 } else {
1612 SkASSERT(NULL == fPixels);
1613 SkASSERT(NULL == fColorTable);
1614 }
1615 }
1616#endif
1617}
1618#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001619
1620#ifdef SK_DEVELOPER
1621void SkBitmap::toString(SkString* str) const {
1622
1623 static const char* gConfigNames[kConfigCount] = {
reed@google.com2a1f4462013-11-25 18:58:29 +00001624 "NONE", "A8", "INDEX8", "565", "4444", "8888"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001625 };
1626
1627 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
1628 gConfigNames[this->config()]);
1629
1630 str->append(" (");
1631 if (this->isOpaque()) {
1632 str->append("opaque");
1633 } else {
1634 str->append("transparent");
1635 }
1636 if (this->isImmutable()) {
1637 str->append(", immutable");
1638 } else {
1639 str->append(", not-immutable");
1640 }
1641 str->append(")");
1642
1643 SkPixelRef* pr = this->pixelRef();
1644 if (NULL == pr) {
1645 // show null or the explicit pixel address (rare)
1646 str->appendf(" pixels:%p", this->getPixels());
1647 } else {
1648 const char* uri = pr->getURI();
1649 if (NULL != uri) {
1650 str->appendf(" uri:\"%s\"", uri);
1651 } else {
1652 str->appendf(" pixelref:%p", pr);
1653 }
1654 }
1655
1656 str->append(")");
1657}
1658#endif