SkBitmapDevice.cpp revision 76f10a3bd936af7dbe2b5873d5a7eedd73cdc5da
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#include "SkSurface.h"
14
15#define CHECK_FOR_ANNOTATION(paint) \
16    do { if (paint.getAnnotation()) { return; } } while (0)
17
18SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
19    : fBitmap(bitmap) {
20    SkASSERT(SkBitmap::kARGB_4444_Config != bitmap.config());
21}
22
23SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties)
24    : SkBaseDevice(deviceProperties)
25    , fBitmap(bitmap) {
26}
27
28void SkBitmapDevice::init(SkBitmap::Config config, int width, int height, bool isOpaque) {
29    fBitmap.setConfig(config, width, height, 0, isOpaque ?
30                      kOpaque_SkAlphaType : kPremul_SkAlphaType);
31
32    if (SkBitmap::kNo_Config != config) {
33        if (!fBitmap.allocPixels()) {
34            // indicate failure by zeroing our bitmap
35            fBitmap.setConfig(config, 0, 0, 0, isOpaque ?
36                              kOpaque_SkAlphaType : kPremul_SkAlphaType);
37        } else if (!isOpaque) {
38            fBitmap.eraseColor(SK_ColorTRANSPARENT);
39        }
40    }
41}
42
43SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque) {
44    this->init(config, width, height, isOpaque);
45}
46
47SkBitmapDevice::SkBitmapDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
48                               const SkDeviceProperties& deviceProperties)
49    : SkBaseDevice(deviceProperties)
50{
51    this->init(config, width, height, isOpaque);
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            if (!dstBmp.allocPixels(SkImageInfo::MakeN32Premul(spriteRect.width(),
169                                                               spriteRect.height()))) {
170                return;
171            }
172            drawSprite = true;
173        }
174
175        // copy pixels to dstBmp and convert from config8888 to native config.
176        SkAutoLockPixels alp(bitmap);
177        uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x,
178                                               spriteRect.fTop - y);
179        SkCopyConfig8888ToBitmap(dstBmp,
180                                 srcPixels,
181                                 bitmap.rowBytes(),
182                                 config8888);
183
184        if (drawSprite) {
185            // we've clipped the sprite when we made a copy
186            x = spriteRect.fLeft;
187            y = spriteRect.fTop;
188            sprite = &dstBmp;
189        } else {
190            return;
191        }
192    }
193
194    SkPaint paint;
195    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
196    SkRasterClip clip(SkIRect::MakeWH(fBitmap.width(), fBitmap.height()));
197    SkDraw  draw;
198    draw.fRC = &clip;
199    draw.fClip = &clip.bwRgn();
200    draw.fBitmap = &fBitmap; // canvas should have already called accessBitmap
201    draw.fMatrix = &SkMatrix::I();
202    this->drawSprite(draw, *sprite, x, y, paint);
203}
204
205///////////////////////////////////////////////////////////////////////////////
206
207void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
208    draw.drawPaint(paint);
209}
210
211void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
212                                const SkPoint pts[], const SkPaint& paint) {
213    CHECK_FOR_ANNOTATION(paint);
214    draw.drawPoints(mode, count, pts, paint);
215}
216
217void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
218    CHECK_FOR_ANNOTATION(paint);
219    draw.drawRect(r, paint);
220}
221
222void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
223    CHECK_FOR_ANNOTATION(paint);
224
225    SkPath path;
226    path.addOval(oval);
227    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
228    // required to override drawOval.
229    this->drawPath(draw, path, paint, NULL, true);
230}
231
232void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) {
233    CHECK_FOR_ANNOTATION(paint);
234
235#ifdef SK_IGNORE_BLURRED_RRECT_OPT
236    SkPath  path;
237
238    path.addRRect(rrect);
239    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
240    // required to override drawRRect.
241    this->drawPath(draw, path, paint, NULL, true);
242#else
243    draw.drawRRect(rrect, paint);
244#endif
245}
246
247void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
248                              const SkPaint& paint, const SkMatrix* prePathMatrix,
249                              bool pathIsMutable) {
250    CHECK_FOR_ANNOTATION(paint);
251    draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
252}
253
254void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
255                                const SkMatrix& matrix, const SkPaint& paint) {
256    draw.drawBitmap(bitmap, matrix, paint);
257}
258
259void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
260                                    const SkRect* src, const SkRect& dst,
261                                    const SkPaint& paint,
262                                    SkCanvas::DrawBitmapRectFlags flags) {
263    SkMatrix    matrix;
264    SkRect      bitmapBounds, tmpSrc, tmpDst;
265    SkBitmap    tmpBitmap;
266
267    bitmapBounds.isetWH(bitmap.width(), bitmap.height());
268
269    // Compute matrix from the two rectangles
270    if (src) {
271        tmpSrc = *src;
272    } else {
273        tmpSrc = bitmapBounds;
274    }
275    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
276
277    const SkRect* dstPtr = &dst;
278    const SkBitmap* bitmapPtr = &bitmap;
279
280    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
281    // needed (if the src was clipped). No check needed if src==null.
282    if (src) {
283        if (!bitmapBounds.contains(*src)) {
284            if (!tmpSrc.intersect(bitmapBounds)) {
285                return; // nothing to draw
286            }
287            // recompute dst, based on the smaller tmpSrc
288            matrix.mapRect(&tmpDst, tmpSrc);
289            dstPtr = &tmpDst;
290        }
291
292        // since we may need to clamp to the borders of the src rect within
293        // the bitmap, we extract a subset.
294        SkIRect srcIR;
295        tmpSrc.roundOut(&srcIR);
296        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
297            return;
298        }
299        bitmapPtr = &tmpBitmap;
300
301        // Since we did an extract, we need to adjust the matrix accordingly
302        SkScalar dx = 0, dy = 0;
303        if (srcIR.fLeft > 0) {
304            dx = SkIntToScalar(srcIR.fLeft);
305        }
306        if (srcIR.fTop > 0) {
307            dy = SkIntToScalar(srcIR.fTop);
308        }
309        if (dx || dy) {
310            matrix.preTranslate(dx, dy);
311        }
312
313        SkRect extractedBitmapBounds;
314        extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
315        if (extractedBitmapBounds == tmpSrc) {
316            // no fractional part in src, we can just call drawBitmap
317            goto USE_DRAWBITMAP;
318        }
319    } else {
320        USE_DRAWBITMAP:
321        // We can go faster by just calling drawBitmap, which will concat the
322        // matrix with the CTM, and try to call drawSprite if it can. If not,
323        // it will make a shader and call drawRect, as we do below.
324        this->drawBitmap(draw, *bitmapPtr, matrix, paint);
325        return;
326    }
327
328    // construct a shader, so we can call drawRect with the dst
329    SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
330                                               SkShader::kClamp_TileMode,
331                                               SkShader::kClamp_TileMode);
332    if (NULL == s) {
333        return;
334    }
335    s->setLocalMatrix(matrix);
336
337    SkPaint paintWithShader(paint);
338    paintWithShader.setStyle(SkPaint::kFill_Style);
339    paintWithShader.setShader(s)->unref();
340
341    // Call ourself, in case the subclass wanted to share this setup code
342    // but handle the drawRect code themselves.
343    this->drawRect(draw, *dstPtr, paintWithShader);
344}
345
346void SkBitmapDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
347                                int x, int y, const SkPaint& paint) {
348    draw.drawSprite(bitmap, x, y, paint);
349}
350
351void SkBitmapDevice::drawText(const SkDraw& draw, const void* text, size_t len,
352                              SkScalar x, SkScalar y, const SkPaint& paint) {
353    draw.drawText((const char*)text, len, x, y, paint);
354}
355
356void SkBitmapDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
357                                 const SkScalar xpos[], SkScalar y,
358                                 int scalarsPerPos, const SkPaint& paint) {
359    draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
360}
361
362void SkBitmapDevice::drawTextOnPath(const SkDraw& draw, const void* text,
363                                    size_t len, const SkPath& path,
364                                    const SkMatrix* matrix,
365                                    const SkPaint& paint) {
366    draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
367}
368
369void SkBitmapDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
370                                  int vertexCount,
371                                  const SkPoint verts[], const SkPoint textures[],
372                                  const SkColor colors[], SkXfermode* xmode,
373                                  const uint16_t indices[], int indexCount,
374                                  const SkPaint& paint) {
375    draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
376                      indices, indexCount, paint);
377}
378
379void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
380                                int x, int y, const SkPaint& paint) {
381    const SkBitmap& src = device->accessBitmap(false);
382    draw.drawSprite(src, x, y, paint);
383}
384
385SkSurface* SkBitmapDevice::newSurface(const SkImageInfo& info) {
386    return SkSurface::NewRaster(info);
387}
388
389///////////////////////////////////////////////////////////////////////////////
390
391bool SkBitmapDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
392    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
393        // we're cool with the paint as is
394        return false;
395    }
396
397    if (SkBitmap::kARGB_8888_Config != fBitmap.config() ||
398        paint.getRasterizer() ||
399        paint.getPathEffect() ||
400        paint.isFakeBoldText() ||
401        paint.getStyle() != SkPaint::kFill_Style ||
402        !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) {
403        // turn off lcd
404        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
405        flags->fHinting = paint.getHinting();
406        return true;
407    }
408    // we're cool with the paint as is
409    return false;
410}
411