1/*
2 * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include "config.h"
20#include "Path.h"
21
22#include "AffineTransform.h"
23#include "FloatRect.h"
24#include "GraphicsContext.h"
25#include "NotImplemented.h"
26#include "StrokeStyleApplier.h"
27
28#include <BlackBerryPlatformGraphics.h>
29#include <BlackBerryPlatformGraphicsContext.h>
30#include <BlackBerryPlatformPath.h>
31#include <stdio.h>
32#include <wtf/MathExtras.h>
33
34namespace WebCore {
35
36static GraphicsContext* scratchContext()
37{
38    static BlackBerry::Platform::Graphics::Buffer* buffer = BlackBerry::Platform::Graphics::createBuffer(IntSize(), BlackBerry::Platform::Graphics::NeverBacked);
39    static PlatformGraphicsContext* pgc = lockBufferDrawable(buffer);
40    static GraphicsContext gc(pgc);
41    return &gc;
42}
43
44Path::Path() : m_path(new BlackBerry::Platform::Graphics::Path)
45{
46}
47
48Path::Path(const PlatformPath& path)
49    : m_path(new BlackBerry::Platform::Graphics::Path(path))
50{
51}
52
53Path::~Path()
54{
55    if (m_path) {
56        delete m_path;
57        m_path = 0;
58    }
59}
60
61Path::Path(const Path& other)
62{
63    m_path = new BlackBerry::Platform::Graphics::Path(*other.m_path);
64}
65
66Path& Path::operator=(const Path& other)
67{
68    if (m_path) {
69        delete m_path;
70        m_path = 0;
71    }
72    m_path = new BlackBerry::Platform::Graphics::Path(*other.m_path);
73    return *this;
74}
75
76FloatPoint Path::currentPoint() const
77{
78    return m_path->currentPoint();
79}
80
81bool Path::contains(const FloatPoint& point, WindRule rule) const
82{
83    return m_path->contains(point, (BlackBerry::Platform::Graphics::WindRule)(rule));
84}
85
86bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
87{
88    GraphicsContext* scratch = scratchContext();
89    scratch->save();
90
91    if (applier)
92        applier->strokeStyle(scratch);
93
94    bool result = scratchContext()->platformContext()->strokeContains(*m_path, point);
95    scratch->restore();
96    return result;
97}
98
99void Path::translate(const FloatSize& size)
100{
101    AffineTransform transformation;
102    transformation.translate(size.width(), size.height());
103    transform(transformation);
104}
105
106FloatRect Path::boundingRect() const
107{
108    return m_path->getBounds();
109}
110
111FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
112{
113    GraphicsContext* scratch = scratchContext();
114    scratch->save();
115
116    if (applier)
117        applier->strokeStyle(scratch);
118
119    FloatRect result = scratch->platformContext()->getStrokeBounds(*m_path);
120
121    scratch->restore();
122    return result;
123}
124
125void Path::moveTo(const FloatPoint& point)
126{
127    m_path->moveTo(point);
128}
129
130void Path::addLineTo(const FloatPoint& point)
131{
132    m_path->addLineTo(point);
133}
134
135void Path::addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint)
136{
137    m_path->addQuadCurveTo(controlPoint, endPoint);
138}
139
140void Path::addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint)
141{
142    m_path->addBezierCurveTo(controlPoint1, controlPoint2, endPoint);
143}
144
145void Path::addArcTo(const FloatPoint& point1, const FloatPoint& point2, float radius)
146{
147    m_path->addArcTo(point1, point2, radius);
148}
149
150void Path::closeSubpath()
151{
152    m_path->closeSubpath();
153}
154
155void Path::addArc(const FloatPoint& center, float radius, float startAngle, float endAngle, bool anticlockwise)
156{
157    m_path->addArc(center, radius, startAngle, endAngle, anticlockwise);
158}
159
160void Path::addRect(const FloatRect& rect)
161{
162    m_path->addRect(rect);
163}
164
165void Path::addEllipse(const FloatRect& rect)
166{
167    m_path->addEllipse(rect);
168}
169
170void Path::platformAddPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
171{
172    if (rect.isEmpty())
173        return;
174
175    if (rect.width() < topLeftRadius.width() + topRightRadius.width()
176        || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width()
177        || rect.height() < topLeftRadius.height() + bottomLeftRadius.height()
178        || rect.height() < topRightRadius.height() + bottomRightRadius.height()) {
179        // If all the radii cannot be accommodated, return a rect.
180        addRect(rect);
181        return;
182    }
183
184    BlackBerry::Platform::FloatRoundedRect rr(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
185    // Make sure the generic path reflects the contents of the rounded rects
186    m_path->addRoundedRect(rr);
187}
188
189void Path::clear()
190{
191    m_path->reset();
192}
193
194bool Path::isEmpty() const
195{
196    return m_path->isEmpty();
197}
198
199bool Path::hasCurrentPoint() const
200{
201    return m_path->hasCurrentPoint();
202}
203
204void Path::apply(void* info, PathApplierFunction function) const
205{
206    m_path->apply(info, (void*)function);
207}
208
209void Path::transform(const AffineTransform& transformation)
210{
211    m_path->transform(reinterpret_cast<const double*>(&transformation));
212}
213
214void GraphicsContext::fillPath(const Path& path)
215{
216    BlackBerry::Platform::Graphics::Path* pp = path.platformPath();
217    if (!pp->isEmpty()) {
218        BlackBerry::Platform::Graphics::Gradient* platformGradient = fillGradient() ? fillGradient()->platformGradient() : 0;
219        BlackBerry::Platform::Graphics::Pattern* platformPattern = fillPattern() ? fillPattern()->platformPattern(AffineTransform()) : 0;
220        platformContext()->addFillPath(*pp, (BlackBerry::Platform::Graphics::WindRule)m_state.fillRule, platformGradient, platformPattern);
221    }
222}
223
224void GraphicsContext::strokePath(const Path& path)
225{
226    BlackBerry::Platform::Graphics::Path* pp = path.platformPath();
227    if (!pp->isEmpty()) {
228        BlackBerry::Platform::Graphics::Gradient* gradient = strokeGradient() ? strokeGradient()->platformGradient() : 0;
229        BlackBerry::Platform::Graphics::Pattern* pattern = strokePattern() ? strokePattern()->platformPattern(AffineTransform()) : 0;
230        platformContext()->addStrokePath(*pp, gradient, pattern);
231    }
232}
233
234void GraphicsContext::drawFocusRing(const Vector<IntRect>&, int, int, const Color&)
235{
236    notImplemented();
237}
238
239void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
240{
241    notImplemented();
242}
243
244void GraphicsContext::drawLine(const IntPoint& from, const IntPoint& to)
245{
246    platformContext()->addDrawLine(from, to);
247}
248
249void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
250{
251    platformContext()->addDrawLineForDocumentMarker(pt, width, (BlackBerry::Platform::Graphics::DocumentMarkerLineStyle)style);
252}
253
254void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool printing)
255{
256    platformContext()->addDrawLineForText(pt, width, printing);
257}
258
259// FIXME: don't ignore the winding rule. https://bugs.webkit.org/show_bug.cgi?id=107064
260void GraphicsContext::clip(const Path& path, WindRule)
261{
262    BlackBerry::Platform::Graphics::Path* pp = path.platformPath();
263    pp->applyAsClip(platformContext());
264}
265
266void GraphicsContext::clipPath(const Path& path, WindRule)
267{
268    if (path.platformPath()->isRectangular())
269        platformContext()->clip(path.boundingRect());
270    else
271        clip(path);
272}
273
274void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
275{
276    clip(path, fillRule);
277}
278
279void GraphicsContext::clipOut(const Path& path)
280{
281    BlackBerry::Platform::Graphics::Path* pp = path.platformPath();
282    pp->applyAsClipOut(platformContext());
283}
284
285}
286