SkDevice.cpp revision ff21c2e0ae23da0f4742b47d4d37969a2a18bd99
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 "SkDevice.h"
9#include "SkDraw.h"
10#include "SkImageFilter.h"
11#include "SkMetaData.h"
12#include "SkRasterClip.h"
13#include "SkRect.h"
14#include "SkShader.h"
15
16SK_DEFINE_INST_COUNT(SkDevice)
17
18///////////////////////////////////////////////////////////////////////////////
19
20#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
21    do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
22
23///////////////////////////////////////////////////////////////////////////////
24
25SkDevice::SkDevice(const SkBitmap& bitmap)
26    : fBitmap(bitmap)
27#ifdef SK_DEBUG
28    , fAttachedToCanvas(false)
29#endif
30{
31    fOrigin.setZero();
32    fMetaData = NULL;
33}
34
35SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque)
36#ifdef SK_DEBUG
37    : fAttachedToCanvas(false)
38#endif
39{
40    fOrigin.setZero();
41    fMetaData = NULL;
42
43    fBitmap.setConfig(config, width, height);
44    fBitmap.allocPixels();
45    fBitmap.setIsOpaque(isOpaque);
46    if (!isOpaque) {
47        fBitmap.eraseColor(SK_ColorTRANSPARENT);
48    }
49}
50
51SkDevice::~SkDevice() {
52    delete fMetaData;
53}
54
55void SkDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
56    SkASSERT(bm.width() == fBitmap.width());
57    SkASSERT(bm.height() == fBitmap.height());
58    fBitmap = bm;   // intent is to use bm's pixelRef (and rowbytes/config)
59    fBitmap.lockPixels();
60}
61
62SkDevice* SkDevice::createCompatibleDevice(SkBitmap::Config config,
63                                           int width, int height,
64                                           bool isOpaque) {
65    return this->onCreateCompatibleDevice(config, width, height,
66                                          isOpaque, kGeneral_Usage);
67}
68
69SkDevice* SkDevice::createCompatibleDeviceForSaveLayer(SkBitmap::Config config,
70                                                       int width, int height,
71                                                       bool isOpaque) {
72    return this->onCreateCompatibleDevice(config, width, height,
73                                          isOpaque, kSaveLayer_Usage);
74}
75
76SkDevice* SkDevice::onCreateCompatibleDevice(SkBitmap::Config config,
77                                             int width, int height,
78                                             bool isOpaque,
79                                             Usage usage) {
80    return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque));
81}
82
83SkMetaData& SkDevice::getMetaData() {
84    // metadata users are rare, so we lazily allocate it. If that changes we
85    // can decide to just make it a field in the device (rather than a ptr)
86    if (NULL == fMetaData) {
87        fMetaData = new SkMetaData;
88    }
89    return *fMetaData;
90}
91
92void SkDevice::lockPixels() {
93    if (fBitmap.lockPixelsAreWritable()) {
94        fBitmap.lockPixels();
95    }
96}
97
98void SkDevice::unlockPixels() {
99    if (fBitmap.lockPixelsAreWritable()) {
100        fBitmap.unlockPixels();
101    }
102}
103
104const SkBitmap& SkDevice::accessBitmap(bool changePixels) {
105    const SkBitmap& bitmap = this->onAccessBitmap(&fBitmap);
106    if (changePixels) {
107        bitmap.notifyPixelsChanged();
108    }
109    return bitmap;
110}
111
112void SkDevice::getGlobalBounds(SkIRect* bounds) const {
113    if (bounds) {
114        bounds->setXYWH(fOrigin.x(), fOrigin.y(),
115                        fBitmap.width(), fBitmap.height());
116    }
117}
118
119void SkDevice::clear(SkColor color) {
120    fBitmap.eraseColor(color);
121}
122
123const SkBitmap& SkDevice::onAccessBitmap(SkBitmap* bitmap) {return *bitmap;}
124
125void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region,
126                             const SkClipStack& clipStack) {
127}
128
129bool SkDevice::canHandleImageFilter(SkImageFilter*) {
130    return false;
131}
132
133bool SkDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
134                           const SkMatrix& ctm, SkBitmap* result,
135                           SkIPoint* offset) {
136    return false;
137}
138
139bool SkDevice::allowImageFilter(SkImageFilter*) {
140    return true;
141}
142
143///////////////////////////////////////////////////////////////////////////////
144
145bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y,
146                          SkCanvas::Config8888 config8888) {
147    if (SkBitmap::kARGB_8888_Config != bitmap->config() ||
148        NULL != bitmap->getTexture()) {
149        return false;
150    }
151
152    const SkBitmap& src = this->accessBitmap(false);
153
154    SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap->width(),
155                                              bitmap->height());
156    SkIRect devbounds = SkIRect::MakeWH(src.width(), src.height());
157    if (!srcRect.intersect(devbounds)) {
158        return false;
159    }
160
161    SkBitmap tmp;
162    SkBitmap* bmp;
163    if (bitmap->isNull()) {
164        tmp.setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(),
165                                                   bitmap->height());
166        if (!tmp.allocPixels()) {
167            return false;
168        }
169        bmp = &tmp;
170    } else {
171        bmp = bitmap;
172    }
173
174    SkIRect subrect = srcRect;
175    subrect.offset(-x, -y);
176    SkBitmap bmpSubset;
177    bmp->extractSubset(&bmpSubset, subrect);
178
179    bool result = this->onReadPixels(bmpSubset,
180                                     srcRect.fLeft,
181                                     srcRect.fTop,
182                                     config8888);
183    if (result && bmp == &tmp) {
184        tmp.swap(*bitmap);
185    }
186    return result;
187}
188
189#ifdef SK_CPU_LENDIAN
190    #if   24 == SK_A32_SHIFT && 16 == SK_R32_SHIFT && \
191           8 == SK_G32_SHIFT &&  0 == SK_B32_SHIFT
192        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
193            SkCanvas::kBGRA_Premul_Config8888;
194    #elif 24 == SK_A32_SHIFT &&  0 == SK_R32_SHIFT && \
195           8 == SK_G32_SHIFT && 16 == SK_B32_SHIFT
196        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
197            SkCanvas::kRGBA_Premul_Config8888;
198    #else
199        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
200            (SkCanvas::Config8888) -1;
201    #endif
202#else
203    #if    0 == SK_A32_SHIFT &&   8 == SK_R32_SHIFT && \
204          16 == SK_G32_SHIFT &&  24 == SK_B32_SHIFT
205        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
206            SkCanvas::kBGRA_Premul_Config8888;
207    #elif  0 == SK_A32_SHIFT &&  24 == SK_R32_SHIFT && \
208          16 == SK_G32_SHIFT &&   8 == SK_B32_SHIFT
209        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
210            SkCanvas::kRGBA_Premul_Config8888;
211    #else
212        const SkCanvas::Config8888 SkDevice::kPMColorAlias =
213            (SkCanvas::Config8888) -1;
214    #endif
215#endif
216
217#include <SkConfig8888.h>
218
219bool SkDevice::onReadPixels(const SkBitmap& bitmap,
220                            int x, int y,
221                            SkCanvas::Config8888 config8888) {
222    SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
223    SkASSERT(!bitmap.isNull());
224    SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));
225
226    SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap.width(),
227                                              bitmap.height());
228    const SkBitmap& src = this->accessBitmap(false);
229
230    SkBitmap subset;
231    if (!src.extractSubset(&subset, srcRect)) {
232        return false;
233    }
234    if (SkBitmap::kARGB_8888_Config != subset.config()) {
235        // It'd be preferable to do this directly to bitmap.
236        subset.copyTo(&subset, SkBitmap::kARGB_8888_Config);
237    }
238    SkAutoLockPixels alp(bitmap);
239    uint32_t* bmpPixels = reinterpret_cast<uint32_t*>(bitmap.getPixels());
240    SkCopyBitmapToConfig8888(bmpPixels, bitmap.rowBytes(), config8888, subset);
241    return true;
242}
243
244void SkDevice::writePixels(const SkBitmap& bitmap,
245                           int x, int y,
246                           SkCanvas::Config8888 config8888) {
247    if (bitmap.isNull() || bitmap.getTexture()) {
248        return;
249    }
250    const SkBitmap* sprite = &bitmap;
251    // check whether we have to handle a config8888 that doesn't match SkPMColor
252    if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
253        SkCanvas::kNative_Premul_Config8888 != config8888 &&
254        kPMColorAlias != config8888) {
255
256        // We're going to have to convert from a config8888 to the native config
257        // First we clip to the device bounds.
258        SkBitmap dstBmp = this->accessBitmap(true);
259        SkIRect spriteRect = SkIRect::MakeXYWH(x, y,
260                                               bitmap.width(), bitmap.height());
261        SkIRect devRect = SkIRect::MakeWH(dstBmp.width(), dstBmp.height());
262        if (!spriteRect.intersect(devRect)) {
263            return;
264        }
265
266        // write directly to the device if it has pixels and is SkPMColor
267        bool drawSprite;
268        if (SkBitmap::kARGB_8888_Config == dstBmp.config() && !dstBmp.isNull()) {
269            // we can write directly to the dst when doing the conversion
270            dstBmp.extractSubset(&dstBmp, spriteRect);
271            drawSprite = false;
272        } else {
273            // we convert to a temporary bitmap and draw that as a sprite
274            dstBmp.setConfig(SkBitmap::kARGB_8888_Config,
275                             spriteRect.width(),
276                             spriteRect.height());
277            if (!dstBmp.allocPixels()) {
278                return;
279            }
280            drawSprite = true;
281        }
282
283        // copy pixels to dstBmp and convert from config8888 to native config.
284        SkAutoLockPixels alp(bitmap);
285        uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x,
286                                               spriteRect.fTop - y);
287        SkCopyConfig8888ToBitmap(dstBmp,
288                                 srcPixels,
289                                 bitmap.rowBytes(),
290                                 config8888);
291
292        if (drawSprite) {
293            // we've clipped the sprite when we made a copy
294            x = spriteRect.fLeft;
295            y = spriteRect.fTop;
296            sprite = &dstBmp;
297        } else {
298            return;
299        }
300    }
301
302    SkPaint paint;
303    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
304    SkRasterClip clip(SkIRect::MakeWH(fBitmap.width(), fBitmap.height()));
305    SkDraw  draw;
306    draw.fRC = &clip;
307    draw.fClip = &clip.bwRgn();
308    draw.fBitmap = &fBitmap; // canvas should have already called accessBitmap
309    draw.fMatrix = &SkMatrix::I();
310    this->drawSprite(draw, *sprite, x, y, paint);
311}
312
313///////////////////////////////////////////////////////////////////////////////
314
315void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
316    draw.drawPaint(paint);
317}
318
319void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
320                              const SkPoint pts[], const SkPaint& paint) {
321    draw.drawPoints(mode, count, pts, paint);
322}
323
324void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
325                            const SkPaint& paint) {
326    CHECK_FOR_NODRAW_ANNOTATION(paint);
327    draw.drawRect(r, paint);
328}
329
330void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
331                        const SkPaint& paint, const SkMatrix* prePathMatrix,
332                        bool pathIsMutable) {
333    CHECK_FOR_NODRAW_ANNOTATION(paint);
334    draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
335}
336
337void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
338                          const SkIRect* srcRect,
339                          const SkMatrix& matrix, const SkPaint& paint) {
340    SkBitmap        tmp;    // storage if we need a subset of bitmap
341    const SkBitmap* bitmapPtr = &bitmap;
342
343    if (srcRect) {
344        if (!bitmap.extractSubset(&tmp, *srcRect)) {
345            return;     // extraction failed
346        }
347        bitmapPtr = &tmp;
348    }
349    draw.drawBitmap(*bitmapPtr, matrix, paint);
350}
351
352void SkDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
353                              const SkRect* src, const SkRect& dst,
354                              const SkPaint& paint) {
355#ifdef SK_SUPPORT_INT_SRCRECT_DRAWBITMAPRECT
356    SkMatrix matrix;
357    // Compute matrix from the two rectangles
358    {
359        SkRect tmpSrc;
360        if (src) {
361            tmpSrc = *src;
362            // if the extract process clipped off the top or left of the
363            // original, we adjust for that here to get the position right.
364            if (tmpSrc.fLeft > 0) {
365                tmpSrc.fRight -= tmpSrc.fLeft;
366                tmpSrc.fLeft = 0;
367            }
368            if (tmpSrc.fTop > 0) {
369                tmpSrc.fBottom -= tmpSrc.fTop;
370                tmpSrc.fTop = 0;
371            }
372        } else {
373            tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
374                       SkIntToScalar(bitmap.height()));
375        }
376        matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
377    }
378
379    // ensure that src is "valid" before we pass it to our internal routines
380    // and to SkDevice. i.e. sure it is contained inside the original bitmap.
381    SkIRect isrcStorage;
382    SkIRect* isrcPtr = NULL;
383    if (src) {
384        src->roundOut(&isrcStorage);
385        if (!isrcStorage.intersect(0, 0, bitmap.width(), bitmap.height())) {
386            return;
387        }
388        isrcPtr = &isrcStorage;
389    }
390
391    this->drawBitmap(draw, bitmap, isrcPtr, matrix, paint);
392#else
393    SkMatrix    matrix;
394    SkRect      bitmapBounds, tmpSrc, tmpDst;
395    SkBitmap    tmpBitmap;
396
397    bitmapBounds.set(0, 0,
398                     SkIntToScalar(bitmap.width()),
399                     SkIntToScalar(bitmap.height()));
400
401    // Compute matrix from the two rectangles
402    if (src) {
403        tmpSrc = *src;
404    } else {
405        tmpSrc = bitmapBounds;
406    }
407    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
408
409    const SkRect* dstPtr = &dst;
410    const SkBitmap* bitmapPtr = &bitmap;
411
412    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
413    // needed (if the src was clipped). No check needed if src==null.
414    if (src) {
415        if (!bitmapBounds.contains(*src)) {
416            if (!tmpSrc.intersect(bitmapBounds)) {
417                return; // nothing to draw
418            }
419            // recompute dst, based on the smaller tmpSrc
420            matrix.mapRect(&tmpDst, tmpSrc);
421            dstPtr = &tmpDst;
422        }
423
424        // since we may need to clamp to the borders of the src rect within
425        // the bitmap, we extract a subset.
426        SkIRect srcIR;
427        tmpSrc.roundOut(&srcIR);
428        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
429            return;
430        }
431        bitmapPtr = &tmpBitmap;
432
433        // Since we did an extract, we need to adjust the matrix accordingly
434        SkScalar dx = 0, dy = 0;
435        if (srcIR.fLeft > 0) {
436            dx = SkIntToScalar(srcIR.fLeft);
437        }
438        if (srcIR.fTop > 0) {
439            dy = SkIntToScalar(srcIR.fTop);
440        }
441        if (dx || dy) {
442            matrix.preTranslate(dx, dy);
443        }
444
445#ifndef SK_IGNORE_DRAWBITMAPRECT_AS_DRAWBITMAP
446        SkRect extractedBitmapBounds;
447        extractedBitmapBounds.set(0, 0,
448                                  SkIntToScalar(bitmapPtr->width()),
449                                  SkIntToScalar(bitmapPtr->height()));
450        if (extractedBitmapBounds == tmpSrc) {
451            // no fractional part in src, we can just call drawBitmap
452            goto USE_DRAWBITMAP;
453        }
454#endif
455    }
456#ifndef SK_IGNORE_DRAWBITMAPRECT_AS_DRAWBITMAP
457    else {
458    USE_DRAWBITMAP:
459        // We can go faster by just calling drawBitmap, which will concat the
460        // matrix with the CTM, and try to call drawSprite if it can. If not,
461        // it will make a shader and call drawRect, as we do below.
462        this->drawBitmap(draw, *bitmapPtr, NULL, matrix, paint);
463        return;
464    }
465#endif
466
467    // construct a shader, so we can call drawRect with the dst
468    SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
469                                               SkShader::kClamp_TileMode,
470                                               SkShader::kClamp_TileMode);
471    if (NULL == s) {
472        return;
473    }
474    s->setLocalMatrix(matrix);
475
476    SkPaint paintWithShader(paint);
477    paintWithShader.setStyle(SkPaint::kFill_Style);
478    paintWithShader.setShader(s)->unref();
479
480    // Call ourself, in case the subclass wanted to share this setup code
481    // but handle the drawRect code themselves.
482    this->drawRect(draw, *dstPtr, paintWithShader);
483#endif
484}
485
486void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
487                              int x, int y, const SkPaint& paint) {
488    draw.drawSprite(bitmap, x, y, paint);
489}
490
491void SkDevice::drawText(const SkDraw& draw, const void* text, size_t len,
492                            SkScalar x, SkScalar y, const SkPaint& paint) {
493    draw.drawText((const char*)text, len, x, y, paint);
494}
495
496void SkDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
497                               const SkScalar xpos[], SkScalar y,
498                               int scalarsPerPos, const SkPaint& paint) {
499    draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
500}
501
502void SkDevice::drawTextOnPath(const SkDraw& draw, const void* text,
503                                  size_t len, const SkPath& path,
504                                  const SkMatrix* matrix,
505                                  const SkPaint& paint) {
506    draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
507}
508
509#ifdef SK_BUILD_FOR_ANDROID
510void SkDevice::drawPosTextOnPath(const SkDraw& draw, const void* text, size_t len,
511                                     const SkPoint pos[], const SkPaint& paint,
512                                     const SkPath& path, const SkMatrix* matrix) {
513    draw.drawPosTextOnPath((const char*)text, len, pos, paint, path, matrix);
514}
515#endif
516
517void SkDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
518                                int vertexCount,
519                                const SkPoint verts[], const SkPoint textures[],
520                                const SkColor colors[], SkXfermode* xmode,
521                                const uint16_t indices[], int indexCount,
522                                const SkPaint& paint) {
523    draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
524                      indices, indexCount, paint);
525}
526
527void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device,
528                              int x, int y, const SkPaint& paint) {
529    const SkBitmap& src = device->accessBitmap(false);
530    draw.drawSprite(src, x, y, paint);
531}
532
533///////////////////////////////////////////////////////////////////////////////
534
535bool SkDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
536    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
537        // we're cool with the paint as is
538        return false;
539    }
540
541    if (SkBitmap::kARGB_8888_Config != fBitmap.config() ||
542        paint.getRasterizer() ||
543        paint.getPathEffect() ||
544        paint.isFakeBoldText() ||
545        paint.getStyle() != SkPaint::kFill_Style ||
546        !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) {
547        // turn off lcd
548        flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
549        flags->fHinting = paint.getHinting();
550        return true;
551    }
552    // we're cool with the paint as is
553    return false;
554}
555
556