blob: f2668f0a20e48d88b19d597b1ec7f2cb792bee9d [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"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000017#include "SkReadBuffer.h"
18#include "SkWriteBuffer.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
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000027static bool reset_return_false(SkBitmap* bm) {
28 bm->reset();
29 return false;
30}
31
reed@android.com8a1c16f2008-12-17 15:59:43 +000032SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000033 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000034}
35
36SkBitmap::SkBitmap(const SkBitmap& src) {
37 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000038 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 *this = src;
40 SkDEBUGCODE(this->validate();)
41}
42
43SkBitmap::~SkBitmap() {
44 SkDEBUGCODE(this->validate();)
45 this->freePixels();
46}
47
48SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
49 if (this != &src) {
50 this->freePixels();
51 memcpy(this, &src, sizeof(src));
52
53 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +000054 SkSafeRef(src.fPixelRef);
reed@android.com8a1c16f2008-12-17 15:59:43 +000055
56 // we reset our locks if we get blown away
57 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +000058
reed@google.com5f62ed72014-01-15 19:59:45 +000059 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 // ignore the values from the memcpy
61 fPixels = NULL;
62 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +000063 // Note that what to for genID is somewhat arbitrary. We have no
64 // way to track changes to raw pixels across multiple SkBitmaps.
65 // Would benefit from an SkRawPixelRef type created by
66 // setPixels.
67 // Just leave the memcpy'ed one but they'll get out of sync
68 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 }
70 }
71
72 SkDEBUGCODE(this->validate();)
73 return *this;
74}
75
76void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +000077 SkTSwap(fColorTable, other.fColorTable);
78 SkTSwap(fPixelRef, other.fPixelRef);
reed@google.com672588b2014-01-08 15:42:01 +000079 SkTSwap(fPixelRefOrigin, other.fPixelRefOrigin);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000080 SkTSwap(fPixelLockCount, other.fPixelLockCount);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000081 SkTSwap(fPixels, other.fPixels);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000082 SkTSwap(fInfo, other.fInfo);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000083 SkTSwap(fRowBytes, other.fRowBytes);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000084 SkTSwap(fFlags, other.fFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
86 SkDEBUGCODE(this->validate();)
87}
88
89void SkBitmap::reset() {
90 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +000091 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000092}
93
reed@google.com86b2e432012-03-15 21:17:03 +000094void SkBitmap::getBounds(SkRect* bounds) const {
95 SkASSERT(bounds);
96 bounds->set(0, 0,
reede5ea5002014-09-03 11:54:58 -070097 SkIntToScalar(fInfo.width()), SkIntToScalar(fInfo.height()));
reed@google.com86b2e432012-03-15 21:17:03 +000098}
99
reed@google.com80e14592012-03-16 14:58:07 +0000100void SkBitmap::getBounds(SkIRect* bounds) const {
101 SkASSERT(bounds);
reede5ea5002014-09-03 11:54:58 -0700102 bounds->set(0, 0, fInfo.width(), fInfo.height());
reed@google.com80e14592012-03-16 14:58:07 +0000103}
104
reed@google.com86b2e432012-03-15 21:17:03 +0000105///////////////////////////////////////////////////////////////////////////////
106
reede5ea5002014-09-03 11:54:58 -0700107bool SkBitmap::setInfo(const SkImageInfo& info, size_t rowBytes) {
108 SkAlphaType newAT = info.alphaType();
109 if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAT)) {
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +0000110 return reset_return_false(this);
111 }
reede5ea5002014-09-03 11:54:58 -0700112 // don't look at info.alphaType(), since newAT is the real value...
skia.committer@gmail.com02d6f542014-02-14 03:02:05 +0000113
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000114 // require that rowBytes fit in 31bits
115 int64_t mrb = info.minRowBytes64();
116 if ((int32_t)mrb != mrb) {
117 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000118 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000119 if ((int64_t)rowBytes != (int32_t)rowBytes) {
120 return reset_return_false(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000122
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000123 if (info.width() < 0 || info.height() < 0) {
124 return reset_return_false(this);
125 }
126
127 if (kUnknown_SkColorType == info.colorType()) {
128 rowBytes = 0;
129 } else if (0 == rowBytes) {
130 rowBytes = (size_t)mrb;
reedf0aed972014-07-01 12:48:11 -0700131 } else if (!info.validRowBytes(rowBytes)) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000132 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000133 }
134
135 this->freePixels();
136
reede5ea5002014-09-03 11:54:58 -0700137 fInfo = info.makeAlphaType(newAT);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000138 fRowBytes = SkToU32(rowBytes);
reed@google.com383a6972013-10-21 14:00:07 +0000139 return true;
reed@google.com383a6972013-10-21 14:00:07 +0000140}
141
reede5ea5002014-09-03 11:54:58 -0700142bool SkBitmap::setAlphaType(SkAlphaType newAlphaType) {
143 if (!SkColorTypeValidateAlphaType(fInfo.colorType(), newAlphaType, &newAlphaType)) {
reed@google.com383a6972013-10-21 14:00:07 +0000144 return false;
145 }
reede5ea5002014-09-03 11:54:58 -0700146 if (fInfo.alphaType() != newAlphaType) {
147 fInfo = fInfo.makeAlphaType(newAlphaType);
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000148 if (fPixelRef) {
reede5ea5002014-09-03 11:54:58 -0700149 fPixelRef->changeAlphaType(newAlphaType);
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000150 }
151 }
reed@google.com383a6972013-10-21 14:00:07 +0000152 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153}
154
155void SkBitmap::updatePixelsFromRef() const {
bsalomon49f085d2014-09-05 13:34:00 -0700156 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000158 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 void* p = fPixelRef->pixels();
bsalomon49f085d2014-09-05 13:34:00 -0700161 if (p) {
reed@google.com672588b2014-01-08 15:42:01 +0000162 p = (char*)p
reed@google.com303c4752014-01-09 20:00:14 +0000163 + fPixelRefOrigin.fY * fRowBytes
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000164 + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 }
166 fPixels = p;
reed@google.com5f62ed72014-01-15 19:59:45 +0000167 fColorTable = fPixelRef->colorTable();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 } else {
169 SkASSERT(0 == fPixelLockCount);
170 fPixels = NULL;
reed@google.com5f62ed72014-01-15 19:59:45 +0000171 fColorTable = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 }
173 }
174}
175
reed@google.com672588b2014-01-08 15:42:01 +0000176SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) {
reed@google.comdcea5302014-01-03 13:43:01 +0000177#ifdef SK_DEBUG
reed@google.com672588b2014-01-08 15:42:01 +0000178 if (pr) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000179 if (kUnknown_SkColorType != fInfo.colorType()) {
reed@google.comdcea5302014-01-03 13:43:01 +0000180 const SkImageInfo& prInfo = pr->info();
reede5ea5002014-09-03 11:54:58 -0700181 SkASSERT(fInfo.width() <= prInfo.width());
182 SkASSERT(fInfo.height() <= prInfo.height());
183 SkASSERT(fInfo.colorType() == prInfo.colorType());
184 switch (prInfo.alphaType()) {
reed44977482015-02-27 10:23:00 -0800185 case kUnknown_SkAlphaType:
186 SkASSERT(fInfo.alphaType() == kUnknown_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000187 break;
188 case kOpaque_SkAlphaType:
189 case kPremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700190 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
191 fInfo.alphaType() == kPremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000192 break;
193 case kUnpremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700194 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
195 fInfo.alphaType() == kUnpremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000196 break;
197 }
198 }
199 }
200#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201
reed@google.com672588b2014-01-08 15:42:01 +0000202 if (pr) {
203 const SkImageInfo& info = pr->info();
reede5ea5002014-09-03 11:54:58 -0700204 fPixelRefOrigin.set(SkPin32(dx, 0, info.width()), SkPin32(dy, 0, info.height()));
reed@google.com672588b2014-01-08 15:42:01 +0000205 } else {
206 // ignore dx,dy if there is no pixelref
207 fPixelRefOrigin.setZero();
208 }
209
210 if (fPixelRef != pr) {
piotaixr0eb02a62014-06-16 11:50:49 -0700211 this->freePixels();
212 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000213
piotaixr0eb02a62014-06-16 11:50:49 -0700214 SkSafeRef(pr);
215 fPixelRef = pr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 this->updatePixelsFromRef();
217 }
218
219 SkDEBUGCODE(this->validate();)
220 return pr;
221}
222
223void SkBitmap::lockPixels() const {
bsalomon49f085d2014-09-05 13:34:00 -0700224 if (fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 fPixelRef->lockPixels();
226 this->updatePixelsFromRef();
227 }
228 SkDEBUGCODE(this->validate();)
229}
230
231void SkBitmap::unlockPixels() const {
232 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
233
bsalomon49f085d2014-09-05 13:34:00 -0700234 if (fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 fPixelRef->unlockPixels();
236 this->updatePixelsFromRef();
237 }
238 SkDEBUGCODE(this->validate();)
239}
240
reed@google.com9c49bc32011-07-07 13:42:37 +0000241bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000242 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000243}
244
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000246 if (NULL == p) {
reed@google.com672588b2014-01-08 15:42:01 +0000247 this->setPixelRef(NULL);
reed@google.com8e1034e2012-07-30 13:16:35 +0000248 return;
249 }
250
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000251 if (kUnknown_SkColorType == fInfo.colorType()) {
reed@google.com672588b2014-01-08 15:42:01 +0000252 this->setPixelRef(NULL);
reed@google.combf790232013-12-13 19:45:58 +0000253 return;
254 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000256 SkPixelRef* pr = SkMallocPixelRef::NewDirect(fInfo, p, fRowBytes, ctable);
reed@google.combf790232013-12-13 19:45:58 +0000257 if (NULL == pr) {
reed@google.com672588b2014-01-08 15:42:01 +0000258 this->setPixelRef(NULL);
reed@google.combf790232013-12-13 19:45:58 +0000259 return;
260 }
261
262 this->setPixelRef(pr)->unref();
263
djsollen@google.comc84b8332012-07-27 13:41:44 +0000264 // since we're already allocated, we lockPixels right away
265 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkDEBUGCODE(this->validate();)
267}
268
reed84825042014-09-02 12:50:45 -0700269bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 HeapAllocator stdalloc;
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000271
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 if (NULL == allocator) {
273 allocator = &stdalloc;
274 }
275 return allocator->allocPixelRef(this, ctable);
276}
277
reed@google.com9ebcac52014-01-24 18:53:42 +0000278///////////////////////////////////////////////////////////////////////////////
279
reed84825042014-09-02 12:50:45 -0700280bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) {
reedbae704b2014-06-28 14:26:35 -0700281 if (kIndex_8_SkColorType == requestedInfo.colorType()) {
282 return reset_return_false(this);
283 }
reedf0aed972014-07-01 12:48:11 -0700284 if (!this->setInfo(requestedInfo, rowBytes)) {
reedbae704b2014-06-28 14:26:35 -0700285 return reset_return_false(this);
286 }
mtklein775b8192014-12-02 09:11:25 -0800287
reedbae704b2014-06-28 14:26:35 -0700288 // setInfo may have corrected info (e.g. 565 is always opaque).
289 const SkImageInfo& correctedInfo = this->info();
reedf0aed972014-07-01 12:48:11 -0700290 // setInfo may have computed a valid rowbytes if 0 were passed in
291 rowBytes = this->rowBytes();
292
reedbae704b2014-06-28 14:26:35 -0700293 SkMallocPixelRef::PRFactory defaultFactory;
mtklein775b8192014-12-02 09:11:25 -0800294
reedf0aed972014-07-01 12:48:11 -0700295 SkPixelRef* pr = defaultFactory.create(correctedInfo, rowBytes, NULL);
reedbae704b2014-06-28 14:26:35 -0700296 if (NULL == pr) {
297 return reset_return_false(this);
298 }
299 this->setPixelRef(pr)->unref();
mtklein775b8192014-12-02 09:11:25 -0800300
reedbae704b2014-06-28 14:26:35 -0700301 // TODO: lockPixels could/should return bool or void*/NULL
302 this->lockPixels();
303 if (NULL == this->getPixels()) {
304 return reset_return_false(this);
305 }
306 return true;
307}
308
reed84825042014-09-02 12:50:45 -0700309bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, SkPixelRefFactory* factory,
310 SkColorTable* ctable) {
reede5ea5002014-09-03 11:54:58 -0700311 if (kIndex_8_SkColorType == requestedInfo.colorType() && NULL == ctable) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000312 return reset_return_false(this);
313 }
scroggo0187dc22014-06-05 11:18:04 -0700314 if (!this->setInfo(requestedInfo)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000315 return reset_return_false(this);
316 }
317
scroggo0187dc22014-06-05 11:18:04 -0700318 // setInfo may have corrected info (e.g. 565 is always opaque).
319 const SkImageInfo& correctedInfo = this->info();
320
reed@google.com9ebcac52014-01-24 18:53:42 +0000321 SkMallocPixelRef::PRFactory defaultFactory;
322 if (NULL == factory) {
323 factory = &defaultFactory;
324 }
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000325
reedf0aed972014-07-01 12:48:11 -0700326 SkPixelRef* pr = factory->create(correctedInfo, correctedInfo.minRowBytes(), ctable);
reed@google.com9ebcac52014-01-24 18:53:42 +0000327 if (NULL == pr) {
328 return reset_return_false(this);
329 }
330 this->setPixelRef(pr)->unref();
331
332 // TODO: lockPixels could/should return bool or void*/NULL
333 this->lockPixels();
334 if (NULL == this->getPixels()) {
335 return reset_return_false(this);
336 }
337 return true;
338}
339
reeddb74f622015-05-30 13:41:15 -0700340static void invoke_release_proc(void (*proc)(void* pixels, void* ctx), void* pixels, void* ctx) {
341 if (proc) {
342 proc(pixels, ctx);
343 }
344}
345
scroggo0187dc22014-06-05 11:18:04 -0700346bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb,
347 SkColorTable* ct, void (*releaseProc)(void* addr, void* context),
348 void* context) {
349 if (!this->setInfo(requestedInfo, rb)) {
reeddb74f622015-05-30 13:41:15 -0700350 invoke_release_proc(releaseProc, pixels, context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000351 this->reset();
352 return false;
353 }
reeddb74f622015-05-30 13:41:15 -0700354 if (NULL == pixels) {
355 invoke_release_proc(releaseProc, pixels, context);
356 return true; // we behaved as if they called setInfo()
357 }
reed@google.com9ebcac52014-01-24 18:53:42 +0000358
scroggo0187dc22014-06-05 11:18:04 -0700359 // setInfo may have corrected info (e.g. 565 is always opaque).
360 const SkImageInfo& correctedInfo = this->info();
361
362 SkPixelRef* pr = SkMallocPixelRef::NewWithProc(correctedInfo, rb, ct, pixels, releaseProc,
363 context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000364 if (!pr) {
365 this->reset();
366 return false;
367 }
368
369 this->setPixelRef(pr)->unref();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000370
371 // since we're already allocated, we lockPixels right away
372 this->lockPixels();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000373 SkDEBUGCODE(this->validate();)
reed@google.com9ebcac52014-01-24 18:53:42 +0000374 return true;
375}
376
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000377bool SkBitmap::installMaskPixels(const SkMask& mask) {
378 if (SkMask::kA8_Format != mask.fFormat) {
379 this->reset();
380 return false;
381 }
382 return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(),
383 mask.fBounds.height()),
384 mask.fImage, mask.fRowBytes);
385}
386
reed@google.comeb9a46c2014-01-25 16:46:20 +0000387///////////////////////////////////////////////////////////////////////////////
388
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389void SkBitmap::freePixels() {
bsalomon49f085d2014-09-05 13:34:00 -0700390 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 if (fPixelLockCount > 0) {
392 fPixelRef->unlockPixels();
393 }
394 fPixelRef->unref();
395 fPixelRef = NULL;
reed@google.com672588b2014-01-08 15:42:01 +0000396 fPixelRefOrigin.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 }
398 fPixelLockCount = 0;
399 fPixels = NULL;
reed@google.com5f62ed72014-01-15 19:59:45 +0000400 fColorTable = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401}
402
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000404 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405}
406
407void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000408 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 if (fPixelRef) {
410 fPixelRef->notifyPixelsChanged();
411 }
412}
413
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000414GrTexture* SkBitmap::getTexture() const {
reed@android.comce4e53a2010-09-09 16:01:26 +0000415 return fPixelRef ? fPixelRef->getTexture() : NULL;
416}
417
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418///////////////////////////////////////////////////////////////////////////////
419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420/** We explicitly use the same allocator for our pixels that SkMask does,
421 so that we can freely assign memory allocated by one class to the other.
422 */
423bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
424 SkColorTable* ctable) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000425 const SkImageInfo info = dst->info();
426 if (kUnknown_SkColorType == info.colorType()) {
reed@google.combf790232013-12-13 19:45:58 +0000427// SkDebugf("unsupported config for info %d\n", dst->config());
428 return false;
429 }
skia.committer@gmail.com96f5fa02013-12-16 07:01:40 +0000430
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000431 SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(), ctable);
reed@google.combf790232013-12-13 19:45:58 +0000432 if (NULL == pr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 return false;
434 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000435
reed@google.com672588b2014-01-08 15:42:01 +0000436 dst->setPixelRef(pr)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 // since we're already allocated, we lockPixels right away
438 dst->lockPixels();
439 return true;
440}
441
442///////////////////////////////////////////////////////////////////////////////
443
reed92fc2ae2015-05-22 08:06:21 -0700444static bool copy_pixels_to(const SkPixmap& src, void* const dst, size_t dstSize,
445 size_t dstRowBytes, bool preserveDstPad) {
446 const SkImageInfo& info = src.info();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000447
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000448 if (0 == dstRowBytes) {
reed92fc2ae2015-05-22 08:06:21 -0700449 dstRowBytes = src.rowBytes();
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000450 }
reed92fc2ae2015-05-22 08:06:21 -0700451 if (dstRowBytes < info.minRowBytes()) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000452 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000453 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000454
reed92fc2ae2015-05-22 08:06:21 -0700455 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == src.rowBytes()) {
456 size_t safeSize = src.getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000457 if (safeSize > dstSize || safeSize == 0)
458 return false;
459 else {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000460 // This implementation will write bytes beyond the end of each row,
461 // excluding the last row, if the bitmap's stride is greater than
462 // strictly required by the current config.
reed92fc2ae2015-05-22 08:06:21 -0700463 memcpy(dst, src.addr(), safeSize);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000464 return true;
465 }
466 } else {
467 // If destination has different stride than us, then copy line by line.
reed92fc2ae2015-05-22 08:06:21 -0700468 if (info.getSafeSize(dstRowBytes) > dstSize) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000469 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000470 } else {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000471 // Just copy what we need on each line.
reed92fc2ae2015-05-22 08:06:21 -0700472 size_t rowBytes = info.minRowBytes();
473 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src.addr());
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000474 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
reed92fc2ae2015-05-22 08:06:21 -0700475 for (int row = 0; row < info.height(); ++row) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000476 memcpy(dstP, srcP, rowBytes);
reed92fc2ae2015-05-22 08:06:21 -0700477 srcP += src.rowBytes();
478 dstP += dstRowBytes;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000479 }
480
481 return true;
482 }
483 }
484}
485
reed92fc2ae2015-05-22 08:06:21 -0700486bool SkBitmap::copyPixelsTo(void* dst, size_t dstSize, size_t dstRB, bool preserveDstPad) const {
487 if (NULL == dst) {
488 return false;
489 }
490 SkAutoPixmapUnlock result;
491 if (!this->requestLock(&result)) {
492 return false;
493 }
494 return copy_pixels_to(result.pixmap(), dst, dstSize, dstRB, preserveDstPad);
495}
496
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000497///////////////////////////////////////////////////////////////////////////////
498
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000499bool SkBitmap::isImmutable() const {
scroggo08470592014-07-15 19:56:48 -0700500 return fPixelRef ? fPixelRef->isImmutable() : false;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000501}
502
503void SkBitmap::setImmutable() {
504 if (fPixelRef) {
505 fPixelRef->setImmutable();
junov@chromium.orgb0521292011-12-15 20:14:06 +0000506 }
507}
508
junov@google.com4ee7ae52011-06-30 17:30:49 +0000509bool SkBitmap::isVolatile() const {
510 return (fFlags & kImageIsVolatile_Flag) != 0;
511}
512
513void SkBitmap::setIsVolatile(bool isVolatile) {
514 if (isVolatile) {
515 fFlags |= kImageIsVolatile_Flag;
516 } else {
517 fFlags &= ~kImageIsVolatile_Flag;
518 }
519}
520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521void* SkBitmap::getAddr(int x, int y) const {
522 SkASSERT((unsigned)x < (unsigned)this->width());
523 SkASSERT((unsigned)y < (unsigned)this->height());
524
525 char* base = (char*)this->getPixels();
526 if (base) {
527 base += y * this->rowBytes();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000528 switch (this->colorType()) {
529 case kRGBA_8888_SkColorType:
530 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 base += x << 2;
532 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000533 case kARGB_4444_SkColorType:
534 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 base += x << 1;
536 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000537 case kAlpha_8_SkColorType:
538 case kIndex_8_SkColorType:
reed0c9b1a82015-03-17 17:44:06 -0700539 case kGray_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 base += x;
541 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000543 SkDEBUGFAIL("Can't return addr for config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 base = NULL;
545 break;
546 }
547 }
548 return base;
549}
550
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000551SkColor SkBitmap::getColor(int x, int y) const {
552 SkASSERT((unsigned)x < (unsigned)this->width());
553 SkASSERT((unsigned)y < (unsigned)this->height());
554
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000555 switch (this->colorType()) {
reed0c9b1a82015-03-17 17:44:06 -0700556 case kGray_8_SkColorType: {
557 uint8_t* addr = this->getAddr8(x, y);
558 return SkColorSetRGB(*addr, *addr, *addr);
559 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000560 case kAlpha_8_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000561 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000562 return SkColorSetA(0, addr[0]);
563 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000564 case kIndex_8_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000565 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000566 return SkUnPreMultiply::PMColorToColor(c);
567 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000568 case kRGB_565_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000569 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000570 return SkPixel16ToColor(addr[0]);
571 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000572 case kARGB_4444_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000573 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000574 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
575 return SkUnPreMultiply::PMColorToColor(c);
576 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000577 case kBGRA_8888_SkColorType:
578 case kRGBA_8888_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000579 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000580 return SkUnPreMultiply::PMColorToColor(addr[0]);
581 }
rmistry@google.comd6bab022013-12-02 13:50:38 +0000582 default:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000583 SkASSERT(false);
584 return 0;
585 }
586 SkASSERT(false); // Not reached.
587 return 0;
588}
589
reed92fc2ae2015-05-22 08:06:21 -0700590static bool compute_is_opaque(const SkPixmap& pmap) {
591 const int height = pmap.height();
592 const int width = pmap.width();
reed@google.com2a7579d2012-11-07 18:30:18 +0000593
reed92fc2ae2015-05-22 08:06:21 -0700594 switch (pmap.colorType()) {
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000595 case kAlpha_8_SkColorType: {
reed@google.com2a7579d2012-11-07 18:30:18 +0000596 unsigned a = 0xFF;
597 for (int y = 0; y < height; ++y) {
reed92fc2ae2015-05-22 08:06:21 -0700598 const uint8_t* row = pmap.addr8(0, y);
reed@google.com2a7579d2012-11-07 18:30:18 +0000599 for (int x = 0; x < width; ++x) {
600 a &= row[x];
601 }
602 if (0xFF != a) {
603 return false;
604 }
605 }
606 return true;
607 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000608 case kIndex_8_SkColorType: {
reed92fc2ae2015-05-22 08:06:21 -0700609 const SkColorTable* ctable = pmap.ctable();
610 if (NULL == ctable) {
reed@google.com2a7579d2012-11-07 18:30:18 +0000611 return false;
612 }
reed92fc2ae2015-05-22 08:06:21 -0700613 const SkPMColor* table = ctable->readColors();
reed@google.com140d7282013-01-07 20:25:04 +0000614 SkPMColor c = (SkPMColor)~0;
reed92fc2ae2015-05-22 08:06:21 -0700615 for (int i = ctable->count() - 1; i >= 0; --i) {
reed@google.com2a7579d2012-11-07 18:30:18 +0000616 c &= table[i];
617 }
618 return 0xFF == SkGetPackedA32(c);
619 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000620 case kRGB_565_SkColorType:
reed0c9b1a82015-03-17 17:44:06 -0700621 case kGray_8_SkColorType:
reed@google.com2a7579d2012-11-07 18:30:18 +0000622 return true;
623 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000624 case kARGB_4444_SkColorType: {
reed@google.com2a7579d2012-11-07 18:30:18 +0000625 unsigned c = 0xFFFF;
626 for (int y = 0; y < height; ++y) {
reed92fc2ae2015-05-22 08:06:21 -0700627 const SkPMColor16* row = pmap.addr16(0, y);
reed@google.com2a7579d2012-11-07 18:30:18 +0000628 for (int x = 0; x < width; ++x) {
629 c &= row[x];
630 }
631 if (0xF != SkGetPackedA4444(c)) {
632 return false;
633 }
634 }
635 return true;
636 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000637 case kBGRA_8888_SkColorType:
638 case kRGBA_8888_SkColorType: {
reed@google.com140d7282013-01-07 20:25:04 +0000639 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000640 for (int y = 0; y < height; ++y) {
reed92fc2ae2015-05-22 08:06:21 -0700641 const SkPMColor* row = pmap.addr32(0, y);
reed@google.com2a7579d2012-11-07 18:30:18 +0000642 for (int x = 0; x < width; ++x) {
643 c &= row[x];
644 }
645 if (0xFF != SkGetPackedA32(c)) {
646 return false;
647 }
648 }
649 return true;
650 }
651 default:
652 break;
653 }
654 return false;
655}
656
reed92fc2ae2015-05-22 08:06:21 -0700657bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
658 SkAutoPixmapUnlock result;
659 if (!bm.requestLock(&result)) {
660 return false;
661 }
662 return compute_is_opaque(result.pixmap());
663}
664
reed@google.com2a7579d2012-11-07 18:30:18 +0000665
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666///////////////////////////////////////////////////////////////////////////////
667///////////////////////////////////////////////////////////////////////////////
668
reed@google.com45f746f2013-06-21 19:51:31 +0000669static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
670 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
671 (SkR32To4444(r) << SK_R4444_SHIFT) |
672 (SkG32To4444(g) << SK_G4444_SHIFT) |
673 (SkB32To4444(b) << SK_B4444_SHIFT);
674 return SkToU16(pixel);
675}
676
reed92fc2ae2015-05-22 08:06:21 -0700677static bool internal_erase(const SkPixmap& pmap, const SkIRect& area,
678 U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
reed@google.com60d32352013-06-28 19:40:50 +0000679 int height = area.height();
680 const int width = area.width();
reed92fc2ae2015-05-22 08:06:21 -0700681 const int rowBytes = pmap.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682
reed92fc2ae2015-05-22 08:06:21 -0700683 switch (pmap.colorType()) {
reed0c9b1a82015-03-17 17:44:06 -0700684 case kGray_8_SkColorType: {
685 if (255 != a) {
686 r = SkMulDiv255Round(r, a);
687 g = SkMulDiv255Round(g, a);
688 b = SkMulDiv255Round(b, a);
689 }
690 int gray = SkComputeLuminance(r, g, b);
reed92fc2ae2015-05-22 08:06:21 -0700691 uint8_t* p = pmap.writable_addr8(area.fLeft, area.fTop);
reed0c9b1a82015-03-17 17:44:06 -0700692 while (--height >= 0) {
693 memset(p, gray, width);
694 p += rowBytes;
695 }
696 break;
697 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000698 case kAlpha_8_SkColorType: {
reed92fc2ae2015-05-22 08:06:21 -0700699 uint8_t* p = pmap.writable_addr8(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 while (--height >= 0) {
701 memset(p, a, width);
702 p += rowBytes;
703 }
704 break;
705 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000706 case kARGB_4444_SkColorType:
707 case kRGB_565_SkColorType: {
reed92fc2ae2015-05-22 08:06:21 -0700708 uint16_t* p = pmap.writable_addr16(area.fLeft, area.fTop);
reed@google.com45f746f2013-06-21 19:51:31 +0000709 uint16_t v;
skia.committer@gmail.com020b25b2013-06-22 07:00:58 +0000710
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000711 // make rgb premultiplied
712 if (255 != a) {
713 r = SkAlphaMul(r, a);
714 g = SkAlphaMul(g, a);
715 b = SkAlphaMul(b, a);
716 }
717
reed92fc2ae2015-05-22 08:06:21 -0700718 if (kARGB_4444_SkColorType == pmap.colorType()) {
reed@google.com45f746f2013-06-21 19:51:31 +0000719 v = pack_8888_to_4444(a, r, g, b);
720 } else {
721 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
722 g >> (8 - SK_G16_BITS),
723 b >> (8 - SK_B16_BITS));
724 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 while (--height >= 0) {
726 sk_memset16(p, v, width);
727 p = (uint16_t*)((char*)p + rowBytes);
728 }
729 break;
730 }
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000731 case kBGRA_8888_SkColorType:
732 case kRGBA_8888_SkColorType: {
reed92fc2ae2015-05-22 08:06:21 -0700733 uint32_t* p = pmap.writable_addr32(area.fLeft, area.fTop);
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000734
reed92fc2ae2015-05-22 08:06:21 -0700735 if (255 != a && kPremul_SkAlphaType == pmap.alphaType()) {
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000736 r = SkAlphaMul(r, a);
737 g = SkAlphaMul(g, a);
738 b = SkAlphaMul(b, a);
739 }
reed92fc2ae2015-05-22 08:06:21 -0700740 uint32_t v = kRGBA_8888_SkColorType == pmap.colorType() ?
741 SkPackARGB_as_RGBA(a, r, g, b) : SkPackARGB_as_BGRA(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742
743 while (--height >= 0) {
744 sk_memset32(p, v, width);
745 p = (uint32_t*)((char*)p + rowBytes);
746 }
747 break;
748 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000749 default:
reed92fc2ae2015-05-22 08:06:21 -0700750 return false; // no change, so don't call notifyPixelsChanged()
751 }
752 return true;
753}
754
755void SkBitmap::internalErase(const SkIRect& area, U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
756#ifdef SK_DEBUG
757 SkDEBUGCODE(this->validate();)
758 SkASSERT(!area.isEmpty());
759 {
760 SkIRect total = { 0, 0, this->width(), this->height() };
761 SkASSERT(total.contains(area));
762 }
763#endif
764
765 switch (fInfo.colorType()) {
766 case kUnknown_SkColorType:
767 case kIndex_8_SkColorType:
768 // TODO: can we ASSERT that we never get here?
769 return; // can't erase. Should we bzero so the memory is not uninitialized?
770 default:
771 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000773
reed92fc2ae2015-05-22 08:06:21 -0700774 SkAutoPixmapUnlock result;
775 if (!this->requestLock(&result)) {
776 return;
777 }
778
779 if (internal_erase(result.pixmap(), area, a, r, g, b)) {
780 this->notifyPixelsChanged();
781 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782}
783
reed@google.com60d32352013-06-28 19:40:50 +0000784void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
reed@google.com92833f92013-06-28 19:47:43 +0000785 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000786 if (!area.isEmpty()) {
787 this->internalErase(area, a, r, g, b);
788 }
789}
790
791void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
reed@google.com92833f92013-06-28 19:47:43 +0000792 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000793 if (area.intersect(rect)) {
794 this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
795 SkColorGetG(c), SkColorGetB(c));
796 }
797}
798
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799//////////////////////////////////////////////////////////////////////////////////////
800//////////////////////////////////////////////////////////////////////////////////////
801
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
803 SkDEBUGCODE(this->validate();)
804
djsollen@google.comc84b8332012-07-27 13:41:44 +0000805 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 return false; // no src pixels
807 }
808
809 SkIRect srcRect, r;
810 srcRect.set(0, 0, this->width(), this->height());
811 if (!r.intersect(srcRect, subset)) {
812 return false; // r is empty (i.e. no intersection)
813 }
814
scroggo@google.coma2a31922012-12-07 19:14:45 +0000815 if (fPixelRef->getTexture() != NULL) {
816 // Do a deep copy
jvanverthfa1e8a72014-12-22 08:31:49 -0800817 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->colorType(), this->profileType(), &subset);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000818 if (pixelRef != NULL) {
819 SkBitmap dst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000820 dst.setInfo(SkImageInfo::Make(subset.width(), subset.height(),
821 this->colorType(), this->alphaType()));
scroggo@google.coma2a31922012-12-07 19:14:45 +0000822 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000823 dst.setPixelRef(pixelRef)->unref();
824 SkDEBUGCODE(dst.validate());
825 result->swap(dst);
826 return true;
827 }
828 }
829
scroggo@google.coma2a31922012-12-07 19:14:45 +0000830 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
831 // exited above.
832 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
833 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
834
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 SkBitmap dst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000836 dst.setInfo(SkImageInfo::Make(r.width(), r.height(), this->colorType(), this->alphaType()),
837 this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000838 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839
840 if (fPixelRef) {
reed@google.com672588b2014-01-08 15:42:01 +0000841 SkIPoint origin = fPixelRefOrigin;
842 origin.fX += r.fLeft;
843 origin.fY += r.fTop;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 // share the pixelref with a custom offset
reed@google.com672588b2014-01-08 15:42:01 +0000845 dst.setPixelRef(fPixelRef, origin);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 }
847 SkDEBUGCODE(dst.validate();)
848
849 // we know we're good, so commit to result
850 result->swap(dst);
851 return true;
852}
853
854///////////////////////////////////////////////////////////////////////////////
855
856#include "SkCanvas.h"
857#include "SkPaint.h"
858
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000859bool SkBitmap::canCopyTo(SkColorType dstColorType) const {
reedb184f7f2014-07-13 04:32:32 -0700860 const SkColorType srcCT = this->colorType();
861
862 if (srcCT == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 return false;
864 }
865
reedb184f7f2014-07-13 04:32:32 -0700866 bool sameConfigs = (srcCT == dstColorType);
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000867 switch (dstColorType) {
868 case kAlpha_8_SkColorType:
869 case kRGB_565_SkColorType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000870 case kRGBA_8888_SkColorType:
871 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000873 case kIndex_8_SkColorType:
weita@google.comf9ab99a2009-05-03 18:23:30 +0000874 if (!sameConfigs) {
875 return false;
876 }
877 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000878 case kARGB_4444_SkColorType:
reedb184f7f2014-07-13 04:32:32 -0700879 return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT;
reed0c9b1a82015-03-17 17:44:06 -0700880 case kGray_8_SkColorType:
881 switch (srcCT) {
882 case kGray_8_SkColorType:
883 case kRGBA_8888_SkColorType:
884 case kBGRA_8888_SkColorType:
885 return true;
886 default:
887 break;
888 }
889 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 default:
891 return false;
892 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000893 return true;
894}
895
reedb184f7f2014-07-13 04:32:32 -0700896bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
897 int x, int y) const {
reed95d343f2015-05-23 13:21:06 -0700898 SkAutoPixmapUnlock src;
899 if (!this->requestLock(&src)) {
reedb184f7f2014-07-13 04:32:32 -0700900 return false;
901 }
reed95d343f2015-05-23 13:21:06 -0700902 return src.pixmap().readPixels(requestedDstInfo, dstPixels, dstRB, x, y);
reedb184f7f2014-07-13 04:32:32 -0700903}
904
905bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000906 if (!this->canCopyTo(dstColorType)) {
reed@android.comfbaa88d2009-05-06 17:44:34 +0000907 return false;
908 }
909
reed@google.com50dfa012011-04-01 19:05:36 +0000910 // if we have a texture, first get those pixels
911 SkBitmap tmpSrc;
912 const SkBitmap* src = this;
913
scroggo@google.coma2a31922012-12-07 19:14:45 +0000914 if (fPixelRef) {
915 SkIRect subset;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000916 subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY,
917 fInfo.width(), fInfo.height());
reed@google.com672588b2014-01-08 15:42:01 +0000918 if (fPixelRef->readPixels(&tmpSrc, &subset)) {
scroggo0187dc22014-06-05 11:18:04 -0700919 if (fPixelRef->info().alphaType() == kUnpremul_SkAlphaType) {
920 // FIXME: The only meaningful implementation of readPixels
921 // (GrPixelRef) assumes premultiplied pixels.
922 return false;
923 }
reed@google.com672588b2014-01-08 15:42:01 +0000924 SkASSERT(tmpSrc.width() == this->width());
925 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +0000926
reed@google.com672588b2014-01-08 15:42:01 +0000927 // did we get lucky and we can just return tmpSrc?
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000928 if (tmpSrc.colorType() == dstColorType && NULL == alloc) {
reed@google.com672588b2014-01-08 15:42:01 +0000929 dst->swap(tmpSrc);
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000930 // If the result is an exact copy, clone the gen ID.
931 if (dst->pixelRef() && dst->pixelRef()->info() == fPixelRef->info()) {
reed@google.com672588b2014-01-08 15:42:01 +0000932 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000933 }
reed@google.com672588b2014-01-08 15:42:01 +0000934 return true;
scroggo@google.comd5764e82012-08-22 15:00:05 +0000935 }
reed@google.com672588b2014-01-08 15:42:01 +0000936
937 // fall through to the raster case
938 src = &tmpSrc;
reed@google.com50dfa012011-04-01 19:05:36 +0000939 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000940 }
reed@android.com311c82d2009-05-05 23:13:23 +0000941
reed95d343f2015-05-23 13:21:06 -0700942 SkAutoPixmapUnlock srcUnlocker;
943 if (!src->requestLock(&srcUnlocker)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000944 return false;
945 }
reed95d343f2015-05-23 13:21:06 -0700946 const SkPixmap& srcPM = srcUnlocker.pixmap();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000947
reed95d343f2015-05-23 13:21:06 -0700948 const SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
reed@google.com50dfa012011-04-01 19:05:36 +0000949 SkBitmap tmpDst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000950 if (!tmpDst.setInfo(dstInfo)) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000951 return false;
952 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000953
weita@google.comf9ab99a2009-05-03 18:23:30 +0000954 // allocate colortable if srcConfig == kIndex8_Config
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000955 SkAutoTUnref<SkColorTable> ctable;
956 if (dstColorType == kIndex_8_SkColorType) {
reed95d343f2015-05-23 13:21:06 -0700957 ctable.reset(SkRef(srcPM.ctable()));
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000958 }
reed84825042014-09-02 12:50:45 -0700959 if (!tmpDst.tryAllocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000960 return false;
961 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000962
reed95d343f2015-05-23 13:21:06 -0700963 SkAutoPixmapUnlock dstUnlocker;
964 if (!tmpDst.requestLock(&dstUnlocker)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 return false;
966 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000967
reed95d343f2015-05-23 13:21:06 -0700968 if (!srcPM.readPixels(dstUnlocker.pixmap())) {
reedb184f7f2014-07-13 04:32:32 -0700969 return false;
970 }
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000971
reedb184f7f2014-07-13 04:32:32 -0700972 // (for BitmapHeap) Clone the pixelref genID even though we have a new pixelref.
973 // The old copyTo impl did this, so we continue it for now.
974 //
975 // TODO: should we ignore rowbytes (i.e. getSize)? Then it could just be
976 // if (src_pixelref->info == dst_pixelref->info)
977 //
reed95d343f2015-05-23 13:21:06 -0700978 if (srcPM.colorType() == dstColorType && tmpDst.getSize() == srcPM.getSize64()) {
reedb184f7f2014-07-13 04:32:32 -0700979 SkPixelRef* dstPixelRef = tmpDst.pixelRef();
980 if (dstPixelRef->info() == fPixelRef->info()) {
981 dstPixelRef->cloneGenID(*fPixelRef);
reed@android.com311c82d2009-05-05 23:13:23 +0000982 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 }
984
reed@google.com50dfa012011-04-01 19:05:36 +0000985 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 return true;
987}
988
commit-bot@chromium.orgfab349c2014-03-05 02:34:58 +0000989bool SkBitmap::deepCopyTo(SkBitmap* dst) const {
reede4538f52014-06-11 06:09:50 -0700990 const SkColorType dstCT = this->colorType();
jvanverthfa1e8a72014-12-22 08:31:49 -0800991 const SkColorProfileType dstPT = this->profileType();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000992
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000993 if (!this->canCopyTo(dstCT)) {
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000994 return false;
995 }
996
997 // If we have a PixelRef, and it supports deep copy, use it.
998 // Currently supported only by texture-backed bitmaps.
999 if (fPixelRef) {
jvanverthfa1e8a72014-12-22 08:31:49 -08001000 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstCT, dstPT, NULL);
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +00001001 if (pixelRef) {
1002 uint32_t rowBytes;
jvanverthfa1e8a72014-12-22 08:31:49 -08001003 if (this->colorType() == dstCT && this->profileType() == dstPT) {
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +00001004 // Since there is no subset to pass to deepCopy, and deepCopy
1005 // succeeded, the new pixel ref must be identical.
1006 SkASSERT(fPixelRef->info() == pixelRef->info());
1007 pixelRef->cloneGenID(*fPixelRef);
1008 // Use the same rowBytes as the original.
1009 rowBytes = fRowBytes;
1010 } else {
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001011 // With the new config, an appropriate fRowBytes will be computed by setInfo.
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +00001012 rowBytes = 0;
1013 }
1014
reede5ea5002014-09-03 11:54:58 -07001015 const SkImageInfo info = fInfo.makeColorType(dstCT);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001016 if (!dst->setInfo(info, rowBytes)) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001017 return false;
1018 }
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +00001019 dst->setPixelRef(pixelRef, fPixelRefOrigin)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001020 return true;
1021 }
1022 }
1023
1024 if (this->getTexture()) {
1025 return false;
1026 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +00001027 return this->copyTo(dst, dstCT, NULL);
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001028 }
1029}
1030
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032
reed92fc2ae2015-05-22 08:06:21 -07001033static void rect_memset(uint8_t* array, U8CPU value, SkISize size, size_t rowBytes) {
1034 for (int y = 0; y < size.height(); ++y) {
1035 memset(array, value, size.width());
1036 array += rowBytes;
reed@android.com1cdcb512009-08-24 19:11:00 +00001037 }
reed92fc2ae2015-05-22 08:06:21 -07001038}
reed@google.com82065d62011-02-07 15:30:46 +00001039
reed92fc2ae2015-05-22 08:06:21 -07001040static void get_bitmap_alpha(const SkPixmap& pmap, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
1041 SkColorType colorType = pmap.colorType();
1042 int w = pmap.width();
1043 int h = pmap.height();
1044 size_t rb = pmap.rowBytes();
1045
1046 if (kAlpha_8_SkColorType == colorType && !pmap.isOpaque()) {
1047 const uint8_t* s = pmap.addr8(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 while (--h >= 0) {
1049 memcpy(alpha, s, w);
1050 s += rb;
1051 alpha += alphaRowBytes;
1052 }
reed92fc2ae2015-05-22 08:06:21 -07001053 } else if (kN32_SkColorType == colorType && !pmap.isOpaque()) {
1054 const SkPMColor* SK_RESTRICT s = pmap.addr32(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 while (--h >= 0) {
1056 for (int x = 0; x < w; x++) {
1057 alpha[x] = SkGetPackedA32(s[x]);
1058 }
1059 s = (const SkPMColor*)((const char*)s + rb);
1060 alpha += alphaRowBytes;
1061 }
reed92fc2ae2015-05-22 08:06:21 -07001062 } else if (kARGB_4444_SkColorType == colorType && !pmap.isOpaque()) {
1063 const SkPMColor16* SK_RESTRICT s = pmap.addr16(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 while (--h >= 0) {
1065 for (int x = 0; x < w; x++) {
1066 alpha[x] = SkPacked4444ToA32(s[x]);
1067 }
1068 s = (const SkPMColor16*)((const char*)s + rb);
1069 alpha += alphaRowBytes;
1070 }
reed92fc2ae2015-05-22 08:06:21 -07001071 } else if (kIndex_8_SkColorType == colorType && !pmap.isOpaque()) {
1072 const SkColorTable* ct = pmap.ctable();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 if (ct) {
mtklein775b8192014-12-02 09:11:25 -08001074 const SkPMColor* SK_RESTRICT table = ct->readColors();
reed92fc2ae2015-05-22 08:06:21 -07001075 const uint8_t* SK_RESTRICT s = pmap.addr8(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 while (--h >= 0) {
1077 for (int x = 0; x < w; x++) {
1078 alpha[x] = SkGetPackedA32(table[s[x]]);
1079 }
1080 s += rb;
1081 alpha += alphaRowBytes;
1082 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 }
1084 } else { // src is opaque, so just fill alpha[] with 0xFF
reed92fc2ae2015-05-22 08:06:21 -07001085 rect_memset(alpha, 0xFF, pmap.info().dimensions(), alphaRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 }
reed92fc2ae2015-05-22 08:06:21 -07001087}
1088
1089static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
1090 SkASSERT(alpha != NULL);
1091 SkASSERT(alphaRowBytes >= src.width());
1092
1093 SkAutoPixmapUnlock apl;
1094 if (!src.requestLock(&apl)) {
1095 rect_memset(alpha, 0, src.info().dimensions(), alphaRowBytes);
1096 return false;
1097 }
1098 get_bitmap_alpha(apl.pixmap(), alpha, alphaRowBytes);
reed@android.com1cdcb512009-08-24 19:11:00 +00001099 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100}
1101
1102#include "SkPaint.h"
1103#include "SkMaskFilter.h"
1104#include "SkMatrix.h"
1105
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001106bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001107 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 SkDEBUGCODE(this->validate();)
1109
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001110 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 SkMatrix identity;
1112 SkMask srcM, dstM;
1113
1114 srcM.fBounds.set(0, 0, this->width(), this->height());
1115 srcM.fRowBytes = SkAlign4(this->width());
1116 srcM.fFormat = SkMask::kA8_Format;
1117
1118 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1119
1120 // compute our (larger?) dst bounds if we have a filter
bsalomon49f085d2014-09-05 13:34:00 -07001121 if (filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 identity.reset();
1123 srcM.fImage = NULL;
1124 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1125 goto NO_FILTER_CASE;
1126 }
1127 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1128 } else {
1129 NO_FILTER_CASE:
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001130 tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes);
reed84825042014-09-02 12:50:45 -07001131 if (!tmpBitmap.tryAllocPixels(allocator, NULL)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001132 // Allocation of pixels for alpha bitmap failed.
1133 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1134 tmpBitmap.width(), tmpBitmap.height());
1135 return false;
1136 }
1137 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 if (offset) {
1139 offset->set(0, 0);
1140 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001141 tmpBitmap.swap(*dst);
1142 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001144 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1145 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146
1147 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1148 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1149 goto NO_FILTER_CASE;
1150 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001151 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001153 tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
1154 dstM.fRowBytes);
reed84825042014-09-02 12:50:45 -07001155 if (!tmpBitmap.tryAllocPixels(allocator, NULL)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001156 // Allocation of pixels for alpha bitmap failed.
1157 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1158 tmpBitmap.width(), tmpBitmap.height());
1159 return false;
1160 }
1161 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 if (offset) {
1163 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1164 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001165 SkDEBUGCODE(tmpBitmap.validate();)
1166
1167 tmpBitmap.swap(*dst);
1168 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169}
1170
1171///////////////////////////////////////////////////////////////////////////////
1172
reed92fc2ae2015-05-22 08:06:21 -07001173static void write_raw_pixels(SkWriteBuffer* buffer, const SkPixmap& pmap) {
1174 const SkImageInfo& info = pmap.info();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001175 const size_t snugRB = info.width() * info.bytesPerPixel();
reed92fc2ae2015-05-22 08:06:21 -07001176 const char* src = (const char*)pmap.addr();
1177 const size_t ramRB = pmap.rowBytes();
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001178
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001179 buffer->write32(SkToU32(snugRB));
1180 info.flatten(*buffer);
1181
1182 const size_t size = snugRB * info.height();
1183 SkAutoMalloc storage(size);
1184 char* dst = (char*)storage.get();
1185 for (int y = 0; y < info.height(); ++y) {
1186 memcpy(dst, src, snugRB);
1187 dst += snugRB;
1188 src += ramRB;
1189 }
1190 buffer->writeByteArray(storage.get(), size);
1191
reed92fc2ae2015-05-22 08:06:21 -07001192 const SkColorTable* ct = pmap.ctable();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001193 if (kIndex_8_SkColorType == info.colorType() && ct) {
1194 buffer->writeBool(true);
1195 ct->writeToBuffer(*buffer);
1196 } else {
1197 buffer->writeBool(false);
1198 }
1199}
1200
reed92fc2ae2015-05-22 08:06:21 -07001201void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) {
1202 const SkImageInfo info = bitmap.info();
1203 if (0 == info.width() || 0 == info.height() || NULL == bitmap.pixelRef()) {
1204 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
1205 return;
1206 }
1207
1208 SkAutoPixmapUnlock result;
1209 if (!bitmap.requestLock(&result)) {
1210 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
1211 return;
1212 }
1213
1214 write_raw_pixels(buffer, result.pixmap());
1215}
1216
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001217bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) {
1218 const size_t snugRB = buffer->readUInt();
1219 if (0 == snugRB) { // no pixels
1220 return false;
1221 }
1222
1223 SkImageInfo info;
1224 info.unflatten(*buffer);
1225
sugoica95c192014-07-08 09:18:48 -07001226 // If there was an error reading "info", don't use it to compute minRowBytes()
1227 if (!buffer->validate(true)) {
1228 return false;
1229 }
1230
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001231 const size_t ramRB = info.minRowBytes();
sugoibd0d9da2015-01-07 08:47:44 -08001232 const int height = SkMax32(info.height(), 0);
1233 const uint64_t snugSize = sk_64_mul(snugRB, height);
1234 const uint64_t ramSize = sk_64_mul(ramRB, height);
1235 static const uint64_t max_size_t = (size_t)(-1);
1236 if (!buffer->validate((snugSize <= ramSize) && (ramSize <= max_size_t))) {
commit-bot@chromium.org05858432014-05-30 01:06:44 +00001237 return false;
1238 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001239
sugoibd0d9da2015-01-07 08:47:44 -08001240 SkAutoDataUnref data(SkData::NewUninitialized(SkToSizeT(ramSize)));
robertphillips28937842015-06-08 07:10:49 -07001241 unsigned char* dst = (unsigned char*)data->writable_data();
sugoibd0d9da2015-01-07 08:47:44 -08001242 buffer->readByteArray(dst, SkToSizeT(snugSize));
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001243
1244 if (snugSize != ramSize) {
robertphillips28937842015-06-08 07:10:49 -07001245 const unsigned char* srcRow = dst + snugRB * (height - 1);
1246 unsigned char* dstRow = dst + ramRB * (height - 1);
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001247 for (int y = height - 1; y >= 1; --y) {
1248 memmove(dstRow, srcRow, snugRB);
1249 srcRow -= snugRB;
1250 dstRow -= ramRB;
1251 }
1252 SkASSERT(srcRow == dstRow); // first row does not need to be moved
1253 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001254
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001255 SkAutoTUnref<SkColorTable> ctable;
1256 if (buffer->readBool()) {
1257 ctable.reset(SkNEW_ARGS(SkColorTable, (*buffer)));
robertphillips28937842015-06-08 07:10:49 -07001258
1259 unsigned char maxIndex = ctable->count() ? ctable->count()-1 : 0;
1260 for (uint64_t i = 0; i < ramSize; ++i) {
1261 dst[i] = SkTMin(dst[i], maxIndex);
1262 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001263 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001264
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001265 SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewWithData(info, info.minRowBytes(),
1266 ctable.get(), data.get()));
sugoi6af31472015-01-28 13:15:32 -08001267 if (!pr.get()) {
1268 return false;
1269 }
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001270 bitmap->setInfo(pr->info());
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001271 bitmap->setPixelRef(pr, 0, 0);
1272 return true;
1273}
1274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275enum {
1276 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001277 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278};
1279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280///////////////////////////////////////////////////////////////////////////////
1281
1282SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1283 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001284 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
1287SkBitmap::RLEPixels::~RLEPixels() {
1288 sk_free(fYPtrs);
1289}
1290
1291///////////////////////////////////////////////////////////////////////////////
1292
1293#ifdef SK_DEBUG
1294void SkBitmap::validate() const {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001295 fInfo.validate();
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +00001296
1297 // ImageInfo may not require this, but Bitmap ensures that opaque-only
1298 // colorTypes report opaque for their alphatype
1299 if (kRGB_565_SkColorType == fInfo.colorType()) {
1300 SkASSERT(kOpaque_SkAlphaType == fInfo.alphaType());
1301 }
1302
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001303 SkASSERT(fInfo.validRowBytes(fRowBytes));
scroggo08470592014-07-15 19:56:48 -07001304 uint8_t allFlags = kImageIsVolatile_Flag;
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001305#ifdef SK_BUILD_FOR_ANDROID
1306 allFlags |= kHasHardwareMipMap_Flag;
1307#endif
scroggo08470592014-07-15 19:56:48 -07001308 SkASSERT((~allFlags & fFlags) == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 SkASSERT(fPixelLockCount >= 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310
reed@google.com615316c2014-01-15 19:15:23 +00001311 if (fPixels) {
1312 SkASSERT(fPixelRef);
robertphillipse77dadd2014-11-21 05:50:21 -08001313 SkASSERT(fPixelLockCount > 0);
reed@google.com615316c2014-01-15 19:15:23 +00001314 SkASSERT(fPixelRef->isLocked());
1315 SkASSERT(fPixelRef->rowBytes() == fRowBytes);
1316 SkASSERT(fPixelRefOrigin.fX >= 0);
1317 SkASSERT(fPixelRefOrigin.fY >= 0);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001318 SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
reede5ea5002014-09-03 11:54:58 -07001319 SkASSERT(fPixelRef->info().height() >= (int)this->height() + fPixelRefOrigin.fY);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001320 SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
reed@google.com615316c2014-01-15 19:15:23 +00001321 } else {
1322 SkASSERT(NULL == fColorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324}
1325#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001326
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001327#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001328void SkBitmap::toString(SkString* str) const {
1329
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001330 static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = {
1331 "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8",
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001332 };
1333
1334 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001335 gColorTypeNames[this->colorType()]);
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001336
1337 str->append(" (");
1338 if (this->isOpaque()) {
1339 str->append("opaque");
1340 } else {
1341 str->append("transparent");
1342 }
1343 if (this->isImmutable()) {
1344 str->append(", immutable");
1345 } else {
1346 str->append(", not-immutable");
1347 }
1348 str->append(")");
1349
1350 SkPixelRef* pr = this->pixelRef();
1351 if (NULL == pr) {
1352 // show null or the explicit pixel address (rare)
1353 str->appendf(" pixels:%p", this->getPixels());
1354 } else {
1355 const char* uri = pr->getURI();
bsalomon49f085d2014-09-05 13:34:00 -07001356 if (uri) {
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001357 str->appendf(" uri:\"%s\"", uri);
1358 } else {
1359 str->appendf(" pixelref:%p", pr);
1360 }
1361 }
1362
1363 str->append(")");
1364}
1365#endif
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001366
1367///////////////////////////////////////////////////////////////////////////////
1368
reed92fc2ae2015-05-22 08:06:21 -07001369bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const {
1370 SkASSERT(result);
1371
1372 SkPixelRef* pr = fPixelRef;
1373 if (NULL == pr) {
1374 return false;
1375 }
1376
1377 SkPixelRef::LockRequest req = { fInfo.dimensions(), kNone_SkFilterQuality };
1378 SkPixelRef::LockResult res;
1379 if (pr->requestLock(req, &res)) {
reede8006572015-05-28 14:06:06 -07001380 SkASSERT(res.fPixels);
reed92fc2ae2015-05-22 08:06:21 -07001381 // The bitmap may be a subset of the pixelref's dimensions
1382 SkASSERT(fPixelRefOrigin.x() + fInfo.width() <= res.fSize.width());
1383 SkASSERT(fPixelRefOrigin.y() + fInfo.height() <= res.fSize.height());
1384 const void* addr = (const char*)res.fPixels + SkColorTypeComputeOffset(fInfo.colorType(),
1385 fPixelRefOrigin.x(),
1386 fPixelRefOrigin.y(),
1387 res.fRowBytes);
1388
1389 result->reset(SkPixmap(this->info(), addr, res.fRowBytes, res.fCTable),
1390 res.fUnlockProc, res.fUnlockContext);
1391 return true;
1392 }
1393 return false;
1394}
1395
reedcb674142015-06-05 06:58:22 -07001396bool SkBitmap::peekPixels(SkPixmap* pmap) const {
1397 if (fPixels) {
1398 if (pmap) {
1399 pmap->reset(fInfo, fPixels, fRowBytes, fColorTable);
1400 }
1401 return true;
1402 }
1403 return false;
1404}
1405
reed92fc2ae2015-05-22 08:06:21 -07001406///////////////////////////////////////////////////////////////////////////////
1407
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001408#ifdef SK_DEBUG
1409void SkImageInfo::validate() const {
1410 SkASSERT(fWidth >= 0);
1411 SkASSERT(fHeight >= 0);
1412 SkASSERT(SkColorTypeIsValid(fColorType));
1413 SkASSERT(SkAlphaTypeIsValid(fAlphaType));
1414}
1415#endif