1/**************************************************************************** 2** 3** Copyright (C) 2016 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the plugins of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 3 as published by the Free Software 20** Foundation and appearing in the file LICENSE.LGPL3 included in the 21** packaging of this file. Please review the following information to 22** ensure the GNU Lesser General Public License version 3 requirements 23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24** 25** GNU General Public License Usage 26** Alternatively, this file may be used under the terms of the GNU 27** General Public License version 2.0 or (at your option) the GNU General 28** Public license version 3 or any later version approved by the KDE Free 29** Qt Foundation. The licenses are as published by the Free Software 30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31** included in the packaging of this file. Please review the following 32** information to ensure the GNU General Public License requirements will 33** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34** https://www.gnu.org/licenses/gpl-3.0.html. 35** 36** $QT_END_LICENSE$ 37** 38****************************************************************************/ 39 40#include <AppKit/AppKit.h> 41#include <CoreGraphics/CoreGraphics.h> 42 43#include "qpaintengine_mac_p.h" 44#if defined(QT_PRINTSUPPORT_LIB) 45#include "qprintengine_mac_p.h" 46#endif 47 48#include <qbitmap.h> 49#include <qpaintdevice.h> 50#include <qpainterpath.h> 51#include <qpixmapcache.h> 52#include <private/qpaintengine_raster_p.h> 53#if defined(QT_PRINTSUPPORT_LIB) 54#include <qprinter.h> 55#endif 56#include <qstack.h> 57#include <qwidget.h> 58#include <qvarlengtharray.h> 59#include <qdebug.h> 60#include <qcoreapplication.h> 61#include <qmath.h> 62 63#include <qpa/qplatformpixmap.h> 64 65#include <private/qfont_p.h> 66#include <private/qfontengine_p.h> 67#include <private/qfontengine_coretext_p.h> 68#include <private/qnumeric_p.h> 69#include <private/qpainter_p.h> 70#include <private/qpainterpath_p.h> 71#include <private/qtextengine_p.h> 72#include <private/qcoregraphics_p.h> 73 74#include "qcocoahelpers.h" 75 76#include <string.h> 77 78QT_BEGIN_NAMESPACE 79 80/***************************************************************************** 81 QCoreGraphicsPaintEngine utility functions 82 *****************************************************************************/ 83 84void qt_mac_cgimage_data_free(void *, const void *memoryToFree, size_t) 85{ 86 free(const_cast<void *>(memoryToFree)); 87} 88 89CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr) 90{ 91 QImage image = pixmap.toImage(); 92 if (image.format() != QImage::Format_ARGB32_Premultiplied) 93 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); 94 95 const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height()); 96 const int sbpr = image.bytesPerLine(); 97 const uint nbytes = sw * sh; 98 // alpha is always 255 for bitmaps, ignore it in this case. 99 const quint32 mask = pixmap.depth() == 1 ? 0x00ffffff : 0xffffffff; 100 quint8 *dptr = static_cast<quint8 *>(malloc(nbytes)); 101 quint32 *sptr = reinterpret_cast<quint32 *>(image.scanLine(0)), *srow; 102 for (int y = sy, offset=0; y < sh; ++y) { 103 srow = sptr + (y * (sbpr / 4)); 104 for (int x = sx; x < sw; ++x) 105 *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0; 106 } 107 QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(nullptr, dptr, nbytes, qt_mac_cgimage_data_free); 108 return CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, nullptr, false); 109} 110 111//conversion 112inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; } 113CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) { 114 return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy()); 115} 116 117inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col) 118{ 119 CGFloat components[] = { 120 qt_mac_convert_color_to_cg(col.red()), 121 qt_mac_convert_color_to_cg(col.green()), 122 qt_mac_convert_color_to_cg(col.blue()), 123 qt_mac_convert_color_to_cg(col.alpha()) 124 }; 125 QCFType<CGColorSpaceRef> colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 126 return CGColorCreate(colorSpace, components); 127} 128 129// There's architectural problems with using native gradients 130// on the Mac at the moment, so disable them. 131// #define QT_MAC_USE_NATIVE_GRADIENTS 132 133#ifdef QT_MAC_USE_NATIVE_GRADIENTS 134static bool drawGradientNatively(const QGradient *gradient) 135{ 136 return gradient->spread() == QGradient::PadSpread; 137} 138 139// gradiant callback 140static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out) 141{ 142 QBrush *brush = static_cast<QBrush *>(info); 143 Q_ASSERT(brush && brush->gradient()); 144 145 const QGradientStops stops = brush->gradient()->stops(); 146 const int n = stops.count(); 147 Q_ASSERT(n >= 1); 148 const QGradientStop *begin = stops.constBegin(); 149 const QGradientStop *end = begin + n; 150 151 qreal p = in[0]; 152 const QGradientStop *i = begin; 153 while (i != end && i->first < p) 154 ++i; 155 156 QRgb c; 157 if (i == begin) { 158 c = begin->second.rgba(); 159 } else if (i == end) { 160 c = (end - 1)->second.rgba(); 161 } else { 162 const QGradientStop &s1 = *(i - 1); 163 const QGradientStop &s2 = *i; 164 qreal p1 = s1.first; 165 qreal p2 = s2.first; 166 QRgb c1 = s1.second.rgba(); 167 QRgb c2 = s2.second.rgba(); 168 int idist = 256 * (p - p1) / (p2 - p1); 169 int dist = 256 - idist; 170 c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist), 171 INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist), 172 INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist), 173 INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist)); 174 } 175 176 out[0] = qt_mac_convert_color_to_cg(qRed(c)); 177 out[1] = qt_mac_convert_color_to_cg(qGreen(c)); 178 out[2] = qt_mac_convert_color_to_cg(qBlue(c)); 179 out[3] = qt_mac_convert_color_to_cg(qAlpha(c)); 180} 181#endif 182 183//clipping handling 184void QCoreGraphicsPaintEnginePrivate::resetClip() 185{ 186 static bool inReset = false; 187 if (inReset) 188 return; 189 inReset = true; 190 191 CGAffineTransform old_xform = CGContextGetCTM(hd); 192 193 //setup xforms 194 CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform)); 195 while (stackCount > 0) { 196 restoreGraphicsState(); 197 } 198 saveGraphicsState(); 199 inReset = false; 200 //reset xforms 201 CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd))); 202 CGContextConcatCTM(hd, old_xform); 203} 204 205static CGRect qt_mac_compose_rect(const QRectF &r, float off=0) 206{ 207 return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height()); 208} 209 210static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0) 211{ 212 CGMutablePathRef ret = CGPathCreateMutable(); 213 QPointF startPt; 214 for (int i=0; i<p.elementCount(); ++i) { 215 const QPainterPath::Element &elm = p.elementAt(i); 216 switch (elm.type) { 217 case QPainterPath::MoveToElement: 218 if (i > 0 219 && p.elementAt(i - 1).x == startPt.x() 220 && p.elementAt(i - 1).y == startPt.y()) 221 CGPathCloseSubpath(ret); 222 startPt = QPointF(elm.x, elm.y); 223 CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off); 224 break; 225 case QPainterPath::LineToElement: 226 CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off); 227 break; 228 case QPainterPath::CurveToElement: 229 Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement); 230 Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement); 231 CGPathAddCurveToPoint(ret, 0, 232 elm.x+off, elm.y+off, 233 p.elementAt(i+1).x+off, p.elementAt(i+1).y+off, 234 p.elementAt(i+2).x+off, p.elementAt(i+2).y+off); 235 i+=2; 236 break; 237 default: 238 qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type); 239 break; 240 } 241 } 242 if (!p.isEmpty() 243 && p.elementAt(p.elementCount() - 1).x == startPt.x() 244 && p.elementAt(p.elementCount() - 1).y == startPt.y()) 245 CGPathCloseSubpath(ret); 246 return ret; 247} 248 249//pattern handling (tiling) 250#if 1 251# define QMACPATTERN_MASK_MULTIPLIER 32 252#else 253# define QMACPATTERN_MASK_MULTIPLIER 1 254#endif 255class QMacPattern 256{ 257public: 258 QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; } 259 ~QMacPattern() { CGImageRelease(image); } 260 int width() { 261 if (image) 262 return CGImageGetWidth(image); 263 if (data.bytes) 264 return 8*QMACPATTERN_MASK_MULTIPLIER; 265 return data.pixmap.width(); 266 } 267 int height() { 268 if (image) 269 return CGImageGetHeight(image); 270 if (data.bytes) 271 return 8*QMACPATTERN_MASK_MULTIPLIER; 272 return data.pixmap.height(); 273 } 274 275 //input 276 QColor foreground; 277 bool as_mask; 278 struct { 279 QPixmap pixmap; 280 const uchar *bytes; 281 } data; 282 QPaintDevice *pdev; 283 //output 284 CGImageRef image; 285}; 286static void qt_mac_draw_pattern(void *info, CGContextRef c) 287{ 288 QMacPattern *pat = (QMacPattern*)info; 289 int w = 0, h = 0; 290 bool isBitmap = (pat->data.pixmap.depth() == 1); 291 if (!pat->image) { //lazy cache 292 if (pat->as_mask) { 293 Q_ASSERT(pat->data.bytes); 294 w = h = 8; 295#if (QMACPATTERN_MASK_MULTIPLIER == 1) 296 CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, pat->data.bytes, w*h, nullptr); 297 pat->image = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false); 298 CGDataProviderRelease(provider); 299#else 300 const int numBytes = (w*h)/sizeof(uchar); 301 uchar xor_bytes[numBytes]; 302 for (int i = 0; i < numBytes; ++i) 303 xor_bytes[i] = pat->data.bytes[i] ^ 0xFF; 304 CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, xor_bytes, w*h, nullptr); 305 CGImageRef swatch = CGImageMaskCreate(w, h, 1, 1, 1, provider, nullptr, false); 306 CGDataProviderRelease(provider); 307 308 const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255); 309 QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER); 310 pm.fill(c0); 311 QMacCGContext pm_ctx(&pm); 312 CGContextSetFillColorWithColor(c, cgColorForQColor(c1)); 313 CGRect rect = CGRectMake(0, 0, w, h); 314 for (int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) { 315 rect.origin.x = x * w; 316 for (int y = 0; y < QMACPATTERN_MASK_MULTIPLIER; ++y) { 317 rect.origin.y = y * h; 318 qt_mac_drawCGImage(pm_ctx, &rect, swatch); 319 } 320 } 321 pat->image = qt_mac_create_imagemask(pm, pm.rect()); 322 CGImageRelease(swatch); 323 w *= QMACPATTERN_MASK_MULTIPLIER; 324 h *= QMACPATTERN_MASK_MULTIPLIER; 325#endif 326 } else { 327 w = pat->data.pixmap.width(); 328 h = pat->data.pixmap.height(); 329 if (isBitmap) 330 pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect()); 331 else 332 pat->image = qt_mac_toCGImage(pat->data.pixmap.toImage()); 333 } 334 } else { 335 w = CGImageGetWidth(pat->image); 336 h = CGImageGetHeight(pat->image); 337 } 338 339 //draw 340 bool needRestore = false; 341 if (CGImageIsMask(pat->image)) { 342 CGContextSaveGState(c); 343 CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground)); 344 } 345 CGRect rect = CGRectMake(0, 0, w, h); 346 qt_mac_drawCGImage(c, &rect, pat->image); 347 if (needRestore) 348 CGContextRestoreGState(c); 349} 350static void qt_mac_dispose_pattern(void *info) 351{ 352 QMacPattern *pat = (QMacPattern*)info; 353 delete pat; 354} 355 356/***************************************************************************** 357 QCoreGraphicsPaintEngine member functions 358 *****************************************************************************/ 359 360inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features() 361{ 362 return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent 363 & ~QPaintEngine::PerspectiveTransform 364 & ~QPaintEngine::ConicalGradientFill 365 & ~QPaintEngine::LinearGradientFill 366 & ~QPaintEngine::RadialGradientFill 367 & ~QPaintEngine::BrushStroke); 368} 369 370QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine() 371: QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features()) 372{ 373} 374 375QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr) 376: QPaintEngine(dptr, qt_mac_cg_features()) 377{ 378} 379 380QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine() 381{ 382} 383 384bool 385QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev) 386{ 387 Q_D(QCoreGraphicsPaintEngine); 388 if (isActive()) { // already active painting 389 qWarning("QCoreGraphicsPaintEngine::begin: Painter already active"); 390 return false; 391 } 392 393 //initialization 394 d->pdev = pdev; 395 d->complexXForm = false; 396 d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth; 397 d->cosmeticPenSize = 1; 398 d->current.clipEnabled = false; 399 d->pixelSize = QPoint(1,1); 400 401 if (pdev->devType() != QInternal::Printer) { 402 QMacCGContext ctx(pdev); 403 d->hd = CGContextRetain(ctx); 404 if (d->hd) { 405 d->saveGraphicsState(); 406 d->orig_xform = CGContextGetCTM(d->hd); 407 if (d->shading) { 408 CGShadingRelease(d->shading); 409 d->shading = nullptr; 410 } 411 d->setClip(nullptr); //clear the context's clipping 412 } 413 } 414 415 setActive(true); 416 417 if (d->pdev->devType() == QInternal::Widget) { // device is a widget 418 QWidget *w = (QWidget*)d->pdev; 419 bool unclipped = w->testAttribute(Qt::WA_PaintUnclipped); 420 421 if ((w->windowType() == Qt::Desktop)) { 422 if (!unclipped) 423 qWarning("QCoreGraphicsPaintEngine::begin: Does not support clipped desktop on OS X"); 424 // ## need to do [qt_mac_window_for(w) makeKeyAndOrderFront]; (need to rename the file) 425 } else if (unclipped) { 426 qWarning("QCoreGraphicsPaintEngine::begin: Does not support unclipped painting"); 427 } 428 } else if (d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap 429 QPixmap *pm = (QPixmap*)d->pdev; 430 if (pm->isNull()) { 431 qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap"); 432 end(); 433 return false; 434 } 435 } 436 437 setDirty(QPaintEngine::DirtyPen); 438 setDirty(QPaintEngine::DirtyBrush); 439 setDirty(QPaintEngine::DirtyBackground); 440 setDirty(QPaintEngine::DirtyHints); 441 return true; 442} 443 444bool 445QCoreGraphicsPaintEngine::end() 446{ 447 Q_D(QCoreGraphicsPaintEngine); 448 setActive(false); 449 if (d->pdev->devType() == QInternal::Widget && static_cast<QWidget*>(d->pdev)->windowType() == Qt::Desktop) { 450 // ### need to do [qt_mac_window_for(static_cast<QWidget *>(d->pdev)) orderOut]; (need to rename) 451 } 452 if (d->shading) { 453 CGShadingRelease(d->shading); 454 d->shading = 0; 455 } 456 d->pdev = nullptr; 457 if (d->hd) { 458 d->restoreGraphicsState(); 459 CGContextSynchronize(d->hd); 460 CGContextRelease(d->hd); 461 d->hd = nullptr; 462 } 463 return true; 464} 465 466void 467QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state) 468{ 469 Q_D(QCoreGraphicsPaintEngine); 470 QPaintEngine::DirtyFlags flags = state.state(); 471 472 if (flags & DirtyTransform) 473 updateMatrix(state.transform()); 474 475 if (flags & DirtyClipEnabled) { 476 if (state.isClipEnabled()) 477 updateClipPath(painter()->clipPath(), Qt::ReplaceClip); 478 else 479 updateClipPath(QPainterPath(), Qt::NoClip); 480 } 481 482 if (flags & DirtyClipPath) { 483 updateClipPath(state.clipPath(), state.clipOperation()); 484 } else if (flags & DirtyClipRegion) { 485 updateClipRegion(state.clipRegion(), state.clipOperation()); 486 } 487 488 // If the clip has changed we need to update all other states 489 // too, since they are included in the system context on OSX, 490 // and changing the clip resets that context back to scratch. 491 if (flags & (DirtyClipPath | DirtyClipRegion | DirtyClipEnabled)) 492 flags |= AllDirty; 493 494 if (flags & DirtyPen) 495 updatePen(state.pen()); 496 if (flags & (DirtyBrush|DirtyBrushOrigin)) 497 updateBrush(state.brush(), state.brushOrigin()); 498 if (flags & DirtyFont) 499 updateFont(state.font()); 500 if (flags & DirtyOpacity) 501 updateOpacity(state.opacity()); 502 if (flags & DirtyHints) 503 updateRenderHints(state.renderHints()); 504 if (flags & DirtyCompositionMode) 505 updateCompositionMode(state.compositionMode()); 506 507 if (flags & (DirtyPen | DirtyTransform | DirtyHints)) { 508 if (!qt_pen_is_cosmetic(d->current.pen, state.renderHints())) { 509 d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticNone; 510 } else if (d->current.transform.m11() < d->current.transform.m22()-1.0 || 511 d->current.transform.m11() > d->current.transform.m22()+1.0) { 512 d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath; 513 d->cosmeticPenSize = d->adjustPenWidth(d->current.pen.widthF()); 514 if (!d->cosmeticPenSize) 515 d->cosmeticPenSize = 1.0; 516 } else { 517 d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth; 518 static const float sqrt2 = std::sqrt(2.0f); 519 qreal width = d->current.pen.widthF(); 520 if (!width) 521 width = 1; 522 d->cosmeticPenSize = std::sqrt(std::pow(d->pixelSize.y(), 2) + std::pow(d->pixelSize.x(), 2)) / sqrt2 * width; 523 } 524 } 525} 526 527void 528QCoreGraphicsPaintEngine::updatePen(const QPen &pen) 529{ 530 Q_D(QCoreGraphicsPaintEngine); 531 Q_ASSERT(isActive()); 532 d->current.pen = pen; 533 d->setStrokePen(pen); 534} 535 536void 537QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin) 538{ 539 Q_D(QCoreGraphicsPaintEngine); 540 Q_ASSERT(isActive()); 541 d->current.brush = brush; 542 543#ifdef QT_MAC_USE_NATIVE_GRADIENTS 544 // Quartz supports only pad spread 545 if (const QGradient *gradient = brush.gradient()) { 546 if (drawGradientNatively(gradient)) { 547 gccaps |= QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill; 548 } else { 549 gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill); 550 } 551 } 552#endif 553 554 if (d->shading) { 555 CGShadingRelease(d->shading); 556 d->shading = nullptr; 557 } 558 d->setFillBrush(brushOrigin); 559} 560 561void 562QCoreGraphicsPaintEngine::updateOpacity(qreal opacity) 563{ 564 Q_D(QCoreGraphicsPaintEngine); 565 CGContextSetAlpha(d->hd, opacity); 566} 567 568void 569QCoreGraphicsPaintEngine::updateFont(const QFont &) 570{ 571 Q_D(QCoreGraphicsPaintEngine); 572 Q_ASSERT(isActive()); 573 updatePen(d->current.pen); 574} 575 576void 577QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform) 578{ 579 Q_D(QCoreGraphicsPaintEngine); 580 Q_ASSERT(isActive()); 581 582 if (qt_is_nan(transform.m11()) || qt_is_nan(transform.m12()) || qt_is_nan(transform.m13()) 583 || qt_is_nan(transform.m21()) || qt_is_nan(transform.m22()) || qt_is_nan(transform.m23()) 584 || qt_is_nan(transform.m31()) || qt_is_nan(transform.m32()) || qt_is_nan(transform.m33())) 585 return; 586 587 d->current.transform = transform; 588 d->setTransform(transform.isIdentity() ? 0 : &transform); 589 d->complexXForm = (transform.m11() != 1 || transform.m22() != 1 590 || transform.m12() != 0 || transform.m21() != 0); 591 d->pixelSize = d->devicePixelSize(d->hd); 592} 593 594void 595QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op) 596{ 597 Q_D(QCoreGraphicsPaintEngine); 598 Q_ASSERT(isActive()); 599 if (op == Qt::NoClip) { 600 if (d->current.clipEnabled) { 601 d->current.clipEnabled = false; 602 d->current.clip = QRegion(); 603 d->setClip(nullptr); 604 } 605 } else { 606 if (!d->current.clipEnabled) 607 op = Qt::ReplaceClip; 608 d->current.clipEnabled = true; 609 QRegion clipRegion(p.toFillPolygon().toPolygon(), p.fillRule()); 610 if (op == Qt::ReplaceClip) { 611 d->current.clip = clipRegion; 612 d->setClip(nullptr); 613 if (p.isEmpty()) { 614 CGRect rect = CGRectMake(0, 0, 0, 0); 615 CGContextClipToRect(d->hd, rect); 616 } else { 617 CGMutablePathRef path = qt_mac_compose_path(p); 618 CGContextBeginPath(d->hd); 619 CGContextAddPath(d->hd, path); 620 if (p.fillRule() == Qt::WindingFill) 621 CGContextClip(d->hd); 622 else 623 CGContextEOClip(d->hd); 624 CGPathRelease(path); 625 } 626 } else if (op == Qt::IntersectClip) { 627 d->current.clip = d->current.clip.intersected(clipRegion); 628 d->setClip(&d->current.clip); 629 } 630 } 631} 632 633void 634QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op) 635{ 636 Q_D(QCoreGraphicsPaintEngine); 637 Q_ASSERT(isActive()); 638 if (op == Qt::NoClip) { 639 d->current.clipEnabled = false; 640 d->current.clip = QRegion(); 641 d->setClip(nullptr); 642 } else { 643 if (!d->current.clipEnabled) 644 op = Qt::ReplaceClip; 645 d->current.clipEnabled = true; 646 if (op == Qt::IntersectClip) 647 d->current.clip = d->current.clip.intersected(clipRegion); 648 else if (op == Qt::ReplaceClip) 649 d->current.clip = clipRegion; 650 d->setClip(&d->current.clip); 651 } 652} 653 654void 655QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p) 656{ 657 Q_D(QCoreGraphicsPaintEngine); 658 Q_ASSERT(isActive()); 659 660 if (state->compositionMode() == QPainter::CompositionMode_Destination) 661 return; 662 663 CGMutablePathRef path = qt_mac_compose_path(p); 664 uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke; 665 if (p.fillRule() == Qt::WindingFill) 666 ops |= QCoreGraphicsPaintEnginePrivate::CGFill; 667 else 668 ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill; 669 CGContextBeginPath(d->hd); 670 d->drawPath(ops, path); 671 CGPathRelease(path); 672} 673 674void 675QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount) 676{ 677 Q_D(QCoreGraphicsPaintEngine); 678 Q_ASSERT(isActive()); 679 680 if (state->compositionMode() == QPainter::CompositionMode_Destination) 681 return; 682 683 for (int i=0; i<rectCount; ++i) { 684 QRectF r = rects[i]; 685 686 CGMutablePathRef path = CGPathCreateMutable(); 687 CGPathAddRect(path, nullptr, qt_mac_compose_rect(r)); 688 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke, 689 path); 690 CGPathRelease(path); 691 } 692} 693 694void 695QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount) 696{ 697 Q_D(QCoreGraphicsPaintEngine); 698 Q_ASSERT(isActive()); 699 700 if (state->compositionMode() == QPainter::CompositionMode_Destination) 701 return; 702 703 if (d->current.pen.capStyle() == Qt::FlatCap) 704 CGContextSetLineCap(d->hd, kCGLineCapSquare); 705 706 CGMutablePathRef path = CGPathCreateMutable(); 707 for (int i=0; i < pointCount; i++) { 708 float x = points[i].x(), y = points[i].y(); 709 CGPathMoveToPoint(path, nullptr, x, y); 710 CGPathAddLineToPoint(path, nullptr, x+0.001, y); 711 } 712 713 bool doRestore = false; 714 if (d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) { 715 //we don't want adjusted pens for point rendering 716 doRestore = true; 717 d->saveGraphicsState(); 718 CGContextSetLineWidth(d->hd, d->current.pen.widthF()); 719 } 720 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path); 721 if (doRestore) 722 d->restoreGraphicsState(); 723 CGPathRelease(path); 724 if (d->current.pen.capStyle() == Qt::FlatCap) 725 CGContextSetLineCap(d->hd, kCGLineCapButt); 726} 727 728void 729QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r) 730{ 731 Q_D(QCoreGraphicsPaintEngine); 732 Q_ASSERT(isActive()); 733 734 if (state->compositionMode() == QPainter::CompositionMode_Destination) 735 return; 736 737 CGMutablePathRef path = CGPathCreateMutable(); 738 CGAffineTransform transform = CGAffineTransformMakeScale(r.width() / r.height(), 1); 739 CGPathAddArc(path, &transform,(r.x() + (r.width() / 2)) / (r.width() / r.height()), 740 r.y() + (r.height() / 2), r.height() / 2, 0, (2 * M_PI), false); 741 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill | QCoreGraphicsPaintEnginePrivate::CGStroke, 742 path); 743 CGPathRelease(path); 744} 745 746void 747QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) 748{ 749 Q_D(QCoreGraphicsPaintEngine); 750 Q_ASSERT(isActive()); 751 752 if (state->compositionMode() == QPainter::CompositionMode_Destination) 753 return; 754 755 CGMutablePathRef path = CGPathCreateMutable(); 756 CGPathMoveToPoint(path, nullptr, points[0].x(), points[0].y()); 757 for (int x = 1; x < pointCount; ++x) 758 CGPathAddLineToPoint(path, nullptr, points[x].x(), points[x].y()); 759 if (mode != PolylineMode && points[0] != points[pointCount-1]) 760 CGPathAddLineToPoint(path, nullptr, points[0].x(), points[0].y()); 761 uint op = QCoreGraphicsPaintEnginePrivate::CGStroke; 762 if (mode != PolylineMode) 763 op |= mode == OddEvenMode ? QCoreGraphicsPaintEnginePrivate::CGEOFill 764 : QCoreGraphicsPaintEnginePrivate::CGFill; 765 d->drawPath(op, path); 766 CGPathRelease(path); 767} 768 769void 770QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount) 771{ 772 Q_D(QCoreGraphicsPaintEngine); 773 Q_ASSERT(isActive()); 774 775 if (state->compositionMode() == QPainter::CompositionMode_Destination) 776 return; 777 778 CGMutablePathRef path = CGPathCreateMutable(); 779 for (int i = 0; i < lineCount; i++) { 780 const QPointF start = lines[i].p1(), end = lines[i].p2(); 781 CGPathMoveToPoint(path, nullptr, start.x(), start.y()); 782 CGPathAddLineToPoint(path, nullptr, end.x(), end.y()); 783 } 784 d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path); 785 CGPathRelease(path); 786} 787 788void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) 789{ 790 Q_D(QCoreGraphicsPaintEngine); 791 Q_ASSERT(isActive()); 792 793 if (state->compositionMode() == QPainter::CompositionMode_Destination) 794 return; 795 796 if (pm.isNull()) 797 return; 798 799 bool differentSize = (QRectF(0, 0, pm.width(), pm.height()) != sr), doRestore = false; 800 CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); 801 QCFType<CGImageRef> image; 802 bool isBitmap = (pm.depth() == 1); 803 if (isBitmap) { 804 doRestore = true; 805 d->saveGraphicsState(); 806 807 const QColor &col = d->current.pen.color(); 808 CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col)); 809 image = qt_mac_create_imagemask(pm, sr); 810 } else if (differentSize) { 811 QCFType<CGImageRef> img = qt_mac_toCGImage(pm.toImage()); 812 if (img) 813 image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height()))); 814 } else { 815 image = qt_mac_toCGImage(pm.toImage()); 816 } 817 qt_mac_drawCGImage(d->hd, &rect, image); 818 if (doRestore) 819 d->restoreGraphicsState(); 820} 821 822void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr, 823 Qt::ImageConversionFlags flags) 824{ 825 Q_D(QCoreGraphicsPaintEngine); 826 Q_UNUSED(flags); 827 Q_ASSERT(isActive()); 828 829 if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination) 830 return; 831 832 QCFType<CGImageRef> cgimage = qt_mac_toCGImage(img); 833 CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); 834 if (QRectF(0, 0, img.width(), img.height()) != sr) 835 cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(), 836 sr.width(), sr.height())); 837 qt_mac_drawCGImage(d->hd, &rect, cgimage); 838} 839 840void QCoreGraphicsPaintEngine::initialize() 841{ 842} 843 844void QCoreGraphicsPaintEngine::cleanup() 845{ 846} 847 848CGContextRef 849QCoreGraphicsPaintEngine::handle() const 850{ 851 return d_func()->hd; 852} 853 854void 855QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, 856 const QPointF &p) 857{ 858 Q_D(QCoreGraphicsPaintEngine); 859 Q_ASSERT(isActive()); 860 861 if (state->compositionMode() == QPainter::CompositionMode_Destination) 862 return; 863 864 //save the old state 865 d->saveGraphicsState(); 866 867 //setup the pattern 868 QMacPattern *qpattern = new QMacPattern; 869 qpattern->data.pixmap = pixmap; 870 qpattern->foreground = d->current.pen.color(); 871 qpattern->pdev = d->pdev; 872 CGPatternCallbacks callbks; 873 callbks.version = 0; 874 callbks.drawPattern = qt_mac_draw_pattern; 875 callbks.releaseInfo = qt_mac_dispose_pattern; 876 const int width = qpattern->width(), height = qpattern->height(); 877 CGAffineTransform trans = CGContextGetCTM(d->hd); 878 CGPatternRef pat = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height), 879 trans, width, height, 880 kCGPatternTilingNoDistortion, true, &callbks); 881 CGColorSpaceRef cs = CGColorSpaceCreatePattern(nullptr); 882 CGContextSetFillColorSpace(d->hd, cs); 883 CGFloat component = 1.0; //just one 884 CGContextSetFillPattern(d->hd, pat, &component); 885 CGSize phase = CGSizeApplyAffineTransform(CGSizeMake(-(p.x()-r.x()), -(p.y()-r.y())), trans); 886 CGContextSetPatternPhase(d->hd, phase); 887 888 //fill the rectangle 889 CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); 890 CGContextFillRect(d->hd, mac_rect); 891 892 //restore the state 893 d->restoreGraphicsState(); 894 //cleanup 895 CGColorSpaceRelease(cs); 896 CGPatternRelease(pat); 897} 898 899void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item) 900{ 901 Q_D(QCoreGraphicsPaintEngine); 902 if (d->current.transform.type() == QTransform::TxProject 903#ifndef QMAC_NATIVE_GRADIENTS 904 || painter()->pen().brush().gradient() //Just let the base engine "emulate" the gradient 905#endif 906 ) { 907 QPaintEngine::drawTextItem(pos, item); 908 return; 909 } 910 911 if (state->compositionMode() == QPainter::CompositionMode_Destination) 912 return; 913 914 const QTextItemInt &ti = static_cast<const QTextItemInt &>(item); 915 916 QPen oldPen = painter()->pen(); 917 QBrush oldBrush = painter()->brush(); 918 QPointF oldBrushOrigin = painter()->brushOrigin(); 919 updatePen(Qt::NoPen); 920 updateBrush(oldPen.brush(), QPointF(0, 0)); 921 922 Q_ASSERT(type() == QPaintEngine::CoreGraphics); 923 924 QFontEngine *fe = ti.fontEngine; 925 926 const bool textAA = ((state->renderHints() & QPainter::TextAntialiasing) 927 && !(fe->fontDef.styleStrategy & QFont::NoAntialias)); 928 const bool lineAA = state->renderHints() & QPainter::Antialiasing; 929 if (textAA != lineAA) 930 CGContextSetShouldAntialias(d->hd, textAA); 931 932 const bool smoothing = textAA && !(fe->fontDef.styleStrategy & QFont::NoSubpixelAntialias); 933 if (d->disabledSmoothFonts == smoothing) 934 CGContextSetShouldSmoothFonts(d->hd, smoothing); 935 936 if (ti.glyphs.numGlyphs) { 937 switch (fe->type()) { 938 case QFontEngine::Mac: 939 static_cast<QCoreTextFontEngine *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height()); 940 break; 941 case QFontEngine::Box: 942 d->drawBoxTextItem(pos, ti); 943 break; 944 default: 945 break; 946 } 947 } 948 949 if (textAA != lineAA) 950 CGContextSetShouldAntialias(d->hd, !textAA); 951 952 if (smoothing == d->disabledSmoothFonts) 953 CGContextSetShouldSmoothFonts(d->hd, !d->disabledSmoothFonts); 954 955 updatePen(oldPen); 956 updateBrush(oldBrush, oldBrushOrigin); 957} 958 959QPainter::RenderHints 960QCoreGraphicsPaintEngine::supportedRenderHints() const 961{ 962 return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); 963} 964enum CGCompositeMode { 965 kCGCompositeModeClear = 0, 966 kCGCompositeModeCopy = 1, 967 kCGCompositeModeSourceOver = 2, 968 kCGCompositeModeSourceIn = 3, 969 kCGCompositeModeSourceOut = 4, 970 kCGCompositeModeSourceAtop = 5, 971 kCGCompositeModeDestinationOver = 6, 972 kCGCompositeModeDestinationIn = 7, 973 kCGCompositeModeDestinationOut = 8, 974 kCGCompositeModeDestinationAtop = 9, 975 kCGCompositeModeXOR = 10, 976 kCGCompositeModePlusDarker = 11, // (max (0, (1-d) + (1-s))) 977 kCGCompositeModePlusLighter = 12, // (min (1, s + d)) 978 }; 979extern "C" { 980 extern void CGContextSetCompositeOperation(CGContextRef, int); 981} // private function, but is in all versions of OS X. 982void 983QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) 984{ 985 int cg_mode = kCGBlendModeNormal; 986 switch (mode) { 987 case QPainter::CompositionMode_Multiply: 988 cg_mode = kCGBlendModeMultiply; 989 break; 990 case QPainter::CompositionMode_Screen: 991 cg_mode = kCGBlendModeScreen; 992 break; 993 case QPainter::CompositionMode_Overlay: 994 cg_mode = kCGBlendModeOverlay; 995 break; 996 case QPainter::CompositionMode_Darken: 997 cg_mode = kCGBlendModeDarken; 998 break; 999 case QPainter::CompositionMode_Lighten: 1000 cg_mode = kCGBlendModeLighten; 1001 break; 1002 case QPainter::CompositionMode_ColorDodge: 1003 cg_mode = kCGBlendModeColorDodge; 1004 break; 1005 case QPainter::CompositionMode_ColorBurn: 1006 cg_mode = kCGBlendModeColorBurn; 1007 break; 1008 case QPainter::CompositionMode_HardLight: 1009 cg_mode = kCGBlendModeHardLight; 1010 break; 1011 case QPainter::CompositionMode_SoftLight: 1012 cg_mode = kCGBlendModeSoftLight; 1013 break; 1014 case QPainter::CompositionMode_Difference: 1015 cg_mode = kCGBlendModeDifference; 1016 break; 1017 case QPainter::CompositionMode_Exclusion: 1018 cg_mode = kCGBlendModeExclusion; 1019 break; 1020 case QPainter::CompositionMode_Plus: 1021 cg_mode = kCGBlendModePlusLighter; 1022 break; 1023 case QPainter::CompositionMode_SourceOver: 1024 cg_mode = kCGBlendModeNormal; 1025 break; 1026 case QPainter::CompositionMode_DestinationOver: 1027 cg_mode = kCGBlendModeDestinationOver; 1028 break; 1029 case QPainter::CompositionMode_Clear: 1030 cg_mode = kCGBlendModeClear; 1031 break; 1032 case QPainter::CompositionMode_Source: 1033 cg_mode = kCGBlendModeCopy; 1034 break; 1035 case QPainter::CompositionMode_Destination: 1036 cg_mode = -1; 1037 break; 1038 case QPainter::CompositionMode_SourceIn: 1039 cg_mode = kCGBlendModeSourceIn; 1040 break; 1041 case QPainter::CompositionMode_DestinationIn: 1042 cg_mode = kCGCompositeModeDestinationIn; 1043 break; 1044 case QPainter::CompositionMode_SourceOut: 1045 cg_mode = kCGBlendModeSourceOut; 1046 break; 1047 case QPainter::CompositionMode_DestinationOut: 1048 cg_mode = kCGBlendModeDestinationOver; 1049 break; 1050 case QPainter::CompositionMode_SourceAtop: 1051 cg_mode = kCGBlendModeSourceAtop; 1052 break; 1053 case QPainter::CompositionMode_DestinationAtop: 1054 cg_mode = kCGBlendModeDestinationAtop; 1055 break; 1056 case QPainter::CompositionMode_Xor: 1057 cg_mode = kCGBlendModeXOR; 1058 break; 1059 default: 1060 break; 1061 } 1062 if (cg_mode > -1) { 1063 CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode)); 1064 } 1065} 1066 1067void 1068QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints) 1069{ 1070 Q_D(QCoreGraphicsPaintEngine); 1071 CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing); 1072 CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? 1073 kCGInterpolationHigh : kCGInterpolationNone); 1074 bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; 1075 if (!textAntialiasing || d->disabledSmoothFonts) { 1076 d->disabledSmoothFonts = !textAntialiasing; 1077 CGContextSetShouldSmoothFonts(d->hd, textAntialiasing); 1078 } 1079} 1080 1081/* 1082 Returns the size of one device pixel in user-space coordinates. 1083*/ 1084QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef) 1085{ 1086 QPointF p1 = current.transform.inverted().map(QPointF(0, 0)); 1087 QPointF p2 = current.transform.inverted().map(QPointF(1, 1)); 1088 return QPointF(qAbs(p2.x() - p1.x()), qAbs(p2.y() - p1.y())); 1089} 1090 1091/* 1092 Adjusts the pen width so we get correct line widths in the 1093 non-transformed, aliased case. 1094*/ 1095float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth) 1096{ 1097 Q_Q(QCoreGraphicsPaintEngine); 1098 float ret = penWidth; 1099 if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) { 1100 if (penWidth < 2) 1101 ret = 1; 1102 else if (penWidth < 3) 1103 ret = 1.5; 1104 else 1105 ret = penWidth -1; 1106 } 1107 return ret; 1108} 1109 1110void 1111QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen) 1112{ 1113 //pencap 1114 CGLineCap cglinecap = kCGLineCapButt; 1115 if (pen.capStyle() == Qt::SquareCap) 1116 cglinecap = kCGLineCapSquare; 1117 else if (pen.capStyle() == Qt::RoundCap) 1118 cglinecap = kCGLineCapRound; 1119 CGContextSetLineCap(hd, cglinecap); 1120 CGContextSetLineWidth(hd, adjustPenWidth(pen.widthF())); 1121 1122 //join 1123 CGLineJoin cglinejoin = kCGLineJoinMiter; 1124 if (pen.joinStyle() == Qt::BevelJoin) 1125 cglinejoin = kCGLineJoinBevel; 1126 else if (pen.joinStyle() == Qt::RoundJoin) 1127 cglinejoin = kCGLineJoinRound; 1128 CGContextSetLineJoin(hd, cglinejoin); 1129// CGContextSetMiterLimit(hd, pen.miterLimit()); 1130 1131 //pen style 1132 QVector<CGFloat> linedashes; 1133 if (pen.style() == Qt::CustomDashLine) { 1134 QVector<qreal> customs = pen.dashPattern(); 1135 for (int i = 0; i < customs.size(); ++i) 1136 linedashes.append(customs.at(i)); 1137 } else if (pen.style() == Qt::DashLine) { 1138 linedashes.append(4); 1139 linedashes.append(2); 1140 } else if (pen.style() == Qt::DotLine) { 1141 linedashes.append(1); 1142 linedashes.append(2); 1143 } else if (pen.style() == Qt::DashDotLine) { 1144 linedashes.append(4); 1145 linedashes.append(2); 1146 linedashes.append(1); 1147 linedashes.append(2); 1148 } else if (pen.style() == Qt::DashDotDotLine) { 1149 linedashes.append(4); 1150 linedashes.append(2); 1151 linedashes.append(1); 1152 linedashes.append(2); 1153 linedashes.append(1); 1154 linedashes.append(2); 1155 } 1156 const CGFloat cglinewidth = pen.widthF() <= 0.0f ? 1.0f : float(pen.widthF()); 1157 for (int i = 0; i < linedashes.size(); ++i) { 1158 linedashes[i] *= cglinewidth; 1159 if (cglinewidth < 3 && (cglinecap == kCGLineCapSquare || cglinecap == kCGLineCapRound)) { 1160 if ((i%2)) 1161 linedashes[i] += cglinewidth/2; 1162 else 1163 linedashes[i] -= cglinewidth/2; 1164 } 1165 } 1166 CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size()); 1167 1168 // color 1169 CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color())); 1170} 1171 1172// Add our own patterns here to deal with the fact that the coordinate system 1173// is flipped vertically with Quartz2D. 1174static const uchar *qt_mac_patternForBrush(int brushStyle) 1175{ 1176 Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern); 1177 static const uchar dense1_pat[] = { 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00 }; 1178 static const uchar dense2_pat[] = { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 }; 1179 static const uchar dense3_pat[] = { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa }; 1180 static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }; 1181 static const uchar dense5_pat[] = { 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 }; 1182 static const uchar dense6_pat[] = { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 }; 1183 static const uchar dense7_pat[] = { 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff }; 1184 static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff }; 1185 static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef }; 1186 static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef }; 1187 static const uchar fdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe }; 1188 static const uchar bdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }; 1189 static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e }; 1190 static const uchar *const pat_tbl[] = { 1191 dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat, 1192 dense6_pat, dense7_pat, 1193 hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat }; 1194 return pat_tbl[brushStyle - Qt::Dense1Pattern]; 1195} 1196 1197void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset) 1198{ 1199 // pattern 1200 Qt::BrushStyle bs = current.brush.style(); 1201#ifdef QT_MAC_USE_NATIVE_GRADIENTS 1202 if (bs == Qt::LinearGradientPattern || bs == Qt::RadialGradientPattern) { 1203 const QGradient *grad = static_cast<const QGradient*>(current.brush.gradient()); 1204 if (drawGradientNatively(grad)) { 1205 Q_ASSERT(grad->spread() == QGradient::PadSpread); 1206 1207 static const CGFloat domain[] = { 0.0f, +1.0f }; 1208 static const CGFunctionCallbacks callbacks = { 0, qt_mac_color_gradient_function, nullptr }; 1209 CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast<void *>(¤t.brush), 1210 1, domain, 4, nullptr, &callbacks); 1211 1212 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB) 1213 if (bs == Qt::LinearGradientPattern) { 1214 const QLinearGradient *linearGrad = static_cast<const QLinearGradient *>(grad); 1215 const QPointF start(linearGrad->start()); 1216 const QPointF stop(linearGrad->finalStop()); 1217 shading = CGShadingCreateAxial(colorspace, CGPointMake(start.x(), start.y()), 1218 CGPointMake(stop.x(), stop.y()), fill_func, true, true); 1219 } else { 1220 Q_ASSERT(bs == Qt::RadialGradientPattern); 1221 const QRadialGradient *radialGrad = static_cast<const QRadialGradient *>(grad); 1222 QPointF center(radialGrad->center()); 1223 QPointF focal(radialGrad->focalPoint()); 1224 qreal radius = radialGrad->radius(); 1225 qreal focalRadius = radialGrad->focalRadius(); 1226 shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()), 1227 focalRadius, CGPointMake(center.x(), center.y()), radius, fill_func, false, true); 1228 } 1229 1230 CGFunctionRelease(fill_func); 1231 } 1232 } else 1233#endif 1234 if (bs != Qt::SolidPattern && bs != Qt::NoBrush 1235#ifndef QT_MAC_USE_NATIVE_GRADIENTS 1236 && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern) 1237#endif 1238 ) 1239 { 1240 QMacPattern *qpattern = new QMacPattern; 1241 qpattern->pdev = pdev; 1242 CGFloat components[4] = { 1.0, 1.0, 1.0, 1.0 }; 1243 CGColorSpaceRef base_colorspace = nullptr; 1244 if (bs == Qt::TexturePattern) { 1245 qpattern->data.pixmap = current.brush.texture(); 1246 if (qpattern->data.pixmap.isQBitmap()) { 1247 const QColor &col = current.brush.color(); 1248 components[0] = qt_mac_convert_color_to_cg(col.red()); 1249 components[1] = qt_mac_convert_color_to_cg(col.green()); 1250 components[2] = qt_mac_convert_color_to_cg(col.blue()); 1251 base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 1252 } 1253 } else { 1254 qpattern->as_mask = true; 1255 1256 qpattern->data.bytes = qt_mac_patternForBrush(bs); 1257 const QColor &col = current.brush.color(); 1258 components[0] = qt_mac_convert_color_to_cg(col.red()); 1259 components[1] = qt_mac_convert_color_to_cg(col.green()); 1260 components[2] = qt_mac_convert_color_to_cg(col.blue()); 1261 base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 1262 } 1263 int width = qpattern->width(), height = qpattern->height(); 1264 qpattern->foreground = current.brush.color(); 1265 1266 CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace); 1267 CGContextSetFillColorSpace(hd, fill_colorspace); 1268 1269 CGAffineTransform xform = CGContextGetCTM(hd); 1270 xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(current.brush.transform()), xform); 1271 xform = CGAffineTransformTranslate(xform, offset.x(), offset.y()); 1272 1273 CGPatternCallbacks callbks; 1274 callbks.version = 0; 1275 callbks.drawPattern = qt_mac_draw_pattern; 1276 callbks.releaseInfo = qt_mac_dispose_pattern; 1277 CGPatternRef fill_pattern = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height), 1278 xform, width, height, kCGPatternTilingNoDistortion, 1279 !base_colorspace, &callbks); 1280 CGContextSetFillPattern(hd, fill_pattern, components); 1281 1282 1283 CGPatternRelease(fill_pattern); 1284 CGColorSpaceRelease(base_colorspace); 1285 CGColorSpaceRelease(fill_colorspace); 1286 } else if (bs != Qt::NoBrush) { 1287 CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color())); 1288 } 1289} 1290 1291void 1292QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn) 1293{ 1294 Q_Q(QCoreGraphicsPaintEngine); 1295 if (hd) { 1296 resetClip(); 1297 QRegion sysClip = q->systemClip(); 1298 if (!sysClip.isEmpty()) 1299 qt_mac_clip_cg(hd, sysClip, &orig_xform); 1300 if (rgn) 1301 qt_mac_clip_cg(hd, *rgn, nullptr); 1302 } 1303} 1304 1305struct qt_mac_cg_transform_path { 1306 CGMutablePathRef path; 1307 CGAffineTransform transform; 1308}; 1309 1310void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element) 1311{ 1312 Q_ASSERT(info && element); 1313 qt_mac_cg_transform_path *t = (qt_mac_cg_transform_path*)info; 1314 switch (element->type) { 1315 case kCGPathElementMoveToPoint: 1316 CGPathMoveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y); 1317 break; 1318 case kCGPathElementAddLineToPoint: 1319 CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y); 1320 break; 1321 case kCGPathElementAddQuadCurveToPoint: 1322 CGPathAddQuadCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y, 1323 element->points[1].x, element->points[1].y); 1324 break; 1325 case kCGPathElementAddCurveToPoint: 1326 CGPathAddCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y, 1327 element->points[1].x, element->points[1].y, 1328 element->points[2].x, element->points[2].y); 1329 break; 1330 case kCGPathElementCloseSubpath: 1331 CGPathCloseSubpath(t->path); 1332 break; 1333 default: 1334 qDebug() << "Unhandled path transform type: " << element->type; 1335 } 1336} 1337 1338void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path) 1339{ 1340 Q_Q(QCoreGraphicsPaintEngine); 1341 Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen 1342 if ((ops & (CGFill | CGEOFill))) { 1343 if (shading) { 1344 Q_ASSERT(path); 1345 CGContextBeginPath(hd); 1346 CGContextAddPath(hd, path); 1347 saveGraphicsState(); 1348 if (ops & CGFill) 1349 CGContextClip(hd); 1350 else if (ops & CGEOFill) 1351 CGContextEOClip(hd); 1352 if (current.brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) { 1353 CGRect boundingBox = CGPathGetBoundingBox(path); 1354 CGContextConcatCTM(hd, 1355 CGAffineTransformMake(boundingBox.size.width, 0, 1356 0, boundingBox.size.height, 1357 boundingBox.origin.x, boundingBox.origin.y)); 1358 } 1359 CGContextDrawShading(hd, shading); 1360 restoreGraphicsState(); 1361 ops &= ~CGFill; 1362 ops &= ~CGEOFill; 1363 } else if (current.brush.style() == Qt::NoBrush) { 1364 ops &= ~CGFill; 1365 ops &= ~CGEOFill; 1366 } 1367 } 1368 if ((ops & CGStroke) && current.pen.style() == Qt::NoPen) 1369 ops &= ~CGStroke; 1370 1371 if (ops & (CGEOFill | CGFill)) { 1372 CGContextBeginPath(hd); 1373 CGContextAddPath(hd, path); 1374 if (ops & CGEOFill) { 1375 CGContextEOFillPath(hd); 1376 } else { 1377 CGContextFillPath(hd); 1378 } 1379 } 1380 1381 // Avoid saving and restoring the context if we can. 1382 const bool needContextSave = (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone || 1383 !(q->state->renderHints() & QPainter::Antialiasing)); 1384 if (ops & CGStroke) { 1385 if (needContextSave) 1386 saveGraphicsState(); 1387 CGContextBeginPath(hd); 1388 1389 // Translate a fraction of a pixel size in the y direction 1390 // to make sure that primitives painted at pixel borders 1391 // fills the right pixel. This is needed since the y xais 1392 // in the Quartz coordinate system is inverted compared to Qt. 1393 if (!(q->state->renderHints() & QPainter::Antialiasing)) { 1394 if (current.pen.style() == Qt::SolidLine || current.pen.width() >= 3) 1395 CGContextTranslateCTM(hd, double(pixelSize.x()) * 0.25, double(pixelSize.y()) * 0.25); 1396 else 1397 CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1); 1398 } 1399 1400 if (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone) { 1401 // If antialiazing is enabled, use the cosmetic pen size directly. 1402 if (q->state->renderHints() & QPainter::Antialiasing) 1403 CGContextSetLineWidth(hd, cosmeticPenSize); 1404 else if (current.pen.widthF() <= 1) 1405 CGContextSetLineWidth(hd, cosmeticPenSize * 0.9f); 1406 else 1407 CGContextSetLineWidth(hd, cosmeticPenSize); 1408 } 1409 if (cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath) { 1410 qt_mac_cg_transform_path t; 1411 t.transform = qt_mac_convert_transform_to_cg(current.transform); 1412 t.path = CGPathCreateMutable(); 1413 CGPathApply(path, &t, qt_mac_cg_transform_path_apply); //transform the path 1414 setTransform(nullptr); //unset the context transform 1415 CGContextSetLineWidth(hd, cosmeticPenSize); 1416 CGContextAddPath(hd, t.path); 1417 CGPathRelease(t.path); 1418 } else { 1419 CGContextAddPath(hd, path); 1420 } 1421 1422 CGContextStrokePath(hd); 1423 if (needContextSave) 1424 restoreGraphicsState(); 1425 } 1426} 1427 1428QT_END_NAMESPACE 1429