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 PlatformGraphicsContextSkia* pgc = new PlatformGraphicsContextSkia(new SkCanvas, true); 106 107 SkBitmap bitmap; 108 109 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); 110 bitmap.allocPixels(); 111 bitmap.eraseColor(0); 112 pgc->canvas()->setBitmapDevice(bitmap); 113 114 GraphicsContext* ctx = new GraphicsContext(pgc); 115 return ctx; 116 } 117 118 //////////////////////////////////////////////////////////////////////////////////////////////// 119 120 void GraphicsContext::platformInit(PlatformGraphicsContext* gc) 121 { 122 if (gc) 123 gc->setGraphicsContext(this); 124 m_data = new GraphicsContextPlatformPrivate(gc); 125 setPaintingDisabled(!gc || gc->isPaintingDisabled()); 126 } 127 128 void GraphicsContext::platformDestroy() 129 { 130 delete m_data; 131 } 132 133 void GraphicsContext::savePlatformState() 134 { 135 if (paintingDisabled()) 136 return; 137 platformContext()->save(); 138 } 139 140 void GraphicsContext::restorePlatformState() 141 { 142 if (paintingDisabled()) 143 return; 144 platformContext()->restore(); 145 } 146 147 bool GraphicsContext::willFill() const 148 { 149 return m_state.fillColor.rgb(); 150 } 151 152 bool GraphicsContext::willStroke() const 153 { 154 return m_state.strokeColor.rgb(); 155 } 156 157 // Draws a filled rectangle with a stroked border. 158 void GraphicsContext::drawRect(const IntRect& rect) 159 { 160 if (paintingDisabled()) 161 return; 162 163 syncPlatformContext(this); 164 platformContext()->drawRect(rect); 165 } 166 167 // This is only used to draw borders. 168 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) 169 { 170 if (paintingDisabled()) 171 return; 172 173 syncPlatformContext(this); 174 platformContext()->drawLine(point1, point2); 175 } 176 177 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool /* printing */) 178 { 179 if (paintingDisabled()) 180 return; 181 182 syncPlatformContext(this); 183 platformContext()->drawLineForText(pt, width); 184 } 185 186 void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, 187 TextCheckingLineStyle style) 188 { 189 if (paintingDisabled()) 190 return; 191 192 syncPlatformContext(this); 193 platformContext()->drawLineForTextChecking(pt, width, style); 194 } 195 196 // This method is only used to draw the little circles used in lists. 197 void GraphicsContext::drawEllipse(const IntRect& rect) 198 { 199 if (paintingDisabled()) 200 return; 201 202 syncPlatformContext(this); 203 platformContext()->drawEllipse(rect); 204 } 205 206 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) 207 { 208 if (paintingDisabled()) 209 return; 210 211 syncPlatformContext(this); 212 platformContext()->strokeArc(r, startAngle, angleSpan); 213 } 214 215 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, 216 bool shouldAntialias) 217 { 218 if (paintingDisabled()) 219 return; 220 221 syncPlatformContext(this); 222 platformContext()->drawConvexPolygon(numPoints, points, shouldAntialias); 223 } 224 225 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, 226 const IntSize& bottomLeft, const IntSize& bottomRight, 227 const Color& color, ColorSpace colorSpace) 228 { 229 if (paintingDisabled()) 230 return; 231 232 syncPlatformContext(this); 233 platformContext()->fillRoundedRect(rect, topLeft, topRight, 234 bottomLeft, bottomRight, color, colorSpace); 235 } 236 237 void GraphicsContext::fillRect(const FloatRect& rect) 238 { 239 if (paintingDisabled()) 240 return; 241 242 syncPlatformContext(this); 243 platformContext()->fillRect(rect); 244 } 245 246 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) 247 { 248 if (paintingDisabled()) 249 return; 250 251 syncPlatformContext(this); 252 platformContext()->fillRect(rect, color, colorSpace); 253 } 254 255 void GraphicsContext::clip(const FloatRect& rect) 256 { 257 if (paintingDisabled()) 258 return; 259 260 platformContext()->clip(rect); 261 } 262 263 void GraphicsContext::clip(const Path& path) 264 { 265 if (paintingDisabled()) 266 return; 267 268 platformContext()->clip(path); 269 } 270 271 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) 272 { 273 if (paintingDisabled()) 274 return; 275 276 platformContext()->addInnerRoundedRectClip(rect, thickness); 277 } 278 279 void GraphicsContext::canvasClip(const Path& path) 280 { 281 if (paintingDisabled()) 282 return; 283 284 platformContext()->canvasClip(path); 285 } 286 287 void GraphicsContext::clipOut(const IntRect& r) 288 { 289 if (paintingDisabled()) 290 return; 291 292 platformContext()->clipOut(r); 293 } 294 295 #if ENABLE(SVG) 296 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) 297 { 298 if (paintingDisabled()) 299 return; 300 301 platformContext()->clipPath(pathToClip, clipRule); 302 } 303 #endif 304 305 void GraphicsContext::clipOut(const Path& p) 306 { 307 if (paintingDisabled()) 308 return; 309 310 platformContext()->clipOut(p); 311 } 312 313 ////////////////////////////////////////////////////////////////////////////////////////////////// 314 315 #if SVG_SUPPORT 316 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() 317 { 318 return new KRenderingDeviceContextQuartz(platformContext()); 319 } 320 #endif 321 322 void GraphicsContext::beginTransparencyLayer(float opacity) 323 { 324 if (paintingDisabled()) 325 return; 326 327 platformContext()->beginTransparencyLayer(opacity); 328 } 329 330 void GraphicsContext::endTransparencyLayer() 331 { 332 if (paintingDisabled()) 333 return; 334 335 platformContext()->endTransparencyLayer(); 336 } 337 338 /////////////////////////////////////////////////////////////////////////// 339 340 void GraphicsContext::setupFillPaint(SkPaint* paint) 341 { 342 if (paintingDisabled()) 343 return; 344 syncPlatformContext(this); 345 platformContext()->setupPaintFill(paint); 346 } 347 348 void GraphicsContext::setupStrokePaint(SkPaint* paint) 349 { 350 if (paintingDisabled()) 351 return; 352 syncPlatformContext(this); 353 platformContext()->setupPaintStroke(paint, 0); 354 } 355 356 bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset) 357 { 358 if (paintingDisabled()) 359 return false; 360 syncPlatformContext(this); 361 return platformContext()->setupPaintShadow(paint, offset); 362 } 363 364 void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace) 365 { 366 } 367 368 void GraphicsContext::setPlatformStrokeThickness(float f) 369 { 370 if (paintingDisabled()) 371 return; 372 platformContext()->setStrokeThickness(f); 373 } 374 375 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style) 376 { 377 if (paintingDisabled()) 378 return; 379 platformContext()->setStrokeStyle(style); 380 } 381 382 void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace) 383 { 384 } 385 386 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace) 387 { 388 if (paintingDisabled()) 389 return; 390 391 if (blur <= 0) 392 this->clearPlatformShadow(); 393 394 SkColor c; 395 if (color.isValid()) 396 c = color.rgb(); 397 else 398 c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color 399 platformContext()->setShadow(blur, size.width(), size.height(), c); 400 } 401 402 void GraphicsContext::clearPlatformShadow() 403 { 404 if (paintingDisabled()) 405 return; 406 407 platformContext()->setShadow(0, 0, 0, 0); 408 } 409 410 /////////////////////////////////////////////////////////////////////////////// 411 412 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) 413 { 414 if (paintingDisabled()) 415 return; 416 417 syncPlatformContext(this); 418 platformContext()->drawFocusRing(rects, width, offset, color); 419 } 420 421 void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&) 422 { 423 // Do nothing, since we draw the focus ring independently. 424 } 425 426 PlatformGraphicsContext* GraphicsContext::platformContext() const 427 { 428 ASSERT(!paintingDisabled()); 429 return m_data->context(); 430 } 431 432 void GraphicsContext::setMiterLimit(float limit) 433 { 434 if (paintingDisabled()) 435 return; 436 platformContext()->setMiterLimit(limit); 437 } 438 439 void GraphicsContext::setAlpha(float alpha) 440 { 441 if (paintingDisabled()) 442 return; 443 platformContext()->setAlpha(alpha); 444 } 445 446 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) 447 { 448 if (paintingDisabled()) 449 return; 450 platformContext()->setCompositeOperation(op); 451 } 452 453 void GraphicsContext::clearRect(const FloatRect& rect) 454 { 455 if (paintingDisabled()) 456 return; 457 458 syncPlatformContext(this); 459 platformContext()->clearRect(rect); 460 } 461 462 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) 463 { 464 if (paintingDisabled()) 465 return; 466 467 syncPlatformContext(this); 468 platformContext()->strokeRect(rect, lineWidth); 469 } 470 471 void GraphicsContext::setLineCap(LineCap cap) 472 { 473 if (paintingDisabled()) 474 return; 475 platformContext()->setLineCap(cap); 476 } 477 478 #if ENABLE(SVG) 479 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) 480 { 481 if (paintingDisabled()) 482 return; 483 484 platformContext()->setLineDash(dashes, dashOffset); 485 } 486 #endif 487 488 void GraphicsContext::setLineJoin(LineJoin join) 489 { 490 if (paintingDisabled()) 491 return; 492 platformContext()->setLineJoin(join); 493 } 494 495 void GraphicsContext::scale(const FloatSize& size) 496 { 497 if (paintingDisabled()) 498 return; 499 platformContext()->scale(size); 500 } 501 502 void GraphicsContext::rotate(float angleInRadians) 503 { 504 if (paintingDisabled()) 505 return; 506 platformContext()->rotate(angleInRadians); 507 } 508 509 void GraphicsContext::translate(float x, float y) 510 { 511 if (paintingDisabled()) 512 return; 513 if (!x && !y) 514 return; 515 platformContext()->translate(x, y); 516 } 517 518 void GraphicsContext::concatCTM(const AffineTransform& affine) 519 { 520 if (paintingDisabled()) 521 return; 522 platformContext()->concatCTM(affine); 523 } 524 525 // This is intended to round the rect to device pixels (through the CTM) 526 // and then invert the result back into source space, with the hope that when 527 // it is drawn (through the matrix), it will land in the "right" place (i.e. 528 // on pixel boundaries). 529 530 // For android, we record this geometry once and then draw it though various 531 // scale factors as the user zooms, without re-recording. Thus this routine 532 // should just leave the original geometry alone. 533 534 // If we instead draw into bitmap tiles, we should then perform this 535 // transform -> round -> inverse step. 536 537 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode) 538 { 539 return rect; 540 } 541 542 ////////////////////////////////////////////////////////////////////////////////////////////////// 543 544 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) 545 { 546 // Appears to be PDF specific, so we ignore it 547 } 548 549 void GraphicsContext::setPlatformShouldAntialias(bool useAA) 550 { 551 if (paintingDisabled()) 552 return; 553 platformContext()->setShouldAntialias(useAA); 554 } 555 556 void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient) 557 { 558 } 559 560 void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern) 561 { 562 } 563 564 void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient) 565 { 566 } 567 568 void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern) 569 { 570 } 571 572 AffineTransform GraphicsContext::getCTM() const 573 { 574 if (paintingDisabled()) 575 return AffineTransform(); 576 const SkMatrix& m = platformContext()->getTotalMatrix(); 577 return AffineTransform(SkScalarToDouble(m.getScaleX()), // a 578 SkScalarToDouble(m.getSkewY()), // b 579 SkScalarToDouble(m.getSkewX()), // c 580 SkScalarToDouble(m.getScaleY()), // d 581 SkScalarToDouble(m.getTranslateX()), // e 582 SkScalarToDouble(m.getTranslateY())); // f 583 } 584 585 void GraphicsContext::setCTM(const AffineTransform& transform) 586 { 587 // The SkPicture mode of Skia does not support SkCanvas::setMatrix(), so we 588 // can not simply use that method here. We could calculate the transform 589 // required to achieve the desired matrix and use SkCanvas::concat(), but 590 // there's currently no need for this. 591 ASSERT_NOT_REACHED(); 592 } 593 594 /////////////////////////////////////////////////////////////////////////////// 595 596 void GraphicsContext::fillPath(const Path& pathToFill) 597 { 598 if (paintingDisabled()) 599 return; 600 601 syncPlatformContext(this); 602 platformContext()->fillPath(pathToFill, fillRule()); 603 } 604 605 void GraphicsContext::strokePath(const Path& pathToStroke) 606 { 607 if (paintingDisabled()) 608 return; 609 610 syncPlatformContext(this); 611 platformContext()->strokePath(pathToStroke); 612 } 613 614 InterpolationQuality GraphicsContext::imageInterpolationQuality() const 615 { 616 notImplemented(); 617 return InterpolationDefault; 618 } 619 620 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode) 621 { 622 #if 0 623 enum InterpolationQuality { 624 InterpolationDefault, 625 InterpolationNone, 626 InterpolationLow, 627 InterpolationMedium, 628 InterpolationHigh 629 }; 630 #endif 631 // TODO: record this, so we can know when to use bitmap-filtering when we draw 632 // ... not sure how meaningful this will be given our playback model. 633 634 // Certainly safe to do nothing for the present. 635 } 636 637 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, 638 bool antialias) 639 { 640 if (paintingDisabled()) 641 return; 642 643 if (numPoints <= 1) 644 return; 645 646 // FIXME: IMPLEMENT! 647 } 648 649 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, 650 const FloatPoint& point, int h, 651 const Color& backgroundColor, 652 ColorSpace colorSpace, int from, 653 int to, bool isActive) 654 { 655 if (paintingDisabled()) 656 return; 657 658 syncPlatformContext(this); 659 platformContext()->drawHighlightForText(font, run, point, h, backgroundColor, 660 colorSpace, from, to, isActive); 661 } 662 663 } // namespace WebCore 664