1/*
2 * Copyright (C) 2006 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4 * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
5 * Copyright (C) 2008 Nuanti Ltd.
6 * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org>
7 * Copyright (C) 2010, 2011 Igalia S.L.
8 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 * Copyright (C) 2012, Intel Corporation
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "config.h"
34#include "GraphicsContext.h"
35
36#if USE(CAIRO)
37
38#include "AffineTransform.h"
39#include "CairoOperations.h"
40#include "FloatRect.h"
41#include "FloatRoundedRect.h"
42#include "GraphicsContextImpl.h"
43#include "GraphicsContextPlatformPrivateCairo.h"
44#include "ImageBuffer.h"
45#include "IntRect.h"
46#include "NotImplemented.h"
47#include "PlatformContextCairo.h"
48#include "RefPtrCairo.h"
49
50#if PLATFORM(WIN)
51#include <cairo-win32.h>
52#endif
53
54
55namespace WebCore {
56
57void GraphicsContext::platformInit(PlatformContextCairo* platformContext)
58{
59 if (!platformContext)
60 return;
61
62 m_data = new GraphicsContextPlatformPrivate(*platformContext);
63 m_data->platformContext.setGraphicsContextPrivate(m_data);
64 m_data->syncContext(platformContext->cr());
65}
66
67void GraphicsContext::platformDestroy()
68{
69 if (m_data) {
70 m_data->platformContext.setGraphicsContextPrivate(nullptr);
71 delete m_data;
72 }
73}
74
75AffineTransform GraphicsContext::getCTM(IncludeDeviceScale includeScale) const
76{
77 if (paintingDisabled())
78 return AffineTransform();
79
80 if (m_impl)
81 return m_impl->getCTM(includeScale);
82
83 ASSERT(hasPlatformContext());
84 return Cairo::State::getCTM(*platformContext());
85}
86
87PlatformContextCairo* GraphicsContext::platformContext() const
88{
89 if (m_impl)
90 return m_impl->platformContext();
91 return &m_data->platformContext;
92}
93
94void GraphicsContext::savePlatformState()
95{
96 ASSERT(hasPlatformContext());
97 Cairo::save(*platformContext());
98}
99
100void GraphicsContext::restorePlatformState()
101{
102 ASSERT(hasPlatformContext());
103 Cairo::restore(*platformContext());
104}
105
106// Draws a filled rectangle with a stroked border.
107void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness)
108{
109 if (paintingDisabled())
110 return;
111
112 if (m_impl) {
113 m_impl->drawRect(rect, borderThickness);
114 return;
115 }
116
117 ASSERT(!rect.isEmpty());
118 ASSERT(hasPlatformContext());
119 auto& state = this->state();
120 Cairo::drawRect(*platformContext(), rect, borderThickness, state.fillColor, state.strokeStyle, state.strokeColor);
121}
122
123void GraphicsContext::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOperator, BlendMode blendMode, ImageOrientation orientation)
124{
125 if (paintingDisabled())
126 return;
127
128 if (m_impl) {
129 m_impl->drawNativeImage(image, imageSize, destRect, srcRect, compositeOperator, blendMode, orientation);
130 return;
131 }
132
133 ASSERT(hasPlatformContext());
134 auto& state = this->state();
135 Cairo::drawNativeImage(*platformContext(), image.get(), destRect, srcRect, compositeOperator, blendMode, orientation, state.imageInterpolationQuality, state.alpha, Cairo::ShadowState(state));
136}
137
138// This is only used to draw borders, so we should not draw shadows.
139void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2)
140{
141 if (paintingDisabled())
142 return;
143
144 if (strokeStyle() == NoStroke)
145 return;
146
147 if (m_impl) {
148 m_impl->drawLine(point1, point2);
149 return;
150 }
151
152 ASSERT(hasPlatformContext());
153 auto& state = this->state();
154 Cairo::drawLine(*platformContext(), point1, point2, state.strokeStyle, state.strokeColor, state.strokeThickness, state.shouldAntialias);
155}
156
157// This method is only used to draw the little circles used in lists.
158void GraphicsContext::drawEllipse(const FloatRect& rect)
159{
160 if (paintingDisabled())
161 return;
162
163 if (m_impl) {
164 m_impl->drawEllipse(rect);
165 return;
166 }
167
168 ASSERT(hasPlatformContext());
169 auto& state = this->state();
170 Cairo::drawEllipse(*platformContext(), rect, state.fillColor, state.strokeStyle, state.strokeColor, state.strokeThickness);
171}
172
173void GraphicsContext::fillPath(const Path& path)
174{
175 if (paintingDisabled() || path.isEmpty())
176 return;
177
178 if (m_impl) {
179 m_impl->fillPath(path);
180 return;
181 }
182
183 ASSERT(hasPlatformContext());
184 auto& state = this->state();
185 Cairo::fillPath(*platformContext(), path, Cairo::FillSource(state), Cairo::ShadowState(state));
186}
187
188void GraphicsContext::strokePath(const Path& path)
189{
190 if (paintingDisabled() || path.isEmpty())
191 return;
192
193 if (m_impl) {
194 m_impl->strokePath(path);
195 return;
196 }
197
198 ASSERT(hasPlatformContext());
199 auto& state = this->state();
200 Cairo::strokePath(*platformContext(), path, Cairo::StrokeSource(state), Cairo::ShadowState(state));
201}
202
203void GraphicsContext::fillRect(const FloatRect& rect)
204{
205 if (paintingDisabled())
206 return;
207
208 if (m_impl) {
209 m_impl->fillRect(rect);
210 return;
211 }
212
213 ASSERT(hasPlatformContext());
214 auto& state = this->state();
215 Cairo::fillRect(*platformContext(), rect, Cairo::FillSource(state), Cairo::ShadowState(state));
216}
217
218void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
219{
220 if (paintingDisabled())
221 return;
222
223 if (m_impl) {
224 m_impl->fillRect(rect, color);
225 return;
226 }
227
228 ASSERT(hasPlatformContext());
229 Cairo::fillRect(*platformContext(), rect, color, Cairo::ShadowState(state()));
230}
231
232void GraphicsContext::clip(const FloatRect& rect)
233{
234 if (paintingDisabled())
235 return;
236
237 if (m_impl) {
238 m_impl->clip(rect);
239 return;
240 }
241
242 ASSERT(hasPlatformContext());
243 Cairo::clip(*platformContext(), rect);
244}
245
246void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
247{
248 if (paintingDisabled())
249 return;
250
251 if (m_impl) {
252 m_impl->clipPath(path, clipRule);
253 return;
254 }
255
256 ASSERT(hasPlatformContext());
257 Cairo::clipPath(*platformContext(), path, clipRule);
258}
259
260void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
261{
262 if (paintingDisabled())
263 return;
264
265 if (m_impl) {
266 m_impl->clipToImageBuffer(buffer, destRect);
267 return;
268 }
269
270 RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore);
271 if (!image)
272 return;
273
274 ASSERT(hasPlatformContext());
275 if (auto surface = image->nativeImageForCurrentFrame())
276 Cairo::clipToImageBuffer(*platformContext(), surface.get(), destRect);
277}
278
279IntRect GraphicsContext::clipBounds() const
280{
281 if (paintingDisabled())
282 return IntRect();
283
284 if (m_impl)
285 return m_impl->clipBounds();
286
287 ASSERT(hasPlatformContext());
288 return Cairo::State::getClipBounds(*platformContext());
289}
290
291void GraphicsContext::drawFocusRing(const Path& path, float width, float offset, const Color& color)
292{
293 if (paintingDisabled())
294 return;
295
296 if (m_impl) {
297 m_impl->drawFocusRing(path, width, offset, color);
298 return;
299 }
300
301 ASSERT(hasPlatformContext());
302 Cairo::drawFocusRing(*platformContext(), path, width, color);
303}
304
305void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
306{
307 if (paintingDisabled())
308 return;
309
310 if (m_impl) {
311 m_impl->drawFocusRing(rects, width, offset, color);
312 return;
313 }
314
315 ASSERT(hasPlatformContext());
316 Cairo::drawFocusRing(*platformContext(), rects, width, color);
317}
318
319void GraphicsContext::drawLineForText(const FloatRect& rect, bool printing, bool doubleUnderlines, StrokeStyle)
320{
321 drawLinesForText(rect.location(), rect.height(), DashArray { 0, rect.width() }, printing, doubleUnderlines);
322}
323
324void GraphicsContext::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleUnderlines, StrokeStyle)
325{
326 if (paintingDisabled())
327 return;
328
329 if (widths.isEmpty())
330 return;
331
332 if (m_impl) {
333 m_impl->drawLinesForText(point, thickness, widths, printing, doubleUnderlines);
334 return;
335 }
336
337 ASSERT(hasPlatformContext());
338 Cairo::drawLinesForText(*platformContext(), point, thickness, widths, printing, doubleUnderlines, m_state.strokeColor);
339}
340
341void GraphicsContext::drawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style)
342{
343 if (paintingDisabled())
344 return;
345
346 if (m_impl) {
347 m_impl->drawDotsForDocumentMarker(rect, style);
348 return;
349 }
350
351 ASSERT(hasPlatformContext());
352 Cairo::drawDotsForDocumentMarker(*platformContext(), rect, style);
353}
354
355FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
356{
357 if (paintingDisabled())
358 return rect;
359
360 if (m_impl)
361 return m_impl->roundToDevicePixels(rect, roundingMode);
362
363 return Cairo::State::roundToDevicePixels(*platformContext(), rect);
364}
365
366void GraphicsContext::translate(float x, float y)
367{
368 if (paintingDisabled())
369 return;
370
371 if (m_impl) {
372 m_impl->translate(x, y);
373 return;
374 }
375
376 ASSERT(hasPlatformContext());
377 Cairo::translate(*platformContext(), x, y);
378}
379
380void GraphicsContext::setPlatformFillColor(const Color&)
381{
382 // Cairo contexts can't hold separate fill and stroke colors
383 // so we set them just before we actually fill or stroke
384}
385
386void GraphicsContext::setPlatformStrokeColor(const Color&)
387{
388 // Cairo contexts can't hold separate fill and stroke colors
389 // so we set them just before we actually fill or stroke
390}
391
392void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
393{
394 if (paintingDisabled())
395 return;
396
397 ASSERT(hasPlatformContext());
398 Cairo::State::setStrokeThickness(*platformContext(), strokeThickness);
399}
400
401void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
402{
403 if (paintingDisabled())
404 return;
405
406 ASSERT(hasPlatformContext());
407 Cairo::State::setStrokeStyle(*platformContext(), strokeStyle);
408}
409
410void GraphicsContext::setURLForRect(const URL&, const FloatRect&)
411{
412 notImplemented();
413}
414
415void GraphicsContext::concatCTM(const AffineTransform& transform)
416{
417 if (paintingDisabled())
418 return;
419
420 if (m_impl) {
421 m_impl->concatCTM(transform);
422 return;
423 }
424
425 ASSERT(hasPlatformContext());
426 Cairo::concatCTM(*platformContext(), transform);
427}
428
429void GraphicsContext::setCTM(const AffineTransform& transform)
430{
431 if (paintingDisabled())
432 return;
433
434 if (m_impl) {
435 m_impl->setCTM(transform);
436 return;
437 }
438
439 ASSERT(hasPlatformContext());
440 Cairo::State::setCTM(*platformContext(), transform);
441}
442
443void GraphicsContext::setPlatformShadow(const FloatSize& offset, float, const Color&)
444{
445 if (m_state.shadowsIgnoreTransforms) {
446 // Meaning that this graphics context is associated with a CanvasRenderingContext
447 // We flip the height since CG and HTML5 Canvas have opposite Y axis
448 m_state.shadowOffset = { offset.width(), -offset.height() };
449 }
450}
451
452void GraphicsContext::clearPlatformShadow()
453{
454}
455
456void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
457{
458 if (paintingDisabled())
459 return;
460
461 ASSERT(hasPlatformContext());
462 Cairo::beginTransparencyLayer(*platformContext(), opacity);
463}
464
465void GraphicsContext::endPlatformTransparencyLayer()
466{
467 if (paintingDisabled())
468 return;
469
470 ASSERT(hasPlatformContext());
471 Cairo::endTransparencyLayer(*platformContext());
472}
473
474bool GraphicsContext::supportsTransparencyLayers()
475{
476 return true;
477}
478
479void GraphicsContext::clearRect(const FloatRect& rect)
480{
481 if (paintingDisabled())
482 return;
483
484 if (m_impl) {
485 m_impl->clearRect(rect);
486 return;
487 }
488
489 ASSERT(hasPlatformContext());
490 Cairo::clearRect(*platformContext(), rect);
491}
492
493void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
494{
495 if (paintingDisabled())
496 return;
497
498 if (m_impl) {
499 m_impl->strokeRect(rect, lineWidth);
500 return;
501 }
502
503 ASSERT(hasPlatformContext());
504 auto& state = this->state();
505 Cairo::strokeRect(*platformContext(), rect, lineWidth, Cairo::StrokeSource(state), Cairo::ShadowState(state));
506}
507
508void GraphicsContext::setLineCap(LineCap lineCap)
509{
510 if (paintingDisabled())
511 return;
512
513 if (m_impl) {
514 m_impl->setLineCap(lineCap);
515 return;
516 }
517
518 ASSERT(hasPlatformContext());
519 Cairo::setLineCap(*platformContext(), lineCap);
520}
521
522void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
523{
524 if (paintingDisabled())
525 return;
526
527 if (m_impl) {
528 m_impl->setLineDash(dashes, dashOffset);
529 return;
530 }
531
532 ASSERT(hasPlatformContext());
533 Cairo::setLineDash(*platformContext(), dashes, dashOffset);
534}
535
536void GraphicsContext::setLineJoin(LineJoin lineJoin)
537{
538 if (paintingDisabled())
539 return;
540
541 if (m_impl) {
542 m_impl->setLineJoin(lineJoin);
543 return;
544 }
545
546 ASSERT(hasPlatformContext());
547 Cairo::setLineJoin(*platformContext(), lineJoin);
548}
549
550void GraphicsContext::setMiterLimit(float miter)
551{
552 if (paintingDisabled())
553 return;
554
555 if (m_impl) {
556 // Maybe this should be part of the state.
557 m_impl->setMiterLimit(miter);
558 return;
559 }
560
561 ASSERT(hasPlatformContext());
562 Cairo::setMiterLimit(*platformContext(), miter);
563}
564
565void GraphicsContext::setPlatformAlpha(float)
566{
567}
568
569void GraphicsContext::setPlatformCompositeOperation(CompositeOperator compositeOperator, BlendMode blendMode)
570{
571 if (paintingDisabled())
572 return;
573
574 ASSERT(hasPlatformContext());
575 Cairo::State::setCompositeOperation(*platformContext(), compositeOperator, blendMode);
576}
577
578void GraphicsContext::canvasClip(const Path& path, WindRule windRule)
579{
580 clipPath(path, windRule);
581}
582
583void GraphicsContext::clipOut(const Path& path)
584{
585 if (paintingDisabled())
586 return;
587
588 if (m_impl) {
589 m_impl->clipOut(path);
590 return;
591 }
592
593 ASSERT(hasPlatformContext());
594 Cairo::clipOut(*platformContext(), path);
595}
596
597void GraphicsContext::rotate(float radians)
598{
599 if (paintingDisabled())
600 return;
601
602 if (m_impl) {
603 m_impl->rotate(radians);
604 return;
605 }
606
607 ASSERT(hasPlatformContext());
608 Cairo::rotate(*platformContext(), radians);
609}
610
611void GraphicsContext::scale(const FloatSize& size)
612{
613 if (paintingDisabled())
614 return;
615
616 if (m_impl) {
617 m_impl->scale(size);
618 return;
619 }
620
621 ASSERT(hasPlatformContext());
622 Cairo::scale(*platformContext(), size);
623}
624
625void GraphicsContext::clipOut(const FloatRect& rect)
626{
627 if (paintingDisabled())
628 return;
629
630 if (m_impl) {
631 m_impl->clipOut(rect);
632 return;
633 }
634
635 ASSERT(hasPlatformContext());
636 Cairo::clipOut(*platformContext(), rect);
637}
638
639void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color)
640{
641 if (paintingDisabled())
642 return;
643
644 ASSERT(hasPlatformContext());
645 Cairo::fillRoundedRect(*platformContext(), rect, color, Cairo::ShadowState(state()));
646}
647
648void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
649{
650 if (paintingDisabled() || !color.isValid())
651 return;
652
653 if (m_impl) {
654 m_impl->fillRectWithRoundedHole(rect, roundedHoleRect, color);
655 return;
656 }
657
658 ASSERT(hasPlatformContext());
659 auto& state = this->state();
660 Cairo::fillRectWithRoundedHole(*platformContext(), rect, roundedHoleRect, Cairo::FillSource(state), Cairo::ShadowState(state));
661}
662
663void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOperator, BlendMode blendMode)
664{
665 if (paintingDisabled())
666 return;
667
668 if (m_impl) {
669 m_impl->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, compositeOperator, blendMode);
670 return;
671 }
672
673 ASSERT(hasPlatformContext());
674 if (auto surface = image.nativeImageForCurrentFrame())
675 Cairo::drawPattern(*platformContext(), surface.get(), IntSize(image.size()), destRect, tileRect, patternTransform, phase, compositeOperator, blendMode);
676}
677
678void GraphicsContext::setPlatformShouldAntialias(bool enable)
679{
680 if (paintingDisabled())
681 return;
682
683 ASSERT(hasPlatformContext());
684 Cairo::State::setShouldAntialias(*platformContext(), enable);
685}
686
687void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality)
688{
689}
690
691bool GraphicsContext::isAcceleratedContext() const
692{
693 if (!hasPlatformContext())
694 return false;
695
696 return Cairo::State::isAcceleratedContext(*platformContext());
697}
698
699} // namespace WebCore
700
701#endif // USE(CAIRO)
702