1/*
2 * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com>
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "GraphicsContext.h"
30
31#include "AffineTransform.h"
32#include "CString.h"
33#include "Color.h"
34#include "Font.h"
35#include "FontData.h"
36#include "NotImplemented.h"
37#include "Path.h"
38#include "Pen.h"
39#include <GraphicsDefs.h>
40#include <Region.h>
41#include <View.h>
42#include <Window.h>
43#include <stdio.h>
44
45
46namespace WebCore {
47
48class GraphicsContextPlatformPrivate {
49public:
50    GraphicsContextPlatformPrivate(BView* view);
51    ~GraphicsContextPlatformPrivate();
52
53    BView* m_view;
54};
55
56GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(BView* view)
57    : m_view(view)
58{
59}
60
61GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
62{
63}
64
65GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
66    : m_common(createGraphicsContextPrivate())
67    , m_data(new GraphicsContextPlatformPrivate(context))
68{
69    setPaintingDisabled(!context);
70}
71
72GraphicsContext::~GraphicsContext()
73{
74    destroyGraphicsContextPrivate(m_common);
75    delete m_data;
76}
77
78PlatformGraphicsContext* GraphicsContext::platformContext() const
79{
80    return m_data->m_view;
81}
82
83void GraphicsContext::savePlatformState()
84{
85    m_data->m_view->PushState();
86}
87
88void GraphicsContext::restorePlatformState()
89{
90    m_data->m_view->PopState();
91}
92
93// Draws a filled rectangle with a stroked border.
94void GraphicsContext::drawRect(const IntRect& rect)
95{
96    if (paintingDisabled())
97        return;
98
99    m_data->m_view->FillRect(rect);
100    if (strokeStyle() != NoStroke)
101        m_data->m_view->StrokeRect(rect, getHaikuStrokeStyle());
102}
103
104// This is only used to draw borders.
105void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
106{
107    if (paintingDisabled())
108        return;
109
110    if (strokeStyle() == NoStroke)
111        return;
112
113    m_data->m_view->StrokeLine(point1, point2, getHaikuStrokeStyle());
114}
115
116// This method is only used to draw the little circles used in lists.
117void GraphicsContext::drawEllipse(const IntRect& rect)
118{
119    if (paintingDisabled())
120        return;
121
122    m_data->m_view->FillEllipse(rect);
123    if (strokeStyle() != NoStroke)
124        m_data->m_view->StrokeEllipse(rect, getHaikuStrokeStyle());
125}
126
127void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
128{
129    if (paintingDisabled())
130        return;
131
132    m_data->m_view->StrokeArc(rect, startAngle, angleSpan, getHaikuStrokeStyle());
133}
134
135void GraphicsContext::strokePath()
136{
137    notImplemented();
138}
139
140void GraphicsContext::drawConvexPolygon(size_t pointsLength, const FloatPoint* points, bool shouldAntialias)
141{
142    if (paintingDisabled())
143        return;
144
145    BPoint bPoints[pointsLength];
146    for (size_t i = 0; i < pointsLength; i++)
147        bPoints[i] = points[i];
148
149    m_data->m_view->FillPolygon(bPoints, pointsLength);
150    if (strokeStyle() != NoStroke)
151        // Stroke with low color
152        m_data->m_view->StrokePolygon(bPoints, pointsLength, true, getHaikuStrokeStyle());
153}
154
155void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
156{
157    if (paintingDisabled())
158        return;
159
160    rgb_color oldColor = m_data->m_view->HighColor();
161    m_data->m_view->SetHighColor(color);
162    m_data->m_view->FillRect(rect);
163    m_data->m_view->SetHighColor(oldColor);
164}
165
166void GraphicsContext::fillRect(const FloatRect& rect)
167{
168    if (paintingDisabled())
169        return;
170}
171
172void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
173{
174    if (paintingDisabled() || !color.alpha())
175        return;
176
177    notImplemented();
178    // FIXME: A simple implementation could just use FillRoundRect if all
179    // the sizes are the same, or even if they are not. Otherwise several
180    // FillRect and FillArc calls are needed.
181}
182
183void GraphicsContext::fillPath()
184{
185    notImplemented();
186}
187
188void GraphicsContext::beginPath()
189{
190    notImplemented();
191}
192
193void GraphicsContext::addPath(const Path& path)
194{
195    notImplemented();
196}
197
198void GraphicsContext::clip(const FloatRect& rect)
199{
200    if (paintingDisabled())
201        return;
202
203    BRegion region(rect);
204    m_data->m_view->ConstrainClippingRegion(&region);
205}
206
207void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
208{
209    // FIXME: implement
210}
211
212void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
213{
214    if (paintingDisabled())
215        return;
216
217    unsigned rectCount = rects.size();
218
219    // FIXME: maybe we should implement this with BShape?
220
221    if (rects.size() > 1) {
222        BRegion    region;
223        for (int i = 0; i < rectCount; ++i)
224            region.Include(BRect(rects[i]));
225
226        m_data->m_view->SetHighColor(color);
227        m_data->m_view->StrokeRect(region.Frame(), B_MIXED_COLORS);
228    }
229}
230
231void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
232{
233    if (paintingDisabled())
234        return;
235
236    IntPoint endPoint = origin + IntSize(width, 0);
237    drawLine(origin, endPoint);
238}
239
240void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int width, bool grammar)
241{
242    if (paintingDisabled())
243        return;
244
245    notImplemented();
246}
247
248FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
249{
250    notImplemented();
251    return rect;
252}
253
254void GraphicsContext::beginTransparencyLayer(float opacity)
255{
256    if (paintingDisabled())
257        return;
258
259    notImplemented();
260}
261
262void GraphicsContext::endTransparencyLayer()
263{
264    if (paintingDisabled())
265        return;
266
267    notImplemented();
268}
269
270void GraphicsContext::clearRect(const FloatRect& rect)
271{
272    if (paintingDisabled())
273        return;
274
275    notImplemented();
276}
277
278void GraphicsContext::strokeRect(const FloatRect& rect, float width)
279{
280    if (paintingDisabled())
281        return;
282
283    float oldSize = m_data->m_view->PenSize();
284    m_data->m_view->SetPenSize(width);
285    m_data->m_view->StrokeRect(rect, getHaikuStrokeStyle());
286    m_data->m_view->SetPenSize(oldSize);
287}
288
289void GraphicsContext::setLineCap(LineCap lineCap)
290{
291    if (paintingDisabled())
292        return;
293
294    cap_mode mode = B_BUTT_CAP;
295    switch (lineCap) {
296    case RoundCap:
297        mode = B_ROUND_CAP;
298        break;
299    case SquareCap:
300        mode = B_SQUARE_CAP;
301        break;
302    case ButtCap:
303    default:
304        break;
305    }
306
307    m_data->m_view->SetLineMode(mode, m_data->m_view->LineJoinMode(), m_data->m_view->LineMiterLimit());
308}
309
310void GraphicsContext::setLineJoin(LineJoin lineJoin)
311{
312    if (paintingDisabled())
313        return;
314
315    join_mode mode = B_MITER_JOIN;
316    switch (lineJoin) {
317    case RoundJoin:
318        mode = B_ROUND_JOIN;
319        break;
320    case BevelJoin:
321        mode = B_BEVEL_JOIN;
322        break;
323    case MiterJoin:
324    default:
325        break;
326    }
327
328    m_data->m_view->SetLineMode(m_data->m_view->LineCapMode(), mode, m_data->m_view->LineMiterLimit());
329}
330
331void GraphicsContext::setMiterLimit(float limit)
332{
333    if (paintingDisabled())
334        return;
335
336    m_data->m_view->SetLineMode(m_data->m_view->LineCapMode(), m_data->m_view->LineJoinMode(), limit);
337}
338
339void GraphicsContext::setAlpha(float opacity)
340{
341    if (paintingDisabled())
342        return;
343
344    notImplemented();
345}
346
347void GraphicsContext::setCompositeOperation(CompositeOperator op)
348{
349    if (paintingDisabled())
350        return;
351
352    drawing_mode mode = B_OP_COPY;
353    switch (op) {
354    case CompositeClear:
355    case CompositeCopy:
356        // Use the default above
357        break;
358    case CompositeSourceOver:
359        mode = B_OP_OVER;
360        break;
361    default:
362        printf("GraphicsContext::setCompositeOperation: Unsupported composite operation %s\n",
363                compositeOperatorName(op).utf8().data());
364    }
365    m_data->m_view->SetDrawingMode(mode);
366}
367
368void GraphicsContext::clip(const Path& path)
369{
370    if (paintingDisabled())
371        return;
372
373    m_data->m_view->ConstrainClippingRegion(path.platformPath());
374}
375
376void GraphicsContext::canvasClip(const Path& path)
377{
378    clip(path);
379}
380
381void GraphicsContext::clipOut(const Path& path)
382{
383    if (paintingDisabled())
384        return;
385
386    notImplemented();
387}
388
389void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*)
390{
391    notImplemented();
392}
393
394AffineTransform GraphicsContext::getCTM() const
395{
396    notImplemented();
397    return AffineTransform();
398}
399
400void GraphicsContext::translate(float x, float y)
401{
402    if (paintingDisabled())
403        return;
404
405    notImplemented();
406}
407
408IntPoint GraphicsContext::origin()
409{
410    notImplemented();
411    return IntPoint(0, 0);
412}
413
414void GraphicsContext::rotate(float radians)
415{
416    if (paintingDisabled())
417        return;
418
419    notImplemented();
420}
421
422void GraphicsContext::scale(const FloatSize& size)
423{
424    if (paintingDisabled())
425        return;
426
427    notImplemented();
428}
429
430void GraphicsContext::clipOut(const IntRect& rect)
431{
432    if (paintingDisabled())
433        return;
434
435    notImplemented();
436}
437
438void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
439{
440    if (paintingDisabled())
441        return;
442
443    notImplemented();
444}
445
446void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
447{
448    if (paintingDisabled())
449        return;
450
451    notImplemented();
452}
453
454void GraphicsContext::concatCTM(const AffineTransform& transform)
455{
456    if (paintingDisabled())
457        return;
458
459    notImplemented();
460}
461
462void GraphicsContext::setPlatformShouldAntialias(bool enable)
463{
464    if (paintingDisabled())
465        return;
466
467    notImplemented();
468}
469
470void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
471{
472}
473
474void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
475{
476    notImplemented();
477}
478
479void GraphicsContext::setPlatformFont(const Font& font)
480{
481    m_data->m_view->SetFont(font.primaryFont()->platformData().font());
482}
483
484void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
485{
486    if (paintingDisabled())
487        return;
488
489    m_data->m_view->SetHighColor(color);
490}
491
492pattern GraphicsContext::getHaikuStrokeStyle()
493{
494    switch (strokeStyle()) {
495    case SolidStroke:
496        return B_SOLID_HIGH;
497        break;
498    case DottedStroke:
499        return B_MIXED_COLORS;
500        break;
501    case DashedStroke:
502        // FIXME: use a better dashed stroke!
503        notImplemented();
504        return B_MIXED_COLORS;
505        break;
506    default:
507        return B_SOLID_LOW;
508        break;
509    }
510}
511
512void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
513{
514    // FIXME: see getHaikuStrokeStyle.
515    notImplemented();
516}
517
518void GraphicsContext::setPlatformStrokeThickness(float thickness)
519{
520    if (paintingDisabled())
521        return;
522
523    m_data->m_view->SetPenSize(thickness);
524}
525
526void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
527{
528    if (paintingDisabled())
529        return;
530
531    m_data->m_view->SetHighColor(color);
532}
533
534void GraphicsContext::clearPlatformShadow()
535{
536    notImplemented();
537}
538
539void GraphicsContext::setPlatformShadow(IntSize const&, int, Color const&, ColorSpace)
540{
541    notImplemented();
542}
543
544} // namespace WebCore
545
546