1 /*
2 * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
3 * Copyright (C) 2006 Zack Rusin <zack@kde.org>
4 * Copyright (C) 2006 George Staikos <staikos@kde.org>
5 * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
6 * Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
7 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
8 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
9 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
10 * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de>
11 *
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
31 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include "config.h"
37 #include "GraphicsContext.h"
38
39 #ifdef Q_WS_WIN
40 #include <windows.h>
41 #endif
42
43 #include "AffineTransform.h"
44 #include "Color.h"
45 #include "FloatConversion.h"
46 #include "Font.h"
47 #include "GraphicsContextPrivate.h"
48 #include "ImageBuffer.h"
49 #include "NotImplemented.h"
50 #include "Path.h"
51 #include "Pattern.h"
52 #include "Pen.h"
53
54 #include <QBrush>
55 #include <QDebug>
56 #include <QGradient>
57 #include <QPaintDevice>
58 #include <QPaintEngine>
59 #include <QPainter>
60 #include <QPainterPath>
61 #include <QPixmap>
62 #include <QPolygonF>
63 #include <QStack>
64 #include <QVector>
65
66 #ifndef M_PI
67 #define M_PI 3.14159265358979323846
68 #endif
69
70 namespace WebCore {
71
toQtCompositionMode(CompositeOperator op)72 static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
73 {
74 switch (op) {
75 case CompositeClear:
76 return QPainter::CompositionMode_Clear;
77 case CompositeCopy:
78 return QPainter::CompositionMode_Source;
79 case CompositeSourceOver:
80 return QPainter::CompositionMode_SourceOver;
81 case CompositeSourceIn:
82 return QPainter::CompositionMode_SourceIn;
83 case CompositeSourceOut:
84 return QPainter::CompositionMode_SourceOut;
85 case CompositeSourceAtop:
86 return QPainter::CompositionMode_SourceAtop;
87 case CompositeDestinationOver:
88 return QPainter::CompositionMode_DestinationOver;
89 case CompositeDestinationIn:
90 return QPainter::CompositionMode_DestinationIn;
91 case CompositeDestinationOut:
92 return QPainter::CompositionMode_DestinationOut;
93 case CompositeDestinationAtop:
94 return QPainter::CompositionMode_DestinationAtop;
95 case CompositeXOR:
96 return QPainter::CompositionMode_Xor;
97 case CompositePlusDarker:
98 // there is no exact match, but this is the closest
99 return QPainter::CompositionMode_Darken;
100 case CompositeHighlight:
101 return QPainter::CompositionMode_SourceOver;
102 case CompositePlusLighter:
103 return QPainter::CompositionMode_Plus;
104 }
105
106 return QPainter::CompositionMode_SourceOver;
107 }
108
toQtLineCap(LineCap lc)109 static inline Qt::PenCapStyle toQtLineCap(LineCap lc)
110 {
111 switch (lc) {
112 case ButtCap:
113 return Qt::FlatCap;
114 case RoundCap:
115 return Qt::RoundCap;
116 case SquareCap:
117 return Qt::SquareCap;
118 }
119
120 return Qt::FlatCap;
121 }
122
toQtLineJoin(LineJoin lj)123 static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
124 {
125 switch (lj) {
126 case MiterJoin:
127 return Qt::SvgMiterJoin;
128 case RoundJoin:
129 return Qt::RoundJoin;
130 case BevelJoin:
131 return Qt::BevelJoin;
132 }
133
134 return Qt::MiterJoin;
135 }
136
toQPenStyle(StrokeStyle style)137 static Qt::PenStyle toQPenStyle(StrokeStyle style)
138 {
139 switch (style) {
140 case NoStroke:
141 return Qt::NoPen;
142 break;
143 case SolidStroke:
144 return Qt::SolidLine;
145 break;
146 case DottedStroke:
147 return Qt::DotLine;
148 break;
149 case DashedStroke:
150 return Qt::DashLine;
151 break;
152 }
153 qWarning("couldn't recognize the pen style");
154 return Qt::NoPen;
155 }
156
toQtFillRule(WindRule rule)157 static inline Qt::FillRule toQtFillRule(WindRule rule)
158 {
159 switch (rule) {
160 case RULE_EVENODD:
161 return Qt::OddEvenFill;
162 case RULE_NONZERO:
163 return Qt::WindingFill;
164 }
165 qDebug("Qt: unrecognized wind rule!");
166 return Qt::OddEvenFill;
167 }
168
169 struct TransparencyLayer : FastAllocBase {
TransparencyLayerWebCore::TransparencyLayer170 TransparencyLayer(const QPainter* p, const QRect &rect)
171 : pixmap(rect.width(), rect.height())
172 {
173 offset = rect.topLeft();
174 pixmap.fill(Qt::transparent);
175 painter.begin(&pixmap);
176 painter.setRenderHint(QPainter::Antialiasing, p->testRenderHint(QPainter::Antialiasing));
177 painter.translate(-offset);
178 painter.setPen(p->pen());
179 painter.setBrush(p->brush());
180 painter.setTransform(p->transform(), true);
181 painter.setOpacity(p->opacity());
182 painter.setFont(p->font());
183 if (painter.paintEngine()->hasFeature(QPaintEngine::PorterDuff))
184 painter.setCompositionMode(p->compositionMode());
185 painter.setClipPath(p->clipPath());
186 }
187
TransparencyLayerWebCore::TransparencyLayer188 TransparencyLayer()
189 {
190 }
191
192 QPixmap pixmap;
193 QPoint offset;
194 QPainter painter;
195 qreal opacity;
196 private:
TransparencyLayerWebCore::TransparencyLayer197 TransparencyLayer(const TransparencyLayer &) {}
operator =WebCore::TransparencyLayer198 TransparencyLayer & operator=(const TransparencyLayer &) { return *this; }
199 };
200
201 class GraphicsContextPlatformPrivate : public Noncopyable {
202 public:
203 GraphicsContextPlatformPrivate(QPainter* painter);
204 ~GraphicsContextPlatformPrivate();
205
p()206 inline QPainter* p()
207 {
208 if (layers.isEmpty()) {
209 if (redirect)
210 return redirect;
211
212 return painter;
213 }
214 return &layers.top()->painter;
215 }
216
217 bool antiAliasingForRectsAndLines;
218
219 QStack<TransparencyLayer*> layers;
220 QPainter* redirect;
221
222 QBrush solidColor;
223
224 InterpolationQuality imageInterpolationQuality;
225
226 // Only used by SVG for now.
227 QPainterPath currentPath;
228
229 private:
230 QPainter* painter;
231 };
232
233
GraphicsContextPlatformPrivate(QPainter * p)234 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
235 {
236 painter = p;
237 redirect = 0;
238
239 solidColor = QBrush(Qt::black);
240
241 imageInterpolationQuality = InterpolationDefault;
242
243 if (painter) {
244 // use the default the QPainter was constructed with
245 antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
246 // FIXME: Maybe only enable in SVG mode?
247 painter->setRenderHint(QPainter::Antialiasing, true);
248 } else
249 antiAliasingForRectsAndLines = false;
250 }
251
~GraphicsContextPlatformPrivate()252 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
253 {
254 }
255
GraphicsContext(PlatformGraphicsContext * context)256 GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
257 : m_common(createGraphicsContextPrivate())
258 , m_data(new GraphicsContextPlatformPrivate(context))
259 {
260 setPaintingDisabled(!context);
261 if (context) {
262 // Make sure the context starts in sync with our state.
263 setPlatformFillColor(fillColor(), DeviceColorSpace);
264 setPlatformStrokeColor(strokeColor(), DeviceColorSpace);
265 }
266 }
267
~GraphicsContext()268 GraphicsContext::~GraphicsContext()
269 {
270 while (!m_data->layers.isEmpty())
271 endTransparencyLayer();
272
273 destroyGraphicsContextPrivate(m_common);
274 delete m_data;
275 }
276
platformContext() const277 PlatformGraphicsContext* GraphicsContext::platformContext() const
278 {
279 return m_data->p();
280 }
281
getCTM() const282 AffineTransform GraphicsContext::getCTM() const
283 {
284 QTransform matrix(platformContext()->combinedTransform());
285 return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
286 matrix.m22(), matrix.dx(), matrix.dy());
287 }
288
savePlatformState()289 void GraphicsContext::savePlatformState()
290 {
291 m_data->p()->save();
292 }
293
restorePlatformState()294 void GraphicsContext::restorePlatformState()
295 {
296 m_data->p()->restore();
297
298 if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) {
299 QTransform matrix = m_common->state.pathTransform;
300 m_data->currentPath = m_data->currentPath * matrix;
301 }
302 }
303
304 // Draws a filled rectangle with a stroked border.
drawRect(const IntRect & rect)305 void GraphicsContext::drawRect(const IntRect& rect)
306 {
307 if (paintingDisabled())
308 return;
309
310 QPainter* p = m_data->p();
311 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
312 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
313
314 IntSize shadowSize;
315 int shadowBlur;
316 Color shadowColor;
317 if (getShadow(shadowSize, shadowBlur, shadowColor)) {
318 IntRect shadowRect = rect;
319 shadowRect.move(shadowSize.width(), shadowSize.height());
320 shadowRect.inflate(static_cast<int>(p->pen().widthF()));
321 p->fillRect(shadowRect, QColor(shadowColor));
322 }
323
324 p->drawRect(rect);
325
326 p->setRenderHint(QPainter::Antialiasing, antiAlias);
327 }
328
329 // This is only used to draw borders.
drawLine(const IntPoint & point1,const IntPoint & point2)330 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
331 {
332 if (paintingDisabled())
333 return;
334
335 StrokeStyle style = strokeStyle();
336 Color color = strokeColor();
337 if (style == NoStroke || !color.alpha())
338 return;
339
340 float width = strokeThickness();
341
342 FloatPoint p1 = point1;
343 FloatPoint p2 = point2;
344 bool isVerticalLine = (p1.x() == p2.x());
345
346 QPainter* p = m_data->p();
347 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
348 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
349 adjustLineToPixelBoundaries(p1, p2, width, style);
350
351 IntSize shadowSize;
352 int shadowBlur;
353 Color shadowColor;
354 if (textDrawingMode() == cTextFill && getShadow(shadowSize, shadowBlur, shadowColor)) {
355 p->save();
356 p->translate(shadowSize.width(), shadowSize.height());
357 p->setPen(shadowColor);
358 p->drawLine(p1, p2);
359 p->restore();
360 }
361
362 int patWidth = 0;
363 switch (style) {
364 case NoStroke:
365 case SolidStroke:
366 break;
367 case DottedStroke:
368 patWidth = static_cast<int>(width);
369 break;
370 case DashedStroke:
371 patWidth = 3 * static_cast<int>(width);
372 break;
373 }
374
375 if (patWidth) {
376 p->save();
377
378 // Do a rect fill of our endpoints. This ensures we always have the
379 // appearance of being a border. We then draw the actual dotted/dashed line.
380 if (isVerticalLine) {
381 p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
382 p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
383 } else {
384 p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
385 p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
386 }
387
388 // Example: 80 pixels with a width of 30 pixels.
389 // Remainder is 20. The maximum pixels of line we could paint
390 // will be 50 pixels.
391 int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
392 int remainder = distance % patWidth;
393 int coverage = distance - remainder;
394 int numSegments = coverage / patWidth;
395
396 float patternOffset = 0.0f;
397 // Special case 1px dotted borders for speed.
398 if (patWidth == 1)
399 patternOffset = 1.0f;
400 else {
401 bool evenNumberOfSegments = !(numSegments % 2);
402 if (remainder)
403 evenNumberOfSegments = !evenNumberOfSegments;
404 if (evenNumberOfSegments) {
405 if (remainder) {
406 patternOffset += patWidth - remainder;
407 patternOffset += remainder / 2;
408 } else
409 patternOffset = patWidth / 2;
410 } else {
411 if (remainder)
412 patternOffset = (patWidth - remainder)/2;
413 }
414 }
415
416 QVector<qreal> dashes;
417 dashes << qreal(patWidth) / width << qreal(patWidth) / width;
418
419 QPen pen = p->pen();
420 pen.setWidthF(width);
421 pen.setCapStyle(Qt::FlatCap);
422 pen.setDashPattern(dashes);
423 pen.setDashOffset(patternOffset / width);
424 p->setPen(pen);
425 }
426
427 p->drawLine(p1, p2);
428
429 if (patWidth)
430 p->restore();
431
432 p->setRenderHint(QPainter::Antialiasing, antiAlias);
433 }
434
435 // This method is only used to draw the little circles used in lists.
drawEllipse(const IntRect & rect)436 void GraphicsContext::drawEllipse(const IntRect& rect)
437 {
438 if (paintingDisabled())
439 return;
440
441 m_data->p()->drawEllipse(rect);
442 }
443
strokeArc(const IntRect & rect,int startAngle,int angleSpan)444 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
445 {
446 if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f || !strokeColor().alpha())
447 return;
448
449 QPainter* p = m_data->p();
450 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
451 p->setRenderHint(QPainter::Antialiasing, true);
452
453 IntSize shadowSize;
454 int shadowBlur;
455 Color shadowColor;
456 startAngle *= 16;
457 angleSpan *= 16;
458 if (getShadow(shadowSize, shadowBlur, shadowColor)) {
459 p->save();
460 p->translate(shadowSize.width(), shadowSize.height());
461 QPen pen(p->pen());
462 pen.setColor(shadowColor);
463 p->setPen(pen);
464 p->drawArc(rect, startAngle, angleSpan);
465 p->restore();
466 }
467 p->drawArc(rect, startAngle, angleSpan);
468
469 p->setRenderHint(QPainter::Antialiasing, antiAlias);
470 }
471
drawConvexPolygon(size_t npoints,const FloatPoint * points,bool shouldAntialias)472 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
473 {
474 if (paintingDisabled())
475 return;
476
477 if (npoints <= 1)
478 return;
479
480 QPolygonF polygon(npoints);
481
482 for (size_t i = 0; i < npoints; i++)
483 polygon[i] = points[i];
484
485 QPainter* p = m_data->p();
486 p->save();
487 p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
488 IntSize shadowSize;
489 int shadowBlur;
490 Color shadowColor;
491 if (getShadow(shadowSize, shadowBlur, shadowColor)) {
492 p->save();
493 p->translate(shadowSize.width(), shadowSize.height());
494 if (p->brush().style() != Qt::NoBrush)
495 p->setBrush(QBrush(shadowColor));
496 QPen pen(p->pen());
497 if (pen.style() != Qt::NoPen) {
498 pen.setColor(shadowColor);
499 p->setPen(pen);
500 }
501 p->drawConvexPolygon(polygon);
502 p->restore();
503 }
504 p->drawConvexPolygon(polygon);
505 p->restore();
506 }
507
pen()508 QPen GraphicsContext::pen()
509 {
510 if (paintingDisabled())
511 return QPen();
512
513 QPainter* p = m_data->p();
514 return p->pen();
515 }
516
drawFilledShadowPath(GraphicsContext * context,QPainter * p,const QPainterPath & path)517 static void inline drawFilledShadowPath(GraphicsContext* context, QPainter* p, const QPainterPath& path)
518 {
519 IntSize shadowSize;
520 int shadowBlur;
521 Color shadowColor;
522 if (context->getShadow(shadowSize, shadowBlur, shadowColor)) {
523 p->translate(shadowSize.width(), shadowSize.height());
524 p->fillPath(path, QBrush(shadowColor));
525 p->translate(-shadowSize.width(), -shadowSize.height());
526 }
527 }
528
fillPath()529 void GraphicsContext::fillPath()
530 {
531 if (paintingDisabled())
532 return;
533
534 QPainter* p = m_data->p();
535 QPainterPath path = m_data->currentPath;
536 path.setFillRule(toQtFillRule(fillRule()));
537
538 if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) {
539 drawFilledShadowPath(this, p, path);
540 if (m_common->state.fillPattern) {
541 AffineTransform affine;
542 p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
543 } else if (m_common->state.fillGradient) {
544 QBrush brush(*m_common->state.fillGradient->platformGradient());
545 brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
546 p->fillPath(path, brush);
547 } else {
548 if (fillColor().alpha())
549 p->fillPath(path, p->brush());
550 }
551 }
552 m_data->currentPath = QPainterPath();
553 }
554
strokePath()555 void GraphicsContext::strokePath()
556 {
557 if (paintingDisabled())
558 return;
559
560 QPainter* p = m_data->p();
561 QPen pen(p->pen());
562 QPainterPath path = m_data->currentPath;
563 path.setFillRule(toQtFillRule(fillRule()));
564
565 if (m_common->state.strokePattern || m_common->state.strokeGradient || strokeColor().alpha()) {
566 IntSize shadowSize;
567 int shadowBlur;
568 Color shadowColor;
569 if (getShadow(shadowSize, shadowBlur, shadowColor)) {
570 QTransform t(p->worldTransform());
571 p->translate(shadowSize.width(), shadowSize.height());
572 QPen shadowPen(pen);
573 shadowPen.setColor(shadowColor);
574 p->strokePath(path, shadowPen);
575 p->setWorldTransform(t);
576 }
577 if (m_common->state.strokePattern) {
578 AffineTransform affine;
579 pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
580 p->setPen(pen);
581 p->strokePath(path, pen);
582 } else if (m_common->state.strokeGradient) {
583 QBrush brush(*m_common->state.strokeGradient->platformGradient());
584 brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
585 pen.setBrush(brush);
586 p->setPen(pen);
587 p->strokePath(path, pen);
588 } else {
589 if (strokeColor().alpha())
590 p->strokePath(path, pen);
591 }
592 }
593 m_data->currentPath = QPainterPath();
594 }
595
drawBorderlessRectShadow(GraphicsContext * context,QPainter * p,const FloatRect & rect)596 static inline void drawBorderlessRectShadow(GraphicsContext* context, QPainter* p, const FloatRect& rect)
597 {
598 IntSize shadowSize;
599 int shadowBlur;
600 Color shadowColor;
601 if (context->getShadow(shadowSize, shadowBlur, shadowColor)) {
602 FloatRect shadowRect(rect);
603 shadowRect.move(shadowSize.width(), shadowSize.height());
604 p->fillRect(shadowRect, QColor(shadowColor));
605 }
606 }
607
fillRect(const FloatRect & rect)608 void GraphicsContext::fillRect(const FloatRect& rect)
609 {
610 if (paintingDisabled())
611 return;
612
613 QPainter* p = m_data->p();
614
615 if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) {
616 drawBorderlessRectShadow(this, p, rect);
617 if (m_common->state.fillPattern) {
618 AffineTransform affine;
619 p->fillRect(rect, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
620 } else if (m_common->state.fillGradient) {
621 QBrush brush(*m_common->state.fillGradient->platformGradient());
622 brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
623 p->fillRect(rect, brush);
624 } else {
625 if (fillColor().alpha())
626 p->fillRect(rect, p->brush());
627 }
628 }
629 }
630
fillRect(const FloatRect & rect,const Color & c,ColorSpace colorSpace)631 void GraphicsContext::fillRect(const FloatRect& rect, const Color& c, ColorSpace colorSpace)
632 {
633 if (paintingDisabled())
634 return;
635
636 m_data->solidColor.setColor(c);
637 QPainter* p = m_data->p();
638 drawBorderlessRectShadow(this, p, rect);
639 p->fillRect(rect, m_data->solidColor);
640 }
641
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color,ColorSpace colorSpace)642 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
643 {
644 if (paintingDisabled() || !color.alpha())
645 return;
646
647 Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight);
648 QPainter* p = m_data->p();
649 drawFilledShadowPath(this, p, path.platformPath());
650 p->fillPath(path.platformPath(), QColor(color));
651 }
652
beginPath()653 void GraphicsContext::beginPath()
654 {
655 m_data->currentPath = QPainterPath();
656 }
657
addPath(const Path & path)658 void GraphicsContext::addPath(const Path& path)
659 {
660 QPainterPath newPath = m_data->currentPath;
661 newPath.addPath(path.platformPath());
662 m_data->currentPath = newPath;
663 }
664
inTransparencyLayer() const665 bool GraphicsContext::inTransparencyLayer() const
666 {
667 return !m_data->layers.isEmpty();
668 }
669
currentPath()670 PlatformPath* GraphicsContext::currentPath()
671 {
672 return &m_data->currentPath;
673 }
674
clip(const FloatRect & rect)675 void GraphicsContext::clip(const FloatRect& rect)
676 {
677 if (paintingDisabled())
678 return;
679
680 m_data->p()->setClipRect(rect, Qt::IntersectClip);
681 }
682
clipPath(WindRule clipRule)683 void GraphicsContext::clipPath(WindRule clipRule)
684 {
685 if (paintingDisabled())
686 return;
687
688 QPainter* p = m_data->p();
689 QPainterPath newPath = m_data->currentPath;
690 newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
691 p->setClipPath(newPath);
692 }
693
drawFocusRing(const Vector<Path> & paths,int width,int offset,const Color & color)694 void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
695 {
696 // FIXME: implement
697 }
698
699 /**
700 * Focus ring handling is not handled here. Qt style in
701 * RenderTheme handles drawing focus on widgets which
702 * need it.
703 */
drawFocusRing(const Vector<IntRect> & rects,int,int,const Color & color)704 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
705 {
706 if (paintingDisabled())
707 return;
708
709 unsigned rectCount = rects.size();
710
711 if (!rects.size())
712 return;
713
714 QPainter* p = m_data->p();
715 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
716 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
717
718 const QPen oldPen = p->pen();
719 const QBrush oldBrush = p->brush();
720
721 QPen nPen = p->pen();
722 nPen.setColor(color);
723 p->setBrush(Qt::NoBrush);
724 nPen.setStyle(Qt::DotLine);
725 p->setPen(nPen);
726 #if 0
727 // FIXME How do we do a bounding outline with Qt?
728 QPainterPath path;
729 for (int i = 0; i < rectCount; ++i)
730 path.addRect(QRectF(rects[i]));
731 QPainterPathStroker stroker;
732 QPainterPath newPath = stroker.createStroke(path);
733 p->strokePath(newPath, nPen);
734 #else
735 for (unsigned i = 0; i < rectCount; ++i)
736 p->drawRect(QRectF(rects[i]));
737 #endif
738 p->setPen(oldPen);
739 p->setBrush(oldBrush);
740
741 p->setRenderHint(QPainter::Antialiasing, antiAlias);
742 }
743
drawLineForText(const IntPoint & origin,int width,bool)744 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool)
745 {
746 if (paintingDisabled())
747 return;
748
749 IntPoint endPoint = origin + IntSize(width, 0);
750 drawLine(origin, endPoint);
751 }
752
drawLineForMisspellingOrBadGrammar(const IntPoint &,int,bool)753 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int, bool)
754 {
755 if (paintingDisabled())
756 return;
757
758 notImplemented();
759 }
760
roundToDevicePixels(const FloatRect & frect)761 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
762 {
763 QRectF rect(frect);
764 rect = m_data->p()->deviceMatrix().mapRect(rect);
765
766 QRect result = rect.toRect(); //round it
767 return FloatRect(QRectF(result));
768 }
769
setPlatformShadow(const IntSize & size,int,const Color &,ColorSpace)770 void GraphicsContext::setPlatformShadow(const IntSize& size, int, const Color&, ColorSpace)
771 {
772 // Qt doesn't support shadows natively, they are drawn manually in the draw*
773 // functions
774
775 if (m_common->state.shadowsIgnoreTransforms) {
776 // Meaning that this graphics context is associated with a CanvasRenderingContext
777 // We flip the height since CG and HTML5 Canvas have opposite Y axis
778 m_common->state.shadowSize = IntSize(size.width(), -size.height());
779 }
780 }
781
clearPlatformShadow()782 void GraphicsContext::clearPlatformShadow()
783 {
784 // Qt doesn't support shadows natively, they are drawn manually in the draw*
785 // functions
786 }
787
beginTransparencyLayer(float opacity)788 void GraphicsContext::beginTransparencyLayer(float opacity)
789 {
790 if (paintingDisabled())
791 return;
792
793 int x, y, w, h;
794 x = y = 0;
795 QPainter* p = m_data->p();
796 const QPaintDevice* device = p->device();
797 w = device->width();
798 h = device->height();
799
800 QRectF clip = p->clipPath().boundingRect();
801 QRectF deviceClip = p->transform().mapRect(clip);
802 x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
803 y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
804 w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
805 h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
806
807 TransparencyLayer * layer = new TransparencyLayer(m_data->p(), QRect(x, y, w, h));
808
809 layer->opacity = opacity;
810 m_data->layers.push(layer);
811 }
812
endTransparencyLayer()813 void GraphicsContext::endTransparencyLayer()
814 {
815 if (paintingDisabled())
816 return;
817
818 TransparencyLayer* layer = m_data->layers.pop();
819 layer->painter.end();
820
821 QPainter* p = m_data->p();
822 p->save();
823 p->resetTransform();
824 p->setOpacity(layer->opacity);
825 p->drawPixmap(layer->offset, layer->pixmap);
826 p->restore();
827
828 delete layer;
829 }
830
clearRect(const FloatRect & rect)831 void GraphicsContext::clearRect(const FloatRect& rect)
832 {
833 if (paintingDisabled())
834 return;
835
836 QPainter* p = m_data->p();
837 QPainter::CompositionMode currentCompositionMode = p->compositionMode();
838 if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
839 p->setCompositionMode(QPainter::CompositionMode_Source);
840 p->fillRect(rect, Qt::transparent);
841 if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
842 p->setCompositionMode(currentCompositionMode);
843 }
844
strokeRect(const FloatRect & rect,float width)845 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
846 {
847 if (paintingDisabled())
848 return;
849
850 QPainterPath path;
851 path.addRect(rect);
852 setStrokeThickness(width);
853 m_data->currentPath = path;
854
855 strokePath();
856 }
857
setLineCap(LineCap lc)858 void GraphicsContext::setLineCap(LineCap lc)
859 {
860 if (paintingDisabled())
861 return;
862
863 QPainter* p = m_data->p();
864 QPen nPen = p->pen();
865 nPen.setCapStyle(toQtLineCap(lc));
866 p->setPen(nPen);
867 }
868
setLineDash(const DashArray & dashes,float dashOffset)869 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
870 {
871 QPainter* p = m_data->p();
872 QPen pen = p->pen();
873 unsigned dashLength = dashes.size();
874 if (dashLength) {
875 QVector<qreal> pattern;
876 unsigned count = dashLength;
877 if (dashLength % 2)
878 count *= 2;
879
880 float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
881 for (unsigned i = 0; i < count; i++)
882 pattern.append(dashes[i % dashLength] / penWidth);
883
884 pen.setDashPattern(pattern);
885 pen.setDashOffset(dashOffset);
886 }
887 p->setPen(pen);
888 }
889
setLineJoin(LineJoin lj)890 void GraphicsContext::setLineJoin(LineJoin lj)
891 {
892 if (paintingDisabled())
893 return;
894
895 QPainter* p = m_data->p();
896 QPen nPen = p->pen();
897 nPen.setJoinStyle(toQtLineJoin(lj));
898 p->setPen(nPen);
899 }
900
setMiterLimit(float limit)901 void GraphicsContext::setMiterLimit(float limit)
902 {
903 if (paintingDisabled())
904 return;
905
906 QPainter* p = m_data->p();
907 QPen nPen = p->pen();
908 nPen.setMiterLimit(limit);
909 p->setPen(nPen);
910 }
911
setAlpha(float opacity)912 void GraphicsContext::setAlpha(float opacity)
913 {
914 if (paintingDisabled())
915 return;
916 QPainter* p = m_data->p();
917 p->setOpacity(opacity);
918 }
919
setCompositeOperation(CompositeOperator op)920 void GraphicsContext::setCompositeOperation(CompositeOperator op)
921 {
922 if (paintingDisabled())
923 return;
924
925 if (m_data->p()->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
926 m_data->p()->setCompositionMode(toQtCompositionMode(op));
927 }
928
clip(const Path & path)929 void GraphicsContext::clip(const Path& path)
930 {
931 if (paintingDisabled())
932 return;
933
934 m_data->p()->setClipPath(path.platformPath(), Qt::IntersectClip);
935 }
936
canvasClip(const Path & path)937 void GraphicsContext::canvasClip(const Path& path)
938 {
939 clip(path);
940 }
941
clipOut(const Path & path)942 void GraphicsContext::clipOut(const Path& path)
943 {
944 if (paintingDisabled())
945 return;
946
947 QPainter* p = m_data->p();
948 QPainterPath clippedOut = path.platformPath();
949 QPainterPath newClip;
950 newClip.setFillRule(Qt::OddEvenFill);
951 if (p->hasClipping()) {
952 newClip.addRect(p->clipRegion().boundingRect());
953 newClip.addPath(clippedOut);
954 p->setClipPath(newClip, Qt::IntersectClip);
955 } else {
956 newClip.addRect(p->window());
957 newClip.addPath(clippedOut.intersected(newClip));
958 p->setClipPath(newClip);
959 }
960 }
961
translate(float x,float y)962 void GraphicsContext::translate(float x, float y)
963 {
964 if (paintingDisabled())
965 return;
966
967 m_data->p()->translate(x, y);
968
969 if (!m_data->currentPath.isEmpty()) {
970 QTransform matrix;
971 m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y);
972 m_common->state.pathTransform.translate(x, y);
973 }
974 }
975
origin()976 IntPoint GraphicsContext::origin()
977 {
978 if (paintingDisabled())
979 return IntPoint();
980 const QTransform &transform = m_data->p()->transform();
981 return IntPoint(qRound(transform.dx()), qRound(transform.dy()));
982 }
983
rotate(float radians)984 void GraphicsContext::rotate(float radians)
985 {
986 if (paintingDisabled())
987 return;
988
989 m_data->p()->rotate(180/M_PI*radians);
990
991 if (!m_data->currentPath.isEmpty()) {
992 QTransform matrix;
993 m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians);
994 m_common->state.pathTransform.rotate(radians);
995 }
996 }
997
scale(const FloatSize & s)998 void GraphicsContext::scale(const FloatSize& s)
999 {
1000 if (paintingDisabled())
1001 return;
1002
1003 m_data->p()->scale(s.width(), s.height());
1004
1005 if (!m_data->currentPath.isEmpty()) {
1006 QTransform matrix;
1007 m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height());
1008 m_common->state.pathTransform.scaleNonUniform(s.width(), s.height());
1009 }
1010 }
1011
clipOut(const IntRect & rect)1012 void GraphicsContext::clipOut(const IntRect& rect)
1013 {
1014 if (paintingDisabled())
1015 return;
1016
1017 QPainter* p = m_data->p();
1018 QPainterPath newClip;
1019 newClip.setFillRule(Qt::OddEvenFill);
1020 if (p->hasClipping()) {
1021 newClip.addRect(p->clipRegion().boundingRect());
1022 newClip.addRect(QRect(rect));
1023 p->setClipPath(newClip, Qt::IntersectClip);
1024 } else {
1025 QRect clipOutRect(rect);
1026 QRect window(p->window());
1027 clipOutRect &= window;
1028 newClip.addRect(window);
1029 newClip.addRect(clipOutRect);
1030 p->setClipPath(newClip);
1031 }
1032 }
1033
clipOutEllipseInRect(const IntRect & rect)1034 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
1035 {
1036 if (paintingDisabled())
1037 return;
1038
1039 QPainter* p = m_data->p();
1040 QPainterPath newClip;
1041 newClip.setFillRule(Qt::OddEvenFill);
1042 if (p->hasClipping()) {
1043 newClip.addRect(p->clipRegion().boundingRect());
1044 newClip.addEllipse(QRect(rect));
1045 p->setClipPath(newClip, Qt::IntersectClip);
1046 } else {
1047 QRect clipOutRect(rect);
1048 QRect window(p->window());
1049 clipOutRect &= window;
1050 newClip.addRect(window);
1051 newClip.addEllipse(clipOutRect);
1052 p->setClipPath(newClip);
1053 }
1054 }
1055
clipToImageBuffer(const FloatRect &,const ImageBuffer *)1056 void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*)
1057 {
1058 notImplemented();
1059 }
1060
addInnerRoundedRectClip(const IntRect & rect,int thickness)1061 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1062 int thickness)
1063 {
1064 if (paintingDisabled())
1065 return;
1066
1067 clip(rect);
1068 QPainterPath path;
1069
1070 // Add outer ellipse
1071 path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1072
1073 // Add inner ellipse.
1074 path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1075 rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1076
1077 path.setFillRule(Qt::OddEvenFill);
1078
1079 QPainter* p = m_data->p();
1080
1081 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
1082 p->setRenderHint(QPainter::Antialiasing, true);
1083 p->setClipPath(path, Qt::IntersectClip);
1084 p->setRenderHint(QPainter::Antialiasing, antiAlias);
1085 }
1086
concatCTM(const AffineTransform & transform)1087 void GraphicsContext::concatCTM(const AffineTransform& transform)
1088 {
1089 if (paintingDisabled())
1090 return;
1091
1092 m_data->p()->setWorldTransform(transform, true);
1093
1094 // Transformations to the context shouldn't transform the currentPath.
1095 // We have to undo every change made to the context from the currentPath
1096 // to avoid wrong drawings.
1097 if (!m_data->currentPath.isEmpty() && transform.isInvertible()) {
1098 QTransform matrix = transform.inverse();
1099 m_data->currentPath = m_data->currentPath * matrix;
1100 m_common->state.pathTransform.multiply(transform.toTransformationMatrix());
1101 }
1102 }
1103
setURLForRect(const KURL &,const IntRect &)1104 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
1105 {
1106 notImplemented();
1107 }
1108
setPlatformStrokeColor(const Color & color,ColorSpace colorSpace)1109 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1110 {
1111 if (paintingDisabled())
1112 return;
1113 QPainter* p = m_data->p();
1114 QPen newPen(p->pen());
1115 newPen.setColor(color);
1116 p->setPen(newPen);
1117 }
1118
setPlatformStrokeStyle(const StrokeStyle & strokeStyle)1119 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
1120 {
1121 if (paintingDisabled())
1122 return;
1123 QPainter* p = m_data->p();
1124 QPen newPen(p->pen());
1125 newPen.setStyle(toQPenStyle(strokeStyle));
1126 p->setPen(newPen);
1127 }
1128
setPlatformStrokeThickness(float thickness)1129 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1130 {
1131 if (paintingDisabled())
1132 return;
1133 QPainter* p = m_data->p();
1134 QPen newPen(p->pen());
1135 newPen.setWidthF(thickness);
1136 p->setPen(newPen);
1137 }
1138
setPlatformFillColor(const Color & color,ColorSpace colorSpace)1139 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1140 {
1141 if (paintingDisabled())
1142 return;
1143 m_data->solidColor.setColor(color);
1144 m_data->p()->setBrush(m_data->solidColor);
1145 }
1146
setPlatformShouldAntialias(bool enable)1147 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1148 {
1149 if (paintingDisabled())
1150 return;
1151 m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1152 }
1153
1154 #ifdef Q_WS_WIN
1155
getWindowsContext(const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1156 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1157 {
1158 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1159 Q_ASSERT(mayCreateBitmap);
1160
1161 if (dstRect.isEmpty())
1162 return 0;
1163
1164 // Create a bitmap DC in which to draw.
1165 BITMAPINFO bitmapInfo;
1166 bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1167 bitmapInfo.bmiHeader.biWidth = dstRect.width();
1168 bitmapInfo.bmiHeader.biHeight = dstRect.height();
1169 bitmapInfo.bmiHeader.biPlanes = 1;
1170 bitmapInfo.bmiHeader.biBitCount = 32;
1171 bitmapInfo.bmiHeader.biCompression = BI_RGB;
1172 bitmapInfo.bmiHeader.biSizeImage = 0;
1173 bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
1174 bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
1175 bitmapInfo.bmiHeader.biClrUsed = 0;
1176 bitmapInfo.bmiHeader.biClrImportant = 0;
1177
1178 void* pixels = 0;
1179 HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1180 if (!bitmap)
1181 return 0;
1182
1183 HDC displayDC = ::GetDC(0);
1184 HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1185 ::ReleaseDC(0, displayDC);
1186
1187 ::SelectObject(bitmapDC, bitmap);
1188
1189 // Fill our buffer with clear if we're going to alpha blend.
1190 if (supportAlphaBlend) {
1191 BITMAP bmpInfo;
1192 GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1193 int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1194 memset(bmpInfo.bmBits, 0, bufferSize);
1195 }
1196
1197 #if !OS(WINCE)
1198 // Make sure we can do world transforms.
1199 SetGraphicsMode(bitmapDC, GM_ADVANCED);
1200
1201 // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1202 XFORM xform;
1203 xform.eM11 = 1.0f;
1204 xform.eM12 = 0.0f;
1205 xform.eM21 = 0.0f;
1206 xform.eM22 = 1.0f;
1207 xform.eDx = -dstRect.x();
1208 xform.eDy = -dstRect.y();
1209 ::SetWorldTransform(bitmapDC, &xform);
1210 #endif
1211
1212 return bitmapDC;
1213 }
1214
releaseWindowsContext(HDC hdc,const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1215 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1216 {
1217 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1218 Q_ASSERT(mayCreateBitmap);
1219
1220 if (hdc) {
1221
1222 if (!dstRect.isEmpty()) {
1223
1224 HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1225 BITMAP info;
1226 GetObject(bitmap, sizeof(info), &info);
1227 ASSERT(info.bmBitsPixel == 32);
1228
1229 QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
1230 m_data->p()->drawPixmap(dstRect, pixmap);
1231
1232 ::DeleteObject(bitmap);
1233 }
1234
1235 ::DeleteDC(hdc);
1236 }
1237 }
1238 #endif
1239
setImageInterpolationQuality(InterpolationQuality quality)1240 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1241 {
1242 m_data->imageInterpolationQuality = quality;
1243
1244 switch (quality) {
1245 case InterpolationDefault:
1246 case InterpolationNone:
1247 case InterpolationLow:
1248 // use nearest-neigbor
1249 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
1250 break;
1251
1252 case InterpolationMedium:
1253 case InterpolationHigh:
1254 default:
1255 // use the filter
1256 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
1257 break;
1258 };
1259 }
1260
imageInterpolationQuality() const1261 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1262 {
1263 return m_data->imageInterpolationQuality;
1264 }
1265
1266 }
1267
1268 // vim: ts=4 sw=4 et
1269