1/* 2 * Copyright 2006, The Android Open Source Project 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 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * 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 THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25#include "config.h" 26#include "GraphicsContext.h" 27 28#include "AffineTransform.h" 29#include "Font.h" 30#include "Gradient.h" 31#include "NotImplemented.h" 32#include "Path.h" 33#include "Pattern.h" 34#include "PlatformGraphicsContext.h" 35#include "PlatformGraphicsContextSkia.h" 36#include "SkBitmapRef.h" 37#include "SkBlurDrawLooper.h" 38#include "SkBlurMaskFilter.h" 39#include "SkCanvas.h" 40#include "SkColorPriv.h" 41#include "SkCornerPathEffect.h" 42#include "SkDashPathEffect.h" 43#include "SkDevice.h" 44#include "SkGradientShader.h" 45#include "SkPaint.h" 46#include "SkString.h" 47#include "SkiaUtils.h" 48#include "TransformationMatrix.h" 49 50using namespace std; 51 52namespace WebCore { 53 54// This class just holds onto a PlatformContextSkia for GraphicsContext. 55class GraphicsContextPlatformPrivate { 56 WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); 57public: 58 GraphicsContextPlatformPrivate(PlatformGraphicsContext* platformContext) 59 : m_context(platformContext) { } 60 ~GraphicsContextPlatformPrivate() 61 { 62 if (m_context && m_context->deleteUs()) 63 delete m_context; 64 } 65 66 67 PlatformGraphicsContext* context() { return m_context; } 68 69private: 70 // Non-owning pointer to the PlatformContext. 71 PlatformGraphicsContext* m_context; 72}; 73 74static void syncPlatformContext(GraphicsContext* gc) 75{ 76 // Stroke and fill sometimes reference each other, so always 77 // sync them both to make sure our state is consistent. 78 79 PlatformGraphicsContext* pgc = gc->platformContext(); 80 Gradient* grad = gc->state().fillGradient.get(); 81 Pattern* pat = gc->state().fillPattern.get(); 82 83 if (grad) 84 pgc->setFillShader(grad->platformGradient()); 85 else if (pat) 86 pgc->setFillShader(pat->platformPattern(AffineTransform())); 87 else 88 pgc->setFillColor(gc->state().fillColor); 89 90 grad = gc->state().strokeGradient.get(); 91 pat = gc->state().strokePattern.get(); 92 93 if (grad) 94 pgc->setStrokeShader(grad->platformGradient()); 95 else if (pat) 96 pgc->setStrokeShader(pat->platformPattern(AffineTransform())); 97 else 98 pgc->setStrokeColor(gc->state().strokeColor); 99} 100 101//////////////////////////////////////////////////////////////////////////////////////////////// 102 103GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height) 104{ 105 SkBitmap bitmap; 106 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); 107 bitmap.allocPixels(); 108 bitmap.eraseColor(0); 109 110 PlatformGraphicsContextSkia* pgc = 111 new PlatformGraphicsContextSkia(new SkCanvas(bitmap), true); 112 GraphicsContext* ctx = new GraphicsContext(pgc); 113 return ctx; 114} 115 116//////////////////////////////////////////////////////////////////////////////////////////////// 117 118void GraphicsContext::platformInit(PlatformGraphicsContext* gc) 119{ 120 if (gc) 121 gc->setGraphicsContext(this); 122 m_data = new GraphicsContextPlatformPrivate(gc); 123 setPaintingDisabled(!gc || gc->isPaintingDisabled()); 124} 125 126void GraphicsContext::platformDestroy() 127{ 128 delete m_data; 129} 130 131void GraphicsContext::savePlatformState() 132{ 133 if (paintingDisabled()) 134 return; 135 platformContext()->save(); 136} 137 138void GraphicsContext::restorePlatformState() 139{ 140 if (paintingDisabled()) 141 return; 142 platformContext()->restore(); 143} 144 145bool GraphicsContext::willFill() const 146{ 147 return m_state.fillColor.rgb(); 148} 149 150bool GraphicsContext::willStroke() const 151{ 152 return m_state.strokeColor.rgb(); 153} 154 155// Draws a filled rectangle with a stroked border. 156void GraphicsContext::drawRect(const IntRect& rect) 157{ 158 if (paintingDisabled()) 159 return; 160 161 syncPlatformContext(this); 162 platformContext()->drawRect(rect); 163} 164 165// This is only used to draw borders. 166void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) 167{ 168 if (paintingDisabled()) 169 return; 170 171 syncPlatformContext(this); 172 platformContext()->drawLine(point1, point2); 173} 174 175void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool /* printing */) 176{ 177 if (paintingDisabled()) 178 return; 179 180 syncPlatformContext(this); 181 platformContext()->drawLineForText(pt, width); 182} 183 184void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, 185 TextCheckingLineStyle style) 186{ 187 if (paintingDisabled()) 188 return; 189 190 syncPlatformContext(this); 191 platformContext()->drawLineForTextChecking(pt, width, style); 192} 193 194// This method is only used to draw the little circles used in lists. 195void GraphicsContext::drawEllipse(const IntRect& rect) 196{ 197 if (paintingDisabled()) 198 return; 199 200 syncPlatformContext(this); 201 platformContext()->drawEllipse(rect); 202} 203 204void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) 205{ 206 if (paintingDisabled()) 207 return; 208 209 syncPlatformContext(this); 210 platformContext()->strokeArc(r, startAngle, angleSpan); 211} 212 213void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, 214 bool shouldAntialias) 215{ 216 if (paintingDisabled()) 217 return; 218 219 syncPlatformContext(this); 220 platformContext()->drawConvexPolygon(numPoints, points, shouldAntialias); 221} 222 223void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, 224 const IntSize& bottomLeft, const IntSize& bottomRight, 225 const Color& color, ColorSpace colorSpace) 226{ 227 if (paintingDisabled()) 228 return; 229 230 syncPlatformContext(this); 231 platformContext()->fillRoundedRect(rect, topLeft, topRight, 232 bottomLeft, bottomRight, color, colorSpace); 233} 234 235void GraphicsContext::fillRect(const FloatRect& rect) 236{ 237 if (paintingDisabled()) 238 return; 239 240 syncPlatformContext(this); 241 platformContext()->fillRect(rect); 242} 243 244void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) 245{ 246 if (paintingDisabled()) 247 return; 248 249 syncPlatformContext(this); 250 platformContext()->fillRect(rect, color, colorSpace); 251} 252 253void GraphicsContext::clip(const FloatRect& rect) 254{ 255 if (paintingDisabled()) 256 return; 257 258 platformContext()->clip(rect); 259} 260 261void GraphicsContext::clip(const Path& path) 262{ 263 if (paintingDisabled()) 264 return; 265 266 platformContext()->clip(path); 267} 268 269void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) 270{ 271 if (paintingDisabled()) 272 return; 273 274 platformContext()->addInnerRoundedRectClip(rect, thickness); 275} 276 277void GraphicsContext::canvasClip(const Path& path) 278{ 279 if (paintingDisabled()) 280 return; 281 282 platformContext()->canvasClip(path); 283} 284 285void GraphicsContext::clipOut(const IntRect& r) 286{ 287 if (paintingDisabled()) 288 return; 289 290 platformContext()->clipOut(r); 291} 292 293#if ENABLE(SVG) 294void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) 295{ 296 if (paintingDisabled()) 297 return; 298 299 platformContext()->clipPath(pathToClip, clipRule); 300} 301#endif 302 303void GraphicsContext::clipOut(const Path& p) 304{ 305 if (paintingDisabled()) 306 return; 307 308 platformContext()->clipOut(p); 309} 310 311////////////////////////////////////////////////////////////////////////////////////////////////// 312 313#if SVG_SUPPORT 314KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() 315{ 316 return new KRenderingDeviceContextQuartz(platformContext()); 317} 318#endif 319 320void GraphicsContext::beginTransparencyLayer(float opacity) 321{ 322 if (paintingDisabled()) 323 return; 324 325 platformContext()->beginTransparencyLayer(opacity); 326} 327 328void GraphicsContext::endTransparencyLayer() 329{ 330 if (paintingDisabled()) 331 return; 332 333 platformContext()->endTransparencyLayer(); 334} 335 336/////////////////////////////////////////////////////////////////////////// 337 338void GraphicsContext::setupFillPaint(SkPaint* paint) 339{ 340 if (paintingDisabled()) 341 return; 342 syncPlatformContext(this); 343 platformContext()->setupPaintFill(paint); 344} 345 346void GraphicsContext::setupStrokePaint(SkPaint* paint) 347{ 348 if (paintingDisabled()) 349 return; 350 syncPlatformContext(this); 351 platformContext()->setupPaintStroke(paint, 0); 352} 353 354bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) 355{ 356 if (paintingDisabled()) 357 return false; 358 syncPlatformContext(this); 359 return platformContext()->setupPaintShadow(paint, offset); 360} 361 362void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace) 363{ 364} 365 366void GraphicsContext::setPlatformStrokeThickness(float f) 367{ 368 if (paintingDisabled()) 369 return; 370 platformContext()->setStrokeThickness(f); 371} 372 373void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style) 374{ 375 if (paintingDisabled()) 376 return; 377 platformContext()->setStrokeStyle(style); 378} 379 380void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace) 381{ 382} 383 384void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace) 385{ 386 if (paintingDisabled()) 387 return; 388 389 if (blur <= 0) 390 this->clearPlatformShadow(); 391 392 SkColor c; 393 if (color.isValid()) 394 c = color.rgb(); 395 else 396 c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color 397 platformContext()->setShadow(blur, size.width(), size.height(), c); 398} 399 400void GraphicsContext::clearPlatformShadow() 401{ 402 if (paintingDisabled()) 403 return; 404 405 platformContext()->setShadow(0, 0, 0, 0); 406} 407 408/////////////////////////////////////////////////////////////////////////////// 409 410void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) 411{ 412 if (paintingDisabled()) 413 return; 414 415 syncPlatformContext(this); 416 platformContext()->drawFocusRing(rects, width, offset, color); 417} 418 419void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&) 420{ 421 // Do nothing, since we draw the focus ring independently. 422} 423 424PlatformGraphicsContext* GraphicsContext::platformContext() const 425{ 426 ASSERT(!paintingDisabled()); 427 return m_data->context(); 428} 429 430void GraphicsContext::setMiterLimit(float limit) 431{ 432 if (paintingDisabled()) 433 return; 434 platformContext()->setMiterLimit(limit); 435} 436 437void GraphicsContext::setAlpha(float alpha) 438{ 439 if (paintingDisabled()) 440 return; 441 platformContext()->setAlpha(alpha); 442} 443 444void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) 445{ 446 if (paintingDisabled()) 447 return; 448 platformContext()->setCompositeOperation(op); 449} 450 451void GraphicsContext::clearRect(const FloatRect& rect) 452{ 453 if (paintingDisabled()) 454 return; 455 456 syncPlatformContext(this); 457 platformContext()->clearRect(rect); 458} 459 460void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) 461{ 462 if (paintingDisabled()) 463 return; 464 465 syncPlatformContext(this); 466 platformContext()->strokeRect(rect, lineWidth); 467} 468 469void GraphicsContext::setLineCap(LineCap cap) 470{ 471 if (paintingDisabled()) 472 return; 473 platformContext()->setLineCap(cap); 474} 475 476#if ENABLE(SVG) 477void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) 478{ 479 if (paintingDisabled()) 480 return; 481 482 platformContext()->setLineDash(dashes, dashOffset); 483} 484#endif 485 486void GraphicsContext::setLineJoin(LineJoin join) 487{ 488 if (paintingDisabled()) 489 return; 490 platformContext()->setLineJoin(join); 491} 492 493void GraphicsContext::scale(const FloatSize& size) 494{ 495 if (paintingDisabled()) 496 return; 497 platformContext()->scale(size); 498} 499 500void GraphicsContext::rotate(float angleInRadians) 501{ 502 if (paintingDisabled()) 503 return; 504 platformContext()->rotate(angleInRadians); 505} 506 507void GraphicsContext::translate(float x, float y) 508{ 509 if (paintingDisabled()) 510 return; 511 if (!x && !y) 512 return; 513 platformContext()->translate(x, y); 514} 515 516void GraphicsContext::concatCTM(const AffineTransform& affine) 517{ 518 if (paintingDisabled()) 519 return; 520 platformContext()->concatCTM(affine); 521} 522 523// This is intended to round the rect to device pixels (through the CTM) 524// and then invert the result back into source space, with the hope that when 525// it is drawn (through the matrix), it will land in the "right" place (i.e. 526// on pixel boundaries). 527 528// For android, we record this geometry once and then draw it though various 529// scale factors as the user zooms, without re-recording. Thus this routine 530// should just leave the original geometry alone. 531 532// If we instead draw into bitmap tiles, we should then perform this 533// transform -> round -> inverse step. 534 535FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode) 536{ 537 return rect; 538} 539 540////////////////////////////////////////////////////////////////////////////////////////////////// 541 542void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) 543{ 544// Appears to be PDF specific, so we ignore it 545} 546 547void GraphicsContext::setPlatformShouldAntialias(bool useAA) 548{ 549 if (paintingDisabled()) 550 return; 551 platformContext()->setShouldAntialias(useAA); 552} 553 554void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient) 555{ 556} 557 558void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern) 559{ 560} 561 562void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient) 563{ 564} 565 566void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern) 567{ 568} 569 570AffineTransform GraphicsContext::getCTM() const 571{ 572 if (paintingDisabled()) 573 return AffineTransform(); 574 const SkMatrix& m = platformContext()->getTotalMatrix(); 575 return AffineTransform(SkScalarToDouble(m.getScaleX()), // a 576 SkScalarToDouble(m.getSkewY()), // b 577 SkScalarToDouble(m.getSkewX()), // c 578 SkScalarToDouble(m.getScaleY()), // d 579 SkScalarToDouble(m.getTranslateX()), // e 580 SkScalarToDouble(m.getTranslateY())); // f 581} 582 583void GraphicsContext::setCTM(const AffineTransform& transform) 584{ 585 // The SkPicture mode of Skia does not support SkCanvas::setMatrix(), so we 586 // can not simply use that method here. We could calculate the transform 587 // required to achieve the desired matrix and use SkCanvas::concat(), but 588 // there's currently no need for this. 589 ASSERT_NOT_REACHED(); 590} 591 592/////////////////////////////////////////////////////////////////////////////// 593 594void GraphicsContext::fillPath(const Path& pathToFill) 595{ 596 if (paintingDisabled()) 597 return; 598 599 syncPlatformContext(this); 600 platformContext()->fillPath(pathToFill, fillRule()); 601} 602 603void GraphicsContext::strokePath(const Path& pathToStroke) 604{ 605 if (paintingDisabled()) 606 return; 607 608 syncPlatformContext(this); 609 platformContext()->strokePath(pathToStroke); 610} 611 612InterpolationQuality GraphicsContext::imageInterpolationQuality() const 613{ 614 notImplemented(); 615 return InterpolationDefault; 616} 617 618void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) 619{ 620#if 0 621 enum InterpolationQuality { 622 InterpolationDefault, 623 InterpolationNone, 624 InterpolationLow, 625 InterpolationMedium, 626 InterpolationHigh 627 }; 628#endif 629 // TODO: record this, so we can know when to use bitmap-filtering when we draw 630 // ... not sure how meaningful this will be given our playback model. 631 632 // Certainly safe to do nothing for the present. 633} 634 635void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, 636 bool antialias) 637{ 638 if (paintingDisabled()) 639 return; 640 641 if (numPoints <= 1) 642 return; 643 644 // FIXME: IMPLEMENT! 645} 646 647void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, 648 const FloatPoint& point, int h, 649 const Color& backgroundColor, 650 ColorSpace colorSpace, int from, 651 int to, bool isActive) 652{ 653 if (paintingDisabled()) 654 return; 655 656 syncPlatformContext(this); 657 platformContext()->drawHighlightForText(font, run, point, h, backgroundColor, 658 colorSpace, from, to, isActive); 659} 660 661} // namespace WebCore 662