SkBitmapProcShader.cpp revision 2ad83ea8309733730c5232bc47c76303bc7240ee
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 "SkBitmapProcShader.h"
9#include "SkColorPriv.h"
10#include "SkFlattenableBuffers.h"
11#include "SkPixelRef.h"
12
13bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) {
14    switch (bm.config()) {
15        case SkBitmap::kA8_Config:
16        case SkBitmap::kRGB_565_Config:
17        case SkBitmap::kIndex8_Config:
18        case SkBitmap::kARGB_8888_Config:
19    //        if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx))
20                return true;
21        default:
22            break;
23    }
24    return false;
25}
26
27SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src,
28                                       TileMode tmx, TileMode tmy) {
29    fRawBitmap = src;
30    fState.fTileModeX = (uint8_t)tmx;
31    fState.fTileModeY = (uint8_t)tmy;
32    fFlags = 0; // computed in setContext
33}
34
35SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer)
36        : INHERITED(buffer) {
37    buffer.readBitmap(&fRawBitmap);
38    fRawBitmap.setImmutable();
39    fState.fTileModeX = buffer.readUInt();
40    fState.fTileModeY = buffer.readUInt();
41    fFlags = 0; // computed in setContext
42}
43
44SkShader::BitmapType SkBitmapProcShader::asABitmap(SkBitmap* texture,
45                                                   SkMatrix* texM,
46                                                   TileMode xy[]) const {
47    if (texture) {
48        *texture = fRawBitmap;
49    }
50    if (texM) {
51        texM->reset();
52    }
53    if (xy) {
54        xy[0] = (TileMode)fState.fTileModeX;
55        xy[1] = (TileMode)fState.fTileModeY;
56    }
57    return kDefault_BitmapType;
58}
59
60void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) const {
61    this->INHERITED::flatten(buffer);
62
63    buffer.writeBitmap(fRawBitmap);
64    buffer.writeUInt(fState.fTileModeX);
65    buffer.writeUInt(fState.fTileModeY);
66}
67
68static bool only_scale_and_translate(const SkMatrix& matrix) {
69    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
70    return (matrix.getType() & ~mask) == 0;
71}
72
73bool SkBitmapProcShader::isOpaque() const {
74    return fRawBitmap.isOpaque();
75}
76
77bool SkBitmapProcShader::setContext(const SkBitmap& device,
78                                    const SkPaint& paint,
79                                    const SkMatrix& matrix) {
80    // do this first, so we have a correct inverse matrix
81    if (!this->INHERITED::setContext(device, paint, matrix)) {
82        return false;
83    }
84
85    fState.fOrigBitmap = fRawBitmap;
86    fState.fOrigBitmap.lockPixels();
87    if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) {
88        fState.fOrigBitmap.unlockPixels();
89        return false;
90    }
91
92    if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
93        fState.fOrigBitmap.unlockPixels();
94        return false;
95    }
96
97    const SkBitmap& bitmap = *fState.fBitmap;
98    bool bitmapIsOpaque = bitmap.isOpaque();
99
100    // update fFlags
101    uint32_t flags = 0;
102    if (bitmapIsOpaque && (255 == this->getPaintAlpha())) {
103        flags |= kOpaqueAlpha_Flag;
104    }
105
106    switch (bitmap.config()) {
107        case SkBitmap::kRGB_565_Config:
108            flags |= (kHasSpan16_Flag | kIntrinsicly16_Flag);
109            break;
110        case SkBitmap::kIndex8_Config:
111        case SkBitmap::kARGB_8888_Config:
112            if (bitmapIsOpaque) {
113                flags |= kHasSpan16_Flag;
114            }
115            break;
116        case SkBitmap::kA8_Config:
117            break;  // never set kHasSpan16_Flag
118        default:
119            break;
120    }
121
122    if (paint.isDither() && bitmap.config() != SkBitmap::kRGB_565_Config) {
123        // gradients can auto-dither in their 16bit sampler, but we don't so
124        // we clear the flag here.
125        flags &= ~kHasSpan16_Flag;
126    }
127
128    // if we're only 1-pixel heigh, and we don't rotate, then we can claim this
129    if (1 == bitmap.height() &&
130            only_scale_and_translate(this->getTotalInverse())) {
131        flags |= kConstInY32_Flag;
132        if (flags & kHasSpan16_Flag) {
133            flags |= kConstInY16_Flag;
134        }
135    }
136
137    fFlags = flags;
138    return true;
139}
140
141void SkBitmapProcShader::endContext() {
142    fState.fOrigBitmap.unlockPixels();
143    this->INHERITED::endContext();
144}
145
146#define BUF_MAX     128
147
148#define TEST_BUFFER_OVERRITEx
149
150#ifdef TEST_BUFFER_OVERRITE
151    #define TEST_BUFFER_EXTRA   32
152    #define TEST_PATTERN    0x88888888
153#else
154    #define TEST_BUFFER_EXTRA   0
155#endif
156
157void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
158    const SkBitmapProcState& state = fState;
159    if (state.getShaderProc32()) {
160        state.getShaderProc32()(state, x, y, dstC, count);
161        return;
162    }
163
164    uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
165    SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
166    SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
167    int max = fState.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
168
169    SkASSERT(state.fBitmap->getPixels());
170    SkASSERT(state.fBitmap->pixelRef() == NULL ||
171             state.fBitmap->pixelRef()->isLocked());
172
173    for (;;) {
174        int n = count;
175        if (n > max) {
176            n = max;
177        }
178        SkASSERT(n > 0 && n < BUF_MAX*2);
179#ifdef TEST_BUFFER_OVERRITE
180        for (int i = 0; i < TEST_BUFFER_EXTRA; i++) {
181            buffer[BUF_MAX + i] = TEST_PATTERN;
182        }
183#endif
184        mproc(state, buffer, n, x, y);
185#ifdef TEST_BUFFER_OVERRITE
186        for (int j = 0; j < TEST_BUFFER_EXTRA; j++) {
187            SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN);
188        }
189#endif
190        sproc(state, buffer, n, dstC);
191
192        if ((count -= n) == 0) {
193            break;
194        }
195        SkASSERT(count > 0);
196        x += n;
197        dstC += n;
198    }
199}
200
201SkShader::ShadeProc SkBitmapProcShader::asAShadeProc(void** ctx) {
202    if (fState.getShaderProc32()) {
203        *ctx = &fState;
204        return (ShadeProc)fState.getShaderProc32();
205    }
206    return NULL;
207}
208
209void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
210    const SkBitmapProcState& state = fState;
211    if (state.getShaderProc16()) {
212        state.getShaderProc16()(state, x, y, dstC, count);
213        return;
214    }
215
216    uint32_t buffer[BUF_MAX];
217    SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
218    SkBitmapProcState::SampleProc16 sproc = state.getSampleProc16();
219    int max = fState.maxCountForBufferSize(sizeof(buffer));
220
221    SkASSERT(state.fBitmap->getPixels());
222    SkASSERT(state.fBitmap->pixelRef() == NULL ||
223             state.fBitmap->pixelRef()->isLocked());
224
225    for (;;) {
226        int n = count;
227        if (n > max) {
228            n = max;
229        }
230        mproc(state, buffer, n, x, y);
231        sproc(state, buffer, n, dstC);
232
233        if ((count -= n) == 0) {
234            break;
235        }
236        x += n;
237        dstC += n;
238    }
239}
240
241///////////////////////////////////////////////////////////////////////////////
242
243#include "SkUnPreMultiply.h"
244#include "SkColorShader.h"
245#include "SkEmptyShader.h"
246
247// returns true and set color if the bitmap can be drawn as a single color
248// (for efficiency)
249static bool canUseColorShader(const SkBitmap& bm, SkColor* color) {
250    if (1 != bm.width() || 1 != bm.height()) {
251        return false;
252    }
253
254    SkAutoLockPixels alp(bm);
255    if (!bm.readyToDraw()) {
256        return false;
257    }
258
259    switch (bm.config()) {
260        case SkBitmap::kARGB_8888_Config:
261            *color = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(0, 0));
262            return true;
263        case SkBitmap::kRGB_565_Config:
264            *color = SkPixel16ToColor(*bm.getAddr16(0, 0));
265            return true;
266        case SkBitmap::kIndex8_Config:
267            *color = SkUnPreMultiply::PMColorToColor(bm.getIndex8Color(0, 0));
268            return true;
269        default: // just skip the other configs for now
270            break;
271    }
272    return false;
273}
274
275#include "SkTemplatesPriv.h"
276
277static bool bitmapIsTooBig(const SkBitmap& bm) {
278    // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
279    // communicates between its matrix-proc and its sampler-proc. Until we can
280    // widen that, we have to reject bitmaps that are larger.
281    //
282    const int maxSize = 65535;
283
284    return bm.width() > maxSize || bm.height() > maxSize;
285}
286
287SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
288                                       TileMode tmx, TileMode tmy,
289                                       void* storage, size_t storageSize) {
290    SkShader* shader;
291    SkColor color;
292    if (src.isNull() || bitmapIsTooBig(src)) {
293        SK_PLACEMENT_NEW(shader, SkEmptyShader, storage, storageSize);
294    }
295    else if (canUseColorShader(src, &color)) {
296        SK_PLACEMENT_NEW_ARGS(shader, SkColorShader, storage, storageSize,
297                              (color));
298    } else {
299        SK_PLACEMENT_NEW_ARGS(shader, SkBitmapProcShader, storage,
300                              storageSize, (src, tmx, tmy));
301    }
302    return shader;
303}
304
305///////////////////////////////////////////////////////////////////////////////
306
307static const char* gTileModeName[] = {
308    "clamp", "repeat", "mirror"
309};
310
311bool SkBitmapProcShader::toDumpString(SkString* str) const {
312    str->printf("BitmapShader: [%d %d %d",
313                fRawBitmap.width(), fRawBitmap.height(),
314                fRawBitmap.bytesPerPixel());
315
316    // add the pixelref
317    SkPixelRef* pr = fRawBitmap.pixelRef();
318    if (pr) {
319        const char* uri = pr->getURI();
320        if (uri) {
321            str->appendf(" \"%s\"", uri);
322        }
323    }
324
325    // add the (optional) matrix
326    {
327        if (this->hasLocalMatrix()) {
328            SkString info;
329            this->getLocalMatrix().toDumpString(&info);
330            str->appendf(" %s", info.c_str());
331        }
332    }
333
334    str->appendf(" [%s %s]]",
335                 gTileModeName[fState.fTileModeX],
336                 gTileModeName[fState.fTileModeY]);
337    return true;
338}
339///////////////////////////////////////////////////////////////////////////////
340
341#if SK_SUPPORT_GPU
342
343#include "GrTextureAccess.h"
344#include "effects/GrSingleTextureEffect.h"
345#include "SkGr.h"
346
347GrEffect* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
348    SkMatrix matrix;
349    matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
350
351    if (this->hasLocalMatrix()) {
352        SkMatrix inverse;
353        if (!this->getLocalMatrix().invert(&inverse)) {
354            return NULL;
355        }
356        matrix.preConcat(inverse);
357    }
358    SkShader::TileMode tm[] = {
359        (TileMode)fState.fTileModeX,
360        (TileMode)fState.fTileModeY,
361    };
362
363    // Must set wrap and filter on the sampler before requesting a texture.
364    GrTextureParams params(tm, paint.isFilterBitmap());
365    GrTexture* texture = GrLockCachedBitmapTexture(context, fRawBitmap, &params);
366
367    if (NULL == texture) {
368        SkDebugf("Couldn't convert bitmap to texture.\n");
369        return NULL;
370    }
371
372    GrEffect* effect = SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix, params));
373    GrUnlockCachedBitmapTexture(texture);
374    return effect;
375}
376#endif
377