1/* 2 * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "GraphicsContext.h" 28 29#include "BidiResolver.h" 30#include "BitmapImage.h" 31#include "FloatRoundedRect.h" 32#include "Gradient.h" 33#include "ImageBuffer.h" 34#include "IntRect.h" 35#include "RoundedRect.h" 36#include "TextRun.h" 37 38#include "stdio.h" 39 40namespace WebCore { 41 42class TextRunIterator { 43public: 44 TextRunIterator() 45 : m_textRun(0) 46 , m_offset(0) 47 { 48 } 49 50 TextRunIterator(const TextRun* textRun, unsigned offset) 51 : m_textRun(textRun) 52 , m_offset(offset) 53 { 54 } 55 56 TextRunIterator(const TextRunIterator& other) 57 : m_textRun(other.m_textRun) 58 , m_offset(other.m_offset) 59 { 60 } 61 62 unsigned offset() const { return m_offset; } 63 void increment() { m_offset++; } 64 bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); } 65 UChar current() const { return (*m_textRun)[m_offset]; } 66 UCharDirection direction() const { return atEnd() ? U_OTHER_NEUTRAL : u_charDirection(current()); } 67 68 bool operator==(const TextRunIterator& other) 69 { 70 return m_offset == other.m_offset && m_textRun == other.m_textRun; 71 } 72 73 bool operator!=(const TextRunIterator& other) { return !operator==(other); } 74 75private: 76 const TextRun* m_textRun; 77 int m_offset; 78}; 79 80class InterpolationQualityMaintainer { 81public: 82 explicit InterpolationQualityMaintainer(GraphicsContext& graphicsContext, InterpolationQuality interpolationQualityToUse) 83 : m_graphicsContext(graphicsContext) 84 , m_currentInterpolationQuality(graphicsContext.imageInterpolationQuality()) 85 , m_interpolationQualityChanged(m_currentInterpolationQuality != interpolationQualityToUse) 86 { 87 if (m_interpolationQualityChanged) 88 m_graphicsContext.setImageInterpolationQuality(interpolationQualityToUse); 89 } 90 91 ~InterpolationQualityMaintainer() 92 { 93 if (m_interpolationQualityChanged) 94 m_graphicsContext.setImageInterpolationQuality(m_currentInterpolationQuality); 95 } 96 97private: 98 GraphicsContext& m_graphicsContext; 99 InterpolationQuality m_currentInterpolationQuality; 100 bool m_interpolationQualityChanged; 101}; 102 103#if !PLATFORM(IOS) 104GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext) 105 : m_updatingControlTints(false) 106 , m_transparencyCount(0) 107{ 108 platformInit(platformGraphicsContext); 109} 110#else 111GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext, bool shouldUseContextColors) 112 : m_updatingControlTints(false) 113 , m_transparencyCount(0) 114{ 115 platformInit(platformGraphicsContext, shouldUseContextColors); 116} 117#endif 118 119GraphicsContext::~GraphicsContext() 120{ 121 ASSERT(m_stack.isEmpty()); 122 ASSERT(!m_transparencyCount); 123 platformDestroy(); 124} 125 126void GraphicsContext::save() 127{ 128 if (paintingDisabled()) 129 return; 130 131 m_stack.append(m_state); 132 133 savePlatformState(); 134} 135 136void GraphicsContext::restore() 137{ 138 if (paintingDisabled()) 139 return; 140 141 if (m_stack.isEmpty()) { 142 LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); 143 return; 144 } 145 m_state = m_stack.last(); 146 m_stack.removeLast(); 147 148 restorePlatformState(); 149} 150 151#if PLATFORM(IOS) 152void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace) 153{ 154 if (paintingDisabled()) 155 return; 156 157 save(); 158 159 setStrokeColor(shadowColor, shadowColorSpace); 160 setFillColor(shadowColor, shadowColorSpace); 161 162 drawEllipse(FloatRect(rect.x(), rect.y() + 1, rect.width(), rect.height())); 163 164 setStrokeColor(ellipseColor, ellipseColorSpace); 165 setFillColor(ellipseColor, ellipseColorSpace); 166 167 drawEllipse(rect); 168 169 restore(); 170} 171#endif 172 173void GraphicsContext::setStrokeThickness(float thickness) 174{ 175 m_state.strokeThickness = thickness; 176 setPlatformStrokeThickness(thickness); 177} 178 179void GraphicsContext::setStrokeStyle(StrokeStyle style) 180{ 181 m_state.strokeStyle = style; 182 setPlatformStrokeStyle(style); 183} 184 185void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace) 186{ 187 m_state.strokeColor = color; 188 m_state.strokeColorSpace = colorSpace; 189 m_state.strokeGradient.clear(); 190 m_state.strokePattern.clear(); 191 setPlatformStrokeColor(color, colorSpace); 192} 193 194void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace) 195{ 196 m_state.shadowOffset = offset; 197 m_state.shadowBlur = blur; 198 m_state.shadowColor = color; 199 m_state.shadowColorSpace = colorSpace; 200 setPlatformShadow(offset, blur, color, colorSpace); 201} 202 203void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace) 204{ 205 m_state.shadowOffset = offset; 206 m_state.shadowBlur = blur; 207 m_state.shadowColor = color; 208 m_state.shadowColorSpace = colorSpace; 209#if USE(CG) 210 m_state.shadowsUseLegacyRadius = true; 211#endif 212 setPlatformShadow(offset, blur, color, colorSpace); 213} 214 215void GraphicsContext::clearShadow() 216{ 217 m_state.shadowOffset = FloatSize(); 218 m_state.shadowBlur = 0; 219 m_state.shadowColor = Color(); 220 m_state.shadowColorSpace = ColorSpaceDeviceRGB; 221 clearPlatformShadow(); 222} 223 224bool GraphicsContext::hasShadow() const 225{ 226 return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() 227 && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height()); 228} 229 230bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const 231{ 232 offset = m_state.shadowOffset; 233 blur = m_state.shadowBlur; 234 color = m_state.shadowColor; 235 colorSpace = m_state.shadowColorSpace; 236 237 return hasShadow(); 238} 239 240bool GraphicsContext::hasBlurredShadow() const 241{ 242 return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() && m_state.shadowBlur; 243} 244 245#if USE(CAIRO) 246bool GraphicsContext::mustUseShadowBlur() const 247{ 248 // We can't avoid ShadowBlur if the shadow has blur. 249 if (hasBlurredShadow()) 250 return true; 251 // We can avoid ShadowBlur and optimize, since we're not drawing on a 252 // canvas and box shadows are affected by the transformation matrix. 253 if (!m_state.shadowsIgnoreTransforms) 254 return false; 255 // We can avoid ShadowBlur, since there are no transformations to apply to the canvas. 256 if (getCTM().isIdentity()) 257 return false; 258 // Otherwise, no chance avoiding ShadowBlur. 259 return true; 260} 261#endif 262 263float GraphicsContext::strokeThickness() const 264{ 265 return m_state.strokeThickness; 266} 267 268StrokeStyle GraphicsContext::strokeStyle() const 269{ 270 return m_state.strokeStyle; 271} 272 273Color GraphicsContext::strokeColor() const 274{ 275 return m_state.strokeColor; 276} 277 278ColorSpace GraphicsContext::strokeColorSpace() const 279{ 280 return m_state.strokeColorSpace; 281} 282 283WindRule GraphicsContext::fillRule() const 284{ 285 return m_state.fillRule; 286} 287 288void GraphicsContext::setFillRule(WindRule fillRule) 289{ 290 m_state.fillRule = fillRule; 291} 292 293void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace) 294{ 295 m_state.fillColor = color; 296 m_state.fillColorSpace = colorSpace; 297 m_state.fillGradient.clear(); 298 m_state.fillPattern.clear(); 299 setPlatformFillColor(color, colorSpace); 300} 301 302Color GraphicsContext::fillColor() const 303{ 304 return m_state.fillColor; 305} 306 307ColorSpace GraphicsContext::fillColorSpace() const 308{ 309 return m_state.fillColorSpace; 310} 311 312void GraphicsContext::setShouldAntialias(bool b) 313{ 314 m_state.shouldAntialias = b; 315 setPlatformShouldAntialias(b); 316} 317 318bool GraphicsContext::shouldAntialias() const 319{ 320 return m_state.shouldAntialias; 321} 322 323void GraphicsContext::setShouldSmoothFonts(bool b) 324{ 325 m_state.shouldSmoothFonts = b; 326 setPlatformShouldSmoothFonts(b); 327} 328 329bool GraphicsContext::shouldSmoothFonts() const 330{ 331 return m_state.shouldSmoothFonts; 332} 333 334void GraphicsContext::setShouldSubpixelQuantizeFonts(bool b) 335{ 336 m_state.shouldSubpixelQuantizeFonts = b; 337} 338 339bool GraphicsContext::shouldSubpixelQuantizeFonts() const 340{ 341 return m_state.shouldSubpixelQuantizeFonts; 342} 343 344const GraphicsContextState& GraphicsContext::state() const 345{ 346 return m_state; 347} 348 349void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) 350{ 351 ASSERT(pattern); 352 if (!pattern) { 353 setStrokeColor(Color::black, ColorSpaceDeviceRGB); 354 return; 355 } 356 m_state.strokeGradient.clear(); 357 m_state.strokePattern = pattern; 358} 359 360void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) 361{ 362 ASSERT(pattern); 363 if (!pattern) { 364 setFillColor(Color::black, ColorSpaceDeviceRGB); 365 return; 366 } 367 m_state.fillGradient.clear(); 368 m_state.fillPattern = pattern; 369} 370 371void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) 372{ 373 ASSERT(gradient); 374 if (!gradient) { 375 setStrokeColor(Color::black, ColorSpaceDeviceRGB); 376 return; 377 } 378 m_state.strokeGradient = gradient; 379 m_state.strokePattern.clear(); 380} 381 382void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) 383{ 384 ASSERT(gradient); 385 if (!gradient) { 386 setFillColor(Color::black, ColorSpaceDeviceRGB); 387 return; 388 } 389 m_state.fillGradient = gradient; 390 m_state.fillPattern.clear(); 391} 392 393Gradient* GraphicsContext::fillGradient() const 394{ 395 return m_state.fillGradient.get(); 396} 397 398Gradient* GraphicsContext::strokeGradient() const 399{ 400 return m_state.strokeGradient.get(); 401} 402 403Pattern* GraphicsContext::fillPattern() const 404{ 405 return m_state.fillPattern.get(); 406} 407 408Pattern* GraphicsContext::strokePattern() const 409{ 410 return m_state.strokePattern.get(); 411} 412 413void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms) 414{ 415 m_state.shadowsIgnoreTransforms = ignoreTransforms; 416} 417 418bool GraphicsContext::shadowsIgnoreTransforms() const 419{ 420 return m_state.shadowsIgnoreTransforms; 421} 422 423void GraphicsContext::beginTransparencyLayer(float opacity) 424{ 425 beginPlatformTransparencyLayer(opacity); 426 ++m_transparencyCount; 427} 428 429void GraphicsContext::endTransparencyLayer() 430{ 431 endPlatformTransparencyLayer(); 432 ASSERT(m_transparencyCount > 0); 433 --m_transparencyCount; 434} 435 436bool GraphicsContext::isInTransparencyLayer() const 437{ 438 return (m_transparencyCount > 0) && supportsTransparencyLayers(); 439} 440 441bool GraphicsContext::updatingControlTints() const 442{ 443 return m_updatingControlTints; 444} 445 446void GraphicsContext::setUpdatingControlTints(bool b) 447{ 448 setPaintingDisabled(b); 449 m_updatingControlTints = b; 450} 451 452void GraphicsContext::setPaintingDisabled(bool f) 453{ 454 m_state.paintingDisabled = f; 455} 456 457bool GraphicsContext::paintingDisabled() const 458{ 459 return m_state.paintingDisabled; 460} 461 462// FIXME: Replace the non-iOS implementation with the iOS implementation since the latter computes returns 463// the width of the drawn text. Ensure that there aren't noticeable differences in layout. 464#if !PLATFORM(IOS) 465#if !USE(WINGDI) 466void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to) 467{ 468 if (paintingDisabled()) 469 return; 470 471 font.drawText(this, run, point, from, to); 472} 473#endif 474#else 475float GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to) 476{ 477 if (paintingDisabled()) 478 return 0; 479 480 return font.drawText(this, run, point, from, to); 481} 482#endif // !PLATFORM(IOS) 483 484void GraphicsContext::drawGlyphs(const Font& font, const SimpleFontData& fontData, const GlyphBuffer& buffer, int from, int numGlyphs, const FloatPoint& point) 485{ 486 if (paintingDisabled()) 487 return; 488 489 font.drawGlyphs(this, &fontData, buffer, from, numGlyphs, point); 490} 491 492void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) 493{ 494 if (paintingDisabled()) 495 return; 496 497 font.drawEmphasisMarks(this, run, mark, point, from, to); 498} 499 500// FIXME: Better merge the iOS and non-iOS differences. In particular, make this method use the 501// returned width of the drawn text, Font::drawText(), instead of computing it. Ensure that there 502// aren't noticeable differences in layout with such a change. 503#if !PLATFORM(IOS) 504void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction) 505#else 506float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction, BidiStatus* status, int length) 507#endif 508{ 509 if (paintingDisabled()) 510#if !PLATFORM(IOS) 511 return; 512#else 513 return 0; 514#endif 515 516 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; 517#if !PLATFORM(IOS) 518 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); 519#else 520 bidiResolver.setStatus(status ? *status : BidiStatus(run.direction(), run.directionalOverride())); 521#endif 522 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); 523 524 // FIXME: This ownership should be reversed. We should pass BidiRunList 525 // to BidiResolver in createBidiRunsForLine. 526 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); 527#if !PLATFORM(IOS) 528 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); 529#else 530 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, length < 0 ? run.length() : length)); 531#endif 532 if (!bidiRuns.runCount()) 533#if !PLATFORM(IOS) 534 return; 535#else 536 return 0; 537#endif 538 539 FloatPoint currPoint = point; 540 BidiCharacterRun* bidiRun = bidiRuns.firstRun(); 541 while (bidiRun) { 542 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); 543 bool isRTL = bidiRun->level() % 2; 544 subrun.setDirection(isRTL ? RTL : LTR); 545 subrun.setDirectionalOverride(bidiRun->dirOverride(false)); 546 547#if !PLATFORM(IOS) 548 font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction); 549 550 bidiRun = bidiRun->next(); 551 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. 552 if (bidiRun) 553 currPoint.move(font.width(subrun), 0); 554#else 555 float width = font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction); 556 currPoint.move(width, 0); 557 558 bidiRun = bidiRun->next(); 559#endif 560 } 561 562#if PLATFORM(IOS) 563 if (status) 564 *status = bidiResolver.status(); 565#endif 566 bidiRuns.deleteRuns(); 567 568#if PLATFORM(IOS) 569 return currPoint.x() - static_cast<float>(point.x()); 570#endif 571} 572 573void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions) 574{ 575 if (!image) 576 return; 577 drawImage(image, colorSpace, FloatRect(destination, image->size()), FloatRect(FloatPoint(), image->size()), imagePaintingOptions); 578} 579 580void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions) 581{ 582 if (!image) 583 return; 584 585#if PLATFORM(IOS) 586 FloatRect srcRect(FloatPoint(), image->originalSize()); 587#else 588 FloatRect srcRect(FloatPoint(), image->size()); 589#endif 590 591 drawImage(image, colorSpace, destination, srcRect, imagePaintingOptions); 592} 593 594void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) 595{ 596 if (paintingDisabled() || !image) 597 return; 598 599 // FIXME (49002): Should be InterpolationLow 600 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality()); 601 image->draw(this, destination, source, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_orientationDescription); 602} 603 604void GraphicsContext::drawTiledImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, 605 const ImagePaintingOptions& imagePaintingOptions) 606{ 607 if (paintingDisabled() || !image) 608 return; 609 610 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality()); 611 image->drawTiled(this, destination, source, tileSize, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode); 612} 613 614void GraphicsContext::drawTiledImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, 615 Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) 616{ 617 if (paintingDisabled() || !image) 618 return; 619 620 if (hRule == Image::StretchTile && vRule == Image::StretchTile) { 621 // Just do a scale. 622 drawImage(image, colorSpace, destination, source, imagePaintingOptions); 623 return; 624 } 625 626 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality()); 627 image->drawTiled(this, destination, source, tileScaleFactor, hRule, vRule, colorSpace, imagePaintingOptions.m_compositeOperator); 628} 629 630void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions) 631{ 632 if (!image) 633 return; 634 drawImageBuffer(image, colorSpace, FloatRect(destination, image->logicalSize()), FloatRect(FloatPoint(), image->logicalSize()), imagePaintingOptions); 635} 636 637void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions) 638{ 639 if (!image) 640 return; 641 drawImageBuffer(image, colorSpace, destination, FloatRect(FloatPoint(), FloatSize(image->logicalSize())), imagePaintingOptions); 642} 643 644void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) 645{ 646 if (paintingDisabled() || !image) 647 return; 648 649 // FIXME (49002): Should be InterpolationLow 650 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality()); 651 image->draw(this, colorSpace, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_useLowQualityScale); 652} 653 654void GraphicsContext::clip(const IntRect& rect) 655{ 656 clip(FloatRect(rect)); 657} 658 659void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect) 660{ 661 if (paintingDisabled()) 662 return; 663 664 Path path; 665 path.addRoundedRect(rect); 666 clip(path); 667} 668 669void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect) 670{ 671 if (paintingDisabled()) 672 return; 673 674 if (!rect.isRounded()) { 675 clipOut(rect.rect()); 676 return; 677 } 678 679 Path path; 680 path.addRoundedRect(rect); 681 clipOut(path); 682} 683 684void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect) 685{ 686 if (paintingDisabled()) 687 return; 688 buffer->clip(this, rect); 689} 690 691#if !USE(CG) && !USE(CAIRO) 692IntRect GraphicsContext::clipBounds() const 693{ 694 ASSERT_NOT_REACHED(); 695 return IntRect(); 696} 697#endif 698 699TextDrawingModeFlags GraphicsContext::textDrawingMode() const 700{ 701 return m_state.textDrawingMode; 702} 703 704void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode) 705{ 706 m_state.textDrawingMode = mode; 707 if (paintingDisabled()) 708 return; 709 setPlatformTextDrawingMode(mode); 710} 711 712void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient) 713{ 714 if (paintingDisabled()) 715 return; 716 gradient.fill(this, rect); 717} 718 719void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode) 720{ 721 if (paintingDisabled()) 722 return; 723 724 CompositeOperator previousOperator = compositeOperation(); 725 setCompositeOperation(op, blendMode); 726 fillRect(rect, color, styleColorSpace); 727 setCompositeOperation(previousOperator); 728} 729 730void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace colorSpace, BlendMode blendMode) 731{ 732 if (rect.isRounded()) { 733 setCompositeOperation(compositeOperation(), blendMode); 734 platformFillRoundedRect(rect, color, colorSpace); 735 setCompositeOperation(compositeOperation()); 736 } else 737 fillRect(rect.rect(), color, colorSpace, compositeOperation(), blendMode); 738} 739 740#if !USE(CG) && !USE(CAIRO) 741void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace) 742{ 743 if (paintingDisabled()) 744 return; 745 746 Path path; 747 path.addRect(rect); 748 749 if (!roundedHoleRect.radii().isZero()) 750 path.addRoundedRect(roundedHoleRect); 751 else 752 path.addRect(roundedHoleRect.rect()); 753 754 WindRule oldFillRule = fillRule(); 755 Color oldFillColor = fillColor(); 756 ColorSpace oldFillColorSpace = fillColorSpace(); 757 758 setFillRule(RULE_EVENODD); 759 setFillColor(color, colorSpace); 760 761 fillPath(path); 762 763 setFillRule(oldFillRule); 764 setFillColor(oldFillColor, oldFillColorSpace); 765} 766#endif 767 768void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode) 769{ 770 m_state.compositeOperator = compositeOperation; 771 m_state.blendMode = blendMode; 772 setPlatformCompositeOperation(compositeOperation, blendMode); 773} 774 775CompositeOperator GraphicsContext::compositeOperation() const 776{ 777 return m_state.compositeOperator; 778} 779 780BlendMode GraphicsContext::blendModeOperation() const 781{ 782 return m_state.blendMode; 783} 784 785#if PLATFORM(IOS) 786bool GraphicsContext::emojiDrawingEnabled() 787{ 788 return m_state.emojiDrawingEnabled; 789} 790 791void GraphicsContext::setEmojiDrawingEnabled(bool emojiDrawingEnabled) 792{ 793 m_state.emojiDrawingEnabled = emojiDrawingEnabled; 794} 795#endif 796 797void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask) 798{ 799 m_state.drawLuminanceMask = drawLuminanceMask; 800} 801 802bool GraphicsContext::drawLuminanceMask() const 803{ 804 return m_state.drawLuminanceMask; 805} 806 807#if !USE(CG) 808// Implement this if you want to go ahead and push the drawing mode into your native context 809// immediately. 810void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags) 811{ 812} 813#endif 814 815#if !USE(CAIRO) 816void GraphicsContext::setPlatformStrokeStyle(StrokeStyle) 817{ 818} 819#endif 820 821#if !USE(CG) 822void GraphicsContext::setPlatformShouldSmoothFonts(bool) 823{ 824} 825#endif 826 827#if !USE(CG) && !USE(CAIRO) 828bool GraphicsContext::isAcceleratedContext() const 829{ 830 return false; 831} 832#endif 833 834void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle) 835{ 836 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic 837 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g., 838 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave 839 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. 840 if (penStyle == DottedStroke || penStyle == DashedStroke) { 841 if (p1.x() == p2.x()) { 842 p1.setY(p1.y() + strokeWidth); 843 p2.setY(p2.y() - strokeWidth); 844 } else { 845 p1.setX(p1.x() + strokeWidth); 846 p2.setX(p2.x() - strokeWidth); 847 } 848 } 849 850 if (static_cast<int>(strokeWidth) % 2) { //odd 851 if (p1.x() == p2.x()) { 852 // We're a vertical line. Adjust our x. 853 p1.setX(p1.x() + 0.5f); 854 p2.setX(p2.x() + 0.5f); 855 } else { 856 // We're a horizontal line. Adjust our y. 857 p1.setY(p1.y() + 0.5f); 858 p2.setY(p2.y() + 0.5f); 859 } 860 } 861} 862 863static bool scalesMatch(AffineTransform a, AffineTransform b) 864{ 865 return a.xScale() == b.xScale() && a.yScale() == b.yScale(); 866} 867 868std::unique_ptr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const FloatSize& size, bool hasAlpha) const 869{ 870 // Make the buffer larger if the context's transform is scaling it so we need a higher 871 // resolution than one pixel per unit. Also set up a corresponding scale factor on the 872 // graphics context. 873 874 AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale); 875 FloatSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale()))); 876 877 std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceDeviceRGB, this, hasAlpha); 878 if (!buffer) 879 return nullptr; 880 881 buffer->context()->scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height())); 882 883 return buffer; 884} 885 886bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer* buffer) const 887{ 888 GraphicsContext* bufferContext = buffer->context(); 889 890 return scalesMatch(getCTM(), bufferContext->getCTM()) && isAcceleratedContext() == bufferContext->isAcceleratedContext(); 891} 892 893#if !USE(CG) 894void GraphicsContext::platformApplyDeviceScaleFactor(float) 895{ 896} 897#endif 898 899void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor) 900{ 901 scale(FloatSize(deviceScaleFactor, deviceScaleFactor)); 902 platformApplyDeviceScaleFactor(deviceScaleFactor); 903} 904 905void GraphicsContext::fillEllipse(const FloatRect& ellipse) 906{ 907 platformFillEllipse(ellipse); 908} 909 910void GraphicsContext::strokeEllipse(const FloatRect& ellipse) 911{ 912 platformStrokeEllipse(ellipse); 913} 914 915void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse) 916{ 917 Path path; 918 path.addEllipse(ellipse); 919 fillPath(path); 920} 921 922void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse) 923{ 924 Path path; 925 path.addEllipse(ellipse); 926 strokePath(path); 927} 928 929#if !USE(CG) 930void GraphicsContext::platformFillEllipse(const FloatRect& ellipse) 931{ 932 if (paintingDisabled()) 933 return; 934 935 fillEllipseAsPath(ellipse); 936} 937 938void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse) 939{ 940 if (paintingDisabled()) 941 return; 942 943 strokeEllipseAsPath(ellipse); 944} 945#endif 946 947FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatPoint& point, float width, bool printing, bool& shouldAntialias, Color& color) 948{ 949 FloatPoint origin; 950 float thickness = std::max(strokeThickness(), 0.5f); 951 952 shouldAntialias = true; 953 if (printing) 954 origin = point; 955 else { 956 AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); 957 if (transform.preservesAxisAlignment()) 958 shouldAntialias = false; 959 960 // This code always draws a line that is at least one-pixel line high, 961 // which tends to visually overwhelm text at small scales. To counter this 962 // effect, an alpha is applied to the underline color when text is at small scales. 963 964 // Just compute scale in x dimension, assuming x and y scales are equal. 965 float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a(); 966 if (scale < 1.0) { 967 static const float minimumUnderlineAlpha = 0.4f; 968 float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha; 969 int alpha = color.alpha() * shade; 970 color = Color(color.red(), color.green(), color.blue(), alpha); 971 } 972 973 FloatPoint devicePoint = transform.mapPoint(point); 974 FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y())); 975 origin = transform.inverse().mapPoint(deviceOrigin); 976 } 977 return FloatRect(origin.x(), origin.y(), width, thickness); 978} 979 980} 981