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 50 using namespace std; 51 52 namespace WebCore { 53 54 // This class just holds onto a PlatformContextSkia for GraphicsContext. 55 class GraphicsContextPlatformPrivate { 56 WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); 57 public: 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 69 private: 70 // Non-owning pointer to the PlatformContext. 71 PlatformGraphicsContext* m_context; 72 }; 73 74 static 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 103 GraphicsContext* 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 118 void 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 126 void GraphicsContext::platformDestroy() 127 { 128 delete m_data; 129 } 130 131 void GraphicsContext::savePlatformState() 132 { 133 if (paintingDisabled()) 134 return; 135 platformContext()->save(); 136 } 137 138 void GraphicsContext::restorePlatformState() 139 { 140 if (paintingDisabled()) 141 return; 142 platformContext()->restore(); 143 } 144 145 bool GraphicsContext::willFill() const 146 { 147 return m_state.fillColor.rgb(); 148 } 149 150 bool GraphicsContext::willStroke() const 151 { 152 return m_state.strokeColor.rgb(); 153 } 154 155 // Draws a filled rectangle with a stroked border. 156 void 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. 166 void 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 175 void 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 184 void 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. 195 void GraphicsContext::drawEllipse(const IntRect& rect) 196 { 197 if (paintingDisabled()) 198 return; 199 200 syncPlatformContext(this); 201 platformContext()->drawEllipse(rect); 202 } 203 204 void 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 213 void 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 223 void 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 235 void GraphicsContext::fillRect(const FloatRect& rect) 236 { 237 if (paintingDisabled()) 238 return; 239 240 syncPlatformContext(this); 241 platformContext()->fillRect(rect); 242 } 243 244 void 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 253 void GraphicsContext::clip(const FloatRect& rect) 254 { 255 if (paintingDisabled()) 256 return; 257 258 platformContext()->clip(rect); 259 } 260 261 void GraphicsContext::clip(const Path& path) 262 { 263 if (paintingDisabled()) 264 return; 265 266 platformContext()->clip(path); 267 } 268 269 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) 270 { 271 if (paintingDisabled()) 272 return; 273 274 platformContext()->addInnerRoundedRectClip(rect, thickness); 275 } 276 277 void GraphicsContext::canvasClip(const Path& path) 278 { 279 if (paintingDisabled()) 280 return; 281 282 platformContext()->canvasClip(path); 283 } 284 285 void GraphicsContext::clipOut(const IntRect& r) 286 { 287 if (paintingDisabled()) 288 return; 289 290 platformContext()->clipOut(r); 291 } 292 293 #if ENABLE(SVG) 294 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) 295 { 296 if (paintingDisabled()) 297 return; 298 299 platformContext()->clipPath(pathToClip, clipRule); 300 } 301 #endif 302 303 void GraphicsContext::clipOut(const Path& p) 304 { 305 if (paintingDisabled()) 306 return; 307 308 platformContext()->clipOut(p); 309 } 310 311 ////////////////////////////////////////////////////////////////////////////////////////////////// 312 313 #if SVG_SUPPORT 314 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() 315 { 316 return new KRenderingDeviceContextQuartz(platformContext()); 317 } 318 #endif 319 320 void GraphicsContext::beginTransparencyLayer(float opacity) 321 { 322 if (paintingDisabled()) 323 return; 324 325 platformContext()->beginTransparencyLayer(opacity); 326 } 327 328 void GraphicsContext::endTransparencyLayer() 329 { 330 if (paintingDisabled()) 331 return; 332 333 platformContext()->endTransparencyLayer(); 334 } 335 336 /////////////////////////////////////////////////////////////////////////// 337 338 void GraphicsContext::setupFillPaint(SkPaint* paint) 339 { 340 if (paintingDisabled()) 341 return; 342 syncPlatformContext(this); 343 platformContext()->setupPaintFill(paint); 344 } 345 346 void GraphicsContext::setupStrokePaint(SkPaint* paint) 347 { 348 if (paintingDisabled()) 349 return; 350 syncPlatformContext(this); 351 platformContext()->setupPaintStroke(paint, 0); 352 } 353 354 bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) 355 { 356 if (paintingDisabled()) 357 return false; 358 syncPlatformContext(this); 359 return platformContext()->setupPaintShadow(paint, offset); 360 } 361 362 void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace) 363 { 364 } 365 366 void GraphicsContext::setPlatformStrokeThickness(float f) 367 { 368 if (paintingDisabled()) 369 return; 370 platformContext()->setStrokeThickness(f); 371 } 372 373 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style) 374 { 375 if (paintingDisabled()) 376 return; 377 platformContext()->setStrokeStyle(style); 378 } 379 380 void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace) 381 { 382 } 383 384 void 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 400 void GraphicsContext::clearPlatformShadow() 401 { 402 if (paintingDisabled()) 403 return; 404 405 platformContext()->setShadow(0, 0, 0, 0); 406 } 407 408 /////////////////////////////////////////////////////////////////////////////// 409 410 void 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 419 void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&) 420 { 421 // Do nothing, since we draw the focus ring independently. 422 } 423 424 PlatformGraphicsContext* GraphicsContext::platformContext() const 425 { 426 ASSERT(!paintingDisabled()); 427 return m_data->context(); 428 } 429 430 void GraphicsContext::setMiterLimit(float limit) 431 { 432 if (paintingDisabled()) 433 return; 434 platformContext()->setMiterLimit(limit); 435 } 436 437 void GraphicsContext::setAlpha(float alpha) 438 { 439 if (paintingDisabled()) 440 return; 441 platformContext()->setAlpha(alpha); 442 } 443 444 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) 445 { 446 if (paintingDisabled()) 447 return; 448 platformContext()->setCompositeOperation(op); 449 } 450 451 void GraphicsContext::clearRect(const FloatRect& rect) 452 { 453 if (paintingDisabled()) 454 return; 455 456 syncPlatformContext(this); 457 platformContext()->clearRect(rect); 458 } 459 460 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) 461 { 462 if (paintingDisabled()) 463 return; 464 465 syncPlatformContext(this); 466 platformContext()->strokeRect(rect, lineWidth); 467 } 468 469 void GraphicsContext::setLineCap(LineCap cap) 470 { 471 if (paintingDisabled()) 472 return; 473 platformContext()->setLineCap(cap); 474 } 475 476 #if ENABLE(SVG) 477 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) 478 { 479 if (paintingDisabled()) 480 return; 481 482 platformContext()->setLineDash(dashes, dashOffset); 483 } 484 #endif 485 486 void GraphicsContext::setLineJoin(LineJoin join) 487 { 488 if (paintingDisabled()) 489 return; 490 platformContext()->setLineJoin(join); 491 } 492 493 void GraphicsContext::scale(const FloatSize& size) 494 { 495 if (paintingDisabled()) 496 return; 497 platformContext()->scale(size); 498 } 499 500 void GraphicsContext::rotate(float angleInRadians) 501 { 502 if (paintingDisabled()) 503 return; 504 platformContext()->rotate(angleInRadians); 505 } 506 507 void 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 516 void 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 535 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode) 536 { 537 return rect; 538 } 539 540 ////////////////////////////////////////////////////////////////////////////////////////////////// 541 542 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) 543 { 544 // Appears to be PDF specific, so we ignore it 545 } 546 547 void GraphicsContext::setPlatformShouldAntialias(bool useAA) 548 { 549 if (paintingDisabled()) 550 return; 551 platformContext()->setShouldAntialias(useAA); 552 } 553 554 void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient) 555 { 556 } 557 558 void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern) 559 { 560 } 561 562 void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient) 563 { 564 } 565 566 void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern) 567 { 568 } 569 570 AffineTransform 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 583 void 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 594 void GraphicsContext::fillPath(const Path& pathToFill) 595 { 596 if (paintingDisabled()) 597 return; 598 599 syncPlatformContext(this); 600 platformContext()->fillPath(pathToFill, fillRule()); 601 } 602 603 void GraphicsContext::strokePath(const Path& pathToStroke) 604 { 605 if (paintingDisabled()) 606 return; 607 608 syncPlatformContext(this); 609 platformContext()->strokePath(pathToStroke); 610 } 611 612 InterpolationQuality GraphicsContext::imageInterpolationQuality() const 613 { 614 notImplemented(); 615 return InterpolationDefault; 616 } 617 618 void 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 635 void 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 647 void 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