1/*
2 * Copyright 2015 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 "SkImageGenerator.h"
9#include "SkCanvas.h"
10#include "SkMatrix.h"
11#include "SkPaint.h"
12#include "SkPicture.h"
13#include "SkSurface.h"
14#include "SkTLazy.h"
15
16class SkPictureImageGenerator : SkImageGenerator {
17public:
18    static SkImageGenerator* Create(const SkISize&, const SkPicture*, const SkMatrix*,
19                                    const SkPaint*);
20
21protected:
22    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[],
23                     int* ctableCount) override;
24    bool onComputeScaledDimensions(SkScalar scale, SupportedSizes*) override;
25    bool onGenerateScaledPixels(const SkISize&, const SkIPoint&, const SkPixmap&) override;
26
27#if SK_SUPPORT_GPU
28    GrTexture* onGenerateTexture(GrContext*, const SkIRect*) override;
29#endif
30
31private:
32    SkPictureImageGenerator(const SkISize&, const SkPicture*, const SkMatrix*, const SkPaint*);
33
34    SkAutoTUnref<const SkPicture> fPicture;
35    SkMatrix                      fMatrix;
36    SkTLazy<SkPaint>              fPaint;
37
38    typedef SkImageGenerator INHERITED;
39};
40
41SkImageGenerator* SkPictureImageGenerator::Create(const SkISize& size, const SkPicture* picture,
42                                const SkMatrix* matrix, const SkPaint* paint) {
43    if (!picture || size.isEmpty()) {
44        return nullptr;
45    }
46
47    return new SkPictureImageGenerator(size, picture, matrix, paint);
48}
49
50SkPictureImageGenerator::SkPictureImageGenerator(const SkISize& size, const SkPicture* picture,
51                                                 const SkMatrix* matrix, const SkPaint* paint)
52    : INHERITED(SkImageInfo::MakeN32Premul(size))
53    , fPicture(SkRef(picture)) {
54
55    if (matrix) {
56        fMatrix = *matrix;
57    } else {
58        fMatrix.reset();
59    }
60
61    if (paint) {
62        fPaint.set(*paint);
63    }
64}
65
66bool SkPictureImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
67                                          SkPMColor ctable[], int* ctableCount) {
68    if (info != getInfo() || ctable || ctableCount) {
69        return false;
70    }
71
72    SkBitmap bitmap;
73    if (!bitmap.installPixels(info, pixels, rowBytes)) {
74        return false;
75    }
76
77    bitmap.eraseColor(SK_ColorTRANSPARENT);
78    SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
79    canvas.drawPicture(fPicture, &fMatrix, fPaint.getMaybeNull());
80
81    return true;
82}
83
84bool SkPictureImageGenerator::onComputeScaledDimensions(SkScalar scale,
85                                                        SupportedSizes* sizes) {
86    SkASSERT(scale > 0 && scale <= 1);
87    const int w = this->getInfo().width();
88    const int h = this->getInfo().height();
89    const int sw = SkScalarRoundToInt(scale * w);
90    const int sh = SkScalarRoundToInt(scale * h);
91    if (sw > 0 && sh > 0) {
92        sizes->fSizes[0].set(sw, sh);
93        sizes->fSizes[1].set(sw, sh);
94        return true;
95    }
96    return false;
97}
98
99bool SkPictureImageGenerator::onGenerateScaledPixels(const SkISize& scaledSize,
100                                                     const SkIPoint& scaledOrigin,
101                                                     const SkPixmap& scaledPixels) {
102    int w = scaledSize.width();
103    int h = scaledSize.height();
104
105    const SkScalar scaleX = SkIntToScalar(w) / this->getInfo().width();
106    const SkScalar scaleY = SkIntToScalar(h) / this->getInfo().height();
107    SkMatrix matrix = SkMatrix::MakeScale(scaleX, scaleY);
108    matrix.postTranslate(-SkIntToScalar(scaledOrigin.x()), -SkIntToScalar(scaledOrigin.y()));
109
110    SkBitmap bitmap;
111    if (!bitmap.installPixels(scaledPixels)) {
112        return false;
113    }
114
115    bitmap.eraseColor(SK_ColorTRANSPARENT);
116    SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
117    matrix.preConcat(fMatrix);
118    canvas.drawPicture(fPicture, &matrix, fPaint.getMaybeNull());
119    return true;
120}
121
122///////////////////////////////////////////////////////////////////////////////////////////////////
123
124SkImageGenerator* SkImageGenerator::NewFromPicture(const SkISize& size, const SkPicture* picture,
125                                                   const SkMatrix* matrix, const SkPaint* paint) {
126    return SkPictureImageGenerator::Create(size, picture, matrix, paint);
127}
128
129///////////////////////////////////////////////////////////////////////////////////////////////////
130
131#if SK_SUPPORT_GPU
132#include "GrTexture.h"
133
134GrTexture* SkPictureImageGenerator::onGenerateTexture(GrContext* ctx, const SkIRect* subset) {
135    const SkImageInfo& info = this->getInfo();
136    SkImageInfo surfaceInfo = subset ? info.makeWH(subset->width(), subset->height()) : info;
137
138    //
139    // TODO: respect the usage, by possibly creating a different (pow2) surface
140    //
141    SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, SkBudgeted::kYes,
142                                                               surfaceInfo));
143    if (!surface.get()) {
144        return nullptr;
145    }
146
147    SkMatrix matrix = fMatrix;
148    if (subset) {
149        matrix.postTranslate(-subset->x(), -subset->y());
150    }
151    surface->getCanvas()->clear(0); // does NewRenderTarget promise to do this for us?
152    surface->getCanvas()->drawPicture(fPicture, &matrix, fPaint.getMaybeNull());
153    SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
154    if (!image.get()) {
155        return nullptr;
156    }
157    return SkSafeRef(image->getTexture());
158}
159#endif
160