SkBitmapDevice.cpp revision 0daa1adb03b4b1fc11d854cb7754416ac05a31e8
1/*
2 * Copyright 2013 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 */
7
8#include "SkBitmapDevice.h"
9#include "SkConfig8888.h"
10#include "SkDraw.h"
11#include "SkRasterClip.h"
12#include "SkShader.h"
13
14#define CHECK_FOR_ANNOTATION(paint) \
15    do { if (paint.getAnnotation()) { return; } } while (0)
16
17SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
18    : fBitmap(bitmap) {
19    SkASSERT(SkBitmap::kARGB_4444_Config != bitmap.config());
20}
21
22SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties)
23    : SkBaseDevice(deviceProperties)
24    , fBitmap(bitmap) {
25}
26
27SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque) {
28    fBitmap.setConfig(config, width, height, 0, isOpaque ?
29                      kOpaque_SkAlphaType : kPremul_SkAlphaType);
30    if (!fBitmap.allocPixels()) {
31        fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
32                          kOpaque_SkAlphaType : kPremul_SkAlphaType);
33    }
34    if (!isOpaque) {
35        fBitmap.eraseColor(SK_ColorTRANSPARENT);
36    }
37}
38
39SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
40                               const SkDeviceProperties& deviceProperties)
41    : SkBaseDevice(deviceProperties) {
42
43    fBitmap.setConfig(config, width, height, 0, isOpaque ?
44                      kOpaque_SkAlphaType : kPremul_SkAlphaType);
45    if (!fBitmap.allocPixels()) {
46        fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
47                          kOpaque_SkAlphaType : kPremul_SkAlphaType);
48    }
49    if (!isOpaque) {
50        fBitmap.eraseColor(SK_ColorTRANSPARENT);
51    }
52}
53
54SkBitmapDevice::~SkBitmapDevice() {
55}
56
57void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
58    SkASSERT(bm.width() == fBitmap.width());
59    SkASSERT(bm.height() == fBitmap.height());
60    fBitmap = bm;   // intent is to use bm's pixelRef (and rowbytes/config)
61    fBitmap.lockPixels();
62}
63
64SkBaseDevice* SkBitmapDevice::onCreateCompatibleDevice(SkBitmap::Config config,
65                                                       int width, int height,
66                                                       bool isOpaque,
67                                                       Usage usage) {
68    SkBitmapDevice* device = SkNEW_ARGS(SkBitmapDevice,(config, width, height,
69                                        isOpaque, this->getDeviceProperties()));
70    // Check if allocation failed and delete device if it did fail
71    if ((device->width() != width) || (device->height() != height)) {
72        SkDELETE(device);
73        device = NULL;
74    }
75    return device;
76}
77
78void SkBitmapDevice::lockPixels() {
79    if (fBitmap.lockPixelsAreWritable()) {
80        fBitmap.lockPixels();
81    }
82}
83
84void SkBitmapDevice::unlockPixels() {
85    if (fBitmap.lockPixelsAreWritable()) {
86        fBitmap.unlockPixels();
87    }
88}
89
90void SkBitmapDevice::clear(SkColor color) {
91    fBitmap.eraseColor(color);
92}
93
94const SkBitmap& SkBitmapDevice::onAccessBitmap() {
95    return fBitmap;
96}
97
98bool SkBitmapDevice::canHandleImageFilter(SkImageFilter*) {
99    return false;
100}
101
102bool SkBitmapDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
103                                 const SkMatrix& ctm, SkBitmap* result,
104                                 SkIPoint* offset) {
105    return false;
106}
107
108bool SkBitmapDevice::allowImageFilter(SkImageFilter*) {
109    return true;
110}
111
112bool SkBitmapDevice::onReadPixels(const SkBitmap& bitmap,
113                                  int x, int y,
114                                  SkCanvas::Config8888 config8888) {
115    SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
116    SkASSERT(!bitmap.isNull());
117    SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y,
118                                                                          bitmap.width(),
119                                                                          bitmap.height())));
120
121    SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
122    const SkBitmap& src = this->accessBitmap(false);
123
124    SkBitmap subset;
125    if (!src.extractSubset(&subset, srcRect)) {
126        return false;
127    }
128    if (SkBitmap::kARGB_8888_Config != subset.config()) {
129        // It'd be preferable to do this directly to bitmap.
130        subset.copyTo(&subset, SkBitmap::kARGB_8888_Config);
131    }
132    SkAutoLockPixels alp(bitmap);
133    uint32_t* bmpPixels = reinterpret_cast<uint32_t*>(bitmap.getPixels());
134    SkCopyBitmapToConfig8888(bmpPixels, bitmap.rowBytes(), config8888, subset);
135    return true;
136}
137
138void SkBitmapDevice::writePixels(const SkBitmap& bitmap,
139                                 int x, int y,
140                                 SkCanvas::Config8888 config8888) {
141    if (bitmap.isNull() || bitmap.getTexture()) {
142        return;
143    }
144    const SkBitmap* sprite = &bitmap;
145    // check whether we have to handle a config8888 that doesn't match SkPMColor
146    if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
147        SkCanvas::kNative_Premul_Config8888 != config8888 &&
148        kPMColorAlias != config8888) {
149
150        // We're going to have to convert from a config8888 to the native config
151        // First we clip to the device bounds.
152        SkBitmap dstBmp = this->accessBitmap(true);
153        SkIRect spriteRect = SkIRect::MakeXYWH(x, y,
154                                               bitmap.width(), bitmap.height());
155        SkIRect devRect = SkIRect::MakeWH(dstBmp.width(), dstBmp.height());
156        if (!spriteRect.intersect(devRect)) {
157            return;
158        }
159
160        // write directly to the device if it has pixels and is SkPMColor
161        bool drawSprite;
162        if (SkBitmap::kARGB_8888_Config == dstBmp.config() && !dstBmp.isNull()) {
163            // we can write directly to the dst when doing the conversion
164            dstBmp.extractSubset(&dstBmp, spriteRect);
165            drawSprite = false;
166        } else {
167            // we convert to a temporary bitmap and draw that as a sprite
168            dstBmp.setConfig(SkBitmap::kARGB_8888_Config,
169                             spriteRect.width(),
170                             spriteRect.height());
171            if (!dstBmp.allocPixels()) {
172                return;
173            }
174            drawSprite = true;
175        }
176
177        // copy pixels to dstBmp and convert from config8888 to native config.
178        SkAutoLockPixels alp(bitmap);
179        uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x,
180                                               spriteRect.fTop - y);
181        SkCopyConfig8888ToBitmap(dstBmp,
182                                 srcPixels,
183                                 bitmap.rowBytes(),
184                                 config8888);
185
186        if (drawSprite) {
187            // we've clipped the sprite when we made a copy
188            x = spriteRect.fLeft;
189            y = spriteRect.fTop;
190            sprite = &dstBmp;
191        } else {
192            return;
193        }
194    }
195
196    SkPaint paint;
197    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
198    SkRasterClip clip(SkIRect::MakeWH(fBitmap.width(), fBitmap.height()));
199    SkDraw  draw;
200    draw.fRC = &clip;
201    draw.fClip = &clip.bwRgn();
202    draw.fBitmap = &fBitmap; // canvas should have already called accessBitmap
203    draw.fMatrix = &SkMatrix::I();
204    this->drawSprite(draw, *sprite, x, y, paint);
205}
206
207///////////////////////////////////////////////////////////////////////////////
208
209void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
210    draw.drawPaint(paint);
211}
212
213void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
214                                const SkPoint pts[], const SkPaint& paint) {
215    CHECK_FOR_ANNOTATION(paint);
216    draw.drawPoints(mode, count, pts, paint);
217}
218
219void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
220    CHECK_FOR_ANNOTATION(paint);
221    draw.drawRect(r, paint);
222}
223
224void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
225    CHECK_FOR_ANNOTATION(paint);
226
227    SkPath path;
228    path.addOval(oval);
229    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
230    // required to override drawOval.
231    this->drawPath(draw, path, paint, NULL, true);
232}
233
234void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) {
235    CHECK_FOR_ANNOTATION(paint);
236
237#ifdef SK_IGNORE_BLURRED_RRECT_OPT
238    SkPath  path;
239
240    path.addRRect(rrect);
241    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
242    // required to override drawRRect.
243    this->drawPath(draw, path, paint, NULL, true);
244#else
245    draw.drawRRect(rrect, paint);
246#endif
247}
248
249void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
250                              const SkPaint& paint, const SkMatrix* prePathMatrix,
251                              bool pathIsMutable) {
252    CHECK_FOR_ANNOTATION(paint);
253    draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
254}
255
256void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
257                                const SkMatrix& matrix, const SkPaint& paint) {
258    draw.drawBitmap(bitmap, matrix, paint);
259}
260
261void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
262                                    const SkRect* src, const SkRect& dst,
263                                    const SkPaint& paint,
264                                    SkCanvas::DrawBitmapRectFlags flags) {
265    SkMatrix    matrix;
266    SkRect      bitmapBounds, tmpSrc, tmpDst;
267    SkBitmap    tmpBitmap;
268
269    bitmapBounds.isetWH(bitmap.width(), bitmap.height());
270
271    // Compute matrix from the two rectangles
272    if (src) {
273        tmpSrc = *src;
274    } else {
275        tmpSrc = bitmapBounds;
276    }
277    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
278
279    const SkRect* dstPtr = &dst;
280    const SkBitmap* bitmapPtr = &bitmap;
281
282    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
283    // needed (if the src was clipped). No check needed if src==null.
284    if (src) {
285        if (!bitmapBounds.contains(*src)) {
286            if (!tmpSrc.intersect(bitmapBounds)) {
287                return; // nothing to draw
288            }
289            // recompute dst, based on the smaller tmpSrc
290            matrix.mapRect(&tmpDst, tmpSrc);
291            dstPtr = &tmpDst;
292        }
293
294        // since we may need to clamp to the borders of the src rect within
295        // the bitmap, we extract a subset.
296        SkIRect srcIR;
297        tmpSrc.roundOut(&srcIR);
298        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
299            return;
300        }
301        bitmapPtr = &tmpBitmap;
302
303        // Since we did an extract, we need to adjust the matrix accordingly
304        SkScalar dx = 0, dy = 0;
305        if (srcIR.fLeft > 0) {
306            dx = SkIntToScalar(srcIR.fLeft);
307        }
308        if (srcIR.fTop > 0) {
309            dy = SkIntToScalar(srcIR.fTop);
310        }
311        if (dx || dy) {
312            matrix.preTranslate(dx, dy);
313        }
314
315        SkRect extractedBitmapBounds;
316        extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
317        if (extractedBitmapBounds == tmpSrc) {
318            // no fractional part in src, we can just call drawBitmap
319            goto USE_DRAWBITMAP;
320        }
321    } else {
322        USE_DRAWBITMAP:
323        // We can go faster by just calling drawBitmap, which will concat the
324        // matrix with the CTM, and try to call drawSprite if it can. If not,
325        // it will make a shader and call drawRect, as we do below.
326        this->drawBitmap(draw, *bitmapPtr, matrix, paint);
327        return;
328    }
329
330    // construct a shader, so we can call drawRect with the dst
331    SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
332                                               SkShader::kClamp_TileMode,
333                                               SkShader::kClamp_TileMode);
334    if (NULL == s) {
335        return;
336    }
337    s->setLocalMatrix(matrix);
338
339    SkPaint paintWithShader(paint);
340    paintWithShader.setStyle(SkPaint::kFill_Style);
341    paintWithShader.setShader(s)->unref();
342
343    // Call ourself, in case the subclass wanted to share this setup code
344    // but handle the drawRect code themselves.
345    this->drawRect(draw, *dstPtr, paintWithShader);
346}
347
348void SkBitmapDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
349                                int x, int y, const SkPaint& paint) {
350    draw.drawSprite(bitmap, x, y, paint);
351}
352
353void SkBitmapDevice::drawText(const SkDraw& draw, const void* text, size_t len,
354                              SkScalar x, SkScalar y, const SkPaint& paint) {
355    draw.drawText((const char*)text, len, x, y, paint);
356}
357
358void SkBitmapDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
359                                 const SkScalar xpos[], SkScalar y,
360                                 int scalarsPerPos, const SkPaint& paint) {
361    draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
362}
363
364void SkBitmapDevice::drawTextOnPath(const SkDraw& draw, const void* text,
365                                    size_t len, const SkPath& path,
366                                    const SkMatrix* matrix,
367                                    const SkPaint& paint) {
368    draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
369}
370
371void SkBitmapDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
372                                  int vertexCount,
373                                  const SkPoint verts[], const SkPoint textures[],
374                                  const SkColor colors[], SkXfermode* xmode,
375                                  const uint16_t indices[], int indexCount,
376                                  const SkPaint& paint) {
377    draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
378                      indices, indexCount, paint);
379}
380
381void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
382                                int x, int y, const SkPaint& paint) {
383    const SkBitmap& src = device->accessBitmap(false);
384    draw.drawSprite(src, x, y, paint);
385}
386
387///////////////////////////////////////////////////////////////////////////////
388
389bool SkBitmapDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
390    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
391        // we're cool with the paint as is
392        return false;
393    }
394
395    if (SkBitmap::kARGB_8888_Config != fBitmap.config() ||
396        paint.getRasterizer() ||
397        paint.getPathEffect() ||
398        paint.isFakeBoldText() ||
399        paint.getStyle() != SkPaint::kFill_Style ||
400        !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) {
401        // turn off lcd
402        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
403        flags->fHinting = paint.getHinting();
404        return true;
405    }
406    // we're cool with the paint as is
407    return false;
408}
409