1/*
2 * Copyright (C) 2007 Kevin Ollivier <kevino@theolliviers.com>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "GraphicsContext.h"
28
29#include "AffineTransform.h"
30#include "FloatRect.h"
31#include "Font.h"
32#include "IntRect.h"
33#include "NotImplemented.h"
34#include <wtf/MathExtras.h>
35
36#include <math.h>
37#include <stdio.h>
38
39#include <wx/defs.h>
40#include <wx/window.h>
41#include <wx/dcclient.h>
42#include <wx/dcgraph.h>
43#include <wx/graphics.h>
44
45#if __WXMAC__
46#include <Carbon/Carbon.h>
47#elif __WXMSW__
48#include <windows.h>
49#endif
50
51namespace WebCore {
52
53int getWxCompositingOperation(CompositeOperator op, bool hasAlpha)
54{
55    // FIXME: Add support for more operators.
56    if (op == CompositeSourceOver && !hasAlpha)
57        op = CompositeCopy;
58
59    int function;
60    switch (op) {
61        case CompositeClear:
62            function = wxCLEAR;
63        case CompositeCopy:
64            function = wxCOPY;
65            break;
66        default:
67            function = wxCOPY;
68    }
69    return function;
70}
71
72static int strokeStyleToWxPenStyle(int p)
73{
74    if (p == SolidStroke)
75        return wxSOLID;
76    if (p == DottedStroke)
77        return wxDOT;
78    if (p == DashedStroke)
79        return wxLONG_DASH;
80    if (p == NoStroke)
81        return wxTRANSPARENT;
82
83    return wxSOLID;
84}
85
86class GraphicsContextPlatformPrivate {
87public:
88    GraphicsContextPlatformPrivate();
89    ~GraphicsContextPlatformPrivate();
90
91#if USE(WXGC)
92    wxGCDC* context;
93#else
94    wxWindowDC* context;
95#endif
96    int mswDCStateID;
97    wxRegion gtkCurrentClipRgn;
98    wxRegion gtkPaintClipRgn;
99};
100
101GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate() :
102    context(0),
103    mswDCStateID(0),
104    gtkCurrentClipRgn(wxRegion()),
105    gtkPaintClipRgn(wxRegion())
106{
107}
108
109GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
110{
111}
112
113
114void GraphicsContext::platformInit(PlatformGraphicsContext* context)
115{
116    m_data = new GraphicsContextPlatformPrivate;
117    setPaintingDisabled(!context);
118
119    if (context) {
120        // Make sure the context starts in sync with our state.
121        setPlatformFillColor(fillColor(), ColorSpaceDeviceRGB);
122        setPlatformStrokeColor(strokeColor(), ColorSpaceDeviceRGB);
123    }
124#if USE(WXGC)
125    m_data->context = (wxGCDC*)context;
126#else
127    m_data->context = (wxWindowDC*)context;
128#endif
129}
130
131void GraphicsContext::platformDestroy()
132{
133    delete m_data;
134}
135
136PlatformGraphicsContext* GraphicsContext::platformContext() const
137{
138    return (PlatformGraphicsContext*)m_data->context;
139}
140
141void GraphicsContext::savePlatformState()
142{
143    if (m_data->context)
144    {
145#if USE(WXGC)
146        wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
147        gc->PushState();
148#else
149    // when everything is working with USE_WXGC, we can remove this
150    #if __WXMAC__
151        CGContextRef context;
152        wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
153        if (gc)
154            context = (CGContextRef)gc->GetNativeContext();
155        if (context)
156            CGContextSaveGState(context);
157    #elif __WXMSW__
158        HDC dc = (HDC)m_data->context->GetHDC();
159        m_data->mswDCStateID = ::SaveDC(dc);
160    #elif __WXGTK__
161        m_data->gtkCurrentClipRgn = m_data->context->m_currentClippingRegion;
162        m_data->gtkPaintClipRgn = m_data->context->m_paintClippingRegion;
163    #endif
164#endif // __WXMAC__
165    }
166}
167
168void GraphicsContext::restorePlatformState()
169{
170    if (m_data->context)
171    {
172#if USE(WXGC)
173        wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
174        gc->PopState();
175#else
176    #if __WXMAC__
177        CGContextRef context;
178        wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
179        if (gc)
180            context = (CGContextRef)gc->GetNativeContext();
181        if (context)
182            CGContextRestoreGState(context);
183    #elif __WXMSW__
184        HDC dc = (HDC)m_data->context->GetHDC();
185        ::RestoreDC(dc, m_data->mswDCStateID);
186    #elif __WXGTK__
187        m_data->context->m_currentClippingRegion = m_data->gtkCurrentClipRgn;
188        m_data->context->m_paintClippingRegion = m_data->gtkPaintClipRgn;
189    #endif
190
191#endif // USE_WXGC
192    }
193}
194
195// Draws a filled rectangle with a stroked border.
196void GraphicsContext::drawRect(const IntRect& rect)
197{
198    if (paintingDisabled())
199        return;
200
201    m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle())));
202    m_data->context->DrawRectangle(rect.x(), rect.y(), rect.width(), rect.height());
203}
204
205// This is only used to draw borders.
206void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
207{
208    if (paintingDisabled())
209        return;
210
211    FloatPoint p1 = point1;
212    FloatPoint p2 = point2;
213
214    m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle())));
215    m_data->context->DrawLine(point1.x(), point1.y(), point2.x(), point2.y());
216}
217
218// This method is only used to draw the little circles used in lists.
219void GraphicsContext::drawEllipse(const IntRect& rect)
220{
221    if (paintingDisabled())
222        return;
223
224    m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle())));
225    m_data->context->DrawEllipse(rect.x(), rect.y(), rect.width(), rect.height());
226}
227
228void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
229{
230    if (paintingDisabled())
231        return;
232
233    m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle())));
234    m_data->context->DrawEllipticArc(rect.x(), rect.y(), rect.width(), rect.height(), startAngle, angleSpan);
235}
236
237void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
238{
239    if (paintingDisabled())
240        return;
241
242    if (npoints <= 1)
243        return;
244
245    wxPoint* polygon = new wxPoint[npoints];
246    for (size_t i = 0; i < npoints; i++)
247        polygon[i] = wxPoint(points[i].x(), points[i].y());
248    m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle())));
249    m_data->context->DrawPolygon((int)npoints, polygon);
250    delete [] polygon;
251}
252
253void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
254{
255    if (paintingDisabled())
256        return;
257
258    if (numPoints <= 1)
259        return;
260
261    // FIXME: IMPLEMENT!!
262}
263
264void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
265{
266    if (paintingDisabled())
267        return;
268
269    savePlatformState();
270
271    m_data->context->SetPen(*wxTRANSPARENT_PEN);
272    m_data->context->SetBrush(wxBrush(color));
273    m_data->context->DrawRectangle(rect.x(), rect.y(), rect.width(), rect.height());
274
275    restorePlatformState();
276}
277
278void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
279{
280    if (paintingDisabled())
281        return;
282
283    notImplemented();
284}
285
286void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color)
287{
288    // FIXME: implement
289}
290
291void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
292{
293    if (paintingDisabled())
294        return;
295
296    notImplemented();
297}
298
299void GraphicsContext::clip(const FloatRect& r)
300{
301    wxWindowDC* windc = dynamic_cast<wxWindowDC*>(m_data->context);
302    wxPoint pos(0, 0);
303
304    if (windc) {
305#if !defined(__WXGTK__) || wxCHECK_VERSION(2,9,0)
306        wxWindow* window = windc->GetWindow();
307#else
308        wxWindow* window = windc->m_owner;
309#endif
310        if (window) {
311            wxWindow* parent = window->GetParent();
312            // we need to convert from WebView "global" to WebFrame "local" coords.
313            // FIXME: We only want to go to the top WebView.
314            while (parent) {
315                pos += window->GetPosition();
316                parent = parent->GetParent();
317            }
318        }
319    }
320
321    m_data->context->SetClippingRegion(r.x() - pos.x, r.y() - pos.y, r.width() + pos.x, r.height() + pos.y);
322}
323
324void GraphicsContext::clipOut(const Path&)
325{
326    notImplemented();
327}
328
329void GraphicsContext::clipOut(const IntRect&)
330{
331    notImplemented();
332}
333
334void GraphicsContext::clipPath(const Path&, WindRule)
335{
336    notImplemented();
337}
338
339void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing)
340{
341    if (paintingDisabled())
342        return;
343
344    FloatPoint endPoint = origin + FloatSize(width, 0);
345    m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), wxSOLID));
346    m_data->context->DrawLine(origin.x(), origin.y(), endPoint.x(), endPoint.y());
347}
348
349void GraphicsContext::drawLineForTextChecking(const FloatPoint& origin, float width, TextCheckingLineStyle style)
350{
351    switch (style) {
352    case TextCheckingSpellingLineStyle:
353        m_data->context->SetPen(wxPen(*wxRED, 2, wxLONG_DASH));
354        break;
355    case TextCheckingGrammarLineStyle:
356        m_data->context->SetPen(wxPen(*wxGREEN, 2, wxLONG_DASH));
357        break;
358    default:
359        return;
360    }
361    m_data->context->DrawLine(origin.x(), origin.y(), origin.x() + width, origin.y());
362}
363
364void GraphicsContext::clip(const Path&)
365{
366    notImplemented();
367}
368
369void GraphicsContext::canvasClip(const Path& path)
370{
371    clip(path);
372}
373
374AffineTransform GraphicsContext::getCTM() const
375{
376#if USE(WXGC)
377    wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
378    if (gc) {
379        wxGraphicsMatrix matrix = gc->GetTransform();
380        double a, b, c, d, e, f;
381        matrix.Get(&a, &b, &c, &d, &e, &f);
382        return AffineTransform(a, b, c, d, e, f);
383    }
384#endif
385    return AffineTransform();
386}
387
388void GraphicsContext::translate(float tx, float ty)
389{
390#if USE(WXGC)
391    if (m_data->context) {
392        wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
393        gc->Translate(tx, ty);
394    }
395#endif
396}
397
398void GraphicsContext::rotate(float angle)
399{
400#if USE(WXGC)
401    if (m_data->context) {
402        wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
403        gc->Rotate(angle);
404    }
405#endif
406}
407
408void GraphicsContext::scale(const FloatSize& scale)
409{
410#if USE(WXGC)
411    if (m_data->context) {
412        wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
413        gc->Scale(scale.width(), scale.height());
414    }
415#endif
416}
417
418
419FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
420{
421    FloatRect result;
422
423    wxCoord x = (wxCoord)frect.x();
424    wxCoord y = (wxCoord)frect.y();
425
426    x = m_data->context->LogicalToDeviceX(x);
427    y = m_data->context->LogicalToDeviceY(y);
428    result.setX((float)x);
429    result.setY((float)y);
430    x = (wxCoord)frect.width();
431    y = (wxCoord)frect.height();
432    x = m_data->context->LogicalToDeviceXRel(x);
433    y = m_data->context->LogicalToDeviceYRel(y);
434    result.setWidth((float)x);
435    result.setHeight((float)y);
436    return result;
437}
438
439void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
440{
441    notImplemented();
442}
443
444void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
445{
446    if (m_data->context)
447    {
448#if wxCHECK_VERSION(2,9,0)
449        m_data->context->SetLogicalFunction(static_cast<wxRasterOperationMode>(getWxCompositingOperation(op, false)));
450#else
451        m_data->context->SetLogicalFunction(getWxCompositingOperation(op, false));
452#endif
453    }
454}
455
456void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
457{
458    if (paintingDisabled())
459        return;
460
461    if (m_data->context)
462        m_data->context->SetPen(wxPen(color, strokeThickness(), strokeStyleToWxPenStyle(strokeStyle())));
463}
464
465void GraphicsContext::setPlatformStrokeThickness(float thickness)
466{
467    if (paintingDisabled())
468        return;
469
470    if (m_data->context)
471        m_data->context->SetPen(wxPen(strokeColor(), thickness, strokeStyleToWxPenStyle(strokeStyle())));
472
473}
474
475void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
476{
477    if (paintingDisabled())
478        return;
479
480    if (m_data->context)
481        m_data->context->SetBrush(wxBrush(color));
482}
483
484void GraphicsContext::concatCTM(const AffineTransform& transform)
485{
486    if (paintingDisabled())
487        return;
488
489#if USE(WXGC)
490    wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
491    if (gc)
492        gc->ConcatTransform(transform);
493#endif
494    return;
495}
496
497void GraphicsContext::setCTM(const AffineTransform& transform)
498{
499    if (paintingDisabled())
500        return;
501
502#if USE(WXGC)
503    wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
504    if (gc)
505        gc->SetTransform(transform);
506#endif
507    return;
508}
509
510void GraphicsContext::setPlatformShouldAntialias(bool enable)
511{
512    if (paintingDisabled())
513        return;
514    notImplemented();
515}
516
517void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
518{
519}
520
521InterpolationQuality GraphicsContext::imageInterpolationQuality() const
522{
523    return InterpolationDefault;
524}
525
526void GraphicsContext::fillPath(const Path& path)
527{
528#if USE(WXGC)
529    wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
530    if (gc)
531        gc->FillPath(*path.platformPath());
532#endif
533}
534
535void GraphicsContext::strokePath(const Path& path)
536{
537#if USE(WXGC)
538    wxGraphicsContext* gc = m_data->context->GetGraphicsContext();
539    if (gc)
540        gc->StrokePath(*path.platformPath());
541#endif
542}
543
544void GraphicsContext::fillRect(const FloatRect& rect)
545{
546    if (paintingDisabled())
547        return;
548}
549
550void GraphicsContext::setPlatformShadow(FloatSize const&, float, Color const&, ColorSpace)
551{
552    notImplemented();
553}
554
555void GraphicsContext::clearPlatformShadow()
556{
557    notImplemented();
558}
559
560void GraphicsContext::beginTransparencyLayer(float)
561{
562    notImplemented();
563}
564
565void GraphicsContext::endTransparencyLayer()
566{
567    notImplemented();
568}
569
570void GraphicsContext::clearRect(const FloatRect&)
571{
572    notImplemented();
573}
574
575void GraphicsContext::strokeRect(const FloatRect&, float)
576{
577    notImplemented();
578}
579
580void GraphicsContext::setLineCap(LineCap)
581{
582    notImplemented();
583}
584
585void GraphicsContext::setLineDash(const DashArray&, float dashOffset)
586{
587    notImplemented();
588}
589
590void GraphicsContext::setLineJoin(LineJoin)
591{
592    notImplemented();
593}
594
595void GraphicsContext::setMiterLimit(float)
596{
597    notImplemented();
598}
599
600void GraphicsContext::setAlpha(float)
601{
602    notImplemented();
603}
604
605void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
606{
607    notImplemented();
608}
609
610#if OS(WINDOWS)
611HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
612{
613    if (dstRect.isEmpty())
614        return 0;
615
616    // Create a bitmap DC in which to draw.
617    BITMAPINFO bitmapInfo;
618    bitmapInfo.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
619    bitmapInfo.bmiHeader.biWidth         = dstRect.width();
620    bitmapInfo.bmiHeader.biHeight        = dstRect.height();
621    bitmapInfo.bmiHeader.biPlanes        = 1;
622    bitmapInfo.bmiHeader.biBitCount      = 32;
623    bitmapInfo.bmiHeader.biCompression   = BI_RGB;
624    bitmapInfo.bmiHeader.biSizeImage     = 0;
625    bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
626    bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
627    bitmapInfo.bmiHeader.biClrUsed       = 0;
628    bitmapInfo.bmiHeader.biClrImportant  = 0;
629
630    void* pixels = 0;
631    HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
632    if (!bitmap)
633        return 0;
634
635    HDC displayDC = ::GetDC(0);
636    HDC bitmapDC = ::CreateCompatibleDC(displayDC);
637    ::ReleaseDC(0, displayDC);
638
639    ::SelectObject(bitmapDC, bitmap);
640
641    // Fill our buffer with clear if we're going to alpha blend.
642    if (supportAlphaBlend) {
643        BITMAP bmpInfo;
644        GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
645        int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
646        memset(bmpInfo.bmBits, 0, bufferSize);
647    }
648    return bitmapDC;
649}
650
651void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
652{
653    if (hdc) {
654
655        if (!dstRect.isEmpty()) {
656
657            HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
658            BITMAP info;
659            GetObject(bitmap, sizeof(info), &info);
660            ASSERT(info.bmBitsPixel == 32);
661
662            wxBitmap bmp;
663            bmp.SetHBITMAP(bitmap);
664#if !wxCHECK_VERSION(2,9,0)
665            if (supportAlphaBlend)
666                bmp.UseAlpha();
667#endif
668            m_data->context->DrawBitmap(bmp, dstRect.x(), dstRect.y(), supportAlphaBlend);
669
670            ::DeleteObject(bitmap);
671        }
672
673        ::DeleteDC(hdc);
674    }
675}
676#endif
677
678}
679