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 * Copyright (C) 2010, 2011 Sencha, Inc.
12 * Copyright (C) 2011 Andreas Kling <kling@webkit.org>
13 *
14 * All rights reserved.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
26 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
33 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include "config.h"
39 #include "GraphicsContext.h"
40
41 #ifdef Q_WS_WIN
42 #include <windows.h>
43 #endif
44
45 #include "AffineTransform.h"
46 #include "Color.h"
47 #include "ContextShadow.h"
48 #include "FloatConversion.h"
49 #include "Font.h"
50 #include "ImageBuffer.h"
51 #include "NotImplemented.h"
52 #include "Path.h"
53 #include "Pattern.h"
54 #include "TransparencyLayer.h"
55
56 #include <QBrush>
57 #include <QGradient>
58 #include <QPaintDevice>
59 #include <QPaintEngine>
60 #include <QPainter>
61 #include <QPainterPath>
62 #include <QPixmap>
63 #include <QPolygonF>
64 #include <QStack>
65 #include <QVector>
66 #include <wtf/MathExtras.h>
67
68 namespace WebCore {
69
toQtCompositionMode(CompositeOperator op)70 static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
71 {
72 switch (op) {
73 case CompositeClear:
74 return QPainter::CompositionMode_Clear;
75 case CompositeCopy:
76 return QPainter::CompositionMode_Source;
77 case CompositeSourceOver:
78 return QPainter::CompositionMode_SourceOver;
79 case CompositeSourceIn:
80 return QPainter::CompositionMode_SourceIn;
81 case CompositeSourceOut:
82 return QPainter::CompositionMode_SourceOut;
83 case CompositeSourceAtop:
84 return QPainter::CompositionMode_SourceAtop;
85 case CompositeDestinationOver:
86 return QPainter::CompositionMode_DestinationOver;
87 case CompositeDestinationIn:
88 return QPainter::CompositionMode_DestinationIn;
89 case CompositeDestinationOut:
90 return QPainter::CompositionMode_DestinationOut;
91 case CompositeDestinationAtop:
92 return QPainter::CompositionMode_DestinationAtop;
93 case CompositeXOR:
94 return QPainter::CompositionMode_Xor;
95 case CompositePlusDarker:
96 // there is no exact match, but this is the closest
97 return QPainter::CompositionMode_Darken;
98 case CompositeHighlight:
99 return QPainter::CompositionMode_SourceOver;
100 case CompositePlusLighter:
101 return QPainter::CompositionMode_Plus;
102 default:
103 ASSERT_NOT_REACHED();
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 default:
119 ASSERT_NOT_REACHED();
120 }
121
122 return Qt::FlatCap;
123 }
124
toQtLineJoin(LineJoin lj)125 static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
126 {
127 switch (lj) {
128 case MiterJoin:
129 return Qt::SvgMiterJoin;
130 case RoundJoin:
131 return Qt::RoundJoin;
132 case BevelJoin:
133 return Qt::BevelJoin;
134 default:
135 ASSERT_NOT_REACHED();
136 }
137
138 return Qt::SvgMiterJoin;
139 }
140
toQPenStyle(StrokeStyle style)141 static Qt::PenStyle toQPenStyle(StrokeStyle style)
142 {
143 switch (style) {
144 case NoStroke:
145 return Qt::NoPen;
146 break;
147 case SolidStroke:
148 return Qt::SolidLine;
149 break;
150 case DottedStroke:
151 return Qt::DotLine;
152 break;
153 case DashedStroke:
154 return Qt::DashLine;
155 break;
156 default:
157 ASSERT_NOT_REACHED();
158 }
159 return Qt::NoPen;
160 }
161
toQtFillRule(WindRule rule)162 static inline Qt::FillRule toQtFillRule(WindRule rule)
163 {
164 switch (rule) {
165 case RULE_EVENODD:
166 return Qt::OddEvenFill;
167 case RULE_NONZERO:
168 return Qt::WindingFill;
169 default:
170 ASSERT_NOT_REACHED();
171 }
172 return Qt::OddEvenFill;
173 }
174
175 class GraphicsContextPlatformPrivate {
176 WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); WTF_MAKE_FAST_ALLOCATED;
177 public:
178 GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor);
179 ~GraphicsContextPlatformPrivate();
180
p() const181 inline QPainter* p() const
182 {
183 if (layers.isEmpty())
184 return painter;
185 return &layers.top()->painter;
186 }
187
188 bool antiAliasingForRectsAndLines;
189
190 QStack<TransparencyLayer*> layers;
191 // Counting real layers. Required by inTransparencyLayer() calls
192 // For example, layers with valid alphaMask are not real layers
193 int layerCount;
194
195 // reuse this brush for solid color (to prevent expensive QBrush construction)
196 QBrush solidColor;
197
198 InterpolationQuality imageInterpolationQuality;
199 bool initialSmoothPixmapTransformHint;
200
201 ContextShadow shadow;
202 QStack<ContextShadow> shadowStack;
203
clipBoundingRect() const204 QRectF clipBoundingRect() const
205 {
206 #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
207 return p()->clipBoundingRect();
208 #else
209 return p()->clipRegion().boundingRect();
210 #endif
211 }
212
takeOwnershipOfPlatformContext()213 void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; }
214
215 private:
216 QPainter* painter;
217 bool platformContextIsOwned;
218 };
219
GraphicsContextPlatformPrivate(QPainter * p,const QColor & initialSolidColor)220 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, const QColor& initialSolidColor)
221 : antiAliasingForRectsAndLines(false)
222 , layerCount(0)
223 , solidColor(initialSolidColor)
224 , imageInterpolationQuality(InterpolationDefault)
225 , initialSmoothPixmapTransformHint(false)
226 , painter(p)
227 , platformContextIsOwned(false)
228 {
229 if (!painter)
230 return;
231
232 #if OS(SYMBIAN)
233 if (painter->paintEngine()->type() == QPaintEngine::OpenVG)
234 antiAliasingForRectsAndLines = true;
235 else
236 antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
237 #else
238 // Use the default the QPainter was constructed with.
239 antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
240 #endif
241
242 // Used for default image interpolation quality.
243 initialSmoothPixmapTransformHint = painter->testRenderHint(QPainter::SmoothPixmapTransform);
244
245 painter->setRenderHint(QPainter::Antialiasing, true);
246 }
247
~GraphicsContextPlatformPrivate()248 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
249 {
250 if (!platformContextIsOwned)
251 return;
252
253 QPaintDevice* device = painter->device();
254 painter->end();
255 delete painter;
256 delete device;
257 }
258
platformInit(PlatformGraphicsContext * painter)259 void GraphicsContext::platformInit(PlatformGraphicsContext* painter)
260 {
261 m_data = new GraphicsContextPlatformPrivate(painter, fillColor());
262
263 setPaintingDisabled(!painter);
264
265 if (!painter)
266 return;
267
268 // solidColor is initialized with the fillColor().
269 painter->setBrush(m_data->solidColor);
270
271 QPen pen(painter->pen());
272 pen.setColor(strokeColor());
273 pen.setJoinStyle(toQtLineJoin(MiterJoin));
274 painter->setPen(pen);
275 }
276
platformDestroy()277 void GraphicsContext::platformDestroy()
278 {
279 while (!m_data->layers.isEmpty())
280 endTransparencyLayer();
281
282 delete m_data;
283 }
284
platformContext() const285 PlatformGraphicsContext* GraphicsContext::platformContext() const
286 {
287 return m_data->p();
288 }
289
getCTM() const290 AffineTransform GraphicsContext::getCTM() const
291 {
292 const QTransform& matrix = platformContext()->combinedTransform();
293 return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
294 matrix.m22(), matrix.dx(), matrix.dy());
295 }
296
savePlatformState()297 void GraphicsContext::savePlatformState()
298 {
299 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
300 ++m_data->layers.top()->saveCounter;
301 m_data->p()->save();
302 m_data->shadowStack.push(m_data->shadow);
303 }
304
restorePlatformState()305 void GraphicsContext::restorePlatformState()
306 {
307 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
308 if (!--m_data->layers.top()->saveCounter)
309 endTransparencyLayer();
310
311 m_data->p()->restore();
312
313 if (m_data->shadowStack.isEmpty())
314 m_data->shadow = ContextShadow();
315 else
316 m_data->shadow = m_data->shadowStack.pop();
317 }
318
319 // Draws a filled rectangle with a stroked border.
320 // This is only used to draw borders (real fill is done via fillRect), and
321 // thus it must not cast any shadow.
drawRect(const IntRect & rect)322 void GraphicsContext::drawRect(const IntRect& rect)
323 {
324 if (paintingDisabled())
325 return;
326
327 QPainter* p = m_data->p();
328 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
329 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
330
331 p->drawRect(rect);
332
333 p->setRenderHint(QPainter::Antialiasing, antiAlias);
334 }
335
336 // This is only used to draw borders.
337 // Must not cast any shadow.
drawLine(const IntPoint & point1,const IntPoint & point2)338 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
339 {
340 if (paintingDisabled())
341 return;
342
343 StrokeStyle style = strokeStyle();
344 Color color = strokeColor();
345 if (style == NoStroke)
346 return;
347
348 float width = strokeThickness();
349
350 FloatPoint p1 = point1;
351 FloatPoint p2 = point2;
352 bool isVerticalLine = (p1.x() == p2.x());
353
354 QPainter* p = m_data->p();
355 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
356 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
357 adjustLineToPixelBoundaries(p1, p2, width, style);
358
359 int patWidth = 0;
360 switch (style) {
361 case NoStroke:
362 case SolidStroke:
363 break;
364 case DottedStroke:
365 patWidth = static_cast<int>(width);
366 break;
367 case DashedStroke:
368 patWidth = 3 * static_cast<int>(width);
369 break;
370 }
371
372 if (patWidth) {
373 p->save();
374
375 // Do a rect fill of our endpoints. This ensures we always have the
376 // appearance of being a border. We then draw the actual dotted/dashed line.
377 if (isVerticalLine) {
378 p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
379 p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
380 } else {
381 p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
382 p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
383 }
384
385 // Example: 80 pixels with a width of 30 pixels.
386 // Remainder is 20. The maximum pixels of line we could paint
387 // will be 50 pixels.
388 int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
389 int remainder = distance % patWidth;
390 int coverage = distance - remainder;
391 int numSegments = coverage / patWidth;
392
393 float patternOffset = 0.0f;
394 // Special case 1px dotted borders for speed.
395 if (patWidth == 1)
396 patternOffset = 1.0f;
397 else {
398 bool evenNumberOfSegments = !(numSegments % 2);
399 if (remainder)
400 evenNumberOfSegments = !evenNumberOfSegments;
401 if (evenNumberOfSegments) {
402 if (remainder) {
403 patternOffset += patWidth - remainder;
404 patternOffset += remainder / 2;
405 } else
406 patternOffset = patWidth / 2;
407 } else {
408 if (remainder)
409 patternOffset = (patWidth - remainder) / 2;
410 }
411 }
412
413 QVector<qreal> dashes;
414 dashes << qreal(patWidth) / width << qreal(patWidth) / width;
415
416 QPen pen = p->pen();
417 pen.setWidthF(width);
418 pen.setCapStyle(Qt::FlatCap);
419 pen.setDashPattern(dashes);
420 pen.setDashOffset(patternOffset / width);
421 p->setPen(pen);
422 }
423
424 p->drawLine(p1, p2);
425
426 if (patWidth)
427 p->restore();
428
429 p->setRenderHint(QPainter::Antialiasing, antiAlias);
430 }
431
432 // This method is only used to draw the little circles used in lists.
drawEllipse(const IntRect & rect)433 void GraphicsContext::drawEllipse(const IntRect& rect)
434 {
435 if (paintingDisabled())
436 return;
437
438 m_data->p()->drawEllipse(rect);
439 }
440
drawConvexPolygon(size_t npoints,const FloatPoint * points,bool shouldAntialias)441 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
442 {
443 if (paintingDisabled())
444 return;
445
446 if (npoints <= 1)
447 return;
448
449 QPolygonF polygon(npoints);
450
451 for (size_t i = 0; i < npoints; i++)
452 polygon[i] = points[i];
453
454 QPainter* p = m_data->p();
455
456 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
457 p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
458
459 p->drawConvexPolygon(polygon);
460
461 p->setRenderHint(QPainter::Antialiasing, antiAlias);
462 }
463
clipConvexPolygon(size_t numPoints,const FloatPoint * points,bool antialiased)464 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
465 {
466 if (paintingDisabled())
467 return;
468
469 if (numPoints <= 1)
470 return;
471
472 QPainterPath path(points[0]);
473 for (size_t i = 1; i < numPoints; ++i)
474 path.lineTo(points[i]);
475 path.setFillRule(Qt::WindingFill);
476
477 QPainter* p = m_data->p();
478
479 bool painterWasAntialiased = p->testRenderHint(QPainter::Antialiasing);
480
481 if (painterWasAntialiased != antialiased)
482 p->setRenderHint(QPainter::Antialiasing, antialiased);
483
484 p->setClipPath(path, Qt::IntersectClip);
485
486 if (painterWasAntialiased != antialiased)
487 p->setRenderHint(QPainter::Antialiasing, painterWasAntialiased);
488 }
489
fillPath(const Path & path)490 void GraphicsContext::fillPath(const Path& path)
491 {
492 if (paintingDisabled())
493 return;
494
495 QPainter* p = m_data->p();
496 QPainterPath platformPath = path.platformPath();
497 platformPath.setFillRule(toQtFillRule(fillRule()));
498
499 if (hasShadow()) {
500 ContextShadow* shadow = contextShadow();
501 if (shadow->mustUseContextShadow(this) || m_state.fillPattern || m_state.fillGradient)
502 {
503 QPainter* shadowPainter = shadow->beginShadowLayer(this, platformPath.controlPointRect());
504 if (shadowPainter) {
505 if (m_state.fillPattern) {
506 AffineTransform affine;
507 shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255);
508 shadowPainter->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern(affine)));
509 } else if (m_state.fillGradient) {
510 QBrush brush(*m_state.fillGradient->platformGradient());
511 brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
512 shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255);
513 shadowPainter->fillPath(platformPath, brush);
514 } else {
515 QColor shadowColor = shadow->m_color;
516 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
517 shadowPainter->fillPath(platformPath, shadowColor);
518 }
519 shadow->endShadowLayer(this);
520 }
521 } else {
522 QPointF offset = shadow->offset();
523 p->translate(offset);
524 QColor shadowColor = shadow->m_color;
525 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
526 p->fillPath(platformPath, shadowColor);
527 p->translate(-offset);
528 }
529 }
530 if (m_state.fillPattern) {
531 AffineTransform affine;
532 p->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern(affine)));
533 } else if (m_state.fillGradient) {
534 QBrush brush(*m_state.fillGradient->platformGradient());
535 brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
536 p->fillPath(platformPath, brush);
537 } else
538 p->fillPath(platformPath, p->brush());
539 }
540
strokePath(const Path & path)541 void GraphicsContext::strokePath(const Path& path)
542 {
543 if (paintingDisabled())
544 return;
545
546 QPainter* p = m_data->p();
547 QPen pen(p->pen());
548 QPainterPath platformPath = path.platformPath();
549 platformPath.setFillRule(toQtFillRule(fillRule()));
550
551 if (hasShadow()) {
552 ContextShadow* shadow = contextShadow();
553 if (shadow->mustUseContextShadow(this) || m_state.strokePattern || m_state.strokeGradient)
554 {
555 FloatRect boundingRect = platformPath.controlPointRect();
556 boundingRect.inflate(pen.miterLimit() + pen.widthF());
557 QPainter* shadowPainter = shadow->beginShadowLayer(this, boundingRect);
558 if (shadowPainter) {
559 if (m_state.strokeGradient) {
560 QBrush brush(*m_state.strokeGradient->platformGradient());
561 brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
562 QPen shadowPen(pen);
563 shadowPen.setBrush(brush);
564 shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255);
565 shadowPainter->strokePath(platformPath, shadowPen);
566 } else {
567 shadowPainter->setOpacity(static_cast<qreal>(m_data->shadow.m_color.alpha()) / 255);
568 shadowPainter->strokePath(platformPath, pen);
569 }
570 shadow->endShadowLayer(this);
571 }
572 } else {
573 QPointF offset = shadow->offset();
574 p->translate(offset);
575 QColor shadowColor = shadow->m_color;
576 shadowColor.setAlphaF(shadowColor.alphaF() * pen.color().alphaF());
577 QPen shadowPen(pen);
578 shadowPen.setColor(shadowColor);
579 p->strokePath(platformPath, shadowPen);
580 p->translate(-offset);
581 }
582 }
583
584 if (m_state.strokePattern) {
585 AffineTransform affine;
586 pen.setBrush(QBrush(m_state.strokePattern->createPlatformPattern(affine)));
587 p->setPen(pen);
588 p->strokePath(platformPath, pen);
589 } else if (m_state.strokeGradient) {
590 QBrush brush(*m_state.strokeGradient->platformGradient());
591 brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
592 pen.setBrush(brush);
593 p->setPen(pen);
594 p->strokePath(platformPath, pen);
595 } else
596 p->strokePath(platformPath, pen);
597 }
598
drawRepeatPattern(QPainter * p,QPixmap * image,const FloatRect & rect,const bool repeatX,const bool repeatY)599 static inline void drawRepeatPattern(QPainter* p, QPixmap* image, const FloatRect& rect, const bool repeatX, const bool repeatY)
600 {
601 // Patterns must be painted so that the top left of the first image is anchored at
602 // the origin of the coordinate space
603 if (image) {
604 int w = image->width();
605 int h = image->height();
606 int startX, startY;
607 QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
608
609 // startX, startY is the coordinate of the first image we need to put on the left-top of the rect
610 if (repeatX && repeatY) {
611 // repeat
612 // startX, startY is at the left top side of the left-top of the rect
613 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
614 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
615 } else {
616 if (!repeatX && !repeatY) {
617 // no-repeat
618 // only draw the image once at orgin once, check if need to draw
619 QRect imageRect(0, 0, w, h);
620 if (imageRect.intersects(r)) {
621 startX = 0;
622 startY = 0;
623 } else
624 return;
625 } else if (repeatX && !repeatY) {
626 // repeat-x
627 // startY is fixed, but startX change based on the left-top of the rect
628 QRect imageRect(r.x(), 0, r.width(), h);
629 if (imageRect.intersects(r)) {
630 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
631 startY = 0;
632 } else
633 return;
634 } else {
635 // repeat-y
636 // startX is fixed, but startY change based on the left-top of the rect
637 QRect imageRect(0, r.y(), w, r.height());
638 if (imageRect.intersects(r)) {
639 startX = 0;
640 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
641 } else
642 return;
643 }
644 }
645
646 int x = startX;
647 int y = startY;
648 do {
649 // repeat Y
650 do {
651 // repeat X
652 QRect imageRect(x, y, w, h);
653 QRect intersectRect = imageRect.intersected(r);
654 QPoint destStart(intersectRect.x(), intersectRect.y());
655 QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height());
656
657 p->drawPixmap(destStart, *image, sourceRect);
658 x += w;
659 } while (repeatX && x < r.x() + r.width());
660 x = startX;
661 y += h;
662 } while (repeatY && y < r.y() + r.height());
663 }
664 }
665
fillRect(const FloatRect & rect)666 void GraphicsContext::fillRect(const FloatRect& rect)
667 {
668 if (paintingDisabled())
669 return;
670
671 QPainter* p = m_data->p();
672 QRectF normalizedRect = rect.normalized();
673 ContextShadow* shadow = contextShadow();
674
675 if (m_state.fillPattern) {
676 QPixmap* image = m_state.fillPattern->tileImage()->nativeImageForCurrentFrame();
677 QPainter* shadowPainter = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0;
678 if (shadowPainter) {
679 drawRepeatPattern(shadowPainter, image, normalizedRect, m_state.fillPattern->repeatX(), m_state.fillPattern->repeatY());
680 shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
681 shadowPainter->fillRect(normalizedRect, shadow->m_color);
682 shadow->endShadowLayer(this);
683 }
684 drawRepeatPattern(p, image, normalizedRect, m_state.fillPattern->repeatX(), m_state.fillPattern->repeatY());
685 } else if (m_state.fillGradient) {
686 QBrush brush(*m_state.fillGradient->platformGradient());
687 brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
688 QPainter* shadowPainter = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0;
689 if (shadowPainter) {
690 shadowPainter->fillRect(normalizedRect, brush);
691 shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
692 shadowPainter->fillRect(normalizedRect, shadow->m_color);
693 shadow->endShadowLayer(this);
694 }
695 p->fillRect(normalizedRect, brush);
696 } else {
697 if (hasShadow()) {
698 if (shadow->mustUseContextShadow(this)) {
699 QPainter* shadowPainter = shadow->beginShadowLayer(this, normalizedRect);
700 if (shadowPainter) {
701 shadowPainter->setOpacity(static_cast<qreal>(shadow->m_color.alpha()) / 255);
702 shadowPainter->fillRect(normalizedRect, p->brush());
703 shadow->endShadowLayer(this);
704 }
705 } else {
706 // Solid rectangle fill with no blur shadow or transformations applied can be done
707 // faster without using the shadow layer at all.
708 QColor shadowColor = shadow->m_color;
709 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
710 p->fillRect(normalizedRect.translated(shadow->offset()), shadowColor);
711 }
712 }
713
714 p->fillRect(normalizedRect, p->brush());
715 }
716 }
717
718
fillRect(const FloatRect & rect,const Color & color,ColorSpace colorSpace)719 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
720 {
721 if (paintingDisabled() || !color.isValid())
722 return;
723
724 m_data->solidColor.setColor(color);
725 QPainter* p = m_data->p();
726 QRectF normalizedRect = rect.normalized();
727
728 if (hasShadow()) {
729 ContextShadow* shadow = contextShadow();
730 if (shadow->mustUseContextShadow(this)) {
731 QPainter* shadowPainter = shadow->beginShadowLayer(this, normalizedRect);
732 if (shadowPainter) {
733 shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
734 shadowPainter->fillRect(normalizedRect, shadow->m_color);
735 shadow->endShadowLayer(this);
736 }
737 } else
738 p->fillRect(normalizedRect.translated(shadow->offset()), shadow->m_color);
739 }
740
741 p->fillRect(normalizedRect, m_data->solidColor);
742 }
743
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color,ColorSpace colorSpace)744 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
745 {
746 if (paintingDisabled() || !color.isValid())
747 return;
748
749 Path path;
750 path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
751 QPainter* p = m_data->p();
752 if (hasShadow()) {
753 ContextShadow* shadow = contextShadow();
754 if (shadow->mustUseContextShadow(this)) {
755 QPainter* shadowPainter = shadow->beginShadowLayer(this, rect);
756 if (shadowPainter) {
757 shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
758 shadowPainter->fillPath(path.platformPath(), QColor(m_data->shadow.m_color));
759 shadow->endShadowLayer(this);
760 }
761 } else {
762 p->translate(m_data->shadow.offset());
763 p->fillPath(path.platformPath(), QColor(m_data->shadow.m_color));
764 p->translate(-m_data->shadow.offset());
765 }
766 }
767 p->fillPath(path.platformPath(), QColor(color));
768 }
769
inTransparencyLayer() const770 bool GraphicsContext::inTransparencyLayer() const
771 {
772 return m_data->layerCount;
773 }
774
contextShadow()775 ContextShadow* GraphicsContext::contextShadow()
776 {
777 return &m_data->shadow;
778 }
779
clip(const IntRect & rect)780 void GraphicsContext::clip(const IntRect& rect)
781 {
782 if (paintingDisabled())
783 return;
784
785 m_data->p()->setClipRect(rect, Qt::IntersectClip);
786 }
787
clip(const FloatRect & rect)788 void GraphicsContext::clip(const FloatRect& rect)
789 {
790 if (paintingDisabled())
791 return;
792
793 m_data->p()->setClipRect(rect, Qt::IntersectClip);
794 }
795
clipPath(const Path & path,WindRule clipRule)796 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
797 {
798 if (paintingDisabled())
799 return;
800
801 QPainter* p = m_data->p();
802 QPainterPath platformPath = path.platformPath();
803 platformPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
804 p->setClipPath(platformPath, Qt::IntersectClip);
805 }
806
drawFocusRingForPath(QPainter * p,const QPainterPath & path,const Color & color,bool antiAliasing)807 void drawFocusRingForPath(QPainter* p, const QPainterPath& path, const Color& color, bool antiAliasing)
808 {
809 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
810 p->setRenderHint(QPainter::Antialiasing, antiAliasing);
811
812 const QPen oldPen = p->pen();
813 const QBrush oldBrush = p->brush();
814
815 QPen nPen = p->pen();
816 nPen.setColor(color);
817 p->setBrush(Qt::NoBrush);
818 nPen.setStyle(Qt::DotLine);
819
820 p->strokePath(path, nPen);
821 p->setBrush(oldBrush);
822 p->setPen(oldPen);
823
824 p->setRenderHint(QPainter::Antialiasing, antiAlias);
825 }
826
drawFocusRing(const Path & path,int,int offset,const Color & color)827 void GraphicsContext::drawFocusRing(const Path& path, int /* width */, int offset, const Color& color)
828 {
829 // FIXME: Use 'offset' for something? http://webkit.org/b/49909
830
831 if (paintingDisabled() || !color.isValid())
832 return;
833
834 drawFocusRingForPath(m_data->p(), path.platformPath(), color, m_data->antiAliasingForRectsAndLines);
835 }
836
837 /**
838 * Focus ring handling for form controls is not handled here. Qt style in
839 * RenderTheme handles drawing focus on widgets which
840 * need it. It is still handled here for links.
841 */
drawFocusRing(const Vector<IntRect> & rects,int width,int offset,const Color & color)842 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
843 {
844 if (paintingDisabled() || !color.isValid())
845 return;
846
847 unsigned rectCount = rects.size();
848
849 if (!rects.size())
850 return;
851
852 int radius = (width - 1) / 2;
853 QPainterPath path;
854 for (unsigned i = 0; i < rectCount; ++i) {
855 QRect rect = QRect((rects[i])).adjusted(-offset - radius, -offset - radius, offset + radius, offset + radius);
856 // This is not the most efficient way to add a rect to a path, but if we don't create the tmpPath,
857 // we will end up with ugly lines in between rows of text on anchors with multiple lines.
858 QPainterPath tmpPath;
859 tmpPath.addRoundedRect(rect, radius, radius);
860 path = path.united(tmpPath);
861 }
862 drawFocusRingForPath(m_data->p(), path, color, m_data->antiAliasingForRectsAndLines);
863 }
864
drawLineForText(const FloatPoint & origin,float width,bool)865 void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool)
866 {
867 if (paintingDisabled())
868 return;
869
870 FloatPoint startPoint = origin;
871 FloatPoint endPoint = origin + FloatSize(width, 0);
872
873 // If paintengine type is X11 to avoid artifacts
874 // like bug https://bugs.webkit.org/show_bug.cgi?id=42248
875 #if defined(Q_WS_X11)
876 QPainter* p = m_data->p();
877 if (p->paintEngine()->type() == QPaintEngine::X11) {
878 // If stroke thickness is odd we need decrease Y coordinate by 1 pixel,
879 // because inside method adjustLineToPixelBoundaries(...), which
880 // called from drawLine(...), Y coordinate will be increased by 0.5f
881 // and then inside Qt painting engine will be rounded to next greater
882 // integer value.
883 float strokeWidth = strokeThickness();
884 if (static_cast<int>(strokeWidth) % 2) {
885 startPoint.setY(startPoint.y() - 1);
886 endPoint.setY(endPoint.y() - 1);
887 }
888 }
889 #endif // defined(Q_WS_X11)
890
891 // FIXME: Loss of precision here. Might consider rounding.
892 drawLine(IntPoint(startPoint.x(), startPoint.y()), IntPoint(endPoint.x(), endPoint.y()));
893 }
894
drawLineForTextChecking(const FloatPoint &,float,TextCheckingLineStyle)895 void GraphicsContext::drawLineForTextChecking(const FloatPoint&, float, TextCheckingLineStyle)
896 {
897 if (paintingDisabled())
898 return;
899
900 notImplemented();
901 }
902
roundToDevicePixels(const FloatRect & frect,RoundingMode)903 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
904 {
905 // It is not enough just to round to pixels in device space. The rotation part of the
906 // affine transform matrix to device space can mess with this conversion if we have a
907 // rotating image like the hands of the world clock widget. We just need the scale, so
908 // we get the affine transform matrix and extract the scale.
909 QPainter* painter = platformContext();
910 QTransform deviceTransform = painter->deviceTransform();
911 if (deviceTransform.isIdentity())
912 return frect;
913
914 qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12());
915 qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22());
916
917 QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY);
918 QPoint deviceLowerRight(frect.maxX() * deviceScaleX, frect.maxY() * deviceScaleY);
919
920 // Don't let the height or width round to 0 unless either was originally 0
921 if (deviceOrigin.y() == deviceLowerRight.y() && frect.height())
922 deviceLowerRight.setY(deviceLowerRight.y() + 1);
923 if (deviceOrigin.x() == deviceLowerRight.x() && frect.width())
924 deviceLowerRight.setX(deviceLowerRight.x() + 1);
925
926 FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY);
927 FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY);
928 return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
929 }
930
setPlatformShadow(const FloatSize & size,float blur,const Color & color,ColorSpace)931 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
932 {
933 // Qt doesn't support shadows natively, they are drawn manually in the draw*
934 // functions
935
936 if (m_state.shadowsIgnoreTransforms) {
937 // Meaning that this graphics context is associated with a CanvasRenderingContext
938 // We flip the height since CG and HTML5 Canvas have opposite Y axis
939 m_state.shadowOffset = FloatSize(size.width(), -size.height());
940 m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height()));
941 } else
942 m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height()));
943
944 m_data->shadow.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms);
945 }
946
clearPlatformShadow()947 void GraphicsContext::clearPlatformShadow()
948 {
949 m_data->shadow.clear();
950 }
951
pushTransparencyLayerInternal(const QRect & rect,qreal opacity,QPixmap & alphaMask)952 void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask)
953 {
954 QPainter* p = m_data->p();
955 m_data->layers.push(new TransparencyLayer(p, p->transform().mapRect(rect), 1.0, alphaMask));
956 }
957
beginTransparencyLayer(float opacity)958 void GraphicsContext::beginTransparencyLayer(float opacity)
959 {
960 if (paintingDisabled())
961 return;
962
963 int x, y, w, h;
964 x = y = 0;
965 QPainter* p = m_data->p();
966 const QPaintDevice* device = p->device();
967 w = device->width();
968 h = device->height();
969
970 QRectF clip = m_data->clipBoundingRect();
971 QRectF deviceClip = p->transform().mapRect(clip);
972 x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
973 y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
974 w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
975 h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
976
977 QPixmap emptyAlphaMask;
978 m_data->layers.push(new TransparencyLayer(p, QRect(x, y, w, h), opacity, emptyAlphaMask));
979 ++m_data->layerCount;
980 }
981
endTransparencyLayer()982 void GraphicsContext::endTransparencyLayer()
983 {
984 if (paintingDisabled())
985 return;
986
987 TransparencyLayer* layer = m_data->layers.pop();
988 if (!layer->alphaMask.isNull()) {
989 layer->painter.resetTransform();
990 layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
991 layer->painter.drawPixmap(QPoint(), layer->alphaMask);
992 } else
993 --m_data->layerCount; // see the comment for layerCount
994 layer->painter.end();
995
996 QPainter* p = m_data->p();
997 p->save();
998 p->resetTransform();
999 p->setOpacity(layer->opacity);
1000 p->drawPixmap(layer->offset, layer->pixmap);
1001 p->restore();
1002
1003 delete layer;
1004 }
1005
clearRect(const FloatRect & rect)1006 void GraphicsContext::clearRect(const FloatRect& rect)
1007 {
1008 if (paintingDisabled())
1009 return;
1010
1011 QPainter* p = m_data->p();
1012 QPainter::CompositionMode currentCompositionMode = p->compositionMode();
1013 p->setCompositionMode(QPainter::CompositionMode_Source);
1014 p->fillRect(rect, Qt::transparent);
1015 p->setCompositionMode(currentCompositionMode);
1016 }
1017
strokeRect(const FloatRect & rect,float lineWidth)1018 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1019 {
1020 if (paintingDisabled())
1021 return;
1022
1023 Path path;
1024 path.addRect(rect);
1025
1026 float previousStrokeThickness = strokeThickness();
1027
1028 if (lineWidth != previousStrokeThickness)
1029 setStrokeThickness(lineWidth);
1030
1031 strokePath(path);
1032
1033 if (lineWidth != previousStrokeThickness)
1034 setStrokeThickness(previousStrokeThickness);
1035 }
1036
setLineCap(LineCap lc)1037 void GraphicsContext::setLineCap(LineCap lc)
1038 {
1039 if (paintingDisabled())
1040 return;
1041
1042 QPainter* p = m_data->p();
1043 QPen nPen = p->pen();
1044 nPen.setCapStyle(toQtLineCap(lc));
1045 p->setPen(nPen);
1046 }
1047
setLineDash(const DashArray & dashes,float dashOffset)1048 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1049 {
1050 QPainter* p = m_data->p();
1051 QPen pen = p->pen();
1052 unsigned dashLength = dashes.size();
1053 if (dashLength) {
1054 QVector<qreal> pattern;
1055 unsigned count = dashLength;
1056 if (dashLength % 2)
1057 count *= 2;
1058
1059 float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
1060 for (unsigned i = 0; i < count; i++)
1061 pattern.append(dashes[i % dashLength] / penWidth);
1062
1063 pen.setDashPattern(pattern);
1064 pen.setDashOffset(dashOffset / penWidth);
1065 } else
1066 pen.setStyle(Qt::SolidLine);
1067 p->setPen(pen);
1068 }
1069
setLineJoin(LineJoin lj)1070 void GraphicsContext::setLineJoin(LineJoin lj)
1071 {
1072 if (paintingDisabled())
1073 return;
1074
1075 QPainter* p = m_data->p();
1076 QPen nPen = p->pen();
1077 nPen.setJoinStyle(toQtLineJoin(lj));
1078 p->setPen(nPen);
1079 }
1080
setMiterLimit(float limit)1081 void GraphicsContext::setMiterLimit(float limit)
1082 {
1083 if (paintingDisabled())
1084 return;
1085
1086 QPainter* p = m_data->p();
1087 QPen nPen = p->pen();
1088 nPen.setMiterLimit(limit);
1089 p->setPen(nPen);
1090 }
1091
setAlpha(float opacity)1092 void GraphicsContext::setAlpha(float opacity)
1093 {
1094 if (paintingDisabled())
1095 return;
1096 QPainter* p = m_data->p();
1097 p->setOpacity(opacity);
1098 }
1099
setPlatformCompositeOperation(CompositeOperator op)1100 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
1101 {
1102 if (paintingDisabled())
1103 return;
1104
1105 m_data->p()->setCompositionMode(toQtCompositionMode(op));
1106 }
1107
clip(const Path & path)1108 void GraphicsContext::clip(const Path& path)
1109 {
1110 if (paintingDisabled())
1111 return;
1112
1113 QPainterPath clipPath = path.platformPath();
1114 clipPath.setFillRule(Qt::WindingFill);
1115 m_data->p()->setClipPath(clipPath, Qt::IntersectClip);
1116 }
1117
canvasClip(const Path & path)1118 void GraphicsContext::canvasClip(const Path& path)
1119 {
1120 clip(path);
1121 }
1122
clipOut(const Path & path)1123 void GraphicsContext::clipOut(const Path& path)
1124 {
1125 if (paintingDisabled())
1126 return;
1127
1128 QPainter* p = m_data->p();
1129 QPainterPath clippedOut = path.platformPath();
1130 QPainterPath newClip;
1131 newClip.setFillRule(Qt::OddEvenFill);
1132 if (p->hasClipping()) {
1133 newClip.addRect(m_data->clipBoundingRect());
1134 newClip.addPath(clippedOut);
1135 p->setClipPath(newClip, Qt::IntersectClip);
1136 } else {
1137 QRect windowRect = p->transform().inverted().mapRect(p->window());
1138 newClip.addRect(windowRect);
1139 newClip.addPath(clippedOut.intersected(newClip));
1140 p->setClipPath(newClip);
1141 }
1142 }
1143
translate(float x,float y)1144 void GraphicsContext::translate(float x, float y)
1145 {
1146 if (paintingDisabled())
1147 return;
1148
1149 m_data->p()->translate(x, y);
1150 }
1151
rotate(float radians)1152 void GraphicsContext::rotate(float radians)
1153 {
1154 if (paintingDisabled())
1155 return;
1156
1157 m_data->p()->rotate(rad2deg(qreal(radians)));
1158 }
1159
scale(const FloatSize & s)1160 void GraphicsContext::scale(const FloatSize& s)
1161 {
1162 if (paintingDisabled())
1163 return;
1164
1165 m_data->p()->scale(s.width(), s.height());
1166 }
1167
clipOut(const IntRect & rect)1168 void GraphicsContext::clipOut(const IntRect& rect)
1169 {
1170 if (paintingDisabled())
1171 return;
1172
1173 QPainter* p = m_data->p();
1174 QPainterPath newClip;
1175 newClip.setFillRule(Qt::OddEvenFill);
1176 if (p->hasClipping()) {
1177 newClip.addRect(m_data->clipBoundingRect());
1178 newClip.addRect(QRect(rect));
1179 p->setClipPath(newClip, Qt::IntersectClip);
1180 } else {
1181 QRect clipOutRect(rect);
1182 QRect window = p->transform().inverted().mapRect(p->window());
1183 clipOutRect &= window;
1184 newClip.addRect(window);
1185 newClip.addRect(clipOutRect);
1186 p->setClipPath(newClip);
1187 }
1188 }
1189
addInnerRoundedRectClip(const IntRect & rect,int thickness)1190 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1191 int thickness)
1192 {
1193 if (paintingDisabled())
1194 return;
1195
1196 clip(rect);
1197 QPainterPath path;
1198
1199 // Add outer ellipse
1200 path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1201
1202 // Add inner ellipse.
1203 path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1204 rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1205
1206 path.setFillRule(Qt::OddEvenFill);
1207
1208 QPainter* p = m_data->p();
1209
1210 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
1211 p->setRenderHint(QPainter::Antialiasing, true);
1212 p->setClipPath(path, Qt::IntersectClip);
1213 p->setRenderHint(QPainter::Antialiasing, antiAlias);
1214 }
1215
concatCTM(const AffineTransform & transform)1216 void GraphicsContext::concatCTM(const AffineTransform& transform)
1217 {
1218 if (paintingDisabled())
1219 return;
1220
1221 m_data->p()->setWorldTransform(transform, true);
1222 }
1223
setCTM(const AffineTransform & transform)1224 void GraphicsContext::setCTM(const AffineTransform& transform)
1225 {
1226 if (paintingDisabled())
1227 return;
1228
1229 m_data->p()->setWorldTransform(transform);
1230 }
1231
setURLForRect(const KURL &,const IntRect &)1232 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
1233 {
1234 notImplemented();
1235 }
1236
setPlatformStrokeColor(const Color & color,ColorSpace colorSpace)1237 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1238 {
1239 if (paintingDisabled() || !color.isValid())
1240 return;
1241
1242 QPainter* p = m_data->p();
1243 QPen newPen(p->pen());
1244 m_data->solidColor.setColor(color);
1245 newPen.setBrush(m_data->solidColor);
1246 p->setPen(newPen);
1247 }
1248
setPlatformStrokeStyle(StrokeStyle strokeStyle)1249 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
1250 {
1251 if (paintingDisabled())
1252 return;
1253 QPainter* p = m_data->p();
1254 QPen newPen(p->pen());
1255 newPen.setStyle(toQPenStyle(strokeStyle));
1256 p->setPen(newPen);
1257 }
1258
setPlatformStrokeThickness(float thickness)1259 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1260 {
1261 if (paintingDisabled())
1262 return;
1263 QPainter* p = m_data->p();
1264 QPen newPen(p->pen());
1265 newPen.setWidthF(thickness);
1266 p->setPen(newPen);
1267 }
1268
setPlatformFillColor(const Color & color,ColorSpace colorSpace)1269 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1270 {
1271 if (paintingDisabled() || !color.isValid())
1272 return;
1273
1274 m_data->solidColor.setColor(color);
1275 m_data->p()->setBrush(m_data->solidColor);
1276 }
1277
setPlatformShouldAntialias(bool enable)1278 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1279 {
1280 if (paintingDisabled())
1281 return;
1282 m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1283 }
1284
1285 #ifdef Q_WS_WIN
1286
getWindowsContext(const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1287 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1288 {
1289 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1290 Q_ASSERT(mayCreateBitmap);
1291
1292 if (dstRect.isEmpty())
1293 return 0;
1294
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;
1308
1309 void* pixels = 0;
1310 HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1311 if (!bitmap)
1312 return 0;
1313
1314 HDC displayDC = ::GetDC(0);
1315 HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1316 ::ReleaseDC(0, displayDC);
1317
1318 ::SelectObject(bitmapDC, bitmap);
1319
1320 // Fill our buffer with clear if we're going to alpha blend.
1321 if (supportAlphaBlend) {
1322 BITMAP bmpInfo;
1323 GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1324 int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1325 memset(bmpInfo.bmBits, 0, bufferSize);
1326 }
1327
1328 #if !OS(WINCE)
1329 // Make sure we can do world transforms.
1330 SetGraphicsMode(bitmapDC, GM_ADVANCED);
1331
1332 // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1333 XFORM xform;
1334 xform.eM11 = 1.0f;
1335 xform.eM12 = 0.0f;
1336 xform.eM21 = 0.0f;
1337 xform.eM22 = 1.0f;
1338 xform.eDx = -dstRect.x();
1339 xform.eDy = -dstRect.y();
1340 ::SetWorldTransform(bitmapDC, &xform);
1341 #endif
1342
1343 return bitmapDC;
1344 }
1345
releaseWindowsContext(HDC hdc,const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1346 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1347 {
1348 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1349 Q_ASSERT(mayCreateBitmap);
1350
1351 if (hdc) {
1352
1353 if (!dstRect.isEmpty()) {
1354
1355 HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1356 BITMAP info;
1357 GetObject(bitmap, sizeof(info), &info);
1358 ASSERT(info.bmBitsPixel == 32);
1359
1360 QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
1361 m_data->p()->drawPixmap(dstRect, pixmap);
1362
1363 ::DeleteObject(bitmap);
1364 }
1365
1366 ::DeleteDC(hdc);
1367 }
1368 }
1369 #endif
1370
setImageInterpolationQuality(InterpolationQuality quality)1371 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1372 {
1373 m_data->imageInterpolationQuality = quality;
1374
1375 switch (quality) {
1376 case InterpolationNone:
1377 case InterpolationLow:
1378 // use nearest-neigbor
1379 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
1380 break;
1381
1382 case InterpolationMedium:
1383 case InterpolationHigh:
1384 // use the filter
1385 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
1386 break;
1387
1388 case InterpolationDefault:
1389 default:
1390 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, m_data->initialSmoothPixmapTransformHint);
1391 break;
1392 };
1393 }
1394
imageInterpolationQuality() const1395 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1396 {
1397 return m_data->imageInterpolationQuality;
1398 }
1399
takeOwnershipOfPlatformContext()1400 void GraphicsContext::takeOwnershipOfPlatformContext()
1401 {
1402 m_data->takeOwnershipOfPlatformContext();
1403 }
1404
1405 }
1406
1407 // vim: ts=4 sw=4 et
1408