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