blob: 2f1c9a49ad126ed9a98a643d3de5bb6d14fc1cb5 [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"
14#include "SkMallocPixelRef.h"
15#include "SkMask.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000016#include "SkOrderedReadBuffer.h"
17#include "SkOrderedWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkPixelRef.h"
19#include "SkThread.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000020#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkUtils.h"
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000022#include "SkValidationUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkPackBits.h"
24#include <new>
25
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000026SK_DEFINE_INST_COUNT(SkBitmap::Allocator)
27
reed@android.com149e2f62009-05-22 14:39:03 +000028static bool isPos32Bits(const Sk64& value) {
29 return !value.isNeg() && value.is32();
30}
31
reed@android.com8a1c16f2008-12-17 15:59:43 +000032struct MipLevel {
33 void* fPixels;
34 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000035 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000036};
37
38struct SkBitmap::MipMap : SkNoncopyable {
39 int32_t fRefCnt;
40 int fLevelCount;
41// MipLevel fLevel[fLevelCount];
42// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000043
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000045 if (levelCount < 0) {
46 return NULL;
47 }
48 Sk64 size;
49 size.setMul(levelCount + 1, sizeof(MipLevel));
50 size.add(sizeof(MipMap));
scroggo@google.come5f48242013-02-25 21:47:41 +000051 size.add(SkToS32(pixelSize));
reed@android.com149e2f62009-05-22 14:39:03 +000052 if (!isPos32Bits(size)) {
53 return NULL;
54 }
55 MipMap* mm = (MipMap*)sk_malloc_throw(size.get32());
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 mm->fRefCnt = 1;
57 mm->fLevelCount = levelCount;
58 return mm;
59 }
60
61 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
62 MipLevel* levels() { return (MipLevel*)(this + 1); }
63
64 const void* pixels() const { return levels() + fLevelCount; }
65 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000066
reed@android.com149e2f62009-05-22 14:39:03 +000067 void ref() {
68 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
69 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 }
71 }
reed@android.com149e2f62009-05-22 14:39:03 +000072 void unref() {
73 SkASSERT(fRefCnt > 0);
74 if (sk_atomic_dec(&fRefCnt) == 1) {
75 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 }
77 }
78};
reed@android.com8a1c16f2008-12-17 15:59:43 +000079
80///////////////////////////////////////////////////////////////////////////////
81///////////////////////////////////////////////////////////////////////////////
82
83SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000084 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000085}
86
87SkBitmap::SkBitmap(const SkBitmap& src) {
88 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000089 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 *this = src;
91 SkDEBUGCODE(this->validate();)
92}
93
94SkBitmap::~SkBitmap() {
95 SkDEBUGCODE(this->validate();)
96 this->freePixels();
97}
98
99SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
100 if (this != &src) {
101 this->freePixels();
102 memcpy(this, &src, sizeof(src));
103
104 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +0000105 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +0000106 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107
108 // we reset our locks if we get blown away
109 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000110
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 /* The src could be in 3 states
112 1. no pixelref, in which case we just copy/ref the pixels/ctable
113 2. unlocked pixelref, pixels/ctable should be null
114 3. locked pixelref, we should lock the ref again ourselves
115 */
116 if (NULL == fPixelRef) {
117 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000118 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 } else { // we have a pixelref, so pixels/ctable reflect it
120 // ignore the values from the memcpy
121 fPixels = NULL;
122 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000123 // Note that what to for genID is somewhat arbitrary. We have no
124 // way to track changes to raw pixels across multiple SkBitmaps.
125 // Would benefit from an SkRawPixelRef type created by
126 // setPixels.
127 // Just leave the memcpy'ed one but they'll get out of sync
128 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 }
130 }
131
132 SkDEBUGCODE(this->validate();)
133 return *this;
134}
135
136void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000137 SkTSwap(fColorTable, other.fColorTable);
138 SkTSwap(fPixelRef, other.fPixelRef);
139 SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
140 SkTSwap(fPixelLockCount, other.fPixelLockCount);
141 SkTSwap(fMipMap, other.fMipMap);
142 SkTSwap(fPixels, other.fPixels);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000143 SkTSwap(fRowBytes, other.fRowBytes);
144 SkTSwap(fWidth, other.fWidth);
145 SkTSwap(fHeight, other.fHeight);
146 SkTSwap(fConfig, other.fConfig);
reed@google.com383a6972013-10-21 14:00:07 +0000147 SkTSwap(fAlphaType, other.fAlphaType);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000148 SkTSwap(fFlags, other.fFlags);
149 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150
151 SkDEBUGCODE(this->validate();)
152}
153
154void SkBitmap::reset() {
155 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000156 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157}
158
159int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
160 int bpp;
161 switch (config) {
162 case kNo_Config:
163 case kA1_Config:
164 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;
196 case kA1_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000197 rowBytes.set(width);
198 rowBytes.add(7);
199 rowBytes.shiftRight(3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 break;
201 case kA8_Config:
202 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000203 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 break;
205 case kRGB_565_Config:
206 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000207 rowBytes.set(width);
208 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 break;
210 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000211 rowBytes.set(width);
212 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 break;
214 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000215 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 break;
217 }
reed@android.com149e2f62009-05-22 14:39:03 +0000218 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219}
220
221Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
222 Sk64 size;
scroggo@google.come5f48242013-02-25 21:47:41 +0000223 size.setMul(SkToS32(SkBitmap::ComputeRowBytes(c, width)), height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 return size;
225}
226
227size_t SkBitmap::ComputeSize(Config c, int width, int height) {
228 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000229 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230}
231
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000232Sk64 SkBitmap::ComputeSafeSize64(Config config,
233 uint32_t width,
234 uint32_t height,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000235 size_t rowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000236 Sk64 safeSize;
237 safeSize.setZero();
238 if (height > 0) {
scroggo@google.come5f48242013-02-25 21:47:41 +0000239 // TODO: Handle the case where the return value from
240 // ComputeRowBytes is more than 31 bits.
241 safeSize.set(SkToS32(ComputeRowBytes(config, width)));
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000242 Sk64 sizeAllButLastRow;
scroggo@google.come5f48242013-02-25 21:47:41 +0000243 sizeAllButLastRow.setMul(height - 1, SkToS32(rowBytes));
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000244 safeSize.add(sizeAllButLastRow);
245 }
246 SkASSERT(!safeSize.isNeg());
247 return safeSize;
248}
249
250size_t SkBitmap::ComputeSafeSize(Config config,
251 uint32_t width,
252 uint32_t height,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000253 size_t rowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000254 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
255 return (safeSize.is32() ? safeSize.get32() : 0);
256}
257
reed@google.com86b2e432012-03-15 21:17:03 +0000258void SkBitmap::getBounds(SkRect* bounds) const {
259 SkASSERT(bounds);
260 bounds->set(0, 0,
261 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
262}
263
reed@google.com80e14592012-03-16 14:58:07 +0000264void SkBitmap::getBounds(SkIRect* bounds) const {
265 SkASSERT(bounds);
266 bounds->set(0, 0, fWidth, fHeight);
267}
268
reed@google.com86b2e432012-03-15 21:17:03 +0000269///////////////////////////////////////////////////////////////////////////////
270
reed@google.com383a6972013-10-21 14:00:07 +0000271static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType,
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000272 SkAlphaType* canonical = NULL) {
reed@google.com383a6972013-10-21 14:00:07 +0000273 switch (config) {
274 case SkBitmap::kNo_Config:
275 alphaType = kIgnore_SkAlphaType;
276 break;
277 case SkBitmap::kA1_Config:
278 case SkBitmap::kA8_Config:
279 if (kUnpremul_SkAlphaType == alphaType) {
280 alphaType = kPremul_SkAlphaType;
281 }
282 // fall-through
283 case SkBitmap::kIndex8_Config:
284 case SkBitmap::kARGB_4444_Config:
285 case SkBitmap::kARGB_8888_Config:
286 if (kIgnore_SkAlphaType == alphaType) {
287 return false;
288 }
289 break;
290 case SkBitmap::kRGB_565_Config:
291 alphaType = kOpaque_SkAlphaType;
292 break;
reed@android.com149e2f62009-05-22 14:39:03 +0000293 }
reed@google.com383a6972013-10-21 14:00:07 +0000294 if (canonical) {
295 *canonical = alphaType;
296 }
297 return true;
298}
reed@android.com149e2f62009-05-22 14:39:03 +0000299
reed@google.com383a6972013-10-21 14:00:07 +0000300bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
301 SkAlphaType alphaType) {
302 if ((width | height) < 0) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000303 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000304 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 if (rowBytes == 0) {
reed@google.com383a6972013-10-21 14:00:07 +0000306 rowBytes = SkBitmap::ComputeRowBytes(config, width);
307 if (0 == rowBytes && kNo_Config != config) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000308 goto BAD_CONFIG;
reed@android.com149e2f62009-05-22 14:39:03 +0000309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000311
reed@google.com383a6972013-10-21 14:00:07 +0000312 if (!validate_alphaType(config, alphaType, &alphaType)) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000313 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000314 }
315
316 this->freePixels();
317
318 fConfig = SkToU8(config);
319 fAlphaType = SkToU8(alphaType);
reed@android.comf459a492009-03-27 12:33:50 +0000320 fWidth = width;
321 fHeight = height;
scroggo@google.come5f48242013-02-25 21:47:41 +0000322 fRowBytes = SkToU32(rowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323
reed@google.com383a6972013-10-21 14:00:07 +0000324 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(config);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325
326 SkDEBUGCODE(this->validate();)
reed@google.com383a6972013-10-21 14:00:07 +0000327 return true;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000328
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000329 // if we got here, we had an error, so we reset the bitmap to empty
reed@google.com9cd697c2013-10-21 14:12:13 +0000330BAD_CONFIG:
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000331 this->reset();
reed@google.com383a6972013-10-21 14:00:07 +0000332 return false;
333}
334
335bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
336 if (!validate_alphaType(this->config(), alphaType, &alphaType)) {
337 return false;
338 }
339 fAlphaType = SkToU8(alphaType);
340 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341}
342
343void SkBitmap::updatePixelsFromRef() const {
344 if (NULL != fPixelRef) {
345 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000346 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000347
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 void* p = fPixelRef->pixels();
349 if (NULL != p) {
350 p = (char*)p + fPixelRefOffset;
351 }
352 fPixels = p;
353 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
354 } else {
355 SkASSERT(0 == fPixelLockCount);
356 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000357 if (fColorTable) {
358 fColorTable->unref();
359 fColorTable = NULL;
360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 }
362 }
363}
364
365SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
366 // do this first, we that we never have a non-zero offset with a null ref
367 if (NULL == pr) {
368 offset = 0;
369 }
370
371 if (fPixelRef != pr || fPixelRefOffset != offset) {
372 if (fPixelRef != pr) {
373 this->freePixels();
374 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000375
reed@google.com82065d62011-02-07 15:30:46 +0000376 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 fPixelRef = pr;
378 }
379 fPixelRefOffset = offset;
380 this->updatePixelsFromRef();
381 }
382
383 SkDEBUGCODE(this->validate();)
384 return pr;
385}
386
387void SkBitmap::lockPixels() const {
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000388 if (NULL != fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 fPixelRef->lockPixels();
390 this->updatePixelsFromRef();
391 }
392 SkDEBUGCODE(this->validate();)
393}
394
395void SkBitmap::unlockPixels() const {
396 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
397
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000398 if (NULL != fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 fPixelRef->unlockPixels();
400 this->updatePixelsFromRef();
401 }
402 SkDEBUGCODE(this->validate();)
403}
404
reed@google.com9c49bc32011-07-07 13:42:37 +0000405bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000406 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000407}
408
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000410 if (NULL == p) {
411 this->setPixelRef(NULL, 0);
412 return;
413 }
414
djsollen@google.comc84b8332012-07-27 13:41:44 +0000415 Sk64 size = this->getSize64();
416 SkASSERT(!size.isNeg() && size.is32());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417
djsollen@google.comc84b8332012-07-27 13:41:44 +0000418 this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref();
419 // since we're already allocated, we lockPixels right away
420 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 SkDEBUGCODE(this->validate();)
422}
423
424bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
425 HeapAllocator stdalloc;
426
427 if (NULL == allocator) {
428 allocator = &stdalloc;
429 }
430 return allocator->allocPixelRef(this, ctable);
431}
432
433void SkBitmap::freePixels() {
434 // if we're gonna free the pixels, we certainly need to free the mipmap
435 this->freeMipMap();
436
reed@android.com149e2f62009-05-22 14:39:03 +0000437 if (fColorTable) {
438 fColorTable->unref();
439 fColorTable = NULL;
440 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441
442 if (NULL != fPixelRef) {
443 if (fPixelLockCount > 0) {
444 fPixelRef->unlockPixels();
445 }
446 fPixelRef->unref();
447 fPixelRef = NULL;
448 fPixelRefOffset = 0;
449 }
450 fPixelLockCount = 0;
451 fPixels = NULL;
452}
453
454void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000455 if (fMipMap) {
456 fMipMap->unref();
457 fMipMap = NULL;
458 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459}
460
461uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000462 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463}
464
465void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000466 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 if (fPixelRef) {
468 fPixelRef->notifyPixelsChanged();
469 }
470}
471
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000472GrTexture* SkBitmap::getTexture() const {
reed@android.comce4e53a2010-09-09 16:01:26 +0000473 return fPixelRef ? fPixelRef->getTexture() : NULL;
474}
475
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476///////////////////////////////////////////////////////////////////////////////
477
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478/** We explicitly use the same allocator for our pixels that SkMask does,
479 so that we can freely assign memory allocated by one class to the other.
480 */
481bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
482 SkColorTable* ctable) {
483 Sk64 size = dst->getSize64();
484 if (size.isNeg() || !size.is32()) {
485 return false;
486 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000487
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
489 if (NULL == addr) {
490 return false;
491 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000492
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
494 // since we're already allocated, we lockPixels right away
495 dst->lockPixels();
496 return true;
497}
498
499///////////////////////////////////////////////////////////////////////////////
500
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000501size_t SkBitmap::getSafeSize() const {
502 // This is intended to be a size_t version of ComputeSafeSize64(), just
503 // faster. The computation is meant to be identical.
504 return (fHeight ? ((fHeight - 1) * fRowBytes) +
505 ComputeRowBytes(getConfig(), fWidth): 0);
506}
507
508Sk64 SkBitmap::getSafeSize64() const {
509 return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
510}
511
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000512bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000513 size_t dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000514
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000515 if (0 == dstRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000516 dstRowBytes = fRowBytes;
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000517 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000518
reed@google.com2cb14802013-06-26 14:35:02 +0000519 if (dstRowBytes < ComputeRowBytes(getConfig(), fWidth) ||
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000520 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
521 return false;
522
bsalomon@google.comc6980972011-11-02 19:57:21 +0000523 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000524 size_t safeSize = getSafeSize();
525 if (safeSize > dstSize || safeSize == 0)
526 return false;
527 else {
528 SkAutoLockPixels lock(*this);
529 // This implementation will write bytes beyond the end of each row,
530 // excluding the last row, if the bitmap's stride is greater than
531 // strictly required by the current config.
532 memcpy(dst, getPixels(), safeSize);
533
534 return true;
535 }
536 } else {
537 // If destination has different stride than us, then copy line by line.
538 if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) >
539 dstSize)
540 return false;
541 else {
542 // Just copy what we need on each line.
scroggo@google.come5f48242013-02-25 21:47:41 +0000543 size_t rowBytes = ComputeRowBytes(getConfig(), fWidth);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000544 SkAutoLockPixels lock(*this);
545 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
546 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
547 for (uint32_t row = 0; row < fHeight;
548 row++, srcP += fRowBytes, dstP += dstRowBytes) {
549 memcpy(dstP, srcP, rowBytes);
550 }
551
552 return true;
553 }
554 }
555}
556
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000557///////////////////////////////////////////////////////////////////////////////
558
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000559bool SkBitmap::isImmutable() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000560 return fPixelRef ? fPixelRef->isImmutable() :
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000561 fFlags & kImageIsImmutable_Flag;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000562}
563
564void SkBitmap::setImmutable() {
565 if (fPixelRef) {
566 fPixelRef->setImmutable();
567 } else {
568 fFlags |= kImageIsImmutable_Flag;
569 }
570}
571
junov@google.com4ee7ae52011-06-30 17:30:49 +0000572bool SkBitmap::isVolatile() const {
573 return (fFlags & kImageIsVolatile_Flag) != 0;
574}
575
576void SkBitmap::setIsVolatile(bool isVolatile) {
577 if (isVolatile) {
578 fFlags |= kImageIsVolatile_Flag;
579 } else {
580 fFlags &= ~kImageIsVolatile_Flag;
581 }
582}
583
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584void* SkBitmap::getAddr(int x, int y) const {
585 SkASSERT((unsigned)x < (unsigned)this->width());
586 SkASSERT((unsigned)y < (unsigned)this->height());
587
588 char* base = (char*)this->getPixels();
589 if (base) {
590 base += y * this->rowBytes();
591 switch (this->config()) {
592 case SkBitmap::kARGB_8888_Config:
593 base += x << 2;
594 break;
595 case SkBitmap::kARGB_4444_Config:
596 case SkBitmap::kRGB_565_Config:
597 base += x << 1;
598 break;
599 case SkBitmap::kA8_Config:
600 case SkBitmap::kIndex8_Config:
601 base += x;
602 break;
603 case SkBitmap::kA1_Config:
604 base += x >> 3;
605 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000607 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 base = NULL;
609 break;
610 }
611 }
612 return base;
613}
614
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000615SkColor SkBitmap::getColor(int x, int y) const {
616 SkASSERT((unsigned)x < (unsigned)this->width());
617 SkASSERT((unsigned)y < (unsigned)this->height());
618
619 switch (this->config()) {
620 case SkBitmap::kA1_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000621 uint8_t* addr = this->getAddr1(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000622 uint8_t mask = 1 << (7 - (x % 8));
623 if (addr[0] & mask) {
624 return SK_ColorBLACK;
625 } else {
626 return 0;
627 }
628 }
629 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000630 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000631 return SkColorSetA(0, addr[0]);
632 }
633 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000634 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000635 return SkUnPreMultiply::PMColorToColor(c);
636 }
637 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000638 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000639 return SkPixel16ToColor(addr[0]);
640 }
641 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000642 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000643 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
644 return SkUnPreMultiply::PMColorToColor(c);
645 }
646 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000647 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000648 return SkUnPreMultiply::PMColorToColor(addr[0]);
649 }
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000650 case kNo_Config:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000651 SkASSERT(false);
652 return 0;
653 }
654 SkASSERT(false); // Not reached.
655 return 0;
656}
657
reed@google.com2a7579d2012-11-07 18:30:18 +0000658bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
659 SkAutoLockPixels alp(bm);
660 if (!bm.getPixels()) {
661 return false;
662 }
663
664 const int height = bm.height();
665 const int width = bm.width();
666
667 switch (bm.config()) {
668 case SkBitmap::kA1_Config: {
669 // TODO
670 } break;
671 case SkBitmap::kA8_Config: {
672 unsigned a = 0xFF;
673 for (int y = 0; y < height; ++y) {
674 const uint8_t* row = bm.getAddr8(0, y);
675 for (int x = 0; x < width; ++x) {
676 a &= row[x];
677 }
678 if (0xFF != a) {
679 return false;
680 }
681 }
682 return true;
683 } break;
reed@google.com2a7579d2012-11-07 18:30:18 +0000684 case SkBitmap::kIndex8_Config: {
685 SkAutoLockColors alc(bm);
686 const SkPMColor* table = alc.colors();
687 if (!table) {
688 return false;
689 }
reed@google.com140d7282013-01-07 20:25:04 +0000690 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000691 for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
692 c &= table[i];
693 }
694 return 0xFF == SkGetPackedA32(c);
695 } break;
696 case SkBitmap::kRGB_565_Config:
697 return true;
698 break;
699 case SkBitmap::kARGB_4444_Config: {
700 unsigned c = 0xFFFF;
701 for (int y = 0; y < height; ++y) {
702 const SkPMColor16* row = bm.getAddr16(0, y);
703 for (int x = 0; x < width; ++x) {
704 c &= row[x];
705 }
706 if (0xF != SkGetPackedA4444(c)) {
707 return false;
708 }
709 }
710 return true;
711 } break;
712 case SkBitmap::kARGB_8888_Config: {
reed@google.com140d7282013-01-07 20:25:04 +0000713 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000714 for (int y = 0; y < height; ++y) {
715 const SkPMColor* row = bm.getAddr32(0, y);
716 for (int x = 0; x < width; ++x) {
717 c &= row[x];
718 }
719 if (0xFF != SkGetPackedA32(c)) {
720 return false;
721 }
722 }
723 return true;
724 }
725 default:
726 break;
727 }
728 return false;
729}
730
731
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732///////////////////////////////////////////////////////////////////////////////
733///////////////////////////////////////////////////////////////////////////////
734
reed@google.com45f746f2013-06-21 19:51:31 +0000735static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
736 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
737 (SkR32To4444(r) << SK_R4444_SHIFT) |
738 (SkG32To4444(g) << SK_G4444_SHIFT) |
739 (SkB32To4444(b) << SK_B4444_SHIFT);
740 return SkToU16(pixel);
741}
742
reed@google.com60d32352013-06-28 19:40:50 +0000743void SkBitmap::internalErase(const SkIRect& area,
744 U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
745#ifdef SK_DEBUG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 SkDEBUGCODE(this->validate();)
reed@google.com60d32352013-06-28 19:40:50 +0000747 SkASSERT(!area.isEmpty());
748 {
reed@google.com92833f92013-06-28 19:47:43 +0000749 SkIRect total = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000750 SkASSERT(total.contains(area));
751 }
752#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753
reed@google.com60d32352013-06-28 19:40:50 +0000754 if (kNo_Config == fConfig || kIndex8_Config == fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 return;
756 }
757
758 SkAutoLockPixels alp(*this);
759 // perform this check after the lock call
760 if (!this->readyToDraw()) {
761 return;
762 }
763
reed@google.com60d32352013-06-28 19:40:50 +0000764 int height = area.height();
765 const int width = area.width();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 const int rowBytes = fRowBytes;
767
768 // make rgb premultiplied
769 if (255 != a) {
770 r = SkAlphaMul(r, a);
771 g = SkAlphaMul(g, a);
772 b = SkAlphaMul(b, a);
773 }
774
775 switch (fConfig) {
776 case kA1_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000777 uint8_t* p = this->getAddr1(area.fLeft, area.fTop);
778 const int left = area.fLeft >> 3;
779 const int right = area.fRight >> 3;
skia.committer@gmail.coma6ff36b2013-06-29 07:03:21 +0000780
reed@google.com60d32352013-06-28 19:40:50 +0000781 int middle = right - left - 1;
782
783 uint8_t leftMask = 0xFF >> (area.fLeft & 7);
784 uint8_t rightMask = ~(0xFF >> (area.fRight & 7));
785 if (left == right) {
786 leftMask &= rightMask;
787 rightMask = 0;
788 }
789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 a = (a >> 7) ? 0xFF : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 while (--height >= 0) {
reed@google.com60d32352013-06-28 19:40:50 +0000792 uint8_t* startP = p;
793
794 *p = (*p & ~leftMask) | (a & leftMask);
795 p++;
796 if (middle > 0) {
797 memset(p, a, middle);
798 p += middle;
799 }
800 if (rightMask) {
801 *p = (*p & ~rightMask) | (a & rightMask);
802 }
skia.committer@gmail.coma6ff36b2013-06-29 07:03:21 +0000803
reed@google.com60d32352013-06-28 19:40:50 +0000804 p = startP + rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 }
806 break;
807 }
808 case kA8_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000809 uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 while (--height >= 0) {
811 memset(p, a, width);
812 p += rowBytes;
813 }
814 break;
815 }
reed@google.com45f746f2013-06-21 19:51:31 +0000816 case kARGB_4444_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 case kRGB_565_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000818 uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
reed@google.com45f746f2013-06-21 19:51:31 +0000819 uint16_t v;
skia.committer@gmail.com020b25b2013-06-22 07:00:58 +0000820
reed@google.com45f746f2013-06-21 19:51:31 +0000821 if (kARGB_4444_Config == fConfig) {
822 v = pack_8888_to_4444(a, r, g, b);
823 } else {
824 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
825 g >> (8 - SK_G16_BITS),
826 b >> (8 - SK_B16_BITS));
827 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 while (--height >= 0) {
829 sk_memset16(p, v, width);
830 p = (uint16_t*)((char*)p + rowBytes);
831 }
832 break;
833 }
834 case kARGB_8888_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000835 uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 uint32_t v = SkPackARGB32(a, r, g, b);
837
838 while (--height >= 0) {
839 sk_memset32(p, v, width);
840 p = (uint32_t*)((char*)p + rowBytes);
841 }
842 break;
843 }
844 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000845
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 this->notifyPixelsChanged();
847}
848
reed@google.com60d32352013-06-28 19:40:50 +0000849void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
reed@google.com92833f92013-06-28 19:47:43 +0000850 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000851 if (!area.isEmpty()) {
852 this->internalErase(area, a, r, g, b);
853 }
854}
855
856void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
reed@google.com92833f92013-06-28 19:47:43 +0000857 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000858 if (area.intersect(rect)) {
859 this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
860 SkColorGetG(c), SkColorGetB(c));
861 }
862}
863
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864//////////////////////////////////////////////////////////////////////////////////////
865//////////////////////////////////////////////////////////////////////////////////////
866
867#define SUB_OFFSET_FAILURE ((size_t)-1)
868
scroggo@google.coma2a31922012-12-07 19:14:45 +0000869/**
870 * Based on the Config and rowBytes() of bm, return the offset into an SkPixelRef of the pixel at
871 * (x, y).
872 * Note that the SkPixelRef does not need to be set yet. deepCopyTo takes advantage of this fact.
873 * Also note that (x, y) may be outside the range of (0 - width(), 0 - height()), so long as it is
874 * within the bounds of the SkPixelRef being used.
875 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000876static size_t get_sub_offset(const SkBitmap& bm, int x, int y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 switch (bm.getConfig()) {
878 case SkBitmap::kA8_Config:
879 case SkBitmap:: kIndex8_Config:
880 // x is fine as is for the calculation
881 break;
882
883 case SkBitmap::kRGB_565_Config:
884 case SkBitmap::kARGB_4444_Config:
885 x <<= 1;
886 break;
887
888 case SkBitmap::kARGB_8888_Config:
889 x <<= 2;
890 break;
891
892 case SkBitmap::kNo_Config:
893 case SkBitmap::kA1_Config:
894 default:
895 return SUB_OFFSET_FAILURE;
896 }
897 return y * bm.rowBytes() + x;
898}
899
scroggo@google.coma2a31922012-12-07 19:14:45 +0000900/**
901 * Using the pixelRefOffset(), rowBytes(), and Config of bm, determine the (x, y) coordinate of the
902 * upper left corner of bm relative to its SkPixelRef.
903 * x and y must be non-NULL.
904 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000905bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com61d6c9e2013-05-21 20:38:40 +0000906 int32_t* x, int32_t* y);
907bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000908 int32_t* x, int32_t* y) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000909 SkASSERT(x != NULL && y != NULL);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000910 if (0 == offset) {
911 *x = *y = 0;
912 return true;
913 }
914 // Use integer division to find the correct y position.
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000915 // The remainder will be the x position, after we reverse get_sub_offset.
commit-bot@chromium.org2c86fbb2013-09-26 19:22:54 +0000916 SkTDivMod(offset, rowBytes, y, x);
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000917 switch (config) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000918 case SkBitmap::kA8_Config:
919 // Fall through.
920 case SkBitmap::kIndex8_Config:
921 // x is unmodified
922 break;
923
924 case SkBitmap::kRGB_565_Config:
925 // Fall through.
926 case SkBitmap::kARGB_4444_Config:
927 *x >>= 1;
928 break;
929
930 case SkBitmap::kARGB_8888_Config:
931 *x >>= 2;
932 break;
933
934 case SkBitmap::kNo_Config:
935 // Fall through.
936 case SkBitmap::kA1_Config:
937 // Fall through.
938 default:
939 return false;
940 }
941 return true;
942}
943
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000944static bool get_upper_left_from_offset(const SkBitmap& bm, int32_t* x, int32_t* y) {
945 return get_upper_left_from_offset(bm.config(), bm.pixelRefOffset(), bm.rowBytes(), x, y);
946}
947
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
949 SkDEBUGCODE(this->validate();)
950
djsollen@google.comc84b8332012-07-27 13:41:44 +0000951 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 return false; // no src pixels
953 }
954
955 SkIRect srcRect, r;
956 srcRect.set(0, 0, this->width(), this->height());
957 if (!r.intersect(srcRect, subset)) {
958 return false; // r is empty (i.e. no intersection)
959 }
960
scroggo@google.coma2a31922012-12-07 19:14:45 +0000961 if (fPixelRef->getTexture() != NULL) {
962 // Do a deep copy
963 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->config(), &subset);
964 if (pixelRef != NULL) {
965 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000966 dst.setConfig(this->config(), subset.width(), subset.height(), 0,
967 this->alphaType());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000968 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000969 dst.setPixelRef(pixelRef)->unref();
970 SkDEBUGCODE(dst.validate());
971 result->swap(dst);
972 return true;
973 }
974 }
975
scroggo@google.coma2a31922012-12-07 19:14:45 +0000976 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
977 // exited above.
978 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
979 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
980
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000981 size_t offset = get_sub_offset(*this, r.fLeft, r.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 if (SUB_OFFSET_FAILURE == offset) {
983 return false; // config not supported
984 }
985
986 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000987 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes(),
988 this->alphaType());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000989 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990
991 if (fPixelRef) {
992 // share the pixelref with a custom offset
993 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 }
995 SkDEBUGCODE(dst.validate();)
996
997 // we know we're good, so commit to result
998 result->swap(dst);
999 return true;
1000}
1001
1002///////////////////////////////////////////////////////////////////////////////
1003
1004#include "SkCanvas.h"
1005#include "SkPaint.h"
1006
reed@android.comfbaa88d2009-05-06 17:44:34 +00001007bool SkBitmap::canCopyTo(Config dstConfig) const {
1008 if (this->getConfig() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 return false;
1010 }
1011
reed@android.comfbaa88d2009-05-06 17:44:34 +00001012 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 switch (dstConfig) {
1014 case kA8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 case kRGB_565_Config:
1016 case kARGB_8888_Config:
1017 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001018 case kA1_Config:
1019 case kIndex8_Config:
1020 if (!sameConfigs) {
1021 return false;
1022 }
1023 break;
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001024 case kARGB_4444_Config:
1025 return sameConfigs || kARGB_8888_Config == this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 default:
1027 return false;
1028 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001029
1030 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
1031 if (this->getConfig() == kA1_Config && !sameConfigs) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 return false;
1033 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001034
reed@android.comfbaa88d2009-05-06 17:44:34 +00001035 return true;
1036}
1037
1038bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
1039 if (!this->canCopyTo(dstConfig)) {
1040 return false;
1041 }
1042
reed@google.com50dfa012011-04-01 19:05:36 +00001043 // if we have a texture, first get those pixels
1044 SkBitmap tmpSrc;
1045 const SkBitmap* src = this;
1046
scroggo@google.coma2a31922012-12-07 19:14:45 +00001047 if (fPixelRef) {
1048 SkIRect subset;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001049 if (get_upper_left_from_offset(*this, &subset.fLeft, &subset.fTop)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001050 subset.fRight = subset.fLeft + fWidth;
1051 subset.fBottom = subset.fTop + fHeight;
1052 if (fPixelRef->readPixels(&tmpSrc, &subset)) {
1053 SkASSERT(tmpSrc.width() == this->width());
1054 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +00001055
scroggo@google.coma2a31922012-12-07 19:14:45 +00001056 // did we get lucky and we can just return tmpSrc?
1057 if (tmpSrc.config() == dstConfig && NULL == alloc) {
1058 dst->swap(tmpSrc);
1059 if (dst->pixelRef() && this->config() == dstConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001060 // TODO(scroggo): fix issue 1742
1061 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001062 }
1063 return true;
1064 }
1065
1066 // fall through to the raster case
1067 src = &tmpSrc;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001068 }
reed@google.com50dfa012011-04-01 19:05:36 +00001069 }
reed@android.comfbaa88d2009-05-06 17:44:34 +00001070 }
reed@android.com311c82d2009-05-05 23:13:23 +00001071
reed@google.com50dfa012011-04-01 19:05:36 +00001072 // we lock this now, since we may need its colortable
1073 SkAutoLockPixels srclock(*src);
1074 if (!src->readyToDraw()) {
1075 return false;
1076 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001077
reed@google.com50dfa012011-04-01 19:05:36 +00001078 SkBitmap tmpDst;
reed@google.com383a6972013-10-21 14:00:07 +00001079 tmpDst.setConfig(dstConfig, src->width(), src->height(), 0,
1080 src->alphaType());
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001081
weita@google.comf9ab99a2009-05-03 18:23:30 +00001082 // allocate colortable if srcConfig == kIndex8_Config
1083 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +00001084 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001085 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +00001086 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +00001087 return false;
1088 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001089
reed@google.com50dfa012011-04-01 19:05:36 +00001090 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 // allocator/lock failed
1092 return false;
1093 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001094
reed@android.comfbaa88d2009-05-06 17:44:34 +00001095 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +00001096 */
reed@google.com50dfa012011-04-01 19:05:36 +00001097 if (src->config() == dstConfig) {
1098 if (tmpDst.getSize() == src->getSize()) {
1099 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
scroggo@google.comd5764e82012-08-22 15:00:05 +00001100 SkPixelRef* pixelRef = tmpDst.pixelRef();
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001101 if (NULL != pixelRef && NULL != fPixelRef) {
1102 // TODO(scroggo): fix issue 1742
1103 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.comd5764e82012-08-22 15:00:05 +00001104 }
reed@android.com311c82d2009-05-05 23:13:23 +00001105 } else {
reed@google.com50dfa012011-04-01 19:05:36 +00001106 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
1107 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +00001108 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +00001109 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
1110 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +00001111 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +00001112 srcP += src->rowBytes();
1113 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +00001114 }
1115 }
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001116 } else if (SkBitmap::kARGB_4444_Config == dstConfig
1117 && SkBitmap::kARGB_8888_Config == src->config()) {
1118 SkASSERT(src->height() == tmpDst.height());
1119 SkASSERT(src->width() == tmpDst.width());
1120 for (int y = 0; y < src->height(); ++y) {
1121 SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*) tmpDst.getAddr16(0, y);
1122 SkPMColor* SK_RESTRICT srcRow = (SkPMColor*) src->getAddr32(0, y);
1123 DITHER_4444_SCAN(y);
1124 for (int x = 0; x < src->width(); ++x) {
1125 dstRow[x] = SkDitherARGB32To4444(srcRow[x],
1126 DITHER_VALUE(x));
1127 }
1128 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001129 } else {
robertphillips@google.com0197b322013-10-10 15:48:16 +00001130 // Always clear the dest in case one of the blitters accesses it
1131 // TODO: switch the allocation of tmpDst to call sk_calloc_throw
1132 tmpDst.eraseColor(SK_ColorTRANSPARENT);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001133
reed@google.com50dfa012011-04-01 19:05:36 +00001134 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001135 SkPaint paint;
1136
1137 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +00001138 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 }
1140
reed@google.com50dfa012011-04-01 19:05:36 +00001141 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 return true;
1143}
1144
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001145bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
1146 if (!this->canCopyTo(dstConfig)) {
1147 return false;
1148 }
1149
1150 // If we have a PixelRef, and it supports deep copy, use it.
1151 // Currently supported only by texture-backed bitmaps.
1152 if (fPixelRef) {
1153 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
1154 if (pixelRef) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001155 uint32_t rowBytes;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001156 if (dstConfig == fConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001157 // TODO(scroggo): fix issue 1742
1158 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001159 // Use the same rowBytes as the original.
1160 rowBytes = fRowBytes;
1161 } else {
1162 // With the new config, an appropriate fRowBytes will be computed by setConfig.
1163 rowBytes = 0;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001164 }
scroggo@google.coma2a31922012-12-07 19:14:45 +00001165 dst->setConfig(dstConfig, fWidth, fHeight, rowBytes);
1166
1167 size_t pixelRefOffset;
1168 if (0 == fPixelRefOffset || dstConfig == fConfig) {
1169 // Use the same offset as the original.
1170 pixelRefOffset = fPixelRefOffset;
1171 } else {
1172 // Find the correct offset in the new config. This needs to be done after calling
1173 // setConfig so dst's fConfig and fRowBytes have been set properly.
scroggo@google.come5f48242013-02-25 21:47:41 +00001174 int32_t x, y;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001175 if (!get_upper_left_from_offset(*this, &x, &y)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001176 return false;
1177 }
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001178 pixelRefOffset = get_sub_offset(*dst, x, y);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001179 if (SUB_OFFSET_FAILURE == pixelRefOffset) {
1180 return false;
1181 }
1182 }
1183 dst->setPixelRef(pixelRef, pixelRefOffset)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001184 return true;
1185 }
1186 }
1187
1188 if (this->getTexture()) {
1189 return false;
1190 } else {
1191 return this->copyTo(dst, dstConfig, NULL);
1192 }
1193}
1194
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195///////////////////////////////////////////////////////////////////////////////
1196///////////////////////////////////////////////////////////////////////////////
1197
1198static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
1199 const SkBitmap& src) {
1200 x <<= 1;
1201 y <<= 1;
1202 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001203 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 SkPMColor c, ag, rb;
1205
1206 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
1207 if (x < src.width() - 1) {
1208 p += 1;
1209 }
1210 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1211
reed@android.com829c83c2009-06-08 12:05:31 +00001212 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001214 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 }
1216 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1217 if (x < src.width() - 1) {
1218 p += 1;
1219 }
1220 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1221
1222 *dst->getAddr32(x >> 1, y >> 1) =
1223 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1224}
1225
1226static inline uint32_t expand16(U16CPU c) {
1227 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1228}
1229
1230// returns dirt in the top 16bits, but we don't care, since we only
1231// store the low 16bits.
1232static inline U16CPU pack16(uint32_t c) {
1233 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1234}
1235
1236static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1237 const SkBitmap& src) {
1238 x <<= 1;
1239 y <<= 1;
1240 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001241 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001243
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001245 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 p += 1;
1247 }
1248 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001249
reed@android.com829c83c2009-06-08 12:05:31 +00001250 p = baseP;
1251 if (y < src.height() - 1) {
1252 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 }
1254 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001255 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 p += 1;
1257 }
1258 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001259
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1261}
1262
1263static uint32_t expand4444(U16CPU c) {
1264 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1265}
1266
1267static U16CPU collaps4444(uint32_t c) {
1268 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1269}
1270
1271static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1272 const SkBitmap& src) {
1273 x <<= 1;
1274 y <<= 1;
1275 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001276 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001278
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 c = expand4444(*p);
1280 if (x < src.width() - 1) {
1281 p += 1;
1282 }
1283 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001284
reed@android.com829c83c2009-06-08 12:05:31 +00001285 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001287 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 }
1289 c += expand4444(*p);
1290 if (x < src.width() - 1) {
1291 p += 1;
1292 }
1293 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001294
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1296}
1297
1298void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 if (forceRebuild)
1300 this->freeMipMap();
1301 else if (fMipMap)
1302 return; // we're already built
1303
1304 SkASSERT(NULL == fMipMap);
1305
1306 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1307
1308 const SkBitmap::Config config = this->getConfig();
1309
1310 switch (config) {
1311 case kARGB_8888_Config:
1312 proc = downsampleby2_proc32;
1313 break;
1314 case kRGB_565_Config:
1315 proc = downsampleby2_proc16;
1316 break;
1317 case kARGB_4444_Config:
1318 proc = downsampleby2_proc4444;
1319 break;
1320 case kIndex8_Config:
1321 case kA8_Config:
1322 default:
1323 return; // don't build mipmaps for these configs
1324 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001325
reed@android.com149e2f62009-05-22 14:39:03 +00001326 SkAutoLockPixels alp(*this);
1327 if (!this->readyToDraw()) {
1328 return;
1329 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330
1331 // whip through our loop to compute the exact size needed
1332 size_t size = 0;
1333 int maxLevels = 0;
1334 {
reed@android.com149e2f62009-05-22 14:39:03 +00001335 int width = this->width();
1336 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 for (;;) {
1338 width >>= 1;
1339 height >>= 1;
1340 if (0 == width || 0 == height) {
1341 break;
1342 }
1343 size += ComputeRowBytes(config, width) * height;
1344 maxLevels += 1;
1345 }
1346 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001347
reed@android.com149e2f62009-05-22 14:39:03 +00001348 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 if (0 == maxLevels) {
1350 return;
1351 }
1352
reed@android.com149e2f62009-05-22 14:39:03 +00001353 SkBitmap srcBM(*this);
1354 srcBM.lockPixels();
1355 if (!srcBM.readyToDraw()) {
1356 return;
1357 }
1358
1359 MipMap* mm = MipMap::Alloc(maxLevels, size);
1360 if (NULL == mm) {
1361 return;
1362 }
1363
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 MipLevel* level = mm->levels();
1365 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001366 int width = this->width();
1367 int height = this->height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001368 uint32_t rowBytes;
reed@android.com149e2f62009-05-22 14:39:03 +00001369 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370
1371 for (int i = 0; i < maxLevels; i++) {
1372 width >>= 1;
1373 height >>= 1;
scroggo@google.come5f48242013-02-25 21:47:41 +00001374 rowBytes = SkToU32(ComputeRowBytes(config, width));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375
1376 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001377 level[i].fWidth = width;
1378 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001379 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380
1381 dstBM.setConfig(config, width, height, rowBytes);
1382 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001383
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001384 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001385 for (int y = 0; y < height; y++) {
1386 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 proc(&dstBM, x, y, srcBM);
1388 }
1389 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001390 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391
1392 srcBM = dstBM;
1393 addr += height * rowBytes;
1394 }
1395 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1396 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397}
1398
1399bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401}
1402
1403int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001404 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001406 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001407
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 int level = ComputeMipLevel(sx, sy) >> 16;
1409 SkASSERT(level >= 0);
1410 if (level <= 0) {
1411 return 0;
1412 }
1413
1414 if (level >= fMipMap->fLevelCount) {
1415 level = fMipMap->fLevelCount - 1;
1416 }
1417 if (dst) {
1418 const MipLevel& mip = fMipMap->levels()[level - 1];
1419 dst->setConfig((SkBitmap::Config)this->config(),
1420 mip.fWidth, mip.fHeight, mip.fRowBytes);
1421 dst->setPixels(mip.fPixels);
1422 }
1423 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424}
1425
1426SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 sx = SkAbs32(sx);
1428 sy = SkAbs32(sy);
1429 if (sx < sy) {
1430 sx = sy;
1431 }
1432 if (sx < SK_Fixed1) {
1433 return 0;
1434 }
1435 int clz = SkCLZ(sx);
1436 SkASSERT(clz >= 1 && clz <= 15);
1437 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438}
1439
1440///////////////////////////////////////////////////////////////////////////////
1441
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001442static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443 int alphaRowBytes) {
1444 SkASSERT(alpha != NULL);
1445 SkASSERT(alphaRowBytes >= src.width());
1446
1447 SkBitmap::Config config = src.getConfig();
1448 int w = src.width();
1449 int h = src.height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001450 size_t rb = src.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451
reed@android.com1cdcb512009-08-24 19:11:00 +00001452 SkAutoLockPixels alp(src);
1453 if (!src.readyToDraw()) {
1454 // zero out the alpha buffer and return
1455 while (--h >= 0) {
1456 memset(alpha, 0, w);
1457 alpha += alphaRowBytes;
1458 }
1459 return false;
1460 }
reed@google.com82065d62011-02-07 15:30:46 +00001461
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1463 const uint8_t* s = src.getAddr8(0, 0);
1464 while (--h >= 0) {
1465 memcpy(alpha, s, w);
1466 s += rb;
1467 alpha += alphaRowBytes;
1468 }
1469 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1470 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1471 while (--h >= 0) {
1472 for (int x = 0; x < w; x++) {
1473 alpha[x] = SkGetPackedA32(s[x]);
1474 }
1475 s = (const SkPMColor*)((const char*)s + rb);
1476 alpha += alphaRowBytes;
1477 }
1478 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1479 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1480 while (--h >= 0) {
1481 for (int x = 0; x < w; x++) {
1482 alpha[x] = SkPacked4444ToA32(s[x]);
1483 }
1484 s = (const SkPMColor16*)((const char*)s + rb);
1485 alpha += alphaRowBytes;
1486 }
1487 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1488 SkColorTable* ct = src.getColorTable();
1489 if (ct) {
1490 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1491 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1492 while (--h >= 0) {
1493 for (int x = 0; x < w; x++) {
1494 alpha[x] = SkGetPackedA32(table[s[x]]);
1495 }
1496 s += rb;
1497 alpha += alphaRowBytes;
1498 }
reed@google.com0a6151d2013-10-10 14:44:56 +00001499 ct->unlockColors();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 }
1501 } else { // src is opaque, so just fill alpha[] with 0xFF
1502 memset(alpha, 0xFF, h * alphaRowBytes);
1503 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001504 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505}
1506
1507#include "SkPaint.h"
1508#include "SkMaskFilter.h"
1509#include "SkMatrix.h"
1510
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001511bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001512 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 SkDEBUGCODE(this->validate();)
1514
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001515 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516 SkMatrix identity;
1517 SkMask srcM, dstM;
1518
1519 srcM.fBounds.set(0, 0, this->width(), this->height());
1520 srcM.fRowBytes = SkAlign4(this->width());
1521 srcM.fFormat = SkMask::kA8_Format;
1522
1523 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1524
1525 // compute our (larger?) dst bounds if we have a filter
1526 if (NULL != filter) {
1527 identity.reset();
1528 srcM.fImage = NULL;
1529 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1530 goto NO_FILTER_CASE;
1531 }
1532 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1533 } else {
1534 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001535 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001537 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1538 // Allocation of pixels for alpha bitmap failed.
1539 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1540 tmpBitmap.width(), tmpBitmap.height());
1541 return false;
1542 }
1543 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544 if (offset) {
1545 offset->set(0, 0);
1546 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001547 tmpBitmap.swap(*dst);
1548 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001550 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1551 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552
1553 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1554 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1555 goto NO_FILTER_CASE;
1556 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001557 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001559 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001560 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001561 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1562 // Allocation of pixels for alpha bitmap failed.
1563 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1564 tmpBitmap.width(), tmpBitmap.height());
1565 return false;
1566 }
1567 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568 if (offset) {
1569 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1570 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001571 SkDEBUGCODE(tmpBitmap.validate();)
1572
1573 tmpBitmap.swap(*dst);
1574 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
1577///////////////////////////////////////////////////////////////////////////////
1578
1579enum {
1580 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001581 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582};
1583
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001585 buffer.writeInt(fWidth);
1586 buffer.writeInt(fHeight);
1587 buffer.writeInt(fRowBytes);
1588 buffer.writeInt(fConfig);
reed@google.com383a6972013-10-21 14:00:07 +00001589 buffer.writeInt(fAlphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001590
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001592 if (fPixelRef->getFactory()) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001593 buffer.writeInt(SERIALIZE_PIXELTYPE_REF_DATA);
scroggo@google.come5f48242013-02-25 21:47:41 +00001594 buffer.writeUInt(SkToU32(fPixelRefOffset));
djsollen@google.com5370cd92012-03-28 20:47:01 +00001595 buffer.writeFlattenable(fPixelRef);
1596 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597 }
1598 // if we get here, we can't record the pixels
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001599 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600 } else {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001601 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602 }
1603}
1604
1605void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1606 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001607
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 int width = buffer.readInt();
1609 int height = buffer.readInt();
1610 int rowBytes = buffer.readInt();
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001611 Config config = (Config)buffer.readInt();
1612 SkAlphaType alphaType = (SkAlphaType)buffer.readInt();
1613 buffer.validate((width >= 0) && (height >= 0) && (rowBytes >= 0) &&
1614 SkIsValidConfig(config) && validate_alphaType(config, alphaType));
weita@google.comf9ab99a2009-05-03 18:23:30 +00001615
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001616 this->setConfig(config, width, height, rowBytes, alphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001617
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001618 int reftype = buffer.readInt();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 switch (reftype) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 case SERIALIZE_PIXELTYPE_REF_DATA: {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001621 size_t offset = buffer.readUInt();
reed@google.com35348222013-10-16 13:05:06 +00001622 SkPixelRef* pr = buffer.readPixelRef();
reed@google.com82065d62011-02-07 15:30:46 +00001623 SkSafeUnref(this->setPixelRef(pr, offset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 break;
1625 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 case SERIALIZE_PIXELTYPE_NONE:
1627 break;
1628 default:
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001629 buffer.validate(false);
tomhudson@google.com0c00f212011-12-28 14:59:50 +00001630 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631 sk_throw();
1632 }
1633}
1634
1635///////////////////////////////////////////////////////////////////////////////
1636
1637SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1638 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001639 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640}
1641
1642SkBitmap::RLEPixels::~RLEPixels() {
1643 sk_free(fYPtrs);
1644}
1645
1646///////////////////////////////////////////////////////////////////////////////
1647
1648#ifdef SK_DEBUG
1649void SkBitmap::validate() const {
1650 SkASSERT(fConfig < kConfigCount);
1651 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001652 uint8_t allFlags = kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag;
1653#ifdef SK_BUILD_FOR_ANDROID
1654 allFlags |= kHasHardwareMipMap_Flag;
1655#endif
1656 SkASSERT(fFlags <= allFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657 SkASSERT(fPixelLockCount >= 0);
1658 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1659 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1660
1661#if 0 // these asserts are not thread-correct, so disable for now
1662 if (fPixelRef) {
1663 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001664 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665 } else {
1666 SkASSERT(NULL == fPixels);
1667 SkASSERT(NULL == fColorTable);
1668 }
1669 }
1670#endif
1671}
1672#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001673
1674#ifdef SK_DEVELOPER
1675void SkBitmap::toString(SkString* str) const {
1676
1677 static const char* gConfigNames[kConfigCount] = {
edisonn@google.comafe397b2013-06-26 14:52:34 +00001678 "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001679 };
1680
1681 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
1682 gConfigNames[this->config()]);
1683
1684 str->append(" (");
1685 if (this->isOpaque()) {
1686 str->append("opaque");
1687 } else {
1688 str->append("transparent");
1689 }
1690 if (this->isImmutable()) {
1691 str->append(", immutable");
1692 } else {
1693 str->append(", not-immutable");
1694 }
1695 str->append(")");
1696
1697 SkPixelRef* pr = this->pixelRef();
1698 if (NULL == pr) {
1699 // show null or the explicit pixel address (rare)
1700 str->appendf(" pixels:%p", this->getPixels());
1701 } else {
1702 const char* uri = pr->getURI();
1703 if (NULL != uri) {
1704 str->appendf(" uri:\"%s\"", uri);
1705 } else {
1706 str->appendf(" pixelref:%p", pr);
1707 }
1708 }
1709
1710 str->append(")");
1711}
1712#endif