blob: 11c7cbd69c289991ea25c8894a6b62037ec34a2c [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
reed@android.com8a1c16f2008-12-17 15:59:43 +000027struct MipLevel {
28 void* fPixels;
29 uint32_t fRowBytes;
reed@android.comf459a492009-03-27 12:33:50 +000030 uint32_t fWidth, fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +000031};
32
33struct SkBitmap::MipMap : SkNoncopyable {
34 int32_t fRefCnt;
35 int fLevelCount;
36// MipLevel fLevel[fLevelCount];
37// Pixels[]
weita@google.comf9ab99a2009-05-03 18:23:30 +000038
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 static MipMap* Alloc(int levelCount, size_t pixelSize) {
reed@android.com149e2f62009-05-22 14:39:03 +000040 if (levelCount < 0) {
41 return NULL;
42 }
reed@google.com57212f92013-12-30 14:40:38 +000043 int64_t size = (levelCount + 1) * sizeof(MipLevel);
44 size += sizeof(MipMap) + pixelSize;
45 if (!sk_64_isS32(size)) {
reed@android.com149e2f62009-05-22 14:39:03 +000046 return NULL;
47 }
reed@google.com57212f92013-12-30 14:40:38 +000048 MipMap* mm = (MipMap*)sk_malloc_throw(sk_64_asS32(size));
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 mm->fRefCnt = 1;
50 mm->fLevelCount = levelCount;
51 return mm;
52 }
53
54 const MipLevel* levels() const { return (const MipLevel*)(this + 1); }
55 MipLevel* levels() { return (MipLevel*)(this + 1); }
56
57 const void* pixels() const { return levels() + fLevelCount; }
58 void* pixels() { return levels() + fLevelCount; }
weita@google.comf9ab99a2009-05-03 18:23:30 +000059
reed@android.com149e2f62009-05-22 14:39:03 +000060 void ref() {
61 if (SK_MaxS32 == sk_atomic_inc(&fRefCnt)) {
62 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +000063 }
64 }
reed@android.com149e2f62009-05-22 14:39:03 +000065 void unref() {
66 SkASSERT(fRefCnt > 0);
67 if (sk_atomic_dec(&fRefCnt) == 1) {
68 sk_free(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 }
70 }
71};
reed@android.com8a1c16f2008-12-17 15:59:43 +000072
73///////////////////////////////////////////////////////////////////////////////
74///////////////////////////////////////////////////////////////////////////////
75
76SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000077 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000078}
79
80SkBitmap::SkBitmap(const SkBitmap& src) {
81 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000082 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 *this = src;
84 SkDEBUGCODE(this->validate();)
85}
86
87SkBitmap::~SkBitmap() {
88 SkDEBUGCODE(this->validate();)
89 this->freePixels();
90}
91
92SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
93 if (this != &src) {
94 this->freePixels();
95 memcpy(this, &src, sizeof(src));
96
97 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +000098 SkSafeRef(src.fPixelRef);
reed@android.com149e2f62009-05-22 14:39:03 +000099 SkSafeRef(src.fMipMap);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100
101 // we reset our locks if we get blown away
102 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +0000103
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 /* The src could be in 3 states
105 1. no pixelref, in which case we just copy/ref the pixels/ctable
106 2. unlocked pixelref, pixels/ctable should be null
107 3. locked pixelref, we should lock the ref again ourselves
108 */
109 if (NULL == fPixelRef) {
110 // leave fPixels as it is
reed@google.com82065d62011-02-07 15:30:46 +0000111 SkSafeRef(fColorTable); // ref the user's ctable if present
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 } else { // we have a pixelref, so pixels/ctable reflect it
113 // ignore the values from the memcpy
114 fPixels = NULL;
115 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000116 // Note that what to for genID is somewhat arbitrary. We have no
117 // way to track changes to raw pixels across multiple SkBitmaps.
118 // Would benefit from an SkRawPixelRef type created by
119 // setPixels.
120 // Just leave the memcpy'ed one but they'll get out of sync
121 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 }
123 }
124
125 SkDEBUGCODE(this->validate();)
126 return *this;
127}
128
129void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000130 SkTSwap(fColorTable, other.fColorTable);
131 SkTSwap(fPixelRef, other.fPixelRef);
132 SkTSwap(fPixelRefOffset, other.fPixelRefOffset);
133 SkTSwap(fPixelLockCount, other.fPixelLockCount);
134 SkTSwap(fMipMap, other.fMipMap);
135 SkTSwap(fPixels, other.fPixels);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000136 SkTSwap(fRowBytes, other.fRowBytes);
137 SkTSwap(fWidth, other.fWidth);
138 SkTSwap(fHeight, other.fHeight);
139 SkTSwap(fConfig, other.fConfig);
reed@google.com383a6972013-10-21 14:00:07 +0000140 SkTSwap(fAlphaType, other.fAlphaType);
bsalomon@google.com586f48c2011-04-14 15:07:22 +0000141 SkTSwap(fFlags, other.fFlags);
142 SkTSwap(fBytesPerPixel, other.fBytesPerPixel);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143
144 SkDEBUGCODE(this->validate();)
145}
146
147void SkBitmap::reset() {
148 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +0000149 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150}
151
152int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
153 int bpp;
154 switch (config) {
155 case kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 bpp = 0; // not applicable
157 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 case kA8_Config:
159 case kIndex8_Config:
160 bpp = 1;
161 break;
162 case kRGB_565_Config:
163 case kARGB_4444_Config:
164 bpp = 2;
165 break;
166 case kARGB_8888_Config:
167 bpp = 4;
168 break;
169 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000170 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 bpp = 0; // error
172 break;
173 }
174 return bpp;
175}
176
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000177size_t SkBitmap::ComputeRowBytes(Config c, int width) {
reed@android.com149e2f62009-05-22 14:39:03 +0000178 if (width < 0) {
179 return 0;
180 }
181
reed@google.com57212f92013-12-30 14:40:38 +0000182 int64_t rowBytes = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
184 switch (c) {
185 case kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 case kA8_Config:
188 case kIndex8_Config:
reed@google.com57212f92013-12-30 14:40:38 +0000189 rowBytes = width;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 break;
191 case kRGB_565_Config:
192 case kARGB_4444_Config:
reed@google.com57212f92013-12-30 14:40:38 +0000193 rowBytes = width << 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 break;
195 case kARGB_8888_Config:
reed@google.com57212f92013-12-30 14:40:38 +0000196 rowBytes = width << 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 break;
198 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000199 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 break;
201 }
reed@google.com57212f92013-12-30 14:40:38 +0000202 return sk_64_isS32(rowBytes) ? sk_64_asS32(rowBytes) : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203}
204
reed@google.com57212f92013-12-30 14:40:38 +0000205int64_t SkBitmap::ComputeSize64(Config config, int width, int height) {
206 int64_t rowBytes = sk_64_mul(ComputeBytesPerPixel(config), width);
207 return rowBytes * height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208}
209
210size_t SkBitmap::ComputeSize(Config c, int width, int height) {
reed@google.com57212f92013-12-30 14:40:38 +0000211 int64_t size = SkBitmap::ComputeSize64(c, width, height);
212 return sk_64_isS32(size) ? sk_64_asS32(size) : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213}
214
reed@google.com57212f92013-12-30 14:40:38 +0000215int64_t SkBitmap::ComputeSafeSize64(Config config,
216 uint32_t width,
217 uint32_t height,
218 size_t rowBytes) {
219 int64_t safeSize = 0;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000220 if (height > 0) {
reed@google.com57212f92013-12-30 14:40:38 +0000221 int64_t lastRow = sk_64_mul(ComputeBytesPerPixel(config), width);
222 safeSize = sk_64_mul(height - 1, rowBytes) + lastRow;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000223 }
reed@google.com57212f92013-12-30 14:40:38 +0000224 SkASSERT(safeSize >= 0);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000225 return safeSize;
226}
227
228size_t SkBitmap::ComputeSafeSize(Config config,
229 uint32_t width,
230 uint32_t height,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000231 size_t rowBytes) {
reed@google.com57212f92013-12-30 14:40:38 +0000232 int64_t safeSize = ComputeSafeSize64(config, width, height, rowBytes);
233 int32_t safeSize32 = (int32_t)safeSize;
234
235 if (safeSize32 != safeSize) {
236 safeSize32 = 0;
237 }
238 return safeSize32;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000239}
240
reed@google.com86b2e432012-03-15 21:17:03 +0000241void SkBitmap::getBounds(SkRect* bounds) const {
242 SkASSERT(bounds);
243 bounds->set(0, 0,
244 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
245}
246
reed@google.com80e14592012-03-16 14:58:07 +0000247void SkBitmap::getBounds(SkIRect* bounds) const {
248 SkASSERT(bounds);
249 bounds->set(0, 0, fWidth, fHeight);
250}
251
reed@google.com86b2e432012-03-15 21:17:03 +0000252///////////////////////////////////////////////////////////////////////////////
253
reed@google.com383a6972013-10-21 14:00:07 +0000254static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType,
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000255 SkAlphaType* canonical = NULL) {
reed@google.com383a6972013-10-21 14:00:07 +0000256 switch (config) {
257 case SkBitmap::kNo_Config:
258 alphaType = kIgnore_SkAlphaType;
259 break;
reed@google.com383a6972013-10-21 14:00:07 +0000260 case SkBitmap::kA8_Config:
261 if (kUnpremul_SkAlphaType == alphaType) {
262 alphaType = kPremul_SkAlphaType;
263 }
264 // fall-through
265 case SkBitmap::kIndex8_Config:
266 case SkBitmap::kARGB_4444_Config:
267 case SkBitmap::kARGB_8888_Config:
268 if (kIgnore_SkAlphaType == alphaType) {
269 return false;
270 }
271 break;
272 case SkBitmap::kRGB_565_Config:
273 alphaType = kOpaque_SkAlphaType;
274 break;
rmistry@google.comd6bab022013-12-02 13:50:38 +0000275 default:
276 return false;
reed@android.com149e2f62009-05-22 14:39:03 +0000277 }
reed@google.com383a6972013-10-21 14:00:07 +0000278 if (canonical) {
279 *canonical = alphaType;
280 }
281 return true;
282}
reed@android.com149e2f62009-05-22 14:39:03 +0000283
reed@google.com383a6972013-10-21 14:00:07 +0000284bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
285 SkAlphaType alphaType) {
286 if ((width | height) < 0) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000287 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000288 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 if (rowBytes == 0) {
reed@google.com383a6972013-10-21 14:00:07 +0000290 rowBytes = SkBitmap::ComputeRowBytes(config, width);
halcanary@google.com44287342013-12-13 18:29:51 +0000291 if (0 == rowBytes && kNo_Config != config && width > 0) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000292 goto BAD_CONFIG;
reed@android.com149e2f62009-05-22 14:39:03 +0000293 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000295
reed@google.com383a6972013-10-21 14:00:07 +0000296 if (!validate_alphaType(config, alphaType, &alphaType)) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000297 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000298 }
299
300 this->freePixels();
301
302 fConfig = SkToU8(config);
303 fAlphaType = SkToU8(alphaType);
reed@android.comf459a492009-03-27 12:33:50 +0000304 fWidth = width;
305 fHeight = height;
scroggo@google.come5f48242013-02-25 21:47:41 +0000306 fRowBytes = SkToU32(rowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307
reed@google.com383a6972013-10-21 14:00:07 +0000308 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(config);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309
310 SkDEBUGCODE(this->validate();)
reed@google.com383a6972013-10-21 14:00:07 +0000311 return true;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000312
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000313 // if we got here, we had an error, so we reset the bitmap to empty
reed@google.com9cd697c2013-10-21 14:12:13 +0000314BAD_CONFIG:
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000315 this->reset();
reed@google.com383a6972013-10-21 14:00:07 +0000316 return false;
317}
318
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +0000319bool SkBitmap::setConfig(const SkImageInfo& info, size_t rowBytes) {
320 return this->setConfig(SkImageInfoToBitmapConfig(info), info.fWidth,
321 info.fHeight, rowBytes, info.fAlphaType);
322}
323
reed@google.com383a6972013-10-21 14:00:07 +0000324bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
325 if (!validate_alphaType(this->config(), alphaType, &alphaType)) {
326 return false;
327 }
328 fAlphaType = SkToU8(alphaType);
329 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330}
331
332void SkBitmap::updatePixelsFromRef() const {
333 if (NULL != fPixelRef) {
334 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000335 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000336
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 void* p = fPixelRef->pixels();
338 if (NULL != p) {
339 p = (char*)p + fPixelRefOffset;
340 }
341 fPixels = p;
342 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
343 } else {
344 SkASSERT(0 == fPixelLockCount);
345 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000346 if (fColorTable) {
347 fColorTable->unref();
348 fColorTable = NULL;
349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 }
351 }
352}
353
reed@google.com9230ea22013-12-09 22:01:03 +0000354static bool config_to_colorType(SkBitmap::Config config, SkColorType* ctOut) {
355 SkColorType ct;
356 switch (config) {
357 case SkBitmap::kA8_Config:
358 ct = kAlpha_8_SkColorType;
359 break;
360 case SkBitmap::kIndex8_Config:
361 ct = kIndex_8_SkColorType;
362 break;
363 case SkBitmap::kRGB_565_Config:
364 ct = kRGB_565_SkColorType;
365 break;
366 case SkBitmap::kARGB_4444_Config:
367 ct = kARGB_4444_SkColorType;
368 break;
369 case SkBitmap::kARGB_8888_Config:
370 ct = kPMColor_SkColorType;
371 break;
372 case SkBitmap::kNo_Config:
373 default:
374 return false;
375 }
376 if (ctOut) {
377 *ctOut = ct;
378 }
379 return true;
380}
381
382bool SkBitmap::asImageInfo(SkImageInfo* info) const {
383 SkColorType ct;
384 if (!config_to_colorType(this->config(), &ct)) {
385 return false;
386 }
387 if (info) {
388 info->fWidth = fWidth;
389 info->fHeight = fHeight;
390 info->fAlphaType = this->alphaType();
391 info->fColorType = ct;
392 }
393 return true;
394}
395
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
397 // do this first, we that we never have a non-zero offset with a null ref
398 if (NULL == pr) {
399 offset = 0;
400 }
401
402 if (fPixelRef != pr || fPixelRefOffset != offset) {
403 if (fPixelRef != pr) {
404 this->freePixels();
405 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000406
reed@google.com82065d62011-02-07 15:30:46 +0000407 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 fPixelRef = pr;
409 }
410 fPixelRefOffset = offset;
411 this->updatePixelsFromRef();
412 }
413
414 SkDEBUGCODE(this->validate();)
415 return pr;
416}
417
418void SkBitmap::lockPixels() const {
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000419 if (NULL != fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 fPixelRef->lockPixels();
421 this->updatePixelsFromRef();
422 }
423 SkDEBUGCODE(this->validate();)
424}
425
426void SkBitmap::unlockPixels() const {
427 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
428
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000429 if (NULL != fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 fPixelRef->unlockPixels();
431 this->updatePixelsFromRef();
432 }
433 SkDEBUGCODE(this->validate();)
434}
435
reed@google.com9c49bc32011-07-07 13:42:37 +0000436bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000437 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000438}
439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000441 if (NULL == p) {
442 this->setPixelRef(NULL, 0);
443 return;
444 }
445
reed@google.combf790232013-12-13 19:45:58 +0000446 SkImageInfo info;
447 if (!this->asImageInfo(&info)) {
448 this->setPixelRef(NULL, 0);
449 return;
450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451
reed@google.combf790232013-12-13 19:45:58 +0000452 SkPixelRef* pr = SkMallocPixelRef::NewDirect(info, p, fRowBytes, ctable);
453 if (NULL == pr) {
454 this->setPixelRef(NULL, 0);
455 return;
456 }
457
458 this->setPixelRef(pr)->unref();
459
djsollen@google.comc84b8332012-07-27 13:41:44 +0000460 // since we're already allocated, we lockPixels right away
461 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkDEBUGCODE(this->validate();)
463}
464
465bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
466 HeapAllocator stdalloc;
467
468 if (NULL == allocator) {
469 allocator = &stdalloc;
470 }
471 return allocator->allocPixelRef(this, ctable);
472}
473
474void SkBitmap::freePixels() {
475 // if we're gonna free the pixels, we certainly need to free the mipmap
476 this->freeMipMap();
477
reed@android.com149e2f62009-05-22 14:39:03 +0000478 if (fColorTable) {
479 fColorTable->unref();
480 fColorTable = NULL;
481 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482
483 if (NULL != fPixelRef) {
484 if (fPixelLockCount > 0) {
485 fPixelRef->unlockPixels();
486 }
487 fPixelRef->unref();
488 fPixelRef = NULL;
489 fPixelRefOffset = 0;
490 }
491 fPixelLockCount = 0;
492 fPixels = NULL;
493}
494
495void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000496 if (fMipMap) {
497 fMipMap->unref();
498 fMipMap = NULL;
499 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500}
501
502uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000503 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504}
505
506void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000507 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 if (fPixelRef) {
509 fPixelRef->notifyPixelsChanged();
510 }
511}
512
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000513GrTexture* SkBitmap::getTexture() const {
reed@android.comce4e53a2010-09-09 16:01:26 +0000514 return fPixelRef ? fPixelRef->getTexture() : NULL;
515}
516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517///////////////////////////////////////////////////////////////////////////////
518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519/** We explicitly use the same allocator for our pixels that SkMask does,
520 so that we can freely assign memory allocated by one class to the other.
521 */
522bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
523 SkColorTable* ctable) {
reed@google.combf790232013-12-13 19:45:58 +0000524 SkImageInfo info;
525 if (!dst->asImageInfo(&info)) {
526// SkDebugf("unsupported config for info %d\n", dst->config());
527 return false;
528 }
skia.committer@gmail.com96f5fa02013-12-16 07:01:40 +0000529
reed@google.combf790232013-12-13 19:45:58 +0000530 SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(),
531 ctable);
532 if (NULL == pr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 return false;
534 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000535
reed@google.combf790232013-12-13 19:45:58 +0000536 dst->setPixelRef(pr, 0)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 // since we're already allocated, we lockPixels right away
538 dst->lockPixels();
539 return true;
540}
541
542///////////////////////////////////////////////////////////////////////////////
543
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000544size_t SkBitmap::getSafeSize() const {
545 // This is intended to be a size_t version of ComputeSafeSize64(), just
546 // faster. The computation is meant to be identical.
547 return (fHeight ? ((fHeight - 1) * fRowBytes) +
reed@google.com44699382013-10-31 17:28:30 +0000548 ComputeRowBytes(this->config(), fWidth): 0);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000549}
550
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000551bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000552 size_t dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000553
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000554 if (0 == dstRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000555 dstRowBytes = fRowBytes;
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000556 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000557
reed@google.com44699382013-10-31 17:28:30 +0000558 if (dstRowBytes < ComputeRowBytes(this->config(), fWidth) ||
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000559 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
560 return false;
561
bsalomon@google.comc6980972011-11-02 19:57:21 +0000562 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
reed@google.com44699382013-10-31 17:28:30 +0000563 size_t safeSize = this->getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000564 if (safeSize > dstSize || safeSize == 0)
565 return false;
566 else {
567 SkAutoLockPixels lock(*this);
568 // This implementation will write bytes beyond the end of each row,
569 // excluding the last row, if the bitmap's stride is greater than
570 // strictly required by the current config.
571 memcpy(dst, getPixels(), safeSize);
572
573 return true;
574 }
575 } else {
576 // If destination has different stride than us, then copy line by line.
reed@google.com44699382013-10-31 17:28:30 +0000577 if (ComputeSafeSize(this->config(), fWidth, fHeight, dstRowBytes) >
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000578 dstSize)
579 return false;
580 else {
581 // Just copy what we need on each line.
reed@google.com44699382013-10-31 17:28:30 +0000582 size_t rowBytes = ComputeRowBytes(this->config(), fWidth);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000583 SkAutoLockPixels lock(*this);
584 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
585 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
586 for (uint32_t row = 0; row < fHeight;
587 row++, srcP += fRowBytes, dstP += dstRowBytes) {
588 memcpy(dstP, srcP, rowBytes);
589 }
590
591 return true;
592 }
593 }
594}
595
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000596///////////////////////////////////////////////////////////////////////////////
597
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000598bool SkBitmap::isImmutable() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000599 return fPixelRef ? fPixelRef->isImmutable() :
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000600 fFlags & kImageIsImmutable_Flag;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000601}
602
603void SkBitmap::setImmutable() {
604 if (fPixelRef) {
605 fPixelRef->setImmutable();
606 } else {
607 fFlags |= kImageIsImmutable_Flag;
608 }
609}
610
junov@google.com4ee7ae52011-06-30 17:30:49 +0000611bool SkBitmap::isVolatile() const {
612 return (fFlags & kImageIsVolatile_Flag) != 0;
613}
614
615void SkBitmap::setIsVolatile(bool isVolatile) {
616 if (isVolatile) {
617 fFlags |= kImageIsVolatile_Flag;
618 } else {
619 fFlags &= ~kImageIsVolatile_Flag;
620 }
621}
622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623void* SkBitmap::getAddr(int x, int y) const {
624 SkASSERT((unsigned)x < (unsigned)this->width());
625 SkASSERT((unsigned)y < (unsigned)this->height());
626
627 char* base = (char*)this->getPixels();
628 if (base) {
629 base += y * this->rowBytes();
630 switch (this->config()) {
631 case SkBitmap::kARGB_8888_Config:
632 base += x << 2;
633 break;
634 case SkBitmap::kARGB_4444_Config:
635 case SkBitmap::kRGB_565_Config:
636 base += x << 1;
637 break;
638 case SkBitmap::kA8_Config:
639 case SkBitmap::kIndex8_Config:
640 base += x;
641 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000644 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 base = NULL;
646 break;
647 }
648 }
649 return base;
650}
651
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000652SkColor SkBitmap::getColor(int x, int y) const {
653 SkASSERT((unsigned)x < (unsigned)this->width());
654 SkASSERT((unsigned)y < (unsigned)this->height());
655
656 switch (this->config()) {
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000657 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000658 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000659 return SkColorSetA(0, addr[0]);
660 }
661 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000662 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000663 return SkUnPreMultiply::PMColorToColor(c);
664 }
665 case SkBitmap::kRGB_565_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000666 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000667 return SkPixel16ToColor(addr[0]);
668 }
669 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000670 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000671 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
672 return SkUnPreMultiply::PMColorToColor(c);
673 }
674 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000675 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000676 return SkUnPreMultiply::PMColorToColor(addr[0]);
677 }
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000678 case kNo_Config:
rmistry@google.comd6bab022013-12-02 13:50:38 +0000679 default:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000680 SkASSERT(false);
681 return 0;
682 }
683 SkASSERT(false); // Not reached.
684 return 0;
685}
686
reed@google.com2a7579d2012-11-07 18:30:18 +0000687bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
688 SkAutoLockPixels alp(bm);
689 if (!bm.getPixels()) {
690 return false;
691 }
692
693 const int height = bm.height();
694 const int width = bm.width();
695
696 switch (bm.config()) {
reed@google.com2a7579d2012-11-07 18:30:18 +0000697 case SkBitmap::kA8_Config: {
698 unsigned a = 0xFF;
699 for (int y = 0; y < height; ++y) {
700 const uint8_t* row = bm.getAddr8(0, y);
701 for (int x = 0; x < width; ++x) {
702 a &= row[x];
703 }
704 if (0xFF != a) {
705 return false;
706 }
707 }
708 return true;
709 } break;
reed@google.com2a7579d2012-11-07 18:30:18 +0000710 case SkBitmap::kIndex8_Config: {
711 SkAutoLockColors alc(bm);
712 const SkPMColor* table = alc.colors();
713 if (!table) {
714 return false;
715 }
reed@google.com140d7282013-01-07 20:25:04 +0000716 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000717 for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
718 c &= table[i];
719 }
720 return 0xFF == SkGetPackedA32(c);
721 } break;
722 case SkBitmap::kRGB_565_Config:
723 return true;
724 break;
725 case SkBitmap::kARGB_4444_Config: {
726 unsigned c = 0xFFFF;
727 for (int y = 0; y < height; ++y) {
728 const SkPMColor16* row = bm.getAddr16(0, y);
729 for (int x = 0; x < width; ++x) {
730 c &= row[x];
731 }
732 if (0xF != SkGetPackedA4444(c)) {
733 return false;
734 }
735 }
736 return true;
737 } break;
738 case SkBitmap::kARGB_8888_Config: {
reed@google.com140d7282013-01-07 20:25:04 +0000739 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000740 for (int y = 0; y < height; ++y) {
741 const SkPMColor* row = bm.getAddr32(0, y);
742 for (int x = 0; x < width; ++x) {
743 c &= row[x];
744 }
745 if (0xFF != SkGetPackedA32(c)) {
746 return false;
747 }
748 }
749 return true;
750 }
751 default:
752 break;
753 }
754 return false;
755}
756
757
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758///////////////////////////////////////////////////////////////////////////////
759///////////////////////////////////////////////////////////////////////////////
760
reed@google.com45f746f2013-06-21 19:51:31 +0000761static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
762 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
763 (SkR32To4444(r) << SK_R4444_SHIFT) |
764 (SkG32To4444(g) << SK_G4444_SHIFT) |
765 (SkB32To4444(b) << SK_B4444_SHIFT);
766 return SkToU16(pixel);
767}
768
reed@google.com60d32352013-06-28 19:40:50 +0000769void SkBitmap::internalErase(const SkIRect& area,
770 U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
771#ifdef SK_DEBUG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 SkDEBUGCODE(this->validate();)
reed@google.com60d32352013-06-28 19:40:50 +0000773 SkASSERT(!area.isEmpty());
774 {
reed@google.com92833f92013-06-28 19:47:43 +0000775 SkIRect total = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000776 SkASSERT(total.contains(area));
777 }
778#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779
reed@google.com60d32352013-06-28 19:40:50 +0000780 if (kNo_Config == fConfig || kIndex8_Config == fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 return;
782 }
783
784 SkAutoLockPixels alp(*this);
785 // perform this check after the lock call
786 if (!this->readyToDraw()) {
787 return;
788 }
789
reed@google.com60d32352013-06-28 19:40:50 +0000790 int height = area.height();
791 const int width = area.width();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 const int rowBytes = fRowBytes;
793
794 // make rgb premultiplied
795 if (255 != a) {
796 r = SkAlphaMul(r, a);
797 g = SkAlphaMul(g, a);
798 b = SkAlphaMul(b, a);
799 }
800
801 switch (fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 case kA8_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000803 uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 while (--height >= 0) {
805 memset(p, a, width);
806 p += rowBytes;
807 }
808 break;
809 }
reed@google.com45f746f2013-06-21 19:51:31 +0000810 case kARGB_4444_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 case kRGB_565_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000812 uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
reed@google.com45f746f2013-06-21 19:51:31 +0000813 uint16_t v;
skia.committer@gmail.com020b25b2013-06-22 07:00:58 +0000814
reed@google.com45f746f2013-06-21 19:51:31 +0000815 if (kARGB_4444_Config == fConfig) {
816 v = pack_8888_to_4444(a, r, g, b);
817 } else {
818 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
819 g >> (8 - SK_G16_BITS),
820 b >> (8 - SK_B16_BITS));
821 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 while (--height >= 0) {
823 sk_memset16(p, v, width);
824 p = (uint16_t*)((char*)p + rowBytes);
825 }
826 break;
827 }
828 case kARGB_8888_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000829 uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 uint32_t v = SkPackARGB32(a, r, g, b);
831
832 while (--height >= 0) {
833 sk_memset32(p, v, width);
834 p = (uint32_t*)((char*)p + rowBytes);
835 }
836 break;
837 }
838 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000839
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 this->notifyPixelsChanged();
841}
842
reed@google.com60d32352013-06-28 19:40:50 +0000843void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
reed@google.com92833f92013-06-28 19:47:43 +0000844 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000845 if (!area.isEmpty()) {
846 this->internalErase(area, a, r, g, b);
847 }
848}
849
850void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
reed@google.com92833f92013-06-28 19:47:43 +0000851 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000852 if (area.intersect(rect)) {
853 this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
854 SkColorGetG(c), SkColorGetB(c));
855 }
856}
857
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858//////////////////////////////////////////////////////////////////////////////////////
859//////////////////////////////////////////////////////////////////////////////////////
860
861#define SUB_OFFSET_FAILURE ((size_t)-1)
862
scroggo@google.coma2a31922012-12-07 19:14:45 +0000863/**
864 * Based on the Config and rowBytes() of bm, return the offset into an SkPixelRef of the pixel at
865 * (x, y).
866 * Note that the SkPixelRef does not need to be set yet. deepCopyTo takes advantage of this fact.
867 * Also note that (x, y) may be outside the range of (0 - width(), 0 - height()), so long as it is
868 * within the bounds of the SkPixelRef being used.
869 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000870static size_t get_sub_offset(const SkBitmap& bm, int x, int y) {
reed@google.com44699382013-10-31 17:28:30 +0000871 switch (bm.config()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 case SkBitmap::kA8_Config:
873 case SkBitmap:: kIndex8_Config:
874 // x is fine as is for the calculation
875 break;
876
877 case SkBitmap::kRGB_565_Config:
878 case SkBitmap::kARGB_4444_Config:
879 x <<= 1;
880 break;
881
882 case SkBitmap::kARGB_8888_Config:
883 x <<= 2;
884 break;
885
886 case SkBitmap::kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 default:
888 return SUB_OFFSET_FAILURE;
889 }
890 return y * bm.rowBytes() + x;
891}
892
scroggo@google.coma2a31922012-12-07 19:14:45 +0000893/**
894 * Using the pixelRefOffset(), rowBytes(), and Config of bm, determine the (x, y) coordinate of the
895 * upper left corner of bm relative to its SkPixelRef.
896 * x and y must be non-NULL.
897 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000898bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com61d6c9e2013-05-21 20:38:40 +0000899 int32_t* x, int32_t* y);
900bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000901 int32_t* x, int32_t* y) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000902 SkASSERT(x != NULL && y != NULL);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000903 if (0 == offset) {
904 *x = *y = 0;
905 return true;
906 }
907 // Use integer division to find the correct y position.
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000908 // The remainder will be the x position, after we reverse get_sub_offset.
commit-bot@chromium.org2c86fbb2013-09-26 19:22:54 +0000909 SkTDivMod(offset, rowBytes, y, x);
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000910 switch (config) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000911 case SkBitmap::kA8_Config:
912 // Fall through.
913 case SkBitmap::kIndex8_Config:
914 // x is unmodified
915 break;
916
917 case SkBitmap::kRGB_565_Config:
918 // Fall through.
919 case SkBitmap::kARGB_4444_Config:
920 *x >>= 1;
921 break;
922
923 case SkBitmap::kARGB_8888_Config:
924 *x >>= 2;
925 break;
926
927 case SkBitmap::kNo_Config:
928 // Fall through.
scroggo@google.coma2a31922012-12-07 19:14:45 +0000929 default:
930 return false;
931 }
932 return true;
933}
934
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000935static bool get_upper_left_from_offset(const SkBitmap& bm, int32_t* x, int32_t* y) {
936 return get_upper_left_from_offset(bm.config(), bm.pixelRefOffset(), bm.rowBytes(), x, y);
937}
938
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
940 SkDEBUGCODE(this->validate();)
941
djsollen@google.comc84b8332012-07-27 13:41:44 +0000942 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 return false; // no src pixels
944 }
945
946 SkIRect srcRect, r;
947 srcRect.set(0, 0, this->width(), this->height());
948 if (!r.intersect(srcRect, subset)) {
949 return false; // r is empty (i.e. no intersection)
950 }
951
scroggo@google.coma2a31922012-12-07 19:14:45 +0000952 if (fPixelRef->getTexture() != NULL) {
953 // Do a deep copy
954 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->config(), &subset);
955 if (pixelRef != NULL) {
956 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000957 dst.setConfig(this->config(), subset.width(), subset.height(), 0,
958 this->alphaType());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000959 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000960 dst.setPixelRef(pixelRef)->unref();
961 SkDEBUGCODE(dst.validate());
962 result->swap(dst);
963 return true;
964 }
965 }
966
scroggo@google.coma2a31922012-12-07 19:14:45 +0000967 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
968 // exited above.
969 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
970 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
971
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000972 size_t offset = get_sub_offset(*this, r.fLeft, r.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 if (SUB_OFFSET_FAILURE == offset) {
974 return false; // config not supported
975 }
976
977 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000978 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes(),
979 this->alphaType());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000980 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981
982 if (fPixelRef) {
983 // share the pixelref with a custom offset
984 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 }
986 SkDEBUGCODE(dst.validate();)
987
988 // we know we're good, so commit to result
989 result->swap(dst);
990 return true;
991}
992
993///////////////////////////////////////////////////////////////////////////////
994
995#include "SkCanvas.h"
996#include "SkPaint.h"
997
reed@android.comfbaa88d2009-05-06 17:44:34 +0000998bool SkBitmap::canCopyTo(Config dstConfig) const {
reed@google.com44699382013-10-31 17:28:30 +0000999 if (this->config() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 return false;
1001 }
1002
reed@android.comfbaa88d2009-05-06 17:44:34 +00001003 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 switch (dstConfig) {
1005 case kA8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 case kRGB_565_Config:
1007 case kARGB_8888_Config:
1008 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001009 case kIndex8_Config:
1010 if (!sameConfigs) {
1011 return false;
1012 }
1013 break;
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001014 case kARGB_4444_Config:
1015 return sameConfigs || kARGB_8888_Config == this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 default:
1017 return false;
1018 }
reed@android.comfbaa88d2009-05-06 17:44:34 +00001019 return true;
1020}
1021
1022bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
1023 if (!this->canCopyTo(dstConfig)) {
1024 return false;
1025 }
1026
reed@google.com50dfa012011-04-01 19:05:36 +00001027 // if we have a texture, first get those pixels
1028 SkBitmap tmpSrc;
1029 const SkBitmap* src = this;
1030
scroggo@google.coma2a31922012-12-07 19:14:45 +00001031 if (fPixelRef) {
1032 SkIRect subset;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001033 if (get_upper_left_from_offset(*this, &subset.fLeft, &subset.fTop)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001034 subset.fRight = subset.fLeft + fWidth;
1035 subset.fBottom = subset.fTop + fHeight;
1036 if (fPixelRef->readPixels(&tmpSrc, &subset)) {
1037 SkASSERT(tmpSrc.width() == this->width());
1038 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +00001039
scroggo@google.coma2a31922012-12-07 19:14:45 +00001040 // did we get lucky and we can just return tmpSrc?
1041 if (tmpSrc.config() == dstConfig && NULL == alloc) {
1042 dst->swap(tmpSrc);
1043 if (dst->pixelRef() && this->config() == dstConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001044 // TODO(scroggo): fix issue 1742
1045 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001046 }
1047 return true;
1048 }
1049
1050 // fall through to the raster case
1051 src = &tmpSrc;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001052 }
reed@google.com50dfa012011-04-01 19:05:36 +00001053 }
reed@android.comfbaa88d2009-05-06 17:44:34 +00001054 }
reed@android.com311c82d2009-05-05 23:13:23 +00001055
reed@google.com50dfa012011-04-01 19:05:36 +00001056 // we lock this now, since we may need its colortable
1057 SkAutoLockPixels srclock(*src);
1058 if (!src->readyToDraw()) {
1059 return false;
1060 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001061
reed@google.com50dfa012011-04-01 19:05:36 +00001062 SkBitmap tmpDst;
reed@google.com383a6972013-10-21 14:00:07 +00001063 tmpDst.setConfig(dstConfig, src->width(), src->height(), 0,
1064 src->alphaType());
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001065
weita@google.comf9ab99a2009-05-03 18:23:30 +00001066 // allocate colortable if srcConfig == kIndex8_Config
1067 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +00001068 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001069 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +00001070 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +00001071 return false;
1072 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001073
reed@google.com50dfa012011-04-01 19:05:36 +00001074 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 // allocator/lock failed
1076 return false;
1077 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001078
reed@android.comfbaa88d2009-05-06 17:44:34 +00001079 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +00001080 */
reed@google.com50dfa012011-04-01 19:05:36 +00001081 if (src->config() == dstConfig) {
1082 if (tmpDst.getSize() == src->getSize()) {
1083 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
scroggo@google.comd5764e82012-08-22 15:00:05 +00001084 SkPixelRef* pixelRef = tmpDst.pixelRef();
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001085 if (NULL != pixelRef && NULL != fPixelRef) {
1086 // TODO(scroggo): fix issue 1742
1087 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.comd5764e82012-08-22 15:00:05 +00001088 }
reed@android.com311c82d2009-05-05 23:13:23 +00001089 } else {
reed@google.com50dfa012011-04-01 19:05:36 +00001090 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
1091 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +00001092 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +00001093 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
1094 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +00001095 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +00001096 srcP += src->rowBytes();
1097 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +00001098 }
1099 }
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001100 } else if (SkBitmap::kARGB_4444_Config == dstConfig
1101 && SkBitmap::kARGB_8888_Config == src->config()) {
1102 SkASSERT(src->height() == tmpDst.height());
1103 SkASSERT(src->width() == tmpDst.width());
1104 for (int y = 0; y < src->height(); ++y) {
1105 SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*) tmpDst.getAddr16(0, y);
1106 SkPMColor* SK_RESTRICT srcRow = (SkPMColor*) src->getAddr32(0, y);
1107 DITHER_4444_SCAN(y);
1108 for (int x = 0; x < src->width(); ++x) {
1109 dstRow[x] = SkDitherARGB32To4444(srcRow[x],
1110 DITHER_VALUE(x));
1111 }
1112 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001113 } else {
robertphillips@google.com0197b322013-10-10 15:48:16 +00001114 // Always clear the dest in case one of the blitters accesses it
1115 // TODO: switch the allocation of tmpDst to call sk_calloc_throw
1116 tmpDst.eraseColor(SK_ColorTRANSPARENT);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001117
reed@google.com50dfa012011-04-01 19:05:36 +00001118 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001119 SkPaint paint;
1120
1121 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +00001122 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 }
1124
reed@google.com50dfa012011-04-01 19:05:36 +00001125 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 return true;
1127}
1128
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001129bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
1130 if (!this->canCopyTo(dstConfig)) {
1131 return false;
1132 }
1133
1134 // If we have a PixelRef, and it supports deep copy, use it.
1135 // Currently supported only by texture-backed bitmaps.
1136 if (fPixelRef) {
1137 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
1138 if (pixelRef) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001139 uint32_t rowBytes;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001140 if (dstConfig == fConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001141 // TODO(scroggo): fix issue 1742
1142 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001143 // Use the same rowBytes as the original.
1144 rowBytes = fRowBytes;
1145 } else {
1146 // With the new config, an appropriate fRowBytes will be computed by setConfig.
1147 rowBytes = 0;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001148 }
scroggo@google.coma2a31922012-12-07 19:14:45 +00001149 dst->setConfig(dstConfig, fWidth, fHeight, rowBytes);
1150
1151 size_t pixelRefOffset;
1152 if (0 == fPixelRefOffset || dstConfig == fConfig) {
1153 // Use the same offset as the original.
1154 pixelRefOffset = fPixelRefOffset;
1155 } else {
1156 // Find the correct offset in the new config. This needs to be done after calling
1157 // setConfig so dst's fConfig and fRowBytes have been set properly.
scroggo@google.come5f48242013-02-25 21:47:41 +00001158 int32_t x, y;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001159 if (!get_upper_left_from_offset(*this, &x, &y)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001160 return false;
1161 }
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001162 pixelRefOffset = get_sub_offset(*dst, x, y);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001163 if (SUB_OFFSET_FAILURE == pixelRefOffset) {
1164 return false;
1165 }
1166 }
1167 dst->setPixelRef(pixelRef, pixelRefOffset)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001168 return true;
1169 }
1170 }
1171
1172 if (this->getTexture()) {
1173 return false;
1174 } else {
1175 return this->copyTo(dst, dstConfig, NULL);
1176 }
1177}
1178
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179///////////////////////////////////////////////////////////////////////////////
1180///////////////////////////////////////////////////////////////////////////////
1181
1182static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
1183 const SkBitmap& src) {
1184 x <<= 1;
1185 y <<= 1;
1186 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001187 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 SkPMColor c, ag, rb;
1189
1190 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
1191 if (x < src.width() - 1) {
1192 p += 1;
1193 }
1194 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1195
reed@android.com829c83c2009-06-08 12:05:31 +00001196 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001198 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 }
1200 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1201 if (x < src.width() - 1) {
1202 p += 1;
1203 }
1204 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1205
1206 *dst->getAddr32(x >> 1, y >> 1) =
1207 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1208}
1209
1210static inline uint32_t expand16(U16CPU c) {
1211 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1212}
1213
1214// returns dirt in the top 16bits, but we don't care, since we only
1215// store the low 16bits.
1216static inline U16CPU pack16(uint32_t c) {
1217 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1218}
1219
1220static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1221 const SkBitmap& src) {
1222 x <<= 1;
1223 y <<= 1;
1224 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001225 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001227
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001229 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 p += 1;
1231 }
1232 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001233
reed@android.com829c83c2009-06-08 12:05:31 +00001234 p = baseP;
1235 if (y < src.height() - 1) {
1236 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 }
1238 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001239 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 p += 1;
1241 }
1242 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001243
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1245}
1246
1247static uint32_t expand4444(U16CPU c) {
1248 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1249}
1250
1251static U16CPU collaps4444(uint32_t c) {
1252 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1253}
1254
1255static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1256 const SkBitmap& src) {
1257 x <<= 1;
1258 y <<= 1;
1259 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001260 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001262
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 c = expand4444(*p);
1264 if (x < src.width() - 1) {
1265 p += 1;
1266 }
1267 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001268
reed@android.com829c83c2009-06-08 12:05:31 +00001269 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001271 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 }
1273 c += expand4444(*p);
1274 if (x < src.width() - 1) {
1275 p += 1;
1276 }
1277 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001278
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1280}
1281
1282void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 if (forceRebuild)
1284 this->freeMipMap();
1285 else if (fMipMap)
1286 return; // we're already built
1287
1288 SkASSERT(NULL == fMipMap);
1289
1290 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1291
reed@google.com44699382013-10-31 17:28:30 +00001292 const SkBitmap::Config config = this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293
1294 switch (config) {
1295 case kARGB_8888_Config:
1296 proc = downsampleby2_proc32;
1297 break;
1298 case kRGB_565_Config:
1299 proc = downsampleby2_proc16;
1300 break;
1301 case kARGB_4444_Config:
1302 proc = downsampleby2_proc4444;
1303 break;
1304 case kIndex8_Config:
1305 case kA8_Config:
1306 default:
1307 return; // don't build mipmaps for these configs
1308 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001309
reed@android.com149e2f62009-05-22 14:39:03 +00001310 SkAutoLockPixels alp(*this);
1311 if (!this->readyToDraw()) {
1312 return;
1313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314
1315 // whip through our loop to compute the exact size needed
1316 size_t size = 0;
1317 int maxLevels = 0;
1318 {
reed@android.com149e2f62009-05-22 14:39:03 +00001319 int width = this->width();
1320 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 for (;;) {
1322 width >>= 1;
1323 height >>= 1;
1324 if (0 == width || 0 == height) {
1325 break;
1326 }
1327 size += ComputeRowBytes(config, width) * height;
1328 maxLevels += 1;
1329 }
1330 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001331
reed@android.com149e2f62009-05-22 14:39:03 +00001332 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 if (0 == maxLevels) {
1334 return;
1335 }
1336
reed@android.com149e2f62009-05-22 14:39:03 +00001337 SkBitmap srcBM(*this);
1338 srcBM.lockPixels();
1339 if (!srcBM.readyToDraw()) {
1340 return;
1341 }
1342
1343 MipMap* mm = MipMap::Alloc(maxLevels, size);
1344 if (NULL == mm) {
1345 return;
1346 }
1347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 MipLevel* level = mm->levels();
1349 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001350 int width = this->width();
1351 int height = this->height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001352 uint32_t rowBytes;
reed@android.com149e2f62009-05-22 14:39:03 +00001353 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354
1355 for (int i = 0; i < maxLevels; i++) {
1356 width >>= 1;
1357 height >>= 1;
scroggo@google.come5f48242013-02-25 21:47:41 +00001358 rowBytes = SkToU32(ComputeRowBytes(config, width));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359
1360 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001361 level[i].fWidth = width;
1362 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001363 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364
1365 dstBM.setConfig(config, width, height, rowBytes);
1366 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001367
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001368 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001369 for (int y = 0; y < height; y++) {
1370 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 proc(&dstBM, x, y, srcBM);
1372 }
1373 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001374 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375
1376 srcBM = dstBM;
1377 addr += height * rowBytes;
1378 }
1379 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1380 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381}
1382
1383bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385}
1386
1387int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001388 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001390 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001391
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392 int level = ComputeMipLevel(sx, sy) >> 16;
1393 SkASSERT(level >= 0);
1394 if (level <= 0) {
1395 return 0;
1396 }
1397
1398 if (level >= fMipMap->fLevelCount) {
1399 level = fMipMap->fLevelCount - 1;
1400 }
1401 if (dst) {
1402 const MipLevel& mip = fMipMap->levels()[level - 1];
1403 dst->setConfig((SkBitmap::Config)this->config(),
1404 mip.fWidth, mip.fHeight, mip.fRowBytes);
1405 dst->setPixels(mip.fPixels);
1406 }
1407 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408}
1409
1410SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 sx = SkAbs32(sx);
1412 sy = SkAbs32(sy);
1413 if (sx < sy) {
1414 sx = sy;
1415 }
1416 if (sx < SK_Fixed1) {
1417 return 0;
1418 }
1419 int clz = SkCLZ(sx);
1420 SkASSERT(clz >= 1 && clz <= 15);
1421 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422}
1423
1424///////////////////////////////////////////////////////////////////////////////
1425
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001426static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 int alphaRowBytes) {
1428 SkASSERT(alpha != NULL);
1429 SkASSERT(alphaRowBytes >= src.width());
1430
reed@google.com44699382013-10-31 17:28:30 +00001431 SkBitmap::Config config = src.config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 int w = src.width();
1433 int h = src.height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001434 size_t rb = src.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435
reed@android.com1cdcb512009-08-24 19:11:00 +00001436 SkAutoLockPixels alp(src);
1437 if (!src.readyToDraw()) {
1438 // zero out the alpha buffer and return
1439 while (--h >= 0) {
1440 memset(alpha, 0, w);
1441 alpha += alphaRowBytes;
1442 }
1443 return false;
1444 }
reed@google.com82065d62011-02-07 15:30:46 +00001445
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1447 const uint8_t* s = src.getAddr8(0, 0);
1448 while (--h >= 0) {
1449 memcpy(alpha, s, w);
1450 s += rb;
1451 alpha += alphaRowBytes;
1452 }
1453 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1454 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1455 while (--h >= 0) {
1456 for (int x = 0; x < w; x++) {
1457 alpha[x] = SkGetPackedA32(s[x]);
1458 }
1459 s = (const SkPMColor*)((const char*)s + rb);
1460 alpha += alphaRowBytes;
1461 }
1462 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1463 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1464 while (--h >= 0) {
1465 for (int x = 0; x < w; x++) {
1466 alpha[x] = SkPacked4444ToA32(s[x]);
1467 }
1468 s = (const SkPMColor16*)((const char*)s + rb);
1469 alpha += alphaRowBytes;
1470 }
1471 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1472 SkColorTable* ct = src.getColorTable();
1473 if (ct) {
1474 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1475 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1476 while (--h >= 0) {
1477 for (int x = 0; x < w; x++) {
1478 alpha[x] = SkGetPackedA32(table[s[x]]);
1479 }
1480 s += rb;
1481 alpha += alphaRowBytes;
1482 }
reed@google.com0a6151d2013-10-10 14:44:56 +00001483 ct->unlockColors();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 }
1485 } else { // src is opaque, so just fill alpha[] with 0xFF
1486 memset(alpha, 0xFF, h * alphaRowBytes);
1487 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001488 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
1491#include "SkPaint.h"
1492#include "SkMaskFilter.h"
1493#include "SkMatrix.h"
1494
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001495bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001496 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 SkDEBUGCODE(this->validate();)
1498
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001499 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 SkMatrix identity;
1501 SkMask srcM, dstM;
1502
1503 srcM.fBounds.set(0, 0, this->width(), this->height());
1504 srcM.fRowBytes = SkAlign4(this->width());
1505 srcM.fFormat = SkMask::kA8_Format;
1506
1507 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1508
1509 // compute our (larger?) dst bounds if we have a filter
1510 if (NULL != filter) {
1511 identity.reset();
1512 srcM.fImage = NULL;
1513 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1514 goto NO_FILTER_CASE;
1515 }
1516 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1517 } else {
1518 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001519 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001521 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1522 // Allocation of pixels for alpha bitmap failed.
1523 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1524 tmpBitmap.width(), tmpBitmap.height());
1525 return false;
1526 }
1527 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528 if (offset) {
1529 offset->set(0, 0);
1530 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001531 tmpBitmap.swap(*dst);
1532 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001534 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1535 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536
1537 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1538 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1539 goto NO_FILTER_CASE;
1540 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001541 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001543 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001545 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1546 // Allocation of pixels for alpha bitmap failed.
1547 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1548 tmpBitmap.width(), tmpBitmap.height());
1549 return false;
1550 }
1551 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552 if (offset) {
1553 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1554 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001555 SkDEBUGCODE(tmpBitmap.validate();)
1556
1557 tmpBitmap.swap(*dst);
1558 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559}
1560
1561///////////////////////////////////////////////////////////////////////////////
1562
1563enum {
1564 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001565 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566};
1567
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001569 buffer.writeInt(fWidth);
1570 buffer.writeInt(fHeight);
1571 buffer.writeInt(fRowBytes);
1572 buffer.writeInt(fConfig);
reed@google.com383a6972013-10-21 14:00:07 +00001573 buffer.writeInt(fAlphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001574
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001576 if (fPixelRef->getFactory()) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001577 buffer.writeInt(SERIALIZE_PIXELTYPE_REF_DATA);
scroggo@google.come5f48242013-02-25 21:47:41 +00001578 buffer.writeUInt(SkToU32(fPixelRefOffset));
djsollen@google.com5370cd92012-03-28 20:47:01 +00001579 buffer.writeFlattenable(fPixelRef);
1580 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581 }
1582 // if we get here, we can't record the pixels
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001583 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 } else {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001585 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 }
1587}
1588
1589void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1590 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001591
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592 int width = buffer.readInt();
1593 int height = buffer.readInt();
1594 int rowBytes = buffer.readInt();
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001595 Config config = (Config)buffer.readInt();
1596 SkAlphaType alphaType = (SkAlphaType)buffer.readInt();
1597 buffer.validate((width >= 0) && (height >= 0) && (rowBytes >= 0) &&
1598 SkIsValidConfig(config) && validate_alphaType(config, alphaType));
weita@google.comf9ab99a2009-05-03 18:23:30 +00001599
commit-bot@chromium.orgc2e9db32013-12-06 20:14:46 +00001600 bool configIsValid = this->setConfig(config, width, height, rowBytes, alphaType);
1601 // Note : Using (fRowBytes >= (fWidth * fBytesPerPixel)) in the following test can create false
1602 // positives if the multiplication causes an integer overflow. Use the division instead.
1603 buffer.validate(configIsValid && (fBytesPerPixel > 0) &&
1604 ((fRowBytes / fBytesPerPixel) >= fWidth));
weita@google.comf9ab99a2009-05-03 18:23:30 +00001605
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001606 int reftype = buffer.readInt();
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001607 if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) ||
1608 (SERIALIZE_PIXELTYPE_NONE == reftype))) {
1609 switch (reftype) {
1610 case SERIALIZE_PIXELTYPE_REF_DATA: {
1611 size_t offset = buffer.readUInt();
1612 SkPixelRef* pr = buffer.readPixelRef();
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +00001613 if (!buffer.validate((NULL == pr) ||
1614 (pr->getAllocatedSizeInBytes() >= (offset + this->getSafeSize())))) {
1615 offset = 0;
1616 }
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001617 SkSafeUnref(this->setPixelRef(pr, offset));
1618 break;
1619 }
1620 case SERIALIZE_PIXELTYPE_NONE:
1621 break;
1622 default:
1623 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
1624 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 }
1627}
1628
1629///////////////////////////////////////////////////////////////////////////////
1630
1631SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1632 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001633 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634}
1635
1636SkBitmap::RLEPixels::~RLEPixels() {
1637 sk_free(fYPtrs);
1638}
1639
1640///////////////////////////////////////////////////////////////////////////////
1641
1642#ifdef SK_DEBUG
1643void SkBitmap::validate() const {
1644 SkASSERT(fConfig < kConfigCount);
1645 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001646 uint8_t allFlags = kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag;
1647#ifdef SK_BUILD_FOR_ANDROID
1648 allFlags |= kHasHardwareMipMap_Flag;
1649#endif
1650 SkASSERT(fFlags <= allFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651 SkASSERT(fPixelLockCount >= 0);
1652 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1653 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1654
1655#if 0 // these asserts are not thread-correct, so disable for now
1656 if (fPixelRef) {
1657 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001658 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001659 } else {
1660 SkASSERT(NULL == fPixels);
1661 SkASSERT(NULL == fColorTable);
1662 }
1663 }
1664#endif
1665}
1666#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001667
1668#ifdef SK_DEVELOPER
1669void SkBitmap::toString(SkString* str) const {
1670
1671 static const char* gConfigNames[kConfigCount] = {
rmistry@google.comd6bab022013-12-02 13:50:38 +00001672 "NONE", "A8", "INDEX8", "565", "4444", "8888"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001673 };
1674
1675 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
1676 gConfigNames[this->config()]);
1677
1678 str->append(" (");
1679 if (this->isOpaque()) {
1680 str->append("opaque");
1681 } else {
1682 str->append("transparent");
1683 }
1684 if (this->isImmutable()) {
1685 str->append(", immutable");
1686 } else {
1687 str->append(", not-immutable");
1688 }
1689 str->append(")");
1690
1691 SkPixelRef* pr = this->pixelRef();
1692 if (NULL == pr) {
1693 // show null or the explicit pixel address (rare)
1694 str->appendf(" pixels:%p", this->getPixels());
1695 } else {
1696 const char* uri = pr->getURI();
1697 if (NULL != uri) {
1698 str->appendf(" uri:\"%s\"", uri);
1699 } else {
1700 str->appendf(" pixelref:%p", pr);
1701 }
1702 }
1703
1704 str->append(")");
1705}
1706#endif