blob: 9b8ea7f96c7baebb6d550be602cfcf768fca4ab8 [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 -0500218void SkBitmap::setPixelRef(sk_sp<SkPixelRef> pr, int dx, int dy) {
reed@google.comdcea5302014-01-03 13:43:01 +0000219#ifdef SK_DEBUG
reed@google.com672588b2014-01-08 15:42:01 +0000220 if (pr) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000221 if (kUnknown_SkColorType != fInfo.colorType()) {
reed@google.comdcea5302014-01-03 13:43:01 +0000222 const SkImageInfo& prInfo = pr->info();
reede5ea5002014-09-03 11:54:58 -0700223 SkASSERT(fInfo.width() <= prInfo.width());
224 SkASSERT(fInfo.height() <= prInfo.height());
225 SkASSERT(fInfo.colorType() == prInfo.colorType());
226 switch (prInfo.alphaType()) {
reed44977482015-02-27 10:23:00 -0800227 case kUnknown_SkAlphaType:
228 SkASSERT(fInfo.alphaType() == kUnknown_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000229 break;
230 case kOpaque_SkAlphaType:
231 case kPremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700232 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
233 fInfo.alphaType() == kPremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000234 break;
235 case kUnpremul_SkAlphaType:
reede5ea5002014-09-03 11:54:58 -0700236 SkASSERT(fInfo.alphaType() == kOpaque_SkAlphaType ||
237 fInfo.alphaType() == kUnpremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000238 break;
239 }
240 }
241 }
242#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
reed@google.com672588b2014-01-08 15:42:01 +0000244 if (pr) {
245 const SkImageInfo& info = pr->info();
bungeman62ce0302015-08-28 09:09:32 -0700246 fPixelRefOrigin.set(SkTPin(dx, 0, info.width()), SkTPin(dy, 0, info.height()));
reed@google.com672588b2014-01-08 15:42:01 +0000247 } else {
248 // ignore dx,dy if there is no pixelref
249 fPixelRefOrigin.setZero();
250 }
251
252 if (fPixelRef != pr) {
piotaixr0eb02a62014-06-16 11:50:49 -0700253 this->freePixels();
Hal Canary1b3387b2016-12-12 13:48:12 -0500254 SkASSERT(!fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000255
Hal Canary1b3387b2016-12-12 13:48:12 -0500256 fPixelRef = std::move(pr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 this->updatePixelsFromRef();
258 }
259
260 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261}
262
263void SkBitmap::lockPixels() const {
bsalomon49f085d2014-09-05 13:34:00 -0700264 if (fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 fPixelRef->lockPixels();
266 this->updatePixelsFromRef();
267 }
268 SkDEBUGCODE(this->validate();)
269}
270
271void SkBitmap::unlockPixels() const {
Hal Canary1b3387b2016-12-12 13:48:12 -0500272 SkASSERT(!fPixelRef || fPixelLockCount > 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273
bsalomon49f085d2014-09-05 13:34:00 -0700274 if (fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 fPixelRef->unlockPixels();
276 this->updatePixelsFromRef();
277 }
278 SkDEBUGCODE(this->validate();)
279}
280
281void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
halcanary96fcdcc2015-08-27 07:41:13 -0700282 if (nullptr == p) {
Hal Canary1b3387b2016-12-12 13:48:12 -0500283 this->setPixelRef(nullptr, 0, 0);
reed@google.com8e1034e2012-07-30 13:16:35 +0000284 return;
285 }
286
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000287 if (kUnknown_SkColorType == fInfo.colorType()) {
Hal Canary1b3387b2016-12-12 13:48:12 -0500288 this->setPixelRef(nullptr, 0, 0);
reed@google.combf790232013-12-13 19:45:58 +0000289 return;
290 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
Mike Reed6b3155c2017-04-03 14:41:44 -0400292 this->setPixelRef(SkMallocPixelRef::MakeDirect(fInfo, p, fRowBytes, sk_ref_sp(ctable)), 0, 0);
Hal Canary1b3387b2016-12-12 13:48:12 -0500293 if (!fPixelRef) {
reed@google.combf790232013-12-13 19:45:58 +0000294 return;
295 }
djsollen@google.comc84b8332012-07-27 13:41:44 +0000296 // since we're already allocated, we lockPixels right away
297 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 SkDEBUGCODE(this->validate();)
299}
300
reed84825042014-09-02 12:50:45 -0700301bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 HeapAllocator stdalloc;
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000303
halcanary96fcdcc2015-08-27 07:41:13 -0700304 if (nullptr == allocator) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 allocator = &stdalloc;
306 }
307 return allocator->allocPixelRef(this, ctable);
308}
309
reed@google.com9ebcac52014-01-24 18:53:42 +0000310///////////////////////////////////////////////////////////////////////////////
311
reed84825042014-09-02 12:50:45 -0700312bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) {
reedbae704b2014-06-28 14:26:35 -0700313 if (kIndex_8_SkColorType == requestedInfo.colorType()) {
314 return reset_return_false(this);
315 }
reedf0aed972014-07-01 12:48:11 -0700316 if (!this->setInfo(requestedInfo, rowBytes)) {
reedbae704b2014-06-28 14:26:35 -0700317 return reset_return_false(this);
318 }
mtklein775b8192014-12-02 09:11:25 -0800319
reedbae704b2014-06-28 14:26:35 -0700320 // setInfo may have corrected info (e.g. 565 is always opaque).
321 const SkImageInfo& correctedInfo = this->info();
reedf0aed972014-07-01 12:48:11 -0700322 // setInfo may have computed a valid rowbytes if 0 were passed in
323 rowBytes = this->rowBytes();
324
Mike Reed6b3155c2017-04-03 14:41:44 -0400325 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(correctedInfo, rowBytes, nullptr);
Hal Canary1b3387b2016-12-12 13:48:12 -0500326 if (!pr) {
reedbae704b2014-06-28 14:26:35 -0700327 return reset_return_false(this);
328 }
Hal Canary1b3387b2016-12-12 13:48:12 -0500329 this->setPixelRef(std::move(pr), 0, 0);
mtklein775b8192014-12-02 09:11:25 -0800330
halcanary96fcdcc2015-08-27 07:41:13 -0700331 // TODO: lockPixels could/should return bool or void*/nullptr
reedbae704b2014-06-28 14:26:35 -0700332 this->lockPixels();
halcanary96fcdcc2015-08-27 07:41:13 -0700333 if (nullptr == this->getPixels()) {
reedbae704b2014-06-28 14:26:35 -0700334 return reset_return_false(this);
335 }
336 return true;
337}
338
Mike Reed6b3155c2017-04-03 14:41:44 -0400339bool SkBitmap::tryAllocPixels(const SkImageInfo& requestedInfo, sk_sp<SkColorTable> ctable,
340 uint32_t allocFlags) {
halcanary96fcdcc2015-08-27 07:41:13 -0700341 if (kIndex_8_SkColorType == requestedInfo.colorType() && nullptr == ctable) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000342 return reset_return_false(this);
343 }
scroggo0187dc22014-06-05 11:18:04 -0700344 if (!this->setInfo(requestedInfo)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000345 return reset_return_false(this);
346 }
347
scroggo0187dc22014-06-05 11:18:04 -0700348 // setInfo may have corrected info (e.g. 565 is always opaque).
349 const SkImageInfo& correctedInfo = this->info();
350
Mike Reed6b3155c2017-04-03 14:41:44 -0400351 sk_sp<SkPixelRef> pr = (allocFlags & kZeroPixels_AllocFlag) ?
352 SkMallocPixelRef::MakeZeroed(correctedInfo, correctedInfo.minRowBytes(), ctable) :
353 SkMallocPixelRef::MakeAllocate(correctedInfo, correctedInfo.minRowBytes(), ctable);
Hal Canary1b3387b2016-12-12 13:48:12 -0500354 if (!pr) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000355 return reset_return_false(this);
356 }
Hal Canary1b3387b2016-12-12 13:48:12 -0500357 this->setPixelRef(std::move(pr), 0, 0);
reed@google.com9ebcac52014-01-24 18:53:42 +0000358
reed@google.com9ebcac52014-01-24 18:53:42 +0000359 this->lockPixels();
halcanary96fcdcc2015-08-27 07:41:13 -0700360 if (nullptr == this->getPixels()) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000361 return reset_return_false(this);
362 }
363 return true;
364}
365
reeddb74f622015-05-30 13:41:15 -0700366static void invoke_release_proc(void (*proc)(void* pixels, void* ctx), void* pixels, void* ctx) {
367 if (proc) {
368 proc(pixels, ctx);
369 }
370}
371
scroggo0187dc22014-06-05 11:18:04 -0700372bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb,
373 SkColorTable* ct, void (*releaseProc)(void* addr, void* context),
374 void* context) {
375 if (!this->setInfo(requestedInfo, rb)) {
reeddb74f622015-05-30 13:41:15 -0700376 invoke_release_proc(releaseProc, pixels, context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000377 this->reset();
378 return false;
379 }
halcanary96fcdcc2015-08-27 07:41:13 -0700380 if (nullptr == pixels) {
reeddb74f622015-05-30 13:41:15 -0700381 invoke_release_proc(releaseProc, pixels, context);
382 return true; // we behaved as if they called setInfo()
383 }
reed@google.com9ebcac52014-01-24 18:53:42 +0000384
scroggo0187dc22014-06-05 11:18:04 -0700385 // setInfo may have corrected info (e.g. 565 is always opaque).
386 const SkImageInfo& correctedInfo = this->info();
387
Mike Reed6b3155c2017-04-03 14:41:44 -0400388 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithProc(correctedInfo, rb, sk_ref_sp(ct),
389 pixels, releaseProc, context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000390 if (!pr) {
391 this->reset();
392 return false;
393 }
394
Hal Canary1b3387b2016-12-12 13:48:12 -0500395 this->setPixelRef(std::move(pr), 0, 0);
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000396
397 // since we're already allocated, we lockPixels right away
398 this->lockPixels();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000399 SkDEBUGCODE(this->validate();)
reed@google.com9ebcac52014-01-24 18:53:42 +0000400 return true;
401}
402
halcanarye36ec872015-12-09 11:36:59 -0800403bool SkBitmap::installPixels(const SkPixmap& pixmap) {
404 return this->installPixels(pixmap.info(), pixmap.writable_addr(),
405 pixmap.rowBytes(), pixmap.ctable(),
406 nullptr, nullptr);
407}
408
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000409bool SkBitmap::installMaskPixels(const SkMask& mask) {
410 if (SkMask::kA8_Format != mask.fFormat) {
411 this->reset();
412 return false;
413 }
414 return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(),
415 mask.fBounds.height()),
416 mask.fImage, mask.fRowBytes);
417}
418
reed@google.comeb9a46c2014-01-25 16:46:20 +0000419///////////////////////////////////////////////////////////////////////////////
420
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421void SkBitmap::freePixels() {
bsalomon49f085d2014-09-05 13:34:00 -0700422 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 if (fPixelLockCount > 0) {
424 fPixelRef->unlockPixels();
425 }
halcanary96fcdcc2015-08-27 07:41:13 -0700426 fPixelRef = nullptr;
reed@google.com672588b2014-01-08 15:42:01 +0000427 fPixelRefOrigin.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428 }
429 fPixelLockCount = 0;
halcanary96fcdcc2015-08-27 07:41:13 -0700430 fPixels = nullptr;
431 fColorTable = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432}
433
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434uint32_t SkBitmap::getGenerationID() const {
Hal Canary1b3387b2016-12-12 13:48:12 -0500435 return fPixelRef ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436}
437
438void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000439 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 if (fPixelRef) {
441 fPixelRef->notifyPixelsChanged();
442 }
443}
444
445///////////////////////////////////////////////////////////////////////////////
446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447/** We explicitly use the same allocator for our pixels that SkMask does,
448 so that we can freely assign memory allocated by one class to the other.
449 */
450bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
451 SkColorTable* ctable) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000452 const SkImageInfo info = dst->info();
453 if (kUnknown_SkColorType == info.colorType()) {
reed@google.combf790232013-12-13 19:45:58 +0000454// SkDebugf("unsupported config for info %d\n", dst->config());
455 return false;
456 }
skia.committer@gmail.com96f5fa02013-12-16 07:01:40 +0000457
Mike Reed6b3155c2017-04-03 14:41:44 -0400458 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, dst->rowBytes(), sk_ref_sp(ctable));
Hal Canary1b3387b2016-12-12 13:48:12 -0500459 if (!pr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 return false;
461 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000462
Hal Canary1b3387b2016-12-12 13:48:12 -0500463 dst->setPixelRef(std::move(pr), 0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 // since we're already allocated, we lockPixels right away
465 dst->lockPixels();
466 return true;
467}
468
469///////////////////////////////////////////////////////////////////////////////
470
Mike Klein7bfdfda2017-04-11 15:37:50 +0000471static bool copy_pixels_to(const SkPixmap& src, void* const dst, size_t dstSize,
472 size_t dstRowBytes, bool preserveDstPad) {
473 const SkImageInfo& info = src.info();
474
475 if (0 == dstRowBytes) {
476 dstRowBytes = src.rowBytes();
477 }
478 if (dstRowBytes < info.minRowBytes()) {
479 return false;
480 }
481
482 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == src.rowBytes()) {
483 size_t safeSize = src.getSafeSize();
484 if (safeSize > dstSize || safeSize == 0)
485 return false;
486 else {
487 // This implementation will write bytes beyond the end of each row,
488 // excluding the last row, if the bitmap's stride is greater than
489 // strictly required by the current config.
490 memcpy(dst, src.addr(), safeSize);
491 return true;
492 }
493 } else {
494 // If destination has different stride than us, then copy line by line.
495 if (info.getSafeSize(dstRowBytes) > dstSize) {
496 return false;
497 } else {
498 // Just copy what we need on each line.
499 size_t rowBytes = info.minRowBytes();
500 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src.addr());
501 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
502 for (int row = 0; row < info.height(); ++row) {
503 memcpy(dstP, srcP, rowBytes);
504 srcP += src.rowBytes();
505 dstP += dstRowBytes;
506 }
507
508 return true;
509 }
510 }
511}
512
513bool SkBitmap::copyPixelsTo(void* dst, size_t dstSize, size_t dstRB, bool preserveDstPad) const {
514 if (nullptr == dst) {
515 return false;
516 }
517 SkAutoPixmapUnlock result;
518 if (!this->requestLock(&result)) {
519 return false;
520 }
521 return copy_pixels_to(result.pixmap(), dst, dstSize, dstRB, preserveDstPad);
522}
523
524///////////////////////////////////////////////////////////////////////////////
525
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000526bool SkBitmap::isImmutable() const {
scroggo08470592014-07-15 19:56:48 -0700527 return fPixelRef ? fPixelRef->isImmutable() : false;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000528}
529
530void SkBitmap::setImmutable() {
531 if (fPixelRef) {
532 fPixelRef->setImmutable();
junov@chromium.orgb0521292011-12-15 20:14:06 +0000533 }
534}
535
junov@google.com4ee7ae52011-06-30 17:30:49 +0000536bool SkBitmap::isVolatile() const {
537 return (fFlags & kImageIsVolatile_Flag) != 0;
538}
539
540void SkBitmap::setIsVolatile(bool isVolatile) {
541 if (isVolatile) {
542 fFlags |= kImageIsVolatile_Flag;
543 } else {
544 fFlags &= ~kImageIsVolatile_Flag;
545 }
546}
547
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548void* SkBitmap::getAddr(int x, int y) const {
549 SkASSERT((unsigned)x < (unsigned)this->width());
550 SkASSERT((unsigned)y < (unsigned)this->height());
551
552 char* base = (char*)this->getPixels();
553 if (base) {
554 base += y * this->rowBytes();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000555 switch (this->colorType()) {
mtklein7fd93e32016-07-26 13:05:30 -0700556 case kRGBA_F16_SkColorType:
557 base += x << 3;
558 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000559 case kRGBA_8888_SkColorType:
560 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 base += x << 2;
562 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000563 case kARGB_4444_SkColorType:
564 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 base += x << 1;
566 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000567 case kAlpha_8_SkColorType:
568 case kIndex_8_SkColorType:
reed0c9b1a82015-03-17 17:44:06 -0700569 case kGray_8_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 base += x;
571 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000573 SkDEBUGFAIL("Can't return addr for config");
halcanary96fcdcc2015-08-27 07:41:13 -0700574 base = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 break;
576 }
577 }
578 return base;
579}
580
581///////////////////////////////////////////////////////////////////////////////
582///////////////////////////////////////////////////////////////////////////////
583
reed7aefe032015-06-08 10:22:22 -0700584void SkBitmap::erase(SkColor c, const SkIRect& area) const {
reed92fc2ae2015-05-22 08:06:21 -0700585 SkDEBUGCODE(this->validate();)
reed92fc2ae2015-05-22 08:06:21 -0700586
587 switch (fInfo.colorType()) {
588 case kUnknown_SkColorType:
589 case kIndex_8_SkColorType:
590 // TODO: can we ASSERT that we never get here?
591 return; // can't erase. Should we bzero so the memory is not uninitialized?
592 default:
593 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000595
reed92fc2ae2015-05-22 08:06:21 -0700596 SkAutoPixmapUnlock result;
597 if (!this->requestLock(&result)) {
598 return;
599 }
600
reed7aefe032015-06-08 10:22:22 -0700601 if (result.pixmap().erase(c, area)) {
reed92fc2ae2015-05-22 08:06:21 -0700602 this->notifyPixelsChanged();
603 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604}
605
reed7aefe032015-06-08 10:22:22 -0700606void SkBitmap::eraseColor(SkColor c) const {
607 this->erase(c, SkIRect::MakeWH(this->width(), this->height()));
reed@google.com60d32352013-06-28 19:40:50 +0000608}
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610//////////////////////////////////////////////////////////////////////////////////////
611//////////////////////////////////////////////////////////////////////////////////////
612
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
614 SkDEBUGCODE(this->validate();)
615
Hal Canary1b3387b2016-12-12 13:48:12 -0500616 if (nullptr == result || !fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 return false; // no src pixels
618 }
619
620 SkIRect srcRect, r;
621 srcRect.set(0, 0, this->width(), this->height());
622 if (!r.intersect(srcRect, subset)) {
623 return false; // r is empty (i.e. no intersection)
624 }
625
scroggo@google.coma2a31922012-12-07 19:14:45 +0000626 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
627 // exited above.
628 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
629 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
630
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 SkBitmap dst;
herbb5d74682016-04-21 08:45:39 -0700632 dst.setInfo(this->info().makeWH(r.width(), r.height()), this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000633 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634
635 if (fPixelRef) {
reed@google.com672588b2014-01-08 15:42:01 +0000636 SkIPoint origin = fPixelRefOrigin;
637 origin.fX += r.fLeft;
638 origin.fY += r.fTop;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 // share the pixelref with a custom offset
Hal Canary1b3387b2016-12-12 13:48:12 -0500640 dst.setPixelRef(fPixelRef, origin.x(), origin.y());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 }
642 SkDEBUGCODE(dst.validate();)
643
644 // we know we're good, so commit to result
645 result->swap(dst);
646 return true;
647}
648
649///////////////////////////////////////////////////////////////////////////////
650
Mike Reed22f34822016-11-28 17:17:38 -0500651bool SkBitmap::canCopyTo(SkColorType dstCT) const {
reedb184f7f2014-07-13 04:32:32 -0700652 const SkColorType srcCT = this->colorType();
653
654 if (srcCT == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 return false;
656 }
Mike Reed22f34822016-11-28 17:17:38 -0500657 if (srcCT == kAlpha_8_SkColorType && dstCT != kAlpha_8_SkColorType) {
658 return false; // can't convert from alpha to non-alpha
659 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660
Mike Reed22f34822016-11-28 17:17:38 -0500661 bool sameConfigs = (srcCT == dstCT);
662 switch (dstCT) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000663 case kAlpha_8_SkColorType:
664 case kRGB_565_SkColorType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000665 case kRGBA_8888_SkColorType:
666 case kBGRA_8888_SkColorType:
Matt Sarettd9836f42017-04-05 15:41:53 -0400667 case kRGBA_F16_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 break;
Matt Sarettcb6266b2017-01-17 10:48:53 -0500669 case kGray_8_SkColorType:
weita@google.comf9ab99a2009-05-03 18:23:30 +0000670 if (!sameConfigs) {
671 return false;
672 }
673 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000674 case kARGB_4444_SkColorType:
reedb184f7f2014-07-13 04:32:32 -0700675 return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 default:
677 return false;
678 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000679 return true;
680}
681
reedb184f7f2014-07-13 04:32:32 -0700682bool SkBitmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB,
683 int x, int y) const {
reed95d343f2015-05-23 13:21:06 -0700684 SkAutoPixmapUnlock src;
685 if (!this->requestLock(&src)) {
reedb184f7f2014-07-13 04:32:32 -0700686 return false;
687 }
reed95d343f2015-05-23 13:21:06 -0700688 return src.pixmap().readPixels(requestedDstInfo, dstPixels, dstRB, x, y);
reedb184f7f2014-07-13 04:32:32 -0700689}
690
Mike Reed68dd8d02017-01-04 16:34:31 -0500691bool SkBitmap::readPixels(const SkPixmap& dst, int srcX, int srcY) const {
692 return this->readPixels(dst.info(), dst.writable_addr(), dst.rowBytes(), srcX, srcY);
693}
694
Matt Sarettd2adc662017-03-27 15:07:35 -0400695bool SkBitmap::writePixels(const SkPixmap& src, int dstX, int dstY,
696 SkTransferFunctionBehavior behavior) {
Mike Reed68dd8d02017-01-04 16:34:31 -0500697 SkAutoPixmapUnlock dst;
698 if (!this->requestLock(&dst)) {
699 return false;
700 }
701
Matt Sarett03dd6d52017-01-23 12:15:09 -0500702 if (!SkImageInfoValidConversion(fInfo, src.info())) {
Mike Reed68dd8d02017-01-04 16:34:31 -0500703 return false;
704 }
705
Matt Sarett03dd6d52017-01-23 12:15:09 -0500706 SkWritePixelsRec rec(src.info(), src.addr(), src.rowBytes(), dstX, dstY);
707 if (!rec.trim(fInfo.width(), fInfo.height())) {
708 return false;
709 }
710
711 void* dstPixels = this->getAddr(rec.fX, rec.fY);
712 const SkImageInfo dstInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
Matt Sarett485c4992017-02-14 14:18:27 -0500713 SkConvertPixels(dstInfo, dstPixels, this->rowBytes(), rec.fInfo, rec.fPixels, rec.fRowBytes,
Matt Sarettd2adc662017-03-27 15:07:35 -0400714 src.ctable(), behavior);
Matt Sarett8572d852017-02-14 11:21:02 -0500715 return true;
Mike Reed68dd8d02017-01-04 16:34:31 -0500716}
717
Mike Reed42ce38f2017-04-06 14:03:25 -0400718bool SkBitmap::internalCopyTo(SkBitmap* dst, SkColorType dstColorType, Allocator* alloc) const {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000719 if (!this->canCopyTo(dstColorType)) {
reed@android.comfbaa88d2009-05-06 17:44:34 +0000720 return false;
721 }
722
reed95d343f2015-05-23 13:21:06 -0700723 SkAutoPixmapUnlock srcUnlocker;
Matt Sarett19873662017-04-05 13:36:08 -0400724 if (!this->requestLock(&srcUnlocker)) {
reed@google.com50dfa012011-04-01 19:05:36 +0000725 return false;
726 }
Matt Sarettcb6266b2017-01-17 10:48:53 -0500727 SkPixmap srcPM = srcUnlocker.pixmap();
Matt Sarettd9836f42017-04-05 15:41:53 -0400728
729 // Various Android specific compatibility modes.
730 // TODO:
731 // Move the logic of this entire function into the framework, then call readPixels() directly.
732 SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
733 switch (dstColorType) {
734 case kRGB_565_SkColorType:
735 // copyTo() is not strict on alpha type. Here we set the src to opaque to allow
736 // the call to readPixels() to succeed and preserve this lenient behavior.
737 if (kOpaque_SkAlphaType != srcPM.alphaType()) {
738 srcPM = SkPixmap(srcPM.info().makeAlphaType(kOpaque_SkAlphaType), srcPM.addr(),
739 srcPM.rowBytes(), srcPM.ctable());
740 dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
741 }
742 break;
743 case kRGBA_F16_SkColorType:
744 // The caller does not have an opportunity to pass a dst color space. Assume that
745 // they want linear sRGB.
746 dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear());
747
748 if (!srcPM.colorSpace()) {
749 // We can't do a sane conversion to F16 without a dst color space. Guess sRGB
750 // in this case.
751 srcPM.setColorSpace(SkColorSpace::MakeSRGB());
752 }
753 break;
754 default:
755 break;
Matt Sarettcb6266b2017-01-17 10:48:53 -0500756 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000757
reed@google.com50dfa012011-04-01 19:05:36 +0000758 SkBitmap tmpDst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000759 if (!tmpDst.setInfo(dstInfo)) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000760 return false;
761 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000762
weita@google.comf9ab99a2009-05-03 18:23:30 +0000763 // allocate colortable if srcConfig == kIndex8_Config
Hal Canary704cd322016-11-07 14:13:52 -0500764 sk_sp<SkColorTable> ctable;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000765 if (dstColorType == kIndex_8_SkColorType) {
reed95d343f2015-05-23 13:21:06 -0700766 ctable.reset(SkRef(srcPM.ctable()));
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000767 }
Hal Canary704cd322016-11-07 14:13:52 -0500768 if (!tmpDst.tryAllocPixels(alloc, ctable.get())) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000769 return false;
770 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000771
reed95d343f2015-05-23 13:21:06 -0700772 SkAutoPixmapUnlock dstUnlocker;
773 if (!tmpDst.requestLock(&dstUnlocker)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 return false;
775 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000776
Matt Sarettd9836f42017-04-05 15:41:53 -0400777 SkPixmap dstPM = dstUnlocker.pixmap();
778
779 // We can't do a sane conversion from F16 without a src color space. Guess sRGB in this case.
780 if (kRGBA_F16_SkColorType == srcPM.colorType() && !dstPM.colorSpace()) {
781 dstPM.setColorSpace(SkColorSpace::MakeSRGB());
782 }
783
784 // readPixels does not yet support color spaces with parametric transfer functions. This
785 // works around that restriction when the color spaces are equal.
786 if (kRGBA_F16_SkColorType != dstColorType && kRGBA_F16_SkColorType != srcPM.colorType() &&
787 dstPM.colorSpace() == srcPM.colorSpace()) {
788 dstPM.setColorSpace(nullptr);
789 srcPM.setColorSpace(nullptr);
790 }
791
792 if (!srcPM.readPixels(dstPM)) {
reedb184f7f2014-07-13 04:32:32 -0700793 return false;
794 }
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000795
reedb184f7f2014-07-13 04:32:32 -0700796 // (for BitmapHeap) Clone the pixelref genID even though we have a new pixelref.
797 // The old copyTo impl did this, so we continue it for now.
798 //
799 // TODO: should we ignore rowbytes (i.e. getSize)? Then it could just be
800 // if (src_pixelref->info == dst_pixelref->info)
801 //
reed95d343f2015-05-23 13:21:06 -0700802 if (srcPM.colorType() == dstColorType && tmpDst.getSize() == srcPM.getSize64()) {
reedb184f7f2014-07-13 04:32:32 -0700803 SkPixelRef* dstPixelRef = tmpDst.pixelRef();
804 if (dstPixelRef->info() == fPixelRef->info()) {
805 dstPixelRef->cloneGenID(*fPixelRef);
reed@android.com311c82d2009-05-05 23:13:23 +0000806 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 }
808
reed@google.com50dfa012011-04-01 19:05:36 +0000809 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 return true;
811}
812
Mike Reed42ce38f2017-04-06 14:03:25 -0400813bool SkBitmap::copyTo(SkBitmap* dst, SkColorType ct) const {
814 return this->internalCopyTo(dst, ct, nullptr);
815}
816
817#ifdef SK_BUILD_FOR_ANDROID
818bool SkBitmap::copyTo(SkBitmap* dst, SkColorType ct, Allocator* alloc) const {
819 return this->internalCopyTo(dst, ct, alloc);
820}
821#endif
822
reedc7ec7c92016-07-25 08:29:10 -0700823// TODO: can we merge this with copyTo?
commit-bot@chromium.orgfab349c2014-03-05 02:34:58 +0000824bool SkBitmap::deepCopyTo(SkBitmap* dst) const {
reede4538f52014-06-11 06:09:50 -0700825 const SkColorType dstCT = this->colorType();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000826
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000827 if (!this->canCopyTo(dstCT)) {
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000828 return false;
829 }
Mike Reed42ce38f2017-04-06 14:03:25 -0400830 return this->copyTo(dst, dstCT);
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +0000831}
832
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834
reed92fc2ae2015-05-22 08:06:21 -0700835static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
halcanary96fcdcc2015-08-27 07:41:13 -0700836 SkASSERT(alpha != nullptr);
reed92fc2ae2015-05-22 08:06:21 -0700837 SkASSERT(alphaRowBytes >= src.width());
838
839 SkAutoPixmapUnlock apl;
840 if (!src.requestLock(&apl)) {
lsalzmana2415ac2016-10-11 14:29:12 -0700841 for (int y = 0; y < src.height(); ++y) {
842 memset(alpha, 0, src.width());
843 alpha += alphaRowBytes;
844 }
reed92fc2ae2015-05-22 08:06:21 -0700845 return false;
846 }
lsalzmana2415ac2016-10-11 14:29:12 -0700847 const SkPixmap& pmap = apl.pixmap();
Matt Sarett485c4992017-02-14 14:18:27 -0500848 SkConvertPixels(SkImageInfo::MakeA8(pmap.width(), pmap.height()), alpha, alphaRowBytes,
Matt Sarettd2adc662017-03-27 15:07:35 -0400849 pmap.info(), pmap.addr(), pmap.rowBytes(), pmap.ctable(),
850 SkTransferFunctionBehavior::kRespect);
reed@android.com1cdcb512009-08-24 19:11:00 +0000851 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852}
853
854#include "SkPaint.h"
855#include "SkMaskFilter.h"
856#include "SkMatrix.h"
857
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000858bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +0000859 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 SkDEBUGCODE(this->validate();)
861
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000862 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 SkMatrix identity;
864 SkMask srcM, dstM;
865
866 srcM.fBounds.set(0, 0, this->width(), this->height());
867 srcM.fRowBytes = SkAlign4(this->width());
868 srcM.fFormat = SkMask::kA8_Format;
869
halcanary96fcdcc2015-08-27 07:41:13 -0700870 SkMaskFilter* filter = paint ? paint->getMaskFilter() : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871
872 // compute our (larger?) dst bounds if we have a filter
bsalomon49f085d2014-09-05 13:34:00 -0700873 if (filter) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 identity.reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700875 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 goto NO_FILTER_CASE;
877 }
878 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
879 } else {
880 NO_FILTER_CASE:
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000881 tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -0700882 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000883 // Allocation of pixels for alpha bitmap failed.
884 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
885 tmpBitmap.width(), tmpBitmap.height());
886 return false;
887 }
888 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 if (offset) {
890 offset->set(0, 0);
891 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000892 tmpBitmap.swap(*dst);
893 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000895 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
896 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897
898 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -0700899 if (!filter->filterMask(&dstM, srcM, identity, nullptr)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 goto NO_FILTER_CASE;
901 }
bungeman@google.com02f55842011-10-04 21:25:00 +0000902 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000904 tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
905 dstM.fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -0700906 if (!tmpBitmap.tryAllocPixels(allocator, nullptr)) {
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000907 // Allocation of pixels for alpha bitmap failed.
908 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
909 tmpBitmap.width(), tmpBitmap.height());
910 return false;
911 }
912 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 if (offset) {
914 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
915 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +0000916 SkDEBUGCODE(tmpBitmap.validate();)
917
918 tmpBitmap.swap(*dst);
919 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920}
921
922///////////////////////////////////////////////////////////////////////////////
923
reed92fc2ae2015-05-22 08:06:21 -0700924static void write_raw_pixels(SkWriteBuffer* buffer, const SkPixmap& pmap) {
925 const SkImageInfo& info = pmap.info();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000926 const size_t snugRB = info.width() * info.bytesPerPixel();
reed92fc2ae2015-05-22 08:06:21 -0700927 const char* src = (const char*)pmap.addr();
928 const size_t ramRB = pmap.rowBytes();
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +0000929
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000930 buffer->write32(SkToU32(snugRB));
931 info.flatten(*buffer);
932
933 const size_t size = snugRB * info.height();
scroggo565901d2015-12-10 10:44:13 -0800934 SkAutoTMalloc<char> storage(size);
935 char* dst = storage.get();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000936 for (int y = 0; y < info.height(); ++y) {
937 memcpy(dst, src, snugRB);
938 dst += snugRB;
939 src += ramRB;
940 }
941 buffer->writeByteArray(storage.get(), size);
942
reed92fc2ae2015-05-22 08:06:21 -0700943 const SkColorTable* ct = pmap.ctable();
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000944 if (kIndex_8_SkColorType == info.colorType() && ct) {
945 buffer->writeBool(true);
946 ct->writeToBuffer(*buffer);
947 } else {
948 buffer->writeBool(false);
949 }
950}
951
reed92fc2ae2015-05-22 08:06:21 -0700952void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) {
953 const SkImageInfo info = bitmap.info();
Hal Canary1b3387b2016-12-12 13:48:12 -0500954 if (0 == info.width() || 0 == info.height() || bitmap.isNull()) {
reed92fc2ae2015-05-22 08:06:21 -0700955 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
956 return;
957 }
958
959 SkAutoPixmapUnlock result;
960 if (!bitmap.requestLock(&result)) {
961 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
962 return;
963 }
964
965 write_raw_pixels(buffer, result.pixmap());
966}
967
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000968bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) {
969 const size_t snugRB = buffer->readUInt();
970 if (0 == snugRB) { // no pixels
971 return false;
972 }
973
974 SkImageInfo info;
975 info.unflatten(*buffer);
976
Robert Phillipsb2cb5352016-12-20 12:44:41 -0500977 if (info.width() < 0 || info.height() < 0) {
978 return false;
979 }
980
mtklein58e389b2016-07-15 07:00:11 -0700981 // If there was an error reading "info" or if it is bogus,
robertphillips74139f12016-06-28 09:04:34 -0700982 // don't use it to compute minRowBytes()
983 if (!buffer->validate(SkColorTypeValidateAlphaType(info.colorType(),
984 info.alphaType()))) {
sugoica95c192014-07-08 09:18:48 -0700985 return false;
986 }
987
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000988 const size_t ramRB = info.minRowBytes();
sugoibd0d9da2015-01-07 08:47:44 -0800989 const int height = SkMax32(info.height(), 0);
990 const uint64_t snugSize = sk_64_mul(snugRB, height);
991 const uint64_t ramSize = sk_64_mul(ramRB, height);
992 static const uint64_t max_size_t = (size_t)(-1);
993 if (!buffer->validate((snugSize <= ramSize) && (ramSize <= max_size_t))) {
commit-bot@chromium.org05858432014-05-30 01:06:44 +0000994 return false;
995 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +0000996
reedfde05112016-03-11 13:02:28 -0800997 sk_sp<SkData> data(SkData::MakeUninitialized(SkToSizeT(ramSize)));
robertphillips28937842015-06-08 07:10:49 -0700998 unsigned char* dst = (unsigned char*)data->writable_data();
sugoibd0d9da2015-01-07 08:47:44 -0800999 buffer->readByteArray(dst, SkToSizeT(snugSize));
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001000
1001 if (snugSize != ramSize) {
robertphillips28937842015-06-08 07:10:49 -07001002 const unsigned char* srcRow = dst + snugRB * (height - 1);
1003 unsigned char* dstRow = dst + ramRB * (height - 1);
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001004 for (int y = height - 1; y >= 1; --y) {
1005 memmove(dstRow, srcRow, snugRB);
1006 srcRow -= snugRB;
1007 dstRow -= ramRB;
1008 }
1009 SkASSERT(srcRow == dstRow); // first row does not need to be moved
1010 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001011
Hal Canary704cd322016-11-07 14:13:52 -05001012 sk_sp<SkColorTable> ctable;
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001013 if (buffer->readBool()) {
Mike Reed6b3155c2017-04-03 14:41:44 -04001014 ctable = SkColorTable::Create(*buffer);
reedb236d1a2015-08-28 10:14:18 -07001015 if (!ctable) {
1016 return false;
1017 }
robertphillips28937842015-06-08 07:10:49 -07001018
reedb236d1a2015-08-28 10:14:18 -07001019 if (info.isEmpty()) {
1020 // require an empty ctable
1021 if (ctable->count() != 0) {
1022 buffer->validate(false);
1023 return false;
1024 }
1025 } else {
1026 // require a non-empty ctable
1027 if (ctable->count() == 0) {
1028 buffer->validate(false);
1029 return false;
1030 }
1031 unsigned char maxIndex = ctable->count() - 1;
1032 for (uint64_t i = 0; i < ramSize; ++i) {
1033 dst[i] = SkTMin(dst[i], maxIndex);
1034 }
robertphillips28937842015-06-08 07:10:49 -07001035 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001036 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001037
Mike Reed6b3155c2017-04-03 14:41:44 -04001038 sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithData(info, info.minRowBytes(),
1039 std::move(ctable), std::move(data));
1040 if (!pr) {
sugoi6af31472015-01-28 13:15:32 -08001041 return false;
1042 }
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001043 bitmap->setInfo(pr->info());
Hal Canary1b3387b2016-12-12 13:48:12 -05001044 bitmap->setPixelRef(std::move(pr), 0, 0);
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001045 return true;
1046}
1047
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048enum {
1049 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001050 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051};
1052
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053///////////////////////////////////////////////////////////////////////////////
1054
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055#ifdef SK_DEBUG
1056void SkBitmap::validate() const {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001057 fInfo.validate();
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +00001058
1059 // ImageInfo may not require this, but Bitmap ensures that opaque-only
1060 // colorTypes report opaque for their alphatype
1061 if (kRGB_565_SkColorType == fInfo.colorType()) {
1062 SkASSERT(kOpaque_SkAlphaType == fInfo.alphaType());
1063 }
1064
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001065 SkASSERT(fInfo.validRowBytes(fRowBytes));
scroggo08470592014-07-15 19:56:48 -07001066 uint8_t allFlags = kImageIsVolatile_Flag;
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001067#ifdef SK_BUILD_FOR_ANDROID
1068 allFlags |= kHasHardwareMipMap_Flag;
1069#endif
scroggo08470592014-07-15 19:56:48 -07001070 SkASSERT((~allFlags & fFlags) == 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 SkASSERT(fPixelLockCount >= 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072
reed@google.com615316c2014-01-15 19:15:23 +00001073 if (fPixels) {
1074 SkASSERT(fPixelRef);
robertphillipse77dadd2014-11-21 05:50:21 -08001075 SkASSERT(fPixelLockCount > 0);
reed@google.com615316c2014-01-15 19:15:23 +00001076 SkASSERT(fPixelRef->isLocked());
1077 SkASSERT(fPixelRef->rowBytes() == fRowBytes);
1078 SkASSERT(fPixelRefOrigin.fX >= 0);
1079 SkASSERT(fPixelRefOrigin.fY >= 0);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001080 SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
reede5ea5002014-09-03 11:54:58 -07001081 SkASSERT(fPixelRef->info().height() >= (int)this->height() + fPixelRefOrigin.fY);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001082 SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
reed@google.com615316c2014-01-15 19:15:23 +00001083 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07001084 SkASSERT(nullptr == fColorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086}
1087#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001088
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001089#ifndef SK_IGNORE_TO_STRING
bungemand3ebb482015-08-05 13:57:49 -07001090#include "SkString.h"
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001091void SkBitmap::toString(SkString* str) const {
1092
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001093 static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = {
1094 "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8",
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001095 };
1096
1097 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001098 gColorTypeNames[this->colorType()]);
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001099
1100 str->append(" (");
1101 if (this->isOpaque()) {
1102 str->append("opaque");
1103 } else {
1104 str->append("transparent");
1105 }
1106 if (this->isImmutable()) {
1107 str->append(", immutable");
1108 } else {
1109 str->append(", not-immutable");
1110 }
1111 str->append(")");
1112
1113 SkPixelRef* pr = this->pixelRef();
halcanary96fcdcc2015-08-27 07:41:13 -07001114 if (nullptr == pr) {
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001115 // show null or the explicit pixel address (rare)
1116 str->appendf(" pixels:%p", this->getPixels());
1117 } else {
1118 const char* uri = pr->getURI();
bsalomon49f085d2014-09-05 13:34:00 -07001119 if (uri) {
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001120 str->appendf(" uri:\"%s\"", uri);
1121 } else {
1122 str->appendf(" pixelref:%p", pr);
1123 }
1124 }
1125
1126 str->append(")");
1127}
1128#endif
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001129
1130///////////////////////////////////////////////////////////////////////////////
1131
reed92fc2ae2015-05-22 08:06:21 -07001132bool SkBitmap::requestLock(SkAutoPixmapUnlock* result) const {
1133 SkASSERT(result);
1134
Hal Canary1b3387b2016-12-12 13:48:12 -05001135 SkPixelRef* pr = fPixelRef.get();
halcanary96fcdcc2015-08-27 07:41:13 -07001136 if (nullptr == pr) {
reed92fc2ae2015-05-22 08:06:21 -07001137 return false;
1138 }
1139
reed871872f2015-06-22 12:48:26 -07001140 // We have to lock the whole thing (using the pixelref's dimensions) until the api supports
1141 // a partial lock (with offset/origin). Hence we can't use our fInfo.
1142 SkPixelRef::LockRequest req = { pr->info().dimensions(), kNone_SkFilterQuality };
reed92fc2ae2015-05-22 08:06:21 -07001143 SkPixelRef::LockResult res;
1144 if (pr->requestLock(req, &res)) {
reede8006572015-05-28 14:06:06 -07001145 SkASSERT(res.fPixels);
reed92fc2ae2015-05-22 08:06:21 -07001146 // The bitmap may be a subset of the pixelref's dimensions
1147 SkASSERT(fPixelRefOrigin.x() + fInfo.width() <= res.fSize.width());
1148 SkASSERT(fPixelRefOrigin.y() + fInfo.height() <= res.fSize.height());
1149 const void* addr = (const char*)res.fPixels + SkColorTypeComputeOffset(fInfo.colorType(),
1150 fPixelRefOrigin.x(),
1151 fPixelRefOrigin.y(),
1152 res.fRowBytes);
1153
1154 result->reset(SkPixmap(this->info(), addr, res.fRowBytes, res.fCTable),
1155 res.fUnlockProc, res.fUnlockContext);
1156 return true;
1157 }
1158 return false;
1159}
1160
reedcb674142015-06-05 06:58:22 -07001161bool SkBitmap::peekPixels(SkPixmap* pmap) const {
1162 if (fPixels) {
1163 if (pmap) {
1164 pmap->reset(fInfo, fPixels, fRowBytes, fColorTable);
1165 }
1166 return true;
1167 }
1168 return false;
1169}
1170
reed92fc2ae2015-05-22 08:06:21 -07001171///////////////////////////////////////////////////////////////////////////////
1172
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001173#ifdef SK_DEBUG
1174void SkImageInfo::validate() const {
1175 SkASSERT(fWidth >= 0);
1176 SkASSERT(fHeight >= 0);
1177 SkASSERT(SkColorTypeIsValid(fColorType));
1178 SkASSERT(SkAlphaTypeIsValid(fAlphaType));
1179}
1180#endif