1/*
2 * Copyright 2006, The Android Open Source Project
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 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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#include "config.h"
26#include "GraphicsContext.h"
27
28#include "AffineTransform.h"
29#include "Font.h"
30#include "Gradient.h"
31#include "NotImplemented.h"
32#include "Path.h"
33#include "Pattern.h"
34#include "PlatformGraphicsContext.h"
35#include "PlatformGraphicsContextSkia.h"
36#include "SkBitmapRef.h"
37#include "SkBlurDrawLooper.h"
38#include "SkBlurMaskFilter.h"
39#include "SkCanvas.h"
40#include "SkColorPriv.h"
41#include "SkCornerPathEffect.h"
42#include "SkDashPathEffect.h"
43#include "SkDevice.h"
44#include "SkGradientShader.h"
45#include "SkPaint.h"
46#include "SkString.h"
47#include "SkiaUtils.h"
48#include "TransformationMatrix.h"
49
50using namespace std;
51
52namespace WebCore {
53
54// This class just holds onto a PlatformContextSkia for GraphicsContext.
55class GraphicsContextPlatformPrivate {
56    WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate);
57public:
58    GraphicsContextPlatformPrivate(PlatformGraphicsContext* platformContext)
59        : m_context(platformContext) { }
60    ~GraphicsContextPlatformPrivate()
61    {
62        if (m_context && m_context->deleteUs())
63            delete m_context;
64    }
65
66
67    PlatformGraphicsContext* context() { return m_context; }
68
69private:
70    // Non-owning pointer to the PlatformContext.
71    PlatformGraphicsContext* m_context;
72};
73
74static void syncPlatformContext(GraphicsContext* gc)
75{
76    // Stroke and fill sometimes reference each other, so always
77    // sync them both to make sure our state is consistent.
78
79    PlatformGraphicsContext* pgc = gc->platformContext();
80    Gradient* grad = gc->state().fillGradient.get();
81    Pattern* pat = gc->state().fillPattern.get();
82
83    if (grad)
84        pgc->setFillShader(grad->platformGradient());
85    else if (pat)
86        pgc->setFillShader(pat->platformPattern(AffineTransform()));
87    else
88        pgc->setFillColor(gc->state().fillColor);
89
90    grad = gc->state().strokeGradient.get();
91    pat = gc->state().strokePattern.get();
92
93    if (grad)
94        pgc->setStrokeShader(grad->platformGradient());
95    else if (pat)
96        pgc->setStrokeShader(pat->platformPattern(AffineTransform()));
97    else
98        pgc->setStrokeColor(gc->state().strokeColor);
99}
100
101////////////////////////////////////////////////////////////////////////////////////////////////
102
103GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
104{
105    SkBitmap bitmap;
106    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
107    bitmap.allocPixels();
108    bitmap.eraseColor(0);
109
110    PlatformGraphicsContextSkia* pgc =
111        new PlatformGraphicsContextSkia(new SkCanvas(bitmap), true);
112    GraphicsContext* ctx = new GraphicsContext(pgc);
113    return ctx;
114}
115
116////////////////////////////////////////////////////////////////////////////////////////////////
117
118void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
119{
120    if (gc)
121        gc->setGraphicsContext(this);
122    m_data = new GraphicsContextPlatformPrivate(gc);
123    setPaintingDisabled(!gc || gc->isPaintingDisabled());
124}
125
126void GraphicsContext::platformDestroy()
127{
128    delete m_data;
129}
130
131void GraphicsContext::savePlatformState()
132{
133    if (paintingDisabled())
134        return;
135    platformContext()->save();
136}
137
138void GraphicsContext::restorePlatformState()
139{
140    if (paintingDisabled())
141        return;
142    platformContext()->restore();
143}
144
145bool GraphicsContext::willFill() const
146{
147    return m_state.fillColor.rgb();
148}
149
150bool GraphicsContext::willStroke() const
151{
152    return m_state.strokeColor.rgb();
153}
154
155// Draws a filled rectangle with a stroked border.
156void GraphicsContext::drawRect(const IntRect& rect)
157{
158    if (paintingDisabled())
159        return;
160
161    syncPlatformContext(this);
162    platformContext()->drawRect(rect);
163}
164
165// This is only used to draw borders.
166void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
167{
168    if (paintingDisabled())
169        return;
170
171    syncPlatformContext(this);
172    platformContext()->drawLine(point1, point2);
173}
174
175void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool /* printing */)
176{
177    if (paintingDisabled())
178        return;
179
180    syncPlatformContext(this);
181    platformContext()->drawLineForText(pt, width);
182}
183
184void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width,
185                                              TextCheckingLineStyle style)
186{
187    if (paintingDisabled())
188        return;
189
190    syncPlatformContext(this);
191    platformContext()->drawLineForTextChecking(pt, width, style);
192}
193
194// This method is only used to draw the little circles used in lists.
195void GraphicsContext::drawEllipse(const IntRect& rect)
196{
197    if (paintingDisabled())
198        return;
199
200    syncPlatformContext(this);
201    platformContext()->drawEllipse(rect);
202}
203
204void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
205{
206    if (paintingDisabled())
207        return;
208
209    syncPlatformContext(this);
210    platformContext()->strokeArc(r, startAngle, angleSpan);
211}
212
213void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points,
214                                        bool shouldAntialias)
215{
216    if (paintingDisabled())
217        return;
218
219    syncPlatformContext(this);
220    platformContext()->drawConvexPolygon(numPoints, points, shouldAntialias);
221}
222
223void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
224                                      const IntSize& bottomLeft, const IntSize& bottomRight,
225                                      const Color& color, ColorSpace colorSpace)
226{
227    if (paintingDisabled())
228        return;
229
230    syncPlatformContext(this);
231    platformContext()->fillRoundedRect(rect, topLeft, topRight,
232            bottomLeft, bottomRight, color, colorSpace);
233}
234
235void GraphicsContext::fillRect(const FloatRect& rect)
236{
237    if (paintingDisabled())
238        return;
239
240    syncPlatformContext(this);
241    platformContext()->fillRect(rect);
242}
243
244void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
245{
246    if (paintingDisabled())
247        return;
248
249    syncPlatformContext(this);
250    platformContext()->fillRect(rect, color, colorSpace);
251}
252
253void GraphicsContext::clip(const FloatRect& rect)
254{
255    if (paintingDisabled())
256        return;
257
258    platformContext()->clip(rect);
259}
260
261void GraphicsContext::clip(const Path& path)
262{
263    if (paintingDisabled())
264        return;
265
266    platformContext()->clip(path);
267}
268
269void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
270{
271    if (paintingDisabled())
272        return;
273
274    platformContext()->addInnerRoundedRectClip(rect, thickness);
275}
276
277void GraphicsContext::canvasClip(const Path& path)
278{
279    if (paintingDisabled())
280        return;
281
282    platformContext()->canvasClip(path);
283}
284
285void GraphicsContext::clipOut(const IntRect& r)
286{
287    if (paintingDisabled())
288        return;
289
290    platformContext()->clipOut(r);
291}
292
293#if ENABLE(SVG)
294void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
295{
296    if (paintingDisabled())
297        return;
298
299    platformContext()->clipPath(pathToClip, clipRule);
300}
301#endif
302
303void GraphicsContext::clipOut(const Path& p)
304{
305    if (paintingDisabled())
306        return;
307
308    platformContext()->clipOut(p);
309}
310
311//////////////////////////////////////////////////////////////////////////////////////////////////
312
313#if SVG_SUPPORT
314KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
315{
316    return new KRenderingDeviceContextQuartz(platformContext());
317}
318#endif
319
320void GraphicsContext::beginTransparencyLayer(float opacity)
321{
322    if (paintingDisabled())
323        return;
324
325    platformContext()->beginTransparencyLayer(opacity);
326}
327
328void GraphicsContext::endTransparencyLayer()
329{
330    if (paintingDisabled())
331        return;
332
333    platformContext()->endTransparencyLayer();
334}
335
336///////////////////////////////////////////////////////////////////////////
337
338void GraphicsContext::setupFillPaint(SkPaint* paint)
339{
340    if (paintingDisabled())
341        return;
342    syncPlatformContext(this);
343    platformContext()->setupPaintFill(paint);
344}
345
346void GraphicsContext::setupStrokePaint(SkPaint* paint)
347{
348    if (paintingDisabled())
349        return;
350    syncPlatformContext(this);
351    platformContext()->setupPaintStroke(paint, 0);
352}
353
354bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset)
355{
356    if (paintingDisabled())
357        return false;
358    syncPlatformContext(this);
359    return platformContext()->setupPaintShadow(paint, offset);
360}
361
362void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace)
363{
364}
365
366void GraphicsContext::setPlatformStrokeThickness(float f)
367{
368    if (paintingDisabled())
369        return;
370    platformContext()->setStrokeThickness(f);
371}
372
373void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style)
374{
375    if (paintingDisabled())
376        return;
377    platformContext()->setStrokeStyle(style);
378}
379
380void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace)
381{
382}
383
384void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
385{
386    if (paintingDisabled())
387        return;
388
389    if (blur <= 0)
390        this->clearPlatformShadow();
391
392    SkColor c;
393    if (color.isValid())
394        c = color.rgb();
395    else
396        c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color
397    platformContext()->setShadow(blur, size.width(), size.height(), c);
398}
399
400void GraphicsContext::clearPlatformShadow()
401{
402    if (paintingDisabled())
403        return;
404
405    platformContext()->setShadow(0, 0, 0, 0);
406}
407
408///////////////////////////////////////////////////////////////////////////////
409
410void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
411{
412    if (paintingDisabled())
413        return;
414
415    syncPlatformContext(this);
416    platformContext()->drawFocusRing(rects, width, offset, color);
417}
418
419void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
420{
421    // Do nothing, since we draw the focus ring independently.
422}
423
424PlatformGraphicsContext* GraphicsContext::platformContext() const
425{
426    ASSERT(!paintingDisabled());
427    return m_data->context();
428}
429
430void GraphicsContext::setMiterLimit(float limit)
431{
432    if (paintingDisabled())
433        return;
434    platformContext()->setMiterLimit(limit);
435}
436
437void GraphicsContext::setAlpha(float alpha)
438{
439    if (paintingDisabled())
440        return;
441    platformContext()->setAlpha(alpha);
442}
443
444void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
445{
446    if (paintingDisabled())
447        return;
448    platformContext()->setCompositeOperation(op);
449}
450
451void GraphicsContext::clearRect(const FloatRect& rect)
452{
453    if (paintingDisabled())
454        return;
455
456    syncPlatformContext(this);
457    platformContext()->clearRect(rect);
458}
459
460void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
461{
462    if (paintingDisabled())
463        return;
464
465    syncPlatformContext(this);
466    platformContext()->strokeRect(rect, lineWidth);
467}
468
469void GraphicsContext::setLineCap(LineCap cap)
470{
471    if (paintingDisabled())
472        return;
473    platformContext()->setLineCap(cap);
474}
475
476#if ENABLE(SVG)
477void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
478{
479    if (paintingDisabled())
480        return;
481
482    platformContext()->setLineDash(dashes, dashOffset);
483}
484#endif
485
486void GraphicsContext::setLineJoin(LineJoin join)
487{
488    if (paintingDisabled())
489        return;
490    platformContext()->setLineJoin(join);
491}
492
493void GraphicsContext::scale(const FloatSize& size)
494{
495    if (paintingDisabled())
496        return;
497    platformContext()->scale(size);
498}
499
500void GraphicsContext::rotate(float angleInRadians)
501{
502    if (paintingDisabled())
503        return;
504    platformContext()->rotate(angleInRadians);
505}
506
507void GraphicsContext::translate(float x, float y)
508{
509    if (paintingDisabled())
510        return;
511    if (!x && !y)
512        return;
513    platformContext()->translate(x, y);
514}
515
516void GraphicsContext::concatCTM(const AffineTransform& affine)
517{
518    if (paintingDisabled())
519        return;
520    platformContext()->concatCTM(affine);
521}
522
523// This is intended to round the rect to device pixels (through the CTM)
524// and then invert the result back into source space, with the hope that when
525// it is drawn (through the matrix), it will land in the "right" place (i.e.
526// on pixel boundaries).
527
528// For android, we record this geometry once and then draw it though various
529// scale factors as the user zooms, without re-recording. Thus this routine
530// should just leave the original geometry alone.
531
532// If we instead draw into bitmap tiles, we should then perform this
533// transform -> round -> inverse step.
534
535FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
536{
537    return rect;
538}
539
540//////////////////////////////////////////////////////////////////////////////////////////////////
541
542void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
543{
544// Appears to be PDF specific, so we ignore it
545}
546
547void GraphicsContext::setPlatformShouldAntialias(bool useAA)
548{
549    if (paintingDisabled())
550        return;
551    platformContext()->setShouldAntialias(useAA);
552}
553
554void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient)
555{
556}
557
558void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern)
559{
560}
561
562void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient)
563{
564}
565
566void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern)
567{
568}
569
570AffineTransform GraphicsContext::getCTM() const
571{
572    if (paintingDisabled())
573        return AffineTransform();
574    const SkMatrix& m = platformContext()->getTotalMatrix();
575    return AffineTransform(SkScalarToDouble(m.getScaleX()), // a
576                           SkScalarToDouble(m.getSkewY()), // b
577                           SkScalarToDouble(m.getSkewX()), // c
578                           SkScalarToDouble(m.getScaleY()), // d
579                           SkScalarToDouble(m.getTranslateX()), // e
580                           SkScalarToDouble(m.getTranslateY())); // f
581}
582
583void GraphicsContext::setCTM(const AffineTransform& transform)
584{
585    // The SkPicture mode of Skia does not support SkCanvas::setMatrix(), so we
586    // can not simply use that method here. We could calculate the transform
587    // required to achieve the desired matrix and use SkCanvas::concat(), but
588    // there's currently no need for this.
589    ASSERT_NOT_REACHED();
590}
591
592///////////////////////////////////////////////////////////////////////////////
593
594void GraphicsContext::fillPath(const Path& pathToFill)
595{
596    if (paintingDisabled())
597        return;
598
599    syncPlatformContext(this);
600    platformContext()->fillPath(pathToFill, fillRule());
601}
602
603void GraphicsContext::strokePath(const Path& pathToStroke)
604{
605    if (paintingDisabled())
606        return;
607
608    syncPlatformContext(this);
609    platformContext()->strokePath(pathToStroke);
610}
611
612InterpolationQuality GraphicsContext::imageInterpolationQuality() const
613{
614    notImplemented();
615    return InterpolationDefault;
616}
617
618void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
619{
620#if 0
621    enum InterpolationQuality {
622        InterpolationDefault,
623        InterpolationNone,
624        InterpolationLow,
625        InterpolationMedium,
626        InterpolationHigh
627    };
628#endif
629    // TODO: record this, so we can know when to use bitmap-filtering when we draw
630    // ... not sure how meaningful this will be given our playback model.
631
632    // Certainly safe to do nothing for the present.
633}
634
635void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*,
636                                        bool antialias)
637{
638    if (paintingDisabled())
639        return;
640
641    if (numPoints <= 1)
642        return;
643
644    // FIXME: IMPLEMENT!
645}
646
647void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run,
648                                           const FloatPoint& point, int h,
649                                           const Color& backgroundColor,
650                                           ColorSpace colorSpace, int from,
651                                           int to, bool isActive)
652{
653    if (paintingDisabled())
654        return;
655
656    syncPlatformContext(this);
657    platformContext()->drawHighlightForText(font, run, point, h, backgroundColor,
658            colorSpace, from, to, isActive);
659}
660
661} // namespace WebCore
662