1/*
2 * Copyright 2016 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 "Request.h"
9
10#include "SkPictureRecorder.h"
11#include "SkPM4fPriv.h"
12#include "picture_utils.h"
13#include "sk_tool_utils.h"
14
15using namespace sk_gpu_test;
16
17static int kDefaultWidth = 1920;
18static int kDefaultHeight = 1080;
19static int kMaxWidth = 8192;
20static int kMaxHeight = 8192;
21
22
23Request::Request(SkString rootUrl)
24    : fUploadContext(nullptr)
25    , fUrlDataManager(rootUrl)
26    , fGPUEnabled(false)
27    , fOverdraw(false)
28    , fColorMode(0) {
29    // create surface
30#if SK_SUPPORT_GPU
31    GrContextOptions grContextOpts;
32    fContextFactory = new GrContextFactory(grContextOpts);
33#else
34    fContextFactory = nullptr;
35#endif
36}
37
38Request::~Request() {
39#if SK_SUPPORT_GPU
40    if (fContextFactory) {
41        delete fContextFactory;
42    }
43#endif
44}
45
46SkBitmap* Request::getBitmapFromCanvas(SkCanvas* canvas) {
47    SkBitmap* bmp = new SkBitmap();
48    if (!bmp->tryAllocPixels(canvas->imageInfo()) || !canvas->readPixels(*bmp, 0, 0)) {
49        fprintf(stderr, "Can't read pixels\n");
50        delete bmp;
51        return nullptr;
52    }
53    return bmp;
54}
55
56sk_sp<SkData> Request::writeCanvasToPng(SkCanvas* canvas) {
57    // capture pixels
58    std::unique_ptr<SkBitmap> bmp(this->getBitmapFromCanvas(canvas));
59    SkASSERT(bmp);
60
61    // Convert to format suitable for PNG output
62    sk_sp<SkData> encodedBitmap = sk_tools::encode_bitmap_for_png(*bmp);
63    SkASSERT(encodedBitmap.get());
64
65    // write to an opaque png (black background)
66    SkDynamicMemoryWStream buffer;
67    SkDrawCommand::WritePNG(encodedBitmap->bytes(), bmp->width(), bmp->height(),
68                            buffer, true);
69    return buffer.detachAsData();
70}
71
72SkCanvas* Request::getCanvas() {
73#if SK_SUPPORT_GPU
74    GrContextFactory* factory = fContextFactory;
75    GLTestContext* gl = factory->getContextInfo(GrContextFactory::kGL_ContextType,
76            GrContextFactory::ContextOverrides::kNone).glContext();
77    if (!gl) {
78        gl = factory->getContextInfo(GrContextFactory::kGLES_ContextType,
79                                     GrContextFactory::ContextOverrides::kNone).glContext();
80    }
81    if (gl) {
82        gl->makeCurrent();
83    }
84#endif
85    SkASSERT(fDebugCanvas);
86
87    // create the appropriate surface if necessary
88    if (!fSurface) {
89        this->enableGPU(fGPUEnabled);
90    }
91    SkCanvas* target = fSurface->getCanvas();
92    return target;
93}
94
95void Request::drawToCanvas(int n, int m) {
96    SkCanvas* target = this->getCanvas();
97    fDebugCanvas->drawTo(target, n, m);
98}
99
100sk_sp<SkData> Request::drawToPng(int n, int m) {
101    //fDebugCanvas->setOverdrawViz(true);
102    this->drawToCanvas(n, m);
103    //fDebugCanvas->setOverdrawViz(false);
104    return writeCanvasToPng(this->getCanvas());
105}
106
107sk_sp<SkData> Request::writeOutSkp() {
108    // Playback into picture recorder
109    SkIRect bounds = this->getBounds();
110    SkPictureRecorder recorder;
111    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bounds.width()),
112                                               SkIntToScalar(bounds.height()));
113
114    fDebugCanvas->draw(canvas);
115
116    return recorder.finishRecordingAsPicture()->serialize();
117}
118
119GrContext* Request::getContext() {
120#if SK_SUPPORT_GPU
121    GrContext* result = fContextFactory->get(GrContextFactory::kGL_ContextType,
122                                             GrContextFactory::ContextOverrides::kNone);
123    if (!result) {
124        result = fContextFactory->get(GrContextFactory::kGLES_ContextType,
125                                      GrContextFactory::ContextOverrides::kNone);
126    }
127    return result;
128#else
129    return nullptr;
130#endif
131}
132
133SkIRect Request::getBounds() {
134    SkIRect bounds;
135    if (fPicture) {
136        bounds = fPicture->cullRect().roundOut();
137        if (fGPUEnabled) {
138#if SK_SUPPORT_GPU
139            int maxRTSize = this->getContext()->caps()->maxRenderTargetSize();
140            bounds = SkIRect::MakeWH(SkTMin(bounds.width(), maxRTSize),
141                                     SkTMin(bounds.height(), maxRTSize));
142#endif
143        }
144    } else {
145        bounds = SkIRect::MakeWH(kDefaultWidth, kDefaultHeight);
146    }
147
148    // We clip to kMaxWidth / kMaxHeight for performance reasons.
149    // TODO make this configurable
150    bounds = SkIRect::MakeWH(SkTMin(bounds.width(), kMaxWidth),
151                             SkTMin(bounds.height(), kMaxHeight));
152    return bounds;
153}
154
155namespace {
156
157struct ColorAndProfile {
158    SkColorType fColorType;
159    bool fSRGB;
160};
161
162ColorAndProfile ColorModes[] = {
163    { kN32_SkColorType,      false },
164    { kN32_SkColorType,       true },
165    { kRGBA_F16_SkColorType,  true },
166};
167
168}
169
170SkSurface* Request::createCPUSurface() {
171    SkIRect bounds = this->getBounds();
172    ColorAndProfile cap = ColorModes[fColorMode];
173    auto colorSpace = kRGBA_F16_SkColorType == cap.fColorType
174                    ? SkColorSpace::MakeSRGBLinear()
175                    : SkColorSpace::MakeSRGB();
176    SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(), cap.fColorType,
177                                         kPremul_SkAlphaType, cap.fSRGB ? colorSpace : nullptr);
178    return SkSurface::MakeRaster(info).release();
179}
180
181SkSurface* Request::createGPUSurface() {
182    GrContext* context = this->getContext();
183    SkIRect bounds = this->getBounds();
184    ColorAndProfile cap = ColorModes[fColorMode];
185    auto colorSpace = kRGBA_F16_SkColorType == cap.fColorType
186                    ? SkColorSpace::MakeSRGBLinear()
187                    : SkColorSpace::MakeSRGB();
188    SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(), cap.fColorType,
189                                         kPremul_SkAlphaType, cap.fSRGB ? colorSpace: nullptr);
190    SkSurface* surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info).release();
191    return surface;
192}
193
194bool Request::setOverdraw(bool enable) {
195    fOverdraw = enable;
196    return true;
197}
198
199bool Request::setColorMode(int mode) {
200    fColorMode = mode;
201    return enableGPU(fGPUEnabled);
202}
203
204bool Request::enableGPU(bool enable) {
205    if (enable) {
206        SkSurface* surface = this->createGPUSurface();
207        if (surface) {
208            fSurface.reset(surface);
209            fGPUEnabled = true;
210
211            // When we switch to GPU, there seems to be some mystery draws in the canvas.  So we
212            // draw once to flush the pipe
213            // TODO understand what is actually happening here
214            if (fDebugCanvas) {
215                fDebugCanvas->drawTo(this->getCanvas(), this->getLastOp());
216                this->getCanvas()->flush();
217            }
218
219            return true;
220        }
221        return false;
222    }
223    fSurface.reset(this->createCPUSurface());
224    fGPUEnabled = false;
225    return true;
226}
227
228bool Request::initPictureFromStream(SkStream* stream) {
229    // parse picture from stream
230    fPicture = SkPicture::MakeFromStream(stream);
231    if (!fPicture) {
232        fprintf(stderr, "Could not create picture from stream.\n");
233        return false;
234    }
235
236    // reinitialize canvas with the new picture dimensions
237    this->enableGPU(fGPUEnabled);
238
239    // pour picture into debug canvas
240    SkIRect bounds = this->getBounds();
241    fDebugCanvas.reset(new SkDebugCanvas(bounds.width(), bounds.height()));
242    fDebugCanvas->drawPicture(fPicture);
243
244    // for some reason we need to 'flush' the debug canvas by drawing all of the ops
245    fDebugCanvas->drawTo(this->getCanvas(), this->getLastOp());
246    this->getCanvas()->flush();
247    return true;
248}
249
250sk_sp<SkData> Request::getJsonOps(int n) {
251    SkCanvas* canvas = this->getCanvas();
252    Json::Value root = fDebugCanvas->toJSON(fUrlDataManager, n, canvas);
253    root["mode"] = Json::Value(fGPUEnabled ? "gpu" : "cpu");
254    root["drawGpuOpBounds"] = Json::Value(fDebugCanvas->getDrawGpuOpBounds());
255    root["colorMode"] = Json::Value(fColorMode);
256    SkDynamicMemoryWStream stream;
257    stream.writeText(Json::FastWriter().write(root).c_str());
258
259    return stream.detachAsData();
260}
261
262sk_sp<SkData> Request::getJsonOpList(int n) {
263    SkCanvas* canvas = this->getCanvas();
264    SkASSERT(fGPUEnabled);
265
266    Json::Value result = fDebugCanvas->toJSONOpList(n, canvas);
267
268    SkDynamicMemoryWStream stream;
269    stream.writeText(Json::FastWriter().write(result).c_str());
270
271    return stream.detachAsData();
272}
273
274sk_sp<SkData> Request::getJsonInfo(int n) {
275    // drawTo
276    sk_sp<SkSurface> surface(this->createCPUSurface());
277    SkCanvas* canvas = surface->getCanvas();
278
279    // TODO this is really slow and we should cache the matrix and clip
280    fDebugCanvas->drawTo(canvas, n);
281
282    // make some json
283    SkMatrix vm = fDebugCanvas->getCurrentMatrix();
284    SkIRect clip = fDebugCanvas->getCurrentClip();
285    Json::Value info(Json::objectValue);
286    info["ViewMatrix"] = SkDrawCommand::MakeJsonMatrix(vm);
287    info["ClipRect"] = SkDrawCommand::MakeJsonIRect(clip);
288
289    std::string json = Json::FastWriter().write(info);
290
291    // We don't want the null terminator so strlen is correct
292    return SkData::MakeWithCopy(json.c_str(), strlen(json.c_str()));
293}
294
295SkColor Request::getPixel(int x, int y) {
296    SkCanvas* canvas = this->getCanvas();
297    canvas->flush();
298    std::unique_ptr<SkBitmap> bitmap(this->getBitmapFromCanvas(canvas));
299    SkASSERT(bitmap);
300
301    // Convert to format suitable for inspection
302    sk_sp<SkData> encodedBitmap = sk_tools::encode_bitmap_for_png(*bitmap);
303    SkASSERT(encodedBitmap);
304
305    const uint8_t* start = encodedBitmap->bytes() + ((y * bitmap->width() + x) * 4);
306    SkColor result = SkColorSetARGB(start[3], start[0], start[1], start[2]);
307    return result;
308}
309