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
15Global* Path2DBuilder::gGlobal = NULL;
16
17void 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.
33void 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
64Path2DBuilder* 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
71void Path2DBuilder::ClosePath(const v8::FunctionCallbackInfo<v8::Value>& args) {
72    Path2DBuilder* path = Unwrap(args);
73    path->fSkPath.close();
74}
75
76void 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
89void 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
102void 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
121void 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
143void 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
179void 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
201void 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
227void 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
250void 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