blob: 151650540bade3554974028cb2f15c8e79451188 [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"
Matt Sarett485c4992017-02-14 14:18:27 -050011#include "SkConvertPixels.h"
bungemand3ebb482015-08-05 13:57:49 -070012#include "SkData.h"
13#include "SkFilterQuality.h"
Hal Canary4cba3fe2016-12-07 14:59:27 -050014#include "SkHalf.h"
Matt Sarett03dd6d52017-01-23 12:15:09 -050015#include "SkImageInfoPriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkMallocPixelRef.h"
17#include "SkMask.h"
bungemand3ebb482015-08-05 13:57:49 -070018#include "SkMath.h"
jvanverth02802f62015-07-02 06:42:49 -070019#include "SkPixelRef.h"
mtklein1b249332015-07-07 12:21:21 -070020#include "SkReadBuffer.h"
bungemand3ebb482015-08-05 13:57:49 -070021#include "SkRect.h"
22#include "SkScalar.h"
scroggo565901d2015-12-10 10:44:13 -080023#include "SkTemplates.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000024#include "SkUnPreMultiply.h"
mtklein1b249332015-07-07 12:21:21 -070025#include "SkWriteBuffer.h"
Matt Sarett03dd6d52017-01-23 12:15:09 -050026#include "SkWritePixelsRec.h"
bungemand3ebb482015-08-05 13:57:49 -070027
28#include <string.h>
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000030static bool reset_return_false(SkBitmap* bm) {
31 bm->reset();
32 return false;
33}
34
Hal Canary1b3387b2016-12-12 13:48:12 -050035SkBitmap::SkBitmap()
36 : fPixelLockCount(0)
37 , fPixels (nullptr)
38 , fColorTable (nullptr)
39 , fPixelRefOrigin{0, 0}
40 , fRowBytes (0)
41 , fFlags (0) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
Hal Canary1b3387b2016-12-12 13:48:12 -050043// copy pixelref, but don't copy lock.
44SkBitmap::SkBitmap(const SkBitmap& src)
45 : fPixelRef (src.fPixelRef)
46 , fPixelLockCount(0)
47 , fPixels (nullptr)
48 , fColorTable (nullptr)
49 , fPixelRefOrigin(src.fPixelRefOrigin)
50 , fInfo (src.fInfo)
51 , fRowBytes (src.fRowBytes)
52 , fFlags (src.fFlags)
53{
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 SkDEBUGCODE(src.validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 SkDEBUGCODE(this->validate();)
56}
57
Hal Canary1b3387b2016-12-12 13:48:12 -050058// take lock and lockcount from other.
59SkBitmap::SkBitmap(SkBitmap&& other)
60 : fPixelRef (std::move(other.fPixelRef))
61 , fPixelLockCount (other.fPixelLockCount)
62 , fPixels (other.fPixels)
63 , fColorTable (other.fColorTable)
64 , fPixelRefOrigin (other.fPixelRefOrigin)
65 , fInfo (std::move(other.fInfo))
66 , fRowBytes (other.fRowBytes)
67 , fFlags (other.fFlags) {
68 SkASSERT(!other.fPixelRef);
69 other.fInfo.reset();
70 other.fPixelLockCount = 0;
71 other.fPixels = nullptr;
72 other.fColorTable = nullptr;
73 other.fPixelRefOrigin = SkIPoint{0, 0};
74 other.fRowBytes = 0;
75 other.fFlags = 0;
76}
halcanary023bda02015-12-14 10:19:17 -080077
reed@android.com8a1c16f2008-12-17 15:59:43 +000078SkBitmap::~SkBitmap() {
79 SkDEBUGCODE(this->validate();)
80 this->freePixels();
81}
82
83SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
84 if (this != &src) {
85 this->freePixels();
Hal Canary1b3387b2016-12-12 13:48:12 -050086 SkASSERT(!fPixels);
87 SkASSERT(!fColorTable);
88 SkASSERT(!fPixelLockCount);
89 fPixelRef = src.fPixelRef;
90 fPixelRefOrigin = src.fPixelRefOrigin;
91 fInfo = src.fInfo;
92 fRowBytes = src.fRowBytes;
93 fFlags = src.fFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 SkDEBUGCODE(this->validate();)
96 return *this;
97}
98
halcanary023bda02015-12-14 10:19:17 -080099SkBitmap& SkBitmap::operator=(SkBitmap&& other) {
100 if (this != &other) {
Hal Canary1b3387b2016-12-12 13:48:12 -0500101 this->freePixels();
102 SkASSERT(!fPixels);
103 SkASSERT(!fColorTable);
104 SkASSERT(!fPixelLockCount);
105 fPixelRef = std::move(other.fPixelRef);
106 fInfo = std::move(other.fInfo);
107 fPixelLockCount = other.fPixelLockCount;
108 fPixels = other.fPixels;
109 fColorTable = other.fColorTable;
110 fPixelRefOrigin = other.fPixelRefOrigin;
111 fRowBytes = other.fRowBytes;
112 fFlags = other.fFlags;
113 SkASSERT(!other.fPixelRef);
114 other.fInfo.reset();
115 other.fPixelLockCount = 0;
116 other.fPixels = nullptr;
117 other.fColorTable = nullptr;
118 other.fPixelRefOrigin = SkIPoint{0, 0};
119 other.fRowBytes = 0;
120 other.fFlags = 0;
halcanary023bda02015-12-14 10:19:17 -0800121 }
122 return *this;
123}
124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125void SkBitmap::swap(SkBitmap& other) {
Hal Canary1b3387b2016-12-12 13:48:12 -0500126 SkTSwap(*this, other);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 SkDEBUGCODE(this->validate();)
128}
129
130void SkBitmap::reset() {
131 this->freePixels();
msarett23c51102016-05-27 07:39:02 -0700132 this->fInfo.reset();
reed@android.com4516f472009-06-29 16:25:36 +0000133 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134}
135
reed@google.com86b2e432012-03-15 21:17:03 +0000136void SkBitmap::getBounds(SkRect* bounds) const {
137 SkASSERT(bounds);
138 bounds->set(0, 0,
reede5ea5002014-09-03 11:54:58 -0700139 SkIntToScalar(fInfo.width()), SkIntToScalar(fInfo.height()));
reed@google.com86b2e432012-03-15 21:17:03 +0000140}
141
reed@google.com80e14592012-03-16 14:58:07 +0000142void SkBitmap::getBounds(SkIRect* bounds) const {
143 SkASSERT(bounds);
reede5ea5002014-09-03 11:54:58 -0700144 bounds->set(0, 0, fInfo.width(), fInfo.height());
reed@google.com80e14592012-03-16 14:58:07 +0000145}
146
reed@google.com86b2e432012-03-15 21:17:03 +0000147///////////////////////////////////////////////////////////////////////////////
148
reede5ea5002014-09-03 11:54:58 -0700149bool SkBitmap::setInfo(const SkImageInfo& info, size_t rowBytes) {
150 SkAlphaType newAT = info.alphaType();
151 if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAT)) {
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +0000152 return reset_return_false(this);
153 }
reede5ea5002014-09-03 11:54:58 -0700154 // don't look at info.alphaType(), since newAT is the real value...
skia.committer@gmail.com02d6f542014-02-14 03:02:05 +0000155
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000156 // require that rowBytes fit in 31bits
157 int64_t mrb = info.minRowBytes64();
158 if ((int32_t)mrb != mrb) {
159 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000160 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000161 if ((int64_t)rowBytes != (int32_t)rowBytes) {
162 return reset_return_false(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000164
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000165 if (info.width() < 0 || info.height() < 0) {
166 return reset_return_false(this);
167 }
168
169 if (kUnknown_SkColorType == info.colorType()) {
170 rowBytes = 0;
171 } else if (0 == rowBytes) {
172 rowBytes = (size_t)mrb;
reedf0aed972014-07-01 12:48:11 -0700173 } else if (!info.validRowBytes(rowBytes)) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000174 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000175 }
176
177 this->freePixels();
178
reede5ea5002014-09-03 11:54:58 -0700179 fInfo = info.makeAlphaType(newAT);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000180 fRowBytes = SkToU32(rowBytes);
reed@google.com383a6972013-10-21 14:00:07 +0000181 return true;
reed@google.com383a6972013-10-21 14:00:07 +0000182}
183
reede5ea5002014-09-03 11:54:58 -0700184bool SkBitmap::setAlphaType(SkAlphaType newAlphaType) {
185 if (!SkColorTypeValidateAlphaType(fInfo.colorType(), newAlphaType, &newAlphaType)) {
reed@google.com383a6972013-10-21 14:00:07 +0000186 return false;
187 }
reede5ea5002014-09-03 11:54:58 -0700188 if (fInfo.alphaType() != newAlphaType) {
189 fInfo = fInfo.makeAlphaType(newAlphaType);
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000190 if (fPixelRef) {
reede5ea5002014-09-03 11:54:58 -0700191 fPixelRef->changeAlphaType(newAlphaType);
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000192 }
193 }
reed@google.com383a6972013-10-21 14:00:07 +0000194 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195}
196
197void SkBitmap::updatePixelsFromRef() const {
bsalomon49f085d2014-09-05 13:34:00 -0700198 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000200 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 void* p = fPixelRef->pixels();
bsalomon49f085d2014-09-05 13:34:00 -0700203 if (p) {
reed@google.com672588b2014-01-08 15:42:01 +0000204 p = (char*)p
reed@google.com303c4752014-01-09 20:00:14 +0000205 + fPixelRefOrigin.fY * fRowBytes
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000206 + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 }
208 fPixels = p;
reed@google.com5f62ed72014-01-15 19:59:45 +0000209 fColorTable = fPixelRef->colorTable();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 } else {
211 SkASSERT(0 == fPixelLockCount);
halcanary96fcdcc2015-08-27 07:41:13 -0700212 fPixels = nullptr;
213 fColorTable = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 }
215 }
216}
217
Hal Canary1b3387b2016-12-12 13:48:12 -0500218#ifdef SK_SUPPORT_LEGACY_BITMAP_SETPIXELREF
reed@google.com672588b2014-01-08 15:42:01 +0000219SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) {
Hal Canary1b3387b2016-12-12 13:48:12 -0500220 this->setPixelRef(sk_ref_sp(pr), dx, dy);
221 return pr;
222}
223#endif
224
225void SkBitmap::setPixelRef(sk_sp<SkPixelRef> pr, int dx, int dy) {
reed@google.comdcea5302014-01-03 13:43:01 +0000226#ifdef SK_DEBUG
reed@google.com672588b2014-01-08 15:42:01 +0000227 if (pr) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000228 if (kUnknown_SkColorType != fInfo.colorType()) {
reed@google.comdcea5302014-01-03 13:43:01 +0000229 const SkImageInfo& prInfo = pr->info();
reede5ea5002014-09-03 11:54:58 -0700230 SkASSERT(fInfo.width() <= prInfo.width());
231 SkASSERT(fInfo.height() <= prInfo.height());
232 SkASSERT(fInfo.colorType() == prInfo.colorType());
233 switch (prInfo.alphaType()) {
reed44977482015-02-27 10:23:00 -0800234 case kUnknown_SkAlphaType:
235 SkASSERT(fInfo.alphaType() == kUnknown_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000236 break;
237 case kOpaque_SkAlphaType:
238 case kPremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700239 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
240 fInfo.alphaType() == kPremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000241 break;
242 case kUnpremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700243 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
244 fInfo.alphaType() == kUnpremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000245 break;
246 }
247 }
248 }
249#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
reed@google.com672588b2014-01-08 15:42:01 +0000251 if (pr) {
252 const SkImageInfo& info = pr->info();
bungeman62ce0302015-08-28 09:09:32 -0700253 fPixelRefOrigin.set(SkTPin(dx, 0, info.width()), SkTPin(dy, 0, info.height()));
reed@google.com672588b2014-01-08 15:42:01 +0000254 } else {
255 // ignore dx,dy if there is no pixelref
256 fPixelRefOrigin.setZero();
257 }
258
259 if (fPixelRef != pr) {
piotaixr0eb02a62014-06-16 11:50:49 -0700260 this->freePixels();
Hal Canary1b3387b2016-12-12 13:48:12 -0500261 SkASSERT(!fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000262
Hal Canary1b3387b2016-12-12 13:48:12 -0500263 fPixelRef = std::move(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 this->updatePixelsFromRef();
265 }
266
267 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268}
269
270void SkBitmap::lockPixels() const {
bsalomon49f085d2014-09-05 13:34:00 -0700271 if (fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 fPixelRef->lockPixels();
273 this->updatePixelsFromRef();
274 }
275 SkDEBUGCODE(this->validate();)
276}
277
278void SkBitmap::unlockPixels() const {
Hal Canary1b3387b2016-12-12 13:48:12 -0500279 SkASSERT(!fPixelRef || fPixelLockCount > 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280
bsalomon49f085d2014-09-05 13:34:00 -0700281 if (fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fPixelRef->unlockPixels();
283 this->updatePixelsFromRef();
284 }
285 SkDEBUGCODE(this->validate();)
286}
287
reed@google.com9c49bc32011-07-07 13:42:37 +0000288bool SkBitmap::lockPixelsAreWritable() const {
Hal Canary1b3387b2016-12-12 13:48:12 -0500289 return fPixelRef ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000290}
291
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
halcanary96fcdcc2015-08-27 07:41:13 -0700293 if (nullptr == p) {
Hal Canary1b3387b2016-12-12 13:48:12 -0500294 this->setPixelRef(nullptr, 0, 0);
reed@google.com8e1034e2012-07-30 13:16:35 +0000295 return;
296 }
297
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000298 if (kUnknown_SkColorType == fInfo.colorType()) {
Hal Canary1b3387b2016-12-12 13:48:12 -0500299 this->setPixelRef(nullptr, 0, 0);
reed@google.combf790232013-12-13 19:45:58 +0000300 return;
301 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302
Mike Reed6b3155c2017-04-03 14:41:44 -0400303 this->setPixelRef(SkMallocPixelRef::MakeDirect(fInfo, p, fRowBytes, sk_ref_sp(ctable)), 0, 0);
Hal Canary1b3387b2016-12-12 13:48:12 -0500304 if (!fPixelRef) {
reed@google.combf790232013-12-13 19:45:58 +0000305 return;
306 }
djsollen@google.comc84b8332012-07-27 13:41:44 +0000307 // since we're already allocated, we lockPixels right away
308 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 SkDEBUGCODE(this->validate();)
310}
311
reed84825042014-09-02 12:50:45 -0700312bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 HeapAllocator stdalloc;
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000314
halcanary96fcdcc2015-08-27 07:41:13 -0700315 if (nullptr == allocator) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 allocator = &stdalloc;
317 }
318 return allocator->allocPixelRef(this, ctable);
319}
320
reed@google.com9ebcac52014-01-24 18:53:42 +0000321///////////////////////////////////////////////////////////////////////////////
322
reed84825042014-09-02 12:50:45 -0700323bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) {
reedbae704b2014-06-28 14:26:35 -0700324 if (kIndex_8_SkColorType == requestedInfo.colorType()) {
325 return reset_return_false(this);
326 }
reedf0aed972014-07-01 12:48:11 -0700327 if (!this->setInfo(requestedInfo, rowBytes)) {
reedbae704b2014-06-28 14:26:35 -0700328 return reset_return_false(this);
329 }
mtklein775b8192014-12-02 09:11:25 -0800330
reedbae704b2014-06-28 14:26:35 -0700331 // setInfo may have corrected info (e.g. 565 is always opaque).
332 const SkImageInfo& correctedInfo = this->info();
reedf0aed972014-07-01 12:48:11 -0700333 // setInfo may have computed a valid rowbytes if 0 were passed in
334 rowBytes = this->rowBytes();
335
Mike Reed6b3155c2017-04-03 14:41:44 -0400336 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(correctedInfo, rowBytes, nullptr);
Hal Canary1b3387b2016-12-12 13:48:12 -0500337 if (!pr) {
reedbae704b2014-06-28 14:26:35 -0700338 return reset_return_false(this);
339 }
Hal Canary1b3387b2016-12-12 13:48:12 -0500340 this->setPixelRef(std::move(pr), 0, 0);
mtklein775b8192014-12-02 09:11:25 -0800341
halcanary96fcdcc2015-08-27 07:41:13 -0700342 // TODO: lockPixels could/should return bool or void*/nullptr
reedbae704b2014-06-28 14:26:35 -0700343 this->lockPixels();
halcanary96fcdcc2015-08-27 07:41:13 -0700344 if (nullptr == this->getPixels()) {
reedbae704b2014-06-28 14:26:35 -0700345 return reset_return_false(this);
346 }
347 return true;
348}
349
Mike Reed6b3155c2017-04-03 14:41:44 -0400350bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, sk_sp<SkColorTable> ctable,
351 uint32_t allocFlags) {
halcanary96fcdcc2015-08-27 07:41:13 -0700352 if (kIndex_8_SkColorType == requestedInfo.colorType() && nullptr == ctable) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000353 return reset_return_false(this);
354 }
scroggo0187dc22014-06-05 11:18:04 -0700355 if (!this->setInfo(requestedInfo)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000356 return reset_return_false(this);
357 }
358
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
Mike Reed6b3155c2017-04-03 14:41:44 -0400362 sk_sp<SkPixelRef> pr = (allocFlags & kZeroPixels_AllocFlag) ?
363 SkMallocPixelRef::MakeZeroed(correctedInfo, correctedInfo.minRowBytes(), ctable) :
364 SkMallocPixelRef::MakeAllocate(correctedInfo, correctedInfo.minRowBytes(), ctable);
Hal Canary1b3387b2016-12-12 13:48:12 -0500365 if (!pr) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000366 return reset_return_false(this);
367 }
Hal Canary1b3387b2016-12-12 13:48:12 -0500368 this->setPixelRef(std::move(pr), 0, 0);
reed@google.com9ebcac52014-01-24 18:53:42 +0000369
reed@google.com9ebcac52014-01-24 18:53:42 +0000370 this->lockPixels();
halcanary96fcdcc2015-08-27 07:41:13 -0700371 if (nullptr == this->getPixels()) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000372 return reset_return_false(this);
373 }
374 return true;
375}
376
reeddb74f622015-05-30 13:41:15 -0700377static void invoke_release_proc(void (*proc)(void* pixels, void* ctx), void* pixels, void* ctx) {
378 if (proc) {
379 proc(pixels, ctx);
380 }
381}
382
scroggo0187dc22014-06-05 11:18:04 -0700383bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb,
384 SkColorTable* ct, void (*releaseProc)(void* addr, void* context),
385 void* context) {
386 if (!this->setInfo(requestedInfo, rb)) {
reeddb74f622015-05-30 13:41:15 -0700387 invoke_release_proc(releaseProc, pixels, context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000388 this->reset();
389 return false;
390 }
halcanary96fcdcc2015-08-27 07:41:13 -0700391 if (nullptr == pixels) {
reeddb74f622015-05-30 13:41:15 -0700392 invoke_release_proc(releaseProc, pixels, context);
393 return true; // we behaved as if they called setInfo()
394 }
reed@google.com9ebcac52014-01-24 18:53:42 +0000395
scroggo0187dc22014-06-05 11:18:04 -0700396 // setInfo may have corrected info (e.g. 565 is always opaque).
397 const SkImageInfo& correctedInfo = this->info();
398
Mike Reed6b3155c2017-04-03 14:41:44 -0400399 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithProc(correctedInfo, rb, sk_ref_sp(ct),
400 pixels, releaseProc, context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000401 if (!pr) {
402 this->reset();
403 return false;
404 }
405
Hal Canary1b3387b2016-12-12 13:48:12 -0500406 this->setPixelRef(std::move(pr), 0, 0);
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000407
408 // since we're already allocated, we lockPixels right away
409 this->lockPixels();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000410 SkDEBUGCODE(this->validate();)
reed@google.com9ebcac52014-01-24 18:53:42 +0000411 return true;
412}
413
halcanarye36ec872015-12-09 11:36:59 -0800414bool SkBitmap::installPixels(const SkPixmap& pixmap) {
415 return this->installPixels(pixmap.info(), pixmap.writable_addr(),
416 pixmap.rowBytes(), pixmap.ctable(),
417 nullptr, nullptr);
418}
419
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000420bool SkBitmap::installMaskPixels(const SkMask& mask) {
421 if (SkMask::kA8_Format != mask.fFormat) {
422 this->reset();
423 return false;
424 }
425 return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(),
426 mask.fBounds.height()),
427 mask.fImage, mask.fRowBytes);
428}
429
reed@google.comeb9a46c2014-01-25 16:46:20 +0000430///////////////////////////////////////////////////////////////////////////////
431
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432void SkBitmap::freePixels() {
bsalomon49f085d2014-09-05 13:34:00 -0700433 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 if (fPixelLockCount > 0) {
435 fPixelRef->unlockPixels();
436 }
halcanary96fcdcc2015-08-27 07:41:13 -0700437 fPixelRef = nullptr;
reed@google.com672588b2014-01-08 15:42:01 +0000438 fPixelRefOrigin.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 }
440 fPixelLockCount = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700441 fPixels = nullptr;
442 fColorTable = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443}
444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445uint32_t SkBitmap::getGenerationID() const {
Hal Canary1b3387b2016-12-12 13:48:12 -0500446 return fPixelRef ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447}
448
449void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000450 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 if (fPixelRef) {
452 fPixelRef->notifyPixelsChanged();
453 }
454}
455
456///////////////////////////////////////////////////////////////////////////////
457
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458/** We explicitly use the same allocator for our pixels that SkMask does,
459 so that we can freely assign memory allocated by one class to the other.
460 */
461bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
462 SkColorTable* ctable) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000463 const SkImageInfo info = dst->info();
464 if (kUnknown_SkColorType == info.colorType()) {
reed@google.combf790232013-12-13 19:45:58 +0000465// SkDebugf("unsupported config for info %d\n", dst->config());
466 return false;
467 }
skia.committer@gmail.com96f5fa02013-12-16 07:01:40 +0000468
Mike Reed6b3155c2017-04-03 14:41:44 -0400469 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, dst->rowBytes(), sk_ref_sp(ctable));
Hal Canary1b3387b2016-12-12 13:48:12 -0500470 if (!pr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 return false;
472 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000473
Hal Canary1b3387b2016-12-12 13:48:12 -0500474 dst->setPixelRef(std::move(pr), 0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 // since we're already allocated, we lockPixels right away
476 dst->lockPixels();
477 return true;
478}
479
480///////////////////////////////////////////////////////////////////////////////
481
reed92fc2ae2015-05-22 08:06:21 -0700482static bool copy_pixels_to(const SkPixmap& src, void* const dst, size_t dstSize,
483 size_t dstRowBytes, bool preserveDstPad) {
484 const SkImageInfo& info = src.info();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000485
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000486 if (0 == dstRowBytes) {
reed92fc2ae2015-05-22 08:06:21 -0700487 dstRowBytes = src.rowBytes();
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000488 }
reed92fc2ae2015-05-22 08:06:21 -0700489 if (dstRowBytes < info.minRowBytes()) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000490 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000491 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000492
reed92fc2ae2015-05-22 08:06:21 -0700493 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == src.rowBytes()) {
494 size_t safeSize = src.getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000495 if (safeSize > dstSize || safeSize == 0)
496 return false;
497 else {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000498 // This implementation will write bytes beyond the end of each row,
499 // excluding the last row, if the bitmap's stride is greater than
500 // strictly required by the current config.
reed92fc2ae2015-05-22 08:06:21 -0700501 memcpy(dst, src.addr(), safeSize);
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000502 return true;
503 }
504 } else {
505 // If destination has different stride than us, then copy line by line.
reed92fc2ae2015-05-22 08:06:21 -0700506 if (info.getSafeSize(dstRowBytes) > dstSize) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000507 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000508 } else {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000509 // Just copy what we need on each line.
reed92fc2ae2015-05-22 08:06:21 -0700510 size_t rowBytes = info.minRowBytes();
511 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src.addr());
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000512 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
reed92fc2ae2015-05-22 08:06:21 -0700513 for (int row = 0; row < info.height(); ++row) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000514 memcpy(dstP, srcP, rowBytes);
reed92fc2ae2015-05-22 08:06:21 -0700515 srcP += src.rowBytes();
516 dstP += dstRowBytes;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000517 }
518
519 return true;
520 }
521 }
522}
523
reed92fc2ae2015-05-22 08:06:21 -0700524bool SkBitmap::copyPixelsTo(void* dst, size_t dstSize, size_t dstRB, bool preserveDstPad) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700525 if (nullptr == dst) {
reed92fc2ae2015-05-22 08:06:21 -0700526 return false;
527 }
528 SkAutoPixmapUnlock result;
529 if (!this->requestLock(&result)) {
530 return false;
531 }
532 return copy_pixels_to(result.pixmap(), dst, dstSize, dstRB, preserveDstPad);
533}
534
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000535///////////////////////////////////////////////////////////////////////////////
536
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000537bool SkBitmap::isImmutable() const {
scroggo08470592014-07-15 19:56:48 -0700538 return fPixelRef ? fPixelRef->isImmutable() : false;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000539}
540
541void SkBitmap::setImmutable() {
542 if (fPixelRef) {
543 fPixelRef->setImmutable();
junov@chromium.orgb0521292011-12-15 20:14:06 +0000544 }
545}
546
junov@google.com4ee7ae52011-06-30 17:30:49 +0000547bool SkBitmap::isVolatile() const {
548 return (fFlags & kImageIsVolatile_Flag) != 0;
549}
550
551void SkBitmap::setIsVolatile(bool isVolatile) {
552 if (isVolatile) {
553 fFlags |= kImageIsVolatile_Flag;
554 } else {
555 fFlags &= ~kImageIsVolatile_Flag;
556 }
557}
558
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559void* SkBitmap::getAddr(int x, int y) const {
560 SkASSERT((unsigned)x < (unsigned)this->width());
561 SkASSERT((unsigned)y < (unsigned)this->height());
562
563 char* base = (char*)this->getPixels();
564 if (base) {
565 base += y * this->rowBytes();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000566 switch (this->colorType()) {
mtklein7fd93e32016-07-26 13:05:30 -0700567 case kRGBA_F16_SkColorType:
568 base += x << 3;
569 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000570 case kRGBA_8888_SkColorType:
571 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 base += x << 2;
573 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000574 case kARGB_4444_SkColorType:
575 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 base += x << 1;
577 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000578 case kAlpha_8_SkColorType:
579 case kIndex_8_SkColorType:
reed0c9b1a82015-03-17 17:44:06 -0700580 case kGray_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 base += x;
582 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000584 SkDEBUGFAIL("Can't return addr for config");
halcanary96fcdcc2015-08-27 07:41:13 -0700585 base = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 break;
587 }
588 }
589 return base;
590}
591
592///////////////////////////////////////////////////////////////////////////////
593///////////////////////////////////////////////////////////////////////////////
594
reed7aefe032015-06-08 10:22:22 -0700595void SkBitmap::erase(SkColor c, const SkIRect& area) const {
reed92fc2ae2015-05-22 08:06:21 -0700596 SkDEBUGCODE(this->validate();)
reed92fc2ae2015-05-22 08:06:21 -0700597
598 switch (fInfo.colorType()) {
599 case kUnknown_SkColorType:
600 case kIndex_8_SkColorType:
601 // TODO: can we ASSERT that we never get here?
602 return; // can't erase. Should we bzero so the memory is not uninitialized?
603 default:
604 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000606
reed92fc2ae2015-05-22 08:06:21 -0700607 SkAutoPixmapUnlock result;
608 if (!this->requestLock(&result)) {
609 return;
610 }
611
reed7aefe032015-06-08 10:22:22 -0700612 if (result.pixmap().erase(c, area)) {
reed92fc2ae2015-05-22 08:06:21 -0700613 this->notifyPixelsChanged();
614 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615}
616
reed7aefe032015-06-08 10:22:22 -0700617void SkBitmap::eraseColor(SkColor c) const {
618 this->erase(c, SkIRect::MakeWH(this->width(), this->height()));
reed@google.com60d32352013-06-28 19:40:50 +0000619}
620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621//////////////////////////////////////////////////////////////////////////////////////
622//////////////////////////////////////////////////////////////////////////////////////
623
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
625 SkDEBUGCODE(this->validate();)
626
Hal Canary1b3387b2016-12-12 13:48:12 -0500627 if (nullptr == result || !fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 return false; // no src pixels
629 }
630
631 SkIRect srcRect, r;
632 srcRect.set(0, 0, this->width(), this->height());
633 if (!r.intersect(srcRect, subset)) {
634 return false; // r is empty (i.e. no intersection)
635 }
636
scroggo@google.coma2a31922012-12-07 19:14:45 +0000637 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
638 // exited above.
639 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
640 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 SkBitmap dst;
herbb5d74682016-04-21 08:45:39 -0700643 dst.setInfo(this->info().makeWH(r.width(), r.height()), this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000644 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645
646 if (fPixelRef) {
reed@google.com672588b2014-01-08 15:42:01 +0000647 SkIPoint origin = fPixelRefOrigin;
648 origin.fX += r.fLeft;
649 origin.fY += r.fTop;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 // share the pixelref with a custom offset
Hal Canary1b3387b2016-12-12 13:48:12 -0500651 dst.setPixelRef(fPixelRef, origin.x(), origin.y());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 }
653 SkDEBUGCODE(dst.validate();)
654
655 // we know we're good, so commit to result
656 result->swap(dst);
657 return true;
658}
659
660///////////////////////////////////////////////////////////////////////////////
661
Mike Reed22f34822016-11-28 17:17:38 -0500662bool SkBitmap::canCopyTo(SkColorType dstCT) const {
reedb184f7f2014-07-13 04:32:32 -0700663 const SkColorType srcCT = this->colorType();
664
665 if (srcCT == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 return false;
667 }
Mike Reed22f34822016-11-28 17:17:38 -0500668 if (srcCT == kAlpha_8_SkColorType && dstCT != kAlpha_8_SkColorType) {
669 return false; // can't convert from alpha to non-alpha
670 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671
Mike Reed22f34822016-11-28 17:17:38 -0500672 bool sameConfigs = (srcCT == dstCT);
673 switch (dstCT) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000674 case kAlpha_8_SkColorType:
675 case kRGB_565_SkColorType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000676 case kRGBA_8888_SkColorType:
677 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 break;
Matt Sarettcb6266b2017-01-17 10:48:53 -0500679 case kGray_8_SkColorType:
weita@google.comf9ab99a2009-05-03 18:23:30 +0000680 if (!sameConfigs) {
681 return false;
682 }
683 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000684 case kARGB_4444_SkColorType:
reedb184f7f2014-07-13 04:32:32 -0700685 return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 default:
687 return false;
688 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000689 return true;
690}
691
reedb184f7f2014-07-13 04:32:32 -0700692bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
693 int x, int y) const {
reed95d343f2015-05-23 13:21:06 -0700694 SkAutoPixmapUnlock src;
695 if (!this->requestLock(&src)) {
reedb184f7f2014-07-13 04:32:32 -0700696 return false;
697 }
reed95d343f2015-05-23 13:21:06 -0700698 return src.pixmap().readPixels(requestedDstInfo, dstPixels, dstRB, x, y);
reedb184f7f2014-07-13 04:32:32 -0700699}
700
Mike Reed68dd8d02017-01-04 16:34:31 -0500701bool SkBitmap::readPixels(const SkPixmap& dst, int srcX, int srcY) const {
702 return this->readPixels(dst.info(), dst.writable_addr(), dst.rowBytes(), srcX, srcY);
703}
704
Matt Sarettd2adc662017-03-27 15:07:35 -0400705bool SkBitmap::writePixels(const SkPixmap& src, int dstX, int dstY,
706 SkTransferFunctionBehavior behavior) {
Mike Reed68dd8d02017-01-04 16:34:31 -0500707 SkAutoPixmapUnlock dst;
708 if (!this->requestLock(&dst)) {
709 return false;
710 }
711
Matt Sarett03dd6d52017-01-23 12:15:09 -0500712 if (!SkImageInfoValidConversion(fInfo, src.info())) {
Mike Reed68dd8d02017-01-04 16:34:31 -0500713 return false;
714 }
715
Matt Sarett03dd6d52017-01-23 12:15:09 -0500716 SkWritePixelsRec rec(src.info(), src.addr(), src.rowBytes(), dstX, dstY);
717 if (!rec.trim(fInfo.width(), fInfo.height())) {
718 return false;
719 }
720
721 void* dstPixels = this->getAddr(rec.fX, rec.fY);
722 const SkImageInfo dstInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
Matt Sarett485c4992017-02-14 14:18:27 -0500723 SkConvertPixels(dstInfo, dstPixels, this->rowBytes(), rec.fInfo, rec.fPixels, rec.fRowBytes,
Matt Sarettd2adc662017-03-27 15:07:35 -0400724 src.ctable(), behavior);
Matt Sarett8572d852017-02-14 11:21:02 -0500725 return true;
Mike Reed68dd8d02017-01-04 16:34:31 -0500726}
727
reedb184f7f2014-07-13 04:32:32 -0700728bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000729 if (!this->canCopyTo(dstColorType)) {
reed@android.comfbaa88d2009-05-06 17:44:34 +0000730 return false;
731 }
732
reed95d343f2015-05-23 13:21:06 -0700733 SkAutoPixmapUnlock srcUnlocker;
Matt Sarett19873662017-04-05 13:36:08 -0400734 if (!this->requestLock(&srcUnlocker)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000735 return false;
736 }
Matt Sarettcb6266b2017-01-17 10:48:53 -0500737 SkPixmap srcPM = srcUnlocker.pixmap();
738 if (kRGB_565_SkColorType == dstColorType && kOpaque_SkAlphaType != srcPM.alphaType()) {
739 // copyTo() is not strict on alpha type. Here we set the src to opaque to allow
740 // the call to readPixels() to succeed and preserve this lenient behavior.
741 srcPM = SkPixmap(srcPM.info().makeAlphaType(kOpaque_SkAlphaType), srcPM.addr(),
742 srcPM.rowBytes(), srcPM.ctable());
743 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000744
reed95d343f2015-05-23 13:21:06 -0700745 const SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
reed@google.com50dfa012011-04-01 19:05:36 +0000746 SkBitmap tmpDst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000747 if (!tmpDst.setInfo(dstInfo)) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000748 return false;
749 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000750
weita@google.comf9ab99a2009-05-03 18:23:30 +0000751 // allocate colortable if srcConfig == kIndex8_Config
Hal Canary704cd322016-11-07 14:13:52 -0500752 sk_sp<SkColorTable> ctable;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000753 if (dstColorType == kIndex_8_SkColorType) {
reed95d343f2015-05-23 13:21:06 -0700754 ctable.reset(SkRef(srcPM.ctable()));
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000755 }
Hal Canary704cd322016-11-07 14:13:52 -0500756 if (!tmpDst.tryAllocPixels(alloc, ctable.get())) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000757 return false;
758 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000759
reed95d343f2015-05-23 13:21:06 -0700760 SkAutoPixmapUnlock dstUnlocker;
761 if (!tmpDst.requestLock(&dstUnlocker)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 return false;
763 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000764
reed95d343f2015-05-23 13:21:06 -0700765 if (!srcPM.readPixels(dstUnlocker.pixmap())) {
reedb184f7f2014-07-13 04:32:32 -0700766 return false;
767 }
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000768
reedb184f7f2014-07-13 04:32:32 -0700769 // (for BitmapHeap) Clone the pixelref genID even though we have a new pixelref.
770 // The old copyTo impl did this, so we continue it for now.
771 //
772 // TODO: should we ignore rowbytes (i.e. getSize)? Then it could just be
773 // if (src_pixelref->info == dst_pixelref->info)
774 //
reed95d343f2015-05-23 13:21:06 -0700775 if (srcPM.colorType() == dstColorType && tmpDst.getSize() == srcPM.getSize64()) {
reedb184f7f2014-07-13 04:32:32 -0700776 SkPixelRef* dstPixelRef = tmpDst.pixelRef();
777 if (dstPixelRef->info() == fPixelRef->info()) {
778 dstPixelRef->cloneGenID(*fPixelRef);
reed@android.com311c82d2009-05-05 23:13:23 +0000779 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 }
781
reed@google.com50dfa012011-04-01 19:05:36 +0000782 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 return true;
784}
785
reedc7ec7c92016-07-25 08:29:10 -0700786// TODO: can we merge this with copyTo?
commit-bot@chromium.orgfab349c2014-03-05 02:34:58 +0000787bool SkBitmap::deepCopyTo(SkBitmap* dst) const {
reede4538f52014-06-11 06:09:50 -0700788 const SkColorType dstCT = this->colorType();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000789
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000790 if (!this->canCopyTo(dstCT)) {
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000791 return false;
792 }
reedc7ec7c92016-07-25 08:29:10 -0700793 return this->copyTo(dst, dstCT, nullptr);
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000794}
795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797
reed92fc2ae2015-05-22 08:06:21 -0700798static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
halcanary96fcdcc2015-08-27 07:41:13 -0700799 SkASSERT(alpha != nullptr);
reed92fc2ae2015-05-22 08:06:21 -0700800 SkASSERT(alphaRowBytes >= src.width());
801
802 SkAutoPixmapUnlock apl;
803 if (!src.requestLock(&apl)) {
lsalzmana2415ac2016-10-11 14:29:12 -0700804 for (int y = 0; y < src.height(); ++y) {
805 memset(alpha, 0, src.width());
806 alpha += alphaRowBytes;
807 }
reed92fc2ae2015-05-22 08:06:21 -0700808 return false;
809 }
lsalzmana2415ac2016-10-11 14:29:12 -0700810 const SkPixmap& pmap = apl.pixmap();
Matt Sarett485c4992017-02-14 14:18:27 -0500811 SkConvertPixels(SkImageInfo::MakeA8(pmap.width(), pmap.height()), alpha, alphaRowBytes,
Matt Sarettd2adc662017-03-27 15:07:35 -0400812 pmap.info(), pmap.addr(), pmap.rowBytes(), pmap.ctable(),
813 SkTransferFunctionBehavior::kRespect);
reed@android.com1cdcb512009-08-24 19:11:00 +0000814 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815}
816
817#include "SkPaint.h"
818#include "SkMaskFilter.h"
819#include "SkMatrix.h"
820
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000821bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +0000822 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 SkDEBUGCODE(this->validate();)
824
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000825 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 SkMatrix identity;
827 SkMask srcM, dstM;
828
829 srcM.fBounds.set(0, 0, this->width(), this->height());
830 srcM.fRowBytes = SkAlign4(this->width());
831 srcM.fFormat = SkMask::kA8_Format;
832
halcanary96fcdcc2015-08-27 07:41:13 -0700833 SkMaskFilter* filter = paint ? paint->getMaskFilter() : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834
835 // compute our (larger?) dst bounds if we have a filter
bsalomon49f085d2014-09-05 13:34:00 -0700836 if (filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 identity.reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700838 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 goto NO_FILTER_CASE;
840 }
841 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
842 } else {
843 NO_FILTER_CASE:
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000844 tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -0700845 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000846 // Allocation of pixels for alpha bitmap failed.
847 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
848 tmpBitmap.width(), tmpBitmap.height());
849 return false;
850 }
851 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 if (offset) {
853 offset->set(0, 0);
854 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000855 tmpBitmap.swap(*dst);
856 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000858 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
859 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860
861 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -0700862 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 goto NO_FILTER_CASE;
864 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000865 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000867 tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
868 dstM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -0700869 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000870 // Allocation of pixels for alpha bitmap failed.
871 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
872 tmpBitmap.width(), tmpBitmap.height());
873 return false;
874 }
875 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 if (offset) {
877 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
878 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000879 SkDEBUGCODE(tmpBitmap.validate();)
880
881 tmpBitmap.swap(*dst);
882 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883}
884
885///////////////////////////////////////////////////////////////////////////////
886
reed92fc2ae2015-05-22 08:06:21 -0700887static void write_raw_pixels(SkWriteBuffer* buffer, const SkPixmap& pmap) {
888 const SkImageInfo& info = pmap.info();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000889 const size_t snugRB = info.width() * info.bytesPerPixel();
reed92fc2ae2015-05-22 08:06:21 -0700890 const char* src = (const char*)pmap.addr();
891 const size_t ramRB = pmap.rowBytes();
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +0000892
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000893 buffer->write32(SkToU32(snugRB));
894 info.flatten(*buffer);
895
896 const size_t size = snugRB * info.height();
scroggo565901d2015-12-10 10:44:13 -0800897 SkAutoTMalloc<char> storage(size);
898 char* dst = storage.get();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000899 for (int y = 0; y < info.height(); ++y) {
900 memcpy(dst, src, snugRB);
901 dst += snugRB;
902 src += ramRB;
903 }
904 buffer->writeByteArray(storage.get(), size);
905
reed92fc2ae2015-05-22 08:06:21 -0700906 const SkColorTable* ct = pmap.ctable();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000907 if (kIndex_8_SkColorType == info.colorType() && ct) {
908 buffer->writeBool(true);
909 ct->writeToBuffer(*buffer);
910 } else {
911 buffer->writeBool(false);
912 }
913}
914
reed92fc2ae2015-05-22 08:06:21 -0700915void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) {
916 const SkImageInfo info = bitmap.info();
Hal Canary1b3387b2016-12-12 13:48:12 -0500917 if (0 == info.width() || 0 == info.height() || bitmap.isNull()) {
reed92fc2ae2015-05-22 08:06:21 -0700918 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
919 return;
920 }
921
922 SkAutoPixmapUnlock result;
923 if (!bitmap.requestLock(&result)) {
924 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
925 return;
926 }
927
928 write_raw_pixels(buffer, result.pixmap());
929}
930
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000931bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) {
932 const size_t snugRB = buffer->readUInt();
933 if (0 == snugRB) { // no pixels
934 return false;
935 }
936
937 SkImageInfo info;
938 info.unflatten(*buffer);
939
Robert Phillipsb2cb5352016-12-20 12:44:41 -0500940 if (info.width() < 0 || info.height() < 0) {
941 return false;
942 }
943
mtklein58e389b2016-07-15 07:00:11 -0700944 // If there was an error reading "info" or if it is bogus,
robertphillips74139f12016-06-28 09:04:34 -0700945 // don't use it to compute minRowBytes()
946 if (!buffer->validate(SkColorTypeValidateAlphaType(info.colorType(),
947 info.alphaType()))) {
sugoica95c192014-07-08 09:18:48 -0700948 return false;
949 }
950
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000951 const size_t ramRB = info.minRowBytes();
sugoibd0d9da2015-01-07 08:47:44 -0800952 const int height = SkMax32(info.height(), 0);
953 const uint64_t snugSize = sk_64_mul(snugRB, height);
954 const uint64_t ramSize = sk_64_mul(ramRB, height);
955 static const uint64_t max_size_t = (size_t)(-1);
956 if (!buffer->validate((snugSize <= ramSize) && (ramSize <= max_size_t))) {
commit-bot@chromium.org05858432014-05-30 01:06:44 +0000957 return false;
958 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000959
reedfde05112016-03-11 13:02:28 -0800960 sk_sp<SkData> data(SkData::MakeUninitialized(SkToSizeT(ramSize)));
robertphillips28937842015-06-08 07:10:49 -0700961 unsigned char* dst = (unsigned char*)data->writable_data();
sugoibd0d9da2015-01-07 08:47:44 -0800962 buffer->readByteArray(dst, SkToSizeT(snugSize));
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000963
964 if (snugSize != ramSize) {
robertphillips28937842015-06-08 07:10:49 -0700965 const unsigned char* srcRow = dst + snugRB * (height - 1);
966 unsigned char* dstRow = dst + ramRB * (height - 1);
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000967 for (int y = height - 1; y >= 1; --y) {
968 memmove(dstRow, srcRow, snugRB);
969 srcRow -= snugRB;
970 dstRow -= ramRB;
971 }
972 SkASSERT(srcRow == dstRow); // first row does not need to be moved
973 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +0000974
Hal Canary704cd322016-11-07 14:13:52 -0500975 sk_sp<SkColorTable> ctable;
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000976 if (buffer->readBool()) {
Mike Reed6b3155c2017-04-03 14:41:44 -0400977 ctable = SkColorTable::Create(*buffer);
reedb236d1a2015-08-28 10:14:18 -0700978 if (!ctable) {
979 return false;
980 }
robertphillips28937842015-06-08 07:10:49 -0700981
reedb236d1a2015-08-28 10:14:18 -0700982 if (info.isEmpty()) {
983 // require an empty ctable
984 if (ctable->count() != 0) {
985 buffer->validate(false);
986 return false;
987 }
988 } else {
989 // require a non-empty ctable
990 if (ctable->count() == 0) {
991 buffer->validate(false);
992 return false;
993 }
994 unsigned char maxIndex = ctable->count() - 1;
995 for (uint64_t i = 0; i < ramSize; ++i) {
996 dst[i] = SkTMin(dst[i], maxIndex);
997 }
robertphillips28937842015-06-08 07:10:49 -0700998 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000999 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001000
Mike Reed6b3155c2017-04-03 14:41:44 -04001001 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithData(info, info.minRowBytes(),
1002 std::move(ctable), std::move(data));
1003 if (!pr) {
sugoi6af31472015-01-28 13:15:32 -08001004 return false;
1005 }
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001006 bitmap->setInfo(pr->info());
Hal Canary1b3387b2016-12-12 13:48:12 -05001007 bitmap->setPixelRef(std::move(pr), 0, 0);
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001008 return true;
1009}
1010
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011enum {
1012 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001013 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014};
1015
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016///////////////////////////////////////////////////////////////////////////////
1017
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018#ifdef SK_DEBUG
1019void SkBitmap::validate() const {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001020 fInfo.validate();
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +00001021
1022 // ImageInfo may not require this, but Bitmap ensures that opaque-only
1023 // colorTypes report opaque for their alphatype
1024 if (kRGB_565_SkColorType == fInfo.colorType()) {
1025 SkASSERT(kOpaque_SkAlphaType == fInfo.alphaType());
1026 }
1027
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001028 SkASSERT(fInfo.validRowBytes(fRowBytes));
scroggo08470592014-07-15 19:56:48 -07001029 uint8_t allFlags = kImageIsVolatile_Flag;
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001030#ifdef SK_BUILD_FOR_ANDROID
1031 allFlags |= kHasHardwareMipMap_Flag;
1032#endif
scroggo08470592014-07-15 19:56:48 -07001033 SkASSERT((~allFlags & fFlags) == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 SkASSERT(fPixelLockCount >= 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035
reed@google.com615316c2014-01-15 19:15:23 +00001036 if (fPixels) {
1037 SkASSERT(fPixelRef);
robertphillipse77dadd2014-11-21 05:50:21 -08001038 SkASSERT(fPixelLockCount > 0);
reed@google.com615316c2014-01-15 19:15:23 +00001039 SkASSERT(fPixelRef->isLocked());
1040 SkASSERT(fPixelRef->rowBytes() == fRowBytes);
1041 SkASSERT(fPixelRefOrigin.fX >= 0);
1042 SkASSERT(fPixelRefOrigin.fY >= 0);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001043 SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
reede5ea5002014-09-03 11:54:58 -07001044 SkASSERT(fPixelRef->info().height() >= (int)this->height() + fPixelRefOrigin.fY);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001045 SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
reed@google.com615316c2014-01-15 19:15:23 +00001046 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07001047 SkASSERT(nullptr == fColorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049}
1050#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001051
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001052#ifndef SK_IGNORE_TO_STRING
bungemand3ebb482015-08-05 13:57:49 -07001053#include "SkString.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001054void SkBitmap::toString(SkString* str) const {
1055
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001056 static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = {
1057 "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8",
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001058 };
1059
1060 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001061 gColorTypeNames[this->colorType()]);
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001062
1063 str->append(" (");
1064 if (this->isOpaque()) {
1065 str->append("opaque");
1066 } else {
1067 str->append("transparent");
1068 }
1069 if (this->isImmutable()) {
1070 str->append(", immutable");
1071 } else {
1072 str->append(", not-immutable");
1073 }
1074 str->append(")");
1075
1076 SkPixelRef* pr = this->pixelRef();
halcanary96fcdcc2015-08-27 07:41:13 -07001077 if (nullptr == pr) {
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001078 // show null or the explicit pixel address (rare)
1079 str->appendf(" pixels:%p", this->getPixels());
1080 } else {
1081 const char* uri = pr->getURI();
bsalomon49f085d2014-09-05 13:34:00 -07001082 if (uri) {
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001083 str->appendf(" uri:\"%s\"", uri);
1084 } else {
1085 str->appendf(" pixelref:%p", pr);
1086 }
1087 }
1088
1089 str->append(")");
1090}
1091#endif
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001092
1093///////////////////////////////////////////////////////////////////////////////
1094
reed92fc2ae2015-05-22 08:06:21 -07001095bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const {
1096 SkASSERT(result);
1097
Hal Canary1b3387b2016-12-12 13:48:12 -05001098 SkPixelRef* pr = fPixelRef.get();
halcanary96fcdcc2015-08-27 07:41:13 -07001099 if (nullptr == pr) {
reed92fc2ae2015-05-22 08:06:21 -07001100 return false;
1101 }
1102
reed871872f2015-06-22 12:48:26 -07001103 // We have to lock the whole thing (using the pixelref's dimensions) until the api supports
1104 // a partial lock (with offset/origin). Hence we can't use our fInfo.
1105 SkPixelRef::LockRequest req = { pr->info().dimensions(), kNone_SkFilterQuality };
reed92fc2ae2015-05-22 08:06:21 -07001106 SkPixelRef::LockResult res;
1107 if (pr->requestLock(req, &res)) {
reede8006572015-05-28 14:06:06 -07001108 SkASSERT(res.fPixels);
reed92fc2ae2015-05-22 08:06:21 -07001109 // The bitmap may be a subset of the pixelref's dimensions
1110 SkASSERT(fPixelRefOrigin.x() + fInfo.width() <= res.fSize.width());
1111 SkASSERT(fPixelRefOrigin.y() + fInfo.height() <= res.fSize.height());
1112 const void* addr = (const char*)res.fPixels + SkColorTypeComputeOffset(fInfo.colorType(),
1113 fPixelRefOrigin.x(),
1114 fPixelRefOrigin.y(),
1115 res.fRowBytes);
1116
1117 result->reset(SkPixmap(this->info(), addr, res.fRowBytes, res.fCTable),
1118 res.fUnlockProc, res.fUnlockContext);
1119 return true;
1120 }
1121 return false;
1122}
1123
reedcb674142015-06-05 06:58:22 -07001124bool SkBitmap::peekPixels(SkPixmap* pmap) const {
1125 if (fPixels) {
1126 if (pmap) {
1127 pmap->reset(fInfo, fPixels, fRowBytes, fColorTable);
1128 }
1129 return true;
1130 }
1131 return false;
1132}
1133
reed92fc2ae2015-05-22 08:06:21 -07001134///////////////////////////////////////////////////////////////////////////////
1135
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001136#ifdef SK_DEBUG
1137void SkImageInfo::validate() const {
1138 SkASSERT(fWidth >= 0);
1139 SkASSERT(fHeight >= 0);
1140 SkASSERT(SkColorTypeIsValid(fColorType));
1141 SkASSERT(SkAlphaTypeIsValid(fAlphaType));
1142}
1143#endif