blob: 895922c6b873a388210e0887d768f08dc05646c5 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 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 */
bungemanf3c15b72015-08-19 11:56:48 -07007
8#include "SkBitmapProcShader.h"
9#include "SkBitmapProcState.h"
reed013e9e32015-09-15 14:46:27 -070010#include "SkBitmapProvider.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkColorPriv.h"
bungemanf3c15b72015-08-19 11:56:48 -070012#include "SkErrorInternals.h"
13#include "SkPixelRef.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000014#include "SkReadBuffer.h"
15#include "SkWriteBuffer.h"
humper@google.com3aad3b02013-09-04 19:23:53 +000016
humper@google.com30df03c2013-09-04 19:57:11 +000017#if SK_SUPPORT_GPU
bsalomonf276ac52015-10-09 13:36:42 -070018#include "SkGrPriv.h"
humper@google.com3aad3b02013-09-04 19:23:53 +000019#include "effects/GrBicubicEffect.h"
bungemanf3c15b72015-08-19 11:56:48 -070020#include "effects/GrSimpleTextureEffect.h"
humper@google.com30df03c2013-09-04 19:57:11 +000021#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000022
reed05a56472016-03-02 09:49:02 -080023static bool only_scale_and_translate(const SkMatrix& matrix) {
24 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
25 return (matrix.getType() & ~mask) == 0;
26}
27
28class BitmapProcInfoContext : public SkShader::Context {
29public:
30 // The context takes ownership of the info. It will call its destructor
31 // but will NOT free the memory.
32 BitmapProcInfoContext(const SkShader& shader, const SkShader::ContextRec& rec,
33 SkBitmapProcInfo* info)
34 : INHERITED(shader, rec)
35 , fInfo(info)
36 {
37 fFlags = 0;
38 if (fInfo->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) {
39 fFlags |= SkShader::kOpaqueAlpha_Flag;
40 }
41
42 if (1 == fInfo->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) {
43 fFlags |= SkShader::kConstInY32_Flag;
44 }
45 }
46
47 ~BitmapProcInfoContext() override {
48 // The bitmap proc state has been created outside of the context on memory that will be freed
49 // elsewhere. Only call the destructor but leave the freeing of the memory to the caller.
50 fInfo->~SkBitmapProcInfo();
51 }
52
53 uint32_t getFlags() const override { return fFlags; }
54
55private:
56 SkBitmapProcInfo* fInfo;
57 uint32_t fFlags;
58
59 typedef SkShader::Context INHERITED;
60};
61
62///////////////////////////////////////////////////////////////////////////////////////////////////
63
64class BitmapProcShaderContext : public BitmapProcInfoContext {
65public:
66 // The context takes ownership of the state. It will call its destructor
67 // but will NOT free the memory.
68 BitmapProcShaderContext(const SkShader& shader, const SkShader::ContextRec& rec,
69 SkBitmapProcState* state)
70 : INHERITED(shader, rec, state)
71 , fState(state)
72 {}
73
74 void shadeSpan(int x, int y, SkPMColor dstC[], int count) override {
75 const SkBitmapProcState& state = *fState;
76 if (state.getShaderProc32()) {
77 state.getShaderProc32()(&state, x, y, dstC, count);
78 return;
79 }
80
81 const int BUF_MAX = 128;
82 uint32_t buffer[BUF_MAX];
83 SkBitmapProcState::MatrixProc mproc = state.getMatrixProc();
84 SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
85 const int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
86
87 SkASSERT(state.fPixmap.addr());
88
89 for (;;) {
90 int n = SkTMin(count, max);
91 SkASSERT(n > 0 && n < BUF_MAX*2);
92 mproc(state, buffer, n, x, y);
93 sproc(state, buffer, n, dstC);
94
95 if ((count -= n) == 0) {
96 break;
97 }
98 SkASSERT(count > 0);
99 x += n;
100 dstC += n;
101 }
102 }
103
104 ShadeProc asAShadeProc(void** ctx) override {
105 if (fState->getShaderProc32()) {
106 *ctx = fState;
107 return (ShadeProc)fState->getShaderProc32();
108 }
109 return nullptr;
110 }
111
112private:
113 SkBitmapProcState* fState;
114
115 typedef BitmapProcInfoContext INHERITED;
116};
117
118///////////////////////////////////////////////////////////////////////////////////////////////////
119
120size_t SkBitmapProcShader::ContextSize(const ContextRec& rec) {
reed7a4d8472015-09-15 13:33:58 -0700121 // The SkBitmapProcState is stored outside of the context object, with the context holding
122 // a pointer to it.
123 return sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState);
124}
125
reed05a56472016-03-02 09:49:02 -0800126SkShader::Context* SkBitmapProcShader::MakeContext(const SkShader& shader,
127 TileMode tmx, TileMode tmy,
128 const SkBitmapProvider& provider,
129 const ContextRec& rec, void* storage) {
130 SkMatrix totalInverse;
131 // Do this first, so we know the matrix can be inverted.
132 if (!shader.computeTotalInverse(rec, &totalInverse)) {
133 return nullptr;
134 }
135
136 void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext);
137 SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy);
138
139 SkASSERT(state);
140 if (!state->setup(totalInverse, *rec.fPaint)) {
141 state->~SkBitmapProcState();
142 return nullptr;
143 }
144
145 return new (storage) BitmapProcShaderContext(shader, rec, state);
146}
147
148SkShader::Context* SkBitmapProcShader::onCreateContext(const ContextRec& rec, void* storage) const {
149 return MakeContext(*this, (TileMode)fTileModeX, (TileMode)fTileModeY,
150 SkBitmapProvider(fRawBitmap), rec, storage);
151}
152
153///////////////////////////////////////////////////////////////////////////////////////////////////
154
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000155SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src, TileMode tmx, TileMode tmy,
156 const SkMatrix* localMatrix)
157 : INHERITED(localMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 fRawBitmap = src;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000159 fTileModeX = (uint8_t)tmx;
160 fTileModeY = (uint8_t)tmy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161}
162
reed0f0af232015-09-08 11:02:04 -0700163bool SkBitmapProcShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 if (texture) {
165 *texture = fRawBitmap;
166 }
167 if (texM) {
168 texM->reset();
169 }
170 if (xy) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000171 xy[0] = (TileMode)fTileModeX;
172 xy[1] = (TileMode)fTileModeY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 }
reed0f0af232015-09-08 11:02:04 -0700174 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175}
176
reed9fa60da2014-08-21 07:59:51 -0700177SkFlattenable* SkBitmapProcShader::CreateProc(SkReadBuffer& buffer) {
178 SkMatrix lm;
179 buffer.readMatrix(&lm);
180 SkBitmap bm;
181 if (!buffer.readBitmap(&bm)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700182 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700183 }
184 bm.setImmutable();
185 TileMode mx = (TileMode)buffer.readUInt();
186 TileMode my = (TileMode)buffer.readUInt();
187 return SkShader::CreateBitmapShader(bm, mx, my, &lm);
188}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189
reed9fa60da2014-08-21 07:59:51 -0700190void SkBitmapProcShader::flatten(SkWriteBuffer& buffer) const {
191 buffer.writeMatrix(this->getLocalMatrix());
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000192 buffer.writeBitmap(fRawBitmap);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000193 buffer.writeUInt(fTileModeX);
194 buffer.writeUInt(fTileModeY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195}
196
junov@chromium.orgb6e16192011-12-09 15:48:03 +0000197bool SkBitmapProcShader::isOpaque() const {
198 return fRawBitmap.isOpaque();
199}
200
reed05a56472016-03-02 09:49:02 -0800201///////////////////////////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
reed@android.comc6459962009-08-25 19:15:31 +0000203#include "SkUnPreMultiply.h"
204#include "SkColorShader.h"
reed@google.com37a20122011-07-05 18:54:12 +0000205#include "SkEmptyShader.h"
reed@android.comc6459962009-08-25 19:15:31 +0000206
207// returns true and set color if the bitmap can be drawn as a single color
208// (for efficiency)
robertphillips5f865b92015-07-29 12:28:04 -0700209static bool can_use_color_shader(const SkBitmap& bm, SkColor* color) {
djsollenab51cbd2015-07-14 10:59:28 -0700210#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
211 // HWUI does not support color shaders (see b/22390304)
212 return false;
213#endif
214
reed@android.comc6459962009-08-25 19:15:31 +0000215 if (1 != bm.width() || 1 != bm.height()) {
216 return false;
217 }
218
219 SkAutoLockPixels alp(bm);
220 if (!bm.readyToDraw()) {
221 return false;
222 }
223
reed@google.com900ecf22014-02-20 20:55:37 +0000224 switch (bm.colorType()) {
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000225 case kN32_SkColorType:
reed@android.comc6459962009-08-25 19:15:31 +0000226 *color = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(0, 0));
227 return true;
reed@google.com900ecf22014-02-20 20:55:37 +0000228 case kRGB_565_SkColorType:
reed@android.comc6459962009-08-25 19:15:31 +0000229 *color = SkPixel16ToColor(*bm.getAddr16(0, 0));
230 return true;
reed@google.com900ecf22014-02-20 20:55:37 +0000231 case kIndex_8_SkColorType:
reed@android.comc6459962009-08-25 19:15:31 +0000232 *color = SkUnPreMultiply::PMColorToColor(bm.getIndex8Color(0, 0));
233 return true;
234 default: // just skip the other configs for now
235 break;
236 }
237 return false;
238}
239
robertphillips5f865b92015-07-29 12:28:04 -0700240static bool bitmap_is_too_big(const SkBitmap& bm) {
reed@google.com99c114e2012-05-03 20:14:26 +0000241 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
242 // communicates between its matrix-proc and its sampler-proc. Until we can
243 // widen that, we have to reject bitmaps that are larger.
244 //
robertphillips5f865b92015-07-29 12:28:04 -0700245 static const int kMaxSize = 65535;
reed@google.com99c114e2012-05-03 20:14:26 +0000246
robertphillips5f865b92015-07-29 12:28:04 -0700247 return bm.width() > kMaxSize || bm.height() > kMaxSize;
reed@google.com99c114e2012-05-03 20:14:26 +0000248}
249
mtklein7ef849d2014-11-24 09:11:45 -0800250SkShader* SkCreateBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
251 SkShader::TileMode tmy, const SkMatrix* localMatrix,
252 SkTBlitterAllocator* allocator) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkShader* shader;
reed@android.comc6459962009-08-25 19:15:31 +0000254 SkColor color;
robertphillips5f865b92015-07-29 12:28:04 -0700255 if (src.isNull() || bitmap_is_too_big(src)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700256 if (nullptr == allocator) {
halcanary385fe4d2015-08-26 13:07:48 -0700257 shader = new SkEmptyShader;
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000258 } else {
259 shader = allocator->createT<SkEmptyShader>();
260 }
robertphillips5f865b92015-07-29 12:28:04 -0700261 } else if (can_use_color_shader(src, &color)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700262 if (nullptr == allocator) {
halcanary385fe4d2015-08-26 13:07:48 -0700263 shader = new SkColorShader(color);
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000264 } else {
265 shader = allocator->createT<SkColorShader>(color);
266 }
reed@android.comc6459962009-08-25 19:15:31 +0000267 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700268 if (nullptr == allocator) {
halcanary385fe4d2015-08-26 13:07:48 -0700269 shader = new SkBitmapProcShader(src, tmx, tmy, localMatrix);
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000270 } else {
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000271 shader = allocator->createT<SkBitmapProcShader>(src, tmx, tmy, localMatrix);
commit-bot@chromium.orga5572e52014-03-07 03:24:41 +0000272 }
reed@android.comc6459962009-08-25 19:15:31 +0000273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 return shader;
275}
276
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277///////////////////////////////////////////////////////////////////////////////
278
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000279#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000280void SkBitmapProcShader::toString(SkString* str) const {
281 static const char* gTileModeName[SkShader::kTileModeCount] = {
282 "clamp", "repeat", "mirror"
283 };
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000285 str->append("BitmapShader: (");
reed@google.com7c2f27d2011-03-07 19:29:00 +0000286
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000287 str->appendf("(%s, %s)",
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000288 gTileModeName[fTileModeX],
289 gTileModeName[fTileModeY]);
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000290
291 str->append(" ");
292 fRawBitmap.toString(str);
293
294 this->INHERITED::toString(str);
295
296 str->append(")");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297}
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000298#endif
299
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000300///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000302#if SK_SUPPORT_GPU
303
304#include "GrTextureAccess.h"
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000305#include "SkGr.h"
joshualitt9bc39542015-08-12 12:57:54 -0700306#include "effects/GrSimpleTextureEffect.h"
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000307
bsalomonc21b09e2015-08-28 18:46:56 -0700308const GrFragmentProcessor* SkBitmapProcShader::asFragmentProcessor(GrContext* context,
309 const SkMatrix& viewM, const SkMatrix* localMatrix,
bsalomon4a339522015-10-06 08:40:50 -0700310 SkFilterQuality filterQuality) const {
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000311 SkMatrix matrix;
312 matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
313
commit-bot@chromium.org79b7eee2013-12-16 21:02:29 +0000314 SkMatrix lmInverse;
315 if (!this->getLocalMatrix().invert(&lmInverse)) {
bsalomonc21b09e2015-08-28 18:46:56 -0700316 return nullptr;
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000317 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000318 if (localMatrix) {
319 SkMatrix inv;
320 if (!localMatrix->invert(&inv)) {
bsalomonc21b09e2015-08-28 18:46:56 -0700321 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000322 }
323 lmInverse.postConcat(inv);
324 }
commit-bot@chromium.org79b7eee2013-12-16 21:02:29 +0000325 matrix.preConcat(lmInverse);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000326
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000327 SkShader::TileMode tm[] = {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000328 (TileMode)fTileModeX,
329 (TileMode)fTileModeY,
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000330 };
331
commit-bot@chromium.org79b7eee2013-12-16 21:02:29 +0000332 // Must set wrap and filter on the sampler before requesting a texture. In two places below
333 // we check the matrix scale factors to determine how to interpret the filter quality setting.
334 // This completely ignores the complexity of the drawVertices case where explicit local coords
335 // are provided by the caller.
joshualitt9bc39542015-08-12 12:57:54 -0700336 bool doBicubic;
337 GrTextureParams::FilterMode textureFilterMode =
bsalomonc21b09e2015-08-28 18:46:56 -0700338 GrSkFilterQualityToGrFilterMode(filterQuality, viewM, this->getLocalMatrix(),
joshualitt9bc39542015-08-12 12:57:54 -0700339 &doBicubic);
humper@google.comb86add12013-07-25 18:49:07 +0000340 GrTextureParams params(tm, textureFilterMode);
bsalomonafa95e22015-10-12 10:39:46 -0700341 SkAutoTUnref<GrTexture> texture(GrRefCachedBitmapTexture(context, fRawBitmap, params));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000342
bsalomonbcf0a522014-10-08 08:40:09 -0700343 if (!texture) {
humper@google.com3aad3b02013-09-04 19:23:53 +0000344 SkErrorInternals::SetError( kInternalError_SkError,
345 "Couldn't convert bitmap to texture.");
bsalomonc21b09e2015-08-28 18:46:56 -0700346 return nullptr;
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000347 }
mtklein7ef849d2014-11-24 09:11:45 -0800348
bsalomon85153772015-11-05 09:35:01 -0800349 SkAutoTUnref<const GrFragmentProcessor> inner;
joshualitt9bc39542015-08-12 12:57:54 -0700350 if (doBicubic) {
bsalomon4a339522015-10-06 08:40:50 -0700351 inner.reset(GrBicubicEffect::Create(texture, matrix, tm));
humper@google.com3aad3b02013-09-04 19:23:53 +0000352 } else {
bsalomon4a339522015-10-06 08:40:50 -0700353 inner.reset(GrSimpleTextureEffect::Create(texture, matrix, params));
humper@google.com3aad3b02013-09-04 19:23:53 +0000354 }
dandov9de5b512014-06-10 14:38:28 -0700355
bsalomonc21b09e2015-08-28 18:46:56 -0700356 if (kAlpha_8_SkColorType == fRawBitmap.colorType()) {
bsalomonf1b7a1d2015-09-28 06:26:28 -0700357 return GrFragmentProcessor::MulOutputByInputUnpremulColor(inner);
bsalomonc21b09e2015-08-28 18:46:56 -0700358 }
bsalomonf1b7a1d2015-09-28 06:26:28 -0700359 return GrFragmentProcessor::MulOutputByInputAlpha(inner);
dandov9de5b512014-06-10 14:38:28 -0700360}
361
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000362#endif