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();
179 if (layers.isEmpty()) {
185 return &layers.top()->painter;
188 bool antiAliasingForRectsAndLines;
190 QStack<TransparencyLayer*> layers;
191 // Counting real layers. Required by inTransparencyLayer() calls
192 // For example, layers with valid alphaMask are not real layers
196 // reuse this brush for solid color (to prevent expensive QBrush construction)
199 InterpolationQuality imageInterpolationQuality;
201 // Only used by SVG for now.
202 QPainterPath currentPath;
204 ContextShadow shadow;
205 QStack<ContextShadow> shadowStack;
207 bool hasShadow() const
209 return shadow.type != ContextShadow::NoShadow;
212 QRectF clipBoundingRect() const
214 #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
215 return painter->clipBoundingRect();
217 return painter->clipRegion().boundingRect();
226 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
232 solidColor = QBrush(Qt::black);
234 imageInterpolationQuality = InterpolationDefault;
237 // use the default the QPainter was constructed with
238 antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
239 // FIXME: Maybe only enable in SVG mode?
240 painter->setRenderHint(QPainter::Antialiasing, true);
241 painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
243 antiAliasingForRectsAndLines = false;
246 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
250 GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
251 : m_common(createGraphicsContextPrivate())
252 , m_data(new GraphicsContextPlatformPrivate(context))
254 setPaintingDisabled(!context);
256 // Make sure the context starts in sync with our state.
257 setPlatformFillColor(fillColor(), DeviceColorSpace);
258 setPlatformStrokeColor(strokeColor(), DeviceColorSpace);
260 // Make sure we start with the correct join mode.
261 setLineJoin(MiterJoin);
265 GraphicsContext::~GraphicsContext()
267 while (!m_data->layers.isEmpty())
268 endTransparencyLayer();
270 destroyGraphicsContextPrivate(m_common);
274 PlatformGraphicsContext* GraphicsContext::platformContext() const
279 AffineTransform GraphicsContext::getCTM() const
281 QTransform matrix(platformContext()->combinedTransform());
282 return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
283 matrix.m22(), matrix.dx(), matrix.dy());
286 void GraphicsContext::savePlatformState()
288 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
289 ++m_data->layers.top()->saveCounter;
291 m_data->shadowStack.push(m_data->shadow);
294 void GraphicsContext::restorePlatformState()
296 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
297 if (!--m_data->layers.top()->saveCounter)
298 endTransparencyLayer();
300 m_data->p()->restore();
302 if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) {
303 QTransform matrix = m_common->state.pathTransform;
304 m_data->currentPath = m_data->currentPath * matrix;
307 if (m_data->shadowStack.isEmpty())
308 m_data->shadow = ContextShadow();
310 m_data->shadow = m_data->shadowStack.pop();
313 // Draws a filled rectangle with a stroked border.
314 // This is only used to draw borders (real fill is done via fillRect), and
315 // thus it must not cast any shadow.
316 void GraphicsContext::drawRect(const IntRect& rect)
318 if (paintingDisabled())
321 QPainter* p = m_data->p();
322 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
323 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
327 p->setRenderHint(QPainter::Antialiasing, antiAlias);
330 // This is only used to draw borders.
331 // Must not cast any shadow.
332 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
334 if (paintingDisabled())
337 StrokeStyle style = strokeStyle();
338 Color color = strokeColor();
339 if (style == NoStroke)
342 float width = strokeThickness();
344 FloatPoint p1 = point1;
345 FloatPoint p2 = point2;
346 bool isVerticalLine = (p1.x() == p2.x());
348 QPainter* p = m_data->p();
349 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
350 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
351 adjustLineToPixelBoundaries(p1, p2, width, style);
359 patWidth = static_cast<int>(width);
362 patWidth = 3 * static_cast<int>(width);
369 // Do a rect fill of our endpoints. This ensures we always have the
370 // appearance of being a border. We then draw the actual dotted/dashed line.
371 if (isVerticalLine) {
372 p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
373 p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
375 p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
376 p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
379 // Example: 80 pixels with a width of 30 pixels.
380 // Remainder is 20. The maximum pixels of line we could paint
381 // will be 50 pixels.
382 int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
383 int remainder = distance % patWidth;
384 int coverage = distance - remainder;
385 int numSegments = coverage / patWidth;
387 float patternOffset = 0.0f;
388 // Special case 1px dotted borders for speed.
390 patternOffset = 1.0f;
392 bool evenNumberOfSegments = !(numSegments % 2);
394 evenNumberOfSegments = !evenNumberOfSegments;
395 if (evenNumberOfSegments) {
397 patternOffset += patWidth - remainder;
398 patternOffset += remainder / 2;
400 patternOffset = patWidth / 2;
403 patternOffset = (patWidth - remainder) / 2;
407 QVector<qreal> dashes;
408 dashes << qreal(patWidth) / width << qreal(patWidth) / width;
411 pen.setWidthF(width);
412 pen.setCapStyle(Qt::FlatCap);
413 pen.setDashPattern(dashes);
414 pen.setDashOffset(patternOffset / width);
423 p->setRenderHint(QPainter::Antialiasing, antiAlias);
426 // This method is only used to draw the little circles used in lists.
427 void GraphicsContext::drawEllipse(const IntRect& rect)
429 if (paintingDisabled())
432 m_data->p()->drawEllipse(rect);
435 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
437 if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f)
440 QPainter* p = m_data->p();
441 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
442 p->setRenderHint(QPainter::Antialiasing, true);
447 if (m_data->hasShadow()) {
449 p->translate(m_data->shadow.offset);
451 pen.setColor(m_data->shadow.color);
453 p->drawArc(rect, startAngle, angleSpan);
456 p->drawArc(rect, startAngle, angleSpan);
458 p->setRenderHint(QPainter::Antialiasing, antiAlias);
461 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
463 if (paintingDisabled())
469 QPolygonF polygon(npoints);
471 for (size_t i = 0; i < npoints; i++)
472 polygon[i] = points[i];
474 QPainter* p = m_data->p();
476 p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
477 if (m_data->hasShadow()) {
479 p->translate(m_data->shadow.offset);
480 if (p->brush().style() != Qt::NoBrush)
481 p->setBrush(QBrush(m_data->shadow.color));
483 if (pen.style() != Qt::NoPen) {
484 pen.setColor(m_data->shadow.color);
487 p->drawConvexPolygon(polygon);
490 p->drawConvexPolygon(polygon);
494 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
496 if (paintingDisabled())
502 QPainterPath path(points[0]);
503 for (size_t i = 1; i < numPoints; ++i)
504 path.lineTo(points[i]);
505 path.setFillRule(Qt::WindingFill);
506 m_data->p()->setClipPath(path, Qt::IntersectClip);
509 QPen GraphicsContext::pen()
511 if (paintingDisabled())
514 QPainter* p = m_data->p();
518 void GraphicsContext::fillPath()
520 if (paintingDisabled())
523 QPainter* p = m_data->p();
524 QPainterPath& path = m_data->currentPath; // Avoid detaching the QPainterPath
525 path.setFillRule(toQtFillRule(fillRule()));
527 if (m_data->hasShadow()) {
528 p->translate(m_data->shadow.offset);
529 p->fillPath(path, m_data->shadow.color);
530 p->translate(-m_data->shadow.offset);
532 if (m_common->state.fillPattern) {
533 AffineTransform affine;
534 p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
535 } else if (m_common->state.fillGradient) {
536 QBrush brush(*m_common->state.fillGradient->platformGradient());
537 brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
538 p->fillPath(path, brush);
540 p->fillPath(path, p->brush());
542 m_data->currentPath = QPainterPath();
545 void GraphicsContext::strokePath()
547 if (paintingDisabled())
550 QPainter* p = m_data->p();
552 QPainterPath& path = m_data->currentPath; // Avoid detaching the QPainterPath
553 path.setFillRule(toQtFillRule(fillRule()));
555 if (m_data->hasShadow()) {
556 p->translate(m_data->shadow.offset);
558 shadowPen.setColor(m_data->shadow.color);
559 p->strokePath(path, shadowPen);
560 p->translate(-m_data->shadow.offset);
562 if (m_common->state.strokePattern) {
563 AffineTransform affine;
564 pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
566 p->strokePath(path, pen);
567 } else if (m_common->state.strokeGradient) {
568 QBrush brush(*m_common->state.strokeGradient->platformGradient());
569 brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
572 p->strokePath(path, pen);
574 p->strokePath(path, pen);
575 m_data->currentPath = QPainterPath();
578 static inline void drawRepeatPattern(QPainter* p, QPixmap* image, const FloatRect& rect, const bool repeatX, const bool repeatY)
580 // Patterns must be painted so that the top left of the first image is anchored at
581 // the origin of the coordinate space
583 int w = image->width();
584 int h = image->height();
586 QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
588 // startX, startY is the coordinate of the first image we need to put on the left-top of the rect
589 if (repeatX && repeatY) {
591 // startX, startY is at the left top side of the left-top of the rect
592 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
593 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
595 if (!repeatX && !repeatY) {
597 // only draw the image once at orgin once, check if need to draw
598 QRect imageRect(0, 0, w, h);
599 if (imageRect.intersects(r)) {
604 } else if (repeatX && !repeatY) {
606 // startY is fixed, but startX change based on the left-top of the rect
607 QRect imageRect(r.x(), 0, r.width(), h);
608 if (imageRect.intersects(r)) {
609 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
615 // startX is fixed, but startY change based on the left-top of the rect
616 QRect imageRect(0, r.y(), w, r.height());
617 if (imageRect.intersects(r)) {
619 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
631 QRect imageRect(x, y, w, h);
632 QRect intersectRect = imageRect.intersected(r);
633 QPoint destStart(intersectRect.x(), intersectRect.y());
634 QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height());
636 p->drawPixmap(destStart, *image, sourceRect);
638 } while (repeatX && x < r.x() + r.width());
641 } while (repeatY && y < r.y() + r.height());
645 void GraphicsContext::fillRect(const FloatRect& rect)
647 if (paintingDisabled())
650 QPainter* p = m_data->p();
651 QRectF normalizedRect = rect.normalized();
652 ContextShadow* shadow = contextShadow();
654 if (m_common->state.fillPattern) {
655 AffineTransform affine;
656 QBrush brush(m_common->state.fillPattern->createPlatformPattern(affine));
657 QPixmap* image = m_common->state.fillPattern->tileImage()->nativeImageForCurrentFrame();
658 QPainter* shadowPainter = m_data->hasShadow() ? shadow->beginShadowLayer(p, normalizedRect) : 0;
660 drawRepeatPattern(shadowPainter, image, normalizedRect, m_common->state.fillPattern->repeatX(), m_common->state.fillPattern->repeatY());
661 shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
662 shadowPainter->fillRect(normalizedRect, shadow->color);
663 shadow->endShadowLayer(p);
665 drawRepeatPattern(p, image, normalizedRect, m_common->state.fillPattern->repeatX(), m_common->state.fillPattern->repeatY());
666 } else if (m_common->state.fillGradient) {
667 QBrush brush(*m_common->state.fillGradient->platformGradient());
668 brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
669 QPainter* shadowPainter = m_data->hasShadow() ? shadow->beginShadowLayer(p, normalizedRect) : 0;
671 shadowPainter->fillRect(normalizedRect, brush);
672 shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
673 shadowPainter->fillRect(normalizedRect, shadow->color);
674 shadow->endShadowLayer(p);
676 p->fillRect(normalizedRect, brush);
678 if (m_data->hasShadow()) {
679 if (shadow->type == ContextShadow::BlurShadow) {
680 QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect);
682 shadowPainter->fillRect(normalizedRect, p->brush());
683 shadow->endShadowLayer(p);
686 // Solid rectangle fill with no blur shadow can be done faster
687 // without using the shadow layer at all.
688 QColor shadowColor = shadow->color;
689 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
690 p->fillRect(normalizedRect.translated(shadow->offset), shadowColor);
693 p->fillRect(normalizedRect, p->brush());
698 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
700 if (paintingDisabled() || !color.isValid())
703 m_data->solidColor.setColor(color);
704 QPainter* p = m_data->p();
705 QRectF normalizedRect = rect.normalized();
707 if (m_data->hasShadow()) {
708 ContextShadow* shadow = contextShadow();
710 if (shadow->type != ContextShadow::BlurShadow) {
711 // We do not need any layer for simple shadow.
712 p->fillRect(normalizedRect.translated(shadow->offset), shadow->color);
714 QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect);
716 shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
717 shadowPainter->fillRect(normalizedRect, shadow->color);
718 shadow->endShadowLayer(p);
723 p->fillRect(normalizedRect, m_data->solidColor);
726 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
728 if (paintingDisabled() || !color.isValid())
731 Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight);
732 QPainter* p = m_data->p();
733 if (m_data->hasShadow()) {
734 p->translate(m_data->shadow.offset);
735 p->fillPath(path.platformPath(), m_data->shadow.color);
736 p->translate(-m_data->shadow.offset);
738 p->fillPath(path.platformPath(), QColor(color));
741 void GraphicsContext::beginPath()
743 m_data->currentPath = QPainterPath();
746 void GraphicsContext::addPath(const Path& path)
748 if (!m_data->currentPath.elementCount()) {
749 m_data->currentPath = path.platformPath();
752 m_data->currentPath.addPath(path.platformPath());
755 bool GraphicsContext::inTransparencyLayer() const
757 return m_data->layerCount;
760 PlatformPath* GraphicsContext::currentPath()
762 return &m_data->currentPath;
765 ContextShadow* GraphicsContext::contextShadow()
767 return &m_data->shadow;
770 void GraphicsContext::clip(const FloatRect& rect)
772 if (paintingDisabled())
775 m_data->p()->setClipRect(rect, Qt::IntersectClip);
778 void GraphicsContext::clipPath(WindRule clipRule)
780 if (paintingDisabled())
783 QPainter* p = m_data->p();
784 QPainterPath newPath = m_data->currentPath;
785 newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
786 p->setClipPath(newPath);
789 void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
795 * Focus ring handling is not handled here. Qt style in
796 * RenderTheme handles drawing focus on widgets which
799 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
801 if (paintingDisabled() || !color.isValid())
804 unsigned rectCount = rects.size();
809 QPainter* p = m_data->p();
810 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
811 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
813 const QPen oldPen = p->pen();
814 const QBrush oldBrush = p->brush();
816 QPen nPen = p->pen();
817 nPen.setColor(color);
818 p->setBrush(Qt::NoBrush);
819 nPen.setStyle(Qt::DotLine);
822 // FIXME How do we do a bounding outline with Qt?
824 for (int i = 0; i < rectCount; ++i)
825 path.addRect(QRectF(rects[i]));
826 QPainterPathStroker stroker;
827 QPainterPath newPath = stroker.createStroke(path);
828 p->strokePath(newPath, nPen);
830 for (unsigned i = 0; i < rectCount; ++i)
831 p->drawRect(QRectF(rects[i]));
834 p->setBrush(oldBrush);
836 p->setRenderHint(QPainter::Antialiasing, antiAlias);
839 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool)
841 if (paintingDisabled())
844 IntPoint endPoint = origin + IntSize(width, 0);
845 drawLine(origin, endPoint);
848 void GraphicsContext::drawLineForTextChecking(const IntPoint&, int, TextCheckingLineStyle)
850 if (paintingDisabled())
856 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
858 // It is not enough just to round to pixels in device space. The rotation part of the
859 // affine transform matrix to device space can mess with this conversion if we have a
860 // rotating image like the hands of the world clock widget. We just need the scale, so
861 // we get the affine transform matrix and extract the scale.
862 QPainter* painter = platformContext();
863 QTransform deviceTransform = painter->deviceTransform();
864 if (deviceTransform.isIdentity())
867 qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12());
868 qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22());
870 QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY);
871 QPoint deviceLowerRight(frect.right() * deviceScaleX, frect.bottom() * deviceScaleY);
873 // Don't let the height or width round to 0 unless either was originally 0
874 if (deviceOrigin.y() == deviceLowerRight.y() && frect.height())
875 deviceLowerRight.setY(deviceLowerRight.y() + 1);
876 if (deviceOrigin.x() == deviceLowerRight.x() && frect.width())
877 deviceLowerRight.setX(deviceLowerRight.x() + 1);
879 FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY);
880 FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY);
881 return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
884 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
886 // Qt doesn't support shadows natively, they are drawn manually in the draw*
889 if (m_common->state.shadowsIgnoreTransforms) {
890 // Meaning that this graphics context is associated with a CanvasRenderingContext
891 // We flip the height since CG and HTML5 Canvas have opposite Y axis
892 m_common->state.shadowOffset = FloatSize(size.width(), -size.height());
893 m_data->shadow = ContextShadow(color, blur, size.width(), -size.height());
895 m_data->shadow = ContextShadow(color, blur, size.width(), size.height());
899 void GraphicsContext::clearPlatformShadow()
901 m_data->shadow.clear();
904 void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask)
906 m_data->layers.push(new TransparencyLayer(m_data->p(), m_data->p()->transform().mapRect(rect), 1.0, alphaMask));
909 void GraphicsContext::beginTransparencyLayer(float opacity)
911 if (paintingDisabled())
916 QPainter* p = m_data->p();
917 const QPaintDevice* device = p->device();
919 h = device->height();
921 QRectF clip = m_data->clipBoundingRect();
922 QRectF deviceClip = p->transform().mapRect(clip);
923 x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
924 y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
925 w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
926 h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
928 QPixmap emptyAlphaMask;
929 m_data->layers.push(new TransparencyLayer(m_data->p(), QRect(x, y, w, h), opacity, emptyAlphaMask));
930 ++m_data->layerCount;
933 void GraphicsContext::endTransparencyLayer()
935 if (paintingDisabled())
938 TransparencyLayer* layer = m_data->layers.pop();
939 if (!layer->alphaMask.isNull()) {
940 layer->painter.resetTransform();
941 layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
942 layer->painter.drawPixmap(QPoint(), layer->alphaMask);
944 --m_data->layerCount; // see the comment for layerCount
945 layer->painter.end();
947 QPainter* p = m_data->p();
950 p->setOpacity(layer->opacity);
951 p->drawPixmap(layer->offset, layer->pixmap);
957 void GraphicsContext::clearRect(const FloatRect& rect)
959 if (paintingDisabled())
962 QPainter* p = m_data->p();
963 QPainter::CompositionMode currentCompositionMode = p->compositionMode();
964 if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
965 p->setCompositionMode(QPainter::CompositionMode_Source);
966 p->fillRect(rect, Qt::transparent);
967 if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
968 p->setCompositionMode(currentCompositionMode);
971 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
973 if (paintingDisabled())
978 setStrokeThickness(width);
979 m_data->currentPath = path;
984 void GraphicsContext::setLineCap(LineCap lc)
986 if (paintingDisabled())
989 QPainter* p = m_data->p();
990 QPen nPen = p->pen();
991 nPen.setCapStyle(toQtLineCap(lc));
995 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
997 QPainter* p = m_data->p();
999 unsigned dashLength = dashes.size();
1001 QVector<qreal> pattern;
1002 unsigned count = dashLength;
1006 float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
1007 for (unsigned i = 0; i < count; i++)
1008 pattern.append(dashes[i % dashLength] / penWidth);
1010 pen.setDashPattern(pattern);
1011 pen.setDashOffset(dashOffset);
1013 pen.setStyle(Qt::SolidLine);
1017 void GraphicsContext::setLineJoin(LineJoin lj)
1019 if (paintingDisabled())
1022 QPainter* p = m_data->p();
1023 QPen nPen = p->pen();
1024 nPen.setJoinStyle(toQtLineJoin(lj));
1028 void GraphicsContext::setMiterLimit(float limit)
1030 if (paintingDisabled())
1033 QPainter* p = m_data->p();
1034 QPen nPen = p->pen();
1035 nPen.setMiterLimit(limit);
1039 void GraphicsContext::setAlpha(float opacity)
1041 if (paintingDisabled())
1043 QPainter* p = m_data->p();
1044 p->setOpacity(opacity);
1047 void GraphicsContext::setCompositeOperation(CompositeOperator op)
1049 if (paintingDisabled())
1052 if (m_data->p()->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
1053 m_data->p()->setCompositionMode(toQtCompositionMode(op));
1056 void GraphicsContext::clip(const Path& path)
1058 if (paintingDisabled())
1061 m_data->p()->setClipPath(path.platformPath(), Qt::IntersectClip);
1064 void GraphicsContext::canvasClip(const Path& path)
1066 if (paintingDisabled())
1069 QPainterPath clipPath = path.platformPath();
1070 clipPath.setFillRule(Qt::WindingFill);
1071 m_data->p()->setClipPath(clipPath, Qt::IntersectClip);
1074 void GraphicsContext::clipOut(const Path& path)
1076 if (paintingDisabled())
1079 QPainter* p = m_data->p();
1080 QPainterPath clippedOut = path.platformPath();
1081 QPainterPath newClip;
1082 newClip.setFillRule(Qt::OddEvenFill);
1083 if (p->hasClipping()) {
1084 newClip.addRect(m_data->clipBoundingRect());
1085 newClip.addPath(clippedOut);
1086 p->setClipPath(newClip, Qt::IntersectClip);
1088 QRect windowRect = p->transform().inverted().mapRect(p->window());
1089 newClip.addRect(windowRect);
1090 newClip.addPath(clippedOut.intersected(newClip));
1091 p->setClipPath(newClip);
1095 void GraphicsContext::translate(float x, float y)
1097 if (paintingDisabled())
1100 m_data->p()->translate(x, y);
1102 if (!m_data->currentPath.isEmpty()) {
1104 m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y);
1105 m_common->state.pathTransform.translate(x, y);
1109 IntPoint GraphicsContext::origin()
1111 if (paintingDisabled())
1113 const QTransform &transform = m_data->p()->transform();
1114 return IntPoint(qRound(transform.dx()), qRound(transform.dy()));
1117 void GraphicsContext::rotate(float radians)
1119 if (paintingDisabled())
1122 m_data->p()->rotate(180 / M_PI*radians);
1124 if (!m_data->currentPath.isEmpty()) {
1126 m_data->currentPath = m_data->currentPath * matrix.rotate(-180 / M_PI*radians);
1127 m_common->state.pathTransform.rotate(radians);
1131 void GraphicsContext::scale(const FloatSize& s)
1133 if (paintingDisabled())
1136 m_data->p()->scale(s.width(), s.height());
1138 if (!m_data->currentPath.isEmpty()) {
1140 m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height());
1141 m_common->state.pathTransform.scaleNonUniform(s.width(), s.height());
1145 void GraphicsContext::clipOut(const IntRect& rect)
1147 if (paintingDisabled())
1150 QPainter* p = m_data->p();
1151 QPainterPath newClip;
1152 newClip.setFillRule(Qt::OddEvenFill);
1153 if (p->hasClipping()) {
1154 newClip.addRect(m_data->clipBoundingRect());
1155 newClip.addRect(QRect(rect));
1156 p->setClipPath(newClip, Qt::IntersectClip);
1158 QRect clipOutRect(rect);
1159 QRect window = p->transform().inverted().mapRect(p->window());
1160 clipOutRect &= window;
1161 newClip.addRect(window);
1162 newClip.addRect(clipOutRect);
1163 p->setClipPath(newClip);
1167 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
1169 if (paintingDisabled())
1172 QPainter* p = m_data->p();
1173 QPainterPath newClip;
1174 newClip.setFillRule(Qt::OddEvenFill);
1175 if (p->hasClipping()) {
1176 newClip.addRect(m_data->clipBoundingRect());
1177 newClip.addEllipse(QRect(rect));
1178 p->setClipPath(newClip, Qt::IntersectClip);
1180 QRect clipOutRect(rect);
1181 QRect window(p->window());
1182 clipOutRect &= window;
1183 newClip.addRect(window);
1184 newClip.addEllipse(clipOutRect);
1185 p->setClipPath(newClip);
1189 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1192 if (paintingDisabled())
1198 // Add outer ellipse
1199 path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1201 // Add inner ellipse.
1202 path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1203 rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1205 path.setFillRule(Qt::OddEvenFill);
1207 QPainter* p = m_data->p();
1209 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
1210 p->setRenderHint(QPainter::Antialiasing, true);
1211 p->setClipPath(path, Qt::IntersectClip);
1212 p->setRenderHint(QPainter::Antialiasing, antiAlias);
1215 void GraphicsContext::concatCTM(const AffineTransform& transform)
1217 if (paintingDisabled())
1220 m_data->p()->setWorldTransform(transform, true);
1222 // Transformations to the context shouldn't transform the currentPath.
1223 // We have to undo every change made to the context from the currentPath
1224 // to avoid wrong drawings.
1225 if (!m_data->currentPath.isEmpty() && transform.isInvertible()) {
1226 QTransform matrix = transform.inverse();
1227 m_data->currentPath = m_data->currentPath * matrix;
1228 m_common->state.pathTransform.multiply(transform.toTransformationMatrix());
1232 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
1237 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1239 if (paintingDisabled() || !color.isValid())
1242 QPainter* p = m_data->p();
1243 QPen newPen(p->pen());
1244 m_data->solidColor.setColor(color);
1245 newPen.setBrush(m_data->solidColor);
1249 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
1251 if (paintingDisabled())
1253 QPainter* p = m_data->p();
1254 QPen newPen(p->pen());
1255 newPen.setStyle(toQPenStyle(strokeStyle));
1259 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1261 if (paintingDisabled())
1263 QPainter* p = m_data->p();
1264 QPen newPen(p->pen());
1265 newPen.setWidthF(thickness);
1269 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1271 if (paintingDisabled() || !color.isValid())
1274 m_data->solidColor.setColor(color);
1275 m_data->p()->setBrush(m_data->solidColor);
1278 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1280 if (paintingDisabled())
1282 m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1287 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1289 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1290 Q_ASSERT(mayCreateBitmap);
1292 if (dstRect.isEmpty())
1295 // Create a bitmap DC in which to draw.
1296 BITMAPINFO bitmapInfo;
1297 bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1298 bitmapInfo.bmiHeader.biWidth = dstRect.width();
1299 bitmapInfo.bmiHeader.biHeight = dstRect.height();
1300 bitmapInfo.bmiHeader.biPlanes = 1;
1301 bitmapInfo.bmiHeader.biBitCount = 32;
1302 bitmapInfo.bmiHeader.biCompression = BI_RGB;
1303 bitmapInfo.bmiHeader.biSizeImage = 0;
1304 bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
1305 bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
1306 bitmapInfo.bmiHeader.biClrUsed = 0;
1307 bitmapInfo.bmiHeader.biClrImportant = 0;
1310 HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1314 HDC displayDC = ::GetDC(0);
1315 HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1316 ::ReleaseDC(0, displayDC);
1318 ::SelectObject(bitmapDC, bitmap);
1320 // Fill our buffer with clear if we're going to alpha blend.
1321 if (supportAlphaBlend) {
1323 GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1324 int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1325 memset(bmpInfo.bmBits, 0, bufferSize);
1329 // Make sure we can do world transforms.
1330 SetGraphicsMode(bitmapDC, GM_ADVANCED);
1332 // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1338 xform.eDx = -dstRect.x();
1339 xform.eDy = -dstRect.y();
1340 ::SetWorldTransform(bitmapDC, &xform);
1346 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1348 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1349 Q_ASSERT(mayCreateBitmap);
1353 if (!dstRect.isEmpty()) {
1355 HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1357 GetObject(bitmap, sizeof(info), &info);
1358 ASSERT(info.bmBitsPixel == 32);
1360 QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
1361 m_data->p()->drawPixmap(dstRect, pixmap);
1363 ::DeleteObject(bitmap);
1371 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1373 m_data->imageInterpolationQuality = quality;
1376 case InterpolationNone:
1377 case InterpolationLow:
1378 // use nearest-neigbor
1379 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
1382 case InterpolationDefault:
1383 case InterpolationMedium:
1384 case InterpolationHigh:
1387 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
1392 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1394 return m_data->imageInterpolationQuality;
1399 // vim: ts=4 sw=4 et