blob: ee741c3df09f4cf0a96b87d17e24d2482d59dcbe [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.com48569642013-12-30 19:21:22 +0000193 // assign and then shift, so we don't overflow int
194 rowBytes = width;
195 rowBytes <<= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 break;
197 case kARGB_8888_Config:
reed@google.com48569642013-12-30 19:21:22 +0000198 // assign and then shift, so we don't overflow int
199 rowBytes = width;
200 rowBytes <<= 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 break;
202 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000203 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 break;
205 }
reed@google.com57212f92013-12-30 14:40:38 +0000206 return sk_64_isS32(rowBytes) ? sk_64_asS32(rowBytes) : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207}
208
reed@google.com57212f92013-12-30 14:40:38 +0000209int64_t SkBitmap::ComputeSize64(Config config, int width, int height) {
210 int64_t rowBytes = sk_64_mul(ComputeBytesPerPixel(config), width);
211 return rowBytes * height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212}
213
214size_t SkBitmap::ComputeSize(Config c, int width, int height) {
reed@google.com57212f92013-12-30 14:40:38 +0000215 int64_t size = SkBitmap::ComputeSize64(c, width, height);
216 return sk_64_isS32(size) ? sk_64_asS32(size) : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217}
218
reed@google.com57212f92013-12-30 14:40:38 +0000219int64_t SkBitmap::ComputeSafeSize64(Config config,
220 uint32_t width,
221 uint32_t height,
222 size_t rowBytes) {
223 int64_t safeSize = 0;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000224 if (height > 0) {
reed@google.com57212f92013-12-30 14:40:38 +0000225 int64_t lastRow = sk_64_mul(ComputeBytesPerPixel(config), width);
226 safeSize = sk_64_mul(height - 1, rowBytes) + lastRow;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000227 }
reed@google.com57212f92013-12-30 14:40:38 +0000228 SkASSERT(safeSize >= 0);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000229 return safeSize;
230}
231
232size_t SkBitmap::ComputeSafeSize(Config config,
233 uint32_t width,
234 uint32_t height,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000235 size_t rowBytes) {
reed@google.com57212f92013-12-30 14:40:38 +0000236 int64_t safeSize = ComputeSafeSize64(config, width, height, rowBytes);
237 int32_t safeSize32 = (int32_t)safeSize;
skia.committer@gmail.comf5e1f632013-12-31 07:01:36 +0000238
reed@google.com57212f92013-12-30 14:40:38 +0000239 if (safeSize32 != safeSize) {
240 safeSize32 = 0;
241 }
242 return safeSize32;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000243}
244
reed@google.com86b2e432012-03-15 21:17:03 +0000245void SkBitmap::getBounds(SkRect* bounds) const {
246 SkASSERT(bounds);
247 bounds->set(0, 0,
248 SkIntToScalar(fWidth), SkIntToScalar(fHeight));
249}
250
reed@google.com80e14592012-03-16 14:58:07 +0000251void SkBitmap::getBounds(SkIRect* bounds) const {
252 SkASSERT(bounds);
253 bounds->set(0, 0, fWidth, fHeight);
254}
255
reed@google.com86b2e432012-03-15 21:17:03 +0000256///////////////////////////////////////////////////////////////////////////////
257
reed@google.com383a6972013-10-21 14:00:07 +0000258static bool validate_alphaType(SkBitmap::Config config, SkAlphaType alphaType,
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +0000259 SkAlphaType* canonical = NULL) {
reed@google.com383a6972013-10-21 14:00:07 +0000260 switch (config) {
261 case SkBitmap::kNo_Config:
262 alphaType = kIgnore_SkAlphaType;
263 break;
reed@google.com383a6972013-10-21 14:00:07 +0000264 case SkBitmap::kA8_Config:
265 if (kUnpremul_SkAlphaType == alphaType) {
266 alphaType = kPremul_SkAlphaType;
267 }
268 // fall-through
269 case SkBitmap::kIndex8_Config:
270 case SkBitmap::kARGB_4444_Config:
271 case SkBitmap::kARGB_8888_Config:
272 if (kIgnore_SkAlphaType == alphaType) {
273 return false;
274 }
275 break;
276 case SkBitmap::kRGB_565_Config:
277 alphaType = kOpaque_SkAlphaType;
278 break;
rmistry@google.comd6bab022013-12-02 13:50:38 +0000279 default:
280 return false;
reed@android.com149e2f62009-05-22 14:39:03 +0000281 }
reed@google.com383a6972013-10-21 14:00:07 +0000282 if (canonical) {
283 *canonical = alphaType;
284 }
285 return true;
286}
reed@android.com149e2f62009-05-22 14:39:03 +0000287
reed@google.com383a6972013-10-21 14:00:07 +0000288bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
289 SkAlphaType alphaType) {
290 if ((width | height) < 0) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000291 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 if (rowBytes == 0) {
reed@google.com383a6972013-10-21 14:00:07 +0000294 rowBytes = SkBitmap::ComputeRowBytes(config, width);
halcanary@google.com44287342013-12-13 18:29:51 +0000295 if (0 == rowBytes && kNo_Config != config && width > 0) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000296 goto BAD_CONFIG;
reed@android.com149e2f62009-05-22 14:39:03 +0000297 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000299
reed@google.com383a6972013-10-21 14:00:07 +0000300 if (!validate_alphaType(config, alphaType, &alphaType)) {
reed@google.com9cd697c2013-10-21 14:12:13 +0000301 goto BAD_CONFIG;
reed@google.com383a6972013-10-21 14:00:07 +0000302 }
303
304 this->freePixels();
305
306 fConfig = SkToU8(config);
307 fAlphaType = SkToU8(alphaType);
reed@android.comf459a492009-03-27 12:33:50 +0000308 fWidth = width;
309 fHeight = height;
scroggo@google.come5f48242013-02-25 21:47:41 +0000310 fRowBytes = SkToU32(rowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311
reed@google.com383a6972013-10-21 14:00:07 +0000312 fBytesPerPixel = (uint8_t)ComputeBytesPerPixel(config);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313
314 SkDEBUGCODE(this->validate();)
reed@google.com383a6972013-10-21 14:00:07 +0000315 return true;
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000316
reed@android.com9e0c2fc2009-06-02 18:54:28 +0000317 // if we got here, we had an error, so we reset the bitmap to empty
reed@google.com9cd697c2013-10-21 14:12:13 +0000318BAD_CONFIG:
agl@chromium.org6b8cb252009-06-01 23:52:37 +0000319 this->reset();
reed@google.com383a6972013-10-21 14:00:07 +0000320 return false;
321}
322
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +0000323bool SkBitmap::setConfig(const SkImageInfo& info, size_t rowBytes) {
324 return this->setConfig(SkImageInfoToBitmapConfig(info), info.fWidth,
325 info.fHeight, rowBytes, info.fAlphaType);
326}
327
reed@google.com383a6972013-10-21 14:00:07 +0000328bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
329 if (!validate_alphaType(this->config(), alphaType, &alphaType)) {
330 return false;
331 }
332 fAlphaType = SkToU8(alphaType);
333 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334}
335
336void SkBitmap::updatePixelsFromRef() const {
337 if (NULL != fPixelRef) {
338 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000339 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 void* p = fPixelRef->pixels();
342 if (NULL != p) {
343 p = (char*)p + fPixelRefOffset;
344 }
345 fPixels = p;
346 SkRefCnt_SafeAssign(fColorTable, fPixelRef->colorTable());
347 } else {
348 SkASSERT(0 == fPixelLockCount);
349 fPixels = NULL;
reed@android.com149e2f62009-05-22 14:39:03 +0000350 if (fColorTable) {
351 fColorTable->unref();
352 fColorTable = NULL;
353 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 }
355 }
356}
357
reed@google.com9230ea22013-12-09 22:01:03 +0000358static bool config_to_colorType(SkBitmap::Config config, SkColorType* ctOut) {
359 SkColorType ct;
360 switch (config) {
361 case SkBitmap::kA8_Config:
362 ct = kAlpha_8_SkColorType;
363 break;
364 case SkBitmap::kIndex8_Config:
365 ct = kIndex_8_SkColorType;
366 break;
367 case SkBitmap::kRGB_565_Config:
368 ct = kRGB_565_SkColorType;
369 break;
370 case SkBitmap::kARGB_4444_Config:
371 ct = kARGB_4444_SkColorType;
372 break;
373 case SkBitmap::kARGB_8888_Config:
374 ct = kPMColor_SkColorType;
375 break;
376 case SkBitmap::kNo_Config:
377 default:
378 return false;
379 }
380 if (ctOut) {
381 *ctOut = ct;
382 }
383 return true;
384}
385
386bool SkBitmap::asImageInfo(SkImageInfo* info) const {
387 SkColorType ct;
388 if (!config_to_colorType(this->config(), &ct)) {
389 return false;
390 }
391 if (info) {
392 info->fWidth = fWidth;
393 info->fHeight = fHeight;
394 info->fAlphaType = this->alphaType();
395 info->fColorType = ct;
396 }
397 return true;
398}
399
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) {
401 // do this first, we that we never have a non-zero offset with a null ref
402 if (NULL == pr) {
403 offset = 0;
404 }
405
406 if (fPixelRef != pr || fPixelRefOffset != offset) {
407 if (fPixelRef != pr) {
408 this->freePixels();
409 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000410
reed@google.com82065d62011-02-07 15:30:46 +0000411 SkSafeRef(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 fPixelRef = pr;
413 }
414 fPixelRefOffset = offset;
415 this->updatePixelsFromRef();
416 }
417
418 SkDEBUGCODE(this->validate();)
419 return pr;
420}
421
422void SkBitmap::lockPixels() const {
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000423 if (NULL != fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 fPixelRef->lockPixels();
425 this->updatePixelsFromRef();
426 }
427 SkDEBUGCODE(this->validate();)
428}
429
430void SkBitmap::unlockPixels() const {
431 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
432
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000433 if (NULL != fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 fPixelRef->unlockPixels();
435 this->updatePixelsFromRef();
436 }
437 SkDEBUGCODE(this->validate();)
438}
439
reed@google.com9c49bc32011-07-07 13:42:37 +0000440bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000441 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000442}
443
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000445 if (NULL == p) {
446 this->setPixelRef(NULL, 0);
447 return;
448 }
449
reed@google.combf790232013-12-13 19:45:58 +0000450 SkImageInfo info;
451 if (!this->asImageInfo(&info)) {
452 this->setPixelRef(NULL, 0);
453 return;
454 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455
reed@google.combf790232013-12-13 19:45:58 +0000456 SkPixelRef* pr = SkMallocPixelRef::NewDirect(info, p, fRowBytes, ctable);
457 if (NULL == pr) {
458 this->setPixelRef(NULL, 0);
459 return;
460 }
461
462 this->setPixelRef(pr)->unref();
463
djsollen@google.comc84b8332012-07-27 13:41:44 +0000464 // since we're already allocated, we lockPixels right away
465 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 SkDEBUGCODE(this->validate();)
467}
468
469bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
470 HeapAllocator stdalloc;
471
472 if (NULL == allocator) {
473 allocator = &stdalloc;
474 }
475 return allocator->allocPixelRef(this, ctable);
476}
477
478void SkBitmap::freePixels() {
479 // if we're gonna free the pixels, we certainly need to free the mipmap
480 this->freeMipMap();
481
reed@android.com149e2f62009-05-22 14:39:03 +0000482 if (fColorTable) {
483 fColorTable->unref();
484 fColorTable = NULL;
485 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486
487 if (NULL != fPixelRef) {
488 if (fPixelLockCount > 0) {
489 fPixelRef->unlockPixels();
490 }
491 fPixelRef->unref();
492 fPixelRef = NULL;
493 fPixelRefOffset = 0;
494 }
495 fPixelLockCount = 0;
496 fPixels = NULL;
497}
498
499void SkBitmap::freeMipMap() {
reed@android.com149e2f62009-05-22 14:39:03 +0000500 if (fMipMap) {
501 fMipMap->unref();
502 fMipMap = NULL;
503 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504}
505
506uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000507 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508}
509
510void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000511 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 if (fPixelRef) {
513 fPixelRef->notifyPixelsChanged();
514 }
515}
516
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000517GrTexture* SkBitmap::getTexture() const {
reed@android.comce4e53a2010-09-09 16:01:26 +0000518 return fPixelRef ? fPixelRef->getTexture() : NULL;
519}
520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521///////////////////////////////////////////////////////////////////////////////
522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523/** We explicitly use the same allocator for our pixels that SkMask does,
524 so that we can freely assign memory allocated by one class to the other.
525 */
526bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
527 SkColorTable* ctable) {
reed@google.combf790232013-12-13 19:45:58 +0000528 SkImageInfo info;
529 if (!dst->asImageInfo(&info)) {
530// SkDebugf("unsupported config for info %d\n", dst->config());
531 return false;
532 }
skia.committer@gmail.com96f5fa02013-12-16 07:01:40 +0000533
reed@google.combf790232013-12-13 19:45:58 +0000534 SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(),
535 ctable);
536 if (NULL == pr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 return false;
538 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000539
reed@google.combf790232013-12-13 19:45:58 +0000540 dst->setPixelRef(pr, 0)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 // since we're already allocated, we lockPixels right away
542 dst->lockPixels();
543 return true;
544}
545
546///////////////////////////////////////////////////////////////////////////////
547
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000548size_t SkBitmap::getSafeSize() const {
549 // This is intended to be a size_t version of ComputeSafeSize64(), just
550 // faster. The computation is meant to be identical.
551 return (fHeight ? ((fHeight - 1) * fRowBytes) +
reed@google.com44699382013-10-31 17:28:30 +0000552 ComputeRowBytes(this->config(), fWidth): 0);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000553}
554
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000555bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000556 size_t dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000557
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000558 if (0 == dstRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000559 dstRowBytes = fRowBytes;
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000560 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000561
reed@google.com44699382013-10-31 17:28:30 +0000562 if (dstRowBytes < ComputeRowBytes(this->config(), fWidth) ||
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000563 dst == NULL || (getPixels() == NULL && pixelRef() == NULL))
564 return false;
565
bsalomon@google.comc6980972011-11-02 19:57:21 +0000566 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
reed@google.com44699382013-10-31 17:28:30 +0000567 size_t safeSize = this->getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000568 if (safeSize > dstSize || safeSize == 0)
569 return false;
570 else {
571 SkAutoLockPixels lock(*this);
572 // This implementation will write bytes beyond the end of each row,
573 // excluding the last row, if the bitmap's stride is greater than
574 // strictly required by the current config.
575 memcpy(dst, getPixels(), safeSize);
576
577 return true;
578 }
579 } else {
580 // If destination has different stride than us, then copy line by line.
reed@google.com44699382013-10-31 17:28:30 +0000581 if (ComputeSafeSize(this->config(), fWidth, fHeight, dstRowBytes) >
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000582 dstSize)
583 return false;
584 else {
585 // Just copy what we need on each line.
reed@google.com44699382013-10-31 17:28:30 +0000586 size_t rowBytes = ComputeRowBytes(this->config(), fWidth);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000587 SkAutoLockPixels lock(*this);
588 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
589 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
590 for (uint32_t row = 0; row < fHeight;
591 row++, srcP += fRowBytes, dstP += dstRowBytes) {
592 memcpy(dstP, srcP, rowBytes);
593 }
594
595 return true;
596 }
597 }
598}
599
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000600///////////////////////////////////////////////////////////////////////////////
601
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000602bool SkBitmap::isImmutable() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000603 return fPixelRef ? fPixelRef->isImmutable() :
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000604 fFlags & kImageIsImmutable_Flag;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000605}
606
607void SkBitmap::setImmutable() {
608 if (fPixelRef) {
609 fPixelRef->setImmutable();
610 } else {
611 fFlags |= kImageIsImmutable_Flag;
612 }
613}
614
junov@google.com4ee7ae52011-06-30 17:30:49 +0000615bool SkBitmap::isVolatile() const {
616 return (fFlags & kImageIsVolatile_Flag) != 0;
617}
618
619void SkBitmap::setIsVolatile(bool isVolatile) {
620 if (isVolatile) {
621 fFlags |= kImageIsVolatile_Flag;
622 } else {
623 fFlags &= ~kImageIsVolatile_Flag;
624 }
625}
626
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627void* SkBitmap::getAddr(int x, int y) const {
628 SkASSERT((unsigned)x < (unsigned)this->width());
629 SkASSERT((unsigned)y < (unsigned)this->height());
630
631 char* base = (char*)this->getPixels();
632 if (base) {
633 base += y * this->rowBytes();
634 switch (this->config()) {
635 case SkBitmap::kARGB_8888_Config:
636 base += x << 2;
637 break;
638 case SkBitmap::kARGB_4444_Config:
639 case SkBitmap::kRGB_565_Config:
640 base += x << 1;
641 break;
642 case SkBitmap::kA8_Config:
643 case SkBitmap::kIndex8_Config:
644 base += x;
645 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000648 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 base = NULL;
650 break;
651 }
652 }
653 return base;
654}
655
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000656SkColor SkBitmap::getColor(int x, int y) const {
657 SkASSERT((unsigned)x < (unsigned)this->width());
658 SkASSERT((unsigned)y < (unsigned)this->height());
659
660 switch (this->config()) {
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000661 case SkBitmap::kA8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000662 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000663 return SkColorSetA(0, addr[0]);
664 }
665 case SkBitmap::kIndex8_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000666 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000667 return SkUnPreMultiply::PMColorToColor(c);
668 }
669 case SkBitmap::kRGB_565_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 return SkPixel16ToColor(addr[0]);
672 }
673 case SkBitmap::kARGB_4444_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000674 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000675 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
676 return SkUnPreMultiply::PMColorToColor(c);
677 }
678 case SkBitmap::kARGB_8888_Config: {
reed@google.com3b521d02011-04-29 11:53:41 +0000679 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000680 return SkUnPreMultiply::PMColorToColor(addr[0]);
681 }
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000682 case kNo_Config:
rmistry@google.comd6bab022013-12-02 13:50:38 +0000683 default:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000684 SkASSERT(false);
685 return 0;
686 }
687 SkASSERT(false); // Not reached.
688 return 0;
689}
690
reed@google.com2a7579d2012-11-07 18:30:18 +0000691bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
692 SkAutoLockPixels alp(bm);
693 if (!bm.getPixels()) {
694 return false;
695 }
696
697 const int height = bm.height();
698 const int width = bm.width();
699
700 switch (bm.config()) {
reed@google.com2a7579d2012-11-07 18:30:18 +0000701 case SkBitmap::kA8_Config: {
702 unsigned a = 0xFF;
703 for (int y = 0; y < height; ++y) {
704 const uint8_t* row = bm.getAddr8(0, y);
705 for (int x = 0; x < width; ++x) {
706 a &= row[x];
707 }
708 if (0xFF != a) {
709 return false;
710 }
711 }
712 return true;
713 } break;
reed@google.com2a7579d2012-11-07 18:30:18 +0000714 case SkBitmap::kIndex8_Config: {
715 SkAutoLockColors alc(bm);
716 const SkPMColor* table = alc.colors();
717 if (!table) {
718 return false;
719 }
reed@google.com140d7282013-01-07 20:25:04 +0000720 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000721 for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
722 c &= table[i];
723 }
724 return 0xFF == SkGetPackedA32(c);
725 } break;
726 case SkBitmap::kRGB_565_Config:
727 return true;
728 break;
729 case SkBitmap::kARGB_4444_Config: {
730 unsigned c = 0xFFFF;
731 for (int y = 0; y < height; ++y) {
732 const SkPMColor16* row = bm.getAddr16(0, y);
733 for (int x = 0; x < width; ++x) {
734 c &= row[x];
735 }
736 if (0xF != SkGetPackedA4444(c)) {
737 return false;
738 }
739 }
740 return true;
741 } break;
742 case SkBitmap::kARGB_8888_Config: {
reed@google.com140d7282013-01-07 20:25:04 +0000743 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000744 for (int y = 0; y < height; ++y) {
745 const SkPMColor* row = bm.getAddr32(0, y);
746 for (int x = 0; x < width; ++x) {
747 c &= row[x];
748 }
749 if (0xFF != SkGetPackedA32(c)) {
750 return false;
751 }
752 }
753 return true;
754 }
755 default:
756 break;
757 }
758 return false;
759}
760
761
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762///////////////////////////////////////////////////////////////////////////////
763///////////////////////////////////////////////////////////////////////////////
764
reed@google.com45f746f2013-06-21 19:51:31 +0000765static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
766 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
767 (SkR32To4444(r) << SK_R4444_SHIFT) |
768 (SkG32To4444(g) << SK_G4444_SHIFT) |
769 (SkB32To4444(b) << SK_B4444_SHIFT);
770 return SkToU16(pixel);
771}
772
reed@google.com60d32352013-06-28 19:40:50 +0000773void SkBitmap::internalErase(const SkIRect& area,
774 U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
775#ifdef SK_DEBUG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 SkDEBUGCODE(this->validate();)
reed@google.com60d32352013-06-28 19:40:50 +0000777 SkASSERT(!area.isEmpty());
778 {
reed@google.com92833f92013-06-28 19:47:43 +0000779 SkIRect total = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000780 SkASSERT(total.contains(area));
781 }
782#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783
reed@google.com60d32352013-06-28 19:40:50 +0000784 if (kNo_Config == fConfig || kIndex8_Config == fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 return;
786 }
787
788 SkAutoLockPixels alp(*this);
789 // perform this check after the lock call
790 if (!this->readyToDraw()) {
791 return;
792 }
793
reed@google.com60d32352013-06-28 19:40:50 +0000794 int height = area.height();
795 const int width = area.width();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 const int rowBytes = fRowBytes;
797
798 // make rgb premultiplied
799 if (255 != a) {
800 r = SkAlphaMul(r, a);
801 g = SkAlphaMul(g, a);
802 b = SkAlphaMul(b, a);
803 }
804
805 switch (fConfig) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 case kA8_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000807 uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 while (--height >= 0) {
809 memset(p, a, width);
810 p += rowBytes;
811 }
812 break;
813 }
reed@google.com45f746f2013-06-21 19:51:31 +0000814 case kARGB_4444_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 case kRGB_565_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000816 uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
reed@google.com45f746f2013-06-21 19:51:31 +0000817 uint16_t v;
skia.committer@gmail.com020b25b2013-06-22 07:00:58 +0000818
reed@google.com45f746f2013-06-21 19:51:31 +0000819 if (kARGB_4444_Config == fConfig) {
820 v = pack_8888_to_4444(a, r, g, b);
821 } else {
822 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
823 g >> (8 - SK_G16_BITS),
824 b >> (8 - SK_B16_BITS));
825 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 while (--height >= 0) {
827 sk_memset16(p, v, width);
828 p = (uint16_t*)((char*)p + rowBytes);
829 }
830 break;
831 }
832 case kARGB_8888_Config: {
reed@google.com60d32352013-06-28 19:40:50 +0000833 uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 uint32_t v = SkPackARGB32(a, r, g, b);
835
836 while (--height >= 0) {
837 sk_memset32(p, v, width);
838 p = (uint32_t*)((char*)p + rowBytes);
839 }
840 break;
841 }
842 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000843
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 this->notifyPixelsChanged();
845}
846
reed@google.com60d32352013-06-28 19:40:50 +0000847void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
reed@google.com92833f92013-06-28 19:47:43 +0000848 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000849 if (!area.isEmpty()) {
850 this->internalErase(area, a, r, g, b);
851 }
852}
853
854void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
reed@google.com92833f92013-06-28 19:47:43 +0000855 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000856 if (area.intersect(rect)) {
857 this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
858 SkColorGetG(c), SkColorGetB(c));
859 }
860}
861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862//////////////////////////////////////////////////////////////////////////////////////
863//////////////////////////////////////////////////////////////////////////////////////
864
865#define SUB_OFFSET_FAILURE ((size_t)-1)
866
scroggo@google.coma2a31922012-12-07 19:14:45 +0000867/**
868 * Based on the Config and rowBytes() of bm, return the offset into an SkPixelRef of the pixel at
869 * (x, y).
870 * Note that the SkPixelRef does not need to be set yet. deepCopyTo takes advantage of this fact.
871 * Also note that (x, y) may be outside the range of (0 - width(), 0 - height()), so long as it is
872 * within the bounds of the SkPixelRef being used.
873 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000874static size_t get_sub_offset(const SkBitmap& bm, int x, int y) {
reed@google.com44699382013-10-31 17:28:30 +0000875 switch (bm.config()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 case SkBitmap::kA8_Config:
877 case SkBitmap:: kIndex8_Config:
878 // x is fine as is for the calculation
879 break;
880
881 case SkBitmap::kRGB_565_Config:
882 case SkBitmap::kARGB_4444_Config:
883 x <<= 1;
884 break;
885
886 case SkBitmap::kARGB_8888_Config:
887 x <<= 2;
888 break;
889
890 case SkBitmap::kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 default:
892 return SUB_OFFSET_FAILURE;
893 }
894 return y * bm.rowBytes() + x;
895}
896
scroggo@google.coma2a31922012-12-07 19:14:45 +0000897/**
898 * Using the pixelRefOffset(), rowBytes(), and Config of bm, determine the (x, y) coordinate of the
899 * upper left corner of bm relative to its SkPixelRef.
900 * x and y must be non-NULL.
901 */
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000902bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com61d6c9e2013-05-21 20:38:40 +0000903 int32_t* x, int32_t* y);
904bool get_upper_left_from_offset(SkBitmap::Config config, size_t offset, size_t rowBytes,
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000905 int32_t* x, int32_t* y) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000906 SkASSERT(x != NULL && y != NULL);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000907 if (0 == offset) {
908 *x = *y = 0;
909 return true;
910 }
911 // Use integer division to find the correct y position.
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000912 // The remainder will be the x position, after we reverse get_sub_offset.
commit-bot@chromium.org2c86fbb2013-09-26 19:22:54 +0000913 SkTDivMod(offset, rowBytes, y, x);
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000914 switch (config) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000915 case SkBitmap::kA8_Config:
916 // Fall through.
917 case SkBitmap::kIndex8_Config:
918 // x is unmodified
919 break;
920
921 case SkBitmap::kRGB_565_Config:
922 // Fall through.
923 case SkBitmap::kARGB_4444_Config:
924 *x >>= 1;
925 break;
926
927 case SkBitmap::kARGB_8888_Config:
928 *x >>= 2;
929 break;
930
931 case SkBitmap::kNo_Config:
932 // Fall through.
scroggo@google.coma2a31922012-12-07 19:14:45 +0000933 default:
934 return false;
935 }
936 return true;
937}
938
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000939static bool get_upper_left_from_offset(const SkBitmap& bm, int32_t* x, int32_t* y) {
940 return get_upper_left_from_offset(bm.config(), bm.pixelRefOffset(), bm.rowBytes(), x, y);
941}
942
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
944 SkDEBUGCODE(this->validate();)
945
djsollen@google.comc84b8332012-07-27 13:41:44 +0000946 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 return false; // no src pixels
948 }
949
950 SkIRect srcRect, r;
951 srcRect.set(0, 0, this->width(), this->height());
952 if (!r.intersect(srcRect, subset)) {
953 return false; // r is empty (i.e. no intersection)
954 }
955
scroggo@google.coma2a31922012-12-07 19:14:45 +0000956 if (fPixelRef->getTexture() != NULL) {
957 // Do a deep copy
958 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->config(), &subset);
959 if (pixelRef != NULL) {
960 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000961 dst.setConfig(this->config(), subset.width(), subset.height(), 0,
962 this->alphaType());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000963 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000964 dst.setPixelRef(pixelRef)->unref();
965 SkDEBUGCODE(dst.validate());
966 result->swap(dst);
967 return true;
968 }
969 }
970
scroggo@google.coma2a31922012-12-07 19:14:45 +0000971 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
972 // exited above.
973 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
974 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
975
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000976 size_t offset = get_sub_offset(*this, r.fLeft, r.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 if (SUB_OFFSET_FAILURE == offset) {
978 return false; // config not supported
979 }
980
981 SkBitmap dst;
reed@google.com383a6972013-10-21 14:00:07 +0000982 dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes(),
983 this->alphaType());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000984 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985
986 if (fPixelRef) {
987 // share the pixelref with a custom offset
988 dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 }
990 SkDEBUGCODE(dst.validate();)
991
992 // we know we're good, so commit to result
993 result->swap(dst);
994 return true;
995}
996
997///////////////////////////////////////////////////////////////////////////////
998
999#include "SkCanvas.h"
1000#include "SkPaint.h"
1001
reed@android.comfbaa88d2009-05-06 17:44:34 +00001002bool SkBitmap::canCopyTo(Config dstConfig) const {
reed@google.com44699382013-10-31 17:28:30 +00001003 if (this->config() == kNo_Config) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 return false;
1005 }
1006
reed@android.comfbaa88d2009-05-06 17:44:34 +00001007 bool sameConfigs = (this->config() == dstConfig);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 switch (dstConfig) {
1009 case kA8_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 case kRGB_565_Config:
1011 case kARGB_8888_Config:
1012 break;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001013 case kIndex8_Config:
1014 if (!sameConfigs) {
1015 return false;
1016 }
1017 break;
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001018 case kARGB_4444_Config:
1019 return sameConfigs || kARGB_8888_Config == this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 default:
1021 return false;
1022 }
reed@android.comfbaa88d2009-05-06 17:44:34 +00001023 return true;
1024}
1025
1026bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const {
1027 if (!this->canCopyTo(dstConfig)) {
1028 return false;
1029 }
1030
reed@google.com50dfa012011-04-01 19:05:36 +00001031 // if we have a texture, first get those pixels
1032 SkBitmap tmpSrc;
1033 const SkBitmap* src = this;
1034
scroggo@google.coma2a31922012-12-07 19:14:45 +00001035 if (fPixelRef) {
1036 SkIRect subset;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001037 if (get_upper_left_from_offset(*this, &subset.fLeft, &subset.fTop)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001038 subset.fRight = subset.fLeft + fWidth;
1039 subset.fBottom = subset.fTop + fHeight;
1040 if (fPixelRef->readPixels(&tmpSrc, &subset)) {
1041 SkASSERT(tmpSrc.width() == this->width());
1042 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +00001043
scroggo@google.coma2a31922012-12-07 19:14:45 +00001044 // did we get lucky and we can just return tmpSrc?
1045 if (tmpSrc.config() == dstConfig && NULL == alloc) {
1046 dst->swap(tmpSrc);
1047 if (dst->pixelRef() && this->config() == dstConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001048 // TODO(scroggo): fix issue 1742
1049 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001050 }
1051 return true;
1052 }
1053
1054 // fall through to the raster case
1055 src = &tmpSrc;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001056 }
reed@google.com50dfa012011-04-01 19:05:36 +00001057 }
reed@android.comfbaa88d2009-05-06 17:44:34 +00001058 }
reed@android.com311c82d2009-05-05 23:13:23 +00001059
reed@google.com50dfa012011-04-01 19:05:36 +00001060 // we lock this now, since we may need its colortable
1061 SkAutoLockPixels srclock(*src);
1062 if (!src->readyToDraw()) {
1063 return false;
1064 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001065
reed@google.com50dfa012011-04-01 19:05:36 +00001066 SkBitmap tmpDst;
reed@google.com383a6972013-10-21 14:00:07 +00001067 tmpDst.setConfig(dstConfig, src->width(), src->height(), 0,
1068 src->alphaType());
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001069
weita@google.comf9ab99a2009-05-03 18:23:30 +00001070 // allocate colortable if srcConfig == kIndex8_Config
1071 SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
reed@google.com50dfa012011-04-01 19:05:36 +00001072 new SkColorTable(*src->getColorTable()) : NULL;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001073 SkAutoUnref au(ctable);
reed@google.com50dfa012011-04-01 19:05:36 +00001074 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +00001075 return false;
1076 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001077
reed@google.com50dfa012011-04-01 19:05:36 +00001078 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 // allocator/lock failed
1080 return false;
1081 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001082
reed@android.comfbaa88d2009-05-06 17:44:34 +00001083 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +00001084 */
reed@google.com50dfa012011-04-01 19:05:36 +00001085 if (src->config() == dstConfig) {
1086 if (tmpDst.getSize() == src->getSize()) {
1087 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
scroggo@google.comd5764e82012-08-22 15:00:05 +00001088 SkPixelRef* pixelRef = tmpDst.pixelRef();
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001089 if (NULL != pixelRef && NULL != fPixelRef) {
1090 // TODO(scroggo): fix issue 1742
1091 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.comd5764e82012-08-22 15:00:05 +00001092 }
reed@android.com311c82d2009-05-05 23:13:23 +00001093 } else {
reed@google.com50dfa012011-04-01 19:05:36 +00001094 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
1095 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +00001096 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +00001097 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
1098 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +00001099 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +00001100 srcP += src->rowBytes();
1101 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +00001102 }
1103 }
scroggo@google.com8dc8bc52013-08-07 19:16:05 +00001104 } else if (SkBitmap::kARGB_4444_Config == dstConfig
1105 && SkBitmap::kARGB_8888_Config == src->config()) {
1106 SkASSERT(src->height() == tmpDst.height());
1107 SkASSERT(src->width() == tmpDst.width());
1108 for (int y = 0; y < src->height(); ++y) {
1109 SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*) tmpDst.getAddr16(0, y);
1110 SkPMColor* SK_RESTRICT srcRow = (SkPMColor*) src->getAddr32(0, y);
1111 DITHER_4444_SCAN(y);
1112 for (int x = 0; x < src->width(); ++x) {
1113 dstRow[x] = SkDitherARGB32To4444(srcRow[x],
1114 DITHER_VALUE(x));
1115 }
1116 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001117 } else {
robertphillips@google.com0197b322013-10-10 15:48:16 +00001118 // Always clear the dest in case one of the blitters accesses it
1119 // TODO: switch the allocation of tmpDst to call sk_calloc_throw
1120 tmpDst.eraseColor(SK_ColorTRANSPARENT);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001121
reed@google.com50dfa012011-04-01 19:05:36 +00001122 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001123 SkPaint paint;
1124
1125 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +00001126 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 }
1128
reed@google.com50dfa012011-04-01 19:05:36 +00001129 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 return true;
1131}
1132
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001133bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const {
1134 if (!this->canCopyTo(dstConfig)) {
1135 return false;
1136 }
1137
1138 // If we have a PixelRef, and it supports deep copy, use it.
1139 // Currently supported only by texture-backed bitmaps.
1140 if (fPixelRef) {
1141 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
1142 if (pixelRef) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001143 uint32_t rowBytes;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001144 if (dstConfig == fConfig) {
commit-bot@chromium.org50a30432013-10-24 17:44:27 +00001145 // TODO(scroggo): fix issue 1742
1146 pixelRef->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001147 // Use the same rowBytes as the original.
1148 rowBytes = fRowBytes;
1149 } else {
1150 // With the new config, an appropriate fRowBytes will be computed by setConfig.
1151 rowBytes = 0;
scroggo@google.comd5764e82012-08-22 15:00:05 +00001152 }
scroggo@google.coma2a31922012-12-07 19:14:45 +00001153 dst->setConfig(dstConfig, fWidth, fHeight, rowBytes);
1154
1155 size_t pixelRefOffset;
1156 if (0 == fPixelRefOffset || dstConfig == fConfig) {
1157 // Use the same offset as the original.
1158 pixelRefOffset = fPixelRefOffset;
1159 } else {
1160 // Find the correct offset in the new config. This needs to be done after calling
1161 // setConfig so dst's fConfig and fRowBytes have been set properly.
scroggo@google.come5f48242013-02-25 21:47:41 +00001162 int32_t x, y;
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001163 if (!get_upper_left_from_offset(*this, &x, &y)) {
scroggo@google.coma2a31922012-12-07 19:14:45 +00001164 return false;
1165 }
scroggo@google.com1b1bcc32013-05-21 20:31:23 +00001166 pixelRefOffset = get_sub_offset(*dst, x, y);
scroggo@google.coma2a31922012-12-07 19:14:45 +00001167 if (SUB_OFFSET_FAILURE == pixelRefOffset) {
1168 return false;
1169 }
1170 }
1171 dst->setPixelRef(pixelRef, pixelRefOffset)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001172 return true;
1173 }
1174 }
1175
1176 if (this->getTexture()) {
1177 return false;
1178 } else {
1179 return this->copyTo(dst, dstConfig, NULL);
1180 }
1181}
1182
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183///////////////////////////////////////////////////////////////////////////////
1184///////////////////////////////////////////////////////////////////////////////
1185
1186static void downsampleby2_proc32(SkBitmap* dst, int x, int y,
1187 const SkBitmap& src) {
1188 x <<= 1;
1189 y <<= 1;
1190 const SkPMColor* p = src.getAddr32(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001191 const SkPMColor* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 SkPMColor c, ag, rb;
1193
1194 c = *p; ag = (c >> 8) & 0xFF00FF; rb = c & 0xFF00FF;
1195 if (x < src.width() - 1) {
1196 p += 1;
1197 }
1198 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1199
reed@android.com829c83c2009-06-08 12:05:31 +00001200 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001202 p += src.rowBytes() >> 2;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 }
1204 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1205 if (x < src.width() - 1) {
1206 p += 1;
1207 }
1208 c = *p; ag += (c >> 8) & 0xFF00FF; rb += c & 0xFF00FF;
1209
1210 *dst->getAddr32(x >> 1, y >> 1) =
1211 ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
1212}
1213
1214static inline uint32_t expand16(U16CPU c) {
1215 return (c & ~SK_G16_MASK_IN_PLACE) | ((c & SK_G16_MASK_IN_PLACE) << 16);
1216}
1217
1218// returns dirt in the top 16bits, but we don't care, since we only
1219// store the low 16bits.
1220static inline U16CPU pack16(uint32_t c) {
1221 return (c & ~SK_G16_MASK_IN_PLACE) | ((c >> 16) & SK_G16_MASK_IN_PLACE);
1222}
1223
1224static void downsampleby2_proc16(SkBitmap* dst, int x, int y,
1225 const SkBitmap& src) {
1226 x <<= 1;
1227 y <<= 1;
1228 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001229 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 SkPMColor c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001231
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 c = expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001233 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234 p += 1;
1235 }
1236 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001237
reed@android.com829c83c2009-06-08 12:05:31 +00001238 p = baseP;
1239 if (y < src.height() - 1) {
1240 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 }
1242 c += expand16(*p);
reed@android.com829c83c2009-06-08 12:05:31 +00001243 if (x < src.width() - 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 p += 1;
1245 }
1246 c += expand16(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001247
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)pack16(c >> 2);
1249}
1250
1251static uint32_t expand4444(U16CPU c) {
1252 return (c & 0xF0F) | ((c & ~0xF0F) << 12);
1253}
1254
1255static U16CPU collaps4444(uint32_t c) {
1256 return (c & 0xF0F) | ((c >> 12) & ~0xF0F);
1257}
1258
1259static void downsampleby2_proc4444(SkBitmap* dst, int x, int y,
1260 const SkBitmap& src) {
1261 x <<= 1;
1262 y <<= 1;
1263 const uint16_t* p = src.getAddr16(x, y);
reed@android.com829c83c2009-06-08 12:05:31 +00001264 const uint16_t* baseP = p;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 uint32_t c;
weita@google.comf9ab99a2009-05-03 18:23:30 +00001266
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 c = expand4444(*p);
1268 if (x < src.width() - 1) {
1269 p += 1;
1270 }
1271 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001272
reed@android.com829c83c2009-06-08 12:05:31 +00001273 p = baseP;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 if (y < src.height() - 1) {
reed@android.com829c83c2009-06-08 12:05:31 +00001275 p += src.rowBytes() >> 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 }
1277 c += expand4444(*p);
1278 if (x < src.width() - 1) {
1279 p += 1;
1280 }
1281 c += expand4444(*p);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 *dst->getAddr16(x >> 1, y >> 1) = (uint16_t)collaps4444(c >> 2);
1284}
1285
1286void SkBitmap::buildMipMap(bool forceRebuild) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 if (forceRebuild)
1288 this->freeMipMap();
1289 else if (fMipMap)
1290 return; // we're already built
1291
1292 SkASSERT(NULL == fMipMap);
1293
1294 void (*proc)(SkBitmap* dst, int x, int y, const SkBitmap& src);
1295
reed@google.com44699382013-10-31 17:28:30 +00001296 const SkBitmap::Config config = this->config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297
1298 switch (config) {
1299 case kARGB_8888_Config:
1300 proc = downsampleby2_proc32;
1301 break;
1302 case kRGB_565_Config:
1303 proc = downsampleby2_proc16;
1304 break;
1305 case kARGB_4444_Config:
1306 proc = downsampleby2_proc4444;
1307 break;
1308 case kIndex8_Config:
1309 case kA8_Config:
1310 default:
1311 return; // don't build mipmaps for these configs
1312 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001313
reed@android.com149e2f62009-05-22 14:39:03 +00001314 SkAutoLockPixels alp(*this);
1315 if (!this->readyToDraw()) {
1316 return;
1317 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318
1319 // whip through our loop to compute the exact size needed
1320 size_t size = 0;
1321 int maxLevels = 0;
1322 {
reed@android.com149e2f62009-05-22 14:39:03 +00001323 int width = this->width();
1324 int height = this->height();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 for (;;) {
1326 width >>= 1;
1327 height >>= 1;
1328 if (0 == width || 0 == height) {
1329 break;
1330 }
1331 size += ComputeRowBytes(config, width) * height;
1332 maxLevels += 1;
1333 }
1334 }
reed@android.com89bb83a2009-05-29 21:30:42 +00001335
reed@android.com149e2f62009-05-22 14:39:03 +00001336 // nothing to build
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 if (0 == maxLevels) {
1338 return;
1339 }
1340
reed@android.com149e2f62009-05-22 14:39:03 +00001341 SkBitmap srcBM(*this);
1342 srcBM.lockPixels();
1343 if (!srcBM.readyToDraw()) {
1344 return;
1345 }
1346
1347 MipMap* mm = MipMap::Alloc(maxLevels, size);
1348 if (NULL == mm) {
1349 return;
1350 }
1351
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 MipLevel* level = mm->levels();
1353 uint8_t* addr = (uint8_t*)mm->pixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001354 int width = this->width();
1355 int height = this->height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001356 uint32_t rowBytes;
reed@android.com149e2f62009-05-22 14:39:03 +00001357 SkBitmap dstBM;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358
1359 for (int i = 0; i < maxLevels; i++) {
1360 width >>= 1;
1361 height >>= 1;
scroggo@google.come5f48242013-02-25 21:47:41 +00001362 rowBytes = SkToU32(ComputeRowBytes(config, width));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363
1364 level[i].fPixels = addr;
reed@android.comf459a492009-03-27 12:33:50 +00001365 level[i].fWidth = width;
1366 level[i].fHeight = height;
reed@android.com49f0ff22009-03-19 21:52:42 +00001367 level[i].fRowBytes = rowBytes;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368
1369 dstBM.setConfig(config, width, height, rowBytes);
1370 dstBM.setPixels(addr);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001371
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001372 srcBM.lockPixels();
reed@android.com149e2f62009-05-22 14:39:03 +00001373 for (int y = 0; y < height; y++) {
1374 for (int x = 0; x < width; x++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 proc(&dstBM, x, y, srcBM);
1376 }
1377 }
bungeman@google.com7cf0e9e2012-07-25 16:09:10 +00001378 srcBM.unlockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379
1380 srcBM = dstBM;
1381 addr += height * rowBytes;
1382 }
1383 SkASSERT(addr == (uint8_t*)mm->pixels() + size);
1384 fMipMap = mm;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385}
1386
1387bool SkBitmap::hasMipMap() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 return fMipMap != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389}
1390
1391int SkBitmap::extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy) {
reed@android.com83f7bc32009-07-17 02:42:41 +00001392 if (NULL == fMipMap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 return 0;
reed@android.com83f7bc32009-07-17 02:42:41 +00001394 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001395
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 int level = ComputeMipLevel(sx, sy) >> 16;
1397 SkASSERT(level >= 0);
1398 if (level <= 0) {
1399 return 0;
1400 }
1401
1402 if (level >= fMipMap->fLevelCount) {
1403 level = fMipMap->fLevelCount - 1;
1404 }
1405 if (dst) {
1406 const MipLevel& mip = fMipMap->levels()[level - 1];
1407 dst->setConfig((SkBitmap::Config)this->config(),
1408 mip.fWidth, mip.fHeight, mip.fRowBytes);
1409 dst->setPixels(mip.fPixels);
1410 }
1411 return level;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412}
1413
1414SkFixed SkBitmap::ComputeMipLevel(SkFixed sx, SkFixed sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 sx = SkAbs32(sx);
1416 sy = SkAbs32(sy);
1417 if (sx < sy) {
1418 sx = sy;
1419 }
1420 if (sx < SK_Fixed1) {
1421 return 0;
1422 }
1423 int clz = SkCLZ(sx);
1424 SkASSERT(clz >= 1 && clz <= 15);
1425 return SkIntToFixed(15 - clz) + ((unsigned)(sx << (clz + 1)) >> 16);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426}
1427
1428///////////////////////////////////////////////////////////////////////////////
1429
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001430static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 int alphaRowBytes) {
1432 SkASSERT(alpha != NULL);
1433 SkASSERT(alphaRowBytes >= src.width());
1434
reed@google.com44699382013-10-31 17:28:30 +00001435 SkBitmap::Config config = src.config();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 int w = src.width();
1437 int h = src.height();
scroggo@google.come5f48242013-02-25 21:47:41 +00001438 size_t rb = src.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439
reed@android.com1cdcb512009-08-24 19:11:00 +00001440 SkAutoLockPixels alp(src);
1441 if (!src.readyToDraw()) {
1442 // zero out the alpha buffer and return
1443 while (--h >= 0) {
1444 memset(alpha, 0, w);
1445 alpha += alphaRowBytes;
1446 }
1447 return false;
1448 }
reed@google.com82065d62011-02-07 15:30:46 +00001449
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 if (SkBitmap::kA8_Config == config && !src.isOpaque()) {
1451 const uint8_t* s = src.getAddr8(0, 0);
1452 while (--h >= 0) {
1453 memcpy(alpha, s, w);
1454 s += rb;
1455 alpha += alphaRowBytes;
1456 }
1457 } else if (SkBitmap::kARGB_8888_Config == config && !src.isOpaque()) {
1458 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1459 while (--h >= 0) {
1460 for (int x = 0; x < w; x++) {
1461 alpha[x] = SkGetPackedA32(s[x]);
1462 }
1463 s = (const SkPMColor*)((const char*)s + rb);
1464 alpha += alphaRowBytes;
1465 }
1466 } else if (SkBitmap::kARGB_4444_Config == config && !src.isOpaque()) {
1467 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1468 while (--h >= 0) {
1469 for (int x = 0; x < w; x++) {
1470 alpha[x] = SkPacked4444ToA32(s[x]);
1471 }
1472 s = (const SkPMColor16*)((const char*)s + rb);
1473 alpha += alphaRowBytes;
1474 }
1475 } else if (SkBitmap::kIndex8_Config == config && !src.isOpaque()) {
1476 SkColorTable* ct = src.getColorTable();
1477 if (ct) {
1478 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1479 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1480 while (--h >= 0) {
1481 for (int x = 0; x < w; x++) {
1482 alpha[x] = SkGetPackedA32(table[s[x]]);
1483 }
1484 s += rb;
1485 alpha += alphaRowBytes;
1486 }
reed@google.com0a6151d2013-10-10 14:44:56 +00001487 ct->unlockColors();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 }
1489 } else { // src is opaque, so just fill alpha[] with 0xFF
1490 memset(alpha, 0xFF, h * alphaRowBytes);
1491 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001492 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
1495#include "SkPaint.h"
1496#include "SkMaskFilter.h"
1497#include "SkMatrix.h"
1498
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001499bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001500 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 SkDEBUGCODE(this->validate();)
1502
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001503 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 SkMatrix identity;
1505 SkMask srcM, dstM;
1506
1507 srcM.fBounds.set(0, 0, this->width(), this->height());
1508 srcM.fRowBytes = SkAlign4(this->width());
1509 srcM.fFormat = SkMask::kA8_Format;
1510
1511 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1512
1513 // compute our (larger?) dst bounds if we have a filter
1514 if (NULL != filter) {
1515 identity.reset();
1516 srcM.fImage = NULL;
1517 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1518 goto NO_FILTER_CASE;
1519 }
1520 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1521 } else {
1522 NO_FILTER_CASE:
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001523 tmpBitmap.setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001525 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1526 // Allocation of pixels for alpha bitmap failed.
1527 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1528 tmpBitmap.width(), tmpBitmap.height());
1529 return false;
1530 }
1531 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 if (offset) {
1533 offset->set(0, 0);
1534 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001535 tmpBitmap.swap(*dst);
1536 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001538 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1539 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540
1541 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1542 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1543 goto NO_FILTER_CASE;
1544 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001545 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001547 tmpBitmap.setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 dstM.fBounds.height(), dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001549 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1550 // Allocation of pixels for alpha bitmap failed.
1551 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1552 tmpBitmap.width(), tmpBitmap.height());
1553 return false;
1554 }
1555 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556 if (offset) {
1557 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1558 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001559 SkDEBUGCODE(tmpBitmap.validate();)
1560
1561 tmpBitmap.swap(*dst);
1562 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563}
1564
1565///////////////////////////////////////////////////////////////////////////////
1566
1567enum {
1568 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001569 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570};
1571
reed@android.com8a1c16f2008-12-17 15:59:43 +00001572void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001573 buffer.writeInt(fWidth);
1574 buffer.writeInt(fHeight);
1575 buffer.writeInt(fRowBytes);
1576 buffer.writeInt(fConfig);
reed@google.com383a6972013-10-21 14:00:07 +00001577 buffer.writeInt(fAlphaType);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001578
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 if (fPixelRef) {
djsollen@google.com5370cd92012-03-28 20:47:01 +00001580 if (fPixelRef->getFactory()) {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001581 buffer.writeInt(SERIALIZE_PIXELTYPE_REF_DATA);
scroggo@google.come5f48242013-02-25 21:47:41 +00001582 buffer.writeUInt(SkToU32(fPixelRefOffset));
djsollen@google.com5370cd92012-03-28 20:47:01 +00001583 buffer.writeFlattenable(fPixelRef);
1584 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 }
1586 // if we get here, we can't record the pixels
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001587 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 } else {
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001589 buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590 }
1591}
1592
1593void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) {
1594 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001595
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596 int width = buffer.readInt();
1597 int height = buffer.readInt();
1598 int rowBytes = buffer.readInt();
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +00001599 Config config = (Config)buffer.readInt();
1600 SkAlphaType alphaType = (SkAlphaType)buffer.readInt();
1601 buffer.validate((width >= 0) && (height >= 0) && (rowBytes >= 0) &&
1602 SkIsValidConfig(config) && validate_alphaType(config, alphaType));
weita@google.comf9ab99a2009-05-03 18:23:30 +00001603
commit-bot@chromium.orgc2e9db32013-12-06 20:14:46 +00001604 bool configIsValid = this->setConfig(config, width, height, rowBytes, alphaType);
1605 // Note : Using (fRowBytes >= (fWidth * fBytesPerPixel)) in the following test can create false
1606 // positives if the multiplication causes an integer overflow. Use the division instead.
1607 buffer.validate(configIsValid && (fBytesPerPixel > 0) &&
1608 ((fRowBytes / fBytesPerPixel) >= fWidth));
weita@google.comf9ab99a2009-05-03 18:23:30 +00001609
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001610 int reftype = buffer.readInt();
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001611 if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) ||
1612 (SERIALIZE_PIXELTYPE_NONE == reftype))) {
1613 switch (reftype) {
1614 case SERIALIZE_PIXELTYPE_REF_DATA: {
1615 size_t offset = buffer.readUInt();
1616 SkPixelRef* pr = buffer.readPixelRef();
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +00001617 if (!buffer.validate((NULL == pr) ||
1618 (pr->getAllocatedSizeInBytes() >= (offset + this->getSafeSize())))) {
1619 offset = 0;
1620 }
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001621 SkSafeUnref(this->setPixelRef(pr, offset));
1622 break;
1623 }
1624 case SERIALIZE_PIXELTYPE_NONE:
1625 break;
1626 default:
1627 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
1628 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630 }
1631}
1632
1633///////////////////////////////////////////////////////////////////////////////
1634
1635SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1636 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001637 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638}
1639
1640SkBitmap::RLEPixels::~RLEPixels() {
1641 sk_free(fYPtrs);
1642}
1643
1644///////////////////////////////////////////////////////////////////////////////
1645
1646#ifdef SK_DEBUG
1647void SkBitmap::validate() const {
1648 SkASSERT(fConfig < kConfigCount);
1649 SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001650 uint8_t allFlags = kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag;
1651#ifdef SK_BUILD_FOR_ANDROID
1652 allFlags |= kHasHardwareMipMap_Flag;
1653#endif
1654 SkASSERT(fFlags <= allFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 SkASSERT(fPixelLockCount >= 0);
1656 SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
1657 SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
1658
1659#if 0 // these asserts are not thread-correct, so disable for now
1660 if (fPixelRef) {
1661 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +00001662 SkASSERT(fPixelRef->isLocked());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663 } else {
1664 SkASSERT(NULL == fPixels);
1665 SkASSERT(NULL == fColorTable);
1666 }
1667 }
1668#endif
1669}
1670#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001671
1672#ifdef SK_DEVELOPER
1673void SkBitmap::toString(SkString* str) const {
1674
1675 static const char* gConfigNames[kConfigCount] = {
rmistry@google.comd6bab022013-12-02 13:50:38 +00001676 "NONE", "A8", "INDEX8", "565", "4444", "8888"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001677 };
1678
1679 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
1680 gConfigNames[this->config()]);
1681
1682 str->append(" (");
1683 if (this->isOpaque()) {
1684 str->append("opaque");
1685 } else {
1686 str->append("transparent");
1687 }
1688 if (this->isImmutable()) {
1689 str->append(", immutable");
1690 } else {
1691 str->append(", not-immutable");
1692 }
1693 str->append(")");
1694
1695 SkPixelRef* pr = this->pixelRef();
1696 if (NULL == pr) {
1697 // show null or the explicit pixel address (rare)
1698 str->appendf(" pixels:%p", this->getPixels());
1699 } else {
1700 const char* uri = pr->getURI();
1701 if (NULL != uri) {
1702 str->appendf(" uri:\"%s\"", uri);
1703 } else {
1704 str->appendf(" pixelref:%p", pr);
1705 }
1706 }
1707
1708 str->append(")");
1709}
1710#endif