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