blob: 863169c458b8d76642d3aae13e923c68cb90ede3 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
herbb906daf2015-09-29 09:37:59 -07008#include "SkAtomics.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkBitmap.h"
10#include "SkColorPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkData.h"
12#include "SkFilterQuality.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkMallocPixelRef.h"
14#include "SkMask.h"
bungemand3ebb482015-08-05 13:57:49 -070015#include "SkMath.h"
jvanverth02802f62015-07-02 06:42:49 -070016#include "SkPixelRef.h"
mtklein1b249332015-07-07 12:21:21 -070017#include "SkReadBuffer.h"
bungemand3ebb482015-08-05 13:57:49 -070018#include "SkRect.h"
19#include "SkScalar.h"
scroggo565901d2015-12-10 10:44:13 -080020#include "SkTemplates.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000021#include "SkUnPreMultiply.h"
mtklein1b249332015-07-07 12:21:21 -070022#include "SkWriteBuffer.h"
bungemand3ebb482015-08-05 13:57:49 -070023
24#include <string.h>
reed@android.com8a1c16f2008-12-17 15:59:43 +000025
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000026static bool reset_return_false(SkBitmap* bm) {
27 bm->reset();
28 return false;
29}
30
reed@android.com8a1c16f2008-12-17 15:59:43 +000031SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000032 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000033}
34
35SkBitmap::SkBitmap(const SkBitmap& src) {
36 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000037 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000038 *this = src;
39 SkDEBUGCODE(this->validate();)
40}
41
halcanary023bda02015-12-14 10:19:17 -080042SkBitmap::SkBitmap(SkBitmap&& other) : SkBitmap() { this->swap(other); }
43
reed@android.com8a1c16f2008-12-17 15:59:43 +000044SkBitmap::~SkBitmap() {
45 SkDEBUGCODE(this->validate();)
46 this->freePixels();
47}
48
49SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
50 if (this != &src) {
51 this->freePixels();
msarett0881fb52016-05-27 11:31:55 -070052 this->fPixelRef = SkSafeRef(src.fPixelRef);
53 if (this->fPixelRef) {
54 // ignore the values if we have a pixelRef
55 this->fPixels = nullptr;
56 this->fColorTable = nullptr;
57 } else {
58 this->fPixels = src.fPixels;
59 this->fColorTable = src.fColorTable;
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 }
msarett0881fb52016-05-27 11:31:55 -070061 // we reset our locks if we get blown away
62 this->fPixelLockCount = 0;
63
64 this->fPixelRefOrigin = src.fPixelRefOrigin;
65 this->fInfo = src.fInfo;
66 this->fRowBytes = src.fRowBytes;
67 this->fFlags = src.fFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 }
69
70 SkDEBUGCODE(this->validate();)
71 return *this;
72}
73
halcanary023bda02015-12-14 10:19:17 -080074SkBitmap& SkBitmap::operator=(SkBitmap&& other) {
75 if (this != &other) {
76 this->swap(other);
77 other.reset();
78 }
79 return *this;
80}
81
reed@android.com8a1c16f2008-12-17 15:59:43 +000082void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +000083 SkTSwap(fColorTable, other.fColorTable);
84 SkTSwap(fPixelRef, other.fPixelRef);
reed@google.com672588b2014-01-08 15:42:01 +000085 SkTSwap(fPixelRefOrigin, other.fPixelRefOrigin);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000086 SkTSwap(fPixelLockCount, other.fPixelLockCount);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000087 SkTSwap(fPixels, other.fPixels);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000088 SkTSwap(fInfo, other.fInfo);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000089 SkTSwap(fRowBytes, other.fRowBytes);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000090 SkTSwap(fFlags, other.fFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
92 SkDEBUGCODE(this->validate();)
93}
94
95void SkBitmap::reset() {
96 this->freePixels();
msarett23c51102016-05-27 07:39:02 -070097 this->fInfo.reset();
reed@android.com4516f472009-06-29 16:25:36 +000098 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000099}
100
reed@google.com86b2e432012-03-15 21:17:03 +0000101void SkBitmap::getBounds(SkRect* bounds) const {
102 SkASSERT(bounds);
103 bounds->set(0, 0,
reede5ea5002014-09-03 11:54:58 -0700104 SkIntToScalar(fInfo.width()), SkIntToScalar(fInfo.height()));
reed@google.com86b2e432012-03-15 21:17:03 +0000105}
106
reed@google.com80e14592012-03-16 14:58:07 +0000107void SkBitmap::getBounds(SkIRect* bounds) const {
108 SkASSERT(bounds);
reede5ea5002014-09-03 11:54:58 -0700109 bounds->set(0, 0, fInfo.width(), fInfo.height());
reed@google.com80e14592012-03-16 14:58:07 +0000110}
111
reed@google.com86b2e432012-03-15 21:17:03 +0000112///////////////////////////////////////////////////////////////////////////////
113
reede5ea5002014-09-03 11:54:58 -0700114bool SkBitmap::setInfo(const SkImageInfo& info, size_t rowBytes) {
115 SkAlphaType newAT = info.alphaType();
116 if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAT)) {
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +0000117 return reset_return_false(this);
118 }
reede5ea5002014-09-03 11:54:58 -0700119 // don't look at info.alphaType(), since newAT is the real value...
skia.committer@gmail.com02d6f542014-02-14 03:02:05 +0000120
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000121 // require that rowBytes fit in 31bits
122 int64_t mrb = info.minRowBytes64();
123 if ((int32_t)mrb != mrb) {
124 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000125 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000126 if ((int64_t)rowBytes != (int32_t)rowBytes) {
127 return reset_return_false(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000129
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000130 if (info.width() < 0 || info.height() < 0) {
131 return reset_return_false(this);
132 }
133
134 if (kUnknown_SkColorType == info.colorType()) {
135 rowBytes = 0;
136 } else if (0 == rowBytes) {
137 rowBytes = (size_t)mrb;
reedf0aed972014-07-01 12:48:11 -0700138 } else if (!info.validRowBytes(rowBytes)) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000139 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000140 }
141
142 this->freePixels();
143
reede5ea5002014-09-03 11:54:58 -0700144 fInfo = info.makeAlphaType(newAT);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000145 fRowBytes = SkToU32(rowBytes);
reed@google.com383a6972013-10-21 14:00:07 +0000146 return true;
reed@google.com383a6972013-10-21 14:00:07 +0000147}
148
reede5ea5002014-09-03 11:54:58 -0700149bool SkBitmap::setAlphaType(SkAlphaType newAlphaType) {
150 if (!SkColorTypeValidateAlphaType(fInfo.colorType(), newAlphaType, &newAlphaType)) {
reed@google.com383a6972013-10-21 14:00:07 +0000151 return false;
152 }
reede5ea5002014-09-03 11:54:58 -0700153 if (fInfo.alphaType() != newAlphaType) {
154 fInfo = fInfo.makeAlphaType(newAlphaType);
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000155 if (fPixelRef) {
reede5ea5002014-09-03 11:54:58 -0700156 fPixelRef->changeAlphaType(newAlphaType);
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000157 }
158 }
reed@google.com383a6972013-10-21 14:00:07 +0000159 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160}
161
162void SkBitmap::updatePixelsFromRef() const {
bsalomon49f085d2014-09-05 13:34:00 -0700163 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000165 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 void* p = fPixelRef->pixels();
bsalomon49f085d2014-09-05 13:34:00 -0700168 if (p) {
reed@google.com672588b2014-01-08 15:42:01 +0000169 p = (char*)p
reed@google.com303c4752014-01-09 20:00:14 +0000170 + fPixelRefOrigin.fY * fRowBytes
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000171 + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 }
173 fPixels = p;
reed@google.com5f62ed72014-01-15 19:59:45 +0000174 fColorTable = fPixelRef->colorTable();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 } else {
176 SkASSERT(0 == fPixelLockCount);
halcanary96fcdcc2015-08-27 07:41:13 -0700177 fPixels = nullptr;
178 fColorTable = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 }
180 }
181}
182
reed@google.com672588b2014-01-08 15:42:01 +0000183SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) {
reed@google.comdcea5302014-01-03 13:43:01 +0000184#ifdef SK_DEBUG
reed@google.com672588b2014-01-08 15:42:01 +0000185 if (pr) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000186 if (kUnknown_SkColorType != fInfo.colorType()) {
reed@google.comdcea5302014-01-03 13:43:01 +0000187 const SkImageInfo& prInfo = pr->info();
reede5ea5002014-09-03 11:54:58 -0700188 SkASSERT(fInfo.width() <= prInfo.width());
189 SkASSERT(fInfo.height() <= prInfo.height());
190 SkASSERT(fInfo.colorType() == prInfo.colorType());
191 switch (prInfo.alphaType()) {
reed44977482015-02-27 10:23:00 -0800192 case kUnknown_SkAlphaType:
193 SkASSERT(fInfo.alphaType() == kUnknown_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000194 break;
195 case kOpaque_SkAlphaType:
196 case kPremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700197 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
198 fInfo.alphaType() == kPremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000199 break;
200 case kUnpremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700201 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
202 fInfo.alphaType() == kUnpremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000203 break;
204 }
205 }
206 }
207#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208
reed@google.com672588b2014-01-08 15:42:01 +0000209 if (pr) {
210 const SkImageInfo& info = pr->info();
bungeman62ce0302015-08-28 09:09:32 -0700211 fPixelRefOrigin.set(SkTPin(dx, 0, info.width()), SkTPin(dy, 0, info.height()));
reed@google.com672588b2014-01-08 15:42:01 +0000212 } else {
213 // ignore dx,dy if there is no pixelref
214 fPixelRefOrigin.setZero();
215 }
216
217 if (fPixelRef != pr) {
piotaixr0eb02a62014-06-16 11:50:49 -0700218 this->freePixels();
halcanary96fcdcc2015-08-27 07:41:13 -0700219 SkASSERT(nullptr == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000220
piotaixr0eb02a62014-06-16 11:50:49 -0700221 SkSafeRef(pr);
222 fPixelRef = pr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 this->updatePixelsFromRef();
224 }
225
226 SkDEBUGCODE(this->validate();)
227 return pr;
228}
229
230void SkBitmap::lockPixels() const {
bsalomon49f085d2014-09-05 13:34:00 -0700231 if (fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 fPixelRef->lockPixels();
233 this->updatePixelsFromRef();
234 }
235 SkDEBUGCODE(this->validate();)
236}
237
238void SkBitmap::unlockPixels() const {
halcanary96fcdcc2015-08-27 07:41:13 -0700239 SkASSERT(nullptr == fPixelRef || fPixelLockCount > 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240
bsalomon49f085d2014-09-05 13:34:00 -0700241 if (fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 fPixelRef->unlockPixels();
243 this->updatePixelsFromRef();
244 }
245 SkDEBUGCODE(this->validate();)
246}
247
reed@google.com9c49bc32011-07-07 13:42:37 +0000248bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000249 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000250}
251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
halcanary96fcdcc2015-08-27 07:41:13 -0700253 if (nullptr == p) {
254 this->setPixelRef(nullptr);
reed@google.com8e1034e2012-07-30 13:16:35 +0000255 return;
256 }
257
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000258 if (kUnknown_SkColorType == fInfo.colorType()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700259 this->setPixelRef(nullptr);
reed@google.combf790232013-12-13 19:45:58 +0000260 return;
261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000263 SkPixelRef* pr = SkMallocPixelRef::NewDirect(fInfo, p, fRowBytes, ctable);
halcanary96fcdcc2015-08-27 07:41:13 -0700264 if (nullptr == pr) {
265 this->setPixelRef(nullptr);
reed@google.combf790232013-12-13 19:45:58 +0000266 return;
267 }
268
269 this->setPixelRef(pr)->unref();
270
djsollen@google.comc84b8332012-07-27 13:41:44 +0000271 // since we're already allocated, we lockPixels right away
272 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 SkDEBUGCODE(this->validate();)
274}
275
reed84825042014-09-02 12:50:45 -0700276bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 HeapAllocator stdalloc;
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000278
halcanary96fcdcc2015-08-27 07:41:13 -0700279 if (nullptr == allocator) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 allocator = &stdalloc;
281 }
282 return allocator->allocPixelRef(this, ctable);
283}
284
reed@google.com9ebcac52014-01-24 18:53:42 +0000285///////////////////////////////////////////////////////////////////////////////
286
reed84825042014-09-02 12:50:45 -0700287bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) {
reedbae704b2014-06-28 14:26:35 -0700288 if (kIndex_8_SkColorType == requestedInfo.colorType()) {
289 return reset_return_false(this);
290 }
reedf0aed972014-07-01 12:48:11 -0700291 if (!this->setInfo(requestedInfo, rowBytes)) {
reedbae704b2014-06-28 14:26:35 -0700292 return reset_return_false(this);
293 }
mtklein775b8192014-12-02 09:11:25 -0800294
reedbae704b2014-06-28 14:26:35 -0700295 // setInfo may have corrected info (e.g. 565 is always opaque).
296 const SkImageInfo& correctedInfo = this->info();
reedf0aed972014-07-01 12:48:11 -0700297 // setInfo may have computed a valid rowbytes if 0 were passed in
298 rowBytes = this->rowBytes();
299
reedbae704b2014-06-28 14:26:35 -0700300 SkMallocPixelRef::PRFactory defaultFactory;
mtklein775b8192014-12-02 09:11:25 -0800301
halcanary96fcdcc2015-08-27 07:41:13 -0700302 SkPixelRef* pr = defaultFactory.create(correctedInfo, rowBytes, nullptr);
303 if (nullptr == pr) {
reedbae704b2014-06-28 14:26:35 -0700304 return reset_return_false(this);
305 }
306 this->setPixelRef(pr)->unref();
mtklein775b8192014-12-02 09:11:25 -0800307
halcanary96fcdcc2015-08-27 07:41:13 -0700308 // TODO: lockPixels could/should return bool or void*/nullptr
reedbae704b2014-06-28 14:26:35 -0700309 this->lockPixels();
halcanary96fcdcc2015-08-27 07:41:13 -0700310 if (nullptr == this->getPixels()) {
reedbae704b2014-06-28 14:26:35 -0700311 return reset_return_false(this);
312 }
313 return true;
314}
315
reed84825042014-09-02 12:50:45 -0700316bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, SkPixelRefFactory* factory,
317 SkColorTable* ctable) {
halcanary96fcdcc2015-08-27 07:41:13 -0700318 if (kIndex_8_SkColorType == requestedInfo.colorType() && nullptr == ctable) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000319 return reset_return_false(this);
320 }
scroggo0187dc22014-06-05 11:18:04 -0700321 if (!this->setInfo(requestedInfo)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000322 return reset_return_false(this);
323 }
324
scroggo0187dc22014-06-05 11:18:04 -0700325 // setInfo may have corrected info (e.g. 565 is always opaque).
326 const SkImageInfo& correctedInfo = this->info();
327
reed@google.com9ebcac52014-01-24 18:53:42 +0000328 SkMallocPixelRef::PRFactory defaultFactory;
halcanary96fcdcc2015-08-27 07:41:13 -0700329 if (nullptr == factory) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000330 factory = &defaultFactory;
331 }
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000332
reedf0aed972014-07-01 12:48:11 -0700333 SkPixelRef* pr = factory->create(correctedInfo, correctedInfo.minRowBytes(), ctable);
halcanary96fcdcc2015-08-27 07:41:13 -0700334 if (nullptr == pr) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000335 return reset_return_false(this);
336 }
337 this->setPixelRef(pr)->unref();
338
halcanary96fcdcc2015-08-27 07:41:13 -0700339 // TODO: lockPixels could/should return bool or void*/nullptr
reed@google.com9ebcac52014-01-24 18:53:42 +0000340 this->lockPixels();
halcanary96fcdcc2015-08-27 07:41:13 -0700341 if (nullptr == this->getPixels()) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000342 return reset_return_false(this);
343 }
344 return true;
345}
346
reeddb74f622015-05-30 13:41:15 -0700347static void invoke_release_proc(void (*proc)(void* pixels, void* ctx), void* pixels, void* ctx) {
348 if (proc) {
349 proc(pixels, ctx);
350 }
351}
352
scroggo0187dc22014-06-05 11:18:04 -0700353bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb,
354 SkColorTable* ct, void (*releaseProc)(void* addr, void* context),
355 void* context) {
356 if (!this->setInfo(requestedInfo, rb)) {
reeddb74f622015-05-30 13:41:15 -0700357 invoke_release_proc(releaseProc, pixels, context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000358 this->reset();
359 return false;
360 }
halcanary96fcdcc2015-08-27 07:41:13 -0700361 if (nullptr == pixels) {
reeddb74f622015-05-30 13:41:15 -0700362 invoke_release_proc(releaseProc, pixels, context);
363 return true; // we behaved as if they called setInfo()
364 }
reed@google.com9ebcac52014-01-24 18:53:42 +0000365
scroggo0187dc22014-06-05 11:18:04 -0700366 // setInfo may have corrected info (e.g. 565 is always opaque).
367 const SkImageInfo& correctedInfo = this->info();
368
369 SkPixelRef* pr = SkMallocPixelRef::NewWithProc(correctedInfo, rb, ct, pixels, releaseProc,
370 context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000371 if (!pr) {
372 this->reset();
373 return false;
374 }
375
376 this->setPixelRef(pr)->unref();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000377
378 // since we're already allocated, we lockPixels right away
379 this->lockPixels();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000380 SkDEBUGCODE(this->validate();)
reed@google.com9ebcac52014-01-24 18:53:42 +0000381 return true;
382}
383
halcanarye36ec872015-12-09 11:36:59 -0800384bool SkBitmap::installPixels(const SkPixmap& pixmap) {
385 return this->installPixels(pixmap.info(), pixmap.writable_addr(),
386 pixmap.rowBytes(), pixmap.ctable(),
387 nullptr, nullptr);
388}
389
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000390bool SkBitmap::installMaskPixels(const SkMask& mask) {
391 if (SkMask::kA8_Format != mask.fFormat) {
392 this->reset();
393 return false;
394 }
395 return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(),
396 mask.fBounds.height()),
397 mask.fImage, mask.fRowBytes);
398}
399
reed@google.comeb9a46c2014-01-25 16:46:20 +0000400///////////////////////////////////////////////////////////////////////////////
401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402void SkBitmap::freePixels() {
bsalomon49f085d2014-09-05 13:34:00 -0700403 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404 if (fPixelLockCount > 0) {
405 fPixelRef->unlockPixels();
406 }
407 fPixelRef->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700408 fPixelRef = nullptr;
reed@google.com672588b2014-01-08 15:42:01 +0000409 fPixelRefOrigin.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410 }
411 fPixelLockCount = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700412 fPixels = nullptr;
413 fColorTable = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414}
415
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000417 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418}
419
420void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000421 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422 if (fPixelRef) {
423 fPixelRef->notifyPixelsChanged();
424 }
425}
426
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000427GrTexture* SkBitmap::getTexture() const {
halcanary96fcdcc2015-08-27 07:41:13 -0700428 return fPixelRef ? fPixelRef->getTexture() : nullptr;
reed@android.comce4e53a2010-09-09 16:01:26 +0000429}
430
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431///////////////////////////////////////////////////////////////////////////////
432
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433/** We explicitly use the same allocator for our pixels that SkMask does,
434 so that we can freely assign memory allocated by one class to the other.
435 */
436bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
437 SkColorTable* ctable) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000438 const SkImageInfo info = dst->info();
439 if (kUnknown_SkColorType == info.colorType()) {
reed@google.combf790232013-12-13 19:45:58 +0000440// SkDebugf("unsupported config for info %d\n", dst->config());
441 return false;
442 }
skia.committer@gmail.com96f5fa02013-12-16 07:01:40 +0000443
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000444 SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(), ctable);
halcanary96fcdcc2015-08-27 07:41:13 -0700445 if (nullptr == pr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 return false;
447 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000448
reed@google.com672588b2014-01-08 15:42:01 +0000449 dst->setPixelRef(pr)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 // since we're already allocated, we lockPixels right away
451 dst->lockPixels();
452 return true;
453}
454
455///////////////////////////////////////////////////////////////////////////////
456
reed92fc2ae2015-05-22 08:06:21 -0700457static bool copy_pixels_to(const SkPixmap& src, void* const dst, size_t dstSize,
458 size_t dstRowBytes, bool preserveDstPad) {
459 const SkImageInfo& info = src.info();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000460
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000461 if (0 == dstRowBytes) {
reed92fc2ae2015-05-22 08:06:21 -0700462 dstRowBytes = src.rowBytes();
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000463 }
reed92fc2ae2015-05-22 08:06:21 -0700464 if (dstRowBytes < info.minRowBytes()) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000465 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000466 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000467
reed92fc2ae2015-05-22 08:06:21 -0700468 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == src.rowBytes()) {
469 size_t safeSize = src.getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000470 if (safeSize > dstSize || safeSize == 0)
471 return false;
472 else {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000473 // This implementation will write bytes beyond the end of each row,
474 // excluding the last row, if the bitmap's stride is greater than
475 // strictly required by the current config.
reed92fc2ae2015-05-22 08:06:21 -0700476 memcpy(dst, src.addr(), safeSize);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000477 return true;
478 }
479 } else {
480 // If destination has different stride than us, then copy line by line.
reed92fc2ae2015-05-22 08:06:21 -0700481 if (info.getSafeSize(dstRowBytes) > dstSize) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000482 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000483 } else {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000484 // Just copy what we need on each line.
reed92fc2ae2015-05-22 08:06:21 -0700485 size_t rowBytes = info.minRowBytes();
486 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src.addr());
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000487 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
reed92fc2ae2015-05-22 08:06:21 -0700488 for (int row = 0; row < info.height(); ++row) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000489 memcpy(dstP, srcP, rowBytes);
reed92fc2ae2015-05-22 08:06:21 -0700490 srcP += src.rowBytes();
491 dstP += dstRowBytes;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000492 }
493
494 return true;
495 }
496 }
497}
498
reed92fc2ae2015-05-22 08:06:21 -0700499bool SkBitmap::copyPixelsTo(void* dst, size_t dstSize, size_t dstRB, bool preserveDstPad) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700500 if (nullptr == dst) {
reed92fc2ae2015-05-22 08:06:21 -0700501 return false;
502 }
503 SkAutoPixmapUnlock result;
504 if (!this->requestLock(&result)) {
505 return false;
506 }
507 return copy_pixels_to(result.pixmap(), dst, dstSize, dstRB, preserveDstPad);
508}
509
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000510///////////////////////////////////////////////////////////////////////////////
511
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000512bool SkBitmap::isImmutable() const {
scroggo08470592014-07-15 19:56:48 -0700513 return fPixelRef ? fPixelRef->isImmutable() : false;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000514}
515
516void SkBitmap::setImmutable() {
517 if (fPixelRef) {
518 fPixelRef->setImmutable();
junov@chromium.orgb0521292011-12-15 20:14:06 +0000519 }
520}
521
junov@google.com4ee7ae52011-06-30 17:30:49 +0000522bool SkBitmap::isVolatile() const {
523 return (fFlags & kImageIsVolatile_Flag) != 0;
524}
525
526void SkBitmap::setIsVolatile(bool isVolatile) {
527 if (isVolatile) {
528 fFlags |= kImageIsVolatile_Flag;
529 } else {
530 fFlags &= ~kImageIsVolatile_Flag;
531 }
532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534void* SkBitmap::getAddr(int x, int y) const {
535 SkASSERT((unsigned)x < (unsigned)this->width());
536 SkASSERT((unsigned)y < (unsigned)this->height());
537
538 char* base = (char*)this->getPixels();
539 if (base) {
540 base += y * this->rowBytes();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000541 switch (this->colorType()) {
542 case kRGBA_8888_SkColorType:
543 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 base += x << 2;
545 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000546 case kARGB_4444_SkColorType:
547 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 base += x << 1;
549 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000550 case kAlpha_8_SkColorType:
551 case kIndex_8_SkColorType:
reed0c9b1a82015-03-17 17:44:06 -0700552 case kGray_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 base += x;
554 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000556 SkDEBUGFAIL("Can't return addr for config");
halcanary96fcdcc2015-08-27 07:41:13 -0700557 base = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 break;
559 }
560 }
561 return base;
562}
563
reedd96e7e52016-02-17 07:15:29 -0800564#include "SkHalf.h"
565
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000566SkColor SkBitmap::getColor(int x, int y) const {
567 SkASSERT((unsigned)x < (unsigned)this->width());
568 SkASSERT((unsigned)y < (unsigned)this->height());
569
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000570 switch (this->colorType()) {
reed0c9b1a82015-03-17 17:44:06 -0700571 case kGray_8_SkColorType: {
572 uint8_t* addr = this->getAddr8(x, y);
573 return SkColorSetRGB(*addr, *addr, *addr);
574 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000575 case kAlpha_8_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000576 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000577 return SkColorSetA(0, addr[0]);
578 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000579 case kIndex_8_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000580 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000581 return SkUnPreMultiply::PMColorToColor(c);
582 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000583 case kRGB_565_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000584 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000585 return SkPixel16ToColor(addr[0]);
586 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000587 case kARGB_4444_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000588 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000589 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
590 return SkUnPreMultiply::PMColorToColor(c);
591 }
halcanaryb260d132015-12-09 10:21:59 -0800592 case kBGRA_8888_SkColorType: {
593 uint32_t* addr = this->getAddr32(x, y);
594 SkPMColor c = SkSwizzle_BGRA_to_PMColor(addr[0]);
595 return SkUnPreMultiply::PMColorToColor(c);
596 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000597 case kRGBA_8888_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000598 uint32_t* addr = this->getAddr32(x, y);
halcanaryb260d132015-12-09 10:21:59 -0800599 SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]);
600 return SkUnPreMultiply::PMColorToColor(c);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000601 }
reedd96e7e52016-02-17 07:15:29 -0800602 case kRGBA_F16_SkColorType: {
603 const uint64_t* addr = (const uint64_t*)fPixels + y * (fRowBytes >> 3) + x;
604 Sk4f p4 = SkHalfToFloat_01(addr[0]);
605 if (p4[3]) {
606 float inva = 1 / p4[3];
607 p4 = p4 * Sk4f(inva, inva, inva, 1);
608 }
609 SkColor c;
610 SkNx_cast<uint8_t>(p4 * Sk4f(255) + Sk4f(0.5f)).store(&c);
611 // p4 is RGBA, but we want BGRA, so we need to swap next
612 return SkSwizzle_RB(c);
613 }
rmistry@google.comd6bab022013-12-02 13:50:38 +0000614 default:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000615 SkASSERT(false);
616 return 0;
617 }
618 SkASSERT(false); // Not reached.
619 return 0;
620}
621
reed92fc2ae2015-05-22 08:06:21 -0700622static bool compute_is_opaque(const SkPixmap& pmap) {
623 const int height = pmap.height();
624 const int width = pmap.width();
reed@google.com2a7579d2012-11-07 18:30:18 +0000625
reed92fc2ae2015-05-22 08:06:21 -0700626 switch (pmap.colorType()) {
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000627 case kAlpha_8_SkColorType: {
reed@google.com2a7579d2012-11-07 18:30:18 +0000628 unsigned a = 0xFF;
629 for (int y = 0; y < height; ++y) {
reed92fc2ae2015-05-22 08:06:21 -0700630 const uint8_t* row = pmap.addr8(0, y);
reed@google.com2a7579d2012-11-07 18:30:18 +0000631 for (int x = 0; x < width; ++x) {
632 a &= row[x];
633 }
634 if (0xFF != a) {
635 return false;
636 }
637 }
638 return true;
639 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000640 case kIndex_8_SkColorType: {
reed92fc2ae2015-05-22 08:06:21 -0700641 const SkColorTable* ctable = pmap.ctable();
halcanary96fcdcc2015-08-27 07:41:13 -0700642 if (nullptr == ctable) {
reed@google.com2a7579d2012-11-07 18:30:18 +0000643 return false;
644 }
reed92fc2ae2015-05-22 08:06:21 -0700645 const SkPMColor* table = ctable->readColors();
reed@google.com140d7282013-01-07 20:25:04 +0000646 SkPMColor c = (SkPMColor)~0;
reed92fc2ae2015-05-22 08:06:21 -0700647 for (int i = ctable->count() - 1; i >= 0; --i) {
reed@google.com2a7579d2012-11-07 18:30:18 +0000648 c &= table[i];
649 }
650 return 0xFF == SkGetPackedA32(c);
651 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000652 case kRGB_565_SkColorType:
reed0c9b1a82015-03-17 17:44:06 -0700653 case kGray_8_SkColorType:
reed@google.com2a7579d2012-11-07 18:30:18 +0000654 return true;
655 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000656 case kARGB_4444_SkColorType: {
reed@google.com2a7579d2012-11-07 18:30:18 +0000657 unsigned c = 0xFFFF;
658 for (int y = 0; y < height; ++y) {
reed92fc2ae2015-05-22 08:06:21 -0700659 const SkPMColor16* row = pmap.addr16(0, y);
reed@google.com2a7579d2012-11-07 18:30:18 +0000660 for (int x = 0; x < width; ++x) {
661 c &= row[x];
662 }
663 if (0xF != SkGetPackedA4444(c)) {
664 return false;
665 }
666 }
667 return true;
668 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000669 case kBGRA_8888_SkColorType:
670 case kRGBA_8888_SkColorType: {
reed@google.com140d7282013-01-07 20:25:04 +0000671 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000672 for (int y = 0; y < height; ++y) {
reed92fc2ae2015-05-22 08:06:21 -0700673 const SkPMColor* row = pmap.addr32(0, y);
reed@google.com2a7579d2012-11-07 18:30:18 +0000674 for (int x = 0; x < width; ++x) {
675 c &= row[x];
676 }
677 if (0xFF != SkGetPackedA32(c)) {
678 return false;
679 }
680 }
681 return true;
682 }
683 default:
684 break;
685 }
686 return false;
687}
688
reed92fc2ae2015-05-22 08:06:21 -0700689bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
690 SkAutoPixmapUnlock result;
691 if (!bm.requestLock(&result)) {
692 return false;
693 }
694 return compute_is_opaque(result.pixmap());
695}
696
reed@google.com2a7579d2012-11-07 18:30:18 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698///////////////////////////////////////////////////////////////////////////////
699///////////////////////////////////////////////////////////////////////////////
700
reed7aefe032015-06-08 10:22:22 -0700701void SkBitmap::erase(SkColor c, const SkIRect& area) const {
reed92fc2ae2015-05-22 08:06:21 -0700702 SkDEBUGCODE(this->validate();)
reed92fc2ae2015-05-22 08:06:21 -0700703
704 switch (fInfo.colorType()) {
705 case kUnknown_SkColorType:
706 case kIndex_8_SkColorType:
707 // TODO: can we ASSERT that we never get here?
708 return; // can't erase. Should we bzero so the memory is not uninitialized?
709 default:
710 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000712
reed92fc2ae2015-05-22 08:06:21 -0700713 SkAutoPixmapUnlock result;
714 if (!this->requestLock(&result)) {
715 return;
716 }
717
reed7aefe032015-06-08 10:22:22 -0700718 if (result.pixmap().erase(c, area)) {
reed92fc2ae2015-05-22 08:06:21 -0700719 this->notifyPixelsChanged();
720 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721}
722
reed7aefe032015-06-08 10:22:22 -0700723void SkBitmap::eraseColor(SkColor c) const {
724 this->erase(c, SkIRect::MakeWH(this->width(), this->height()));
reed@google.com60d32352013-06-28 19:40:50 +0000725}
726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727//////////////////////////////////////////////////////////////////////////////////////
728//////////////////////////////////////////////////////////////////////////////////////
729
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
731 SkDEBUGCODE(this->validate();)
732
halcanary96fcdcc2015-08-27 07:41:13 -0700733 if (nullptr == result || nullptr == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 return false; // no src pixels
735 }
736
737 SkIRect srcRect, r;
738 srcRect.set(0, 0, this->width(), this->height());
739 if (!r.intersect(srcRect, subset)) {
740 return false; // r is empty (i.e. no intersection)
741 }
742
halcanary96fcdcc2015-08-27 07:41:13 -0700743 if (fPixelRef->getTexture() != nullptr) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000744 // Do a deep copy
brianosmanb109b8c2016-06-16 13:03:24 -0700745 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->colorType(), this->colorSpace(), &subset);
halcanary96fcdcc2015-08-27 07:41:13 -0700746 if (pixelRef != nullptr) {
scroggo@google.coma2a31922012-12-07 19:14:45 +0000747 SkBitmap dst;
herbb5d74682016-04-21 08:45:39 -0700748 dst.setInfo(this->info().makeWH(subset.width(), subset.height()));
scroggo@google.coma2a31922012-12-07 19:14:45 +0000749 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000750 dst.setPixelRef(pixelRef)->unref();
751 SkDEBUGCODE(dst.validate());
752 result->swap(dst);
753 return true;
754 }
755 }
756
scroggo@google.coma2a31922012-12-07 19:14:45 +0000757 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
758 // exited above.
759 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
760 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
761
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 SkBitmap dst;
herbb5d74682016-04-21 08:45:39 -0700763 dst.setInfo(this->info().makeWH(r.width(), r.height()), this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000764 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765
766 if (fPixelRef) {
reed@google.com672588b2014-01-08 15:42:01 +0000767 SkIPoint origin = fPixelRefOrigin;
768 origin.fX += r.fLeft;
769 origin.fY += r.fTop;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 // share the pixelref with a custom offset
reed@google.com672588b2014-01-08 15:42:01 +0000771 dst.setPixelRef(fPixelRef, origin);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 }
773 SkDEBUGCODE(dst.validate();)
774
775 // we know we're good, so commit to result
776 result->swap(dst);
777 return true;
778}
779
780///////////////////////////////////////////////////////////////////////////////
781
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000782bool SkBitmap::canCopyTo(SkColorType dstColorType) const {
reedb184f7f2014-07-13 04:32:32 -0700783 const SkColorType srcCT = this->colorType();
784
785 if (srcCT == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 return false;
787 }
788
reedb184f7f2014-07-13 04:32:32 -0700789 bool sameConfigs = (srcCT == dstColorType);
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000790 switch (dstColorType) {
791 case kAlpha_8_SkColorType:
792 case kRGB_565_SkColorType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000793 case kRGBA_8888_SkColorType:
794 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000796 case kIndex_8_SkColorType:
weita@google.comf9ab99a2009-05-03 18:23:30 +0000797 if (!sameConfigs) {
798 return false;
799 }
800 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000801 case kARGB_4444_SkColorType:
reedb184f7f2014-07-13 04:32:32 -0700802 return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT;
reed0c9b1a82015-03-17 17:44:06 -0700803 case kGray_8_SkColorType:
804 switch (srcCT) {
805 case kGray_8_SkColorType:
806 case kRGBA_8888_SkColorType:
807 case kBGRA_8888_SkColorType:
808 return true;
809 default:
810 break;
811 }
812 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 default:
814 return false;
815 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000816 return true;
817}
818
reedb184f7f2014-07-13 04:32:32 -0700819bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
820 int x, int y) const {
reed95d343f2015-05-23 13:21:06 -0700821 SkAutoPixmapUnlock src;
822 if (!this->requestLock(&src)) {
reedb184f7f2014-07-13 04:32:32 -0700823 return false;
824 }
reed95d343f2015-05-23 13:21:06 -0700825 return src.pixmap().readPixels(requestedDstInfo, dstPixels, dstRB, x, y);
reedb184f7f2014-07-13 04:32:32 -0700826}
827
828bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000829 if (!this->canCopyTo(dstColorType)) {
reed@android.comfbaa88d2009-05-06 17:44:34 +0000830 return false;
831 }
832
reed@google.com50dfa012011-04-01 19:05:36 +0000833 // if we have a texture, first get those pixels
834 SkBitmap tmpSrc;
835 const SkBitmap* src = this;
836
scroggo@google.coma2a31922012-12-07 19:14:45 +0000837 if (fPixelRef) {
838 SkIRect subset;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000839 subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY,
840 fInfo.width(), fInfo.height());
bsalomon9d22fd62016-01-11 11:14:17 -0800841 if (fPixelRef->readPixels(&tmpSrc, dstColorType, &subset)) {
scroggo0187dc22014-06-05 11:18:04 -0700842 if (fPixelRef->info().alphaType() == kUnpremul_SkAlphaType) {
843 // FIXME: The only meaningful implementation of readPixels
844 // (GrPixelRef) assumes premultiplied pixels.
845 return false;
846 }
reed@google.com672588b2014-01-08 15:42:01 +0000847 SkASSERT(tmpSrc.width() == this->width());
848 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +0000849
reed@google.com672588b2014-01-08 15:42:01 +0000850 // did we get lucky and we can just return tmpSrc?
halcanary96fcdcc2015-08-27 07:41:13 -0700851 if (tmpSrc.colorType() == dstColorType && nullptr == alloc) {
reed@google.com672588b2014-01-08 15:42:01 +0000852 dst->swap(tmpSrc);
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000853 // If the result is an exact copy, clone the gen ID.
854 if (dst->pixelRef() && dst->pixelRef()->info() == fPixelRef->info()) {
reed@google.com672588b2014-01-08 15:42:01 +0000855 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000856 }
reed@google.com672588b2014-01-08 15:42:01 +0000857 return true;
scroggo@google.comd5764e82012-08-22 15:00:05 +0000858 }
reed@google.com672588b2014-01-08 15:42:01 +0000859
860 // fall through to the raster case
861 src = &tmpSrc;
reed@google.com50dfa012011-04-01 19:05:36 +0000862 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000863 }
reed@android.com311c82d2009-05-05 23:13:23 +0000864
reed95d343f2015-05-23 13:21:06 -0700865 SkAutoPixmapUnlock srcUnlocker;
866 if (!src->requestLock(&srcUnlocker)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000867 return false;
868 }
reed95d343f2015-05-23 13:21:06 -0700869 const SkPixmap& srcPM = srcUnlocker.pixmap();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000870
reed95d343f2015-05-23 13:21:06 -0700871 const SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
reed@google.com50dfa012011-04-01 19:05:36 +0000872 SkBitmap tmpDst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000873 if (!tmpDst.setInfo(dstInfo)) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000874 return false;
875 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000876
weita@google.comf9ab99a2009-05-03 18:23:30 +0000877 // allocate colortable if srcConfig == kIndex8_Config
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000878 SkAutoTUnref<SkColorTable> ctable;
879 if (dstColorType == kIndex_8_SkColorType) {
reed95d343f2015-05-23 13:21:06 -0700880 ctable.reset(SkRef(srcPM.ctable()));
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000881 }
reed84825042014-09-02 12:50:45 -0700882 if (!tmpDst.tryAllocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000883 return false;
884 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000885
reed95d343f2015-05-23 13:21:06 -0700886 SkAutoPixmapUnlock dstUnlocker;
887 if (!tmpDst.requestLock(&dstUnlocker)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 return false;
889 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000890
reed95d343f2015-05-23 13:21:06 -0700891 if (!srcPM.readPixels(dstUnlocker.pixmap())) {
reedb184f7f2014-07-13 04:32:32 -0700892 return false;
893 }
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000894
reedb184f7f2014-07-13 04:32:32 -0700895 // (for BitmapHeap) Clone the pixelref genID even though we have a new pixelref.
896 // The old copyTo impl did this, so we continue it for now.
897 //
898 // TODO: should we ignore rowbytes (i.e. getSize)? Then it could just be
899 // if (src_pixelref->info == dst_pixelref->info)
900 //
reed95d343f2015-05-23 13:21:06 -0700901 if (srcPM.colorType() == dstColorType && tmpDst.getSize() == srcPM.getSize64()) {
reedb184f7f2014-07-13 04:32:32 -0700902 SkPixelRef* dstPixelRef = tmpDst.pixelRef();
903 if (dstPixelRef->info() == fPixelRef->info()) {
904 dstPixelRef->cloneGenID(*fPixelRef);
reed@android.com311c82d2009-05-05 23:13:23 +0000905 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 }
907
reed@google.com50dfa012011-04-01 19:05:36 +0000908 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 return true;
910}
911
commit-bot@chromium.orgfab349c2014-03-05 02:34:58 +0000912bool SkBitmap::deepCopyTo(SkBitmap* dst) const {
reede4538f52014-06-11 06:09:50 -0700913 const SkColorType dstCT = this->colorType();
brianosmanb109b8c2016-06-16 13:03:24 -0700914 SkColorSpace* dstCS = this->colorSpace();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000915
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000916 if (!this->canCopyTo(dstCT)) {
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000917 return false;
918 }
919
920 // If we have a PixelRef, and it supports deep copy, use it.
921 // Currently supported only by texture-backed bitmaps.
922 if (fPixelRef) {
brianosmanb109b8c2016-06-16 13:03:24 -0700923 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstCT, dstCS, nullptr);
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +0000924 if (pixelRef) {
925 uint32_t rowBytes;
brianosmanb109b8c2016-06-16 13:03:24 -0700926 if (this->colorType() == dstCT && this->colorSpace() == dstCS) {
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +0000927 // Since there is no subset to pass to deepCopy, and deepCopy
928 // succeeded, the new pixel ref must be identical.
929 SkASSERT(fPixelRef->info() == pixelRef->info());
930 pixelRef->cloneGenID(*fPixelRef);
931 // Use the same rowBytes as the original.
932 rowBytes = fRowBytes;
933 } else {
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000934 // With the new config, an appropriate fRowBytes will be computed by setInfo.
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +0000935 rowBytes = 0;
936 }
937
reede5ea5002014-09-03 11:54:58 -0700938 const SkImageInfo info = fInfo.makeColorType(dstCT);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000939 if (!dst->setInfo(info, rowBytes)) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000940 return false;
941 }
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +0000942 dst->setPixelRef(pixelRef, fPixelRefOrigin)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000943 return true;
944 }
945 }
946
947 if (this->getTexture()) {
948 return false;
949 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700950 return this->copyTo(dst, dstCT, nullptr);
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000951 }
952}
953
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955
reed92fc2ae2015-05-22 08:06:21 -0700956static void rect_memset(uint8_t* array, U8CPU value, SkISize size, size_t rowBytes) {
957 for (int y = 0; y < size.height(); ++y) {
958 memset(array, value, size.width());
959 array += rowBytes;
reed@android.com1cdcb512009-08-24 19:11:00 +0000960 }
reed92fc2ae2015-05-22 08:06:21 -0700961}
reed@google.com82065d62011-02-07 15:30:46 +0000962
reed92fc2ae2015-05-22 08:06:21 -0700963static void get_bitmap_alpha(const SkPixmap& pmap, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
964 SkColorType colorType = pmap.colorType();
965 int w = pmap.width();
966 int h = pmap.height();
967 size_t rb = pmap.rowBytes();
968
969 if (kAlpha_8_SkColorType == colorType && !pmap.isOpaque()) {
970 const uint8_t* s = pmap.addr8(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 while (--h >= 0) {
972 memcpy(alpha, s, w);
973 s += rb;
974 alpha += alphaRowBytes;
975 }
reed92fc2ae2015-05-22 08:06:21 -0700976 } else if (kN32_SkColorType == colorType && !pmap.isOpaque()) {
977 const SkPMColor* SK_RESTRICT s = pmap.addr32(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 while (--h >= 0) {
979 for (int x = 0; x < w; x++) {
980 alpha[x] = SkGetPackedA32(s[x]);
981 }
982 s = (const SkPMColor*)((const char*)s + rb);
983 alpha += alphaRowBytes;
984 }
reed92fc2ae2015-05-22 08:06:21 -0700985 } else if (kARGB_4444_SkColorType == colorType && !pmap.isOpaque()) {
986 const SkPMColor16* SK_RESTRICT s = pmap.addr16(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 while (--h >= 0) {
988 for (int x = 0; x < w; x++) {
989 alpha[x] = SkPacked4444ToA32(s[x]);
990 }
991 s = (const SkPMColor16*)((const char*)s + rb);
992 alpha += alphaRowBytes;
993 }
reed92fc2ae2015-05-22 08:06:21 -0700994 } else if (kIndex_8_SkColorType == colorType && !pmap.isOpaque()) {
995 const SkColorTable* ct = pmap.ctable();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 if (ct) {
mtklein775b8192014-12-02 09:11:25 -0800997 const SkPMColor* SK_RESTRICT table = ct->readColors();
reed92fc2ae2015-05-22 08:06:21 -0700998 const uint8_t* SK_RESTRICT s = pmap.addr8(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 while (--h >= 0) {
1000 for (int x = 0; x < w; x++) {
1001 alpha[x] = SkGetPackedA32(table[s[x]]);
1002 }
1003 s += rb;
1004 alpha += alphaRowBytes;
1005 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 }
1007 } else { // src is opaque, so just fill alpha[] with 0xFF
reed92fc2ae2015-05-22 08:06:21 -07001008 rect_memset(alpha, 0xFF, pmap.info().dimensions(), alphaRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 }
reed92fc2ae2015-05-22 08:06:21 -07001010}
1011
1012static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
halcanary96fcdcc2015-08-27 07:41:13 -07001013 SkASSERT(alpha != nullptr);
reed92fc2ae2015-05-22 08:06:21 -07001014 SkASSERT(alphaRowBytes >= src.width());
1015
1016 SkAutoPixmapUnlock apl;
1017 if (!src.requestLock(&apl)) {
1018 rect_memset(alpha, 0, src.info().dimensions(), alphaRowBytes);
1019 return false;
1020 }
1021 get_bitmap_alpha(apl.pixmap(), alpha, alphaRowBytes);
reed@android.com1cdcb512009-08-24 19:11:00 +00001022 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023}
1024
1025#include "SkPaint.h"
1026#include "SkMaskFilter.h"
1027#include "SkMatrix.h"
1028
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001029bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001030 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 SkDEBUGCODE(this->validate();)
1032
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001033 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 SkMatrix identity;
1035 SkMask srcM, dstM;
1036
1037 srcM.fBounds.set(0, 0, this->width(), this->height());
1038 srcM.fRowBytes = SkAlign4(this->width());
1039 srcM.fFormat = SkMask::kA8_Format;
1040
halcanary96fcdcc2015-08-27 07:41:13 -07001041 SkMaskFilter* filter = paint ? paint->getMaskFilter() : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042
1043 // compute our (larger?) dst bounds if we have a filter
bsalomon49f085d2014-09-05 13:34:00 -07001044 if (filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 identity.reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001046 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 goto NO_FILTER_CASE;
1048 }
1049 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1050 } else {
1051 NO_FILTER_CASE:
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001052 tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -07001053 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001054 // Allocation of pixels for alpha bitmap failed.
1055 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1056 tmpBitmap.width(), tmpBitmap.height());
1057 return false;
1058 }
1059 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 if (offset) {
1061 offset->set(0, 0);
1062 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001063 tmpBitmap.swap(*dst);
1064 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001066 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1067 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068
1069 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -07001070 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 goto NO_FILTER_CASE;
1072 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001073 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001075 tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
1076 dstM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -07001077 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001078 // Allocation of pixels for alpha bitmap failed.
1079 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1080 tmpBitmap.width(), tmpBitmap.height());
1081 return false;
1082 }
1083 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 if (offset) {
1085 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1086 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001087 SkDEBUGCODE(tmpBitmap.validate();)
1088
1089 tmpBitmap.swap(*dst);
1090 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091}
1092
1093///////////////////////////////////////////////////////////////////////////////
1094
reed92fc2ae2015-05-22 08:06:21 -07001095static void write_raw_pixels(SkWriteBuffer* buffer, const SkPixmap& pmap) {
1096 const SkImageInfo& info = pmap.info();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001097 const size_t snugRB = info.width() * info.bytesPerPixel();
reed92fc2ae2015-05-22 08:06:21 -07001098 const char* src = (const char*)pmap.addr();
1099 const size_t ramRB = pmap.rowBytes();
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001100
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001101 buffer->write32(SkToU32(snugRB));
1102 info.flatten(*buffer);
1103
1104 const size_t size = snugRB * info.height();
scroggo565901d2015-12-10 10:44:13 -08001105 SkAutoTMalloc<char> storage(size);
1106 char* dst = storage.get();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001107 for (int y = 0; y < info.height(); ++y) {
1108 memcpy(dst, src, snugRB);
1109 dst += snugRB;
1110 src += ramRB;
1111 }
1112 buffer->writeByteArray(storage.get(), size);
1113
reed92fc2ae2015-05-22 08:06:21 -07001114 const SkColorTable* ct = pmap.ctable();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001115 if (kIndex_8_SkColorType == info.colorType() && ct) {
1116 buffer->writeBool(true);
1117 ct->writeToBuffer(*buffer);
1118 } else {
1119 buffer->writeBool(false);
1120 }
1121}
1122
reed92fc2ae2015-05-22 08:06:21 -07001123void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) {
1124 const SkImageInfo info = bitmap.info();
halcanary96fcdcc2015-08-27 07:41:13 -07001125 if (0 == info.width() || 0 == info.height() || nullptr == bitmap.pixelRef()) {
reed92fc2ae2015-05-22 08:06:21 -07001126 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
1127 return;
1128 }
1129
1130 SkAutoPixmapUnlock result;
1131 if (!bitmap.requestLock(&result)) {
1132 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
1133 return;
1134 }
1135
1136 write_raw_pixels(buffer, result.pixmap());
1137}
1138
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001139bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) {
1140 const size_t snugRB = buffer->readUInt();
1141 if (0 == snugRB) { // no pixels
1142 return false;
1143 }
1144
1145 SkImageInfo info;
1146 info.unflatten(*buffer);
1147
robertphillips74139f12016-06-28 09:04:34 -07001148 // If there was an error reading "info" or if it is bogus,
1149 // don't use it to compute minRowBytes()
1150 if (!buffer->validate(SkColorTypeValidateAlphaType(info.colorType(),
1151 info.alphaType()))) {
sugoica95c192014-07-08 09:18:48 -07001152 return false;
1153 }
1154
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001155 const size_t ramRB = info.minRowBytes();
sugoibd0d9da2015-01-07 08:47:44 -08001156 const int height = SkMax32(info.height(), 0);
1157 const uint64_t snugSize = sk_64_mul(snugRB, height);
1158 const uint64_t ramSize = sk_64_mul(ramRB, height);
1159 static const uint64_t max_size_t = (size_t)(-1);
1160 if (!buffer->validate((snugSize <= ramSize) && (ramSize <= max_size_t))) {
commit-bot@chromium.org05858432014-05-30 01:06:44 +00001161 return false;
1162 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001163
reedfde05112016-03-11 13:02:28 -08001164 sk_sp<SkData> data(SkData::MakeUninitialized(SkToSizeT(ramSize)));
robertphillips28937842015-06-08 07:10:49 -07001165 unsigned char* dst = (unsigned char*)data->writable_data();
sugoibd0d9da2015-01-07 08:47:44 -08001166 buffer->readByteArray(dst, SkToSizeT(snugSize));
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001167
1168 if (snugSize != ramSize) {
robertphillips28937842015-06-08 07:10:49 -07001169 const unsigned char* srcRow = dst + snugRB * (height - 1);
1170 unsigned char* dstRow = dst + ramRB * (height - 1);
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001171 for (int y = height - 1; y >= 1; --y) {
1172 memmove(dstRow, srcRow, snugRB);
1173 srcRow -= snugRB;
1174 dstRow -= ramRB;
1175 }
1176 SkASSERT(srcRow == dstRow); // first row does not need to be moved
1177 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001178
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001179 SkAutoTUnref<SkColorTable> ctable;
1180 if (buffer->readBool()) {
reedb236d1a2015-08-28 10:14:18 -07001181 ctable.reset(SkColorTable::Create(*buffer));
1182 if (!ctable) {
1183 return false;
1184 }
robertphillips28937842015-06-08 07:10:49 -07001185
reedb236d1a2015-08-28 10:14:18 -07001186 if (info.isEmpty()) {
1187 // require an empty ctable
1188 if (ctable->count() != 0) {
1189 buffer->validate(false);
1190 return false;
1191 }
1192 } else {
1193 // require a non-empty ctable
1194 if (ctable->count() == 0) {
1195 buffer->validate(false);
1196 return false;
1197 }
1198 unsigned char maxIndex = ctable->count() - 1;
1199 for (uint64_t i = 0; i < ramSize; ++i) {
1200 dst[i] = SkTMin(dst[i], maxIndex);
1201 }
robertphillips28937842015-06-08 07:10:49 -07001202 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001203 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001204
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001205 SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewWithData(info, info.minRowBytes(),
1206 ctable.get(), data.get()));
sugoi6af31472015-01-28 13:15:32 -08001207 if (!pr.get()) {
1208 return false;
1209 }
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001210 bitmap->setInfo(pr->info());
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001211 bitmap->setPixelRef(pr, 0, 0);
1212 return true;
1213}
1214
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215enum {
1216 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001217 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218};
1219
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220///////////////////////////////////////////////////////////////////////////////
1221
1222SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1223 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001224 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225}
1226
1227SkBitmap::RLEPixels::~RLEPixels() {
1228 sk_free(fYPtrs);
1229}
1230
1231///////////////////////////////////////////////////////////////////////////////
1232
1233#ifdef SK_DEBUG
1234void SkBitmap::validate() const {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001235 fInfo.validate();
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +00001236
1237 // ImageInfo may not require this, but Bitmap ensures that opaque-only
1238 // colorTypes report opaque for their alphatype
1239 if (kRGB_565_SkColorType == fInfo.colorType()) {
1240 SkASSERT(kOpaque_SkAlphaType == fInfo.alphaType());
1241 }
1242
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001243 SkASSERT(fInfo.validRowBytes(fRowBytes));
scroggo08470592014-07-15 19:56:48 -07001244 uint8_t allFlags = kImageIsVolatile_Flag;
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001245#ifdef SK_BUILD_FOR_ANDROID
1246 allFlags |= kHasHardwareMipMap_Flag;
1247#endif
scroggo08470592014-07-15 19:56:48 -07001248 SkASSERT((~allFlags & fFlags) == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 SkASSERT(fPixelLockCount >= 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250
reed@google.com615316c2014-01-15 19:15:23 +00001251 if (fPixels) {
1252 SkASSERT(fPixelRef);
robertphillipse77dadd2014-11-21 05:50:21 -08001253 SkASSERT(fPixelLockCount > 0);
reed@google.com615316c2014-01-15 19:15:23 +00001254 SkASSERT(fPixelRef->isLocked());
1255 SkASSERT(fPixelRef->rowBytes() == fRowBytes);
1256 SkASSERT(fPixelRefOrigin.fX >= 0);
1257 SkASSERT(fPixelRefOrigin.fY >= 0);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001258 SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
reede5ea5002014-09-03 11:54:58 -07001259 SkASSERT(fPixelRef->info().height() >= (int)this->height() + fPixelRefOrigin.fY);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001260 SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
reed@google.com615316c2014-01-15 19:15:23 +00001261 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07001262 SkASSERT(nullptr == fColorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264}
1265#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001266
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001267#ifndef SK_IGNORE_TO_STRING
bungemand3ebb482015-08-05 13:57:49 -07001268#include "SkString.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001269void SkBitmap::toString(SkString* str) const {
1270
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001271 static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = {
1272 "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8",
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001273 };
1274
1275 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001276 gColorTypeNames[this->colorType()]);
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001277
1278 str->append(" (");
1279 if (this->isOpaque()) {
1280 str->append("opaque");
1281 } else {
1282 str->append("transparent");
1283 }
1284 if (this->isImmutable()) {
1285 str->append(", immutable");
1286 } else {
1287 str->append(", not-immutable");
1288 }
1289 str->append(")");
1290
1291 SkPixelRef* pr = this->pixelRef();
halcanary96fcdcc2015-08-27 07:41:13 -07001292 if (nullptr == pr) {
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001293 // show null or the explicit pixel address (rare)
1294 str->appendf(" pixels:%p", this->getPixels());
1295 } else {
1296 const char* uri = pr->getURI();
bsalomon49f085d2014-09-05 13:34:00 -07001297 if (uri) {
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001298 str->appendf(" uri:\"%s\"", uri);
1299 } else {
1300 str->appendf(" pixelref:%p", pr);
1301 }
1302 }
1303
1304 str->append(")");
1305}
1306#endif
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001307
1308///////////////////////////////////////////////////////////////////////////////
1309
reed92fc2ae2015-05-22 08:06:21 -07001310bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const {
1311 SkASSERT(result);
1312
1313 SkPixelRef* pr = fPixelRef;
halcanary96fcdcc2015-08-27 07:41:13 -07001314 if (nullptr == pr) {
reed92fc2ae2015-05-22 08:06:21 -07001315 return false;
1316 }
1317
reed871872f2015-06-22 12:48:26 -07001318 // We have to lock the whole thing (using the pixelref's dimensions) until the api supports
1319 // a partial lock (with offset/origin). Hence we can't use our fInfo.
1320 SkPixelRef::LockRequest req = { pr->info().dimensions(), kNone_SkFilterQuality };
reed92fc2ae2015-05-22 08:06:21 -07001321 SkPixelRef::LockResult res;
1322 if (pr->requestLock(req, &res)) {
reede8006572015-05-28 14:06:06 -07001323 SkASSERT(res.fPixels);
reed92fc2ae2015-05-22 08:06:21 -07001324 // The bitmap may be a subset of the pixelref's dimensions
1325 SkASSERT(fPixelRefOrigin.x() + fInfo.width() <= res.fSize.width());
1326 SkASSERT(fPixelRefOrigin.y() + fInfo.height() <= res.fSize.height());
1327 const void* addr = (const char*)res.fPixels + SkColorTypeComputeOffset(fInfo.colorType(),
1328 fPixelRefOrigin.x(),
1329 fPixelRefOrigin.y(),
1330 res.fRowBytes);
1331
1332 result->reset(SkPixmap(this->info(), addr, res.fRowBytes, res.fCTable),
1333 res.fUnlockProc, res.fUnlockContext);
1334 return true;
1335 }
1336 return false;
1337}
1338
reedcb674142015-06-05 06:58:22 -07001339bool SkBitmap::peekPixels(SkPixmap* pmap) const {
1340 if (fPixels) {
1341 if (pmap) {
1342 pmap->reset(fInfo, fPixels, fRowBytes, fColorTable);
1343 }
1344 return true;
1345 }
1346 return false;
1347}
1348
reed92fc2ae2015-05-22 08:06:21 -07001349///////////////////////////////////////////////////////////////////////////////
1350
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001351#ifdef SK_DEBUG
1352void SkImageInfo::validate() const {
1353 SkASSERT(fWidth >= 0);
1354 SkASSERT(fHeight >= 0);
1355 SkASSERT(SkColorTypeIsValid(fColorType));
1356 SkASSERT(SkAlphaTypeIsValid(fAlphaType));
1357}
1358#endif