blob: 316878bad0efd1ee0323f2acda3df44ac3e8a952 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkBitmap.h"
11#include "SkColorPriv.h"
12#include "SkDither.h"
13#include "SkFlattenable.h"
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +000014#include "SkImagePriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkMallocPixelRef.h"
16#include "SkMask.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000017#include "SkReadBuffer.h"
18#include "SkWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPixelRef.h"
20#include "SkThread.h"
vandebo@chromium.org112706d2011-02-24 22:50:55 +000021#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkUtils.h"
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000023#include "SkValidationUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPackBits.h"
25#include <new>
26
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000027static bool reset_return_false(SkBitmap* bm) {
28 bm->reset();
29 return false;
30}
31
reed@android.com8a1c16f2008-12-17 15:59:43 +000032SkBitmap::SkBitmap() {
reed@android.com4516f472009-06-29 16:25:36 +000033 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000034}
35
36SkBitmap::SkBitmap(const SkBitmap& src) {
37 SkDEBUGCODE(src.validate();)
reed@android.com4516f472009-06-29 16:25:36 +000038 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 *this = src;
40 SkDEBUGCODE(this->validate();)
41}
42
43SkBitmap::~SkBitmap() {
44 SkDEBUGCODE(this->validate();)
45 this->freePixels();
46}
47
48SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
49 if (this != &src) {
50 this->freePixels();
51 memcpy(this, &src, sizeof(src));
52
53 // inc src reference counts
reed@android.com83f7bc32009-07-17 02:42:41 +000054 SkSafeRef(src.fPixelRef);
reed@android.com8a1c16f2008-12-17 15:59:43 +000055
56 // we reset our locks if we get blown away
57 fPixelLockCount = 0;
weita@google.comf9ab99a2009-05-03 18:23:30 +000058
reed@google.com5f62ed72014-01-15 19:59:45 +000059 if (fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 // ignore the values from the memcpy
61 fPixels = NULL;
62 fColorTable = NULL;
bsalomon@google.com586f48c2011-04-14 15:07:22 +000063 // Note that what to for genID is somewhat arbitrary. We have no
64 // way to track changes to raw pixels across multiple SkBitmaps.
65 // Would benefit from an SkRawPixelRef type created by
66 // setPixels.
67 // Just leave the memcpy'ed one but they'll get out of sync
68 // as soon either is modified.
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 }
70 }
71
72 SkDEBUGCODE(this->validate();)
73 return *this;
74}
75
76void SkBitmap::swap(SkBitmap& other) {
bsalomon@google.com586f48c2011-04-14 15:07:22 +000077 SkTSwap(fColorTable, other.fColorTable);
78 SkTSwap(fPixelRef, other.fPixelRef);
reed@google.com672588b2014-01-08 15:42:01 +000079 SkTSwap(fPixelRefOrigin, other.fPixelRefOrigin);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000080 SkTSwap(fPixelLockCount, other.fPixelLockCount);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000081 SkTSwap(fPixels, other.fPixels);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000082 SkTSwap(fInfo, other.fInfo);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000083 SkTSwap(fRowBytes, other.fRowBytes);
bsalomon@google.com586f48c2011-04-14 15:07:22 +000084 SkTSwap(fFlags, other.fFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
86 SkDEBUGCODE(this->validate();)
87}
88
89void SkBitmap::reset() {
90 this->freePixels();
reed@android.com4516f472009-06-29 16:25:36 +000091 sk_bzero(this, sizeof(*this));
reed@android.com8a1c16f2008-12-17 15:59:43 +000092}
93
reedc3b32662014-06-17 08:38:31 -070094#ifdef SK_SUPPORT_LEGACY_BITMAP_CONFIG
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000095SkBitmap::Config SkBitmap::config() const {
96 return SkColorTypeToBitmapConfig(fInfo.colorType());
97}
reedc3b32662014-06-17 08:38:31 -070098#endif
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +000099
reedddd014e2014-06-05 08:51:20 -0700100#ifdef SK_SUPPORT_LEGACY_COMPUTE_CONFIG_SIZE
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) {
102 int bpp;
103 switch (config) {
104 case kNo_Config:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 bpp = 0; // not applicable
106 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 case kA8_Config:
108 case kIndex8_Config:
109 bpp = 1;
110 break;
111 case kRGB_565_Config:
112 case kARGB_4444_Config:
113 bpp = 2;
114 break;
115 case kARGB_8888_Config:
116 bpp = 4;
117 break;
118 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000119 SkDEBUGFAIL("unknown config");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 bpp = 0; // error
121 break;
122 }
123 return bpp;
124}
125
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000126size_t SkBitmap::ComputeRowBytes(Config c, int width) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000127 return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128}
129
reed@google.com57212f92013-12-30 14:40:38 +0000130int64_t SkBitmap::ComputeSize64(Config config, int width, int height) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000131 SkColorType ct = SkBitmapConfigToColorType(config);
132 int64_t rowBytes = sk_64_mul(SkColorTypeBytesPerPixel(ct), width);
reed@google.com57212f92013-12-30 14:40:38 +0000133 return rowBytes * height;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134}
135
136size_t SkBitmap::ComputeSize(Config c, int width, int height) {
reed@google.com57212f92013-12-30 14:40:38 +0000137 int64_t size = SkBitmap::ComputeSize64(c, width, height);
138 return sk_64_isS32(size) ? sk_64_asS32(size) : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139}
reedddd014e2014-06-05 08:51:20 -0700140#endif
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000141
reed@google.com86b2e432012-03-15 21:17:03 +0000142void SkBitmap::getBounds(SkRect* bounds) const {
143 SkASSERT(bounds);
144 bounds->set(0, 0,
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000145 SkIntToScalar(fInfo.fWidth), SkIntToScalar(fInfo.fHeight));
reed@google.com86b2e432012-03-15 21:17:03 +0000146}
147
reed@google.com80e14592012-03-16 14:58:07 +0000148void SkBitmap::getBounds(SkIRect* bounds) const {
149 SkASSERT(bounds);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000150 bounds->set(0, 0, fInfo.fWidth, fInfo.fHeight);
reed@google.com80e14592012-03-16 14:58:07 +0000151}
152
reed@google.com86b2e432012-03-15 21:17:03 +0000153///////////////////////////////////////////////////////////////////////////////
154
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000155bool SkBitmap::setInfo(const SkImageInfo& origInfo, size_t rowBytes) {
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +0000156 SkImageInfo info = origInfo;
157
scroggo2fd0d142014-07-01 07:08:19 -0700158 if (!SkColorTypeValidateAlphaType(info.fColorType, info.fAlphaType,
159 &info.fAlphaType)) {
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +0000160 return reset_return_false(this);
161 }
skia.committer@gmail.com02d6f542014-02-14 03:02:05 +0000162
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000163 // require that rowBytes fit in 31bits
164 int64_t mrb = info.minRowBytes64();
165 if ((int32_t)mrb != mrb) {
166 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000167 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000168 if ((int64_t)rowBytes != (int32_t)rowBytes) {
169 return reset_return_false(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 }
reed@android.com89bb83a2009-05-29 21:30:42 +0000171
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000172 if (info.width() < 0 || info.height() < 0) {
173 return reset_return_false(this);
174 }
175
176 if (kUnknown_SkColorType == info.colorType()) {
177 rowBytes = 0;
178 } else if (0 == rowBytes) {
179 rowBytes = (size_t)mrb;
reedf0aed972014-07-01 12:48:11 -0700180 } else if (!info.validRowBytes(rowBytes)) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000181 return reset_return_false(this);
reed@google.com383a6972013-10-21 14:00:07 +0000182 }
183
184 this->freePixels();
185
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000186 fInfo = info;
187 fRowBytes = SkToU32(rowBytes);
reed@google.com383a6972013-10-21 14:00:07 +0000188 return true;
reed@google.com383a6972013-10-21 14:00:07 +0000189}
190
reed6c225732014-06-09 19:52:07 -0700191#ifdef SK_SUPPORT_LEGACY_SETCONFIG
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000192bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes,
193 SkAlphaType alphaType) {
194 SkColorType ct = SkBitmapConfigToColorType(config);
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000195 return this->setInfo(SkImageInfo::Make(width, height, ct, alphaType), rowBytes);
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +0000196}
reed6c225732014-06-09 19:52:07 -0700197#endif
commit-bot@chromium.org6e3e4222013-11-06 10:08:30 +0000198
reed@google.com383a6972013-10-21 14:00:07 +0000199bool SkBitmap::setAlphaType(SkAlphaType alphaType) {
scroggo2fd0d142014-07-01 07:08:19 -0700200 if (!SkColorTypeValidateAlphaType(fInfo.fColorType, alphaType, &alphaType)) {
reed@google.com383a6972013-10-21 14:00:07 +0000201 return false;
202 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000203 if (fInfo.fAlphaType != alphaType) {
204 fInfo.fAlphaType = alphaType;
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000205 if (fPixelRef) {
reed@google.comc1587f92014-01-28 16:05:39 +0000206 fPixelRef->changeAlphaType(alphaType);
commit-bot@chromium.org0e8d0d62014-01-27 15:41:07 +0000207 }
208 }
reed@google.com383a6972013-10-21 14:00:07 +0000209 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210}
211
212void SkBitmap::updatePixelsFromRef() const {
213 if (NULL != fPixelRef) {
214 if (fPixelLockCount > 0) {
reed@google.comff0da4f2012-05-17 13:14:52 +0000215 SkASSERT(fPixelRef->isLocked());
weita@google.comf9ab99a2009-05-03 18:23:30 +0000216
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 void* p = fPixelRef->pixels();
218 if (NULL != p) {
reed@google.com672588b2014-01-08 15:42:01 +0000219 p = (char*)p
reed@google.com303c4752014-01-09 20:00:14 +0000220 + fPixelRefOrigin.fY * fRowBytes
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000221 + fPixelRefOrigin.fX * fInfo.bytesPerPixel();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 }
223 fPixels = p;
reed@google.com5f62ed72014-01-15 19:59:45 +0000224 fColorTable = fPixelRef->colorTable();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 } else {
226 SkASSERT(0 == fPixelLockCount);
227 fPixels = NULL;
reed@google.com5f62ed72014-01-15 19:59:45 +0000228 fColorTable = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 }
230 }
231}
232
reed@google.com672588b2014-01-08 15:42:01 +0000233SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, int dx, int dy) {
reed@google.comdcea5302014-01-03 13:43:01 +0000234#ifdef SK_DEBUG
reed@google.com672588b2014-01-08 15:42:01 +0000235 if (pr) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000236 if (kUnknown_SkColorType != fInfo.colorType()) {
reed@google.comdcea5302014-01-03 13:43:01 +0000237 const SkImageInfo& prInfo = pr->info();
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000238 SkASSERT(fInfo.fWidth <= prInfo.fWidth);
239 SkASSERT(fInfo.fHeight <= prInfo.fHeight);
240 SkASSERT(fInfo.fColorType == prInfo.fColorType);
reed@google.comdcea5302014-01-03 13:43:01 +0000241 switch (prInfo.fAlphaType) {
242 case kIgnore_SkAlphaType:
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000243 SkASSERT(fInfo.fAlphaType == kIgnore_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000244 break;
245 case kOpaque_SkAlphaType:
246 case kPremul_SkAlphaType:
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000247 SkASSERT(fInfo.fAlphaType == kOpaque_SkAlphaType ||
248 fInfo.fAlphaType == kPremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000249 break;
250 case kUnpremul_SkAlphaType:
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000251 SkASSERT(fInfo.fAlphaType == kOpaque_SkAlphaType ||
252 fInfo.fAlphaType == kUnpremul_SkAlphaType);
reed@google.comdcea5302014-01-03 13:43:01 +0000253 break;
254 }
255 }
256 }
257#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258
reed@google.com672588b2014-01-08 15:42:01 +0000259 if (pr) {
260 const SkImageInfo& info = pr->info();
261 fPixelRefOrigin.set(SkPin32(dx, 0, info.fWidth),
262 SkPin32(dy, 0, info.fHeight));
263 } else {
264 // ignore dx,dy if there is no pixelref
265 fPixelRefOrigin.setZero();
266 }
267
268 if (fPixelRef != pr) {
piotaixr0eb02a62014-06-16 11:50:49 -0700269 this->freePixels();
270 SkASSERT(NULL == fPixelRef);
weita@google.comf9ab99a2009-05-03 18:23:30 +0000271
piotaixr0eb02a62014-06-16 11:50:49 -0700272 SkSafeRef(pr);
273 fPixelRef = pr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 this->updatePixelsFromRef();
275 }
276
277 SkDEBUGCODE(this->validate();)
278 return pr;
279}
280
281void SkBitmap::lockPixels() const {
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000282 if (NULL != fPixelRef && 0 == sk_atomic_inc(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fPixelRef->lockPixels();
284 this->updatePixelsFromRef();
285 }
286 SkDEBUGCODE(this->validate();)
287}
288
289void SkBitmap::unlockPixels() const {
290 SkASSERT(NULL == fPixelRef || fPixelLockCount > 0);
291
djsollen@google.com7c6d2642013-08-06 12:19:38 +0000292 if (NULL != fPixelRef && 1 == sk_atomic_dec(&fPixelLockCount)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 fPixelRef->unlockPixels();
294 this->updatePixelsFromRef();
295 }
296 SkDEBUGCODE(this->validate();)
297}
298
reed@google.com9c49bc32011-07-07 13:42:37 +0000299bool SkBitmap::lockPixelsAreWritable() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000300 return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
reed@google.com9c49bc32011-07-07 13:42:37 +0000301}
302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
reed@google.com8e1034e2012-07-30 13:16:35 +0000304 if (NULL == p) {
reed@google.com672588b2014-01-08 15:42:01 +0000305 this->setPixelRef(NULL);
reed@google.com8e1034e2012-07-30 13:16:35 +0000306 return;
307 }
308
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000309 if (kUnknown_SkColorType == fInfo.colorType()) {
reed@google.com672588b2014-01-08 15:42:01 +0000310 this->setPixelRef(NULL);
reed@google.combf790232013-12-13 19:45:58 +0000311 return;
312 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000314 SkPixelRef* pr = SkMallocPixelRef::NewDirect(fInfo, p, fRowBytes, ctable);
reed@google.combf790232013-12-13 19:45:58 +0000315 if (NULL == pr) {
reed@google.com672588b2014-01-08 15:42:01 +0000316 this->setPixelRef(NULL);
reed@google.combf790232013-12-13 19:45:58 +0000317 return;
318 }
319
320 this->setPixelRef(pr)->unref();
321
djsollen@google.comc84b8332012-07-27 13:41:44 +0000322 // since we're already allocated, we lockPixels right away
323 this->lockPixels();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 SkDEBUGCODE(this->validate();)
325}
326
327bool SkBitmap::allocPixels(Allocator* allocator, SkColorTable* ctable) {
328 HeapAllocator stdalloc;
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000329
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 if (NULL == allocator) {
331 allocator = &stdalloc;
332 }
333 return allocator->allocPixelRef(this, ctable);
334}
335
reed@google.com9ebcac52014-01-24 18:53:42 +0000336///////////////////////////////////////////////////////////////////////////////
337
reedbae704b2014-06-28 14:26:35 -0700338bool SkBitmap::allocPixels(const SkImageInfo& requestedInfo, size_t rowBytes) {
339 if (kIndex_8_SkColorType == requestedInfo.colorType()) {
340 return reset_return_false(this);
341 }
reedf0aed972014-07-01 12:48:11 -0700342 if (!this->setInfo(requestedInfo, rowBytes)) {
reedbae704b2014-06-28 14:26:35 -0700343 return reset_return_false(this);
344 }
345
346 // setInfo may have corrected info (e.g. 565 is always opaque).
347 const SkImageInfo& correctedInfo = this->info();
reedf0aed972014-07-01 12:48:11 -0700348 // setInfo may have computed a valid rowbytes if 0 were passed in
349 rowBytes = this->rowBytes();
350
reedbae704b2014-06-28 14:26:35 -0700351 SkMallocPixelRef::PRFactory defaultFactory;
352
reedf0aed972014-07-01 12:48:11 -0700353 SkPixelRef* pr = defaultFactory.create(correctedInfo, rowBytes, NULL);
reedbae704b2014-06-28 14:26:35 -0700354 if (NULL == pr) {
355 return reset_return_false(this);
356 }
357 this->setPixelRef(pr)->unref();
358
359 // TODO: lockPixels could/should return bool or void*/NULL
360 this->lockPixels();
361 if (NULL == this->getPixels()) {
362 return reset_return_false(this);
363 }
364 return true;
365}
366
scroggo0187dc22014-06-05 11:18:04 -0700367bool SkBitmap::allocPixels(const SkImageInfo& requestedInfo, SkPixelRefFactory* factory,
reed@google.com9ebcac52014-01-24 18:53:42 +0000368 SkColorTable* ctable) {
scroggo0187dc22014-06-05 11:18:04 -0700369 if (kIndex_8_SkColorType == requestedInfo.fColorType && NULL == ctable) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000370 return reset_return_false(this);
371 }
scroggo0187dc22014-06-05 11:18:04 -0700372 if (!this->setInfo(requestedInfo)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000373 return reset_return_false(this);
374 }
375
scroggo0187dc22014-06-05 11:18:04 -0700376 // setInfo may have corrected info (e.g. 565 is always opaque).
377 const SkImageInfo& correctedInfo = this->info();
378
reed@google.com9ebcac52014-01-24 18:53:42 +0000379 SkMallocPixelRef::PRFactory defaultFactory;
380 if (NULL == factory) {
381 factory = &defaultFactory;
382 }
skia.committer@gmail.comd2ac07b2014-01-25 07:01:49 +0000383
reedf0aed972014-07-01 12:48:11 -0700384 SkPixelRef* pr = factory->create(correctedInfo, correctedInfo.minRowBytes(), ctable);
reed@google.com9ebcac52014-01-24 18:53:42 +0000385 if (NULL == pr) {
386 return reset_return_false(this);
387 }
388 this->setPixelRef(pr)->unref();
389
390 // TODO: lockPixels could/should return bool or void*/NULL
391 this->lockPixels();
392 if (NULL == this->getPixels()) {
393 return reset_return_false(this);
394 }
395 return true;
396}
397
scroggo0187dc22014-06-05 11:18:04 -0700398bool SkBitmap::installPixels(const SkImageInfo& requestedInfo, void* pixels, size_t rb,
399 SkColorTable* ct, void (*releaseProc)(void* addr, void* context),
400 void* context) {
401 if (!this->setInfo(requestedInfo, rb)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000402 this->reset();
403 return false;
404 }
405
scroggo0187dc22014-06-05 11:18:04 -0700406 // setInfo may have corrected info (e.g. 565 is always opaque).
407 const SkImageInfo& correctedInfo = this->info();
408
409 SkPixelRef* pr = SkMallocPixelRef::NewWithProc(correctedInfo, rb, ct, pixels, releaseProc,
410 context);
reed@google.com9ebcac52014-01-24 18:53:42 +0000411 if (!pr) {
412 this->reset();
413 return false;
414 }
415
416 this->setPixelRef(pr)->unref();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000417
418 // since we're already allocated, we lockPixels right away
419 this->lockPixels();
mike@reedtribe.org6e58cf32014-02-16 20:54:21 +0000420 SkDEBUGCODE(this->validate();)
reed@google.com9ebcac52014-01-24 18:53:42 +0000421 return true;
422}
423
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000424bool SkBitmap::installMaskPixels(const SkMask& mask) {
425 if (SkMask::kA8_Format != mask.fFormat) {
426 this->reset();
427 return false;
428 }
429 return this->installPixels(SkImageInfo::MakeA8(mask.fBounds.width(),
430 mask.fBounds.height()),
431 mask.fImage, mask.fRowBytes);
432}
433
reed@google.comeb9a46c2014-01-25 16:46:20 +0000434///////////////////////////////////////////////////////////////////////////////
435
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436void SkBitmap::freePixels() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 if (NULL != fPixelRef) {
438 if (fPixelLockCount > 0) {
439 fPixelRef->unlockPixels();
440 }
441 fPixelRef->unref();
442 fPixelRef = NULL;
reed@google.com672588b2014-01-08 15:42:01 +0000443 fPixelRefOrigin.setZero();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 }
445 fPixelLockCount = 0;
446 fPixels = NULL;
reed@google.com5f62ed72014-01-15 19:59:45 +0000447 fColorTable = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448}
449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450uint32_t SkBitmap::getGenerationID() const {
djsollen@google.comc84b8332012-07-27 13:41:44 +0000451 return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452}
453
454void SkBitmap::notifyPixelsChanged() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000455 SkASSERT(!this->isImmutable());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 if (fPixelRef) {
457 fPixelRef->notifyPixelsChanged();
458 }
459}
460
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000461GrTexture* SkBitmap::getTexture() const {
reed@android.comce4e53a2010-09-09 16:01:26 +0000462 return fPixelRef ? fPixelRef->getTexture() : NULL;
463}
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465///////////////////////////////////////////////////////////////////////////////
466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467/** We explicitly use the same allocator for our pixels that SkMask does,
468 so that we can freely assign memory allocated by one class to the other.
469 */
470bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst,
471 SkColorTable* ctable) {
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000472 const SkImageInfo info = dst->info();
473 if (kUnknown_SkColorType == info.colorType()) {
reed@google.combf790232013-12-13 19:45:58 +0000474// SkDebugf("unsupported config for info %d\n", dst->config());
475 return false;
476 }
skia.committer@gmail.com96f5fa02013-12-16 07:01:40 +0000477
commit-bot@chromium.org466f5f32014-05-27 21:30:37 +0000478 SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, dst->rowBytes(), ctable);
reed@google.combf790232013-12-13 19:45:58 +0000479 if (NULL == pr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000480 return false;
481 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000482
reed@google.com672588b2014-01-08 15:42:01 +0000483 dst->setPixelRef(pr)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 // since we're already allocated, we lockPixels right away
485 dst->lockPixels();
486 return true;
487}
488
489///////////////////////////////////////////////////////////////////////////////
490
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000491bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000492 size_t dstRowBytes, bool preserveDstPad) const {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000493
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000494 if (0 == dstRowBytes) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000495 dstRowBytes = fRowBytes;
scroggo@google.com0ba4bf42013-02-25 16:02:36 +0000496 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000497
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000498 if (dstRowBytes < fInfo.minRowBytes() ||
499 dst == NULL || (getPixels() == NULL && pixelRef() == NULL)) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000500 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000501 }
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000502
bsalomon@google.comc6980972011-11-02 19:57:21 +0000503 if (!preserveDstPad && static_cast<uint32_t>(dstRowBytes) == fRowBytes) {
reed@google.com44699382013-10-31 17:28:30 +0000504 size_t safeSize = this->getSafeSize();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000505 if (safeSize > dstSize || safeSize == 0)
506 return false;
507 else {
508 SkAutoLockPixels lock(*this);
509 // This implementation will write bytes beyond the end of each row,
510 // excluding the last row, if the bitmap's stride is greater than
511 // strictly required by the current config.
512 memcpy(dst, getPixels(), safeSize);
513
514 return true;
515 }
516 } else {
517 // If destination has different stride than us, then copy line by line.
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000518 if (fInfo.getSafeSize(dstRowBytes) > dstSize) {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000519 return false;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000520 } else {
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000521 // Just copy what we need on each line.
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000522 size_t rowBytes = fInfo.minRowBytes();
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000523 SkAutoLockPixels lock(*this);
524 const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels());
525 uint8_t* dstP = reinterpret_cast<uint8_t*>(dst);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000526 for (int row = 0; row < fInfo.fHeight;
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000527 row++, srcP += fRowBytes, dstP += dstRowBytes) {
528 memcpy(dstP, srcP, rowBytes);
529 }
530
531 return true;
532 }
533 }
534}
535
wjmaclean@chromium.org86bff1f2010-11-16 20:22:41 +0000536///////////////////////////////////////////////////////////////////////////////
537
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000538bool SkBitmap::isImmutable() const {
junov@chromium.orgb0521292011-12-15 20:14:06 +0000539 return fPixelRef ? fPixelRef->isImmutable() :
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000540 fFlags & kImageIsImmutable_Flag;
junov@chromium.orgb0521292011-12-15 20:14:06 +0000541}
542
543void SkBitmap::setImmutable() {
544 if (fPixelRef) {
545 fPixelRef->setImmutable();
546 } else {
547 fFlags |= kImageIsImmutable_Flag;
548 }
549}
550
junov@google.com4ee7ae52011-06-30 17:30:49 +0000551bool SkBitmap::isVolatile() const {
552 return (fFlags & kImageIsVolatile_Flag) != 0;
553}
554
555void SkBitmap::setIsVolatile(bool isVolatile) {
556 if (isVolatile) {
557 fFlags |= kImageIsVolatile_Flag;
558 } else {
559 fFlags &= ~kImageIsVolatile_Flag;
560 }
561}
562
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563void* SkBitmap::getAddr(int x, int y) const {
564 SkASSERT((unsigned)x < (unsigned)this->width());
565 SkASSERT((unsigned)y < (unsigned)this->height());
566
567 char* base = (char*)this->getPixels();
568 if (base) {
569 base += y * this->rowBytes();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000570 switch (this->colorType()) {
571 case kRGBA_8888_SkColorType:
572 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 base += x << 2;
574 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000575 case kARGB_4444_SkColorType:
576 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 base += x << 1;
578 break;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000579 case kAlpha_8_SkColorType:
580 case kIndex_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");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 base = NULL;
586 break;
587 }
588 }
589 return base;
590}
591
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000592SkColor SkBitmap::getColor(int x, int y) const {
593 SkASSERT((unsigned)x < (unsigned)this->width());
594 SkASSERT((unsigned)y < (unsigned)this->height());
595
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000596 switch (this->colorType()) {
597 case kAlpha_8_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000598 uint8_t* addr = this->getAddr8(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000599 return SkColorSetA(0, addr[0]);
600 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000601 case kIndex_8_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000602 SkPMColor c = this->getIndex8Color(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000603 return SkUnPreMultiply::PMColorToColor(c);
604 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000605 case kRGB_565_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000606 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000607 return SkPixel16ToColor(addr[0]);
608 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000609 case kARGB_4444_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000610 uint16_t* addr = this->getAddr16(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000611 SkPMColor c = SkPixel4444ToPixel32(addr[0]);
612 return SkUnPreMultiply::PMColorToColor(c);
613 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000614 case kBGRA_8888_SkColorType:
615 case kRGBA_8888_SkColorType: {
reed@google.com3b521d02011-04-29 11:53:41 +0000616 uint32_t* addr = this->getAddr32(x, y);
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000617 return SkUnPreMultiply::PMColorToColor(addr[0]);
618 }
rmistry@google.comd6bab022013-12-02 13:50:38 +0000619 default:
vandebo@chromium.org112706d2011-02-24 22:50:55 +0000620 SkASSERT(false);
621 return 0;
622 }
623 SkASSERT(false); // Not reached.
624 return 0;
625}
626
reed@google.com2a7579d2012-11-07 18:30:18 +0000627bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
628 SkAutoLockPixels alp(bm);
629 if (!bm.getPixels()) {
630 return false;
631 }
632
633 const int height = bm.height();
634 const int width = bm.width();
635
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000636 switch (bm.colorType()) {
637 case kAlpha_8_SkColorType: {
reed@google.com2a7579d2012-11-07 18:30:18 +0000638 unsigned a = 0xFF;
639 for (int y = 0; y < height; ++y) {
640 const uint8_t* row = bm.getAddr8(0, y);
641 for (int x = 0; x < width; ++x) {
642 a &= row[x];
643 }
644 if (0xFF != a) {
645 return false;
646 }
647 }
648 return true;
649 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000650 case kIndex_8_SkColorType: {
reed@google.com2a7579d2012-11-07 18:30:18 +0000651 SkAutoLockColors alc(bm);
652 const SkPMColor* table = alc.colors();
653 if (!table) {
654 return false;
655 }
reed@google.com140d7282013-01-07 20:25:04 +0000656 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000657 for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
658 c &= table[i];
659 }
660 return 0xFF == SkGetPackedA32(c);
661 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000662 case kRGB_565_SkColorType:
reed@google.com2a7579d2012-11-07 18:30:18 +0000663 return true;
664 break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000665 case kARGB_4444_SkColorType: {
reed@google.com2a7579d2012-11-07 18:30:18 +0000666 unsigned c = 0xFFFF;
667 for (int y = 0; y < height; ++y) {
668 const SkPMColor16* row = bm.getAddr16(0, y);
669 for (int x = 0; x < width; ++x) {
670 c &= row[x];
671 }
672 if (0xF != SkGetPackedA4444(c)) {
673 return false;
674 }
675 }
676 return true;
677 } break;
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +0000678 case kBGRA_8888_SkColorType:
679 case kRGBA_8888_SkColorType: {
reed@google.com140d7282013-01-07 20:25:04 +0000680 SkPMColor c = (SkPMColor)~0;
reed@google.com2a7579d2012-11-07 18:30:18 +0000681 for (int y = 0; y < height; ++y) {
682 const SkPMColor* row = bm.getAddr32(0, y);
683 for (int x = 0; x < width; ++x) {
684 c &= row[x];
685 }
686 if (0xFF != SkGetPackedA32(c)) {
687 return false;
688 }
689 }
690 return true;
691 }
692 default:
693 break;
694 }
695 return false;
696}
697
698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699///////////////////////////////////////////////////////////////////////////////
700///////////////////////////////////////////////////////////////////////////////
701
reed@google.com45f746f2013-06-21 19:51:31 +0000702static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
703 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
704 (SkR32To4444(r) << SK_R4444_SHIFT) |
705 (SkG32To4444(g) << SK_G4444_SHIFT) |
706 (SkB32To4444(b) << SK_B4444_SHIFT);
707 return SkToU16(pixel);
708}
709
reed@google.com60d32352013-06-28 19:40:50 +0000710void SkBitmap::internalErase(const SkIRect& area,
711 U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
712#ifdef SK_DEBUG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 SkDEBUGCODE(this->validate();)
reed@google.com60d32352013-06-28 19:40:50 +0000714 SkASSERT(!area.isEmpty());
715 {
reed@google.com92833f92013-06-28 19:47:43 +0000716 SkIRect total = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000717 SkASSERT(total.contains(area));
718 }
719#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000721 switch (fInfo.colorType()) {
722 case kUnknown_SkColorType:
723 case kIndex_8_SkColorType:
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000724 return; // can't erase. Should we bzero so the memory is not uninitialized?
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000725 default:
726 break;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 }
728
729 SkAutoLockPixels alp(*this);
730 // perform this check after the lock call
731 if (!this->readyToDraw()) {
732 return;
733 }
734
reed@google.com60d32352013-06-28 19:40:50 +0000735 int height = area.height();
736 const int width = area.width();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 const int rowBytes = fRowBytes;
738
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000739 switch (this->colorType()) {
740 case kAlpha_8_SkColorType: {
reed@google.com60d32352013-06-28 19:40:50 +0000741 uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 while (--height >= 0) {
743 memset(p, a, width);
744 p += rowBytes;
745 }
746 break;
747 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000748 case kARGB_4444_SkColorType:
749 case kRGB_565_SkColorType: {
reed@google.com60d32352013-06-28 19:40:50 +0000750 uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
reed@google.com45f746f2013-06-21 19:51:31 +0000751 uint16_t v;
skia.committer@gmail.com020b25b2013-06-22 07:00:58 +0000752
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000753 // make rgb premultiplied
754 if (255 != a) {
755 r = SkAlphaMul(r, a);
756 g = SkAlphaMul(g, a);
757 b = SkAlphaMul(b, a);
758 }
759
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000760 if (kARGB_4444_SkColorType == this->colorType()) {
reed@google.com45f746f2013-06-21 19:51:31 +0000761 v = pack_8888_to_4444(a, r, g, b);
762 } else {
763 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
764 g >> (8 - SK_G16_BITS),
765 b >> (8 - SK_B16_BITS));
766 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 while (--height >= 0) {
768 sk_memset16(p, v, width);
769 p = (uint16_t*)((char*)p + rowBytes);
770 }
771 break;
772 }
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000773 case kBGRA_8888_SkColorType:
774 case kRGBA_8888_SkColorType: {
reed@google.com60d32352013-06-28 19:40:50 +0000775 uint32_t* p = this->getAddr32(area.fLeft, area.fTop);
commit-bot@chromium.org7669a772014-03-27 15:30:35 +0000776
777 if (255 != a && kPremul_SkAlphaType == this->alphaType()) {
778 r = SkAlphaMul(r, a);
779 g = SkAlphaMul(g, a);
780 b = SkAlphaMul(b, a);
781 }
782 uint32_t v = kRGBA_8888_SkColorType == this->colorType() ?
783 SkPackARGB_as_RGBA(a, r, g, b) : SkPackARGB_as_BGRA(a, r, g, b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784
785 while (--height >= 0) {
786 sk_memset32(p, v, width);
787 p = (uint32_t*)((char*)p + rowBytes);
788 }
789 break;
790 }
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000791 default:
792 return; // no change, so don't call notifyPixelsChanged()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 }
weita@google.comf9ab99a2009-05-03 18:23:30 +0000794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 this->notifyPixelsChanged();
796}
797
reed@google.com60d32352013-06-28 19:40:50 +0000798void SkBitmap::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) const {
reed@google.com92833f92013-06-28 19:47:43 +0000799 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000800 if (!area.isEmpty()) {
801 this->internalErase(area, a, r, g, b);
802 }
803}
804
805void SkBitmap::eraseArea(const SkIRect& rect, SkColor c) const {
reed@google.com92833f92013-06-28 19:47:43 +0000806 SkIRect area = { 0, 0, this->width(), this->height() };
reed@google.com60d32352013-06-28 19:40:50 +0000807 if (area.intersect(rect)) {
808 this->internalErase(area, SkColorGetA(c), SkColorGetR(c),
809 SkColorGetG(c), SkColorGetB(c));
810 }
811}
812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813//////////////////////////////////////////////////////////////////////////////////////
814//////////////////////////////////////////////////////////////////////////////////////
815
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
817 SkDEBUGCODE(this->validate();)
818
djsollen@google.comc84b8332012-07-27 13:41:44 +0000819 if (NULL == result || NULL == fPixelRef) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 return false; // no src pixels
821 }
822
823 SkIRect srcRect, r;
824 srcRect.set(0, 0, this->width(), this->height());
825 if (!r.intersect(srcRect, subset)) {
826 return false; // r is empty (i.e. no intersection)
827 }
828
scroggo@google.coma2a31922012-12-07 19:14:45 +0000829 if (fPixelRef->getTexture() != NULL) {
830 // Do a deep copy
reede4538f52014-06-11 06:09:50 -0700831 SkPixelRef* pixelRef = fPixelRef->deepCopy(this->colorType(), &subset);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000832 if (pixelRef != NULL) {
833 SkBitmap dst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000834 dst.setInfo(SkImageInfo::Make(subset.width(), subset.height(),
835 this->colorType(), this->alphaType()));
scroggo@google.coma2a31922012-12-07 19:14:45 +0000836 dst.setIsVolatile(this->isVolatile());
scroggo@google.coma2a31922012-12-07 19:14:45 +0000837 dst.setPixelRef(pixelRef)->unref();
838 SkDEBUGCODE(dst.validate());
839 result->swap(dst);
840 return true;
841 }
842 }
843
scroggo@google.coma2a31922012-12-07 19:14:45 +0000844 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
845 // exited above.
846 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
847 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
848
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 SkBitmap dst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000850 dst.setInfo(SkImageInfo::Make(r.width(), r.height(), this->colorType(), this->alphaType()),
851 this->rowBytes());
skyostil@google.com0eb75762012-01-16 10:45:53 +0000852 dst.setIsVolatile(this->isVolatile());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853
854 if (fPixelRef) {
reed@google.com672588b2014-01-08 15:42:01 +0000855 SkIPoint origin = fPixelRefOrigin;
856 origin.fX += r.fLeft;
857 origin.fY += r.fTop;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 // share the pixelref with a custom offset
reed@google.com672588b2014-01-08 15:42:01 +0000859 dst.setPixelRef(fPixelRef, origin);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 }
861 SkDEBUGCODE(dst.validate();)
862
863 // we know we're good, so commit to result
864 result->swap(dst);
865 return true;
866}
867
868///////////////////////////////////////////////////////////////////////////////
869
870#include "SkCanvas.h"
871#include "SkPaint.h"
872
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000873bool SkBitmap::canCopyTo(SkColorType dstColorType) const {
874 if (this->colorType() == kUnknown_SkColorType) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 return false;
876 }
877
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000878 bool sameConfigs = (this->colorType() == dstColorType);
879 switch (dstColorType) {
880 case kAlpha_8_SkColorType:
881 case kRGB_565_SkColorType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000882 case kRGBA_8888_SkColorType:
883 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000885 case kIndex_8_SkColorType:
weita@google.comf9ab99a2009-05-03 18:23:30 +0000886 if (!sameConfigs) {
887 return false;
888 }
889 break;
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000890 case kARGB_4444_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000891 return sameConfigs || kN32_SkColorType == this->colorType();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 default:
893 return false;
894 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000895 return true;
896}
897
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000898bool SkBitmap::copyTo(SkBitmap* dst, SkColorType dstColorType,
899 Allocator* alloc) const {
900 if (!this->canCopyTo(dstColorType)) {
reed@android.comfbaa88d2009-05-06 17:44:34 +0000901 return false;
902 }
903
reed@google.com50dfa012011-04-01 19:05:36 +0000904 // if we have a texture, first get those pixels
905 SkBitmap tmpSrc;
906 const SkBitmap* src = this;
907
scroggo@google.coma2a31922012-12-07 19:14:45 +0000908 if (fPixelRef) {
909 SkIRect subset;
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000910 subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY,
911 fInfo.width(), fInfo.height());
reed@google.com672588b2014-01-08 15:42:01 +0000912 if (fPixelRef->readPixels(&tmpSrc, &subset)) {
scroggo0187dc22014-06-05 11:18:04 -0700913 if (fPixelRef->info().alphaType() == kUnpremul_SkAlphaType) {
914 // FIXME: The only meaningful implementation of readPixels
915 // (GrPixelRef) assumes premultiplied pixels.
916 return false;
917 }
reed@google.com672588b2014-01-08 15:42:01 +0000918 SkASSERT(tmpSrc.width() == this->width());
919 SkASSERT(tmpSrc.height() == this->height());
reed@google.com50dfa012011-04-01 19:05:36 +0000920
reed@google.com672588b2014-01-08 15:42:01 +0000921 // did we get lucky and we can just return tmpSrc?
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000922 if (tmpSrc.colorType() == dstColorType && NULL == alloc) {
reed@google.com672588b2014-01-08 15:42:01 +0000923 dst->swap(tmpSrc);
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000924 // If the result is an exact copy, clone the gen ID.
925 if (dst->pixelRef() && dst->pixelRef()->info() == fPixelRef->info()) {
reed@google.com672588b2014-01-08 15:42:01 +0000926 dst->pixelRef()->cloneGenID(*fPixelRef);
scroggo@google.coma2a31922012-12-07 19:14:45 +0000927 }
reed@google.com672588b2014-01-08 15:42:01 +0000928 return true;
scroggo@google.comd5764e82012-08-22 15:00:05 +0000929 }
reed@google.com672588b2014-01-08 15:42:01 +0000930
931 // fall through to the raster case
932 src = &tmpSrc;
reed@google.com50dfa012011-04-01 19:05:36 +0000933 }
reed@android.comfbaa88d2009-05-06 17:44:34 +0000934 }
reed@android.com311c82d2009-05-05 23:13:23 +0000935
reed@google.com50dfa012011-04-01 19:05:36 +0000936 // we lock this now, since we may need its colortable
937 SkAutoLockPixels srclock(*src);
938 if (!src->readyToDraw()) {
939 return false;
940 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000941
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000942 // The only way to be readyToDraw is if fPixelRef is non NULL.
943 SkASSERT(fPixelRef != NULL);
944
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000945 SkImageInfo dstInfo = src->info();
946 dstInfo.fColorType = dstColorType;
947
reed@google.com50dfa012011-04-01 19:05:36 +0000948 SkBitmap tmpDst;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000949 if (!tmpDst.setInfo(dstInfo)) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000950 return false;
951 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000952
weita@google.comf9ab99a2009-05-03 18:23:30 +0000953 // allocate colortable if srcConfig == kIndex8_Config
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000954 SkAutoTUnref<SkColorTable> ctable;
955 if (dstColorType == kIndex_8_SkColorType) {
956 // TODO: can we just ref() the src colortable? Is it reentrant-safe?
957 ctable.reset(SkNEW_ARGS(SkColorTable, (*src->getColorTable())));
958 }
reed@google.com50dfa012011-04-01 19:05:36 +0000959 if (!tmpDst.allocPixels(alloc, ctable)) {
weita@google.comf9ab99a2009-05-03 18:23:30 +0000960 return false;
961 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000962
reed@google.com50dfa012011-04-01 19:05:36 +0000963 if (!tmpDst.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 // allocator/lock failed
965 return false;
966 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000967
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000968 // pixelRef must be non NULL or tmpDst.readyToDraw() would have
969 // returned false.
970 SkASSERT(tmpDst.pixelRef() != NULL);
971
reed@android.comfbaa88d2009-05-06 17:44:34 +0000972 /* do memcpy for the same configs cases, else use drawing
weita@google.comf9ab99a2009-05-03 18:23:30 +0000973 */
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000974 if (src->colorType() == dstColorType) {
reed@google.com50dfa012011-04-01 19:05:36 +0000975 if (tmpDst.getSize() == src->getSize()) {
976 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
scroggo@google.com5ccae2c2014-01-15 16:56:52 +0000977
scroggo0187dc22014-06-05 11:18:04 -0700978 SkPixelRef* dstPixelRef = tmpDst.pixelRef();
979 if (dstPixelRef->info() == fPixelRef->info()) {
980 dstPixelRef->cloneGenID(*fPixelRef);
scroggo@google.comd5764e82012-08-22 15:00:05 +0000981 }
reed@android.com311c82d2009-05-05 23:13:23 +0000982 } else {
reed@google.com50dfa012011-04-01 19:05:36 +0000983 const char* srcP = reinterpret_cast<const char*>(src->getPixels());
984 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
reed@android.com311c82d2009-05-05 23:13:23 +0000985 // to be sure we don't read too much, only copy our logical pixels
reed@google.com50dfa012011-04-01 19:05:36 +0000986 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel();
987 for (int y = 0; y < tmpDst.height(); y++) {
reed@android.com311c82d2009-05-05 23:13:23 +0000988 memcpy(dstP, srcP, bytesToCopy);
reed@google.com50dfa012011-04-01 19:05:36 +0000989 srcP += src->rowBytes();
990 dstP += tmpDst.rowBytes();
reed@android.com311c82d2009-05-05 23:13:23 +0000991 }
992 }
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000993 } else if (kARGB_4444_SkColorType == dstColorType
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000994 && kN32_SkColorType == src->colorType()) {
scroggo0187dc22014-06-05 11:18:04 -0700995 if (src->alphaType() == kUnpremul_SkAlphaType) {
996 // Our method for converting to 4444 assumes premultiplied.
997 return false;
998 }
scroggo@google.com8dc8bc52013-08-07 19:16:05 +0000999 SkASSERT(src->height() == tmpDst.height());
1000 SkASSERT(src->width() == tmpDst.width());
1001 for (int y = 0; y < src->height(); ++y) {
1002 SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*) tmpDst.getAddr16(0, y);
1003 SkPMColor* SK_RESTRICT srcRow = (SkPMColor*) src->getAddr32(0, y);
1004 DITHER_4444_SCAN(y);
1005 for (int x = 0; x < src->width(); ++x) {
1006 dstRow[x] = SkDitherARGB32To4444(srcRow[x],
1007 DITHER_VALUE(x));
1008 }
1009 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001010 } else {
scroggo0187dc22014-06-05 11:18:04 -07001011 if (tmpDst.alphaType() == kUnpremul_SkAlphaType) {
1012 // We do not support drawing to unpremultiplied bitmaps.
1013 return false;
1014 }
1015
robertphillips@google.com0197b322013-10-10 15:48:16 +00001016 // Always clear the dest in case one of the blitters accesses it
1017 // TODO: switch the allocation of tmpDst to call sk_calloc_throw
1018 tmpDst.eraseColor(SK_ColorTRANSPARENT);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001019
reed@google.com50dfa012011-04-01 19:05:36 +00001020 SkCanvas canvas(tmpDst);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001021 SkPaint paint;
1022
1023 paint.setDither(true);
reed@google.com50dfa012011-04-01 19:05:36 +00001024 canvas.drawBitmap(*src, 0, 0, &paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 }
1026
reed@google.com50dfa012011-04-01 19:05:36 +00001027 dst->swap(tmpDst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 return true;
1029}
1030
commit-bot@chromium.orgfab349c2014-03-05 02:34:58 +00001031bool SkBitmap::deepCopyTo(SkBitmap* dst) const {
reede4538f52014-06-11 06:09:50 -07001032 const SkColorType dstCT = this->colorType();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001033
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +00001034 if (!this->canCopyTo(dstCT)) {
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001035 return false;
1036 }
1037
1038 // If we have a PixelRef, and it supports deep copy, use it.
1039 // Currently supported only by texture-backed bitmaps.
1040 if (fPixelRef) {
reede4538f52014-06-11 06:09:50 -07001041 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstCT, NULL);
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +00001042 if (pixelRef) {
1043 uint32_t rowBytes;
1044 if (this->colorType() == dstCT) {
1045 // Since there is no subset to pass to deepCopy, and deepCopy
1046 // succeeded, the new pixel ref must be identical.
1047 SkASSERT(fPixelRef->info() == pixelRef->info());
1048 pixelRef->cloneGenID(*fPixelRef);
1049 // Use the same rowBytes as the original.
1050 rowBytes = fRowBytes;
1051 } else {
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001052 // With the new config, an appropriate fRowBytes will be computed by setInfo.
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +00001053 rowBytes = 0;
1054 }
1055
1056 SkImageInfo info = fInfo;
1057 info.fColorType = dstCT;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001058 if (!dst->setInfo(info, rowBytes)) {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001059 return false;
1060 }
commit-bot@chromium.orgeeef0cc2014-05-21 15:58:00 +00001061 dst->setPixelRef(pixelRef, fPixelRefOrigin)->unref();
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001062 return true;
1063 }
1064 }
1065
1066 if (this->getTexture()) {
1067 return false;
1068 } else {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +00001069 return this->copyTo(dst, dstCT, NULL);
senorblanco@chromium.orgef843cd2011-12-02 19:11:17 +00001070 }
1071}
1072
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074
tomhudson@google.coma87cd2a2011-06-15 16:50:27 +00001075static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 int alphaRowBytes) {
1077 SkASSERT(alpha != NULL);
1078 SkASSERT(alphaRowBytes >= src.width());
1079
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001080 SkColorType colorType = src.colorType();
1081 int w = src.width();
1082 int h = src.height();
1083 size_t rb = src.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084
reed@android.com1cdcb512009-08-24 19:11:00 +00001085 SkAutoLockPixels alp(src);
1086 if (!src.readyToDraw()) {
1087 // zero out the alpha buffer and return
1088 while (--h >= 0) {
1089 memset(alpha, 0, w);
1090 alpha += alphaRowBytes;
1091 }
1092 return false;
1093 }
reed@google.com82065d62011-02-07 15:30:46 +00001094
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001095 if (kAlpha_8_SkColorType == colorType && !src.isOpaque()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 const uint8_t* s = src.getAddr8(0, 0);
1097 while (--h >= 0) {
1098 memcpy(alpha, s, w);
1099 s += rb;
1100 alpha += alphaRowBytes;
1101 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001102 } else if (kN32_SkColorType == colorType && !src.isOpaque()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 const SkPMColor* SK_RESTRICT s = src.getAddr32(0, 0);
1104 while (--h >= 0) {
1105 for (int x = 0; x < w; x++) {
1106 alpha[x] = SkGetPackedA32(s[x]);
1107 }
1108 s = (const SkPMColor*)((const char*)s + rb);
1109 alpha += alphaRowBytes;
1110 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001111 } else if (kARGB_4444_SkColorType == colorType && !src.isOpaque()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 const SkPMColor16* SK_RESTRICT s = src.getAddr16(0, 0);
1113 while (--h >= 0) {
1114 for (int x = 0; x < w; x++) {
1115 alpha[x] = SkPacked4444ToA32(s[x]);
1116 }
1117 s = (const SkPMColor16*)((const char*)s + rb);
1118 alpha += alphaRowBytes;
1119 }
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001120 } else if (kIndex_8_SkColorType == colorType && !src.isOpaque()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 SkColorTable* ct = src.getColorTable();
1122 if (ct) {
1123 const SkPMColor* SK_RESTRICT table = ct->lockColors();
1124 const uint8_t* SK_RESTRICT s = src.getAddr8(0, 0);
1125 while (--h >= 0) {
1126 for (int x = 0; x < w; x++) {
1127 alpha[x] = SkGetPackedA32(table[s[x]]);
1128 }
1129 s += rb;
1130 alpha += alphaRowBytes;
1131 }
reed@google.com0a6151d2013-10-10 14:44:56 +00001132 ct->unlockColors();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 }
1134 } else { // src is opaque, so just fill alpha[] with 0xFF
1135 memset(alpha, 0xFF, h * alphaRowBytes);
1136 }
reed@android.com1cdcb512009-08-24 19:11:00 +00001137 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138}
1139
1140#include "SkPaint.h"
1141#include "SkMaskFilter.h"
1142#include "SkMatrix.h"
1143
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001144bool SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
djsollen@google.com57f49692011-02-23 20:46:31 +00001145 Allocator *allocator, SkIPoint* offset) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146 SkDEBUGCODE(this->validate();)
1147
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001148 SkBitmap tmpBitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 SkMatrix identity;
1150 SkMask srcM, dstM;
1151
1152 srcM.fBounds.set(0, 0, this->width(), this->height());
1153 srcM.fRowBytes = SkAlign4(this->width());
1154 srcM.fFormat = SkMask::kA8_Format;
1155
1156 SkMaskFilter* filter = paint ? paint->getMaskFilter() : NULL;
1157
1158 // compute our (larger?) dst bounds if we have a filter
1159 if (NULL != filter) {
1160 identity.reset();
1161 srcM.fImage = NULL;
1162 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1163 goto NO_FILTER_CASE;
1164 }
1165 dstM.fRowBytes = SkAlign4(dstM.fBounds.width());
1166 } else {
1167 NO_FILTER_CASE:
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001168 tmpBitmap.setInfo(SkImageInfo::MakeA8(this->width(), this->height()), srcM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001169 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1170 // Allocation of pixels for alpha bitmap failed.
1171 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1172 tmpBitmap.width(), tmpBitmap.height());
1173 return false;
1174 }
1175 GetBitmapAlpha(*this, tmpBitmap.getAddr8(0, 0), srcM.fRowBytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 if (offset) {
1177 offset->set(0, 0);
1178 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001179 tmpBitmap.swap(*dst);
1180 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001182 srcM.fImage = SkMask::AllocImage(srcM.computeImageSize());
1183 SkAutoMaskFreeImage srcCleanup(srcM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184
1185 GetBitmapAlpha(*this, srcM.fImage, srcM.fRowBytes);
1186 if (!filter->filterMask(&dstM, srcM, identity, NULL)) {
1187 goto NO_FILTER_CASE;
1188 }
bungeman@google.com02f55842011-10-04 21:25:00 +00001189 SkAutoMaskFreeImage dstCleanup(dstM.fImage);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001191 tmpBitmap.setInfo(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
1192 dstM.fRowBytes);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001193 if (!tmpBitmap.allocPixels(allocator, NULL)) {
1194 // Allocation of pixels for alpha bitmap failed.
1195 SkDebugf("extractAlpha failed to allocate (%d,%d) alpha bitmap\n",
1196 tmpBitmap.width(), tmpBitmap.height());
1197 return false;
1198 }
1199 memcpy(tmpBitmap.getPixels(), dstM.fImage, dstM.computeImageSize());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 if (offset) {
1201 offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
1202 }
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001203 SkDEBUGCODE(tmpBitmap.validate();)
1204
1205 tmpBitmap.swap(*dst);
1206 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207}
1208
1209///////////////////////////////////////////////////////////////////////////////
1210
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001211void SkBitmap::WriteRawPixels(SkWriteBuffer* buffer, const SkBitmap& bitmap) {
1212 const SkImageInfo info = bitmap.info();
1213 SkAutoLockPixels alp(bitmap);
1214 if (0 == info.width() || 0 == info.height() || NULL == bitmap.getPixels()) {
1215 buffer->writeUInt(0); // instead of snugRB, signaling no pixels
1216 return;
1217 }
1218
1219 const size_t snugRB = info.width() * info.bytesPerPixel();
1220 const char* src = (const char*)bitmap.getPixels();
1221 const size_t ramRB = bitmap.rowBytes();
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001222
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001223 buffer->write32(SkToU32(snugRB));
1224 info.flatten(*buffer);
1225
1226 const size_t size = snugRB * info.height();
1227 SkAutoMalloc storage(size);
1228 char* dst = (char*)storage.get();
1229 for (int y = 0; y < info.height(); ++y) {
1230 memcpy(dst, src, snugRB);
1231 dst += snugRB;
1232 src += ramRB;
1233 }
1234 buffer->writeByteArray(storage.get(), size);
1235
1236 SkColorTable* ct = bitmap.getColorTable();
1237 if (kIndex_8_SkColorType == info.colorType() && ct) {
1238 buffer->writeBool(true);
1239 ct->writeToBuffer(*buffer);
1240 } else {
1241 buffer->writeBool(false);
1242 }
1243}
1244
1245bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) {
1246 const size_t snugRB = buffer->readUInt();
1247 if (0 == snugRB) { // no pixels
1248 return false;
1249 }
1250
1251 SkImageInfo info;
1252 info.unflatten(*buffer);
1253
1254 const size_t ramRB = info.minRowBytes();
1255 const int height = info.height();
1256 const size_t snugSize = snugRB * height;
1257 const size_t ramSize = ramRB * height;
commit-bot@chromium.org05858432014-05-30 01:06:44 +00001258 if (!buffer->validate(snugSize <= ramSize)) {
1259 return false;
1260 }
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001261
1262 char* dst = (char*)sk_malloc_throw(ramSize);
1263 buffer->readByteArray(dst, snugSize);
1264 SkAutoDataUnref data(SkData::NewFromMalloc(dst, ramSize));
1265
1266 if (snugSize != ramSize) {
1267 const char* srcRow = dst + snugRB * (height - 1);
1268 char* dstRow = dst + ramRB * (height - 1);
1269 for (int y = height - 1; y >= 1; --y) {
1270 memmove(dstRow, srcRow, snugRB);
1271 srcRow -= snugRB;
1272 dstRow -= ramRB;
1273 }
1274 SkASSERT(srcRow == dstRow); // first row does not need to be moved
1275 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001276
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001277 SkAutoTUnref<SkColorTable> ctable;
1278 if (buffer->readBool()) {
1279 ctable.reset(SkNEW_ARGS(SkColorTable, (*buffer)));
1280 }
skia.committer@gmail.com3c134a92014-05-24 03:05:26 +00001281
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001282 SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewWithData(info, info.minRowBytes(),
1283 ctable.get(), data.get()));
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001284 bitmap->setInfo(pr->info());
commit-bot@chromium.org968edca2014-05-23 13:21:55 +00001285 bitmap->setPixelRef(pr, 0, 0);
1286 return true;
1287}
1288
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289enum {
1290 SERIALIZE_PIXELTYPE_NONE,
djsollen@google.com21830d92012-08-07 19:49:41 +00001291 SERIALIZE_PIXELTYPE_REF_DATA
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292};
1293
commit-bot@chromium.org851155c2014-05-27 14:03:51 +00001294void SkBitmap::legacyUnflatten(SkReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 this->reset();
weita@google.comf9ab99a2009-05-03 18:23:30 +00001296
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001297 SkImageInfo info;
1298 info.unflatten(buffer);
1299 size_t rowBytes = buffer.readInt();
commit-bot@chromium.org33fed142014-02-13 18:46:13 +00001300 if (!buffer.validate((info.width() >= 0) && (info.height() >= 0) &&
1301 SkColorTypeIsValid(info.fColorType) &&
1302 SkAlphaTypeIsValid(info.fAlphaType) &&
scroggo2fd0d142014-07-01 07:08:19 -07001303 SkColorTypeValidateAlphaType(info.fColorType, info.fAlphaType) &&
commit-bot@chromium.org33fed142014-02-13 18:46:13 +00001304 info.validRowBytes(rowBytes))) {
1305 return;
1306 }
weita@google.comf9ab99a2009-05-03 18:23:30 +00001307
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +00001308 bool configIsValid = this->setInfo(info, rowBytes);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001309 buffer.validate(configIsValid);
weita@google.comf9ab99a2009-05-03 18:23:30 +00001310
djsollen@google.comc73dd5c2012-08-07 15:54:32 +00001311 int reftype = buffer.readInt();
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001312 if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) ||
1313 (SERIALIZE_PIXELTYPE_NONE == reftype))) {
1314 switch (reftype) {
1315 case SERIALIZE_PIXELTYPE_REF_DATA: {
reed@google.com672588b2014-01-08 15:42:01 +00001316 SkIPoint origin;
1317 origin.fX = buffer.readInt();
1318 origin.fY = buffer.readInt();
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001319 size_t offset = origin.fY * rowBytes + origin.fX * info.bytesPerPixel();
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001320 SkPixelRef* pr = buffer.readPixelRef();
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +00001321 if (!buffer.validate((NULL == pr) ||
1322 (pr->getAllocatedSizeInBytes() >= (offset + this->getSafeSize())))) {
reed@google.com672588b2014-01-08 15:42:01 +00001323 origin.setZero();
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +00001324 }
reed@google.com672588b2014-01-08 15:42:01 +00001325 SkSafeUnref(this->setPixelRef(pr, origin));
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +00001326 break;
1327 }
1328 case SERIALIZE_PIXELTYPE_NONE:
1329 break;
1330 default:
1331 SkDEBUGFAIL("unrecognized pixeltype in serialized data");
1332 sk_throw();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 }
1335}
1336
1337///////////////////////////////////////////////////////////////////////////////
1338
1339SkBitmap::RLEPixels::RLEPixels(int width, int height) {
1340 fHeight = height;
commit-bot@chromium.org235002f2013-10-09 18:39:59 +00001341 fYPtrs = (uint8_t**)sk_calloc_throw(height * sizeof(uint8_t*));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342}
1343
1344SkBitmap::RLEPixels::~RLEPixels() {
1345 sk_free(fYPtrs);
1346}
1347
1348///////////////////////////////////////////////////////////////////////////////
1349
1350#ifdef SK_DEBUG
1351void SkBitmap::validate() const {
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001352 fInfo.validate();
commit-bot@chromium.orgd5414e52014-02-13 22:30:38 +00001353
1354 // ImageInfo may not require this, but Bitmap ensures that opaque-only
1355 // colorTypes report opaque for their alphatype
1356 if (kRGB_565_SkColorType == fInfo.colorType()) {
1357 SkASSERT(kOpaque_SkAlphaType == fInfo.alphaType());
1358 }
1359
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001360 SkASSERT(fInfo.validRowBytes(fRowBytes));
scroggo@google.com8e990eb2013-06-14 15:55:56 +00001361 uint8_t allFlags = kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag;
1362#ifdef SK_BUILD_FOR_ANDROID
1363 allFlags |= kHasHardwareMipMap_Flag;
1364#endif
1365 SkASSERT(fFlags <= allFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 SkASSERT(fPixelLockCount >= 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367
reed@google.com615316c2014-01-15 19:15:23 +00001368 if (fPixels) {
1369 SkASSERT(fPixelRef);
1370 SkASSERT(fPixelLockCount > 0);
1371 SkASSERT(fPixelRef->isLocked());
1372 SkASSERT(fPixelRef->rowBytes() == fRowBytes);
1373 SkASSERT(fPixelRefOrigin.fX >= 0);
1374 SkASSERT(fPixelRefOrigin.fY >= 0);
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001375 SkASSERT(fPixelRef->info().width() >= (int)this->width() + fPixelRefOrigin.fX);
1376 SkASSERT(fPixelRef->info().fHeight >= (int)this->height() + fPixelRefOrigin.fY);
1377 SkASSERT(fPixelRef->rowBytes() >= fInfo.minRowBytes());
reed@google.com615316c2014-01-15 19:15:23 +00001378 } else {
1379 SkASSERT(NULL == fColorTable);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381}
1382#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001383
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001384#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001385void SkBitmap::toString(SkString* str) const {
1386
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001387 static const char* gColorTypeNames[kLastEnum_SkColorType + 1] = {
1388 "UNKNOWN", "A8", "565", "4444", "RGBA", "BGRA", "INDEX8",
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001389 };
1390
1391 str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
commit-bot@chromium.orgcba73782014-05-29 15:57:47 +00001392 gColorTypeNames[this->colorType()]);
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001393
1394 str->append(" (");
1395 if (this->isOpaque()) {
1396 str->append("opaque");
1397 } else {
1398 str->append("transparent");
1399 }
1400 if (this->isImmutable()) {
1401 str->append(", immutable");
1402 } else {
1403 str->append(", not-immutable");
1404 }
1405 str->append(")");
1406
1407 SkPixelRef* pr = this->pixelRef();
1408 if (NULL == pr) {
1409 // show null or the explicit pixel address (rare)
1410 str->appendf(" pixels:%p", this->getPixels());
1411 } else {
1412 const char* uri = pr->getURI();
1413 if (NULL != uri) {
1414 str->appendf(" uri:\"%s\"", uri);
1415 } else {
1416 str->appendf(" pixelref:%p", pr);
1417 }
1418 }
1419
1420 str->append(")");
1421}
1422#endif
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +00001423
1424///////////////////////////////////////////////////////////////////////////////
1425
1426#ifdef SK_DEBUG
1427void SkImageInfo::validate() const {
1428 SkASSERT(fWidth >= 0);
1429 SkASSERT(fHeight >= 0);
1430 SkASSERT(SkColorTypeIsValid(fColorType));
1431 SkASSERT(SkAlphaTypeIsValid(fAlphaType));
1432}
1433#endif