blob: a893d0a1ddfe7fdc837da3776a0d9530285235ef [file] [log] [blame]
joshualitt4dcbe432016-02-25 10:50:28 -08001/*
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 "png.h"
11
joshualittee5348b2016-02-26 08:36:25 -080012#include "SkJSONCanvas.h"
13
joshualitt4dcbe432016-02-25 10:50:28 -080014const int Request::kImageWidth = 1920;
15const int Request::kImageHeight = 1080;
16
17static void write_png_callback(png_structp png_ptr, png_bytep data, png_size_t length) {
18 SkWStream* out = (SkWStream*) png_get_io_ptr(png_ptr);
19 out->write(data, length);
20}
21
22static void write_png(const png_bytep rgba, png_uint_32 width, png_uint_32 height, SkWStream& out) {
23 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
24 SkASSERT(png != nullptr);
25 png_infop info_ptr = png_create_info_struct(png);
26 SkASSERT(info_ptr != nullptr);
27 if (setjmp(png_jmpbuf(png))) {
28 SkFAIL("png encode error");
29 }
30 png_set_IHDR(png, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
31 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
32 png_set_compression_level(png, 1);
33 png_bytepp rows = (png_bytepp) sk_malloc_throw(height * sizeof(png_byte*));
34 png_bytep pixels = (png_bytep) sk_malloc_throw(width * height * 3);
35 for (png_size_t y = 0; y < height; ++y) {
36 const png_bytep src = rgba + y * width * 4;
37 rows[y] = pixels + y * width * 3;
38 // convert from RGBA to RGB
39 for (png_size_t x = 0; x < width; ++x) {
40 rows[y][x * 3] = src[x * 4];
41 rows[y][x * 3 + 1] = src[x * 4 + 1];
42 rows[y][x * 3 + 2] = src[x * 4 + 2];
43 }
44 }
45 png_set_filter(png, 0, PNG_NO_FILTERS);
46 png_set_rows(png, info_ptr, &rows[0]);
47 png_set_write_fn(png, &out, write_png_callback, NULL);
48 png_write_png(png, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
49 png_destroy_write_struct(&png, NULL);
50 sk_free(rows);
51}
52
joshualittee5348b2016-02-26 08:36:25 -080053Request::Request(SkString rootUrl)
54 : fUploadContext(nullptr)
55 , fUrlDataManager(rootUrl)
56 , fGPUEnabled(false) {
57 // create surface
58 GrContextOptions grContextOpts;
59 fContextFactory.reset(new GrContextFactory(grContextOpts));
60 fSurface.reset(this->createCPUSurface());
61}
62
joshualitt4dcbe432016-02-25 10:50:28 -080063SkBitmap* Request::getBitmapFromCanvas(SkCanvas* canvas) {
64 SkBitmap* bmp = new SkBitmap();
65 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType,
66 kOpaque_SkAlphaType);
67 bmp->setInfo(info);
68 if (!canvas->readPixels(bmp, 0, 0)) {
69 fprintf(stderr, "Can't read pixels\n");
70 return nullptr;
71 }
72 return bmp;
73}
74
75SkData* Request::writeCanvasToPng(SkCanvas* canvas) {
76 // capture pixels
77 SkAutoTDelete<SkBitmap> bmp(getBitmapFromCanvas(canvas));
78 SkASSERT(bmp);
79
80 // write to png
81 SkDynamicMemoryWStream buffer;
82 write_png((const png_bytep) bmp->getPixels(), bmp->width(), bmp->height(), buffer);
83 return buffer.copyToData();
84}
85
86SkCanvas* Request::getCanvas() {
87 GrContextFactory* factory = fContextFactory;
88 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
89 GrContextFactory::kNone_GLContextOptions).fGLContext;
90 gl->makeCurrent();
91 SkASSERT(fDebugCanvas);
92 SkCanvas* target = fSurface->getCanvas();
93 return target;
94}
95
96void Request::drawToCanvas(int n) {
97 SkCanvas* target = this->getCanvas();
98 fDebugCanvas->drawTo(target, n);
99}
100
101SkData* Request::drawToPng(int n) {
102 this->drawToCanvas(n);
103 return writeCanvasToPng(this->getCanvas());
104}
105
106SkSurface* Request::createCPUSurface() {
107 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
108 kPremul_SkAlphaType);
109 return SkSurface::NewRaster(info);
110}
111
112SkSurface* Request::createGPUSurface() {
113 GrContext* context = fContextFactory->get(GrContextFactory::kNative_GLContextType,
114 GrContextFactory::kNone_GLContextOptions);
115 int maxRTSize = context->caps()->maxRenderTargetSize();
116 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
117 SkTMin(kImageHeight, maxRTSize),
118 kN32_SkColorType, kPremul_SkAlphaType);
119 uint32_t flags = 0;
120 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
121 SkSurface* surface = SkSurface::NewRenderTarget(context, SkBudgeted::kNo, info, 0,
122 &props);
123 return surface;
124}
125
joshualittee5348b2016-02-26 08:36:25 -0800126bool Request::enableGPU(bool enable) {
127 if (enable) {
128 SkSurface* surface = this->createGPUSurface();
129 if (surface) {
130 fSurface.reset(surface);
131 fGPUEnabled = true;
132 return true;
133 }
134 return false;
135 }
136 fSurface.reset(this->createCPUSurface());
137 fGPUEnabled = false;
138 return true;
joshualitt6bc96792016-02-29 05:35:04 -0800139}
140
141bool Request::initPictureFromStream(SkStream* stream) {
142 // parse picture from stream
143 fPicture.reset(SkPicture::CreateFromStream(stream));
144 if (!fPicture.get()) {
145 fprintf(stderr, "Could not create picture from stream.\n");
146 return false;
147 }
148
149 // pour picture into debug canvas
150 fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, Request::kImageHeight));
151 fDebugCanvas->drawPicture(fPicture);
joshualitt3a9be692016-02-29 11:38:11 -0800152
153 // for some reason we need to 'flush' the debug canvas by drawing all of the ops
154 fDebugCanvas->drawTo(this->getCanvas(), this->getLastOp());
joshualitt6bc96792016-02-29 05:35:04 -0800155 return true;
joshualittee5348b2016-02-26 08:36:25 -0800156}
157
joshualittb95c7722016-02-29 07:44:02 -0800158GrAuditTrail* Request::getAuditTrail(SkCanvas* canvas) {
159 GrAuditTrail* at = nullptr;
160#if SK_SUPPORT_GPU
161 GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
162 if (rt) {
163 GrContext* ctx = rt->getContext();
164 if (ctx) {
165 at = ctx->getAuditTrail();
166 }
167 }
168#endif
169 return at;
170}
171
172void Request::cleanupAuditTrail(SkCanvas* canvas) {
173 GrAuditTrail* at = this->getAuditTrail(canvas);
174 if (at) {
175 GrAuditTrail::AutoEnable ae(at);
176 at->fullReset();
177 }
178}
179
joshualitt1e5884b2016-02-26 08:22:49 -0800180SkData* Request::getJsonOps(int n) {
181 SkCanvas* canvas = this->getCanvas();
182 Json::Value root = fDebugCanvas->toJSON(fUrlDataManager, n, canvas);
183 root["mode"] = Json::Value(fGPUEnabled ? "gpu" : "cpu");
184 SkDynamicMemoryWStream stream;
185 stream.writeText(Json::FastWriter().write(root).c_str());
186
joshualittb95c7722016-02-29 07:44:02 -0800187 this->cleanupAuditTrail(canvas);
188
joshualitt1e5884b2016-02-26 08:22:49 -0800189 return stream.copyToData();
190}
191
192SkData* Request::getJsonBatchList(int n) {
193 SkCanvas* canvas = this->getCanvas();
194 SkASSERT(fGPUEnabled);
195
196 // TODO if this is inefficient we could add a method to GrAuditTrail which takes
197 // a Json::Value and is only compiled in this file
198 Json::Value parsedFromString;
199#if SK_SUPPORT_GPU
joshualitt3a9be692016-02-29 11:38:11 -0800200 // we use the toJSON method on debug canvas, but then just ignore the results and pull
201 // the information we care about from the audit trail
202 fDebugCanvas->toJSON(fUrlDataManager, n, canvas);
203
joshualittb95c7722016-02-29 07:44:02 -0800204 GrAuditTrail* at = this->getAuditTrail(canvas);
joshualitt1e5884b2016-02-26 08:22:49 -0800205 GrAuditTrail::AutoManageBatchList enable(at);
joshualitt1e5884b2016-02-26 08:22:49 -0800206 Json::Reader reader;
joshualitt3a9be692016-02-29 11:38:11 -0800207 SkDEBUGCODE(bool parsingSuccessful = )reader.parse(at->toJson().c_str(),
joshualitt1e5884b2016-02-26 08:22:49 -0800208 parsedFromString);
209 SkASSERT(parsingSuccessful);
210#endif
211
212 SkDynamicMemoryWStream stream;
213 stream.writeText(Json::FastWriter().write(parsedFromString).c_str());
214
215 return stream.copyToData();
216}
joshualittee5348b2016-02-26 08:36:25 -0800217
218SkData* Request::getJsonInfo(int n) {
219 // drawTo
220 SkAutoTUnref<SkSurface> surface(this->createCPUSurface());
221 SkCanvas* canvas = surface->getCanvas();
222
223 // TODO this is really slow and we should cache the matrix and clip
224 fDebugCanvas->drawTo(canvas, n);
225
226 // make some json
227 SkMatrix vm = fDebugCanvas->getCurrentMatrix();
228 SkIRect clip = fDebugCanvas->getCurrentClip();
229 Json::Value info(Json::objectValue);
230 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
231 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
232
233 std::string json = Json::FastWriter().write(info);
234
235 // We don't want the null terminator so strlen is correct
236 return SkData::NewWithCopy(json.c_str(), strlen(json.c_str()));
237}