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 * Copyright (C) 2010 Sencha, Inc.
13 * All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
24 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
32 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include "GraphicsContext.h"
44 #include "AffineTransform.h"
46 #include "ContextShadow.h"
47 #include "FloatConversion.h"
49 #include "GraphicsContextPrivate.h"
50 #include "ImageBuffer.h"
51 #include "NotImplemented.h"
55 #include "TransparencyLayer.h"
60 #include <QPaintDevice>
61 #include <QPaintEngine>
63 #include <QPainterPath>
70 #define M_PI 3.14159265358979323846
75 QPainter::CompositionMode GraphicsContext::toQtCompositionMode(CompositeOperator op)
79 return QPainter::CompositionMode_Clear;
81 return QPainter::CompositionMode_Source;
82 case CompositeSourceOver:
83 return QPainter::CompositionMode_SourceOver;
84 case CompositeSourceIn:
85 return QPainter::CompositionMode_SourceIn;
86 case CompositeSourceOut:
87 return QPainter::CompositionMode_SourceOut;
88 case CompositeSourceAtop:
89 return QPainter::CompositionMode_SourceAtop;
90 case CompositeDestinationOver:
91 return QPainter::CompositionMode_DestinationOver;
92 case CompositeDestinationIn:
93 return QPainter::CompositionMode_DestinationIn;
94 case CompositeDestinationOut:
95 return QPainter::CompositionMode_DestinationOut;
96 case CompositeDestinationAtop:
97 return QPainter::CompositionMode_DestinationAtop;
99 return QPainter::CompositionMode_Xor;
100 case CompositePlusDarker:
101 // there is no exact match, but this is the closest
102 return QPainter::CompositionMode_Darken;
103 case CompositeHighlight:
104 return QPainter::CompositionMode_SourceOver;
105 case CompositePlusLighter:
106 return QPainter::CompositionMode_Plus;
109 return QPainter::CompositionMode_SourceOver;
112 static inline Qt::PenCapStyle toQtLineCap(LineCap lc)
120 return Qt::SquareCap;
126 static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
130 return Qt::SvgMiterJoin;
132 return Qt::RoundJoin;
134 return Qt::BevelJoin;
137 return Qt::MiterJoin;
140 static Qt::PenStyle toQPenStyle(StrokeStyle style)
147 return Qt::SolidLine;
156 qWarning("couldn't recognize the pen style");
160 static inline Qt::FillRule toQtFillRule(WindRule rule)
164 return Qt::OddEvenFill;
166 return Qt::WindingFill;
168 qDebug("Qt: unrecognized wind rule!");
169 return Qt::OddEvenFill;
172 class GraphicsContextPlatformPrivate : public Noncopyable {
174 GraphicsContextPlatformPrivate(QPainter* painter);
175 ~GraphicsContextPlatformPrivate();
177 inline QPainter* p() const
179 if (layers.isEmpty())
181 return &layers.top()->painter;
184 bool antiAliasingForRectsAndLines;
186 QStack<TransparencyLayer*> layers;
187 // Counting real layers. Required by inTransparencyLayer() calls
188 // For example, layers with valid alphaMask are not real layers
191 // reuse this brush for solid color (to prevent expensive QBrush construction)
194 InterpolationQuality imageInterpolationQuality;
196 // Only used by SVG for now.
197 QPainterPath currentPath;
199 ContextShadow shadow;
200 QStack<ContextShadow> shadowStack;
202 bool hasShadow() const
204 return shadow.m_type != ContextShadow::NoShadow;
207 QRectF clipBoundingRect() const
209 #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
210 return p()->clipBoundingRect();
212 return p()->clipRegion().boundingRect();
221 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
226 solidColor = QBrush(Qt::black);
228 imageInterpolationQuality = InterpolationDefault;
231 // use the default the QPainter was constructed with
232 antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
233 // FIXME: Maybe only enable in SVG mode?
234 painter->setRenderHint(QPainter::Antialiasing, true);
235 painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
237 antiAliasingForRectsAndLines = false;
240 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
244 GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
245 : m_common(createGraphicsContextPrivate())
246 , m_data(new GraphicsContextPlatformPrivate(context))
248 setPaintingDisabled(!context);
250 // Make sure the context starts in sync with our state.
251 setPlatformFillColor(fillColor(), DeviceColorSpace);
252 setPlatformStrokeColor(strokeColor(), DeviceColorSpace);
254 // Make sure we start with the correct join mode.
255 setLineJoin(MiterJoin);
259 GraphicsContext::~GraphicsContext()
261 while (!m_data->layers.isEmpty())
262 endTransparencyLayer();
264 destroyGraphicsContextPrivate(m_common);
268 PlatformGraphicsContext* GraphicsContext::platformContext() const
273 AffineTransform GraphicsContext::getCTM() const
275 QTransform matrix(platformContext()->combinedTransform());
276 return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
277 matrix.m22(), matrix.dx(), matrix.dy());
280 void GraphicsContext::savePlatformState()
282 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
283 ++m_data->layers.top()->saveCounter;
285 m_data->shadowStack.push(m_data->shadow);
288 void GraphicsContext::restorePlatformState()
290 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
291 if (!--m_data->layers.top()->saveCounter)
292 endTransparencyLayer();
294 m_data->p()->restore();
296 if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) {
297 QTransform matrix = m_common->state.pathTransform;
298 m_data->currentPath = m_data->currentPath * matrix;
301 if (m_data->shadowStack.isEmpty())
302 m_data->shadow = ContextShadow();
304 m_data->shadow = m_data->shadowStack.pop();
307 // Draws a filled rectangle with a stroked border.
308 // This is only used to draw borders (real fill is done via fillRect), and
309 // thus it must not cast any shadow.
310 void GraphicsContext::drawRect(const IntRect& rect)
312 if (paintingDisabled())
315 QPainter* p = m_data->p();
316 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
317 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
321 p->setRenderHint(QPainter::Antialiasing, antiAlias);
324 // This is only used to draw borders.
325 // Must not cast any shadow.
326 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
328 if (paintingDisabled())
331 StrokeStyle style = strokeStyle();
332 Color color = strokeColor();
333 if (style == NoStroke)
336 float width = strokeThickness();
338 FloatPoint p1 = point1;
339 FloatPoint p2 = point2;
340 bool isVerticalLine = (p1.x() == p2.x());
342 QPainter* p = m_data->p();
343 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
344 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
345 adjustLineToPixelBoundaries(p1, p2, width, style);
353 patWidth = static_cast<int>(width);
356 patWidth = 3 * static_cast<int>(width);
363 // Do a rect fill of our endpoints. This ensures we always have the
364 // appearance of being a border. We then draw the actual dotted/dashed line.
365 if (isVerticalLine) {
366 p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
367 p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
369 p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
370 p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
373 // Example: 80 pixels with a width of 30 pixels.
374 // Remainder is 20. The maximum pixels of line we could paint
375 // will be 50 pixels.
376 int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
377 int remainder = distance % patWidth;
378 int coverage = distance - remainder;
379 int numSegments = coverage / patWidth;
381 float patternOffset = 0.0f;
382 // Special case 1px dotted borders for speed.
384 patternOffset = 1.0f;
386 bool evenNumberOfSegments = !(numSegments % 2);
388 evenNumberOfSegments = !evenNumberOfSegments;
389 if (evenNumberOfSegments) {
391 patternOffset += patWidth - remainder;
392 patternOffset += remainder / 2;
394 patternOffset = patWidth / 2;
397 patternOffset = (patWidth - remainder) / 2;
401 QVector<qreal> dashes;
402 dashes << qreal(patWidth) / width << qreal(patWidth) / width;
405 pen.setWidthF(width);
406 pen.setCapStyle(Qt::FlatCap);
407 pen.setDashPattern(dashes);
408 pen.setDashOffset(patternOffset / width);
417 p->setRenderHint(QPainter::Antialiasing, antiAlias);
420 // This method is only used to draw the little circles used in lists.
421 void GraphicsContext::drawEllipse(const IntRect& rect)
423 if (paintingDisabled())
426 m_data->p()->drawEllipse(rect);
429 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
431 if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f)
434 QPainter* p = m_data->p();
435 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
436 p->setRenderHint(QPainter::Antialiasing, true);
441 if (m_data->hasShadow()) {
443 p->translate(m_data->shadow.offset());
445 pen.setColor(m_data->shadow.m_color);
447 p->drawArc(rect, startAngle, angleSpan);
450 p->drawArc(rect, startAngle, angleSpan);
452 p->setRenderHint(QPainter::Antialiasing, antiAlias);
455 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
457 if (paintingDisabled())
463 QPolygonF polygon(npoints);
465 for (size_t i = 0; i < npoints; i++)
466 polygon[i] = points[i];
468 QPainter* p = m_data->p();
470 p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
471 if (m_data->hasShadow()) {
473 p->translate(m_data->shadow.offset());
474 if (p->brush().style() != Qt::NoBrush)
475 p->setBrush(QBrush(m_data->shadow.m_color));
477 if (pen.style() != Qt::NoPen) {
478 pen.setColor(m_data->shadow.m_color);
481 p->drawConvexPolygon(polygon);
484 p->drawConvexPolygon(polygon);
488 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
490 if (paintingDisabled())
496 QPainterPath path(points[0]);
497 for (size_t i = 1; i < numPoints; ++i)
498 path.lineTo(points[i]);
499 path.setFillRule(Qt::WindingFill);
500 m_data->p()->setClipPath(path, Qt::IntersectClip);
503 QPen GraphicsContext::pen()
505 if (paintingDisabled())
508 QPainter* p = m_data->p();
512 void GraphicsContext::fillPath()
514 if (paintingDisabled())
517 QPainter* p = m_data->p();
518 QPainterPath& path = m_data->currentPath; // Avoid detaching the QPainterPath
519 path.setFillRule(toQtFillRule(fillRule()));
521 if (m_data->hasShadow()) {
522 p->translate(m_data->shadow.offset());
523 p->fillPath(path, QColor(m_data->shadow.m_color));
524 p->translate(-m_data->shadow.offset());
526 if (m_common->state.fillPattern) {
527 AffineTransform affine;
528 p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
529 } else if (m_common->state.fillGradient) {
530 QBrush brush(*m_common->state.fillGradient->platformGradient());
531 brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
532 p->fillPath(path, brush);
534 p->fillPath(path, p->brush());
536 m_data->currentPath = QPainterPath();
539 void GraphicsContext::strokePath()
541 if (paintingDisabled())
544 QPainter* p = m_data->p();
546 QPainterPath& path = m_data->currentPath; // Avoid detaching the QPainterPath
547 path.setFillRule(toQtFillRule(fillRule()));
549 if (m_data->hasShadow()) {
550 p->translate(m_data->shadow.offset());
552 shadowPen.setColor(m_data->shadow.m_color);
553 p->strokePath(path, shadowPen);
554 p->translate(-m_data->shadow.offset());
556 if (m_common->state.strokePattern) {
557 AffineTransform affine;
558 pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
560 p->strokePath(path, pen);
561 } else if (m_common->state.strokeGradient) {
562 QBrush brush(*m_common->state.strokeGradient->platformGradient());
563 brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
566 p->strokePath(path, pen);
568 p->strokePath(path, pen);
569 m_data->currentPath = QPainterPath();
572 static inline void drawRepeatPattern(QPainter* p, QPixmap* image, const FloatRect& rect, const bool repeatX, const bool repeatY)
574 // Patterns must be painted so that the top left of the first image is anchored at
575 // the origin of the coordinate space
577 int w = image->width();
578 int h = image->height();
580 QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
582 // startX, startY is the coordinate of the first image we need to put on the left-top of the rect
583 if (repeatX && repeatY) {
585 // startX, startY is at the left top side of the left-top of the rect
586 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
587 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
589 if (!repeatX && !repeatY) {
591 // only draw the image once at orgin once, check if need to draw
592 QRect imageRect(0, 0, w, h);
593 if (imageRect.intersects(r)) {
598 } else if (repeatX && !repeatY) {
600 // startY is fixed, but startX change based on the left-top of the rect
601 QRect imageRect(r.x(), 0, r.width(), h);
602 if (imageRect.intersects(r)) {
603 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
609 // startX is fixed, but startY change based on the left-top of the rect
610 QRect imageRect(0, r.y(), w, r.height());
611 if (imageRect.intersects(r)) {
613 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
625 QRect imageRect(x, y, w, h);
626 QRect intersectRect = imageRect.intersected(r);
627 QPoint destStart(intersectRect.x(), intersectRect.y());
628 QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height());
630 p->drawPixmap(destStart, *image, sourceRect);
632 } while (repeatX && x < r.x() + r.width());
635 } while (repeatY && y < r.y() + r.height());
639 void GraphicsContext::fillRect(const FloatRect& rect)
641 if (paintingDisabled())
644 QPainter* p = m_data->p();
645 QRectF normalizedRect = rect.normalized();
646 ContextShadow* shadow = contextShadow();
648 if (m_common->state.fillPattern) {
649 AffineTransform affine;
650 QBrush brush(m_common->state.fillPattern->createPlatformPattern(affine));
651 QPixmap* image = m_common->state.fillPattern->tileImage()->nativeImageForCurrentFrame();
652 QPainter* shadowPainter = m_data->hasShadow() ? shadow->beginShadowLayer(p, normalizedRect) : 0;
654 drawRepeatPattern(shadowPainter, image, normalizedRect, m_common->state.fillPattern->repeatX(), m_common->state.fillPattern->repeatY());
655 shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
656 shadowPainter->fillRect(normalizedRect, shadow->m_color);
657 shadow->endShadowLayer(p);
659 drawRepeatPattern(p, image, normalizedRect, m_common->state.fillPattern->repeatX(), m_common->state.fillPattern->repeatY());
660 } else if (m_common->state.fillGradient) {
661 QBrush brush(*m_common->state.fillGradient->platformGradient());
662 brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
663 QPainter* shadowPainter = m_data->hasShadow() ? shadow->beginShadowLayer(p, normalizedRect) : 0;
665 shadowPainter->fillRect(normalizedRect, brush);
666 shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
667 shadowPainter->fillRect(normalizedRect, shadow->m_color);
668 shadow->endShadowLayer(p);
670 p->fillRect(normalizedRect, brush);
672 if (m_data->hasShadow()) {
673 if (shadow->m_type == ContextShadow::BlurShadow) {
674 QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect);
676 shadowPainter->fillRect(normalizedRect, p->brush());
677 shadow->endShadowLayer(p);
680 // Solid rectangle fill with no blur shadow can be done faster
681 // without using the shadow layer at all.
682 QColor shadowColor = shadow->m_color;
683 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
684 p->fillRect(normalizedRect.translated(shadow->offset()), shadowColor);
687 p->fillRect(normalizedRect, p->brush());
692 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
694 if (paintingDisabled() || !color.isValid())
697 m_data->solidColor.setColor(color);
698 QPainter* p = m_data->p();
699 QRectF normalizedRect = rect.normalized();
701 if (m_data->hasShadow()) {
702 ContextShadow* shadow = contextShadow();
704 if (shadow->m_type != ContextShadow::BlurShadow) {
705 // We do not need any layer for simple shadow.
706 p->fillRect(normalizedRect.translated(shadow->offset()), shadow->m_color);
708 QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect);
710 shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
711 shadowPainter->fillRect(normalizedRect, shadow->m_color);
712 shadow->endShadowLayer(p);
717 p->fillRect(normalizedRect, m_data->solidColor);
720 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
722 if (paintingDisabled() || !color.isValid())
725 Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight);
726 QPainter* p = m_data->p();
727 if (m_data->hasShadow()) {
728 p->translate(m_data->shadow.offset());
729 p->fillPath(path.platformPath(), QColor(m_data->shadow.m_color));
730 p->translate(-m_data->shadow.offset());
732 p->fillPath(path.platformPath(), QColor(color));
735 void GraphicsContext::beginPath()
737 m_data->currentPath = QPainterPath();
740 void GraphicsContext::addPath(const Path& path)
742 if (!m_data->currentPath.elementCount()) {
743 m_data->currentPath = path.platformPath();
746 m_data->currentPath.addPath(path.platformPath());
749 bool GraphicsContext::inTransparencyLayer() const
751 return m_data->layerCount;
754 PlatformPath* GraphicsContext::currentPath()
756 return &m_data->currentPath;
759 ContextShadow* GraphicsContext::contextShadow()
761 return &m_data->shadow;
764 void GraphicsContext::clip(const FloatRect& rect)
766 if (paintingDisabled())
769 m_data->p()->setClipRect(rect, Qt::IntersectClip);
772 void GraphicsContext::clipPath(WindRule clipRule)
774 if (paintingDisabled())
777 QPainter* p = m_data->p();
778 QPainterPath newPath = m_data->currentPath;
779 newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
780 p->setClipPath(newPath);
783 void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
789 * Focus ring handling is not handled here. Qt style in
790 * RenderTheme handles drawing focus on widgets which
793 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
795 if (paintingDisabled() || !color.isValid())
798 unsigned rectCount = rects.size();
803 QPainter* p = m_data->p();
804 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
805 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
807 const QPen oldPen = p->pen();
808 const QBrush oldBrush = p->brush();
810 QPen nPen = p->pen();
811 nPen.setColor(color);
812 p->setBrush(Qt::NoBrush);
813 nPen.setStyle(Qt::DotLine);
816 // FIXME How do we do a bounding outline with Qt?
818 for (int i = 0; i < rectCount; ++i)
819 path.addRect(QRectF(rects[i]));
820 QPainterPathStroker stroker;
821 QPainterPath newPath = stroker.createStroke(path);
822 p->strokePath(newPath, nPen);
824 for (unsigned i = 0; i < rectCount; ++i)
825 p->drawRect(QRectF(rects[i]));
828 p->setBrush(oldBrush);
830 p->setRenderHint(QPainter::Antialiasing, antiAlias);
833 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool)
835 if (paintingDisabled())
838 IntPoint endPoint = origin + IntSize(width, 0);
839 drawLine(origin, endPoint);
842 void GraphicsContext::drawLineForTextChecking(const IntPoint&, int, TextCheckingLineStyle)
844 if (paintingDisabled())
850 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
852 // It is not enough just to round to pixels in device space. The rotation part of the
853 // affine transform matrix to device space can mess with this conversion if we have a
854 // rotating image like the hands of the world clock widget. We just need the scale, so
855 // we get the affine transform matrix and extract the scale.
856 QPainter* painter = platformContext();
857 QTransform deviceTransform = painter->deviceTransform();
858 if (deviceTransform.isIdentity())
861 qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12());
862 qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22());
864 QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY);
865 QPoint deviceLowerRight(frect.right() * deviceScaleX, frect.bottom() * deviceScaleY);
867 // Don't let the height or width round to 0 unless either was originally 0
868 if (deviceOrigin.y() == deviceLowerRight.y() && frect.height())
869 deviceLowerRight.setY(deviceLowerRight.y() + 1);
870 if (deviceOrigin.x() == deviceLowerRight.x() && frect.width())
871 deviceLowerRight.setX(deviceLowerRight.x() + 1);
873 FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY);
874 FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY);
875 return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
878 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
880 // Qt doesn't support shadows natively, they are drawn manually in the draw*
883 if (m_common->state.shadowsIgnoreTransforms) {
884 // Meaning that this graphics context is associated with a CanvasRenderingContext
885 // We flip the height since CG and HTML5 Canvas have opposite Y axis
886 m_common->state.shadowOffset = FloatSize(size.width(), -size.height());
887 m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height()));
889 m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height()));
893 void GraphicsContext::clearPlatformShadow()
895 m_data->shadow.clear();
898 void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask)
900 m_data->layers.push(new TransparencyLayer(m_data->p(), m_data->p()->transform().mapRect(rect), 1.0, alphaMask));
903 void GraphicsContext::beginTransparencyLayer(float opacity)
905 if (paintingDisabled())
910 QPainter* p = m_data->p();
911 const QPaintDevice* device = p->device();
913 h = device->height();
915 QRectF clip = m_data->clipBoundingRect();
916 QRectF deviceClip = p->transform().mapRect(clip);
917 x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
918 y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
919 w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
920 h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
922 QPixmap emptyAlphaMask;
923 m_data->layers.push(new TransparencyLayer(m_data->p(), QRect(x, y, w, h), opacity, emptyAlphaMask));
924 ++m_data->layerCount;
927 void GraphicsContext::endTransparencyLayer()
929 if (paintingDisabled())
932 TransparencyLayer* layer = m_data->layers.pop();
933 if (!layer->alphaMask.isNull()) {
934 layer->painter.resetTransform();
935 layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
936 layer->painter.drawPixmap(QPoint(), layer->alphaMask);
938 --m_data->layerCount; // see the comment for layerCount
939 layer->painter.end();
941 QPainter* p = m_data->p();
944 p->setOpacity(layer->opacity);
945 p->drawPixmap(layer->offset, layer->pixmap);
951 void GraphicsContext::clearRect(const FloatRect& rect)
953 if (paintingDisabled())
956 QPainter* p = m_data->p();
957 QPainter::CompositionMode currentCompositionMode = p->compositionMode();
958 if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
959 p->setCompositionMode(QPainter::CompositionMode_Source);
960 p->fillRect(rect, Qt::transparent);
961 if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
962 p->setCompositionMode(currentCompositionMode);
965 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
967 if (paintingDisabled())
972 setStrokeThickness(width);
973 m_data->currentPath = path;
978 void GraphicsContext::setLineCap(LineCap lc)
980 if (paintingDisabled())
983 QPainter* p = m_data->p();
984 QPen nPen = p->pen();
985 nPen.setCapStyle(toQtLineCap(lc));
989 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
991 QPainter* p = m_data->p();
993 unsigned dashLength = dashes.size();
995 QVector<qreal> pattern;
996 unsigned count = dashLength;
1000 float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
1001 for (unsigned i = 0; i < count; i++)
1002 pattern.append(dashes[i % dashLength] / penWidth);
1004 pen.setDashPattern(pattern);
1005 pen.setDashOffset(dashOffset / penWidth);
1007 pen.setStyle(Qt::SolidLine);
1011 void GraphicsContext::setLineJoin(LineJoin lj)
1013 if (paintingDisabled())
1016 QPainter* p = m_data->p();
1017 QPen nPen = p->pen();
1018 nPen.setJoinStyle(toQtLineJoin(lj));
1022 void GraphicsContext::setMiterLimit(float limit)
1024 if (paintingDisabled())
1027 QPainter* p = m_data->p();
1028 QPen nPen = p->pen();
1029 nPen.setMiterLimit(limit);
1033 void GraphicsContext::setAlpha(float opacity)
1035 if (paintingDisabled())
1037 QPainter* p = m_data->p();
1038 p->setOpacity(opacity);
1041 void GraphicsContext::setCompositeOperation(CompositeOperator op)
1043 if (paintingDisabled())
1046 if (m_data->p()->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
1047 m_data->p()->setCompositionMode(toQtCompositionMode(op));
1050 void GraphicsContext::clip(const Path& path)
1052 if (paintingDisabled())
1055 m_data->p()->setClipPath(path.platformPath(), Qt::IntersectClip);
1058 void GraphicsContext::canvasClip(const Path& path)
1060 if (paintingDisabled())
1063 QPainterPath clipPath = path.platformPath();
1064 clipPath.setFillRule(Qt::WindingFill);
1065 m_data->p()->setClipPath(clipPath, Qt::IntersectClip);
1068 void GraphicsContext::clipOut(const Path& path)
1070 if (paintingDisabled())
1073 QPainter* p = m_data->p();
1074 QPainterPath clippedOut = path.platformPath();
1075 QPainterPath newClip;
1076 newClip.setFillRule(Qt::OddEvenFill);
1077 if (p->hasClipping()) {
1078 newClip.addRect(m_data->clipBoundingRect());
1079 newClip.addPath(clippedOut);
1080 p->setClipPath(newClip, Qt::IntersectClip);
1082 QRect windowRect = p->transform().inverted().mapRect(p->window());
1083 newClip.addRect(windowRect);
1084 newClip.addPath(clippedOut.intersected(newClip));
1085 p->setClipPath(newClip);
1089 void GraphicsContext::translate(float x, float y)
1091 if (paintingDisabled())
1094 m_data->p()->translate(x, y);
1096 if (!m_data->currentPath.isEmpty()) {
1098 m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y);
1099 m_common->state.pathTransform.translate(x, y);
1103 IntPoint GraphicsContext::origin()
1105 if (paintingDisabled())
1107 const QTransform &transform = m_data->p()->transform();
1108 return IntPoint(qRound(transform.dx()), qRound(transform.dy()));
1111 void GraphicsContext::rotate(float radians)
1113 if (paintingDisabled())
1116 m_data->p()->rotate(180 / M_PI*radians);
1118 if (!m_data->currentPath.isEmpty()) {
1120 m_data->currentPath = m_data->currentPath * matrix.rotate(-180 / M_PI*radians);
1121 m_common->state.pathTransform.rotate(radians);
1125 void GraphicsContext::scale(const FloatSize& s)
1127 if (paintingDisabled())
1130 m_data->p()->scale(s.width(), s.height());
1132 if (!m_data->currentPath.isEmpty()) {
1134 m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height());
1135 m_common->state.pathTransform.scaleNonUniform(s.width(), s.height());
1139 void GraphicsContext::clipOut(const IntRect& rect)
1141 if (paintingDisabled())
1144 QPainter* p = m_data->p();
1145 QPainterPath newClip;
1146 newClip.setFillRule(Qt::OddEvenFill);
1147 if (p->hasClipping()) {
1148 newClip.addRect(m_data->clipBoundingRect());
1149 newClip.addRect(QRect(rect));
1150 p->setClipPath(newClip, Qt::IntersectClip);
1152 QRect clipOutRect(rect);
1153 QRect window = p->transform().inverted().mapRect(p->window());
1154 clipOutRect &= window;
1155 newClip.addRect(window);
1156 newClip.addRect(clipOutRect);
1157 p->setClipPath(newClip);
1161 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
1163 if (paintingDisabled())
1166 QPainter* p = m_data->p();
1167 QPainterPath newClip;
1168 newClip.setFillRule(Qt::OddEvenFill);
1169 if (p->hasClipping()) {
1170 newClip.addRect(m_data->clipBoundingRect());
1171 newClip.addEllipse(QRect(rect));
1172 p->setClipPath(newClip, Qt::IntersectClip);
1174 QRect clipOutRect(rect);
1175 QRect window(p->window());
1176 clipOutRect &= window;
1177 newClip.addRect(window);
1178 newClip.addEllipse(clipOutRect);
1179 p->setClipPath(newClip);
1183 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1186 if (paintingDisabled())
1192 // Add outer ellipse
1193 path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1195 // Add inner ellipse.
1196 path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1197 rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1199 path.setFillRule(Qt::OddEvenFill);
1201 QPainter* p = m_data->p();
1203 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
1204 p->setRenderHint(QPainter::Antialiasing, true);
1205 p->setClipPath(path, Qt::IntersectClip);
1206 p->setRenderHint(QPainter::Antialiasing, antiAlias);
1209 void GraphicsContext::concatCTM(const AffineTransform& transform)
1211 if (paintingDisabled())
1214 m_data->p()->setWorldTransform(transform, true);
1216 // Transformations to the context shouldn't transform the currentPath.
1217 // We have to undo every change made to the context from the currentPath
1218 // to avoid wrong drawings.
1219 if (!m_data->currentPath.isEmpty() && transform.isInvertible()) {
1220 QTransform matrix = transform.inverse();
1221 m_data->currentPath = m_data->currentPath * matrix;
1222 m_common->state.pathTransform.multiply(transform.toTransformationMatrix());
1226 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
1231 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1233 if (paintingDisabled() || !color.isValid())
1236 QPainter* p = m_data->p();
1237 QPen newPen(p->pen());
1238 m_data->solidColor.setColor(color);
1239 newPen.setBrush(m_data->solidColor);
1243 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
1245 if (paintingDisabled())
1247 QPainter* p = m_data->p();
1248 QPen newPen(p->pen());
1249 newPen.setStyle(toQPenStyle(strokeStyle));
1253 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1255 if (paintingDisabled())
1257 QPainter* p = m_data->p();
1258 QPen newPen(p->pen());
1259 newPen.setWidthF(thickness);
1263 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1265 if (paintingDisabled() || !color.isValid())
1268 m_data->solidColor.setColor(color);
1269 m_data->p()->setBrush(m_data->solidColor);
1272 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1274 if (paintingDisabled())
1276 m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1281 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1283 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1284 Q_ASSERT(mayCreateBitmap);
1286 if (dstRect.isEmpty())
1289 // Create a bitmap DC in which to draw.
1290 BITMAPINFO bitmapInfo;
1291 bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1292 bitmapInfo.bmiHeader.biWidth = dstRect.width();
1293 bitmapInfo.bmiHeader.biHeight = dstRect.height();
1294 bitmapInfo.bmiHeader.biPlanes = 1;
1295 bitmapInfo.bmiHeader.biBitCount = 32;
1296 bitmapInfo.bmiHeader.biCompression = BI_RGB;
1297 bitmapInfo.bmiHeader.biSizeImage = 0;
1298 bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
1299 bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
1300 bitmapInfo.bmiHeader.biClrUsed = 0;
1301 bitmapInfo.bmiHeader.biClrImportant = 0;
1304 HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1308 HDC displayDC = ::GetDC(0);
1309 HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1310 ::ReleaseDC(0, displayDC);
1312 ::SelectObject(bitmapDC, bitmap);
1314 // Fill our buffer with clear if we're going to alpha blend.
1315 if (supportAlphaBlend) {
1317 GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1318 int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1319 memset(bmpInfo.bmBits, 0, bufferSize);
1323 // Make sure we can do world transforms.
1324 SetGraphicsMode(bitmapDC, GM_ADVANCED);
1326 // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1332 xform.eDx = -dstRect.x();
1333 xform.eDy = -dstRect.y();
1334 ::SetWorldTransform(bitmapDC, &xform);
1340 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1342 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1343 Q_ASSERT(mayCreateBitmap);
1347 if (!dstRect.isEmpty()) {
1349 HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1351 GetObject(bitmap, sizeof(info), &info);
1352 ASSERT(info.bmBitsPixel == 32);
1354 QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
1355 m_data->p()->drawPixmap(dstRect, pixmap);
1357 ::DeleteObject(bitmap);
1365 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1367 m_data->imageInterpolationQuality = quality;
1370 case InterpolationNone:
1371 case InterpolationLow:
1372 // use nearest-neigbor
1373 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
1376 case InterpolationDefault:
1377 case InterpolationMedium:
1378 case InterpolationHigh:
1381 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
1386 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1388 return m_data->imageInterpolationQuality;
1393 // vim: ts=4 sw=4 et