| 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 |