OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/renderer/skia_benchmarking_extension.h" | |
6 | |
7 #include "base/values.h" | |
8 #include "cc/base/math_util.h" | |
9 #include "cc/resources/picture.h" | |
10 #include "content/public/renderer/v8_value_converter.h" | |
11 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" | |
12 #include "third_party/WebKit/public/web/WebFrame.h" | |
13 #include "third_party/skia/include/core/SkCanvas.h" | |
14 #include "third_party/skia/include/core/SkColorPriv.h" | |
15 #include "third_party/skia/include/core/SkGraphics.h" | |
16 #include "third_party/skia/src/utils/debugger/SkDebugCanvas.h" | |
17 #include "third_party/skia/src/utils/debugger/SkDrawCommand.h" | |
18 #include "ui/gfx/rect_conversions.h" | |
19 #include "ui/gfx/skia_util.h" | |
20 #include "v8/include/v8.h" | |
21 | |
22 using WebKit::WebFrame; | |
23 | |
24 namespace { | |
25 | |
26 const char kSkiaBenchmarkingExtensionName[] = "v8/SkiaBenchmarking"; | |
27 | |
28 static scoped_refptr<cc::Picture> ParsePictureArg(v8::Handle<v8::Value> arg) { | |
29 scoped_ptr<content::V8ValueConverter> converter( | |
30 content::V8ValueConverter::create()); | |
31 | |
32 v8::String::Value v8_picture(arg); | |
33 scoped_ptr<base::Value> picture_value( | |
34 converter->FromV8Value(arg, v8::Context::GetCurrent())); | |
35 if (!picture_value) | |
36 return NULL; | |
37 | |
38 return cc::Picture::CreateFromValue(picture_value.get()); | |
39 } | |
40 | |
41 class SkiaBenchmarkingWrapper : public v8::Extension { | |
42 public: | |
43 SkiaBenchmarkingWrapper() : | |
44 v8::Extension(kSkiaBenchmarkingExtensionName, | |
45 "if (typeof(chrome) == 'undefined') {" | |
46 " chrome = {};" | |
47 "};" | |
48 "if (typeof(chrome.skiaBenchmarking) == 'undefined') {" | |
49 " chrome.skiaBenchmarking = {};" | |
50 "};" | |
51 "chrome.skiaBenchmarking.rasterize = function(picture, params) {" | |
52 " /* " | |
53 " Rasterizes a Picture JSON-encoded by cc::Picture::AsValue()." | |
54 " @param {Object} picture A json-encoded cc::Picture." | |
55 " @param {" | |
56 " 'scale': {Number}," | |
57 " 'stop': {Number}," | |
58 " 'clip': [Number, Number, Number, Number]" | |
59 " } (optional) Rasterization parameters." | |
60 " @returns {" | |
61 " 'width': {Number}," | |
62 " 'height': {Number}," | |
63 " 'data': {ArrayBuffer}" | |
64 " }" | |
65 " @returns undefined if the arguments are invalid or the picture" | |
66 " version is not supported." | |
67 " */" | |
68 " native function Rasterize();" | |
69 " return Rasterize(picture, params);" | |
70 "};" | |
71 "chrome.skiaBenchmarking.getOps = function(picture) {" | |
72 " /* " | |
73 " Extracts the Skia draw commands from a JSON-encoded cc::Picture" | |
74 " @param {Object} picture A json-encoded cc::Picture." | |
75 " @returns [{ 'cmd': {String}, 'info': [String, ...] }, ...]" | |
76 " @returns undefined if the arguments are invalid or the picture" | |
77 " version is not supported." | |
78 " */" | |
79 " native function GetOps();" | |
80 " return GetOps(picture);" | |
81 "};" | |
82 ) { | |
83 content::SkiaBenchmarkingExtension::InitSkGraphics(); | |
84 } | |
85 | |
86 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | |
87 v8::Handle<v8::String> name) OVERRIDE { | |
88 if (name->Equals(v8::String::New("Rasterize"))) | |
89 return v8::FunctionTemplate::New(Rasterize); | |
90 if (name->Equals(v8::String::New("GetOps"))) | |
91 return v8::FunctionTemplate::New(GetOps); | |
92 | |
93 return v8::Handle<v8::FunctionTemplate>(); | |
94 } | |
95 | |
96 static void Rasterize(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
97 if (args.Length() < 1) | |
98 return; | |
99 | |
100 scoped_refptr<cc::Picture> picture = ParsePictureArg(args[0]); | |
101 if (!picture.get()) | |
102 return; | |
103 | |
104 double scale = 1.0; | |
105 gfx::Rect clip_rect(picture->LayerRect()); | |
106 int stop_index = -1; | |
107 | |
108 if (args.Length() > 1) { | |
109 scoped_ptr<content::V8ValueConverter> converter( | |
110 content::V8ValueConverter::create()); | |
111 scoped_ptr<base::Value> params_value( | |
112 converter->FromV8Value(args[1], v8::Context::GetCurrent())); | |
113 | |
114 const base::DictionaryValue* params_dict = NULL; | |
115 if (params_value.get() && params_value->GetAsDictionary(¶ms_dict)) { | |
116 params_dict->GetDouble("scale", &scale); | |
117 params_dict->GetInteger("stop", &stop_index); | |
118 | |
119 const base::Value* clip_value = NULL; | |
120 if (params_dict->Get("clip", &clip_value)) | |
121 cc::MathUtil::FromValue(clip_value, &clip_rect); | |
122 } | |
123 } | |
124 | |
125 gfx::RectF clip(clip_rect); | |
126 clip.Intersect(picture->LayerRect()); | |
127 clip.Scale(scale); | |
128 gfx::Rect snapped_clip = gfx::ToEnclosingRect(clip); | |
129 | |
130 const int kMaxBitmapSize = 4096; | |
131 if (snapped_clip.width() > kMaxBitmapSize | |
132 || snapped_clip.height() > kMaxBitmapSize) | |
133 return; | |
134 | |
135 SkBitmap bitmap; | |
136 bitmap.setConfig(SkBitmap::kARGB_8888_Config, snapped_clip.width(), | |
137 snapped_clip.height()); | |
138 if (!bitmap.allocPixels()) | |
139 return; | |
140 bitmap.eraseARGB(0, 0, 0, 0); | |
141 | |
142 SkCanvas canvas(bitmap); | |
143 canvas.translate(SkFloatToScalar(-clip.x()), | |
144 SkFloatToScalar(-clip.y())); | |
145 canvas.clipRect(gfx::RectToSkRect(snapped_clip)); | |
146 canvas.scale(scale, scale); | |
147 canvas.translate(picture->LayerRect().x(), | |
148 picture->LayerRect().y()); | |
149 | |
150 // First, build a debug canvas for the given picture. | |
151 SkDebugCanvas debug_canvas(picture->LayerRect().width(), | |
152 picture->LayerRect().height()); | |
153 picture->Replay(&debug_canvas); | |
154 | |
155 // Raster the requested command subset into the bitmap-backed canvas. | |
156 int last_index = debug_canvas.getSize() - 1; | |
157 debug_canvas.drawTo(&canvas, stop_index < 0 | |
158 ? last_index | |
159 : std::min(last_index, stop_index)); | |
160 | |
161 WebKit::WebArrayBuffer buffer = | |
162 WebKit::WebArrayBuffer::create(bitmap.getSize(), 1); | |
163 uint32* packed_pixels = reinterpret_cast<uint32*>(bitmap.getPixels()); | |
164 uint8* buffer_pixels = reinterpret_cast<uint8*>(buffer.data()); | |
165 // Swizzle from native Skia format to RGBA as we copy out. | |
166 for (size_t i = 0; i < bitmap.getSize(); i += 4) { | |
167 uint32 c = packed_pixels[i >> 2]; | |
168 buffer_pixels[i] = SkGetPackedR32(c); | |
169 buffer_pixels[i + 1] = SkGetPackedG32(c); | |
170 buffer_pixels[i + 2] = SkGetPackedB32(c); | |
171 buffer_pixels[i + 3] = SkGetPackedA32(c); | |
172 } | |
173 | |
174 v8::Handle<v8::Object> result = v8::Object::New(); | |
175 result->Set(v8::String::New("width"), | |
176 v8::Number::New(snapped_clip.width())); | |
177 result->Set(v8::String::New("height"), | |
178 v8::Number::New(snapped_clip.height())); | |
179 result->Set(v8::String::New("data"), buffer.toV8Value()); | |
180 | |
181 args.GetReturnValue().Set(result); | |
182 } | |
183 | |
184 static void GetOps(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
185 if (args.Length() != 1) | |
186 return; | |
187 | |
188 scoped_refptr<cc::Picture> picture = ParsePictureArg(args[0]); | |
189 if (!picture.get()) | |
190 return; | |
191 | |
192 gfx::Rect bounds = picture->LayerRect(); | |
193 SkDebugCanvas canvas(bounds.width(), bounds.height()); | |
194 picture->Replay(&canvas); | |
195 | |
196 v8::Local<v8::Array> result = v8::Array::New(canvas.getSize()); | |
197 for (int i = 0; i < canvas.getSize(); ++i) { | |
198 DrawType cmd_type = canvas.getDrawCommandAt(i)->getType(); | |
199 v8::Handle<v8::Object> cmd = v8::Object::New(); | |
200 cmd->Set(v8::String::New("cmd_type"), v8::Integer::New(cmd_type)); | |
201 cmd->Set(v8::String::New("cmd_string"), v8::String::New( | |
202 SkDrawCommand::GetCommandString(cmd_type))); | |
203 | |
204 SkTDArray<SkString*>* info = canvas.getCommandInfo(i); | |
205 DCHECK(info); | |
206 | |
207 v8::Local<v8::Array> v8_info = v8::Array::New(info->count()); | |
208 for (int j = 0; j < info->count(); ++j) { | |
209 const SkString* info_str = (*info)[j]; | |
210 DCHECK(info_str); | |
211 v8_info->Set(j, v8::String::New(info_str->c_str())); | |
212 } | |
213 | |
214 cmd->Set(v8::String::New("info"), v8_info); | |
215 | |
216 result->Set(i, cmd); | |
217 } | |
218 | |
219 args.GetReturnValue().Set(result); | |
220 } | |
221 }; | |
222 | |
223 } // namespace | |
224 | |
225 namespace content { | |
226 | |
227 v8::Extension* SkiaBenchmarkingExtension::Get() { | |
228 return new SkiaBenchmarkingWrapper(); | |
229 } | |
230 | |
231 void SkiaBenchmarkingExtension::InitSkGraphics() { | |
232 // Always call on the main render thread. | |
233 // Does not need to be thread-safe, as long as the above holds. | |
234 // FIXME: remove this after Skia updates SkGraphics::Init() to be | |
235 // thread-safe and idempotent. | |
236 static bool skia_initialized = false; | |
237 if (!skia_initialized) { | |
238 SkGraphics::Init(); | |
239 skia_initialized = true; | |
240 } | |
241 } | |
242 | |
243 } // namespace content | |
OLD | NEW |