blob: 85f964528ff97eeafd32b0ccbefa97fd851dd8a5 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkBitmapProcShader.h"
9#include "SkColorPriv.h"
10#include "SkPixelRef.h"
11
12bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) {
13 switch (bm.config()) {
14 case SkBitmap::kA8_Config:
15 case SkBitmap::kRGB_565_Config:
16 case SkBitmap::kIndex8_Config:
17 case SkBitmap::kARGB_8888_Config:
18 // if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx))
19 return true;
20 default:
21 break;
22 }
23 return false;
24}
25
26SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src,
27 TileMode tmx, TileMode tmy) {
28 fRawBitmap = src;
29 fState.fTileModeX = (uint8_t)tmx;
30 fState.fTileModeY = (uint8_t)tmy;
reed@android.com89bb83a2009-05-29 21:30:42 +000031 fFlags = 0; // computed in setContext
reed@android.com8a1c16f2008-12-17 15:59:43 +000032}
33
34SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer)
35 : INHERITED(buffer) {
36 fRawBitmap.unflatten(buffer);
37 fState.fTileModeX = buffer.readU8();
38 fState.fTileModeY = buffer.readU8();
reed@android.com89bb83a2009-05-29 21:30:42 +000039 fFlags = 0; // computed in setContext
reed@android.com8a1c16f2008-12-17 15:59:43 +000040}
41
42void SkBitmapProcShader::beginSession() {
43 this->INHERITED::beginSession();
44
45 fRawBitmap.lockPixels();
46}
47
48void SkBitmapProcShader::endSession() {
49 fRawBitmap.unlockPixels();
50
51 this->INHERITED::endSession();
52}
53
reed@google.com7c2f27d2011-03-07 19:29:00 +000054SkShader::BitmapType SkBitmapProcShader::asABitmap(SkBitmap* texture,
55 SkMatrix* texM,
reed@android.comf2b98d62010-12-20 18:26:13 +000056 TileMode xy[],
reed@google.com7c2f27d2011-03-07 19:29:00 +000057 SkScalar* twoPointRadialParams) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 if (texture) {
59 *texture = fRawBitmap;
60 }
61 if (texM) {
62 texM->reset();
63 }
64 if (xy) {
65 xy[0] = (TileMode)fState.fTileModeX;
66 xy[1] = (TileMode)fState.fTileModeY;
67 }
reed@android.comf2b98d62010-12-20 18:26:13 +000068 return kDefault_BitmapType;
reed@android.com8a1c16f2008-12-17 15:59:43 +000069}
70
71void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) {
72 this->INHERITED::flatten(buffer);
73
74 fRawBitmap.flatten(buffer);
75 buffer.write8(fState.fTileModeX);
76 buffer.write8(fState.fTileModeY);
77}
78
reed@android.com5119bdb2009-06-12 21:27:03 +000079static bool only_scale_and_translate(const SkMatrix& matrix) {
80 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
81 return (matrix.getType() & ~mask) == 0;
82}
83
reed@android.com8a1c16f2008-12-17 15:59:43 +000084bool SkBitmapProcShader::setContext(const SkBitmap& device,
85 const SkPaint& paint,
86 const SkMatrix& matrix) {
87 // do this first, so we have a correct inverse matrix
88 if (!this->INHERITED::setContext(device, paint, matrix)) {
89 return false;
90 }
91
92 fState.fOrigBitmap = fRawBitmap;
93 fState.fOrigBitmap.lockPixels();
reed@android.comf2b98d62010-12-20 18:26:13 +000094 if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 fState.fOrigBitmap.unlockPixels();
96 return false;
97 }
98
99 if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
100 return false;
101 }
102
reed@android.com7f6e1e92009-10-14 21:06:29 +0000103 const SkBitmap& bitmap = *fState.fBitmap;
104 bool bitmapIsOpaque = bitmap.isOpaque();
reed@google.com7c2f27d2011-03-07 19:29:00 +0000105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 // update fFlags
reed@android.comcafc9f92009-08-22 03:44:57 +0000107 uint32_t flags = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 if (bitmapIsOpaque && (255 == this->getPaintAlpha())) {
reed@android.comcafc9f92009-08-22 03:44:57 +0000109 flags |= kOpaqueAlpha_Flag;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 }
111
reed@android.com7f6e1e92009-10-14 21:06:29 +0000112 switch (bitmap.config()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 case SkBitmap::kRGB_565_Config:
reed@android.comcafc9f92009-08-22 03:44:57 +0000114 flags |= (kHasSpan16_Flag | kIntrinsicly16_Flag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 break;
116 case SkBitmap::kIndex8_Config:
117 case SkBitmap::kARGB_8888_Config:
118 if (bitmapIsOpaque) {
reed@android.comcafc9f92009-08-22 03:44:57 +0000119 flags |= kHasSpan16_Flag;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 }
121 break;
122 case SkBitmap::kA8_Config:
123 break; // never set kHasSpan16_Flag
124 default:
125 break;
126 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000127
reed@android.com7f6e1e92009-10-14 21:06:29 +0000128 if (paint.isDither() && bitmap.config() != SkBitmap::kRGB_565_Config) {
reed@android.comcafc9f92009-08-22 03:44:57 +0000129 // gradients can auto-dither in their 16bit sampler, but we don't so
reed@android.com7f6e1e92009-10-14 21:06:29 +0000130 // we clear the flag here.
reed@android.comcafc9f92009-08-22 03:44:57 +0000131 flags &= ~kHasSpan16_Flag;
132 }
133
reed@android.com5119bdb2009-06-12 21:27:03 +0000134 // if we're only 1-pixel heigh, and we don't rotate, then we can claim this
reed@android.com7f6e1e92009-10-14 21:06:29 +0000135 if (1 == bitmap.height() &&
reed@android.com5119bdb2009-06-12 21:27:03 +0000136 only_scale_and_translate(this->getTotalInverse())) {
reed@android.com3c9b2a42009-08-27 19:28:37 +0000137 flags |= kConstInY32_Flag;
138 if (flags & kHasSpan16_Flag) {
139 flags |= kConstInY16_Flag;
140 }
reed@android.com5119bdb2009-06-12 21:27:03 +0000141 }
reed@android.comcafc9f92009-08-22 03:44:57 +0000142
143 fFlags = flags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 return true;
145}
146
147#define BUF_MAX 128
148
reed@android.com258cb222010-04-14 13:36:33 +0000149#define TEST_BUFFER_OVERRITEx
150
151#ifdef TEST_BUFFER_OVERRITE
152 #define TEST_BUFFER_EXTRA 32
153 #define TEST_PATTERN 0x88888888
154#else
155 #define TEST_BUFFER_EXTRA 0
156#endif
157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
reed@android.com7a99eb12009-07-16 01:13:14 +0000159 const SkBitmapProcState& state = fState;
160 if (state.fShaderProc32) {
161 state.fShaderProc32(state, x, y, dstC, count);
162 return;
163 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
reed@android.com258cb222010-04-14 13:36:33 +0000165 uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 SkBitmapProcState::MatrixProc mproc = state.fMatrixProc;
167 SkBitmapProcState::SampleProc32 sproc = state.fSampleProc32;
reed@android.com258cb222010-04-14 13:36:33 +0000168 int max = fState.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
170 SkASSERT(state.fBitmap->getPixels());
171 SkASSERT(state.fBitmap->pixelRef() == NULL ||
172 state.fBitmap->pixelRef()->getLockCount());
173
174 for (;;) {
175 int n = count;
176 if (n > max) {
177 n = max;
178 }
reed@android.com258cb222010-04-14 13:36:33 +0000179 SkASSERT(n > 0 && n < BUF_MAX*2);
180#ifdef TEST_BUFFER_OVERRITE
181 for (int i = 0; i < TEST_BUFFER_EXTRA; i++) {
182 buffer[BUF_MAX + i] = TEST_PATTERN;
183 }
184#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 mproc(state, buffer, n, x, y);
reed@android.com258cb222010-04-14 13:36:33 +0000186#ifdef TEST_BUFFER_OVERRITE
187 for (int j = 0; j < TEST_BUFFER_EXTRA; j++) {
188 SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN);
189 }
190#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 sproc(state, buffer, n, dstC);
reed@google.com7c2f27d2011-03-07 19:29:00 +0000192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 if ((count -= n) == 0) {
194 break;
195 }
reed@android.com258cb222010-04-14 13:36:33 +0000196 SkASSERT(count > 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 x += n;
198 dstC += n;
199 }
200}
201
202void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
reed@android.com7a99eb12009-07-16 01:13:14 +0000203 const SkBitmapProcState& state = fState;
204 if (state.fShaderProc16) {
205 state.fShaderProc16(state, x, y, dstC, count);
206 return;
207 }
reed@google.com7c2f27d2011-03-07 19:29:00 +0000208
reed@android.com7a99eb12009-07-16 01:13:14 +0000209 uint32_t buffer[BUF_MAX];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 SkBitmapProcState::MatrixProc mproc = state.fMatrixProc;
211 SkBitmapProcState::SampleProc16 sproc = state.fSampleProc16;
reed@android.com4c128c42009-08-14 13:54:37 +0000212 int max = fState.maxCountForBufferSize(sizeof(buffer));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213
214 SkASSERT(state.fBitmap->getPixels());
215 SkASSERT(state.fBitmap->pixelRef() == NULL ||
216 state.fBitmap->pixelRef()->getLockCount());
217
218 for (;;) {
219 int n = count;
220 if (n > max) {
221 n = max;
222 }
223 mproc(state, buffer, n, x, y);
224 sproc(state, buffer, n, dstC);
reed@google.com7c2f27d2011-03-07 19:29:00 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 if ((count -= n) == 0) {
227 break;
228 }
229 x += n;
230 dstC += n;
231 }
232}
233
234///////////////////////////////////////////////////////////////////////////////
235
reed@android.comc6459962009-08-25 19:15:31 +0000236#include "SkUnPreMultiply.h"
237#include "SkColorShader.h"
reed@google.com37a20122011-07-05 18:54:12 +0000238#include "SkEmptyShader.h"
reed@android.comc6459962009-08-25 19:15:31 +0000239
240// returns true and set color if the bitmap can be drawn as a single color
241// (for efficiency)
242static bool canUseColorShader(const SkBitmap& bm, SkColor* color) {
243 if (1 != bm.width() || 1 != bm.height()) {
244 return false;
245 }
246
247 SkAutoLockPixels alp(bm);
248 if (!bm.readyToDraw()) {
249 return false;
250 }
251
252 switch (bm.config()) {
253 case SkBitmap::kARGB_8888_Config:
254 *color = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(0, 0));
255 return true;
256 case SkBitmap::kRGB_565_Config:
257 *color = SkPixel16ToColor(*bm.getAddr16(0, 0));
258 return true;
259 case SkBitmap::kIndex8_Config:
260 *color = SkUnPreMultiply::PMColorToColor(bm.getIndex8Color(0, 0));
261 return true;
262 default: // just skip the other configs for now
263 break;
264 }
265 return false;
266}
267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268#include "SkTemplatesPriv.h"
269
270SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
271 TileMode tmx, TileMode tmy,
272 void* storage, size_t storageSize) {
273 SkShader* shader;
reed@android.comc6459962009-08-25 19:15:31 +0000274 SkColor color;
reed@google.com37a20122011-07-05 18:54:12 +0000275 if (src.isNull()) {
276 SK_PLACEMENT_NEW(shader, SkEmptyShader, storage, storageSize);
277 }
278 else if (canUseColorShader(src, &color)) {
reed@android.comc6459962009-08-25 19:15:31 +0000279 SK_PLACEMENT_NEW_ARGS(shader, SkColorShader, storage, storageSize,
280 (color));
281 } else {
282 SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
283 storageSize, (src, tmx, tmy));
284 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 return shader;
286}
287
288static SkFlattenable::Registrar gBitmapProcShaderReg("SkBitmapProcShader",
289 SkBitmapProcShader::CreateProc);
290
291///////////////////////////////////////////////////////////////////////////////
292
293static const char* gTileModeName[] = {
294 "clamp", "repeat", "mirror"
295};
296
297bool SkBitmapProcShader::toDumpString(SkString* str) const {
298 str->printf("BitmapShader: [%d %d %d",
299 fRawBitmap.width(), fRawBitmap.height(),
300 fRawBitmap.bytesPerPixel());
reed@google.com7c2f27d2011-03-07 19:29:00 +0000301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 // add the pixelref
303 SkPixelRef* pr = fRawBitmap.pixelRef();
304 if (pr) {
305 const char* uri = pr->getURI();
306 if (uri) {
307 str->appendf(" \"%s\"", uri);
308 }
309 }
reed@google.com7c2f27d2011-03-07 19:29:00 +0000310
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 // add the (optional) matrix
312 {
313 SkMatrix m;
314 if (this->getLocalMatrix(&m)) {
315 SkString info;
316 m.toDumpString(&info);
317 str->appendf(" %s", info.c_str());
318 }
319 }
reed@google.com7c2f27d2011-03-07 19:29:00 +0000320
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 str->appendf(" [%s %s]]",
322 gTileModeName[fState.fTileModeX],
323 gTileModeName[fState.fTileModeY]);
324 return true;
325}
326