1 /*
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
8 #include "SkBitmap.h"
9 #include "SkCanvas.h"
10 #include "SkColorData.h"
11 #include "SkConvertPixels.h"
12 #include "SkData.h"
13 #include "SkImageInfoPriv.h"
14 #include "SkImageShader.h"
15 #include "SkHalf.h"
16 #include "SkMask.h"
17 #include "SkNx.h"
18 #include "SkPM4f.h"
19 #include "SkPixmapPriv.h"
20 #include "SkReadPixelsRec.h"
21 #include "SkSurface.h"
22 #include "SkTemplates.h"
23 #include "SkUnPreMultiply.h"
24 #include "SkUtils.h"
25
26 /////////////////////////////////////////////////////////////////////////////////////////////////
27
reset()28 void SkPixmap::reset() {
29 fPixels = nullptr;
30 fRowBytes = 0;
31 fInfo = SkImageInfo::MakeUnknown();
32 }
33
reset(const SkImageInfo & info,const void * addr,size_t rowBytes)34 void SkPixmap::reset(const SkImageInfo& info, const void* addr, size_t rowBytes) {
35 if (addr) {
36 SkASSERT(info.validRowBytes(rowBytes));
37 }
38 fPixels = addr;
39 fRowBytes = rowBytes;
40 fInfo = info;
41 }
42
reset(const SkMask & src)43 bool SkPixmap::reset(const SkMask& src) {
44 if (SkMask::kA8_Format == src.fFormat) {
45 this->reset(SkImageInfo::MakeA8(src.fBounds.width(), src.fBounds.height()),
46 src.fImage, src.fRowBytes);
47 return true;
48 }
49 this->reset();
50 return false;
51 }
52
setColorSpace(sk_sp<SkColorSpace> cs)53 void SkPixmap::setColorSpace(sk_sp<SkColorSpace> cs) {
54 fInfo = fInfo.makeColorSpace(std::move(cs));
55 }
56
extractSubset(SkPixmap * result,const SkIRect & subset) const57 bool SkPixmap::extractSubset(SkPixmap* result, const SkIRect& subset) const {
58 SkIRect srcRect, r;
59 srcRect.set(0, 0, this->width(), this->height());
60 if (!r.intersect(srcRect, subset)) {
61 return false; // r is empty (i.e. no intersection)
62 }
63
64 // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
65 // exited above.
66 SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
67 SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
68
69 const void* pixels = nullptr;
70 if (fPixels) {
71 const size_t bpp = fInfo.bytesPerPixel();
72 pixels = (const uint8_t*)fPixels + r.fTop * fRowBytes + r.fLeft * bpp;
73 }
74 result->reset(fInfo.makeWH(r.width(), r.height()), pixels, fRowBytes);
75 return true;
76 }
77
readPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int x,int y,SkTransferFunctionBehavior behavior) const78 bool SkPixmap::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, int x, int y,
79 SkTransferFunctionBehavior behavior) const {
80 if (!SkImageInfoValidConversion(dstInfo, fInfo)) {
81 return false;
82 }
83
84 SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, x, y);
85 if (!rec.trim(fInfo.width(), fInfo.height())) {
86 return false;
87 }
88
89 const void* srcPixels = this->addr(rec.fX, rec.fY);
90 const SkImageInfo srcInfo = fInfo.makeWH(rec.fInfo.width(), rec.fInfo.height());
91 SkConvertPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, srcInfo, srcPixels, this->rowBytes(),
92 nullptr, behavior);
93 return true;
94 }
95
pack_8888_to_4444(unsigned a,unsigned r,unsigned g,unsigned b)96 static uint16_t pack_8888_to_4444(unsigned a, unsigned r, unsigned g, unsigned b) {
97 unsigned pixel = (SkA32To4444(a) << SK_A4444_SHIFT) |
98 (SkR32To4444(r) << SK_R4444_SHIFT) |
99 (SkG32To4444(g) << SK_G4444_SHIFT) |
100 (SkB32To4444(b) << SK_B4444_SHIFT);
101 return SkToU16(pixel);
102 }
103
erase(SkColor color,const SkIRect & inArea) const104 bool SkPixmap::erase(SkColor color, const SkIRect& inArea) const {
105 if (nullptr == fPixels) {
106 return false;
107 }
108 SkIRect area;
109 if (!area.intersect(this->bounds(), inArea)) {
110 return false;
111 }
112
113 U8CPU a = SkColorGetA(color);
114 U8CPU r = SkColorGetR(color);
115 U8CPU g = SkColorGetG(color);
116 U8CPU b = SkColorGetB(color);
117
118 int height = area.height();
119 const int width = area.width();
120 const int rowBytes = this->rowBytes();
121
122 if (color == 0
123 && width == this->rowBytesAsPixels()
124 && inArea == this->bounds()) {
125 // All formats represent SkColor(0) as byte 0.
126 memset(this->writable_addr(), 0, (int64_t)height * rowBytes);
127 return true;
128 }
129
130 switch (this->colorType()) {
131 case kGray_8_SkColorType: {
132 if (255 != a) {
133 r = SkMulDiv255Round(r, a);
134 g = SkMulDiv255Round(g, a);
135 b = SkMulDiv255Round(b, a);
136 }
137 int gray = SkComputeLuminance(r, g, b);
138 uint8_t* p = this->writable_addr8(area.fLeft, area.fTop);
139 while (--height >= 0) {
140 memset(p, gray, width);
141 p += rowBytes;
142 }
143 break;
144 }
145 case kAlpha_8_SkColorType: {
146 uint8_t* p = this->writable_addr8(area.fLeft, area.fTop);
147 while (--height >= 0) {
148 memset(p, a, width);
149 p += rowBytes;
150 }
151 break;
152 }
153 case kARGB_4444_SkColorType:
154 case kRGB_565_SkColorType: {
155 uint16_t* p = this->writable_addr16(area.fLeft, area.fTop);
156 uint16_t v;
157
158 // make rgb premultiplied
159 if (255 != a) {
160 r = SkMulDiv255Round(r, a);
161 g = SkMulDiv255Round(g, a);
162 b = SkMulDiv255Round(b, a);
163 }
164
165 if (kARGB_4444_SkColorType == this->colorType()) {
166 v = pack_8888_to_4444(a, r, g, b);
167 } else {
168 v = SkPackRGB16(r >> (8 - SK_R16_BITS),
169 g >> (8 - SK_G16_BITS),
170 b >> (8 - SK_B16_BITS));
171 }
172 while (--height >= 0) {
173 sk_memset16(p, v, width);
174 p = (uint16_t*)((char*)p + rowBytes);
175 }
176 break;
177 }
178 case kBGRA_8888_SkColorType:
179 case kRGBA_8888_SkColorType: {
180 uint32_t* p = this->writable_addr32(area.fLeft, area.fTop);
181
182 if (255 != a && kPremul_SkAlphaType == this->alphaType()) {
183 r = SkMulDiv255Round(r, a);
184 g = SkMulDiv255Round(g, a);
185 b = SkMulDiv255Round(b, a);
186 }
187 uint32_t v = kRGBA_8888_SkColorType == this->colorType()
188 ? SkPackARGB_as_RGBA(a, r, g, b)
189 : SkPackARGB_as_BGRA(a, r, g, b);
190
191 while (--height >= 0) {
192 sk_memset32(p, v, width);
193 p = (uint32_t*)((char*)p + rowBytes);
194 }
195 break;
196 }
197 case kRGBA_F16_SkColorType:
198 // The colorspace is unspecified, so assume linear just like getColor().
199 this->erase(SkColor4f{(1 / 255.0f) * r,
200 (1 / 255.0f) * g,
201 (1 / 255.0f) * b,
202 (1 / 255.0f) * a}, &area);
203 break;
204 default:
205 return false; // no change, so don't call notifyPixelsChanged()
206 }
207 return true;
208 }
209
erase(const SkColor4f & origColor,const SkIRect * subset) const210 bool SkPixmap::erase(const SkColor4f& origColor, const SkIRect* subset) const {
211 SkPixmap pm;
212 if (subset) {
213 if (!this->extractSubset(&pm, *subset)) {
214 return false;
215 }
216 } else {
217 pm = *this;
218 }
219
220 const SkColor4f color = origColor.pin();
221
222 if (kRGBA_F16_SkColorType != pm.colorType()) {
223 return pm.erase(color.toSkColor());
224 }
225
226 const uint64_t half4 = color.premul().toF16();
227 for (int y = 0; y < pm.height(); ++y) {
228 sk_memset64(pm.writable_addr64(0, y), half4, pm.width());
229 }
230 return true;
231 }
232
scalePixels(const SkPixmap & actualDst,SkFilterQuality quality) const233 bool SkPixmap::scalePixels(const SkPixmap& actualDst, SkFilterQuality quality) const {
234 // We may need to tweak how we interpret these just a little below, so we make copies.
235 SkPixmap src = *this,
236 dst = actualDst;
237
238 // Can't do anthing with empty src or dst
239 if (src.width() <= 0 || src.height() <= 0 ||
240 dst.width() <= 0 || dst.height() <= 0) {
241 return false;
242 }
243
244 // no scaling involved?
245 if (src.width() == dst.width() && src.height() == dst.height()) {
246 return src.readPixels(dst);
247 }
248
249 // If src and dst are both unpremul, we'll fake them out to appear as if premul.
250 bool clampAsIfUnpremul = false;
251 if (src.alphaType() == kUnpremul_SkAlphaType &&
252 dst.alphaType() == kUnpremul_SkAlphaType) {
253 src.reset(src.info().makeAlphaType(kPremul_SkAlphaType), src.addr(), src.rowBytes());
254 dst.reset(dst.info().makeAlphaType(kPremul_SkAlphaType), dst.addr(), dst.rowBytes());
255
256 // In turn, we'll need to tell the image shader to clamp to [0,1] instead
257 // of the usual [0,a] when using a bicubic scaling (kHigh_SkFilterQuality)
258 // or a gamut transformation.
259 clampAsIfUnpremul = true;
260 }
261
262 SkBitmap bitmap;
263 if (!bitmap.installPixels(src)) {
264 return false;
265 }
266 bitmap.setImmutable(); // Don't copy when we create an image.
267 bitmap.setIsVolatile(true); // Disable any caching.
268
269 SkMatrix scale = SkMatrix::MakeRectToRect(SkRect::Make(src.bounds()),
270 SkRect::Make(dst.bounds()),
271 SkMatrix::kFill_ScaleToFit);
272
273 // We'll create a shader to do this draw so we have control over the bicubic clamp.
274 sk_sp<SkShader> shader = SkImageShader::Make(SkImage::MakeFromBitmap(bitmap),
275 SkShader::kClamp_TileMode,
276 SkShader::kClamp_TileMode,
277 &scale,
278 clampAsIfUnpremul);
279
280 sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(dst.info(),
281 dst.writable_addr(),
282 dst.rowBytes());
283 if (!shader || !surface) {
284 return false;
285 }
286
287 SkPaint paint;
288 paint.setBlendMode(SkBlendMode::kSrc);
289 paint.setFilterQuality(quality);
290 paint.setShader(std::move(shader));
291 surface->getCanvas()->drawPaint(paint);
292 return true;
293 }
294
295 //////////////////////////////////////////////////////////////////////////////////////////////////
296
getColor(int x,int y) const297 SkColor SkPixmap::getColor(int x, int y) const {
298 SkASSERT(this->addr());
299 SkASSERT((unsigned)x < (unsigned)this->width());
300 SkASSERT((unsigned)y < (unsigned)this->height());
301
302 const bool needsUnpremul = (kPremul_SkAlphaType == fInfo.alphaType());
303 auto toColor = [needsUnpremul](uint32_t maybePremulColor) {
304 return needsUnpremul ? SkUnPreMultiply::PMColorToColor(maybePremulColor)
305 : SkSwizzle_BGRA_to_PMColor(maybePremulColor);
306 };
307
308 switch (this->colorType()) {
309 case kGray_8_SkColorType: {
310 uint8_t value = *this->addr8(x, y);
311 return SkColorSetRGB(value, value, value);
312 }
313 case kAlpha_8_SkColorType: {
314 return SkColorSetA(0, *this->addr8(x, y));
315 }
316 case kRGB_565_SkColorType: {
317 return SkPixel16ToColor(*this->addr16(x, y));
318 }
319 case kARGB_4444_SkColorType: {
320 uint16_t value = *this->addr16(x, y);
321 SkPMColor c = SkPixel4444ToPixel32(value);
322 return toColor(c);
323 }
324 case kBGRA_8888_SkColorType: {
325 uint32_t value = *this->addr32(x, y);
326 SkPMColor c = SkSwizzle_BGRA_to_PMColor(value);
327 return toColor(c);
328 }
329 case kRGBA_8888_SkColorType: {
330 uint32_t value = *this->addr32(x, y);
331 SkPMColor c = SkSwizzle_RGBA_to_PMColor(value);
332 return toColor(c);
333 }
334 case kRGBA_F16_SkColorType: {
335 const uint64_t* addr =
336 (const uint64_t*)fPixels + y * (fRowBytes >> 3) + x;
337 Sk4f p4 = SkHalfToFloat_finite_ftz(*addr);
338 if (p4[3] && needsUnpremul) {
339 float inva = 1 / p4[3];
340 p4 = p4 * Sk4f(inva, inva, inva, 1);
341 }
342 SkColor c;
343 SkNx_cast<uint8_t>(p4 * Sk4f(255) + Sk4f(0.5f)).store(&c);
344 // p4 is RGBA, but we want BGRA, so we need to swap next
345 return SkSwizzle_RB(c);
346 }
347 default:
348 SkDEBUGFAIL("");
349 return SkColorSetARGB(0, 0, 0, 0);
350 }
351 }
352
computeIsOpaque() const353 bool SkPixmap::computeIsOpaque() const {
354 const int height = this->height();
355 const int width = this->width();
356
357 switch (this->colorType()) {
358 case kAlpha_8_SkColorType: {
359 unsigned a = 0xFF;
360 for (int y = 0; y < height; ++y) {
361 const uint8_t* row = this->addr8(0, y);
362 for (int x = 0; x < width; ++x) {
363 a &= row[x];
364 }
365 if (0xFF != a) {
366 return false;
367 }
368 }
369 return true;
370 } break;
371 case kRGB_565_SkColorType:
372 case kGray_8_SkColorType:
373 return true;
374 break;
375 case kARGB_4444_SkColorType: {
376 unsigned c = 0xFFFF;
377 for (int y = 0; y < height; ++y) {
378 const SkPMColor16* row = this->addr16(0, y);
379 for (int x = 0; x < width; ++x) {
380 c &= row[x];
381 }
382 if (0xF != SkGetPackedA4444(c)) {
383 return false;
384 }
385 }
386 return true;
387 } break;
388 case kBGRA_8888_SkColorType:
389 case kRGBA_8888_SkColorType: {
390 SkPMColor c = (SkPMColor)~0;
391 for (int y = 0; y < height; ++y) {
392 const SkPMColor* row = this->addr32(0, y);
393 for (int x = 0; x < width; ++x) {
394 c &= row[x];
395 }
396 if (0xFF != SkGetPackedA32(c)) {
397 return false;
398 }
399 }
400 return true;
401 }
402 case kRGBA_F16_SkColorType: {
403 const SkHalf* row = (const SkHalf*)this->addr();
404 for (int y = 0; y < height; ++y) {
405 for (int x = 0; x < width; ++x) {
406 if (row[4 * x + 3] < SK_Half1) {
407 return false;
408 }
409 }
410 row += this->rowBytes() >> 1;
411 }
412 return true;
413 }
414 default:
415 break;
416 }
417 return false;
418 }
419
420 //////////////////////////////////////////////////////////////////////////////////////////////////
421
draw_orientation(const SkPixmap & dst,const SkPixmap & src,unsigned flags)422 static bool draw_orientation(const SkPixmap& dst, const SkPixmap& src, unsigned flags) {
423 auto surf = SkSurface::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
424 if (!surf) {
425 return false;
426 }
427
428 SkBitmap bm;
429 bm.installPixels(src);
430
431 SkMatrix m;
432 m.setIdentity();
433
434 SkScalar W = SkIntToScalar(src.width());
435 SkScalar H = SkIntToScalar(src.height());
436 if (flags & SkPixmapPriv::kSwapXY) {
437 SkMatrix s;
438 s.setAll(0, 1, 0, 1, 0, 0, 0, 0, 1);
439 m.postConcat(s);
440 SkTSwap(W, H);
441 }
442 if (flags & SkPixmapPriv::kMirrorX) {
443 m.postScale(-1, 1);
444 m.postTranslate(W, 0);
445 }
446 if (flags & SkPixmapPriv::kMirrorY) {
447 m.postScale(1, -1);
448 m.postTranslate(0, H);
449 }
450 SkPaint p;
451 p.setBlendMode(SkBlendMode::kSrc);
452 surf->getCanvas()->concat(m);
453 surf->getCanvas()->drawBitmap(bm, 0, 0, &p);
454 return true;
455 }
456
Orient(const SkPixmap & dst,const SkPixmap & src,OrientFlags flags)457 bool SkPixmapPriv::Orient(const SkPixmap& dst, const SkPixmap& src, OrientFlags flags) {
458 SkASSERT((flags & ~(kMirrorX | kMirrorY | kSwapXY)) == 0);
459 if (src.colorType() != dst.colorType()) {
460 return false;
461 }
462 // note: we just ignore alphaType and colorSpace for this transformation
463
464 int w = src.width();
465 int h = src.height();
466 if (flags & kSwapXY) {
467 SkTSwap(w, h);
468 }
469 if (dst.width() != w || dst.height() != h) {
470 return false;
471 }
472 if (w == 0 || h == 0) {
473 return true;
474 }
475
476 // check for aliasing to self
477 if (src.addr() == dst.addr()) {
478 return flags == 0;
479 }
480 return draw_orientation(dst, src, flags);
481 }
482
483 #define kMirrorX SkPixmapPriv::kMirrorX
484 #define kMirrorY SkPixmapPriv::kMirrorY
485 #define kSwapXY SkPixmapPriv::kSwapXY
486
487 static constexpr uint8_t gOrientationFlags[] = {
488 0, // kTopLeft_SkEncodedOrigin
489 kMirrorX, // kTopRight_SkEncodedOrigin
490 kMirrorX | kMirrorY, // kBottomRight_SkEncodedOrigin
491 kMirrorY, // kBottomLeft_SkEncodedOrigin
492 kSwapXY, // kLeftTop_SkEncodedOrigin
493 kMirrorX | kSwapXY, // kRightTop_SkEncodedOrigin
494 kMirrorX | kMirrorY | kSwapXY, // kRightBottom_SkEncodedOrigin
495 kMirrorY | kSwapXY, // kLeftBottom_SkEncodedOrigin
496 };
497
OriginToOrient(SkEncodedOrigin o)498 SkPixmapPriv::OrientFlags SkPixmapPriv::OriginToOrient(SkEncodedOrigin o) {
499 unsigned io = static_cast<int>(o) - 1;
500 SkASSERT(io < SK_ARRAY_COUNT(gOrientationFlags));
501 return static_cast<SkPixmapPriv::OrientFlags>(gOrientationFlags[io]);
502 }
503
ShouldSwapWidthHeight(SkEncodedOrigin o)504 bool SkPixmapPriv::ShouldSwapWidthHeight(SkEncodedOrigin o) {
505 return SkToBool(OriginToOrient(o) & kSwapXY);
506 }
507
SwapWidthHeight(const SkImageInfo & info)508 SkImageInfo SkPixmapPriv::SwapWidthHeight(const SkImageInfo& info) {
509 return info.makeWH(info.height(), info.width());
510 }
511
512