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