1#define LOG_TAG "PlatformGraphicsContextSkia" 2#define LOG_NDEBUG 1 3 4#include "config.h" 5#include "PlatformGraphicsContextSkia.h" 6 7#include "AndroidLog.h" 8#include "Font.h" 9#include "GraphicsContext.h" 10#include "SkCanvas.h" 11#include "SkCornerPathEffect.h" 12#include "SkPaint.h" 13#include "SkShader.h" 14#include "SkiaUtils.h" 15 16namespace WebCore { 17 18// These are the flags we need when we call saveLayer for transparency. 19// Since it does not appear that webkit intends this to also save/restore 20// the matrix or clip, I do not give those flags (for performance) 21#define TRANSPARENCY_SAVEFLAGS \ 22 (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | \ 23 SkCanvas::kFullColorLayer_SaveFlag) 24 25//************************************** 26// Helper functions 27//************************************** 28 29static void setrectForUnderline(SkRect* r, float lineThickness, 30 const FloatPoint& point, int yOffset, float width) 31{ 32#if 0 33 if (lineThickness < 1) // Do we really need/want this? 34 lineThickness = 1; 35#endif 36 r->fLeft = point.x(); 37 r->fTop = point.y() + yOffset; 38 r->fRight = r->fLeft + width; 39 r->fBottom = r->fTop + lineThickness; 40} 41 42static inline int fastMod(int value, int max) 43{ 44 int sign = SkExtractSign(value); 45 46 value = SkApplySign(value, sign); 47 if (value >= max) 48 value %= max; 49 return SkApplySign(value, sign); 50} 51 52static inline void fixPaintForBitmapsThatMaySeam(SkPaint* paint) { 53 /* Bitmaps may be drawn to seem next to other images. If we are drawn 54 zoomed, or at fractional coordinates, we may see cracks/edges if 55 we antialias, because that will cause us to draw the same pixels 56 more than once (e.g. from the left and right bitmaps that share 57 an edge). 58 59 Disabling antialiasing fixes this, and since so far we are never 60 rotated at non-multiple-of-90 angles, this seems to do no harm 61 */ 62 paint->setAntiAlias(false); 63} 64 65//************************************** 66// PlatformGraphicsContextSkia 67//************************************** 68 69PlatformGraphicsContextSkia::PlatformGraphicsContextSkia(SkCanvas* canvas, 70 bool takeCanvasOwnership) 71 : PlatformGraphicsContext() 72 , mCanvas(canvas) 73 , m_deleteCanvas(takeCanvasOwnership) 74{ 75 m_gc = 0; 76} 77 78PlatformGraphicsContextSkia::~PlatformGraphicsContextSkia() 79{ 80 if (m_deleteCanvas) 81 delete mCanvas; 82} 83 84bool PlatformGraphicsContextSkia::isPaintingDisabled() 85{ 86 return !mCanvas; 87} 88 89//************************************** 90// State management 91//************************************** 92 93void PlatformGraphicsContextSkia::beginTransparencyLayer(float opacity) 94{ 95 SkCanvas* canvas = mCanvas; 96 canvas->saveLayerAlpha(0, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS); 97} 98 99void PlatformGraphicsContextSkia::endTransparencyLayer() 100{ 101 if (!mCanvas) 102 return; 103 mCanvas->restore(); 104} 105 106void PlatformGraphicsContextSkia::save() 107{ 108 PlatformGraphicsContext::save(); 109 // Save our native canvas. 110 mCanvas->save(); 111} 112 113void PlatformGraphicsContextSkia::restore() 114{ 115 PlatformGraphicsContext::restore(); 116 // Restore our native canvas. 117 mCanvas->restore(); 118} 119 120//************************************** 121// Matrix operations 122//************************************** 123 124void PlatformGraphicsContextSkia::concatCTM(const AffineTransform& affine) 125{ 126 mCanvas->concat(affine); 127} 128 129void PlatformGraphicsContextSkia::rotate(float angleInRadians) 130{ 131 float value = angleInRadians * (180.0f / 3.14159265f); 132 mCanvas->rotate(SkFloatToScalar(value)); 133} 134 135void PlatformGraphicsContextSkia::scale(const FloatSize& size) 136{ 137 mCanvas->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height())); 138} 139 140void PlatformGraphicsContextSkia::translate(float x, float y) 141{ 142 mCanvas->translate(SkFloatToScalar(x), SkFloatToScalar(y)); 143} 144 145const SkMatrix& PlatformGraphicsContextSkia::getTotalMatrix() 146{ 147 return mCanvas->getTotalMatrix(); 148} 149 150//************************************** 151// Clipping 152//************************************** 153 154void PlatformGraphicsContextSkia::addInnerRoundedRectClip(const IntRect& rect, 155 int thickness) 156{ 157 SkPath path; 158 SkRect r(rect); 159 160 path.addOval(r, SkPath::kCW_Direction); 161 // Only perform the inset if we won't invert r 162 if (2 * thickness < rect.width() && 2 * thickness < rect.height()) { 163 // Adding one to the thickness doesn't make the border too thick as 164 // it's painted over afterwards. But without this adjustment the 165 // border appears a little anemic after anti-aliasing. 166 r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1)); 167 path.addOval(r, SkPath::kCCW_Direction); 168 } 169 mCanvas->clipPath(path, SkRegion::kIntersect_Op, true); 170} 171 172void PlatformGraphicsContextSkia::canvasClip(const Path& path) 173{ 174 clip(path); 175} 176 177bool PlatformGraphicsContextSkia::clip(const FloatRect& rect) 178{ 179 return mCanvas->clipRect(rect); 180} 181 182bool PlatformGraphicsContextSkia::clip(const Path& path) 183{ 184 return mCanvas->clipPath(*path.platformPath(), SkRegion::kIntersect_Op, true); 185} 186 187bool PlatformGraphicsContextSkia::clipConvexPolygon(size_t numPoints, 188 const FloatPoint*, bool antialias) 189{ 190 if (numPoints <= 1) 191 return true; 192 193 // This is only used if HAVE_PATH_BASED_BORDER_RADIUS_DRAWING is defined 194 // in RenderObject.h which it isn't for us. TODO: Support that :) 195 return true; 196} 197 198bool PlatformGraphicsContextSkia::clipOut(const IntRect& r) 199{ 200 return mCanvas->clipRect(r, SkRegion::kDifference_Op); 201} 202 203bool PlatformGraphicsContextSkia::clipOut(const Path& path) 204{ 205 return mCanvas->clipPath(*path.platformPath(), SkRegion::kDifference_Op); 206} 207 208bool PlatformGraphicsContextSkia::clipPath(const Path& pathToClip, WindRule clipRule) 209{ 210 SkPath path = *pathToClip.platformPath(); 211 path.setFillType(clipRule == RULE_EVENODD 212 ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); 213 return mCanvas->clipPath(path); 214} 215 216void PlatformGraphicsContextSkia::clearRect(const FloatRect& rect) 217{ 218 SkPaint paint; 219 220 setupPaintFill(&paint); 221 paint.setXfermodeMode(SkXfermode::kClear_Mode); 222 223 mCanvas->drawRect(rect, paint); 224} 225 226//************************************** 227// Drawing 228//************************************** 229 230void PlatformGraphicsContextSkia::drawBitmapPattern( 231 const SkBitmap& bitmap, const SkMatrix& matrix, 232 CompositeOperator compositeOp, const FloatRect& destRect) 233{ 234 SkShader* shader = SkShader::CreateBitmapShader(bitmap, 235 SkShader::kRepeat_TileMode, 236 SkShader::kRepeat_TileMode); 237 shader->setLocalMatrix(matrix); 238 SkPaint paint; 239 setupPaintCommon(&paint); 240 paint.setAlpha(getNormalizedAlpha()); 241 paint.setShader(shader); 242 paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); 243 fixPaintForBitmapsThatMaySeam(&paint); 244 mCanvas->drawRect(destRect, paint); 245} 246 247void PlatformGraphicsContextSkia::drawBitmapRect(const SkBitmap& bitmap, 248 const SkIRect* src, const SkRect& dst, 249 CompositeOperator op) 250{ 251 SkPaint paint; 252 setupPaintCommon(&paint); 253 paint.setAlpha(getNormalizedAlpha()); 254 paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op)); 255 fixPaintForBitmapsThatMaySeam(&paint); 256 257 mCanvas->drawBitmapRect(bitmap, src, dst, &paint); 258} 259 260void PlatformGraphicsContextSkia::drawConvexPolygon(size_t numPoints, 261 const FloatPoint* points, 262 bool shouldAntialias) 263{ 264 if (numPoints <= 1) 265 return; 266 267 SkPaint paint; 268 SkPath path; 269 270 path.incReserve(numPoints); 271 path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y())); 272 for (size_t i = 1; i < numPoints; i++) 273 path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y())); 274 275 if (mCanvas->quickReject(path, shouldAntialias ? 276 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) { 277 return; 278 } 279 280 if (m_state->fillColor & 0xFF000000) { 281 setupPaintFill(&paint); 282 paint.setAntiAlias(shouldAntialias); 283 mCanvas->drawPath(path, paint); 284 } 285 286 if (m_state->strokeStyle != NoStroke) { 287 paint.reset(); 288 setupPaintStroke(&paint, 0); 289 paint.setAntiAlias(shouldAntialias); 290 mCanvas->drawPath(path, paint); 291 } 292} 293 294void PlatformGraphicsContextSkia::drawEllipse(const IntRect& rect) 295{ 296 SkPaint paint; 297 SkRect oval(rect); 298 299 if (m_state->fillColor & 0xFF000000) { 300 setupPaintFill(&paint); 301 mCanvas->drawOval(oval, paint); 302 } 303 if (m_state->strokeStyle != NoStroke) { 304 paint.reset(); 305 setupPaintStroke(&paint, &oval); 306 mCanvas->drawOval(oval, paint); 307 } 308} 309 310void PlatformGraphicsContextSkia::drawFocusRing(const Vector<IntRect>& rects, 311 int /* width */, int /* offset */, 312 const Color& color) 313{ 314 unsigned rectCount = rects.size(); 315 if (!rectCount) 316 return; 317 318 SkRegion focusRingRegion; 319 const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.8); 320 for (unsigned i = 0; i < rectCount; i++) { 321 SkIRect r = rects[i]; 322 r.inset(-focusRingOutset, -focusRingOutset); 323 focusRingRegion.op(r, SkRegion::kUnion_Op); 324 } 325 326 SkPath path; 327 SkPaint paint; 328 paint.setAntiAlias(true); 329 paint.setStyle(SkPaint::kStroke_Style); 330 331 paint.setColor(color.rgb()); 332 paint.setStrokeWidth(focusRingOutset * 2); 333 paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref(); 334 focusRingRegion.getBoundaryPath(&path); 335 mCanvas->drawPath(path, paint); 336} 337 338void PlatformGraphicsContextSkia::drawHighlightForText( 339 const Font& font, const TextRun& run, const FloatPoint& point, int h, 340 const Color& backgroundColor, ColorSpace colorSpace, int from, 341 int to, bool isActive) 342{ 343 IntRect rect = (IntRect)font.selectionRectForText(run, point, h, from, to); 344 if (isActive) 345 fillRect(rect, backgroundColor); 346 else { 347 int x = rect.x(), y = rect.y(), w = rect.width(), h = rect.height(); 348 const int t = 3, t2 = t * 2; 349 350 fillRect(IntRect(x, y, w, t), backgroundColor); 351 fillRect(IntRect(x, y+h-t, w, t), backgroundColor); 352 fillRect(IntRect(x, y+t, t, h-t2), backgroundColor); 353 fillRect(IntRect(x+w-t, y+t, t, h-t2), backgroundColor); 354 } 355} 356 357void PlatformGraphicsContextSkia::drawLine(const IntPoint& point1, 358 const IntPoint& point2) 359{ 360 StrokeStyle style = m_state->strokeStyle; 361 if (style == NoStroke) 362 return; 363 364 SkPaint paint; 365 SkCanvas* canvas = mCanvas; 366 const int idx = SkAbs32(point2.x() - point1.x()); 367 const int idy = SkAbs32(point2.y() - point1.y()); 368 369 // Special-case horizontal and vertical lines that are really just dots 370 if (setupPaintStroke(&paint, 0, !idy) && (!idx || !idy)) { 371 const SkScalar diameter = paint.getStrokeWidth(); 372 const SkScalar radius = SkScalarHalf(diameter); 373 SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x())); 374 SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y())); 375 SkScalar dx, dy; 376 int count; 377 SkRect bounds; 378 379 if (!idy) { // Horizontal 380 bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius); 381 x += radius; 382 dx = diameter * 2; 383 dy = 0; 384 count = idx; 385 } else { // Vertical 386 bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy)); 387 y += radius; 388 dx = 0; 389 dy = diameter * 2; 390 count = idy; 391 } 392 393 // The actual count is the number of ONs we hit alternating 394 // ON(diameter), OFF(diameter), ... 395 { 396 SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter); 397 // Now compute the number of cells (ON and OFF) 398 count = SkScalarRound(width); 399 // Now compute the number of ONs 400 count = (count + 1) >> 1; 401 } 402 403 SkAutoMalloc storage(count * sizeof(SkPoint)); 404 SkPoint* verts = (SkPoint*)storage.get(); 405 // Now build the array of vertices to past to drawPoints 406 for (int i = 0; i < count; i++) { 407 verts[i].set(x, y); 408 x += dx; 409 y += dy; 410 } 411 412 paint.setStyle(SkPaint::kFill_Style); 413 paint.setPathEffect(0); 414 415 // Clipping to bounds is not required for correctness, but it does 416 // allow us to reject the entire array of points if we are completely 417 // offscreen. This is common in a webpage for android, where most of 418 // the content is clipped out. If drawPoints took an (optional) bounds 419 // parameter, that might even be better, as we would *just* use it for 420 // culling, and not both wacking the canvas' save/restore stack. 421 canvas->save(SkCanvas::kClip_SaveFlag); 422 canvas->clipRect(bounds); 423 canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint); 424 canvas->restore(); 425 } else { 426 SkPoint pts[2] = { point1, point2 }; 427 canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); 428 } 429} 430 431void PlatformGraphicsContextSkia::drawLineForText(const FloatPoint& pt, float width) 432{ 433 SkRect r; 434 setrectForUnderline(&r, m_state->strokeThickness, pt, 0, width); 435 436 SkPaint paint; 437 paint.setAntiAlias(true); 438 paint.setColor(m_state->strokeColor); 439 440 mCanvas->drawRect(r, paint); 441} 442 443void PlatformGraphicsContextSkia::drawLineForTextChecking(const FloatPoint& pt, 444 float width, GraphicsContext::TextCheckingLineStyle) 445{ 446 // TODO: Should we draw different based on TextCheckingLineStyle? 447 SkRect r; 448 setrectForUnderline(&r, m_state->strokeThickness, pt, 0, width); 449 450 SkPaint paint; 451 paint.setAntiAlias(true); 452 paint.setColor(SK_ColorRED); // Is this specified somewhere? 453 454 mCanvas->drawRect(r, paint); 455} 456 457void PlatformGraphicsContextSkia::drawRect(const IntRect& rect) 458{ 459 SkPaint paint; 460 SkRect r(rect); 461 462 if (m_state->fillColor & 0xFF000000) { 463 setupPaintFill(&paint); 464 mCanvas->drawRect(r, paint); 465 } 466 467 // According to GraphicsContext.h, stroking inside drawRect always means 468 // a stroke of 1 inside the rect. 469 if (m_state->strokeStyle != NoStroke && (m_state->strokeColor & 0xFF000000)) { 470 paint.reset(); 471 setupPaintStroke(&paint, &r); 472 paint.setPathEffect(0); // No dashing please 473 paint.setStrokeWidth(SK_Scalar1); // Always just 1.0 width 474 r.inset(SK_ScalarHalf, SK_ScalarHalf); // Ensure we're "inside" 475 mCanvas->drawRect(r, paint); 476 } 477} 478 479void PlatformGraphicsContextSkia::fillPath(const Path& pathToFill, WindRule fillRule) 480{ 481 SkPath* path = pathToFill.platformPath(); 482 if (!path) 483 return; 484 485 switch (fillRule) { 486 case RULE_NONZERO: 487 path->setFillType(SkPath::kWinding_FillType); 488 break; 489 case RULE_EVENODD: 490 path->setFillType(SkPath::kEvenOdd_FillType); 491 break; 492 } 493 494 SkPaint paint; 495 setupPaintFill(&paint); 496 497 mCanvas->drawPath(*path, paint); 498} 499 500void PlatformGraphicsContextSkia::fillRect(const FloatRect& rect) 501{ 502 SkPaint paint; 503 setupPaintFill(&paint); 504 mCanvas->drawRect(rect, paint); 505} 506 507void PlatformGraphicsContextSkia::fillRect(const FloatRect& rect, 508 const Color& color) 509{ 510 if (color.rgb() & 0xFF000000) { 511 SkPaint paint; 512 513 setupPaintCommon(&paint); 514 paint.setColor(color.rgb()); // Punch in the specified color 515 paint.setShader(0); // In case we had one set 516 517 // Sometimes we record and draw portions of the page, using clips 518 // for each portion. The problem with this is that webkit, sometimes, 519 // sees that we're only recording a portion, and they adjust some of 520 // their rectangle coordinates accordingly (e.g. 521 // RenderBoxModelObject::paintFillLayerExtended() which calls 522 // rect.intersect(paintInfo.rect) and then draws the bg with that 523 // rect. The result is that we end up drawing rects that are meant to 524 // seam together (one for each portion), but if the rects have 525 // fractional coordinates (e.g. we are zoomed by a fractional amount) 526 // we will double-draw those edges, resulting in visual cracks or 527 // artifacts. 528 529 // The fix seems to be to just turn off antialasing for rects (this 530 // entry-point in GraphicsContext seems to have been sufficient, 531 // though perhaps we'll find we need to do this as well in fillRect(r) 532 // as well.) Currently setupPaintCommon() enables antialiasing. 533 534 // Since we never show the page rotated at a funny angle, disabling 535 // antialiasing seems to have no real down-side, and it does fix the 536 // bug when we're zoomed (and drawing portions that need to seam). 537 paint.setAntiAlias(false); 538 539 mCanvas->drawRect(rect, paint); 540 } 541} 542 543void PlatformGraphicsContextSkia::fillRoundedRect( 544 const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, 545 const IntSize& bottomLeft, const IntSize& bottomRight, 546 const Color& color) 547{ 548 SkPaint paint; 549 SkPath path; 550 SkScalar radii[8]; 551 552 radii[0] = SkIntToScalar(topLeft.width()); 553 radii[1] = SkIntToScalar(topLeft.height()); 554 radii[2] = SkIntToScalar(topRight.width()); 555 radii[3] = SkIntToScalar(topRight.height()); 556 radii[4] = SkIntToScalar(bottomRight.width()); 557 radii[5] = SkIntToScalar(bottomRight.height()); 558 radii[6] = SkIntToScalar(bottomLeft.width()); 559 radii[7] = SkIntToScalar(bottomLeft.height()); 560 path.addRoundRect(rect, radii); 561 562 setupPaintFill(&paint); 563 paint.setColor(color.rgb()); 564 mCanvas->drawPath(path, paint); 565} 566 567void PlatformGraphicsContextSkia::strokeArc(const IntRect& r, int startAngle, 568 int angleSpan) 569{ 570 SkPath path; 571 SkPaint paint; 572 SkRect oval(r); 573 574 if (m_state->strokeStyle == NoStroke) { 575 setupPaintFill(&paint); // We want the fill color 576 paint.setStyle(SkPaint::kStroke_Style); 577 paint.setStrokeWidth(SkFloatToScalar(m_state->strokeThickness)); 578 } else 579 setupPaintStroke(&paint, 0); 580 581 // We do this before converting to scalar, so we don't overflow SkFixed 582 startAngle = fastMod(startAngle, 360); 583 angleSpan = fastMod(angleSpan, 360); 584 585 path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan)); 586 mCanvas->drawPath(path, paint); 587} 588 589void PlatformGraphicsContextSkia::strokePath(const Path& pathToStroke) 590{ 591 const SkPath* path = pathToStroke.platformPath(); 592 if (!path) 593 return; 594 595 SkPaint paint; 596 setupPaintStroke(&paint, 0); 597 598 mCanvas->drawPath(*path, paint); 599} 600 601void PlatformGraphicsContextSkia::strokeRect(const FloatRect& rect, float lineWidth) 602{ 603 SkPaint paint; 604 605 setupPaintStroke(&paint, 0); 606 paint.setStrokeWidth(SkFloatToScalar(lineWidth)); 607 mCanvas->drawRect(rect, paint); 608} 609 610void PlatformGraphicsContextSkia::drawPosText(const void* text, size_t byteLength, 611 const SkPoint pos[], const SkPaint& paint) 612{ 613 mCanvas->drawPosText(text, byteLength, pos, paint); 614} 615 616void PlatformGraphicsContextSkia::drawMediaButton(const IntRect& rect, RenderSkinMediaButton::MediaButton buttonType, 617 bool translucent, bool drawBackground, 618 const IntRect& thumb) 619{ 620 RenderSkinMediaButton::Draw(mCanvas, rect, buttonType, translucent, drawBackground, thumb); 621} 622 623} // WebCore 624