1 /*
2  * Copyright 2014 Google Inc.
3  *
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  *
8  */
9 
10 #include "Global.h"
11 #include "Path2DBuilder.h"
12 #include "Path2D.h"
13 #include "SkPath.h"
14 
15 Global* Path2DBuilder::gGlobal = NULL;
16 
ConstructPath(const v8::FunctionCallbackInfo<v8::Value> & args)17 void Path2DBuilder::ConstructPath(const v8::FunctionCallbackInfo<v8::Value>& args) {
18     v8::HandleScope handleScope(gGlobal->getIsolate());
19     Path2DBuilder* path = new Path2DBuilder();
20     args.This()->SetInternalField(
21             0, v8::External::New(gGlobal->getIsolate(), path));
22 }
23 
24 #define ADD_METHOD(name, fn) \
25     constructor->InstanceTemplate()->Set( \
26             v8::String::NewFromUtf8( \
27                     global->getIsolate(), name, \
28                     v8::String::kInternalizedString), \
29             v8::FunctionTemplate::New(global->getIsolate(), fn))
30 
31 // Install the constructor in the global scope so Path2DBuilders can be constructed
32 // in JS.
AddToGlobal(Global * global)33 void Path2DBuilder::AddToGlobal(Global* global) {
34     gGlobal = global;
35 
36     // Create a stack-allocated handle scope.
37     v8::HandleScope handleScope(gGlobal->getIsolate());
38 
39     v8::Handle<v8::Context> context = gGlobal->getContext();
40 
41     // Enter the scope so all operations take place in the scope.
42     v8::Context::Scope contextScope(context);
43 
44     v8::Local<v8::FunctionTemplate> constructor = v8::FunctionTemplate::New(
45             gGlobal->getIsolate(), Path2DBuilder::ConstructPath);
46     constructor->InstanceTemplate()->SetInternalFieldCount(1);
47 
48     ADD_METHOD("close", ClosePath);
49     ADD_METHOD("moveTo", MoveTo);
50     ADD_METHOD("lineTo", LineTo);
51     ADD_METHOD("quadraticCurveTo", QuadraticCurveTo);
52     ADD_METHOD("bezierCurveTo", BezierCurveTo);
53     ADD_METHOD("arc", Arc);
54     ADD_METHOD("rect", Rect);
55     ADD_METHOD("oval", Oval);
56     ADD_METHOD("conicTo", ConicTo);
57 
58     ADD_METHOD("finalize", Finalize);
59 
60     context->Global()->Set(v8::String::NewFromUtf8(
61             gGlobal->getIsolate(), "Path2DBuilder"), constructor->GetFunction());
62 }
63 
Unwrap(const v8::FunctionCallbackInfo<v8::Value> & args)64 Path2DBuilder* Path2DBuilder::Unwrap(const v8::FunctionCallbackInfo<v8::Value>& args) {
65     v8::Handle<v8::External> field = v8::Handle<v8::External>::Cast(
66             args.This()->GetInternalField(0));
67     void* ptr = field->Value();
68     return static_cast<Path2DBuilder*>(ptr);
69 }
70 
ClosePath(const v8::FunctionCallbackInfo<v8::Value> & args)71 void Path2DBuilder::ClosePath(const v8::FunctionCallbackInfo<v8::Value>& args) {
72     Path2DBuilder* path = Unwrap(args);
73     path->fSkPath.close();
74 }
75 
MoveTo(const v8::FunctionCallbackInfo<v8::Value> & args)76 void Path2DBuilder::MoveTo(const v8::FunctionCallbackInfo<v8::Value>& args) {
77     if (args.Length() != 2) {
78         args.GetIsolate()->ThrowException(
79                 v8::String::NewFromUtf8(
80                         args.GetIsolate(), "Error: 2 arguments required."));
81         return;
82     }
83     double x = args[0]->NumberValue();
84     double y = args[1]->NumberValue();
85     Path2DBuilder* path = Unwrap(args);
86     path->fSkPath.moveTo(SkDoubleToScalar(x), SkDoubleToScalar(y));
87 }
88 
LineTo(const v8::FunctionCallbackInfo<v8::Value> & args)89 void Path2DBuilder::LineTo(const v8::FunctionCallbackInfo<v8::Value>& args) {
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 x = args[0]->NumberValue();
97     double y = args[1]->NumberValue();
98     Path2DBuilder* path = Unwrap(args);
99     path->fSkPath.lineTo(SkDoubleToScalar(x), SkDoubleToScalar(y));
100 }
101 
QuadraticCurveTo(const v8::FunctionCallbackInfo<v8::Value> & args)102 void Path2DBuilder::QuadraticCurveTo(const v8::FunctionCallbackInfo<v8::Value>& args) {
103     if (args.Length() != 4) {
104         args.GetIsolate()->ThrowException(
105                 v8::String::NewFromUtf8(
106                         args.GetIsolate(), "Error: 4 arguments required."));
107         return;
108     }
109     double cpx = args[0]->NumberValue();
110     double cpy = args[1]->NumberValue();
111     double x = args[2]->NumberValue();
112     double y = args[3]->NumberValue();
113     Path2DBuilder* path = Unwrap(args);
114     // TODO(jcgregorio) Doesn't handle the empty last path case correctly per
115     // the HTML 5 spec.
116     path->fSkPath.quadTo(
117             SkDoubleToScalar(cpx), SkDoubleToScalar(cpy),
118             SkDoubleToScalar(x), SkDoubleToScalar(y));
119 }
120 
BezierCurveTo(const v8::FunctionCallbackInfo<v8::Value> & args)121 void Path2DBuilder::BezierCurveTo(const v8::FunctionCallbackInfo<v8::Value>& args) {
122     if (args.Length() != 6) {
123         args.GetIsolate()->ThrowException(
124                 v8::String::NewFromUtf8(
125                         args.GetIsolate(), "Error: 6 arguments required."));
126         return;
127     }
128     double cp1x = args[0]->NumberValue();
129     double cp1y = args[1]->NumberValue();
130     double cp2x = args[2]->NumberValue();
131     double cp2y = args[3]->NumberValue();
132     double x = args[4]->NumberValue();
133     double y = args[5]->NumberValue();
134     Path2DBuilder* path = Unwrap(args);
135     // TODO(jcgregorio) Doesn't handle the empty last path case correctly per
136     // the HTML 5 spec.
137     path->fSkPath.cubicTo(
138             SkDoubleToScalar(cp1x), SkDoubleToScalar(cp1y),
139             SkDoubleToScalar(cp2x), SkDoubleToScalar(cp2y),
140             SkDoubleToScalar(x), SkDoubleToScalar(y));
141 }
142 
Arc(const v8::FunctionCallbackInfo<v8::Value> & args)143 void Path2DBuilder::Arc(const v8::FunctionCallbackInfo<v8::Value>& args) {
144     if (args.Length() != 5 && args.Length() != 6) {
145         args.GetIsolate()->ThrowException(
146                 v8::String::NewFromUtf8(
147                         args.GetIsolate(), "Error: 5 or 6 args required."));
148         return;
149     }
150     double x          = args[0]->NumberValue();
151     double y          = args[1]->NumberValue();
152     double radius     = args[2]->NumberValue();
153     double startAngle = args[3]->NumberValue();
154     double endAngle   = args[4]->NumberValue();
155     bool antiClockwise = false;
156     if (args.Length() == 6) {
157        antiClockwise = args[5]->BooleanValue();
158     }
159     double sweepAngle;
160     if (!antiClockwise) {
161       sweepAngle = endAngle - startAngle;
162     } else {
163       sweepAngle = startAngle - endAngle;
164       startAngle = endAngle;
165     }
166 
167     Path2DBuilder* path = Unwrap(args);
168     SkRect rect = {
169         SkDoubleToScalar(x-radius),
170         SkDoubleToScalar(y-radius),
171         SkDoubleToScalar(x+radius),
172         SkDoubleToScalar(y+radius)
173     };
174 
175     path->fSkPath.addArc(rect, SkRadiansToDegrees(startAngle),
176                          SkRadiansToDegrees(sweepAngle));
177 }
178 
Rect(const v8::FunctionCallbackInfo<v8::Value> & args)179 void Path2DBuilder::Rect(const v8::FunctionCallbackInfo<v8::Value>& args) {
180     if (args.Length() != 4) {
181         args.GetIsolate()->ThrowException(
182                 v8::String::NewFromUtf8(
183                         args.GetIsolate(), "Error: 4 arguments required."));
184         return;
185     }
186     double x = args[0]->NumberValue();
187     double y = args[1]->NumberValue();
188     double w = args[2]->NumberValue();
189     double h = args[3]->NumberValue();
190 
191     SkRect rect = {
192         SkDoubleToScalar(x),
193         SkDoubleToScalar(y),
194         SkDoubleToScalar(x) + SkDoubleToScalar(w),
195         SkDoubleToScalar(y) + SkDoubleToScalar(h)
196     };
197     Path2DBuilder* path = Unwrap(args);
198     path->fSkPath.addRect(rect);
199 }
200 
Oval(const v8::FunctionCallbackInfo<v8::Value> & args)201 void Path2DBuilder::Oval(const v8::FunctionCallbackInfo<v8::Value>& args) {
202     if (args.Length() != 4 && args.Length() != 5) {
203         args.GetIsolate()->ThrowException(
204                 v8::String::NewFromUtf8(
205                         args.GetIsolate(), "Error: 4 or 5 args required."));
206         return;
207     }
208     double x          = args[0]->NumberValue();
209     double y          = args[1]->NumberValue();
210     double radiusX    = args[2]->NumberValue();
211     double radiusY    = args[3]->NumberValue();
212     SkPath::Direction dir = SkPath::kCW_Direction;
213     if (args.Length() == 5 && !args[4]->BooleanValue()) {
214         dir = SkPath::kCCW_Direction;
215     }
216     Path2DBuilder* path = Unwrap(args);
217     SkRect rect = {
218         SkDoubleToScalar(x-radiusX),
219         SkDoubleToScalar(y-radiusX),
220         SkDoubleToScalar(x+radiusY),
221         SkDoubleToScalar(y+radiusY)
222     };
223 
224     path->fSkPath.addOval(rect, dir);
225 }
226 
ConicTo(const v8::FunctionCallbackInfo<v8::Value> & args)227 void Path2DBuilder::ConicTo(const v8::FunctionCallbackInfo<v8::Value>& args) {
228     if (args.Length() != 5) {
229         args.GetIsolate()->ThrowException(
230                 v8::String::NewFromUtf8(
231                         args.GetIsolate(), "Error: 5 args required."));
232         return;
233     }
234     double x1 = args[0]->NumberValue();
235     double y1 = args[1]->NumberValue();
236     double x2 = args[2]->NumberValue();
237     double y2 = args[3]->NumberValue();
238     double w  = args[4]->NumberValue();
239     Path2DBuilder* path = Unwrap(args);
240 
241     path->fSkPath.conicTo(
242             SkDoubleToScalar(x1),
243             SkDoubleToScalar(y1),
244             SkDoubleToScalar(x2),
245             SkDoubleToScalar(y2),
246             SkDoubleToScalar(w)
247             );
248 }
249 
Finalize(const v8::FunctionCallbackInfo<v8::Value> & args)250 void Path2DBuilder::Finalize(const v8::FunctionCallbackInfo<v8::Value>& args) {
251     Path2DBuilder* path = Unwrap(args);
252 
253     // Build Path2D from out fSkPath and return it.
254     SkPath* skPath = new SkPath(path->fSkPath);
255 
256     path->fSkPath.reset();
257 
258     Path2D* pathWrap = new Path2D(skPath);
259 
260     args.GetReturnValue().Set(pathWrap->persistent());
261 }
262