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