blob: 1cee0a9d0286f63f1ada2193207583a31c917d98 [file] [log] [blame]
reed92fc2ae2015-05-22 08:06:21 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Hal Canary4cba3fe2016-12-07 14:59:27 -05008#include "SkBitmap.h"
9#include "SkCanvas.h"
Cary Clarka4083c92017-09-15 11:59:23 -040010#include "SkColorData.h"
Matt Sarett485c4992017-02-14 14:18:27 -050011#include "SkConvertPixels.h"
fmalita3a94c6c2016-02-04 13:09:59 -080012#include "SkData.h"
Matt Sarettcb6266b2017-01-17 10:48:53 -050013#include "SkImageInfoPriv.h"
Hal Canary4cba3fe2016-12-07 14:59:27 -050014#include "SkHalf.h"
reed183b57f2015-06-05 14:33:17 -070015#include "SkMask.h"
Hal Canary4cba3fe2016-12-07 14:59:27 -050016#include "SkNx.h"
reeddd9ffea2016-02-18 12:39:14 -080017#include "SkPM4f.h"
Hal Canary4cba3fe2016-12-07 14:59:27 -050018#include "SkPixmap.h"
Matt Sarett03dd6d52017-01-23 12:15:09 -050019#include "SkReadPixelsRec.h"
Hal Canary4cba3fe2016-12-07 14:59:27 -050020#include "SkSurface.h"
21#include "SkUtils.h"
reed92fc2ae2015-05-22 08:06:21 -070022
reed95d343f2015-05-23 13:21:06 -070023/////////////////////////////////////////////////////////////////////////////////////////////////
24
reed884e97c2015-05-26 11:31:54 -070025void SkPixmap::reset() {
halcanary96fcdcc2015-08-27 07:41:13 -070026 fPixels = nullptr;
reed884e97c2015-05-26 11:31:54 -070027 fRowBytes = 0;
28 fInfo = SkImageInfo::MakeUnknown();
29}
30
Mike Reed086a4272017-07-18 10:53:11 -040031void SkPixmap::reset(const SkImageInfo& info, const void* addr, size_t rowBytes) {
reed884e97c2015-05-26 11:31:54 -070032 if (addr) {
33 SkASSERT(info.validRowBytes(rowBytes));
34 }
35 fPixels = addr;
reed884e97c2015-05-26 11:31:54 -070036 fRowBytes = rowBytes;
37 fInfo = info;
38}
39
reed183b57f2015-06-05 14:33:17 -070040bool SkPixmap::reset(const SkMask& src) {
41 if (SkMask::kA8_Format == src.fFormat) {
42 this->reset(SkImageInfo::MakeA8(src.fBounds.width(), src.fBounds.height()),
Mike Reed086a4272017-07-18 10:53:11 -040043 src.fImage, src.fRowBytes);
reed183b57f2015-06-05 14:33:17 -070044 return true;
45 }
46 this->reset();
47 return false;
48}
49
msarett804b4612016-06-09 11:03:45 -070050void SkPixmap::setColorSpace(sk_sp<SkColorSpace> cs) {
51 fInfo = fInfo.makeColorSpace(std::move(cs));
52}
53
reed183b57f2015-06-05 14:33:17 -070054bool SkPixmap::extractSubset(SkPixmap* result, const SkIRect& subset) const {
55 SkIRect srcRect, r;
56 srcRect.set(0, 0, this->width(), this->height());
57 if (!r.intersect(srcRect, subset)) {
58 return false; // r is empty (i.e. no intersection)
59 }
halcanary9d524f22016-03-29 09:03:52 -070060
reed183b57f2015-06-05 14:33:17 -070061 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
62 // exited above.
63 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
64 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
65
halcanary96fcdcc2015-08-27 07:41:13 -070066 const void* pixels = nullptr;
reed183b57f2015-06-05 14:33:17 -070067 if (fPixels) {
68 const size_t bpp = fInfo.bytesPerPixel();
69 pixels = (const uint8_t*)fPixels + r.fTop * fRowBytes + r.fLeft * bpp;
70 }
Mike Reed26e9ddd2017-07-17 17:33:53 -040071 result->reset(fInfo.makeWH(r.width(), r.height()), pixels, fRowBytes);
reed183b57f2015-06-05 14:33:17 -070072 return true;
73}
74
Matt Sarett3928ff82017-06-06 10:11:34 -040075bool SkPixmap::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, int x, int y,
76 SkTransferFunctionBehavior behavior) const {
Matt Sarett03dd6d52017-01-23 12:15:09 -050077 if (!SkImageInfoValidConversion(dstInfo, fInfo)) {
reed95d343f2015-05-23 13:21:06 -070078 return false;
79 }
Matt Sarettcb6266b2017-01-17 10:48:53 -050080
Matt Sarett03dd6d52017-01-23 12:15:09 -050081 SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, x, y);
82 if (!rec.trim(fInfo.width(), fInfo.height())) {
reed95d343f2015-05-23 13:21:06 -070083 return false;
84 }
halcanary9d524f22016-03-29 09:03:52 -070085
Matt Sarett03dd6d52017-01-23 12:15:09 -050086 const void* srcPixels = this->addr(rec.fX, rec.fY);
87 const SkImageInfo srcInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
Matt Sarett485c4992017-02-14 14:18:27 -050088 SkConvertPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, srcInfo, srcPixels, this->rowBytes(),
Mike Reed086a4272017-07-18 10:53:11 -040089 nullptr, behavior);
Matt Sarett8572d852017-02-14 11:21:02 -050090 return true;
reed95d343f2015-05-23 13:21:06 -070091}
reed183b57f2015-06-05 14:33:17 -070092
reed7aefe032015-06-08 10:22:22 -070093static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
94 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
95 (SkR32To4444(r) << SK_R4444_SHIFT) |
96 (SkG32To4444(g) << SK_G4444_SHIFT) |
97 (SkB32To4444(b) << SK_B4444_SHIFT);
98 return SkToU16(pixel);
99}
100
101bool SkPixmap::erase(SkColor color, const SkIRect& inArea) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700102 if (nullptr == fPixels) {
reed7aefe032015-06-08 10:22:22 -0700103 return false;
104 }
105 SkIRect area;
106 if (!area.intersect(this->bounds(), inArea)) {
107 return false;
108 }
109
110 U8CPU a = SkColorGetA(color);
111 U8CPU r = SkColorGetR(color);
112 U8CPU g = SkColorGetG(color);
113 U8CPU b = SkColorGetB(color);
114
115 int height = area.height();
116 const int width = area.width();
117 const int rowBytes = this->rowBytes();
halcanary9d524f22016-03-29 09:03:52 -0700118
Mike Kleinadbbfa62017-09-01 11:20:46 -0400119 if (color == 0
120 && width == this->rowBytesAsPixels()
121 && inArea == this->bounds()) {
122 // All formats represent SkColor(0) as byte 0.
123 memset(this->writable_addr(), 0, height * rowBytes);
124 return true;
125 }
126
reed7aefe032015-06-08 10:22:22 -0700127 switch (this->colorType()) {
128 case kGray_8_SkColorType: {
129 if (255 != a) {
130 r = SkMulDiv255Round(r, a);
131 g = SkMulDiv255Round(g, a);
132 b = SkMulDiv255Round(b, a);
133 }
134 int gray = SkComputeLuminance(r, g, b);
135 uint8_t* p = this->writable_addr8(area.fLeft, area.fTop);
136 while (--height >= 0) {
137 memset(p, gray, width);
138 p += rowBytes;
139 }
140 break;
141 }
142 case kAlpha_8_SkColorType: {
143 uint8_t* p = this->writable_addr8(area.fLeft, area.fTop);
144 while (--height >= 0) {
145 memset(p, a, width);
146 p += rowBytes;
147 }
148 break;
149 }
150 case kARGB_4444_SkColorType:
151 case kRGB_565_SkColorType: {
152 uint16_t* p = this->writable_addr16(area.fLeft, area.fTop);
153 uint16_t v;
halcanary9d524f22016-03-29 09:03:52 -0700154
reed7aefe032015-06-08 10:22:22 -0700155 // make rgb premultiplied
156 if (255 != a) {
benjaminwagnera1bb8e02015-12-11 14:08:58 -0800157 r = SkMulDiv255Round(r, a);
158 g = SkMulDiv255Round(g, a);
159 b = SkMulDiv255Round(b, a);
reed7aefe032015-06-08 10:22:22 -0700160 }
halcanary9d524f22016-03-29 09:03:52 -0700161
reed7aefe032015-06-08 10:22:22 -0700162 if (kARGB_4444_SkColorType == this->colorType()) {
163 v = pack_8888_to_4444(a, r, g, b);
164 } else {
165 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
166 g >> (8 - SK_G16_BITS),
167 b >> (8 - SK_B16_BITS));
168 }
169 while (--height >= 0) {
170 sk_memset16(p, v, width);
171 p = (uint16_t*)((char*)p + rowBytes);
172 }
173 break;
174 }
175 case kBGRA_8888_SkColorType:
176 case kRGBA_8888_SkColorType: {
177 uint32_t* p = this->writable_addr32(area.fLeft, area.fTop);
halcanary9d524f22016-03-29 09:03:52 -0700178
reed7aefe032015-06-08 10:22:22 -0700179 if (255 != a && kPremul_SkAlphaType == this->alphaType()) {
benjaminwagnera1bb8e02015-12-11 14:08:58 -0800180 r = SkMulDiv255Round(r, a);
181 g = SkMulDiv255Round(g, a);
182 b = SkMulDiv255Round(b, a);
reed7aefe032015-06-08 10:22:22 -0700183 }
benjaminwagnera1bb8e02015-12-11 14:08:58 -0800184 uint32_t v = kRGBA_8888_SkColorType == this->colorType()
185 ? SkPackARGB_as_RGBA(a, r, g, b)
186 : SkPackARGB_as_BGRA(a, r, g, b);
187
reed7aefe032015-06-08 10:22:22 -0700188 while (--height >= 0) {
189 sk_memset32(p, v, width);
190 p = (uint32_t*)((char*)p + rowBytes);
191 }
192 break;
193 }
Hal Canary4cba3fe2016-12-07 14:59:27 -0500194 case kRGBA_F16_SkColorType:
195 // The colorspace is unspecified, so assume linear just like getColor().
196 this->erase(SkColor4f{(1 / 255.0f) * r,
197 (1 / 255.0f) * g,
198 (1 / 255.0f) * b,
199 (1 / 255.0f) * a}, &area);
200 break;
reed7aefe032015-06-08 10:22:22 -0700201 default:
202 return false; // no change, so don't call notifyPixelsChanged()
203 }
204 return true;
205}
206
reed3601f282016-02-05 11:18:39 -0800207bool SkPixmap::erase(const SkColor4f& origColor, const SkIRect* subset) const {
208 SkPixmap pm;
209 if (subset) {
210 if (!this->extractSubset(&pm, *subset)) {
211 return false;
212 }
213 } else {
214 pm = *this;
215 }
216
217 const SkColor4f color = origColor.pin();
218
219 if (kRGBA_F16_SkColorType != pm.colorType()) {
brianosmane074d1f2016-06-24 06:31:47 -0700220 return pm.erase(color.toSkColor());
reed3601f282016-02-05 11:18:39 -0800221 }
222
223 const uint64_t half4 = color.premul().toF16();
224 for (int y = 0; y < pm.height(); ++y) {
225 sk_memset64(pm.writable_addr64(0, y), half4, pm.width());
226 }
227 return true;
228}
229
reed09553032015-11-23 12:32:16 -0800230bool SkPixmap::scalePixels(const SkPixmap& dst, SkFilterQuality quality) const {
231 // Can't do anthing with empty src or dst
232 if (this->width() <= 0 || this->height() <= 0 || dst.width() <= 0 || dst.height() <= 0) {
233 return false;
234 }
235
236 // no scaling involved?
237 if (dst.width() == this->width() && dst.height() == this->height()) {
238 return this->readPixels(dst);
239 }
240
241 SkBitmap bitmap;
halcanarye36ec872015-12-09 11:36:59 -0800242 if (!bitmap.installPixels(*this)) {
reed09553032015-11-23 12:32:16 -0800243 return false;
244 }
245 bitmap.setIsVolatile(true); // so we don't try to cache it
246
reede8f30622016-03-23 18:59:25 -0700247 auto surface(SkSurface::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes()));
reed09553032015-11-23 12:32:16 -0800248 if (!surface) {
249 return false;
250 }
251
252 SkPaint paint;
253 paint.setFilterQuality(quality);
reed374772b2016-10-05 17:33:02 -0700254 paint.setBlendMode(SkBlendMode::kSrc);
reed09553032015-11-23 12:32:16 -0800255 surface->getCanvas()->drawBitmapRect(bitmap, SkRect::MakeIWH(dst.width(), dst.height()),
256 &paint);
257 return true;
258}
259
reed183b57f2015-06-05 14:33:17 -0700260//////////////////////////////////////////////////////////////////////////////////////////////////
Hal Canary94e1a2f2016-10-31 09:38:12 -0400261
262SkColor SkPixmap::getColor(int x, int y) const {
263 SkASSERT(this->addr());
264 SkASSERT((unsigned)x < (unsigned)this->width());
265 SkASSERT((unsigned)y < (unsigned)this->height());
Matt Sarett9466bf52017-06-09 09:54:55 -0400266
267 const bool needsUnpremul = (kPremul_SkAlphaType == fInfo.alphaType());
268 auto toColor = [needsUnpremul](uint32_t maybePremulColor) {
269 return needsUnpremul ? SkUnPreMultiply::PMColorToColor(maybePremulColor)
270 : SkSwizzle_BGRA_to_PMColor(maybePremulColor);
271 };
272
Hal Canary94e1a2f2016-10-31 09:38:12 -0400273 switch (this->colorType()) {
274 case kGray_8_SkColorType: {
275 uint8_t value = *this->addr8(x, y);
276 return SkColorSetRGB(value, value, value);
277 }
278 case kAlpha_8_SkColorType: {
279 return SkColorSetA(0, *this->addr8(x, y));
280 }
Hal Canary94e1a2f2016-10-31 09:38:12 -0400281 case kRGB_565_SkColorType: {
282 return SkPixel16ToColor(*this->addr16(x, y));
283 }
284 case kARGB_4444_SkColorType: {
285 uint16_t value = *this->addr16(x, y);
286 SkPMColor c = SkPixel4444ToPixel32(value);
Matt Sarett9466bf52017-06-09 09:54:55 -0400287 return toColor(c);
Hal Canary94e1a2f2016-10-31 09:38:12 -0400288 }
289 case kBGRA_8888_SkColorType: {
290 uint32_t value = *this->addr32(x, y);
291 SkPMColor c = SkSwizzle_BGRA_to_PMColor(value);
Matt Sarett9466bf52017-06-09 09:54:55 -0400292 return toColor(c);
Hal Canary94e1a2f2016-10-31 09:38:12 -0400293 }
294 case kRGBA_8888_SkColorType: {
295 uint32_t value = *this->addr32(x, y);
296 SkPMColor c = SkSwizzle_RGBA_to_PMColor(value);
Matt Sarett9466bf52017-06-09 09:54:55 -0400297 return toColor(c);
Hal Canary94e1a2f2016-10-31 09:38:12 -0400298 }
299 case kRGBA_F16_SkColorType: {
300 const uint64_t* addr =
301 (const uint64_t*)fPixels + y * (fRowBytes >> 3) + x;
302 Sk4f p4 = SkHalfToFloat_finite_ftz(*addr);
Matt Sarett9466bf52017-06-09 09:54:55 -0400303 if (p4[3] && needsUnpremul) {
Hal Canary94e1a2f2016-10-31 09:38:12 -0400304 float inva = 1 / p4[3];
305 p4 = p4 * Sk4f(inva, inva, inva, 1);
306 }
307 SkColor c;
308 SkNx_cast<uint8_t>(p4 * Sk4f(255) + Sk4f(0.5f)).store(&c);
309 // p4 is RGBA, but we want BGRA, so we need to swap next
310 return SkSwizzle_RB(c);
311 }
312 default:
313 SkDEBUGFAIL("");
314 return SkColorSetARGB(0, 0, 0, 0);
315 }
316}
Hal Canary58a76942016-12-07 15:24:59 -0500317
318bool SkPixmap::computeIsOpaque() const {
319 const int height = this->height();
320 const int width = this->width();
321
322 switch (this->colorType()) {
323 case kAlpha_8_SkColorType: {
324 unsigned a = 0xFF;
325 for (int y = 0; y < height; ++y) {
326 const uint8_t* row = this->addr8(0, y);
327 for (int x = 0; x < width; ++x) {
328 a &= row[x];
329 }
330 if (0xFF != a) {
331 return false;
332 }
333 }
334 return true;
335 } break;
Hal Canary58a76942016-12-07 15:24:59 -0500336 case kRGB_565_SkColorType:
337 case kGray_8_SkColorType:
338 return true;
339 break;
340 case kARGB_4444_SkColorType: {
341 unsigned c = 0xFFFF;
342 for (int y = 0; y < height; ++y) {
343 const SkPMColor16* row = this->addr16(0, y);
344 for (int x = 0; x < width; ++x) {
345 c &= row[x];
346 }
347 if (0xF != SkGetPackedA4444(c)) {
348 return false;
349 }
350 }
351 return true;
352 } break;
353 case kBGRA_8888_SkColorType:
354 case kRGBA_8888_SkColorType: {
355 SkPMColor c = (SkPMColor)~0;
356 for (int y = 0; y < height; ++y) {
357 const SkPMColor* row = this->addr32(0, y);
358 for (int x = 0; x < width; ++x) {
359 c &= row[x];
360 }
361 if (0xFF != SkGetPackedA32(c)) {
362 return false;
363 }
364 }
365 return true;
366 }
367 case kRGBA_F16_SkColorType: {
368 const SkHalf* row = (const SkHalf*)this->addr();
369 for (int y = 0; y < height; ++y) {
370 for (int x = 0; x < width; ++x) {
371 if (row[4 * x + 3] < SK_Half1) {
372 return false;
373 }
374 }
375 row += this->rowBytes() >> 1;
376 }
377 return true;
378 }
379 default:
380 break;
381 }
382 return false;
383}