| OLD | NEW |
| 1 | 1 |
| 2 /* | 2 /* |
| 3 * Copyright 2013 Google Inc. | 3 * Copyright 2013 Google Inc. |
| 4 * | 4 * |
| 5 * | 5 * |
| 6 * Use of this source code is governed by a BSD-style license that can be | 6 * Use of this source code is governed by a BSD-style license that can be |
| 7 * found in the LICENSE file. | 7 * found in the LICENSE file. |
| 8 * | 8 * |
| 9 */ | 9 */ |
| 10 #include <v8.h> | 10 #include <v8.h> |
| 11 | 11 |
| 12 using namespace v8; | 12 using namespace v8; |
| 13 | 13 |
| 14 #include "Global.h" | 14 #include "Global.h" |
| 15 #include "JsContext.h" | 15 #include "JsContext.h" |
| 16 #include "Path2D.h" | 16 #include "Path2D.h" |
| 17 #include "SkCanvas.h" | 17 #include "SkCanvas.h" |
| 18 | 18 |
| 19 | 19 |
| 20 // Extracts a C string from a V8 Utf8Value. | 20 // Extracts a C string from a V8 Utf8Value. |
| 21 // TODO(jcgregrio) Currently dup'd in two files, fix. | 21 // TODO(jcgregrio) Currently dup'd in two files, fix. |
| 22 static const char* to_cstring(const v8::String::Utf8Value& value) { | 22 static const char* to_cstring(const v8::String::Utf8Value& value) { |
| 23 return *value ? *value : "<string conversion failed>"; | 23 return *value ? *value : "<string conversion failed>"; |
| 24 } | 24 } |
| 25 | 25 |
| 26 JsContext* JsContext::Unwrap(Handle<Object> obj) { | |
| 27 Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0)); | |
| 28 void* ptr = field->Value(); | |
| 29 return static_cast<JsContext*>(ptr); | |
| 30 } | |
| 31 | |
| 32 void JsContext::FillRect(const v8::FunctionCallbackInfo<Value>& args) { | |
| 33 JsContext* jsContext = Unwrap(args.This()); | |
| 34 SkCanvas* canvas = jsContext->fCanvas; | |
| 35 | |
| 36 if (args.Length() != 4) { | |
| 37 args.GetIsolate()->ThrowException( | |
| 38 v8::String::NewFromUtf8( | |
| 39 args.GetIsolate(), "Error: 4 arguments required.")); | |
| 40 return; | |
| 41 } | |
| 42 // TODO(jcgregorio) Really figure out the conversion from JS numbers to | |
| 43 // SkScalars. Maybe test if int first? Not sure of the performance impact. | |
| 44 double x = args[0]->NumberValue(); | |
| 45 double y = args[1]->NumberValue(); | |
| 46 double w = args[2]->NumberValue(); | |
| 47 double h = args[3]->NumberValue(); | |
| 48 | |
| 49 SkRect rect = { | |
| 50 SkDoubleToScalar(x), | |
| 51 SkDoubleToScalar(y), | |
| 52 SkDoubleToScalar(x) + SkDoubleToScalar(w), | |
| 53 SkDoubleToScalar(y) + SkDoubleToScalar(h) | |
| 54 }; | |
| 55 canvas->drawRect(rect, jsContext->fFillStyle); | |
| 56 } | |
| 57 | |
| 58 void JsContext::Save(const v8::FunctionCallbackInfo<Value>& args) { | |
| 59 JsContext* jsContext = Unwrap(args.This()); | |
| 60 SkCanvas* canvas = jsContext->fCanvas; | |
| 61 | |
| 62 canvas->save(); | |
| 63 } | |
| 64 | |
| 65 void JsContext::Restore(const v8::FunctionCallbackInfo<Value>& args) { | |
| 66 JsContext* jsContext = Unwrap(args.This()); | |
| 67 SkCanvas* canvas = jsContext->fCanvas; | |
| 68 | |
| 69 canvas->restore(); | |
| 70 } | |
| 71 | |
| 72 void JsContext::Rotate(const v8::FunctionCallbackInfo<Value>& args) { | |
| 73 JsContext* jsContext = Unwrap(args.This()); | |
| 74 SkCanvas* canvas = jsContext->fCanvas; | |
| 75 | |
| 76 if (args.Length() != 1) { | |
| 77 args.GetIsolate()->ThrowException( | |
| 78 v8::String::NewFromUtf8( | |
| 79 args.GetIsolate(), "Error: 1 arguments required.")); | |
| 80 return; | |
| 81 } | |
| 82 double angle = args[0]->NumberValue(); | |
| 83 canvas->rotate(SkRadiansToDegrees(angle)); | |
| 84 } | |
| 85 | |
| 86 void JsContext::Translate(const v8::FunctionCallbackInfo<Value>& args) { | |
| 87 JsContext* jsContext = Unwrap(args.This()); | |
| 88 SkCanvas* canvas = jsContext->fCanvas; | |
| 89 | |
| 90 if (args.Length() != 2) { | |
| 91 args.GetIsolate()->ThrowException( | |
| 92 v8::String::NewFromUtf8( | |
| 93 args.GetIsolate(), "Error: 2 arguments required.")); | |
| 94 return; | |
| 95 } | |
| 96 double dx = args[0]->NumberValue(); | |
| 97 double dy = args[1]->NumberValue(); | |
| 98 canvas->translate(SkDoubleToScalar(dx), SkDoubleToScalar(dy)); | |
| 99 } | |
| 100 | |
| 101 void JsContext::ResetTransform(const v8::FunctionCallbackInfo<Value>& args) { | |
| 102 JsContext* jsContext = Unwrap(args.This()); | |
| 103 SkCanvas* canvas = jsContext->fCanvas; | |
| 104 | |
| 105 canvas->resetMatrix(); | |
| 106 } | |
| 107 | |
| 108 void JsContext::Stroke(const v8::FunctionCallbackInfo<Value>& args) { | |
| 109 JsContext* jsContext = Unwrap(args.This()); | |
| 110 SkCanvas* canvas = jsContext->fCanvas; | |
| 111 | |
| 112 if (args.Length() != 1) { | |
| 113 args.GetIsolate()->ThrowException( | |
| 114 v8::String::NewFromUtf8( | |
| 115 args.GetIsolate(), "Error: 1 arguments required.")); | |
| 116 return; | |
| 117 } | |
| 118 | |
| 119 Handle<External> field = Handle<External>::Cast( | |
| 120 args[0]->ToObject()->GetInternalField(0)); | |
| 121 void* ptr = field->Value(); | |
| 122 Path2D* path = static_cast<Path2D*>(ptr); | |
| 123 | |
| 124 canvas->drawPath(path->getSkPath(), jsContext->fStrokeStyle); | |
| 125 } | |
| 126 | |
| 127 void JsContext::Fill(const v8::FunctionCallbackInfo<Value>& args) { | |
| 128 JsContext* jsContext = Unwrap(args.This()); | |
| 129 SkCanvas* canvas = jsContext->fCanvas; | |
| 130 | |
| 131 if (args.Length() != 1) { | |
| 132 args.GetIsolate()->ThrowException( | |
| 133 v8::String::NewFromUtf8( | |
| 134 args.GetIsolate(), "Error: 1 arguments required.")); | |
| 135 return; | |
| 136 } | |
| 137 | |
| 138 Handle<External> field = Handle<External>::Cast( | |
| 139 args[0]->ToObject()->GetInternalField(0)); | |
| 140 void* ptr = field->Value(); | |
| 141 Path2D* path = static_cast<Path2D*>(ptr); | |
| 142 | |
| 143 canvas->drawPath(path->getSkPath(), jsContext->fFillStyle); | |
| 144 } | |
| 145 | |
| 146 void JsContext::GetStyle(Local<String> name, | |
| 147 const PropertyCallbackInfo<Value>& info, | |
| 148 const SkPaint& style) { | |
| 149 char buf[8]; | |
| 150 SkColor color = style.getColor(); | |
| 151 sprintf(buf, "#%02X%02X%02X", SkColorGetR(color), SkColorGetG(color), | |
| 152 SkColorGetB(color)); | |
| 153 | |
| 154 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buf)); | |
| 155 } | |
| 156 | |
| 157 void JsContext::SetStyle(Local<String> name, Local<Value> value, | |
| 158 const PropertyCallbackInfo<void>& info, | |
| 159 SkPaint& style) { | |
| 160 Local<String> s = value->ToString(); | |
| 161 if (s->Length() != 7 && s->Length() != 9) { | |
| 162 info.GetIsolate()->ThrowException( | |
| 163 v8::String::NewFromUtf8( | |
| 164 info.GetIsolate(), | |
| 165 "Invalid fill style format length.")); | |
| 166 return; | |
| 167 } | |
| 168 char buf[10]; | |
| 169 s->WriteUtf8(buf, sizeof(buf)); | |
| 170 | |
| 171 if (buf[0] != '#') { | |
| 172 info.GetIsolate()->ThrowException( | |
| 173 v8::String::NewFromUtf8( | |
| 174 info.GetIsolate(), "Invalid fill style format.")); | |
| 175 return; | |
| 176 } | |
| 177 | |
| 178 // Colors can be RRGGBBAA, but SkColor uses ARGB. | |
| 179 long color = strtol(buf+1, NULL, 16); | |
| 180 uint32_t alpha = SK_AlphaOPAQUE; | |
| 181 if (s->Length() == 9) { | |
| 182 alpha = color & 0xFF; | |
| 183 color >>= 8; | |
| 184 } | |
| 185 style.setColor(SkColorSetA(SkColor(color), alpha)); | |
| 186 } | |
| 187 | |
| 188 void JsContext::GetFillStyle(Local<String> name, | |
| 189 const PropertyCallbackInfo<Value>& info) { | |
| 190 JsContext* jsContext = Unwrap(info.This()); | |
| 191 GetStyle(name, info, jsContext->fFillStyle); | |
| 192 } | |
| 193 | |
| 194 void JsContext::GetStrokeStyle(Local<String> name, | |
| 195 const PropertyCallbackInfo<Value>& info) { | |
| 196 JsContext* jsContext = Unwrap(info.This()); | |
| 197 GetStyle(name, info, jsContext->fStrokeStyle); | |
| 198 } | |
| 199 | |
| 200 void JsContext::SetFillStyle(Local<String> name, Local<Value> value, | |
| 201 const PropertyCallbackInfo<void>& info) { | |
| 202 JsContext* jsContext = Unwrap(info.This()); | |
| 203 SetStyle(name, value, info, jsContext->fFillStyle); | |
| 204 } | |
| 205 | |
| 206 void JsContext::SetStrokeStyle(Local<String> name, Local<Value> value, | |
| 207 const PropertyCallbackInfo<void>& info) { | |
| 208 JsContext* jsContext = Unwrap(info.This()); | |
| 209 SetStyle(name, value, info, jsContext->fStrokeStyle); | |
| 210 } | |
| 211 | |
| 212 | |
| 213 void JsContext::GetWidth(Local<String> name, | |
| 214 const PropertyCallbackInfo<Value>& info) { | |
| 215 JsContext* jsContext = Unwrap(info.This()); | |
| 216 SkISize size = jsContext->fCanvas->getDeviceSize(); | |
| 217 | |
| 218 info.GetReturnValue().Set( | |
| 219 Int32::New(jsContext->fGlobal->getIsolate(), size.fWidth)); | |
| 220 } | |
| 221 | |
| 222 void JsContext::GetHeight(Local<String> name, | |
| 223 const PropertyCallbackInfo<Value>& info) { | |
| 224 JsContext* jsContext = Unwrap(info.This()); | |
| 225 SkISize size = jsContext->fCanvas->getDeviceSize(); | |
| 226 | |
| 227 info.GetReturnValue().Set( | |
| 228 Int32::New(jsContext->fGlobal->getIsolate(), size.fHeight)); | |
| 229 } | |
| 230 | |
| 231 | |
| 232 Persistent<ObjectTemplate> JsContext::gContextTemplate; | 26 Persistent<ObjectTemplate> JsContext::gContextTemplate; |
| 233 | 27 |
| 234 #define ADD_METHOD(name, fn) \ | |
| 235 result->Set(String::NewFromUtf8( \ | |
| 236 fGlobal->getIsolate(), name, \ | |
| 237 String::kInternalizedString), \ | |
| 238 FunctionTemplate::New(fGlobal->getIsolate(), fn)) | |
| 239 | |
| 240 Handle<ObjectTemplate> JsContext::makeContextTemplate() { | |
| 241 EscapableHandleScope handleScope(fGlobal->getIsolate()); | |
| 242 | |
| 243 Local<ObjectTemplate> result = ObjectTemplate::New(); | |
| 244 | |
| 245 // Add a field to store the pointer to a JsContext instance. | |
| 246 result->SetInternalFieldCount(1); | |
| 247 | |
| 248 // Add accessors for each of the fields of the context object. | |
| 249 result->SetAccessor(String::NewFromUtf8( | |
| 250 fGlobal->getIsolate(), "fillStyle", String::kInternalizedString), | |
| 251 GetFillStyle, SetFillStyle); | |
| 252 result->SetAccessor(String::NewFromUtf8( | |
| 253 fGlobal->getIsolate(), "strokeStyle", String::kInternalizedString), | |
| 254 GetStrokeStyle, SetStrokeStyle); | |
| 255 result->SetAccessor(String::NewFromUtf8( | |
| 256 fGlobal->getIsolate(), "width", String::kInternalizedString), | |
| 257 GetWidth); | |
| 258 result->SetAccessor(String::NewFromUtf8( | |
| 259 fGlobal->getIsolate(), "height", String::kInternalizedString), | |
| 260 GetHeight); | |
| 261 | |
| 262 // Add methods. | |
| 263 ADD_METHOD("fillRect", FillRect); | |
| 264 ADD_METHOD("stroke", Stroke); | |
| 265 ADD_METHOD("fill", Fill); | |
| 266 ADD_METHOD("rotate", Rotate); | |
| 267 ADD_METHOD("save", Save); | |
| 268 ADD_METHOD("restore", Restore); | |
| 269 ADD_METHOD("translate", Translate); | |
| 270 ADD_METHOD("resetTransform", ResetTransform); | |
| 271 | |
| 272 // Return the result through the current handle scope. | |
| 273 return handleScope.Escape(result); | |
| 274 } | |
| 275 | |
| 276 | |
| 277 // Wraps 'this' in a Javascript object. | 28 // Wraps 'this' in a Javascript object. |
| 278 Handle<Object> JsContext::wrap() { | 29 Handle<Object> JsContext::wrap() { |
| 279 // Handle scope for temporary handles. | 30 // Handle scope for temporary handles. |
| 280 EscapableHandleScope handleScope(fGlobal->getIsolate()); | 31 EscapableHandleScope handleScope(fGlobal->getIsolate()); |
| 281 | 32 |
| 282 // Fetch the template for creating JavaScript JsContext wrappers. | 33 // Fetch the template for creating JavaScript JsContext wrappers. |
| 283 // It only has to be created once, which we do on demand. | 34 // It only has to be created once, which we do on demand. |
| 284 if (gContextTemplate.IsEmpty()) { | 35 if (gContextTemplate.IsEmpty()) { |
| 285 Handle<ObjectTemplate> raw_template = this->makeContextTemplate(); | 36 Local<ObjectTemplate> localTemplate = ObjectTemplate::New(); |
| 286 gContextTemplate.Reset(fGlobal->getIsolate(), raw_template); | 37 |
| 38 // Add a field to store the pointer to a JsContext instance. |
| 39 localTemplate->SetInternalFieldCount(1); |
| 40 |
| 41 this->addAttributesAndMethods(localTemplate); |
| 42 |
| 43 gContextTemplate.Reset(fGlobal->getIsolate(), localTemplate); |
| 287 } | 44 } |
| 288 Handle<ObjectTemplate> templ = | 45 Handle<ObjectTemplate> templ = |
| 289 Local<ObjectTemplate>::New(fGlobal->getIsolate(), gContextTemplate); | 46 Local<ObjectTemplate>::New(fGlobal->getIsolate(), gContextTemplate); |
| 290 | 47 |
| 291 // Create an empty JsContext wrapper. | 48 // Create an empty JsContext wrapper. |
| 292 Local<Object> result = templ->NewInstance(); | 49 Local<Object> result = templ->NewInstance(); |
| 293 | 50 |
| 294 // Wrap the raw C++ pointer in an External so it can be referenced | 51 // Wrap the raw C++ pointer in an External so it can be referenced |
| 295 // from within JavaScript. | 52 // from within JavaScript. |
| 296 Handle<External> contextPtr = External::New(fGlobal->getIsolate(), this); | 53 Handle<External> contextPtr = External::New(fGlobal->getIsolate(), this); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 | 131 |
| 375 // It is a function; cast it to a Function. | 132 // It is a function; cast it to a Function. |
| 376 Handle<Function> fn_fun = Handle<Function>::Cast(fn_val); | 133 Handle<Function> fn_fun = Handle<Function>::Cast(fn_val); |
| 377 | 134 |
| 378 // Store the function in a Persistent handle, since we also want that to | 135 // Store the function in a Persistent handle, since we also want that to |
| 379 // remain after this call returns. | 136 // remain after this call returns. |
| 380 fOnDraw.Reset(fGlobal->getIsolate(), fn_fun); | 137 fOnDraw.Reset(fGlobal->getIsolate(), fn_fun); |
| 381 | 138 |
| 382 return true; | 139 return true; |
| 383 } | 140 } |
| OLD | NEW |