1 /* 2 * Copyright (C) 2006 Dirk Mueller <mueller (at) kde.org> 3 * Copyright (C) 2006 Zack Rusin <zack (at) kde.org> 4 * Copyright (C) 2006 George Staikos <staikos (at) kde.org> 5 * Copyright (C) 2006 Simon Hausmann <hausmann (at) kde.org> 6 * Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld (at) kde.org> 7 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) 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 (at) gmx.de> 11 * Copyright (C) 2010, 2011 Sencha, Inc. 12 * Copyright (C) 2011 Andreas Kling <kling (at) 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 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 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 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 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 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 181 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 204 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 213 void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; } 214 215 private: 216 QPainter* painter; 217 bool platformContextIsOwned; 218 }; 219 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 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 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 277 void GraphicsContext::platformDestroy() 278 { 279 while (!m_data->layers.isEmpty()) 280 endTransparencyLayer(); 281 282 delete m_data; 283 } 284 285 PlatformGraphicsContext* GraphicsContext::platformContext() const 286 { 287 return m_data->p(); 288 } 289 290 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 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 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. 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. 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. 433 void GraphicsContext::drawEllipse(const IntRect& rect) 434 { 435 if (paintingDisabled()) 436 return; 437 438 m_data->p()->drawEllipse(rect); 439 } 440 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 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 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 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 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 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 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 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 770 bool GraphicsContext::inTransparencyLayer() const 771 { 772 return m_data->layerCount; 773 } 774 775 ContextShadow* GraphicsContext::contextShadow() 776 { 777 return &m_data->shadow; 778 } 779 780 void GraphicsContext::clip(const IntRect& rect) 781 { 782 if (paintingDisabled()) 783 return; 784 785 m_data->p()->setClipRect(rect, Qt::IntersectClip); 786 } 787 788 void GraphicsContext::clip(const FloatRect& rect) 789 { 790 if (paintingDisabled()) 791 return; 792 793 m_data->p()->setClipRect(rect, Qt::IntersectClip); 794 } 795 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 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 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 */ 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 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 895 void GraphicsContext::drawLineForTextChecking(const FloatPoint&, float, TextCheckingLineStyle) 896 { 897 if (paintingDisabled()) 898 return; 899 900 notImplemented(); 901 } 902 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 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 947 void GraphicsContext::clearPlatformShadow() 948 { 949 m_data->shadow.clear(); 950 } 951 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 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 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 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 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 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 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 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 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 1092 void GraphicsContext::setAlpha(float opacity) 1093 { 1094 if (paintingDisabled()) 1095 return; 1096 QPainter* p = m_data->p(); 1097 p->setOpacity(opacity); 1098 } 1099 1100 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) 1101 { 1102 if (paintingDisabled()) 1103 return; 1104 1105 m_data->p()->setCompositionMode(toQtCompositionMode(op)); 1106 } 1107 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 1118 void GraphicsContext::canvasClip(const Path& path) 1119 { 1120 clip(path); 1121 } 1122 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 1144 void GraphicsContext::translate(float x, float y) 1145 { 1146 if (paintingDisabled()) 1147 return; 1148 1149 m_data->p()->translate(x, y); 1150 } 1151 1152 void GraphicsContext::rotate(float radians) 1153 { 1154 if (paintingDisabled()) 1155 return; 1156 1157 m_data->p()->rotate(rad2deg(qreal(radians))); 1158 } 1159 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 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 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 1216 void GraphicsContext::concatCTM(const AffineTransform& transform) 1217 { 1218 if (paintingDisabled()) 1219 return; 1220 1221 m_data->p()->setWorldTransform(transform, true); 1222 } 1223 1224 void GraphicsContext::setCTM(const AffineTransform& transform) 1225 { 1226 if (paintingDisabled()) 1227 return; 1228 1229 m_data->p()->setWorldTransform(transform); 1230 } 1231 1232 void GraphicsContext::setURLForRect(const KURL&, const IntRect&) 1233 { 1234 notImplemented(); 1235 } 1236 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 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 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 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 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 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 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 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 1395 InterpolationQuality GraphicsContext::imageInterpolationQuality() const 1396 { 1397 return m_data->imageInterpolationQuality; 1398 } 1399 1400 void GraphicsContext::takeOwnershipOfPlatformContext() 1401 { 1402 m_data->takeOwnershipOfPlatformContext(); 1403 } 1404 1405 } 1406 1407 // vim: ts=4 sw=4 et 1408