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