1
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 */
8 #include "SkColorPriv.h"
9 #include "SkFlattenableBuffers.h"
10 #include "SkPixelRef.h"
11 #include "SkErrorInternals.h"
12 #include "SkBitmapProcShader.h"
13
14 #if SK_SUPPORT_GPU
15 #include "effects/GrSimpleTextureEffect.h"
16 #include "effects/GrBicubicEffect.h"
17 #endif
18
CanDo(const SkBitmap & bm,TileMode tx,TileMode ty)19 bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) {
20 switch (bm.config()) {
21 case SkBitmap::kA8_Config:
22 case SkBitmap::kRGB_565_Config:
23 case SkBitmap::kIndex8_Config:
24 case SkBitmap::kARGB_8888_Config:
25 // if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx))
26 return true;
27 default:
28 break;
29 }
30 return false;
31 }
32
SkBitmapProcShader(const SkBitmap & src,TileMode tmx,TileMode tmy)33 SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src,
34 TileMode tmx, TileMode tmy) {
35 fRawBitmap = src;
36 fState.fTileModeX = (uint8_t)tmx;
37 fState.fTileModeY = (uint8_t)tmy;
38 fFlags = 0; // computed in setContext
39 }
40
SkBitmapProcShader(SkFlattenableReadBuffer & buffer)41 SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer)
42 : INHERITED(buffer) {
43 buffer.readBitmap(&fRawBitmap);
44 fRawBitmap.setImmutable();
45 fState.fTileModeX = buffer.readUInt();
46 fState.fTileModeY = buffer.readUInt();
47 fFlags = 0; // computed in setContext
48 }
49
asABitmap(SkBitmap * texture,SkMatrix * texM,TileMode xy[]) const50 SkShader::BitmapType SkBitmapProcShader::asABitmap(SkBitmap* texture,
51 SkMatrix* texM,
52 TileMode xy[]) const {
53 if (texture) {
54 *texture = fRawBitmap;
55 }
56 if (texM) {
57 texM->reset();
58 }
59 if (xy) {
60 xy[0] = (TileMode)fState.fTileModeX;
61 xy[1] = (TileMode)fState.fTileModeY;
62 }
63 return kDefault_BitmapType;
64 }
65
flatten(SkFlattenableWriteBuffer & buffer) const66 void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) const {
67 this->INHERITED::flatten(buffer);
68
69 buffer.writeBitmap(fRawBitmap);
70 buffer.writeUInt(fState.fTileModeX);
71 buffer.writeUInt(fState.fTileModeY);
72 }
73
only_scale_and_translate(const SkMatrix & matrix)74 static bool only_scale_and_translate(const SkMatrix& matrix) {
75 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
76 return (matrix.getType() & ~mask) == 0;
77 }
78
isOpaque() const79 bool SkBitmapProcShader::isOpaque() const {
80 return fRawBitmap.isOpaque();
81 }
82
valid_for_drawing(const SkBitmap & bm)83 static bool valid_for_drawing(const SkBitmap& bm) {
84 if (0 == bm.width() || 0 == bm.height()) {
85 return false; // nothing to draw
86 }
87 if (NULL == bm.pixelRef()) {
88 return false; // no pixels to read
89 }
90 if (SkBitmap::kIndex8_Config == bm.config()) {
91 // ugh, I have to lock-pixels to inspect the colortable
92 SkAutoLockPixels alp(bm);
93 if (!bm.getColorTable()) {
94 return false;
95 }
96 }
97 return true;
98 }
99
setContext(const SkBitmap & device,const SkPaint & paint,const SkMatrix & matrix)100 bool SkBitmapProcShader::setContext(const SkBitmap& device,
101 const SkPaint& paint,
102 const SkMatrix& matrix) {
103 if (!fRawBitmap.getTexture() && !valid_for_drawing(fRawBitmap)) {
104 return false;
105 }
106
107 // do this first, so we have a correct inverse matrix
108 if (!this->INHERITED::setContext(device, paint, matrix)) {
109 return false;
110 }
111
112 fState.fOrigBitmap = fRawBitmap;
113 if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
114 this->INHERITED::endContext();
115 return false;
116 }
117
118 const SkBitmap& bitmap = *fState.fBitmap;
119 bool bitmapIsOpaque = bitmap.isOpaque();
120
121 // update fFlags
122 uint32_t flags = 0;
123 if (bitmapIsOpaque && (255 == this->getPaintAlpha())) {
124 flags |= kOpaqueAlpha_Flag;
125 }
126
127 switch (bitmap.config()) {
128 case SkBitmap::kRGB_565_Config:
129 flags |= (kHasSpan16_Flag | kIntrinsicly16_Flag);
130 break;
131 case SkBitmap::kIndex8_Config:
132 case SkBitmap::kARGB_8888_Config:
133 if (bitmapIsOpaque) {
134 flags |= kHasSpan16_Flag;
135 }
136 break;
137 case SkBitmap::kA8_Config:
138 break; // never set kHasSpan16_Flag
139 default:
140 break;
141 }
142
143 if (paint.isDither() && bitmap.config() != SkBitmap::kRGB_565_Config) {
144 // gradients can auto-dither in their 16bit sampler, but we don't so
145 // we clear the flag here.
146 flags &= ~kHasSpan16_Flag;
147 }
148
149 // if we're only 1-pixel high, and we don't rotate, then we can claim this
150 if (1 == bitmap.height() &&
151 only_scale_and_translate(this->getTotalInverse())) {
152 flags |= kConstInY32_Flag;
153 if (flags & kHasSpan16_Flag) {
154 flags |= kConstInY16_Flag;
155 }
156 }
157
158 fFlags = flags;
159 return true;
160 }
161
endContext()162 void SkBitmapProcShader::endContext() {
163 fState.endContext();
164 this->INHERITED::endContext();
165 }
166
167 #define BUF_MAX 128
168
169 #define TEST_BUFFER_OVERRITEx
170
171 #ifdef TEST_BUFFER_OVERRITE
172 #define TEST_BUFFER_EXTRA 32
173 #define TEST_PATTERN 0x88888888
174 #else
175 #define TEST_BUFFER_EXTRA 0
176 #endif
177
shadeSpan(int x,int y,SkPMColor dstC[],int count)178 void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
179 const SkBitmapProcState& state = fState;
180 if (state.getShaderProc32()) {
181 state.getShaderProc32()(state, x, y, dstC, count);
182 return;
183 }
184
185 uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
186 SkBitmapProcState::MatrixProc mproc = state.getMatrixProc();
187 SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
188 int max = fState.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
189
190 SkASSERT(state.fBitmap->getPixels());
191 SkASSERT(state.fBitmap->pixelRef() == NULL ||
192 state.fBitmap->pixelRef()->isLocked());
193
194 for (;;) {
195 int n = count;
196 if (n > max) {
197 n = max;
198 }
199 SkASSERT(n > 0 && n < BUF_MAX*2);
200 #ifdef TEST_BUFFER_OVERRITE
201 for (int i = 0; i < TEST_BUFFER_EXTRA; i++) {
202 buffer[BUF_MAX + i] = TEST_PATTERN;
203 }
204 #endif
205 mproc(state, buffer, n, x, y);
206 #ifdef TEST_BUFFER_OVERRITE
207 for (int j = 0; j < TEST_BUFFER_EXTRA; j++) {
208 SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN);
209 }
210 #endif
211 sproc(state, buffer, n, dstC);
212
213 if ((count -= n) == 0) {
214 break;
215 }
216 SkASSERT(count > 0);
217 x += n;
218 dstC += n;
219 }
220 }
221
asAShadeProc(void ** ctx)222 SkShader::ShadeProc SkBitmapProcShader::asAShadeProc(void** ctx) {
223 if (fState.getShaderProc32()) {
224 *ctx = &fState;
225 return (ShadeProc)fState.getShaderProc32();
226 }
227 return NULL;
228 }
229
shadeSpan16(int x,int y,uint16_t dstC[],int count)230 void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
231 const SkBitmapProcState& state = fState;
232 if (state.getShaderProc16()) {
233 state.getShaderProc16()(state, x, y, dstC, count);
234 return;
235 }
236
237 uint32_t buffer[BUF_MAX];
238 SkBitmapProcState::MatrixProc mproc = state.getMatrixProc();
239 SkBitmapProcState::SampleProc16 sproc = state.getSampleProc16();
240 int max = fState.maxCountForBufferSize(sizeof(buffer));
241
242 SkASSERT(state.fBitmap->getPixels());
243 SkASSERT(state.fBitmap->pixelRef() == NULL ||
244 state.fBitmap->pixelRef()->isLocked());
245
246 for (;;) {
247 int n = count;
248 if (n > max) {
249 n = max;
250 }
251 mproc(state, buffer, n, x, y);
252 sproc(state, buffer, n, dstC);
253
254 if ((count -= n) == 0) {
255 break;
256 }
257 x += n;
258 dstC += n;
259 }
260 }
261
262 ///////////////////////////////////////////////////////////////////////////////
263
264 #include "SkUnPreMultiply.h"
265 #include "SkColorShader.h"
266 #include "SkEmptyShader.h"
267
268 // returns true and set color if the bitmap can be drawn as a single color
269 // (for efficiency)
canUseColorShader(const SkBitmap & bm,SkColor * color)270 static bool canUseColorShader(const SkBitmap& bm, SkColor* color) {
271 if (1 != bm.width() || 1 != bm.height()) {
272 return false;
273 }
274
275 SkAutoLockPixels alp(bm);
276 if (!bm.readyToDraw()) {
277 return false;
278 }
279
280 switch (bm.config()) {
281 case SkBitmap::kARGB_8888_Config:
282 *color = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(0, 0));
283 return true;
284 case SkBitmap::kRGB_565_Config:
285 *color = SkPixel16ToColor(*bm.getAddr16(0, 0));
286 return true;
287 case SkBitmap::kIndex8_Config:
288 *color = SkUnPreMultiply::PMColorToColor(bm.getIndex8Color(0, 0));
289 return true;
290 default: // just skip the other configs for now
291 break;
292 }
293 return false;
294 }
295
296 #include "SkTemplatesPriv.h"
297
bitmapIsTooBig(const SkBitmap & bm)298 static bool bitmapIsTooBig(const SkBitmap& bm) {
299 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
300 // communicates between its matrix-proc and its sampler-proc. Until we can
301 // widen that, we have to reject bitmaps that are larger.
302 //
303 const int maxSize = 65535;
304
305 return bm.width() > maxSize || bm.height() > maxSize;
306 }
307
CreateBitmapShader(const SkBitmap & src,TileMode tmx,TileMode tmy,void * storage,size_t storageSize)308 SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
309 TileMode tmx, TileMode tmy,
310 void* storage, size_t storageSize) {
311 SkShader* shader;
312 SkColor color;
313 if (src.isNull() || bitmapIsTooBig(src)) {
314 SK_PLACEMENT_NEW(shader, SkEmptyShader, storage, storageSize);
315 }
316 else if (canUseColorShader(src, &color)) {
317 SK_PLACEMENT_NEW_ARGS(shader, SkColorShader, storage, storageSize,
318 (color));
319 } else {
320 SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
321 storageSize, (src, tmx, tmy));
322 }
323 return shader;
324 }
325
326 ///////////////////////////////////////////////////////////////////////////////
327
328 #ifdef SK_DEVELOPER
toString(SkString * str) const329 void SkBitmapProcShader::toString(SkString* str) const {
330 static const char* gTileModeName[SkShader::kTileModeCount] = {
331 "clamp", "repeat", "mirror"
332 };
333
334 str->append("BitmapShader: (");
335
336 str->appendf("(%s, %s)",
337 gTileModeName[fState.fTileModeX],
338 gTileModeName[fState.fTileModeY]);
339
340 str->append(" ");
341 fRawBitmap.toString(str);
342
343 this->INHERITED::toString(str);
344
345 str->append(")");
346 }
347 #endif
348
349 ///////////////////////////////////////////////////////////////////////////////
350
351 #if SK_SUPPORT_GPU
352
353 #include "GrTextureAccess.h"
354 #include "effects/GrSimpleTextureEffect.h"
355 #include "SkGr.h"
356
asNewEffect(GrContext * context,const SkPaint & paint) const357 GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
358 SkMatrix matrix;
359 matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
360
361 SkMatrix inverse;
362 if (!this->getLocalMatrix().invert(&inverse)) {
363 return NULL;
364 }
365 matrix.preConcat(inverse);
366
367 SkShader::TileMode tm[] = {
368 (TileMode)fState.fTileModeX,
369 (TileMode)fState.fTileModeY,
370 };
371
372 // Must set wrap and filter on the sampler before requesting a texture.
373 SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
374 GrTextureParams::FilterMode textureFilterMode;
375 switch(paintFilterLevel) {
376 case SkPaint::kNone_FilterLevel:
377 textureFilterMode = GrTextureParams::kNone_FilterMode;
378 break;
379 case SkPaint::kLow_FilterLevel:
380 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
381 break;
382 case SkPaint::kMedium_FilterLevel:
383 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
384 break;
385 case SkPaint::kHigh_FilterLevel:
386 // Minification can look bad with the bicubic effect. This is an overly aggressive
387 // check for MIP fallbacks. It doesn't consider the fact that minification in the local
388 // matrix could be offset by the view matrix and vice versa. We also don't know whether
389 // the draw has explicit local coords (e.g. drawVertices) where the scale factor is
390 // unknown and varies.
391 if (context->getMatrix().getMinStretch() >= SK_Scalar1 &&
392 this->getLocalMatrix().getMaxStretch() <= SK_Scalar1) {
393 // fall back to no filtering here; we will install another
394 // shader that will do the HQ filtering.
395 textureFilterMode = GrTextureParams::kNone_FilterMode;
396 } else {
397 // Fall back to mip-mapping.
398 paintFilterLevel = SkPaint::kMedium_FilterLevel;
399 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
400 }
401 break;
402 default:
403 SkErrorInternals::SetError( kInvalidPaint_SkError,
404 "Sorry, I don't understand the filtering "
405 "mode you asked for. Falling back to "
406 "MIPMaps.");
407 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
408 break;
409
410 }
411 GrTextureParams params(tm, textureFilterMode);
412 GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, fRawBitmap, ¶ms);
413
414 if (NULL == texture) {
415 SkErrorInternals::SetError( kInternalError_SkError,
416 "Couldn't convert bitmap to texture.");
417 return NULL;
418 }
419
420 GrEffectRef* effect = NULL;
421 if (paintFilterLevel == SkPaint::kHigh_FilterLevel) {
422 effect = GrBicubicEffect::Create(texture, matrix, tm);
423 } else {
424 effect = GrSimpleTextureEffect::Create(texture, matrix, params);
425 }
426 GrUnlockAndUnrefCachedBitmapTexture(texture);
427 return effect;
428 }
429 #endif
430