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 * Copyright (C) 2013 Digia Plc. and/or its subsidiary(-ies). 14 * 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 26 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 34 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include "config.h" 40#include "GraphicsContext.h" 41 42#if OS(WINDOWS) 43#include <windows.h> 44#endif 45 46#include "AffineTransform.h" 47#include "Color.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 "ShadowBlur.h" 55#include "TransformationMatrix.h" 56#include "TransparencyLayer.h" 57 58#include <QBrush> 59#include <QGradient> 60#include <QPaintDevice> 61#include <QPaintEngine> 62#include <QPainter> 63#include <QPainterPath> 64#include <QPainterPathStroker> 65#include <QPixmap> 66#include <QPolygonF> 67#include <QStack> 68#include <QVector> 69#include <wtf/MathExtras.h> 70 71#if OS(WINDOWS) 72QT_BEGIN_NAMESPACE 73Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP, int hbitmapFormat = 0); 74QT_END_NAMESPACE 75 76enum HBitmapFormat { 77 HBitmapNoAlpha, 78 HBitmapPremultipliedAlpha, 79 HBitmapAlpha 80}; 81#endif 82 83namespace WebCore { 84 85static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op) 86{ 87 switch (op) { 88 case CompositeClear: 89 return QPainter::CompositionMode_Clear; 90 case CompositeCopy: 91 return QPainter::CompositionMode_Source; 92 case CompositeSourceOver: 93 return QPainter::CompositionMode_SourceOver; 94 case CompositeSourceIn: 95 return QPainter::CompositionMode_SourceIn; 96 case CompositeSourceOut: 97 return QPainter::CompositionMode_SourceOut; 98 case CompositeSourceAtop: 99 return QPainter::CompositionMode_SourceAtop; 100 case CompositeDestinationOver: 101 return QPainter::CompositionMode_DestinationOver; 102 case CompositeDestinationIn: 103 return QPainter::CompositionMode_DestinationIn; 104 case CompositeDestinationOut: 105 return QPainter::CompositionMode_DestinationOut; 106 case CompositeDestinationAtop: 107 return QPainter::CompositionMode_DestinationAtop; 108 case CompositeXOR: 109 return QPainter::CompositionMode_Xor; 110 case CompositePlusDarker: 111 // there is no exact match, but this is the closest 112 return QPainter::CompositionMode_Darken; 113 case CompositePlusLighter: 114 return QPainter::CompositionMode_Plus; 115 case CompositeDifference: 116 return QPainter::CompositionMode_Difference; 117 default: 118 ASSERT_NOT_REACHED(); 119 } 120 121 return QPainter::CompositionMode_SourceOver; 122} 123 124static inline Qt::PenCapStyle toQtLineCap(LineCap lc) 125{ 126 switch (lc) { 127 case ButtCap: 128 return Qt::FlatCap; 129 case RoundCap: 130 return Qt::RoundCap; 131 case SquareCap: 132 return Qt::SquareCap; 133 default: 134 ASSERT_NOT_REACHED(); 135 } 136 137 return Qt::FlatCap; 138} 139 140static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj) 141{ 142 switch (lj) { 143 case MiterJoin: 144 return Qt::SvgMiterJoin; 145 case RoundJoin: 146 return Qt::RoundJoin; 147 case BevelJoin: 148 return Qt::BevelJoin; 149 default: 150 ASSERT_NOT_REACHED(); 151 } 152 153 return Qt::SvgMiterJoin; 154} 155 156static Qt::PenStyle toQPenStyle(StrokeStyle style) 157{ 158 switch (style) { 159 case NoStroke: 160 return Qt::NoPen; 161 break; 162 case SolidStroke: 163#if ENABLE(CSS3_TEXT) 164 case DoubleStroke: 165 case WavyStroke: 166#endif 167 return Qt::SolidLine; 168 break; 169 case DottedStroke: 170 return Qt::DotLine; 171 break; 172 case DashedStroke: 173 return Qt::DashLine; 174 break; 175 default: 176 ASSERT_NOT_REACHED(); 177 } 178 return Qt::NoPen; 179} 180 181static inline Qt::FillRule toQtFillRule(WindRule rule) 182{ 183 switch (rule) { 184 case RULE_EVENODD: 185 return Qt::OddEvenFill; 186 case RULE_NONZERO: 187 return Qt::WindingFill; 188 default: 189 ASSERT_NOT_REACHED(); 190 } 191 return Qt::OddEvenFill; 192} 193 194static inline void adjustPointsForDottedLine(FloatPoint& p1, FloatPoint& p2, float width, bool isVerticalLine) 195{ 196 if (isVerticalLine) { 197 p1.setY(p1.y() - width / 2); 198 p2.setY(p2.y() + width / 2); 199 } else { 200 p1.setX(p1.x() - width / 2); 201 p2.setX(p2.x() + width / 2); 202 } 203} 204 205static inline void drawLineEndpointsForStyle(QPainter *painter, const FloatPoint& p1, const FloatPoint& p2, float width, bool isVerticalLine, StrokeStyle style, Color color) 206{ 207 // Do a rect fill of our endpoints. This ensures we always have the 208 // appearance of being a border. 209 if (style == DashedStroke) { 210 if (isVerticalLine) { 211 painter->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color)); 212 painter->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color)); 213 } else { 214 painter->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color)); 215 painter->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color)); 216 } 217 } 218 219 // As per css spec a dotted stroke should be made of circles so we're 220 // drawing circles as endpoints. 221 if (style == DottedStroke) { 222 painter->setPen(Qt::NoPen); 223 painter->setBrush(QColor(color)); 224 painter->drawEllipse(p1.x() - width / 2, p1.y() - width / 2, width, width); 225 painter->drawEllipse(p2.x() - width / 2, p2.y() - width / 2, width, width); 226 } 227} 228 229class GraphicsContextPlatformPrivate { 230 WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); WTF_MAKE_FAST_ALLOCATED; 231public: 232 GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor); 233 ~GraphicsContextPlatformPrivate(); 234 235 inline QPainter* p() const 236 { 237 if (layers.isEmpty()) 238 return painter; 239 return &layers.top()->painter; 240 } 241 242 bool antiAliasingForRectsAndLines; 243 244 QStack<TransparencyLayer*> layers; 245 // Counting real layers. Required by isInTransparencyLayer() calls 246 // For example, layers with valid alphaMask are not real layers 247 int layerCount; 248 249 // reuse this brush for solid color (to prevent expensive QBrush construction) 250 QBrush solidColor; 251 252 InterpolationQuality imageInterpolationQuality; 253 bool initialSmoothPixmapTransformHint; 254 255 QRectF clipBoundingRect() const 256 { 257 return p()->clipBoundingRect(); 258 } 259 260 void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; } 261 262private: 263 QPainter* painter; 264 bool platformContextIsOwned; 265}; 266 267GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, const QColor& initialSolidColor) 268 : antiAliasingForRectsAndLines(false) 269 , layerCount(0) 270 , solidColor(initialSolidColor) 271 , imageInterpolationQuality(InterpolationDefault) 272 , initialSmoothPixmapTransformHint(false) 273 , painter(p) 274 , platformContextIsOwned(false) 275{ 276 if (!painter) 277 return; 278 279 // Use the default the QPainter was constructed with. 280 antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing); 281 282 // Used for default image interpolation quality. 283 initialSmoothPixmapTransformHint = painter->testRenderHint(QPainter::SmoothPixmapTransform); 284 285 painter->setRenderHint(QPainter::Antialiasing, true); 286 287} 288 289GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate() 290{ 291 if (!platformContextIsOwned) 292 return; 293 294 QPaintDevice* device = painter->device(); 295 painter->end(); 296 delete painter; 297 delete device; 298} 299 300void GraphicsContext::platformInit(PlatformGraphicsContext* painter) 301{ 302 m_data = new GraphicsContextPlatformPrivate(painter, fillColor()); 303 304 setPaintingDisabled(!painter); 305 306 if (!painter) 307 return; 308 309 // solidColor is initialized with the fillColor(). 310 painter->setBrush(m_data->solidColor); 311 312 QPen pen(painter->pen()); 313 pen.setColor(strokeColor()); 314 pen.setJoinStyle(toQtLineJoin(MiterJoin)); 315 painter->setPen(pen); 316} 317 318void GraphicsContext::platformDestroy() 319{ 320 while (!m_data->layers.isEmpty()) 321 endTransparencyLayer(); 322 323 delete m_data; 324} 325 326PlatformGraphicsContext* GraphicsContext::platformContext() const 327{ 328 return m_data->p(); 329} 330 331AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const 332{ 333 if (paintingDisabled()) 334 return AffineTransform(); 335 336 const QTransform& matrix = platformContext()->combinedTransform(); 337 return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(), 338 matrix.m22(), matrix.dx(), matrix.dy()); 339} 340 341void GraphicsContext::savePlatformState() 342{ 343 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull()) 344 ++m_data->layers.top()->saveCounter; 345 m_data->p()->save(); 346} 347 348void GraphicsContext::restorePlatformState() 349{ 350 if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull()) 351 if (!--m_data->layers.top()->saveCounter) 352 endPlatformTransparencyLayer(); 353 354 m_data->p()->restore(); 355} 356 357// Draws a filled rectangle with a stroked border. 358// This is only used to draw borders (real fill is done via fillRect), and 359// thus it must not cast any shadow. 360void GraphicsContext::drawRect(const IntRect& rect) 361{ 362 if (paintingDisabled()) 363 return; 364 365 ASSERT(!rect.isEmpty()); 366 367 QPainter* p = m_data->p(); 368 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); 369 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); 370 371 p->drawRect(rect); 372 373 p->setRenderHint(QPainter::Antialiasing, antiAlias); 374} 375 376// This is only used to draw borders. 377// Must not cast any shadow. 378void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) 379{ 380 if (paintingDisabled()) 381 return; 382 383 StrokeStyle style = strokeStyle(); 384 Color color = strokeColor(); 385 if (style == NoStroke) 386 return; 387 388 float width = strokeThickness(); 389 390 FloatPoint p1 = point1; 391 FloatPoint p2 = point2; 392 bool isVerticalLine = (p1.x() == p2.x()); 393 394 QPainter* p = m_data->p(); 395 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); 396 p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); 397 adjustLineToPixelBoundaries(p1, p2, width, style); 398 399 Qt::PenCapStyle capStyle = Qt::FlatCap; 400 QVector<qreal> dashes; 401 int patWidth = 0; 402 403 switch (style) { 404 case NoStroke: 405 case SolidStroke: 406#if ENABLE(CSS3_TEXT) 407 case DoubleStroke: 408 case WavyStroke: 409#endif 410 break; 411 case DottedStroke: { 412 capStyle = Qt::RoundCap; 413 patWidth = static_cast<int>(width); 414 // The actual length of one line element can not be set to zero and at 0.1 the dots 415 // are still slightly elongated. Setting it to 0.01 will make it look like the 416 // line endings are being stuck together, close enough to look like a circle. 417 // For the distance of the line elements we subtract the small amount again. 418 const qreal lineElementLength = 0.01; 419 dashes << lineElementLength << qreal(2 * patWidth) / width - lineElementLength; 420 adjustPointsForDottedLine(p1, p2, width, isVerticalLine); 421 break; 422 } 423 case DashedStroke: 424 capStyle = Qt::FlatCap; 425 patWidth = 3 * static_cast<int>(width); 426 dashes << qreal(patWidth) / width << qreal(patWidth) / width; 427 break; 428 } 429 430 if (patWidth) { 431 p->save(); 432 433 QPen pen = p->pen(); 434 435 drawLineEndpointsForStyle(p, p1, p2, width, isVerticalLine, style, color); 436 437 // Example: 80 pixels with a width of 30 pixels. 438 // Remainder is 20. The maximum pixels of line we could paint 439 // will be 50 pixels. 440 int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width; 441 int remainder = distance % patWidth; 442 int coverage = distance - remainder; 443 int numSegments = coverage / patWidth; 444 445 float patternOffset = 0.0f; 446 // Special case 1px dotted borders for speed. 447 if (patWidth == 1) 448 patternOffset = 1.0f; 449 else { 450 bool evenNumberOfSegments = !(numSegments % 2); 451 if (remainder) 452 evenNumberOfSegments = !evenNumberOfSegments; 453 if (evenNumberOfSegments) { 454 if (remainder) { 455 patternOffset += patWidth - remainder; 456 patternOffset += remainder / 2; 457 } else 458 patternOffset = patWidth / 2; 459 } else { 460 if (remainder) 461 patternOffset = (patWidth - remainder) / 2; 462 } 463 } 464 465 pen.setWidthF(width); 466 pen.setCapStyle(capStyle); 467 pen.setDashPattern(dashes); 468 pen.setDashOffset(patternOffset / width); 469 p->setPen(pen); 470 } 471 472#if ENABLE(CSS3_TEXT) 473 if (style == WavyStroke) { 474 const float step = 2 * width; // Make wave height equal to two times strokeThickness(). 475 const float flat = width; // Set size of flat lines between diagonal lines. 476 short signal = -1; 477 QPainterPath path; 478 float x1, y1, x2, y2; 479 480 if (isVerticalLine) { 481 x1 = x2 = p1.x(); 482 483 // Make sure (x1, y1) < (x2, y2) 484 if (p1.y() < p2.y()) { 485 y1 = p1.y(); 486 y2 = p2.y(); 487 } else { 488 y1 = p2.y(); 489 y2 = p1.y(); 490 } 491 492 // Qt interprets geometric units as end-point inclusive, while WebCore interprets geometric units as endpoint exclusive. 493 // This means we need to subtract one from the endpoint, or the line will be painted one pixel too long. 494 y2 -= 1; 495 path.moveTo(x1 + signal * step, y1); 496 float y = y1 + 2 * step; 497 498 while (y <= y2) { 499 signal = -signal; 500 path.lineTo(x1 + signal * step, y); 501 path.lineTo(x1 + signal * step, y + flat); // Draw flat line between diagonal lines. 502 y += 2 * step + flat; 503 } 504 } else { 505 y1 = y2 = p1.y(); 506 507 // Make sure (x1, y1) < (x2, y2) 508 if (p1.x() < p2.x()) { 509 x1 = p1.x(); 510 x2 = p2.x(); 511 } else { 512 x1 = p2.x(); 513 x2 = p1.x(); 514 } 515 516 // Qt interprets geometric units as end-point inclusive, while WebCore interprets geometric units as endpoint exclusive. 517 // This means we need to subtract one from the endpoint, or the line will be painted one pixel too long. 518 x2 -= 1; 519 path.moveTo(x1, y1 + signal * step); 520 float x = x1 + 2 * step; 521 522 while (x <= x2) { 523 signal = -signal; 524 path.lineTo(x, y1 + signal * step); 525 path.lineTo(x + flat, y1 + signal * step); // Draw flat line between diagonal lines. 526 x += 2 * step + flat; 527 } 528 } 529 530 // The last point created by the while loops above may not be the end 531 // point, so complete the wave by connecting the end point. 532 path.lineTo(x2, y2); 533 QPen pen = p->pen(); 534 pen.setJoinStyle(Qt::BevelJoin); // A bevelled line join is more suitable for wavy than miter or round. 535 pen.setWidth(width); 536 const bool oldAntiAliasing = p->testRenderHint(QPainter::Antialiasing); 537 p->setRenderHint(QPainter::Antialiasing, true); // AntiAliasing is needed for diagonal lines of wavy stroke 538 p->strokePath(path, pen); 539 p->setRenderHint(QPainter::Antialiasing, oldAntiAliasing); 540 } else { 541#endif // CSS3_TEXT 542 // Qt interprets geometric units as end-point inclusive, while WebCore interprets geomtric units as endpoint exclusive. 543 // This means we need to subtract one from the endpoint, or the line will be painted one pixel too long. 544 if (p1.x() == p2.x()) 545 p->drawLine(p1, p2 - FloatSize(0, 1)); 546 else 547 p->drawLine(p1, p2 - FloatSize(1, 0)); 548#if ENABLE(CSS3_TEXT) 549 } 550#endif // CSS3_TEXT 551 552 if (patWidth) 553 p->restore(); 554 555 p->setRenderHint(QPainter::Antialiasing, antiAlias); 556} 557 558// This method is only used to draw the little circles used in lists. 559void GraphicsContext::drawEllipse(const IntRect& rect) 560{ 561 if (paintingDisabled()) 562 return; 563 564 m_data->p()->drawEllipse(rect); 565} 566 567void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) 568{ 569 if (paintingDisabled()) 570 return; 571 572 if (npoints <= 1) 573 return; 574 575 QPolygonF polygon(npoints); 576 577 for (size_t i = 0; i < npoints; i++) 578 polygon[i] = points[i]; 579 580 QPainter* p = m_data->p(); 581 582 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); 583 p->setRenderHint(QPainter::Antialiasing, shouldAntialias); 584 585 p->drawConvexPolygon(polygon); 586 587 p->setRenderHint(QPainter::Antialiasing, antiAlias); 588} 589 590void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) 591{ 592 if (paintingDisabled()) 593 return; 594 595 if (numPoints <= 1) 596 return; 597 598 QPainterPath path(points[0]); 599 for (size_t i = 1; i < numPoints; ++i) 600 path.lineTo(points[i]); 601 path.setFillRule(Qt::WindingFill); 602 603 QPainter* p = m_data->p(); 604 605 bool painterWasAntialiased = p->testRenderHint(QPainter::Antialiasing); 606 607 if (painterWasAntialiased != antialiased) 608 p->setRenderHint(QPainter::Antialiasing, antialiased); 609 610 p->setClipPath(path, Qt::IntersectClip); 611 612 if (painterWasAntialiased != antialiased) 613 p->setRenderHint(QPainter::Antialiasing, painterWasAntialiased); 614} 615 616void GraphicsContext::fillPath(const Path& path) 617{ 618 if (paintingDisabled()) 619 return; 620 621 QPainter* p = m_data->p(); 622 QPainterPath platformPath = path.platformPath(); 623 platformPath.setFillRule(toQtFillRule(fillRule())); 624 625 if (hasShadow()) { 626 if (mustUseShadowBlur() || m_state.fillPattern || m_state.fillGradient) 627 { 628 ShadowBlur shadow(m_state); 629 GraphicsContext* shadowContext = shadow.beginShadowLayer(this, platformPath.controlPointRect()); 630 if (shadowContext) { 631 QPainter* shadowPainter = shadowContext->platformContext(); 632 if (m_state.fillPattern) { 633 shadowPainter->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern())); 634 } else if (m_state.fillGradient) { 635 QBrush brush(*m_state.fillGradient->platformGradient()); 636 brush.setTransform(m_state.fillGradient->gradientSpaceTransform()); 637 shadowPainter->fillPath(platformPath, brush); 638 } else { 639 shadowPainter->fillPath(platformPath, p->brush()); 640 } 641 shadow.endShadowLayer(this); 642 } 643 } else { 644 QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height()); 645 p->translate(offset); 646 QColor shadowColor = m_state.shadowColor; 647 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF()); 648 p->fillPath(platformPath, shadowColor); 649 p->translate(-offset); 650 } 651 } 652 if (m_state.fillPattern) { 653 p->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern())); 654 } else if (m_state.fillGradient) { 655 QBrush brush(*m_state.fillGradient->platformGradient()); 656 brush.setTransform(m_state.fillGradient->gradientSpaceTransform()); 657 p->fillPath(platformPath, brush); 658 } else 659 p->fillPath(platformPath, p->brush()); 660} 661 662inline static void fillPathStroke(QPainter* painter, QPainterPathStroker& pathStroker, const QPainterPath& platformPath, const QBrush& brush) 663{ 664 QPainterPath stroke = pathStroker.createStroke(platformPath); 665 painter->fillPath(stroke, brush); 666} 667 668void GraphicsContext::strokePath(const Path& path) 669{ 670 if (paintingDisabled()) 671 return; 672 673 QPainter* p = m_data->p(); 674 QPen pen(p->pen()); 675 QPainterPath platformPath = path.platformPath(); 676 platformPath.setFillRule(toQtFillRule(fillRule())); 677 QPainterPathStroker pathStroker; 678 pathStroker.setJoinStyle(pen.joinStyle()); 679 pathStroker.setDashOffset(pen.dashOffset()); 680 pathStroker.setMiterLimit(pen.miterLimit()); 681 pathStroker.setCapStyle(pen.capStyle()); 682 pathStroker.setWidth(pen.widthF()); 683 684 if (hasShadow()) { 685 if (mustUseShadowBlur() || m_state.strokePattern || m_state.strokeGradient) 686 { 687 ShadowBlur shadow(m_state); 688 FloatRect boundingRect = platformPath.controlPointRect(); 689 boundingRect.inflate(pen.miterLimit() + pen.widthF()); 690 GraphicsContext* shadowContext = shadow.beginShadowLayer(this, boundingRect); 691 if (shadowContext) { 692 QPainter* shadowPainter = shadowContext->platformContext(); 693 if (m_state.strokeGradient) { 694 QBrush brush(*m_state.strokeGradient->platformGradient()); 695 brush.setTransform(m_state.strokeGradient->gradientSpaceTransform()); 696 fillPathStroke(shadowPainter, pathStroker, platformPath, brush); 697 } else 698 fillPathStroke(shadowPainter, pathStroker, platformPath, pen.brush()); 699 shadow.endShadowLayer(this); 700 } 701 } else { 702 QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height()); 703 p->translate(offset); 704 QColor shadowColor = m_state.shadowColor; 705 shadowColor.setAlphaF(shadowColor.alphaF() * pen.color().alphaF()); 706 QPen shadowPen(pen); 707 shadowPen.setColor(shadowColor); 708 fillPathStroke(p, pathStroker, platformPath, shadowPen.brush()); 709 p->translate(-offset); 710 } 711 } 712 713 if (m_state.strokePattern) { 714 QBrush brush = m_state.strokePattern->createPlatformPattern(); 715 fillPathStroke(p, pathStroker, platformPath, brush); 716 } else if (m_state.strokeGradient) { 717 QBrush brush(*m_state.strokeGradient->platformGradient()); 718 brush.setTransform(m_state.strokeGradient->gradientSpaceTransform()); 719 fillPathStroke(p, pathStroker, platformPath, brush); 720 } else 721 fillPathStroke(p, pathStroker, platformPath, pen.brush()); 722} 723 724static inline void drawRepeatPattern(QPainter* p, PassRefPtr<Pattern> pattern, const FloatRect& rect) 725{ 726 ASSERT(pattern); 727 728 const QBrush brush = pattern->createPlatformPattern(); 729 if (brush.style() != Qt::TexturePattern) 730 return; 731 732 const bool repeatX = pattern->repeatX(); 733 const bool repeatY = pattern->repeatY(); 734 // Patterns must be painted so that the top left of the first image is anchored at 735 // the origin of the coordinate space 736 737 QRectF targetRect(rect); 738 const int w = brush.texture().width(); 739 const int h = brush.texture().height(); 740 741 ASSERT(p); 742 QRegion oldClip; 743 if (p->hasClipping()) 744 oldClip = p->clipRegion(); 745 746 // The only type of transforms supported for the brush are translations. 747 ASSERT(!brush.transform().isRotating()); 748 749 QRectF clip = targetRect; 750 QRectF patternRect = brush.transform().mapRect(QRectF(0, 0, w, h)); 751 if (!repeatX) { 752 clip.setLeft(patternRect.left()); 753 clip.setWidth(patternRect.width()); 754 } 755 if (!repeatY) { 756 clip.setTop(patternRect.top()); 757 clip.setHeight(patternRect.height()); 758 } 759 if (!repeatX || !repeatY) 760 p->setClipRect(clip); 761 762 p->fillRect(targetRect, brush); 763 764 if (!oldClip.isEmpty()) 765 p->setClipRegion(oldClip); 766 else if (!repeatX || !repeatY) 767 p->setClipping(false); 768} 769 770void GraphicsContext::fillRect(const FloatRect& rect) 771{ 772 if (paintingDisabled()) 773 return; 774 775 QPainter* p = m_data->p(); 776 QRectF normalizedRect = rect.normalized(); 777 778 if (m_state.fillPattern) { 779 if (hasShadow()) { 780 ShadowBlur shadow(m_state); 781 GraphicsContext* shadowContext = shadow.beginShadowLayer(this, normalizedRect); 782 if (shadowContext) { 783 QPainter* shadowPainter = shadowContext->platformContext(); 784 drawRepeatPattern(shadowPainter, m_state.fillPattern, normalizedRect); 785 shadow.endShadowLayer(this); 786 } 787 } 788 drawRepeatPattern(p, m_state.fillPattern, normalizedRect); 789 } else if (m_state.fillGradient) { 790 QBrush brush(*m_state.fillGradient->platformGradient()); 791 brush.setTransform(m_state.fillGradient->gradientSpaceTransform()); 792 if (hasShadow()) { 793 ShadowBlur shadow(m_state); 794 GraphicsContext* shadowContext = shadow.beginShadowLayer(this, normalizedRect); 795 if (shadowContext) { 796 QPainter* shadowPainter = shadowContext->platformContext(); 797 shadowPainter->fillRect(normalizedRect, brush); 798 shadow.endShadowLayer(this); 799 } 800 } 801 p->fillRect(normalizedRect, brush); 802 } else { 803 if (hasShadow()) { 804 if (mustUseShadowBlur()) { 805 ShadowBlur shadow(m_state); 806 // drawRectShadowWithTiling does not work with rotations, and the fallback of 807 // drawing though clipToImageBuffer() produces scaling artifacts for us. 808 if (!getCTM().preservesAxisAlignment()) { 809 GraphicsContext* shadowContext = shadow.beginShadowLayer(this, normalizedRect); 810 if (shadowContext) { 811 QPainter* shadowPainter = shadowContext->platformContext(); 812 shadowPainter->fillRect(normalizedRect, p->brush()); 813 shadow.endShadowLayer(this); 814 } 815 } else 816 shadow.drawRectShadow(this, rect, RoundedRect::Radii()); 817 } else { 818 // Solid rectangle fill with no blur shadow or transformations applied can be done 819 // faster without using the shadow layer at all. 820 QColor shadowColor = m_state.shadowColor; 821 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF()); 822 p->fillRect(normalizedRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor); 823 } 824 } 825 826 p->fillRect(normalizedRect, p->brush()); 827 } 828} 829 830 831void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) 832{ 833 if (paintingDisabled() || !color.isValid()) 834 return; 835 836 QRectF platformRect(rect); 837 QPainter* p = m_data->p(); 838 if (hasShadow()) { 839 if (mustUseShadowBlur()) { 840 ShadowBlur shadow(m_state); 841 shadow.drawRectShadow(this, platformRect, RoundedRect::Radii()); 842 } else { 843 QColor shadowColor = m_state.shadowColor; 844 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF()); 845 p->fillRect(platformRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor); 846 } 847 } 848 p->fillRect(platformRect, QColor(color)); 849} 850 851void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace) 852{ 853 if (paintingDisabled() || !color.isValid()) 854 return; 855 856 Path path; 857 path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); 858 QPainter* p = m_data->p(); 859 if (hasShadow()) { 860 if (mustUseShadowBlur()) { 861 ShadowBlur shadow(m_state); 862 shadow.drawRectShadow(this, rect, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight)); 863 } else { 864 const QPointF shadowOffset(m_state.shadowOffset.width(), m_state.shadowOffset.height()); 865 p->translate(shadowOffset); 866 p->fillPath(path.platformPath(), QColor(m_state.shadowColor)); 867 p->translate(-shadowOffset); 868 } 869 } 870 p->fillPath(path.platformPath(), QColor(color)); 871} 872 873void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace) 874{ 875 if (paintingDisabled() || !color.isValid()) 876 return; 877 878 Path path; 879 path.addRect(rect); 880 if (!roundedHoleRect.radii().isZero()) 881 path.addRoundedRect(roundedHoleRect); 882 else 883 path.addRect(roundedHoleRect.rect()); 884 885 QPainterPath platformPath = path.platformPath(); 886 platformPath.setFillRule(Qt::OddEvenFill); 887 888 QPainter* p = m_data->p(); 889 if (hasShadow()) { 890 if (mustUseShadowBlur()) { 891 ShadowBlur shadow(m_state); 892 shadow.drawInsetShadow(this, rect, roundedHoleRect.rect(), roundedHoleRect.radii()); 893 } else { 894 const QPointF shadowOffset(m_state.shadowOffset.width(), m_state.shadowOffset.height()); 895 p->translate(shadowOffset); 896 p->fillPath(platformPath, QColor(m_state.shadowColor)); 897 p->translate(-shadowOffset); 898 } 899 } 900 901 p->fillPath(platformPath, QColor(color)); 902} 903 904bool GraphicsContext::isInTransparencyLayer() const 905{ 906 return m_data->layerCount; 907} 908 909void GraphicsContext::clip(const IntRect& rect) 910{ 911 if (paintingDisabled()) 912 return; 913 914 m_data->p()->setClipRect(rect, Qt::IntersectClip); 915} 916 917void GraphicsContext::clip(const FloatRect& rect) 918{ 919 if (paintingDisabled()) 920 return; 921 922 m_data->p()->setClipRect(rect, Qt::IntersectClip); 923} 924IntRect GraphicsContext::clipBounds() const 925{ 926 QPainter* p = m_data->p(); 927 QRectF clipRect; 928 929 clipRect = p->transform().inverted().mapRect(p->window()); 930 931 if (p->hasClipping()) 932 clipRect = clipRect.intersected(m_data->clipBoundingRect()); 933 934 return enclosingIntRect(clipRect); 935} 936 937void GraphicsContext::clipPath(const Path& path, WindRule clipRule) 938{ 939 if (paintingDisabled()) 940 return; 941 942 QPainter* p = m_data->p(); 943 QPainterPath platformPath = path.platformPath(); 944 platformPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); 945 p->setClipPath(platformPath, Qt::IntersectClip); 946} 947 948void drawFocusRingForPath(QPainter* p, const QPainterPath& path, const Color& color, bool antiAliasing) 949{ 950 const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); 951 p->setRenderHint(QPainter::Antialiasing, antiAliasing); 952 953 const QPen oldPen = p->pen(); 954 const QBrush oldBrush = p->brush(); 955 956 QPen nPen = p->pen(); 957 nPen.setColor(color); 958 p->setBrush(Qt::NoBrush); 959 nPen.setStyle(Qt::DotLine); 960 961 p->strokePath(path, nPen); 962 p->setBrush(oldBrush); 963 p->setPen(oldPen); 964 965 p->setRenderHint(QPainter::Antialiasing, antiAlias); 966} 967 968void GraphicsContext::drawFocusRing(const Path& path, int /* width */, int offset, const Color& color) 969{ 970 // FIXME: Use 'offset' for something? http://webkit.org/b/49909 971 972 if (paintingDisabled() || !color.isValid()) 973 return; 974 975 drawFocusRingForPath(m_data->p(), path.platformPath(), color, m_data->antiAliasingForRectsAndLines); 976} 977 978/** 979 * Focus ring handling for form controls is not handled here. Qt style in 980 * RenderTheme handles drawing focus on widgets which 981 * need it. It is still handled here for links. 982 */ 983void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) 984{ 985 if (paintingDisabled() || !color.isValid()) 986 return; 987 988 unsigned rectCount = rects.size(); 989 990 if (!rects.size()) 991 return; 992 993 int radius = (width - 1) / 2; 994 QPainterPath path; 995 for (unsigned i = 0; i < rectCount; ++i) { 996 QRect rect = QRect((rects[i])).adjusted(-offset - radius, -offset - radius, offset + radius, offset + radius); 997 // This is not the most efficient way to add a rect to a path, but if we don't create the tmpPath, 998 // we will end up with ugly lines in between rows of text on anchors with multiple lines. 999 QPainterPath tmpPath; 1000 tmpPath.addRoundedRect(rect, radius, radius); 1001 path = path.united(tmpPath); 1002 } 1003 drawFocusRingForPath(m_data->p(), path, color, m_data->antiAliasingForRectsAndLines); 1004} 1005 1006void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool) 1007{ 1008 if (paintingDisabled()) 1009 return; 1010 1011 FloatPoint startPoint = origin; 1012 FloatPoint endPoint = origin + FloatSize(width, 0); 1013 1014 // If paintengine type is X11 to avoid artifacts 1015 // like bug https://bugs.webkit.org/show_bug.cgi?id=42248 1016#if defined(Q_WS_X11) 1017 QPainter* p = m_data->p(); 1018 if (p->paintEngine()->type() == QPaintEngine::X11) { 1019 // If stroke thickness is odd we need decrease Y coordinate by 1 pixel, 1020 // because inside method adjustLineToPixelBoundaries(...), which 1021 // called from drawLine(...), Y coordinate will be increased by 0.5f 1022 // and then inside Qt painting engine will be rounded to next greater 1023 // integer value. 1024 float strokeWidth = strokeThickness(); 1025 if (static_cast<int>(strokeWidth) % 2) { 1026 startPoint.setY(startPoint.y() - 1); 1027 endPoint.setY(endPoint.y() - 1); 1028 } 1029 } 1030#endif // defined(Q_WS_X11) 1031 1032 drawLine(roundedIntPoint(startPoint), roundedIntPoint(endPoint)); 1033} 1034 1035 1036/* 1037 * NOTE: This code is completely based upon the one from 1038 * Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.{h|cpp} 1039 * 1040 * Draws an error underline that looks like one of: 1041 * 1042 * H E H 1043 * /\ /\ /\ /\ /\ - 1044 * A/ \ / \ / \ A/ \ / \ | 1045 * \ \ / \ / /D \ \ / \ | 1046 * \ \/ C \/ / \ \/ C \ | height = heightSquares * square 1047 * \ /\ F / \ F /\ \ | 1048 * \ / \ / \ / \ \G | 1049 * \ / \ / \ / \ / | 1050 * \/ \/ \/ \/ - 1051 * B B 1052 * |---| 1053 * unitWidth = (heightSquares - 1) * square 1054 * 1055 * The x, y, width, height passed in give the desired bounding box; 1056 * x/width are adjusted to make the underline a integer number of units wide. 1057*/ 1058static void drawErrorUnderline(QPainter *painter, qreal x, qreal y, qreal width, qreal height) 1059{ 1060 const qreal heightSquares = 2.5; 1061 1062 qreal square = height / heightSquares; 1063 qreal halfSquare = 0.5 * square; 1064 1065 qreal unitWidth = (heightSquares - 1.0) * square; 1066 int widthUnits = static_cast<int>((width + 0.5 * unitWidth) / unitWidth); 1067 1068 x += 0.5 * (width - widthUnits * unitWidth); 1069 width = widthUnits * unitWidth; 1070 1071 qreal bottom = y + height; 1072 qreal top = y; 1073 1074 QPainterPath path; 1075 1076 // Bottom of squiggle. 1077 path.moveTo(x - halfSquare, top + halfSquare); // A 1078 1079 int i = 0; 1080 for (i = 0; i < widthUnits; i += 2) { 1081 qreal middle = x + (i + 1) * unitWidth; 1082 qreal right = x + (i + 2) * unitWidth; 1083 1084 path.lineTo(middle, bottom); // B 1085 1086 if (i + 2 == widthUnits) 1087 path.lineTo(right + halfSquare, top + halfSquare); // D 1088 else if (i + 1 != widthUnits) 1089 path.lineTo(right, top + square); // C 1090 } 1091 1092 // Top of squiggle. 1093 for (i -= 2; i >= 0; i -= 2) { 1094 qreal left = x + i * unitWidth; 1095 qreal middle = x + (i + 1) * unitWidth; 1096 qreal right = x + (i + 2) * unitWidth; 1097 1098 if (i + 1 == widthUnits) 1099 path.lineTo(middle + halfSquare, bottom - halfSquare); // G 1100 else { 1101 if (i + 2 == widthUnits) 1102 path.lineTo(right, top); // E 1103 1104 path.lineTo(middle, bottom - halfSquare); // F 1105 } 1106 1107 path.lineTo(left, top); // H 1108 } 1109 1110 painter->drawPath(path); 1111} 1112 1113 1114void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style) 1115{ 1116 if (paintingDisabled()) 1117 return; 1118 1119 QPainter* painter = platformContext(); 1120 const QPen originalPen = painter->pen(); 1121 1122 switch (style) { 1123 case DocumentMarkerSpellingLineStyle: 1124 painter->setPen(Qt::red); 1125 break; 1126 case DocumentMarkerGrammarLineStyle: 1127 painter->setPen(Qt::green); 1128 break; 1129 default: 1130 return; 1131 } 1132 1133 drawErrorUnderline(painter, origin.x(), origin.y(), width, cMisspellingLineThickness); 1134 painter->setPen(originalPen); 1135} 1136 1137FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode) 1138{ 1139 // It is not enough just to round to pixels in device space. The rotation part of the 1140 // affine transform matrix to device space can mess with this conversion if we have a 1141 // rotating image like the hands of the world clock widget. We just need the scale, so 1142 // we get the affine transform matrix and extract the scale. 1143 QPainter* painter = platformContext(); 1144 QTransform deviceTransform = painter->deviceTransform(); 1145 if (deviceTransform.isIdentity()) 1146 return frect; 1147 1148 qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12()); 1149 qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22()); 1150 1151 QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY); 1152 QPoint deviceLowerRight(frect.maxX() * deviceScaleX, frect.maxY() * deviceScaleY); 1153 1154 // Don't let the height or width round to 0 unless either was originally 0 1155 if (deviceOrigin.y() == deviceLowerRight.y() && frect.height()) 1156 deviceLowerRight.setY(deviceLowerRight.y() + 1); 1157 if (deviceOrigin.x() == deviceLowerRight.x() && frect.width()) 1158 deviceLowerRight.setX(deviceLowerRight.x() + 1); 1159 1160 FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY); 1161 FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY); 1162 return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin); 1163} 1164 1165void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace colorSpace) 1166{ 1167 // Qt doesn't support shadows natively, they are drawn manually in the draw* 1168 // functions 1169 if (m_state.shadowsIgnoreTransforms) { 1170 // Meaning that this graphics context is associated with a CanvasRenderingContext 1171 // We flip the height since CG and HTML5 Canvas have opposite Y axis 1172 m_state.shadowOffset = FloatSize(size.width(), -size.height()); 1173 } 1174} 1175 1176void GraphicsContext::clearPlatformShadow() 1177{ 1178} 1179 1180void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask) 1181{ 1182 QPainter* p = m_data->p(); 1183 1184 QTransform deviceTransform = p->transform(); 1185 QRect deviceClip = deviceTransform.mapRect(rect); 1186 1187 alphaMask = alphaMask.transformed(deviceTransform); 1188 if (alphaMask.width() != deviceClip.width() || alphaMask.height() != deviceClip.height()) 1189 alphaMask = alphaMask.scaled(deviceClip.width(), deviceClip.height()); 1190 1191 m_data->layers.push(new TransparencyLayer(p, deviceClip, 1.0, alphaMask)); 1192} 1193 1194void GraphicsContext::beginPlatformTransparencyLayer(float opacity) 1195{ 1196 if (paintingDisabled()) 1197 return; 1198 1199 int x, y, w, h; 1200 x = y = 0; 1201 QPainter* p = m_data->p(); 1202 const QPaintDevice* device = p->device(); 1203 w = device->width(); 1204 h = device->height(); 1205 1206 if (p->hasClipping()) { 1207 QRectF clip = m_data->clipBoundingRect(); 1208 QRectF deviceClip = p->transform().mapRect(clip); 1209 x = int(qBound(qreal(0), deviceClip.x(), (qreal)w)); 1210 y = int(qBound(qreal(0), deviceClip.y(), (qreal)h)); 1211 w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2); 1212 h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2); 1213 } 1214 1215 QPixmap emptyAlphaMask; 1216 m_data->layers.push(new TransparencyLayer(p, QRect(x, y, w, h), opacity, emptyAlphaMask)); 1217 ++m_data->layerCount; 1218} 1219 1220void GraphicsContext::endPlatformTransparencyLayer() 1221{ 1222 if (paintingDisabled()) 1223 return; 1224 1225 TransparencyLayer* layer = m_data->layers.pop(); 1226 if (!layer->alphaMask.isNull()) { 1227 layer->painter.resetTransform(); 1228 layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); 1229 layer->painter.drawPixmap(QPoint(), layer->alphaMask); 1230 } else 1231 --m_data->layerCount; // see the comment for layerCount 1232 layer->painter.end(); 1233 1234 QPainter* p = m_data->p(); 1235 p->save(); 1236 p->resetTransform(); 1237 p->setOpacity(layer->opacity); 1238 p->drawPixmap(layer->offset, layer->pixmap); 1239 p->restore(); 1240 1241 delete layer; 1242} 1243 1244bool GraphicsContext::supportsTransparencyLayers() 1245{ 1246 return true; 1247} 1248 1249void GraphicsContext::clearRect(const FloatRect& rect) 1250{ 1251 if (paintingDisabled()) 1252 return; 1253 1254 QPainter* p = m_data->p(); 1255 QPainter::CompositionMode currentCompositionMode = p->compositionMode(); 1256 p->setCompositionMode(QPainter::CompositionMode_Source); 1257 p->fillRect(rect, Qt::transparent); 1258 p->setCompositionMode(currentCompositionMode); 1259} 1260 1261void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) 1262{ 1263 if (paintingDisabled()) 1264 return; 1265 1266 Path path; 1267 path.addRect(rect); 1268 1269 float previousStrokeThickness = strokeThickness(); 1270 1271 if (lineWidth != previousStrokeThickness) 1272 setStrokeThickness(lineWidth); 1273 1274 strokePath(path); 1275 1276 if (lineWidth != previousStrokeThickness) 1277 setStrokeThickness(previousStrokeThickness); 1278} 1279 1280void GraphicsContext::setLineCap(LineCap lc) 1281{ 1282 if (paintingDisabled()) 1283 return; 1284 1285 QPainter* p = m_data->p(); 1286 QPen nPen = p->pen(); 1287 nPen.setCapStyle(toQtLineCap(lc)); 1288 p->setPen(nPen); 1289} 1290 1291void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) 1292{ 1293 QPainter* p = m_data->p(); 1294 QPen pen = p->pen(); 1295 unsigned dashLength = dashes.size(); 1296 if (dashLength) { 1297 QVector<qreal> pattern; 1298 unsigned count = dashLength; 1299 if (dashLength % 2) 1300 count *= 2; 1301 1302 float penWidth = narrowPrecisionToFloat(double(pen.widthF())); 1303 if (penWidth <= 0.f) 1304 penWidth = 1.f; 1305 1306 for (unsigned i = 0; i < count; i++) 1307 pattern.append(dashes[i % dashLength] / penWidth); 1308 1309 pen.setDashPattern(pattern); 1310 pen.setDashOffset(dashOffset / penWidth); 1311 } else 1312 pen.setStyle(Qt::SolidLine); 1313 p->setPen(pen); 1314} 1315 1316void GraphicsContext::setLineJoin(LineJoin lj) 1317{ 1318 if (paintingDisabled()) 1319 return; 1320 1321 QPainter* p = m_data->p(); 1322 QPen nPen = p->pen(); 1323 nPen.setJoinStyle(toQtLineJoin(lj)); 1324 p->setPen(nPen); 1325} 1326 1327void GraphicsContext::setMiterLimit(float limit) 1328{ 1329 if (paintingDisabled()) 1330 return; 1331 1332 QPainter* p = m_data->p(); 1333 QPen nPen = p->pen(); 1334 nPen.setMiterLimit(limit); 1335 p->setPen(nPen); 1336} 1337 1338void GraphicsContext::setAlpha(float opacity) 1339{ 1340 if (paintingDisabled()) 1341 return; 1342 QPainter* p = m_data->p(); 1343 p->setOpacity(opacity); 1344} 1345 1346void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendMode) 1347{ 1348 if (paintingDisabled()) 1349 return; 1350 1351 m_data->p()->setCompositionMode(toQtCompositionMode(op)); 1352} 1353 1354void GraphicsContext::clip(const Path& path, WindRule windRule) 1355{ 1356 if (paintingDisabled()) 1357 return; 1358 1359 QPainterPath clipPath = path.platformPath(); 1360 clipPath.setFillRule(toQtFillRule(windRule)); 1361 m_data->p()->setClipPath(clipPath, Qt::IntersectClip); 1362} 1363 1364void GraphicsContext::canvasClip(const Path& path, WindRule windRule) 1365{ 1366 clip(path, windRule); 1367} 1368 1369void GraphicsContext::clipOut(const Path& path) 1370{ 1371 if (paintingDisabled()) 1372 return; 1373 1374 QPainter* p = m_data->p(); 1375 QPainterPath clippedOut = path.platformPath(); 1376 QPainterPath newClip; 1377 newClip.setFillRule(Qt::OddEvenFill); 1378 if (p->hasClipping()) { 1379 newClip.addRect(m_data->clipBoundingRect()); 1380 newClip.addPath(clippedOut); 1381 p->setClipPath(newClip, Qt::IntersectClip); 1382 } else { 1383 QRect windowRect = p->transform().inverted().mapRect(p->window()); 1384 newClip.addRect(windowRect); 1385 newClip.addPath(clippedOut.intersected(newClip)); 1386 p->setClipPath(newClip); 1387 } 1388} 1389 1390void GraphicsContext::translate(float x, float y) 1391{ 1392 if (paintingDisabled()) 1393 return; 1394 1395 m_data->p()->translate(x, y); 1396} 1397 1398void GraphicsContext::rotate(float radians) 1399{ 1400 if (paintingDisabled()) 1401 return; 1402 1403 QTransform rotation = QTransform().rotateRadians(radians); 1404 m_data->p()->setTransform(rotation, true); 1405} 1406 1407void GraphicsContext::scale(const FloatSize& s) 1408{ 1409 if (paintingDisabled()) 1410 return; 1411 1412 m_data->p()->scale(s.width(), s.height()); 1413} 1414 1415void GraphicsContext::clipOut(const IntRect& rect) 1416{ 1417 if (paintingDisabled()) 1418 return; 1419 1420 QPainter* p = m_data->p(); 1421 QPainterPath newClip; 1422 newClip.setFillRule(Qt::OddEvenFill); 1423 if (p->hasClipping()) { 1424 newClip.addRect(m_data->clipBoundingRect()); 1425 newClip.addRect(QRect(rect)); 1426 p->setClipPath(newClip, Qt::IntersectClip); 1427 } else { 1428 QRect clipOutRect(rect); 1429 QRect window = p->transform().inverted().mapRect(p->window()); 1430 clipOutRect &= window; 1431 newClip.addRect(window); 1432 newClip.addRect(clipOutRect); 1433 p->setClipPath(newClip); 1434 } 1435} 1436 1437void GraphicsContext::concatCTM(const AffineTransform& transform) 1438{ 1439 if (paintingDisabled()) 1440 return; 1441 1442 m_data->p()->setWorldTransform(transform, true); 1443} 1444 1445void GraphicsContext::setCTM(const AffineTransform& transform) 1446{ 1447 if (paintingDisabled()) 1448 return; 1449 1450 m_data->p()->setWorldTransform(transform); 1451} 1452 1453#if ENABLE(3D_RENDERING) 1454TransformationMatrix GraphicsContext::get3DTransform() const 1455{ 1456 if (paintingDisabled()) 1457 return TransformationMatrix(); 1458 1459 return platformContext()->combinedTransform(); 1460} 1461 1462void GraphicsContext::concat3DTransform(const TransformationMatrix& transform) 1463{ 1464 if (paintingDisabled()) 1465 return; 1466 1467 m_data->p()->setWorldTransform(transform, true); 1468} 1469 1470void GraphicsContext::set3DTransform(const TransformationMatrix& transform) 1471{ 1472 if (paintingDisabled()) 1473 return; 1474 1475 m_data->p()->setWorldTransform(transform, false); 1476} 1477#endif 1478 1479void GraphicsContext::setURLForRect(const KURL&, const IntRect&) 1480{ 1481 notImplemented(); 1482} 1483 1484void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace) 1485{ 1486 if (paintingDisabled() || !color.isValid()) 1487 return; 1488 1489 QPainter* p = m_data->p(); 1490 QPen newPen(p->pen()); 1491 m_data->solidColor.setColor(color); 1492 newPen.setBrush(m_data->solidColor); 1493 p->setPen(newPen); 1494} 1495 1496void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle) 1497{ 1498 if (paintingDisabled()) 1499 return; 1500 QPainter* p = m_data->p(); 1501 QPen newPen(p->pen()); 1502 newPen.setStyle(toQPenStyle(strokeStyle)); 1503 p->setPen(newPen); 1504} 1505 1506void GraphicsContext::setPlatformStrokeThickness(float thickness) 1507{ 1508 if (paintingDisabled()) 1509 return; 1510 QPainter* p = m_data->p(); 1511 QPen newPen(p->pen()); 1512 newPen.setWidthF(thickness); 1513 p->setPen(newPen); 1514} 1515 1516void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) 1517{ 1518 if (paintingDisabled() || !color.isValid()) 1519 return; 1520 1521 m_data->solidColor.setColor(color); 1522 m_data->p()->setBrush(m_data->solidColor); 1523} 1524 1525void GraphicsContext::setPlatformShouldAntialias(bool enable) 1526{ 1527 if (paintingDisabled()) 1528 return; 1529 m_data->p()->setRenderHint(QPainter::Antialiasing, enable); 1530} 1531 1532#if OS(WINDOWS) 1533 1534HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) 1535{ 1536 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true 1537 Q_ASSERT(mayCreateBitmap); 1538 1539 if (dstRect.isEmpty()) 1540 return 0; 1541 1542 // Create a bitmap DC in which to draw. 1543 BITMAPINFO bitmapInfo; 1544 bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 1545 bitmapInfo.bmiHeader.biWidth = dstRect.width(); 1546 bitmapInfo.bmiHeader.biHeight = dstRect.height(); 1547 bitmapInfo.bmiHeader.biPlanes = 1; 1548 bitmapInfo.bmiHeader.biBitCount = 32; 1549 bitmapInfo.bmiHeader.biCompression = BI_RGB; 1550 bitmapInfo.bmiHeader.biSizeImage = 0; 1551 bitmapInfo.bmiHeader.biXPelsPerMeter = 0; 1552 bitmapInfo.bmiHeader.biYPelsPerMeter = 0; 1553 bitmapInfo.bmiHeader.biClrUsed = 0; 1554 bitmapInfo.bmiHeader.biClrImportant = 0; 1555 1556 void* pixels = 0; 1557 HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); 1558 if (!bitmap) 1559 return 0; 1560 1561 HDC displayDC = ::GetDC(0); 1562 HDC bitmapDC = ::CreateCompatibleDC(displayDC); 1563 ::ReleaseDC(0, displayDC); 1564 1565 ::SelectObject(bitmapDC, bitmap); 1566 1567 // Fill our buffer with clear if we're going to alpha blend. 1568 if (supportAlphaBlend) { 1569 BITMAP bmpInfo; 1570 GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); 1571 int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; 1572 memset(bmpInfo.bmBits, 0, bufferSize); 1573 } 1574 1575#if !OS(WINCE) 1576 // Make sure we can do world transforms. 1577 SetGraphicsMode(bitmapDC, GM_ADVANCED); 1578 1579 // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap. 1580 XFORM xform; 1581 xform.eM11 = 1.0f; 1582 xform.eM12 = 0.0f; 1583 xform.eM21 = 0.0f; 1584 xform.eM22 = 1.0f; 1585 xform.eDx = -dstRect.x(); 1586 xform.eDy = -dstRect.y(); 1587 ::SetWorldTransform(bitmapDC, &xform); 1588#endif 1589 1590 return bitmapDC; 1591} 1592 1593void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) 1594{ 1595 // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true 1596 Q_ASSERT(mayCreateBitmap); 1597 1598 if (hdc) { 1599 1600 if (!dstRect.isEmpty()) { 1601 1602 HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); 1603 BITMAP info; 1604 GetObject(bitmap, sizeof(info), &info); 1605 ASSERT(info.bmBitsPixel == 32); 1606 1607 QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap, supportAlphaBlend ? HBitmapPremultipliedAlpha : HBitmapNoAlpha); 1608 m_data->p()->drawPixmap(dstRect, pixmap); 1609 1610 ::DeleteObject(bitmap); 1611 } 1612 1613 ::DeleteDC(hdc); 1614 } 1615} 1616#endif 1617 1618void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality) 1619{ 1620 m_data->imageInterpolationQuality = quality; 1621 1622 switch (quality) { 1623 case InterpolationNone: 1624 case InterpolationLow: 1625 // use nearest-neigbor 1626 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false); 1627 break; 1628 1629 case InterpolationMedium: 1630 case InterpolationHigh: 1631 // use the filter 1632 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true); 1633 break; 1634 1635 case InterpolationDefault: 1636 default: 1637 m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, m_data->initialSmoothPixmapTransformHint); 1638 break; 1639 }; 1640} 1641 1642InterpolationQuality GraphicsContext::imageInterpolationQuality() const 1643{ 1644 return m_data->imageInterpolationQuality; 1645} 1646 1647void GraphicsContext::takeOwnershipOfPlatformContext() 1648{ 1649 m_data->takeOwnershipOfPlatformContext(); 1650} 1651 1652} 1653 1654// vim: ts=4 sw=4 et 1655