blob: d9a95a91ef1246e92c2519945c4305d3acf9bdd8 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
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
Herb Derby57bfa022017-02-09 17:25:43 -05008#include "SkArenaAlloc.h"
Mike Klein5cc9da62017-05-31 14:18:07 -04009#include "SkColorSpace.h"
Mike Kleine28a6b52018-07-25 13:05:17 -040010#include "SkColorSpacePriv.h"
Mike Kleinfcc10da2018-07-11 14:30:27 -040011#include "SkColorSpaceXformSteps.h"
Mike Klein5cc9da62017-05-31 14:18:07 -040012#include "SkCoreBlitters.h"
herba62038c2016-05-26 10:56:17 -070013#include "SkOpts.h"
Mike Klein5cc9da62017-05-31 14:18:07 -040014#include "SkPM4fPriv.h"
15#include "SkRasterPipeline.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkSpriteBlitter.h"
Mike Klein45c16fa2017-07-18 18:15:13 -040017#include "../jumper/SkJumper.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018
herb7df9e4a2016-06-10 13:01:27 -070019SkSpriteBlitter::SkSpriteBlitter(const SkPixmap& source)
20 : fSource(source) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000021
reedcb674142015-06-05 06:58:22 -070022void SkSpriteBlitter::setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) {
23 fDst = dst;
reed@android.com8a1c16f2008-12-17 15:59:43 +000024 fLeft = left;
25 fTop = top;
26 fPaint = &paint;
27}
28
mike@reedtribe.orgebe5bcd2011-04-20 11:01:37 +000029void SkSpriteBlitter::blitH(int x, int y, int width) {
tomhudson@google.com0c00f212011-12-28 14:59:50 +000030 SkDEBUGFAIL("how did we get here?");
herb7df9e4a2016-06-10 13:01:27 -070031
32 // Fallback to blitRect.
33 this->blitRect(x, y, width, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +000034}
35
herb7df9e4a2016-06-10 13:01:27 -070036void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) {
tomhudson@google.com0c00f212011-12-28 14:59:50 +000037 SkDEBUGFAIL("how did we get here?");
herb7df9e4a2016-06-10 13:01:27 -070038
39 // No fallback strategy.
reed@android.com8a1c16f2008-12-17 15:59:43 +000040}
41
mike@reedtribe.orgebe5bcd2011-04-20 11:01:37 +000042void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
tomhudson@google.com0c00f212011-12-28 14:59:50 +000043 SkDEBUGFAIL("how did we get here?");
herb7df9e4a2016-06-10 13:01:27 -070044
45 // Fall back to superclass if the code gets here in release mode.
46 INHERITED::blitV(x, y, height, alpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +000047}
48
herb7df9e4a2016-06-10 13:01:27 -070049void SkSpriteBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
tomhudson@google.com0c00f212011-12-28 14:59:50 +000050 SkDEBUGFAIL("how did we get here?");
herb7df9e4a2016-06-10 13:01:27 -070051
52 // Fall back to superclass if the code gets here in release mode.
53 INHERITED::blitMask(mask, clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +000054}
reed@android.com8a1c16f2008-12-17 15:59:43 +000055
mike@reedtribe.orgebe5bcd2011-04-20 11:01:37 +000056///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000057
Mike Reed1919dc42017-05-31 09:25:18 -040058class SkSpriteBlitter_Memcpy final : public SkSpriteBlitter {
reed8c3fd4f2016-04-15 06:59:38 -070059public:
60 static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) {
61 if (dst.colorType() != src.colorType()) {
62 return false;
63 }
Mike Klein23980332017-05-31 12:13:42 -040064 if (!SkColorSpace::Equals(dst.colorSpace(), src.colorSpace())) {
65 return false;
66 }
reed8c3fd4f2016-04-15 06:59:38 -070067 if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) {
68 return false;
69 }
70 if (0xFF != paint.getAlpha()) {
71 return false;
72 }
reed374772b2016-10-05 17:33:02 -070073 SkBlendMode mode = paint.getBlendMode();
Mike Reed1919dc42017-05-31 09:25:18 -040074 return SkBlendMode::kSrc == mode || (SkBlendMode::kSrcOver == mode && src.isOpaque());
reed8c3fd4f2016-04-15 06:59:38 -070075 }
76
Mike Reed1919dc42017-05-31 09:25:18 -040077 SkSpriteBlitter_Memcpy(const SkPixmap& src)
herb7df9e4a2016-06-10 13:01:27 -070078 : INHERITED(src) {}
reed8c3fd4f2016-04-15 06:59:38 -070079
reed8c3fd4f2016-04-15 06:59:38 -070080 void blitRect(int x, int y, int width, int height) override {
81 SkASSERT(fDst.colorType() == fSource.colorType());
reed8c3fd4f2016-04-15 06:59:38 -070082 SkASSERT(width > 0 && height > 0);
83
Mike Reed1919dc42017-05-31 09:25:18 -040084 char* dst = (char*)fDst.writable_addr(x, y);
85 const char* src = (const char*)fSource.addr(x - fLeft, y - fTop);
86 const size_t dstRB = fDst.rowBytes();
87 const size_t srcRB = fSource.rowBytes();
88 const size_t bytesToCopy = width << fSource.shiftPerPixel();
reed8c3fd4f2016-04-15 06:59:38 -070089
Mike Reed1919dc42017-05-31 09:25:18 -040090 while (height --> 0) {
91 memcpy(dst, src, bytesToCopy);
92 dst += dstRB;
93 src += srcRB;
reed8c3fd4f2016-04-15 06:59:38 -070094 }
95 }
96
herba62038c2016-05-26 10:56:17 -070097private:
reed8c3fd4f2016-04-15 06:59:38 -070098 typedef SkSpriteBlitter INHERITED;
herba62038c2016-05-26 10:56:17 -070099};
reed8c3fd4f2016-04-15 06:59:38 -0700100
Mike Klein5cc9da62017-05-31 14:18:07 -0400101class SkRasterPipelineSpriteBlitter : public SkSpriteBlitter {
102public:
103 SkRasterPipelineSpriteBlitter(const SkPixmap& src, SkArenaAlloc* alloc)
104 : INHERITED(src)
105 , fAlloc(alloc)
106 , fBlitter(nullptr)
Mike Klein45c16fa2017-07-18 18:15:13 -0400107 , fSrcPtr{nullptr, 0}
Mike Klein5cc9da62017-05-31 14:18:07 -0400108 {}
109
Mike Klein5cc9da62017-05-31 14:18:07 -0400110 void setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override {
111 fDst = dst;
112 fLeft = left;
113 fTop = top;
Mike Kleinbe569492018-09-14 09:34:21 -0400114 fPaintColor = paint.getColor4f();
Mike Klein5cc9da62017-05-31 14:18:07 -0400115
116 SkRasterPipeline p(fAlloc);
Mike Kleinafa35022018-09-17 16:41:26 -0400117 p.append_load(fSource.colorType(), &fSrcPtr);
Mike Klein9fb5a532018-09-13 15:23:38 -0400118
Mike Klein5cc9da62017-05-31 14:18:07 -0400119 if (fSource.colorType() == kAlpha_8_SkColorType) {
Mike Kleinfcc10da2018-07-11 14:30:27 -0400120 // The color for A8 images comes from the (sRGB) paint color.
Mike Kleinbe569492018-09-14 09:34:21 -0400121 p.append_set_rgb(fAlloc, fPaintColor);
Mike Klein5cc9da62017-05-31 14:18:07 -0400122 p.append(SkRasterPipeline::premul);
123 }
Mike Kleinfcc10da2018-07-11 14:30:27 -0400124 if (auto dstCS = fDst.colorSpace()) {
125 auto srcCS = fSource.colorSpace();
126 if (!srcCS || fSource.colorType() == kAlpha_8_SkColorType) {
127 // We treat untagged images as sRGB.
128 // A8 images get their r,g,b from the paint color, so they're also sRGB.
Mike Kleine28a6b52018-07-25 13:05:17 -0400129 srcCS = sk_srgb_singleton();
Mike Kleinfcc10da2018-07-11 14:30:27 -0400130 }
Mike Kleine28c7912018-08-14 11:49:34 -0400131 auto srcAT = fSource.isOpaque() ? kOpaque_SkAlphaType
132 : kPremul_SkAlphaType;
133 fAlloc->make<SkColorSpaceXformSteps>(srcCS, srcAT,
Mike Kleind4181d72018-08-14 10:28:05 -0400134 dstCS, kPremul_SkAlphaType)
Mike Kleinfcc10da2018-07-11 14:30:27 -0400135 ->apply(&p);
136 }
Mike Klein5cc9da62017-05-31 14:18:07 -0400137 if (fPaintColor.fA != 1.0f) {
138 p.append(SkRasterPipeline::scale_1_float, &fPaintColor.fA);
139 }
140
Mike Kleind4181d72018-08-14 10:28:05 -0400141 // TODO: use this knowledge when creating SkColorSpaceXformSteps above.
Mike Reedeb7dc792017-06-12 12:48:45 -0400142 bool is_opaque = fSource.isOpaque() && fPaintColor.fA == 1.0f;
143 fBlitter = SkCreateRasterPipelineBlitter(fDst, paint, p, is_opaque, fAlloc);
Mike Klein5cc9da62017-05-31 14:18:07 -0400144 }
145
146 void blitRect(int x, int y, int width, int height) override {
Mike Klein45c16fa2017-07-18 18:15:13 -0400147 fSrcPtr.stride = fSource.rowBytesAsPixels();
Mike Klein548d3872018-01-24 15:36:35 -0500148
149 // We really want fSrcPtr.pixels = fSource.addr(-fLeft, -fTop) here, but that asserts.
150 // Instead we ask for addr(-fLeft+x, -fTop+y), then back up (x,y) manually.
151 // Representing bpp as a size_t keeps all this math in size_t instead of int,
152 // which could wrap around with large enough fSrcPtr.stride and y.
153 size_t bpp = fSource.info().bytesPerPixel();
154 fSrcPtr.pixels = (char*)fSource.addr(-fLeft+x, -fTop+y) - bpp * x
155 - bpp * y * fSrcPtr.stride;
Mike Klein5cc9da62017-05-31 14:18:07 -0400156
Mike Klein45c16fa2017-07-18 18:15:13 -0400157 fBlitter->blitRect(x,y,width,height);
Mike Klein5cc9da62017-05-31 14:18:07 -0400158 }
159
160private:
Mike Klein45c16fa2017-07-18 18:15:13 -0400161 SkArenaAlloc* fAlloc;
162 SkBlitter* fBlitter;
163 SkJumper_MemoryCtx fSrcPtr;
164 SkColor4f fPaintColor;
Mike Klein5cc9da62017-05-31 14:18:07 -0400165
166 typedef SkSpriteBlitter INHERITED;
167};
168
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169// returning null means the caller will call SkBlitter::Choose() and
170// have wrapped the source bitmap inside a shader
reedcb674142015-06-05 06:58:22 -0700171SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint,
Herb Derby57bfa022017-02-09 17:25:43 -0500172 const SkPixmap& source, int left, int top, SkArenaAlloc* allocator) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 /* We currently ignore antialiasing and filtertype, meaning we will take our
174 special blitters regardless of these settings. Ignoring filtertype seems fine
175 since by definition there is no scale in the matrix. Ignoring antialiasing is
176 a bit of a hack, since we "could" pass in the fractional left/top for the bitmap,
177 and respect that by blending the edges of the bitmap against the device. To support
178 this we could either add more special blitters here, or detect antialiasing in the
179 paint and return null if it is set, forcing the client to take the slow shader case
180 (which does respect soft edges).
181 */
halcanary96fcdcc2015-08-27 07:41:13 -0700182 SkASSERT(allocator != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
herb58238462016-06-03 09:44:53 -0700184 if (source.alphaType() == kUnpremul_SkAlphaType) {
185 return nullptr;
186 }
187
herba62038c2016-05-26 10:56:17 -0700188 SkSpriteBlitter* blitter = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189
Mike Klein5cc9da62017-05-31 14:18:07 -0400190 if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) {
Mike Reed1919dc42017-05-31 09:25:18 -0400191 blitter = allocator->make<SkSpriteBlitter_Memcpy>(source);
Mike Klein5cc9da62017-05-31 14:18:07 -0400192 }
Mike Reedef8ce282017-10-05 16:04:41 -0400193 if (!blitter && !dst.colorSpace()) {
194 switch (dst.colorType()) {
195 case kN32_SkColorType:
196 blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator);
197 break;
198 case kRGB_565_SkColorType:
199 blitter = SkSpriteBlitter::ChooseL565(source, paint, allocator);
200 break;
201 case kAlpha_8_SkColorType:
202 blitter = SkSpriteBlitter::ChooseLA8(source, paint, allocator);
203 break;
204 default:
205 break;
206 }
Mike Klein5cc9da62017-05-31 14:18:07 -0400207 }
Mike Reed28266272018-01-17 23:04:04 -0500208 if (!blitter && !paint.getMaskFilter()) {
Mike Klein5cc9da62017-05-31 14:18:07 -0400209 blitter = allocator->make<SkRasterPipelineSpriteBlitter>(source, allocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 }
211
mike@reedtribe.orgebe5bcd2011-04-20 11:01:37 +0000212 if (blitter) {
reedcb674142015-06-05 06:58:22 -0700213 blitter->setup(dst, left, top, paint);
mike@reedtribe.orgebe5bcd2011-04-20 11:01:37 +0000214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 return blitter;
216}