blob: 429d09216a1328156d47d98a175383d79bf7452d [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@google.com72e78082013-11-25 20:54:56 +0000164 case kA1_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 bpp = 0; // not applicable
166 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 case kA8_Config:
168 case kIndex8_Config:
169 bpp = 1;
170 break;
171 case kRGB_565_Config:
172 case kARGB_4444_Config:
173 bpp = 2;
174 break;
175 case kARGB_8888_Config:
176 bpp = 4;
177 break;
178 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000179 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 bpp = 0; // error
181 break;
182 }
183 return bpp;
184}
185
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000186size_t SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000187 if (width < 0) {
188 return 0;
189 }
190
191 Sk64 rowBytes;
192 rowBytes.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
194 switch (c) {
195 case kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 break;
reed@google.com72e78082013-11-25 20:54:56 +0000197 case kA1_Config:
198 rowBytes.set(width);
199 rowBytes.add(7);
200 rowBytes.shiftRight(3);
201 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 case kA8_Config:
203 case kIndex8_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000204 rowBytes.set(width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 break;
206 case kRGB_565_Config:
207 case kARGB_4444_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000208 rowBytes.set(width);
209 rowBytes.shiftLeft(1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 break;
211 case kARGB_8888_Config:
reed@android.com149e2f62009-05-22 14:39:03 +0000212 rowBytes.set(width);
213 rowBytes.shiftLeft(2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 break;
215 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000216 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 break;
218 }
reed@android.com149e2f62009-05-22 14:39:03 +0000219 return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220}
221
222Sk64 SkBitmap::ComputeSize64(Config c, int width, int height) {
223 Sk64 size;
scroggo@google.come5f48242013-02-25 21:47:41 +0000224 size.setMul(SkToS32(SkBitmap::ComputeRowBytes(c, width)), height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 return size;
226}
227
228size_t SkBitmap::ComputeSize(Config c, int width, int height) {
229 Sk64 size = SkBitmap::ComputeSize64(c, width, height);
reed@android.com149e2f62009-05-22 14:39:03 +0000230 return isPos32Bits(size) ? size.get32() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231}
232
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000233Sk64 SkBitmap::ComputeSafeSize64(Config config,
234 uint32_t width,
235 uint32_t height,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000236 size_t rowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000237 Sk64 safeSize;
238 safeSize.setZero();
239 if (height > 0) {
scroggo@google.come5f48242013-02-25 21:47:41 +0000240 // TODO: Handle the case where the return value from
241 // ComputeRowBytes is more than 31 bits.
242 safeSize.set(SkToS32(ComputeRowBytes(config, width)));
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000243 Sk64 sizeAllButLastRow;
scroggo@google.come5f48242013-02-25 21:47:41 +0000244 sizeAllButLastRow.setMul(height - 1, SkToS32(rowBytes));
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000245 safeSize.add(sizeAllButLastRow);
246 }
247 SkASSERT(!safeSize.isNeg());
248 return safeSize;
249}
250
251size_t SkBitmap::ComputeSafeSize(Config config,
252 uint32_t width,
253 uint32_t height,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000254 size_t rowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000255 Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes);
256 return (safeSize.is32() ? safeSize.get32() : 0);
257}
258
reed@google.com86b2e432012-03-15 21:17:03 +0000259void SkBitmap::getBounds(SkRect* bounds) const {
260 SkASSERT(bounds);
261 bounds->set(0, 0,
262 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
263}
264
reed@google.com80e14592012-03-16 14:58:07 +0000265void SkBitmap::getBounds(SkIRect* bounds) const {
266 SkASSERT(bounds);
267 bounds->set(0, 0, fWidth, fHeight);
268}
269
reed@google.com86b2e432012-03-15 21:17:03 +0000270///////////////////////////////////////////////////////////////////////////////
271
reed@google.com383a6972013-10-21 14:00:07 +0000272static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType,
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000273 SkAlphaType* canonical = NULL) {
reed@google.com383a6972013-10-21 14:00:07 +0000274 switch (config) {
275 case SkBitmap::kNo_Config:
276 alphaType = kIgnore_SkAlphaType;
277 break;
reed@google.com72e78082013-11-25 20:54:56 +0000278 case SkBitmap::kA1_Config:
reed@google.com383a6972013-10-21 14:00:07 +0000279 case SkBitmap::kA8_Config:
280 if (kUnpremul_SkAlphaType == alphaType) {
281 alphaType = kPremul_SkAlphaType;
282 }
283 // fall-through
284 case SkBitmap::kIndex8_Config:
285 case SkBitmap::kARGB_4444_Config:
286 case SkBitmap::kARGB_8888_Config:
287 if (kIgnore_SkAlphaType == alphaType) {
288 return false;
289 }
290 break;
291 case SkBitmap::kRGB_565_Config:
292 alphaType = kOpaque_SkAlphaType;
293 break;
reed@android.com149e2f62009-05-22 14:39:03 +0000294 }
reed@google.com383a6972013-10-21 14:00:07 +0000295 if (canonical) {
296 *canonical = alphaType;
297 }
298 return true;
299}
reed@android.com149e2f62009-05-22 14:39:03 +0000300
reed@google.com383a6972013-10-21 14:00:07 +0000301bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
302 SkAlphaType alphaType) {
303 if ((width | height) < 0) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000304 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 if (rowBytes == 0) {
reed@google.com383a6972013-10-21 14:00:07 +0000307 rowBytes = SkBitmap::ComputeRowBytes(config, width);
308 if (0 == rowBytes && kNo_Config != config) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000309 goto BAD_CONFIG;
reed@android.com149e2f62009-05-22 14:39:03 +0000310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000312
reed@google.com383a6972013-10-21 14:00:07 +0000313 if (!validate_alphaType(config, alphaType, &alphaType)) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000314 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000315 }
316
317 this->freePixels();
318
319 fConfig = SkToU8(config);
320 fAlphaType = SkToU8(alphaType);
reed@android.comf459a492009-03-27 12:33:50 +0000321 fWidth = width;
322 fHeight = height;
scroggo@google.come5f48242013-02-25 21:47:41 +0000323 fRowBytes = SkToU32(rowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324
reed@google.com383a6972013-10-21 14:00:07 +0000325 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(config);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326
327 SkDEBUGCODE(this->validate();)
reed@google.com383a6972013-10-21 14:00:07 +0000328 return true;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000329
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000330 // if we got here, we had an error, so we reset the bitmap to empty
reed@google.com9cd697c2013-10-21 14:12:13 +0000331BAD_CONFIG:
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000332 this->reset();
reed@google.com383a6972013-10-21 14:00:07 +0000333 return false;
334}
335
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +0000336bool SkBitmap::setConfig(const SkImageInfo& info, size_t rowBytes) {
337 return this->setConfig(SkImageInfoToBitmapConfig(info), info.fWidth,
338 info.fHeight, rowBytes, info.fAlphaType);
339}
340
reed@google.com383a6972013-10-21 14:00:07 +0000341bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
342 if (!validate_alphaType(this->config(), alphaType, &alphaType)) {
343 return false;
344 }
345 fAlphaType = SkToU8(alphaType);
346 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347}
348
349void SkBitmap::updatePixelsFromRef() const {
350 if (NULL != fPixelRef) {
351 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000352 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000353
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 void* p = fPixelRef->pixels();
355 if (NULL != p) {
356 p = (char*)p + fPixelRefOffset;
357 }
358 fPixels = p;
359 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
360 } else {
361 SkASSERT(0 == fPixelLockCount);
362 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000363 if (fColorTable) {
364 fColorTable->unref();
365 fColorTable = NULL;
366 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 }
368 }
369}
370
371SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
372 // do this first, we that we never have a non-zero offset with a null ref
373 if (NULL == pr) {
374 offset = 0;
375 }
376
377 if (fPixelRef != pr || fPixelRefOffset != offset) {
378 if (fPixelRef != pr) {
379 this->freePixels();
380 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000381
reed@google.com82065d62011-02-07 15:30:46 +0000382 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 fPixelRef = pr;
384 }
385 fPixelRefOffset = offset;
386 this->updatePixelsFromRef();
387 }
388
389 SkDEBUGCODE(this->validate();)
390 return pr;
391}
392
393void SkBitmap::lockPixels() const {
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000394 if (NULL != fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 fPixelRef->lockPixels();
396 this->updatePixelsFromRef();
397 }
398 SkDEBUGCODE(this->validate();)
399}
400
401void SkBitmap::unlockPixels() const {
402 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
403
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000404 if (NULL != fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 fPixelRef->unlockPixels();
406 this->updatePixelsFromRef();
407 }
408 SkDEBUGCODE(this->validate();)
409}
410
reed@google.com9c49bc32011-07-07 13:42:37 +0000411bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000412 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000413}
414
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000416 if (NULL == p) {
417 this->setPixelRef(NULL, 0);
418 return;
419 }
420
djsollen@google.comc84b8332012-07-27 13:41:44 +0000421 Sk64 size = this->getSize64();
422 SkASSERT(!size.isNeg() && size.is32());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423
djsollen@google.comc84b8332012-07-27 13:41:44 +0000424 this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref();
425 // since we're already allocated, we lockPixels right away
426 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 SkDEBUGCODE(this->validate();)
428}
429
430bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
431 HeapAllocator stdalloc;
432
433 if (NULL == allocator) {
434 allocator = &stdalloc;
435 }
436 return allocator->allocPixelRef(this, ctable);
437}
438
439void SkBitmap::freePixels() {
440 // if we're gonna free the pixels, we certainly need to free the mipmap
441 this->freeMipMap();
442
reed@android.com149e2f62009-05-22 14:39:03 +0000443 if (fColorTable) {
444 fColorTable->unref();
445 fColorTable = NULL;
446 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447
448 if (NULL != fPixelRef) {
449 if (fPixelLockCount > 0) {
450 fPixelRef->unlockPixels();
451 }
452 fPixelRef->unref();
453 fPixelRef = NULL;
454 fPixelRefOffset = 0;
455 }
456 fPixelLockCount = 0;
457 fPixels = NULL;
458}
459
460void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000461 if (fMipMap) {
462 fMipMap->unref();
463 fMipMap = NULL;
464 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465}
466
467uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000468 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469}
470
471void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000472 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 if (fPixelRef) {
474 fPixelRef->notifyPixelsChanged();
475 }
476}
477
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000478GrTexture* SkBitmap::getTexture() const {
reed@android.comce4e53a2010-09-09 16:01:26 +0000479 return fPixelRef ? fPixelRef->getTexture() : NULL;
480}
481
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482///////////////////////////////////////////////////////////////////////////////
483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484/** We explicitly use the same allocator for our pixels that SkMask does,
485 so that we can freely assign memory allocated by one class to the other.
486 */
487bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
488 SkColorTable* ctable) {
489 Sk64 size = dst->getSize64();
490 if (size.isNeg() || !size.is32()) {
491 return false;
492 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000493
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 void* addr = sk_malloc_flags(size.get32(), 0); // returns NULL on failure
495 if (NULL == addr) {
496 return false;
497 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 dst->setPixelRef(new SkMallocPixelRef(addr, size.get32(), ctable))->unref();
500 // since we're already allocated, we lockPixels right away
501 dst->lockPixels();
502 return true;
503}
504
505///////////////////////////////////////////////////////////////////////////////
506
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000507size_t SkBitmap::getSafeSize() const {
508 // This is intended to be a size_t version of ComputeSafeSize64(), just
509 // faster. The computation is meant to be identical.
510 return (fHeight ? ((fHeight - 1) * fRowBytes) +
reed@google.com44699382013-10-31 17:28:30 +0000511 ComputeRowBytes(this->config(), fWidth): 0);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000512}
513
514Sk64 SkBitmap::getSafeSize64() const {
reed@google.com44699382013-10-31 17:28:30 +0000515 return ComputeSafeSize64(this->config(), fWidth, fHeight, fRowBytes);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000516}
517
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000518bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000519 size_t dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000520
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000521 if (0 == dstRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000522 dstRowBytes = fRowBytes;
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000523 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000524
reed@google.com44699382013-10-31 17:28:30 +0000525 if (dstRowBytes < ComputeRowBytes(this->config(), fWidth) ||
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000526 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
527 return false;
528
bsalomon@google.comc6980972011-11-02 19:57:21 +0000529 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
reed@google.com44699382013-10-31 17:28:30 +0000530 size_t safeSize = this->getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000531 if (safeSize > dstSize || safeSize == 0)
532 return false;
533 else {
534 SkAutoLockPixels lock(*this);
535 // This implementation will write bytes beyond the end of each row,
536 // excluding the last row, if the bitmap's stride is greater than
537 // strictly required by the current config.
538 memcpy(dst, getPixels(), safeSize);
539
540 return true;
541 }
542 } else {
543 // If destination has different stride than us, then copy line by line.
reed@google.com44699382013-10-31 17:28:30 +0000544 if (ComputeSafeSize(this->config(), fWidth, fHeight, dstRowBytes) >
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000545 dstSize)
546 return false;
547 else {
548 // Just copy what we need on each line.
reed@google.com44699382013-10-31 17:28:30 +0000549 size_t rowBytes = ComputeRowBytes(this->config(), fWidth);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000550 SkAutoLockPixels lock(*this);
551 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
552 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
553 for (uint32_t row = 0; row < fHeight;
554 row++, srcP += fRowBytes, dstP += dstRowBytes) {
555 memcpy(dstP, srcP, rowBytes);
556 }
557
558 return true;
559 }
560 }
561}
562
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000563///////////////////////////////////////////////////////////////////////////////
564
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000565bool SkBitmap::isImmutable() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000566 return fPixelRef ? fPixelRef->isImmutable() :
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000567 fFlags & kImageIsImmutable_Flag;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000568}
569
570void SkBitmap::setImmutable() {
571 if (fPixelRef) {
572 fPixelRef->setImmutable();
573 } else {
574 fFlags |= kImageIsImmutable_Flag;
575 }
576}
577
junov@google.com4ee7ae52011-06-30 17:30:49 +0000578bool SkBitmap::isVolatile() const {
579 return (fFlags & kImageIsVolatile_Flag) != 0;
580}
581
582void SkBitmap::setIsVolatile(bool isVolatile) {
583 if (isVolatile) {
584 fFlags |= kImageIsVolatile_Flag;
585 } else {
586 fFlags &= ~kImageIsVolatile_Flag;
587 }
588}
589
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590void* SkBitmap::getAddr(int x, int y) const {
591 SkASSERT((unsigned)x < (unsigned)this->width());
592 SkASSERT((unsigned)y < (unsigned)this->height());
593
594 char* base = (char*)this->getPixels();
595 if (base) {
596 base += y * this->rowBytes();
597 switch (this->config()) {
598 case SkBitmap::kARGB_8888_Config:
599 base += x << 2;
600 break;
601 case SkBitmap::kARGB_4444_Config:
602 case SkBitmap::kRGB_565_Config:
603 base += x << 1;
604 break;
605 case SkBitmap::kA8_Config:
606 case SkBitmap::kIndex8_Config:
607 base += x;
608 break;
reed@google.com72e78082013-11-25 20:54:56 +0000609 case SkBitmap::kA1_Config:
610 base += x >> 3;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000613 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 base = NULL;
615 break;
616 }
617 }
618 return base;
619}
620
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000621SkColor SkBitmap::getColor(int x, int y) const {
622 SkASSERT((unsigned)x < (unsigned)this->width());
623 SkASSERT((unsigned)y < (unsigned)this->height());
624
625 switch (this->config()) {
reed@google.com72e78082013-11-25 20:54:56 +0000626 case SkBitmap::kA1_Config: {
627 uint8_t* addr = this->getAddr1(x, y);
628 uint8_t mask = 1 << (7 - (x % 8));
629 if (addr[0] & mask) {
630 return SK_ColorBLACK;
631 } else {
632 return 0;
633 }
634 }
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000635 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000636 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000637 return SkColorSetA(0, addr[0]);
638 }
639 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000640 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000641 return SkUnPreMultiply::PMColorToColor(c);
642 }
643 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000644 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000645 return SkPixel16ToColor(addr[0]);
646 }
647 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000648 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000649 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
650 return SkUnPreMultiply::PMColorToColor(c);
651 }
652 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000653 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000654 return SkUnPreMultiply::PMColorToColor(addr[0]);
655 }
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000656 case kNo_Config:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000657 SkASSERT(false);
658 return 0;
659 }
660 SkASSERT(false); // Not reached.
661 return 0;
662}
663
reed@google.com2a7579d2012-11-07 18:30:18 +0000664bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
665 SkAutoLockPixels alp(bm);
666 if (!bm.getPixels()) {
667 return false;
668 }
669
670 const int height = bm.height();
671 const int width = bm.width();
672
673 switch (bm.config()) {
reed@google.com72e78082013-11-25 20:54:56 +0000674 case SkBitmap::kA1_Config: {
675 // TODO
676 } break;
reed@google.com2a7579d2012-11-07 18:30:18 +0000677 case SkBitmap::kA8_Config: {
678 unsigned a = 0xFF;
679 for (int y = 0; y < height; ++y) {
680 const uint8_t* row = bm.getAddr8(0, y);
681 for (int x = 0; x < width; ++x) {
682 a &= row[x];
683 }
684 if (0xFF != a) {
685 return false;
686 }
687 }
688 return true;
689 } break;
reed@google.com2a7579d2012-11-07 18:30:18 +0000690 case SkBitmap::kIndex8_Config: {
691 SkAutoLockColors alc(bm);
692 const SkPMColor* table = alc.colors();
693 if (!table) {
694 return false;
695 }
reed@google.com140d7282013-01-07 20:25:04 +0000696 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000697 for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
698 c &= table[i];
699 }
700 return 0xFF == SkGetPackedA32(c);
701 } break;
702 case SkBitmap::kRGB_565_Config:
703 return true;
704 break;
705 case SkBitmap::kARGB_4444_Config: {
706 unsigned c = 0xFFFF;
707 for (int y = 0; y < height; ++y) {
708 const SkPMColor16* row = bm.getAddr16(0, y);
709 for (int x = 0; x < width; ++x) {
710 c &= row[x];
711 }
712 if (0xF != SkGetPackedA4444(c)) {
713 return false;
714 }
715 }
716 return true;
717 } break;
718 case SkBitmap::kARGB_8888_Config: {
reed@google.com140d7282013-01-07 20:25:04 +0000719 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000720 for (int y = 0; y < height; ++y) {
721 const SkPMColor* row = bm.getAddr32(0, y);
722 for (int x = 0; x < width; ++x) {
723 c &= row[x];
724 }
725 if (0xFF != SkGetPackedA32(c)) {
726 return false;
727 }
728 }
729 return true;
730 }
731 default:
732 break;
733 }
734 return false;
735}
736
737
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738///////////////////////////////////////////////////////////////////////////////
739///////////////////////////////////////////////////////////////////////////////
740
reed@google.com45f746f2013-06-21 19:51:31 +0000741static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
742 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
743 (SkR32To4444(r) << SK_R4444_SHIFT) |
744 (SkG32To4444(g) << SK_G4444_SHIFT) |
745 (SkB32To4444(b) << SK_B4444_SHIFT);
746 return SkToU16(pixel);
747}
748
reed@google.com60d32352013-06-28 19:40:50 +0000749void SkBitmap::internalErase(const SkIRect& area,
750 U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
751#ifdef SK_DEBUG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 SkDEBUGCODE(this->validate();)
reed@google.com60d32352013-06-28 19:40:50 +0000753 SkASSERT(!area.isEmpty());
754 {
reed@google.com92833f92013-06-28 19:47:43 +0000755 SkIRect total = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000756 SkASSERT(total.contains(area));
757 }
758#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759
reed@google.com60d32352013-06-28 19:40:50 +0000760 if (kNo_Config == fConfig || kIndex8_Config == fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 return;
762 }
763
764 SkAutoLockPixels alp(*this);
765 // perform this check after the lock call
766 if (!this->readyToDraw()) {
767 return;
768 }
769
reed@google.com60d32352013-06-28 19:40:50 +0000770 int height = area.height();
771 const int width = area.width();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 const int rowBytes = fRowBytes;
773
774 // make rgb premultiplied
775 if (255 != a) {
776 r = SkAlphaMul(r, a);
777 g = SkAlphaMul(g, a);
778 b = SkAlphaMul(b, a);
779 }
780
781 switch (fConfig) {
reed@google.com72e78082013-11-25 20:54:56 +0000782 case kA1_Config: {
783 uint8_t* p = this->getAddr1(area.fLeft, area.fTop);
784 const int left = area.fLeft >> 3;
785 const int right = area.fRight >> 3;
786
787 int middle = right - left - 1;
788
789 uint8_t leftMask = 0xFF >> (area.fLeft & 7);
790 uint8_t rightMask = ~(0xFF >> (area.fRight & 7));
791 if (left == right) {
792 leftMask &= rightMask;
793 rightMask = 0;
794 }
795
796 a = (a >> 7) ? 0xFF : 0;
797 while (--height >= 0) {
798 uint8_t* startP = p;
799
800 *p = (*p & ~leftMask) | (a & leftMask);
801 p++;
802 if (middle > 0) {
803 memset(p, a, middle);
804 p += middle;
805 }
806 if (rightMask) {
807 *p = (*p & ~rightMask) | (a & rightMask);
808 }
809
810 p = startP + rowBytes;
811 }
812 break;
813 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 case kA8_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000815 uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 while (--height >= 0) {
817 memset(p, a, width);
818 p += rowBytes;
819 }
820 break;
821 }
reed@google.com45f746f2013-06-21 19:51:31 +0000822 case kARGB_4444_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 case kRGB_565_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000824 uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
reed@google.com45f746f2013-06-21 19:51:31 +0000825 uint16_t v;
skia.committer@gmail.com020b25b2013-06-22 07:00:58 +0000826
reed@google.com45f746f2013-06-21 19:51:31 +0000827 if (kARGB_4444_Config == fConfig) {
828 v = pack_8888_to_4444(a, r, g, b);
829 } else {
830 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
831 g >> (8 - SK_G16_BITS),
832 b >> (8 - SK_B16_BITS));
833 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 while (--height >= 0) {
835 sk_memset16(p, v, width);
836 p = (uint16_t*)((char*)p + rowBytes);
837 }
838 break;
839 }
840 case kARGB_8888_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000841 uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 uint32_t v = SkPackARGB32(a, r, g, b);
843
844 while (--height >= 0) {
845 sk_memset32(p, v, width);
846 p = (uint32_t*)((char*)p + rowBytes);
847 }
848 break;
849 }
850 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000851
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 this->notifyPixelsChanged();
853}
854
reed@google.com60d32352013-06-28 19:40:50 +0000855void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
reed@google.com92833f92013-06-28 19:47:43 +0000856 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000857 if (!area.isEmpty()) {
858 this->internalErase(area, a, r, g, b);
859 }
860}
861
862void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
reed@google.com92833f92013-06-28 19:47:43 +0000863 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000864 if (area.intersect(rect)) {
865 this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
866 SkColorGetG(c), SkColorGetB(c));
867 }
868}
869
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870//////////////////////////////////////////////////////////////////////////////////////
871//////////////////////////////////////////////////////////////////////////////////////
872
873#define SUB_OFFSET_FAILURE ((size_t)-1)
874
scroggo@google.coma2a31922012-12-07 19:14:45 +0000875/**
876 * Based on the Config and rowBytes() of bm, return the offset into an SkPixelRef of the pixel at
877 * (x, y).
878 * Note that the SkPixelRef does not need to be set yet. deepCopyTo takes advantage of this fact.
879 * Also note that (x, y) may be outside the range of (0 - width(), 0 - height()), so long as it is
880 * within the bounds of the SkPixelRef being used.
881 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000882static size_t get_sub_offset(const SkBitmap& bm, int x, int y) {
reed@google.com44699382013-10-31 17:28:30 +0000883 switch (bm.config()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 case SkBitmap::kA8_Config:
885 case SkBitmap:: kIndex8_Config:
886 // x is fine as is for the calculation
887 break;
888
889 case SkBitmap::kRGB_565_Config:
890 case SkBitmap::kARGB_4444_Config:
891 x <<= 1;
892 break;
893
894 case SkBitmap::kARGB_8888_Config:
895 x <<= 2;
896 break;
897
898 case SkBitmap::kNo_Config:
reed@google.com72e78082013-11-25 20:54:56 +0000899 case SkBitmap::kA1_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 default:
901 return SUB_OFFSET_FAILURE;
902 }
903 return y * bm.rowBytes() + x;
904}
905
scroggo@google.coma2a31922012-12-07 19:14:45 +0000906/**
907 * Using the pixelRefOffset(), rowBytes(), and Config of bm, determine the (x, y) coordinate of the
908 * upper left corner of bm relative to its SkPixelRef.
909 * x and y must be non-NULL.
910 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000911bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com61d6c9e2013-05-21 20:38:40 +0000912 int32_t* x, int32_t* y);
913bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000914 int32_t* x, int32_t* y) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000915 SkASSERT(x != NULL && y != NULL);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000916 if (0 == offset) {
917 *x = *y = 0;
918 return true;
919 }
920 // Use integer division to find the correct y position.
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000921 // The remainder will be the x position, after we reverse get_sub_offset.
commit-bot@chromium.org2c86fbb2013-09-26 19:22:54 +0000922 SkTDivMod(offset, rowBytes, y, x);
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000923 switch (config) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000924 case SkBitmap::kA8_Config:
925 // Fall through.
926 case SkBitmap::kIndex8_Config:
927 // x is unmodified
928 break;
929
930 case SkBitmap::kRGB_565_Config:
931 // Fall through.
932 case SkBitmap::kARGB_4444_Config:
933 *x >>= 1;
934 break;
935
936 case SkBitmap::kARGB_8888_Config:
937 *x >>= 2;
938 break;
939
940 case SkBitmap::kNo_Config:
941 // Fall through.
reed@google.com72e78082013-11-25 20:54:56 +0000942 case SkBitmap::kA1_Config:
943 // Fall through.
scroggo@google.coma2a31922012-12-07 19:14:45 +0000944 default:
945 return false;
946 }
947 return true;
948}
949
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000950static bool get_upper_left_from_offset(const SkBitmap& bm, int32_t* x, int32_t* y) {
951 return get_upper_left_from_offset(bm.config(), bm.pixelRefOffset(), bm.rowBytes(), x, y);
952}
953
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
955 SkDEBUGCODE(this->validate();)
956
djsollen@google.comc84b8332012-07-27 13:41:44 +0000957 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 return false; // no src pixels
959 }
960
961 SkIRect srcRect, r;
962 srcRect.set(0, 0, this->width(), this->height());
963 if (!r.intersect(srcRect, subset)) {
964 return false; // r is empty (i.e. no intersection)
965 }
966
scroggo@google.coma2a31922012-12-07 19:14:45 +0000967 if (fPixelRef->getTexture() != NULL) {
968 // Do a deep copy
969 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->config(), &subset);
970 if (pixelRef != NULL) {
971 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000972 dst.setConfig(this->config(), subset.width(), subset.height(), 0,
973 this->alphaType());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000974 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000975 dst.setPixelRef(pixelRef)->unref();
976 SkDEBUGCODE(dst.validate());
977 result->swap(dst);
978 return true;
979 }
980 }
981
scroggo@google.coma2a31922012-12-07 19:14:45 +0000982 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
983 // exited above.
984 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
985 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
986
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000987 size_t offset = get_sub_offset(*this, r.fLeft, r.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 if (SUB_OFFSET_FAILURE == offset) {
989 return false; // config not supported
990 }
991
992 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000993 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes(),
994 this->alphaType());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000995 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996
997 if (fPixelRef) {
998 // share the pixelref with a custom offset
999 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 }
1001 SkDEBUGCODE(dst.validate();)
1002
1003 // we know we're good, so commit to result
1004 result->swap(dst);
1005 return true;
1006}
1007
1008///////////////////////////////////////////////////////////////////////////////
1009
1010#include "SkCanvas.h"
1011#include "SkPaint.h"
1012
reed@android.comfbaa88d2009-05-06 17:44:34 +00001013bool SkBitmap::canCopyTo(Config dstConfig) const {
reed@google.com44699382013-10-31 17:28:30 +00001014 if (this->config() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 return false;
1016 }
1017
reed@android.comfbaa88d2009-05-06 17:44:34 +00001018 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 switch (dstConfig) {
1020 case kA8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 case kRGB_565_Config:
1022 case kARGB_8888_Config:
1023 break;
reed@google.com72e78082013-11-25 20:54:56 +00001024 case kA1_Config:
weita@google.comf9ab99a2009-05-03 18:23:30 +00001025 case kIndex8_Config:
1026 if (!sameConfigs) {
1027 return false;
1028 }
1029 break;
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001030 case kARGB_4444_Config:
1031 return sameConfigs || kARGB_8888_Config == this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 default:
1033 return false;
1034 }
reed@google.com72e78082013-11-25 20:54:56 +00001035
1036 // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
1037 if (this->config() == kA1_Config && !sameConfigs) {
1038 return false;
1039 }
1040
reed@android.comfbaa88d2009-05-06 17:44:34 +00001041 return true;
1042}
1043
1044bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
1045 if (!this->canCopyTo(dstConfig)) {
1046 return false;
1047 }
1048
reed@google.com50dfa012011-04-01 19:05:36 +00001049 // if we have a texture, first get those pixels
1050 SkBitmap tmpSrc;
1051 const SkBitmap* src = this;
1052
scroggo@google.coma2a31922012-12-07 19:14:45 +00001053 if (fPixelRef) {
1054 SkIRect subset;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001055 if (get_upper_left_from_offset(*this, &subset.fLeft, &subset.fTop)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001056 subset.fRight = subset.fLeft + fWidth;
1057 subset.fBottom = subset.fTop + fHeight;
1058 if (fPixelRef->readPixels(&tmpSrc, &subset)) {
1059 SkASSERT(tmpSrc.width() == this->width());
1060 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +00001061
scroggo@google.coma2a31922012-12-07 19:14:45 +00001062 // did we get lucky and we can just return tmpSrc?
1063 if (tmpSrc.config() == dstConfig && NULL == alloc) {
1064 dst->swap(tmpSrc);
1065 if (dst->pixelRef() && this->config() == dstConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001066 // TODO(scroggo): fix issue 1742
1067 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001068 }
1069 return true;
1070 }
1071
1072 // fall through to the raster case
1073 src = &tmpSrc;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001074 }
reed@google.com50dfa012011-04-01 19:05:36 +00001075 }
reed@android.comfbaa88d2009-05-06 17:44:34 +00001076 }
reed@android.com311c82d2009-05-05 23:13:23 +00001077
reed@google.com50dfa012011-04-01 19:05:36 +00001078 // we lock this now, since we may need its colortable
1079 SkAutoLockPixels srclock(*src);
1080 if (!src->readyToDraw()) {
1081 return false;
1082 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001083
reed@google.com50dfa012011-04-01 19:05:36 +00001084 SkBitmap tmpDst;
reed@google.com383a6972013-10-21 14:00:07 +00001085 tmpDst.setConfig(dstConfig, src->width(), src->height(), 0,
1086 src->alphaType());
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001087
weita@google.comf9ab99a2009-05-03 18:23:30 +00001088 // allocate colortable if srcConfig == kIndex8_Config
1089 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +00001090 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001091 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +00001092 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +00001093 return false;
1094 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001095
reed@google.com50dfa012011-04-01 19:05:36 +00001096 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 // allocator/lock failed
1098 return false;
1099 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001100
reed@android.comfbaa88d2009-05-06 17:44:34 +00001101 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +00001102 */
reed@google.com50dfa012011-04-01 19:05:36 +00001103 if (src->config() == dstConfig) {
1104 if (tmpDst.getSize() == src->getSize()) {
1105 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
scroggo@google.comd5764e82012-08-22 15:00:05 +00001106 SkPixelRef* pixelRef = tmpDst.pixelRef();
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001107 if (NULL != pixelRef && NULL != fPixelRef) {
1108 // TODO(scroggo): fix issue 1742
1109 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.comd5764e82012-08-22 15:00:05 +00001110 }
reed@android.com311c82d2009-05-05 23:13:23 +00001111 } else {
reed@google.com50dfa012011-04-01 19:05:36 +00001112 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
1113 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +00001114 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +00001115 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
1116 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +00001117 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +00001118 srcP += src->rowBytes();
1119 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +00001120 }
1121 }
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001122 } else if (SkBitmap::kARGB_4444_Config == dstConfig
1123 && SkBitmap::kARGB_8888_Config == src->config()) {
1124 SkASSERT(src->height() == tmpDst.height());
1125 SkASSERT(src->width() == tmpDst.width());
1126 for (int y = 0; y < src->height(); ++y) {
1127 SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*) tmpDst.getAddr16(0, y);
1128 SkPMColor* SK_RESTRICT srcRow = (SkPMColor*) src->getAddr32(0, y);
1129 DITHER_4444_SCAN(y);
1130 for (int x = 0; x < src->width(); ++x) {
1131 dstRow[x] = SkDitherARGB32To4444(srcRow[x],
1132 DITHER_VALUE(x));
1133 }
1134 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001135 } else {
robertphillips@google.com0197b322013-10-10 15:48:16 +00001136 // Always clear the dest in case one of the blitters accesses it
1137 // TODO: switch the allocation of tmpDst to call sk_calloc_throw
1138 tmpDst.eraseColor(SK_ColorTRANSPARENT);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001139
reed@google.com50dfa012011-04-01 19:05:36 +00001140 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001141 SkPaint paint;
1142
1143 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +00001144 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 }
1146
reed@google.com50dfa012011-04-01 19:05:36 +00001147 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 return true;
1149}
1150
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001151bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
1152 if (!this->canCopyTo(dstConfig)) {
1153 return false;
1154 }
1155
1156 // If we have a PixelRef, and it supports deep copy, use it.
1157 // Currently supported only by texture-backed bitmaps.
1158 if (fPixelRef) {
1159 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
1160 if (pixelRef) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001161 uint32_t rowBytes;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001162 if (dstConfig == fConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001163 // TODO(scroggo): fix issue 1742
1164 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001165 // Use the same rowBytes as the original.
1166 rowBytes = fRowBytes;
1167 } else {
1168 // With the new config, an appropriate fRowBytes will be computed by setConfig.
1169 rowBytes = 0;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001170 }
scroggo@google.coma2a31922012-12-07 19:14:45 +00001171 dst->setConfig(dstConfig, fWidth, fHeight, rowBytes);
1172
1173 size_t pixelRefOffset;
1174 if (0 == fPixelRefOffset || dstConfig == fConfig) {
1175 // Use the same offset as the original.
1176 pixelRefOffset = fPixelRefOffset;
1177 } else {
1178 // Find the correct offset in the new config. This needs to be done after calling
1179 // setConfig so dst's fConfig and fRowBytes have been set properly.
scroggo@google.come5f48242013-02-25 21:47:41 +00001180 int32_t x, y;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001181 if (!get_upper_left_from_offset(*this, &x, &y)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001182 return false;
1183 }
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001184 pixelRefOffset = get_sub_offset(*dst, x, y);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001185 if (SUB_OFFSET_FAILURE == pixelRefOffset) {
1186 return false;
1187 }
1188 }
1189 dst->setPixelRef(pixelRef, pixelRefOffset)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001190 return true;
1191 }
1192 }
1193
1194 if (this->getTexture()) {
1195 return false;
1196 } else {
1197 return this->copyTo(dst, dstConfig, NULL);
1198 }
1199}
1200
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201///////////////////////////////////////////////////////////////////////////////
1202///////////////////////////////////////////////////////////////////////////////
1203
1204static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
1205 const SkBitmap& src) {
1206 x <<= 1;
1207 y <<= 1;
1208 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001209 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 SkPMColor c, ag, rb;
1211
1212 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
1213 if (x < src.width() - 1) {
1214 p += 1;
1215 }
1216 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1217
reed@android.com829c83c2009-06-08 12:05:31 +00001218 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001220 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 }
1222 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1223 if (x < src.width() - 1) {
1224 p += 1;
1225 }
1226 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1227
1228 *dst->getAddr32(x >> 1, y >> 1) =
1229 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1230}
1231
1232static inline uint32_t expand16(U16CPU c) {
1233 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1234}
1235
1236// returns dirt in the top 16bits, but we don't care, since we only
1237// store the low 16bits.
1238static inline U16CPU pack16(uint32_t c) {
1239 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1240}
1241
1242static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1243 const SkBitmap& src) {
1244 x <<= 1;
1245 y <<= 1;
1246 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001247 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001249
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001251 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 p += 1;
1253 }
1254 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001255
reed@android.com829c83c2009-06-08 12:05:31 +00001256 p = baseP;
1257 if (y < src.height() - 1) {
1258 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259 }
1260 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001261 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 p += 1;
1263 }
1264 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001265
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1267}
1268
1269static uint32_t expand4444(U16CPU c) {
1270 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1271}
1272
1273static U16CPU collaps4444(uint32_t c) {
1274 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1275}
1276
1277static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1278 const SkBitmap& src) {
1279 x <<= 1;
1280 y <<= 1;
1281 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001282 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001284
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 c = expand4444(*p);
1286 if (x < src.width() - 1) {
1287 p += 1;
1288 }
1289 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001290
reed@android.com829c83c2009-06-08 12:05:31 +00001291 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001293 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 }
1295 c += expand4444(*p);
1296 if (x < src.width() - 1) {
1297 p += 1;
1298 }
1299 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1302}
1303
1304void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 if (forceRebuild)
1306 this->freeMipMap();
1307 else if (fMipMap)
1308 return; // we're already built
1309
1310 SkASSERT(NULL == fMipMap);
1311
1312 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1313
reed@google.com44699382013-10-31 17:28:30 +00001314 const SkBitmap::Config config = this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315
1316 switch (config) {
1317 case kARGB_8888_Config:
1318 proc = downsampleby2_proc32;
1319 break;
1320 case kRGB_565_Config:
1321 proc = downsampleby2_proc16;
1322 break;
1323 case kARGB_4444_Config:
1324 proc = downsampleby2_proc4444;
1325 break;
1326 case kIndex8_Config:
1327 case kA8_Config:
1328 default:
1329 return; // don't build mipmaps for these configs
1330 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001331
reed@android.com149e2f62009-05-22 14:39:03 +00001332 SkAutoLockPixels alp(*this);
1333 if (!this->readyToDraw()) {
1334 return;
1335 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336
1337 // whip through our loop to compute the exact size needed
1338 size_t size = 0;
1339 int maxLevels = 0;
1340 {
reed@android.com149e2f62009-05-22 14:39:03 +00001341 int width = this->width();
1342 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 for (;;) {
1344 width >>= 1;
1345 height >>= 1;
1346 if (0 == width || 0 == height) {
1347 break;
1348 }
1349 size += ComputeRowBytes(config, width) * height;
1350 maxLevels += 1;
1351 }
1352 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001353
reed@android.com149e2f62009-05-22 14:39:03 +00001354 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 if (0 == maxLevels) {
1356 return;
1357 }
1358
reed@android.com149e2f62009-05-22 14:39:03 +00001359 SkBitmap srcBM(*this);
1360 srcBM.lockPixels();
1361 if (!srcBM.readyToDraw()) {
1362 return;
1363 }
1364
1365 MipMap* mm = MipMap::Alloc(maxLevels, size);
1366 if (NULL == mm) {
1367 return;
1368 }
1369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 MipLevel* level = mm->levels();
1371 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001372 int width = this->width();
1373 int height = this->height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001374 uint32_t rowBytes;
reed@android.com149e2f62009-05-22 14:39:03 +00001375 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001376
1377 for (int i = 0; i < maxLevels; i++) {
1378 width >>= 1;
1379 height >>= 1;
scroggo@google.come5f48242013-02-25 21:47:41 +00001380 rowBytes = SkToU32(ComputeRowBytes(config, width));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381
1382 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001383 level[i].fWidth = width;
1384 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001385 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386
1387 dstBM.setConfig(config, width, height, rowBytes);
1388 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001389
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001390 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001391 for (int y = 0; y < height; y++) {
1392 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 proc(&dstBM, x, y, srcBM);
1394 }
1395 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001396 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397
1398 srcBM = dstBM;
1399 addr += height * rowBytes;
1400 }
1401 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1402 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403}
1404
1405bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407}
1408
1409int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001410 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001412 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 int level = ComputeMipLevel(sx, sy) >> 16;
1415 SkASSERT(level >= 0);
1416 if (level <= 0) {
1417 return 0;
1418 }
1419
1420 if (level >= fMipMap->fLevelCount) {
1421 level = fMipMap->fLevelCount - 1;
1422 }
1423 if (dst) {
1424 const MipLevel& mip = fMipMap->levels()[level - 1];
1425 dst->setConfig((SkBitmap::Config)this->config(),
1426 mip.fWidth, mip.fHeight, mip.fRowBytes);
1427 dst->setPixels(mip.fPixels);
1428 }
1429 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430}
1431
1432SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 sx = SkAbs32(sx);
1434 sy = SkAbs32(sy);
1435 if (sx < sy) {
1436 sx = sy;
1437 }
1438 if (sx < SK_Fixed1) {
1439 return 0;
1440 }
1441 int clz = SkCLZ(sx);
1442 SkASSERT(clz >= 1 && clz <= 15);
1443 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444}
1445
1446///////////////////////////////////////////////////////////////////////////////
1447
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001448static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 int alphaRowBytes) {
1450 SkASSERT(alpha != NULL);
1451 SkASSERT(alphaRowBytes >= src.width());
1452
reed@google.com44699382013-10-31 17:28:30 +00001453 SkBitmap::Config config = src.config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 int w = src.width();
1455 int h = src.height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001456 size_t rb = src.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457
reed@android.com1cdcb512009-08-24 19:11:00 +00001458 SkAutoLockPixels alp(src);
1459 if (!src.readyToDraw()) {
1460 // zero out the alpha buffer and return
1461 while (--h >= 0) {
1462 memset(alpha, 0, w);
1463 alpha += alphaRowBytes;
1464 }
1465 return false;
1466 }
reed@google.com82065d62011-02-07 15:30:46 +00001467
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1469 const uint8_t* s = src.getAddr8(0, 0);
1470 while (--h >= 0) {
1471 memcpy(alpha, s, w);
1472 s += rb;
1473 alpha += alphaRowBytes;
1474 }
1475 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1476 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1477 while (--h >= 0) {
1478 for (int x = 0; x < w; x++) {
1479 alpha[x] = SkGetPackedA32(s[x]);
1480 }
1481 s = (const SkPMColor*)((const char*)s + rb);
1482 alpha += alphaRowBytes;
1483 }
1484 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1485 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1486 while (--h >= 0) {
1487 for (int x = 0; x < w; x++) {
1488 alpha[x] = SkPacked4444ToA32(s[x]);
1489 }
1490 s = (const SkPMColor16*)((const char*)s + rb);
1491 alpha += alphaRowBytes;
1492 }
1493 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1494 SkColorTable* ct = src.getColorTable();
1495 if (ct) {
1496 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1497 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1498 while (--h >= 0) {
1499 for (int x = 0; x < w; x++) {
1500 alpha[x] = SkGetPackedA32(table[s[x]]);
1501 }
1502 s += rb;
1503 alpha += alphaRowBytes;
1504 }
reed@google.com0a6151d2013-10-10 14:44:56 +00001505 ct->unlockColors();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 }
1507 } else { // src is opaque, so just fill alpha[] with 0xFF
1508 memset(alpha, 0xFF, h * alphaRowBytes);
1509 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001510 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511}
1512
1513#include "SkPaint.h"
1514#include "SkMaskFilter.h"
1515#include "SkMatrix.h"
1516
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001517bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001518 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519 SkDEBUGCODE(this->validate();)
1520
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001521 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 SkMatrix identity;
1523 SkMask srcM, dstM;
1524
1525 srcM.fBounds.set(0, 0, this->width(), this->height());
1526 srcM.fRowBytes = SkAlign4(this->width());
1527 srcM.fFormat = SkMask::kA8_Format;
1528
1529 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1530
1531 // compute our (larger?) dst bounds if we have a filter
1532 if (NULL != filter) {
1533 identity.reset();
1534 srcM.fImage = NULL;
1535 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1536 goto NO_FILTER_CASE;
1537 }
1538 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1539 } else {
1540 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001541 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001543 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1544 // Allocation of pixels for alpha bitmap failed.
1545 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1546 tmpBitmap.width(), tmpBitmap.height());
1547 return false;
1548 }
1549 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 if (offset) {
1551 offset->set(0, 0);
1552 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001553 tmpBitmap.swap(*dst);
1554 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001556 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1557 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558
1559 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1560 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1561 goto NO_FILTER_CASE;
1562 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001563 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001565 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001567 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1568 // Allocation of pixels for alpha bitmap failed.
1569 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1570 tmpBitmap.width(), tmpBitmap.height());
1571 return false;
1572 }
1573 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574 if (offset) {
1575 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1576 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001577 SkDEBUGCODE(tmpBitmap.validate();)
1578
1579 tmpBitmap.swap(*dst);
1580 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581}
1582
1583///////////////////////////////////////////////////////////////////////////////
1584
1585enum {
1586 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001587 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588};
1589
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001591 buffer.writeInt(fWidth);
1592 buffer.writeInt(fHeight);
1593 buffer.writeInt(fRowBytes);
1594 buffer.writeInt(fConfig);
reed@google.com383a6972013-10-21 14:00:07 +00001595 buffer.writeInt(fAlphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001596
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001598 if (fPixelRef->getFactory()) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001599 buffer.writeInt(SERIALIZE_PIXELTYPE_REF_DATA);
scroggo@google.come5f48242013-02-25 21:47:41 +00001600 buffer.writeUInt(SkToU32(fPixelRefOffset));
djsollen@google.com5370cd92012-03-28 20:47:01 +00001601 buffer.writeFlattenable(fPixelRef);
1602 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 }
1604 // if we get here, we can't record the pixels
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001605 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606 } else {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001607 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 }
1609}
1610
1611void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1612 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001613
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 int width = buffer.readInt();
1615 int height = buffer.readInt();
1616 int rowBytes = buffer.readInt();
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001617 Config config = (Config)buffer.readInt();
1618 SkAlphaType alphaType = (SkAlphaType)buffer.readInt();
1619 buffer.validate((width >= 0) && (height >= 0) && (rowBytes >= 0) &&
1620 SkIsValidConfig(config) && validate_alphaType(config, alphaType));
weita@google.comf9ab99a2009-05-03 18:23:30 +00001621
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001622 this->setConfig(config, width, height, rowBytes, alphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001623
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001624 int reftype = buffer.readInt();
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001625 if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) ||
1626 (SERIALIZE_PIXELTYPE_NONE == reftype))) {
1627 switch (reftype) {
1628 case SERIALIZE_PIXELTYPE_REF_DATA: {
1629 size_t offset = buffer.readUInt();
1630 SkPixelRef* pr = buffer.readPixelRef();
1631 SkSafeUnref(this->setPixelRef(pr, offset));
1632 break;
1633 }
1634 case SERIALIZE_PIXELTYPE_NONE:
1635 break;
1636 default:
1637 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
1638 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640 }
1641}
1642
1643///////////////////////////////////////////////////////////////////////////////
1644
1645SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1646 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001647 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648}
1649
1650SkBitmap::RLEPixels::~RLEPixels() {
1651 sk_free(fYPtrs);
1652}
1653
1654///////////////////////////////////////////////////////////////////////////////
1655
1656#ifdef SK_DEBUG
1657void SkBitmap::validate() const {
1658 SkASSERT(fConfig < kConfigCount);
1659 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001660 uint8_t allFlags = kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag;
1661#ifdef SK_BUILD_FOR_ANDROID
1662 allFlags |= kHasHardwareMipMap_Flag;
1663#endif
1664 SkASSERT(fFlags <= allFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665 SkASSERT(fPixelLockCount >= 0);
1666 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1667 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1668
1669#if 0 // these asserts are not thread-correct, so disable for now
1670 if (fPixelRef) {
1671 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001672 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 } else {
1674 SkASSERT(NULL == fPixels);
1675 SkASSERT(NULL == fColorTable);
1676 }
1677 }
1678#endif
1679}
1680#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001681
1682#ifdef SK_DEVELOPER
1683void SkBitmap::toString(SkString* str) const {
1684
1685 static const char* gConfigNames[kConfigCount] = {
reed@google.com72e78082013-11-25 20:54:56 +00001686 "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001687 };
1688
1689 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
1690 gConfigNames[this->config()]);
1691
1692 str->append(" (");
1693 if (this->isOpaque()) {
1694 str->append("opaque");
1695 } else {
1696 str->append("transparent");
1697 }
1698 if (this->isImmutable()) {
1699 str->append(", immutable");
1700 } else {
1701 str->append(", not-immutable");
1702 }
1703 str->append(")");
1704
1705 SkPixelRef* pr = this->pixelRef();
1706 if (NULL == pr) {
1707 // show null or the explicit pixel address (rare)
1708 str->appendf(" pixels:%p", this->getPixels());
1709 } else {
1710 const char* uri = pr->getURI();
1711 if (NULL != uri) {
1712 str->appendf(" uri:\"%s\"", uri);
1713 } else {
1714 str->appendf(" pixelref:%p", pr);
1715 }
1716 }
1717
1718 str->append(")");
1719}
1720#endif