SkBitmapDevice.cpp revision 07764cefbb18041a77897df3453903b0a2016583
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 "SkImageFilter.h"
12#include "SkImageFilterCache.h"
13#include "SkMallocPixelRef.h"
14#include "SkMatrix.h"
15#include "SkPaint.h"
16#include "SkPath.h"
17#include "SkPixelRef.h"
18#include "SkPixmap.h"
19#include "SkRasterClip.h"
20#include "SkShader.h"
21#include "SkSpecialImage.h"
22#include "SkSurface.h"
23
24class SkColorTable;
25
26static bool valid_for_bitmap_device(const SkImageInfo& info,
27                                    SkAlphaType* newAlphaType) {
28    if (info.width() < 0 || info.height() < 0) {
29        return false;
30    }
31
32    // TODO: can we stop supporting kUnknown in SkBitmkapDevice?
33    if (kUnknown_SkColorType == info.colorType()) {
34        if (newAlphaType) {
35            *newAlphaType = kUnknown_SkAlphaType;
36        }
37        return true;
38    }
39
40    switch (info.alphaType()) {
41        case kPremul_SkAlphaType:
42        case kOpaque_SkAlphaType:
43            break;
44        default:
45            return false;
46    }
47
48    SkAlphaType canonicalAlphaType = info.alphaType();
49
50    switch (info.colorType()) {
51        case kAlpha_8_SkColorType:
52            break;
53        case kRGB_565_SkColorType:
54            canonicalAlphaType = kOpaque_SkAlphaType;
55            break;
56        case kN32_SkColorType:
57            break;
58        case kRGBA_F16_SkColorType:
59            break;
60        default:
61            return false;
62    }
63
64    if (newAlphaType) {
65        *newAlphaType = canonicalAlphaType;
66    }
67    return true;
68}
69
70SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
71    : INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))
72    , fBitmap(bitmap)
73{
74    SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
75    fBitmap.lockPixels();
76}
77
78SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) {
79    return Create(info, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
80}
81
82SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps)
83    : INHERITED(bitmap.info(), surfaceProps)
84    , fBitmap(bitmap)
85{
86    SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
87    fBitmap.lockPixels();
88}
89
90SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
91                                       const SkSurfaceProps& surfaceProps) {
92    SkAlphaType newAT = origInfo.alphaType();
93    if (!valid_for_bitmap_device(origInfo, &newAT)) {
94        return nullptr;
95    }
96
97    const SkImageInfo info = origInfo.makeAlphaType(newAT);
98    SkBitmap bitmap;
99
100    if (kUnknown_SkColorType == info.colorType()) {
101        if (!bitmap.setInfo(info)) {
102            return nullptr;
103        }
104    } else if (info.isOpaque()) {
105        // If this bitmap is opaque, we don't have any sensible default color,
106        // so we just return uninitialized pixels.
107        if (!bitmap.tryAllocPixels(info)) {
108            return nullptr;
109        }
110    } else {
111        // This bitmap has transparency, so we'll zero the pixels (to transparent).
112        // We use a ZeroedPRFactory as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT).
113        SkMallocPixelRef::ZeroedPRFactory factory;
114        if (!bitmap.tryAllocPixels(info, &factory, nullptr/*color table*/)) {
115            return nullptr;
116        }
117    }
118
119    return new SkBitmapDevice(bitmap, surfaceProps);
120}
121
122void SkBitmapDevice::setNewSize(const SkISize& size) {
123    SkASSERT(!fBitmap.pixelRef());
124    fBitmap.setInfo(fBitmap.info().makeWH(size.fWidth, size.fHeight));
125    this->privateResize(fBitmap.info().width(), fBitmap.info().height());
126}
127
128void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
129    SkASSERT(bm.width() == fBitmap.width());
130    SkASSERT(bm.height() == fBitmap.height());
131    fBitmap = bm;   // intent is to use bm's pixelRef (and rowbytes/config)
132    fBitmap.lockPixels();
133    this->privateResize(fBitmap.info().width(), fBitmap.info().height());
134}
135
136SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
137    const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
138    return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps);
139}
140
141const SkBitmap& SkBitmapDevice::onAccessBitmap() {
142    return fBitmap;
143}
144
145bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) {
146    if (this->onPeekPixels(pmap)) {
147        fBitmap.notifyPixelsChanged();
148        return true;
149    }
150    return false;
151}
152
153bool SkBitmapDevice::onPeekPixels(SkPixmap* pmap) {
154    const SkImageInfo info = fBitmap.info();
155    if (fBitmap.getPixels() && (kUnknown_SkColorType != info.colorType())) {
156        SkColorTable* ctable = nullptr;
157        pmap->reset(fBitmap.info(), fBitmap.getPixels(), fBitmap.rowBytes(), ctable);
158        return true;
159    }
160    return false;
161}
162
163bool SkBitmapDevice::onWritePixels(const SkImageInfo& srcInfo, const void* srcPixels,
164                                   size_t srcRowBytes, int x, int y) {
165    // since we don't stop creating un-pixeled devices yet, check for no pixels here
166    if (nullptr == fBitmap.getPixels()) {
167        return false;
168    }
169
170    const SkImageInfo dstInfo = fBitmap.info().makeWH(srcInfo.width(), srcInfo.height());
171
172    void* dstPixels = fBitmap.getAddr(x, y);
173    size_t dstRowBytes = fBitmap.rowBytes();
174
175    if (SkPixelInfo::CopyPixels(dstInfo, dstPixels, dstRowBytes, srcInfo, srcPixels, srcRowBytes)) {
176        fBitmap.notifyPixelsChanged();
177        return true;
178    }
179    return false;
180}
181
182bool SkBitmapDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
183                                  int x, int y) {
184    return fBitmap.readPixels(dstInfo, dstPixels, dstRowBytes, x, y);
185}
186
187///////////////////////////////////////////////////////////////////////////////
188
189void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
190    draw.drawPaint(paint);
191}
192
193void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
194                                const SkPoint pts[], const SkPaint& paint) {
195    draw.drawPoints(mode, count, pts, paint);
196}
197
198void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
199    draw.drawRect(r, paint);
200}
201
202void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
203    SkPath path;
204    path.addOval(oval);
205    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
206    // required to override drawOval.
207    this->drawPath(draw, path, paint, nullptr, true);
208}
209
210void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) {
211#ifdef SK_IGNORE_BLURRED_RRECT_OPT
212    SkPath  path;
213
214    path.addRRect(rrect);
215    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
216    // required to override drawRRect.
217    this->drawPath(draw, path, paint, nullptr, true);
218#else
219    draw.drawRRect(rrect, paint);
220#endif
221}
222
223void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
224                              const SkPaint& paint, const SkMatrix* prePathMatrix,
225                              bool pathIsMutable) {
226    draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
227}
228
229void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
230                                const SkMatrix& matrix, const SkPaint& paint) {
231    LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality());
232    draw.drawBitmap(bitmap, matrix, nullptr, paint);
233}
234
235static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) {
236    if (!paint.getMaskFilter()) {
237        return true;
238    }
239
240    // Some mask filters parameters (sigma) depend on the CTM/scale.
241    return m.getType() <= SkMatrix::kTranslate_Mask;
242}
243
244void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
245                                    const SkRect* src, const SkRect& dst,
246                                    const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
247    SkMatrix    matrix;
248    SkRect      bitmapBounds, tmpSrc, tmpDst;
249    SkBitmap    tmpBitmap;
250
251    bitmapBounds.isetWH(bitmap.width(), bitmap.height());
252
253    // Compute matrix from the two rectangles
254    if (src) {
255        tmpSrc = *src;
256    } else {
257        tmpSrc = bitmapBounds;
258    }
259    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
260
261    LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality());
262
263    const SkRect* dstPtr = &dst;
264    const SkBitmap* bitmapPtr = &bitmap;
265
266    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
267    // needed (if the src was clipped). No check needed if src==null.
268    if (src) {
269        if (!bitmapBounds.contains(*src)) {
270            if (!tmpSrc.intersect(bitmapBounds)) {
271                return; // nothing to draw
272            }
273            // recompute dst, based on the smaller tmpSrc
274            matrix.mapRect(&tmpDst, tmpSrc);
275            dstPtr = &tmpDst;
276        }
277    }
278
279    if (src && !src->contains(bitmapBounds) &&
280        SkCanvas::kFast_SrcRectConstraint == constraint &&
281        paint.getFilterQuality() != kNone_SkFilterQuality) {
282        // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know
283        // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap,
284        // but we must use a shader w/ dst bounds (which can access all of the bitmap needed).
285        goto USE_SHADER;
286    }
287
288    if (src) {
289        // since we may need to clamp to the borders of the src rect within
290        // the bitmap, we extract a subset.
291        const SkIRect srcIR = tmpSrc.roundOut();
292        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
293            return;
294        }
295        bitmapPtr = &tmpBitmap;
296
297        // Since we did an extract, we need to adjust the matrix accordingly
298        SkScalar dx = 0, dy = 0;
299        if (srcIR.fLeft > 0) {
300            dx = SkIntToScalar(srcIR.fLeft);
301        }
302        if (srcIR.fTop > 0) {
303            dy = SkIntToScalar(srcIR.fTop);
304        }
305        if (dx || dy) {
306            matrix.preTranslate(dx, dy);
307        }
308
309        SkRect extractedBitmapBounds;
310        extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
311        if (extractedBitmapBounds == tmpSrc) {
312            // no fractional part in src, we can just call drawBitmap
313            goto USE_DRAWBITMAP;
314        }
315    } else {
316        USE_DRAWBITMAP:
317        // We can go faster by just calling drawBitmap, which will concat the
318        // matrix with the CTM, and try to call drawSprite if it can. If not,
319        // it will make a shader and call drawRect, as we do below.
320        if (CanApplyDstMatrixAsCTM(matrix, paint)) {
321            draw.drawBitmap(*bitmapPtr, matrix, dstPtr, paint);
322            return;
323        }
324    }
325
326    USE_SHADER:
327
328    // Since the shader need only live for our stack-frame, pass in a custom allocator. This
329    // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap
330    // if its mutable, since that precaution is not needed (give the short lifetime of the shader).
331    SkTBlitterAllocator allocator;
332    // construct a shader, so we can call drawRect with the dst
333    auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
334                                &matrix, kNever_SkCopyPixelsMode, &allocator);
335    if (!s) {
336        return;
337    }
338    // we deliberately add a ref, since the allocator wants to be the last owner
339    s.get()->ref();
340
341    SkPaint paintWithShader(paint);
342    paintWithShader.setStyle(SkPaint::kFill_Style);
343    paintWithShader.setShader(s);
344
345    // Call ourself, in case the subclass wanted to share this setup code
346    // but handle the drawRect code themselves.
347    this->drawRect(draw, *dstPtr, paintWithShader);
348}
349
350void SkBitmapDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
351                                int x, int y, const SkPaint& paint) {
352    draw.drawSprite(bitmap, x, y, paint);
353}
354
355void SkBitmapDevice::drawText(const SkDraw& draw, const void* text, size_t len,
356                              SkScalar x, SkScalar y, const SkPaint& paint) {
357    draw.drawText((const char*)text, len, x, y, paint);
358}
359
360void SkBitmapDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
361                                 const SkScalar xpos[], int scalarsPerPos,
362                                 const SkPoint& offset, const SkPaint& paint) {
363    draw.drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint);
364}
365
366void SkBitmapDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
367                                  int vertexCount,
368                                  const SkPoint verts[], const SkPoint textures[],
369                                  const SkColor colors[], SkBlendMode bmode,
370                                  const uint16_t indices[], int indexCount,
371                                  const SkPaint& paint) {
372    draw.drawVertices(vmode, vertexCount, verts, textures, colors, bmode,
373                      indices, indexCount, paint);
374}
375
376void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
377                                int x, int y, const SkPaint& paint) {
378    SkASSERT(!paint.getImageFilter());
379    draw.drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, paint);
380}
381
382///////////////////////////////////////////////////////////////////////////////
383
384void SkBitmapDevice::drawSpecial(const SkDraw& draw, SkSpecialImage* srcImg, int x, int y,
385                                 const SkPaint& paint) {
386    SkASSERT(!srcImg->isTextureBacked());
387
388    SkBitmap resultBM;
389
390    SkImageFilter* filter = paint.getImageFilter();
391    if (filter) {
392        SkIPoint offset = SkIPoint::Make(0, 0);
393        SkMatrix matrix = *draw.fMatrix;
394        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
395        const SkIRect clipBounds = draw.fRC->getBounds().makeOffset(-x, -y);
396        sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
397        SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace());
398        SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties);
399
400        sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset));
401        if (resultImg) {
402            SkPaint tmpUnfiltered(paint);
403            tmpUnfiltered.setImageFilter(nullptr);
404            if (resultImg->getROPixels(&resultBM)) {
405                this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
406            }
407        }
408    } else {
409        if (srcImg->getROPixels(&resultBM)) {
410            this->drawSprite(draw, resultBM, x, y, paint);
411        }
412    }
413}
414
415sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) {
416    return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap);
417}
418
419sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) {
420    // This is called when we're about to draw the special-ized version of image to *this* device,
421    // so we can use our own presense/absence of a color space to decide how to decode the image.
422    SkDestinationSurfaceColorMode decodeColorMode = fBitmap.colorSpace()
423        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
424        : SkDestinationSurfaceColorMode::kLegacy;
425    return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()),
426                                         image->makeNonTextureImage(), decodeColorMode);
427}
428
429sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial() {
430    return this->makeSpecial(fBitmap);
431}
432
433///////////////////////////////////////////////////////////////////////////////
434
435sk_sp<SkSurface> SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
436    return SkSurface::MakeRaster(info, &props);
437}
438
439SkImageFilterCache* SkBitmapDevice::getImageFilterCache() {
440    SkImageFilterCache* cache = SkImageFilterCache::Get();
441    cache->ref();
442    return cache;
443}
444
445///////////////////////////////////////////////////////////////////////////////
446
447bool SkBitmapDevice::onShouldDisableLCD(const SkPaint& paint) const {
448    if (kN32_SkColorType != fBitmap.colorType() ||
449        paint.getRasterizer() ||
450        paint.getPathEffect() ||
451        paint.isFakeBoldText() ||
452        paint.getStyle() != SkPaint::kFill_Style ||
453        !paint.isSrcOver())
454    {
455        return true;
456    }
457    return false;
458}
459