2 * Copyright (c) 2006, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "GraphicsContext.h"
34 #include "AffineTransform.h"
36 #include "FloatRect.h"
37 #include "GLES2Canvas.h"
39 #include "GraphicsContextPlatformPrivate.h"
40 #include "ImageBuffer.h"
42 #include "NativeImageSkia.h"
43 #include "NotImplemented.h"
44 #include "PlatformContextSkia.h"
47 #include "SkBlurDrawLooper.h"
48 #include "SkCornerPathEffect.h"
50 #include "SkiaUtils.h"
51 #include "skia/ext/platform_canvas.h"
54 #include <wtf/Assertions.h>
55 #include <wtf/MathExtras.h>
56 #include <wtf/UnusedParam.h>
64 inline int fastMod(int value, int max)
66 int sign = SkExtractSign(value);
68 value = SkApplySign(value, sign);
71 return SkApplySign(value, sign);
74 inline float square(float n)
81 // "Seatbelt" functions ------------------------------------------------------
83 // These functions check certain graphics primitives for being "safe".
84 // Skia has historically crashed when sent crazy data. These functions do
85 // additional checking to prevent crashes.
87 // Ideally, all of these would be fixed in the graphics layer and we would not
88 // have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA
89 // flag to check the graphics layer.
91 // Disabling these checks (20/01/2010), since we think we've fixed all the Skia
92 // bugs. Leaving the code in for now, so we can revert easily if necessary.
93 // #define ENSURE_VALUE_SAFETY_FOR_SKIA
95 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
96 static bool isCoordinateSkiaSafe(float coord)
98 // First check for valid floats.
106 // Skia uses 16.16 fixed point and 26.6 fixed point in various places. If
107 // the transformed point exceeds 15 bits, we just declare that it's
108 // unreasonable to catch both of these cases.
109 static const int maxPointMagnitude = 32767;
110 if (coord > maxPointMagnitude || coord < -maxPointMagnitude)
117 static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt)
119 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
120 // Now check for points that will overflow. We check the *transformed*
121 // points since this is what will be rasterized.
123 transform.mapPoints(&xPt, &pt, 1);
124 return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY);
130 static bool isRectSkiaSafe(const SkMatrix& transform, const SkRect& rc)
132 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
133 SkPoint topleft = {rc.fLeft, rc.fTop};
134 SkPoint bottomright = {rc.fRight, rc.fBottom};
135 return isPointSkiaSafe(transform, topleft) && isPointSkiaSafe(transform, bottomright);
141 bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path)
143 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
144 SkPoint current_points[4];
145 SkPath::Iter iter(path, false);
146 for (SkPath::Verb verb = iter.next(current_points);
147 verb != SkPath::kDone_Verb;
148 verb = iter.next(current_points)) {
150 case SkPath::kMove_Verb:
151 // This move will be duplicated in the next verb, so we can ignore.
153 case SkPath::kLine_Verb:
154 // iter.next returns 2 points.
155 if (!isPointSkiaSafe(transform, current_points[0])
156 || !isPointSkiaSafe(transform, current_points[1]))
159 case SkPath::kQuad_Verb:
160 // iter.next returns 3 points.
161 if (!isPointSkiaSafe(transform, current_points[0])
162 || !isPointSkiaSafe(transform, current_points[1])
163 || !isPointSkiaSafe(transform, current_points[2]))
166 case SkPath::kCubic_Verb:
167 // iter.next returns 4 points.
168 if (!isPointSkiaSafe(transform, current_points[0])
169 || !isPointSkiaSafe(transform, current_points[1])
170 || !isPointSkiaSafe(transform, current_points[2])
171 || !isPointSkiaSafe(transform, current_points[3]))
174 case SkPath::kClose_Verb:
175 case SkPath::kDone_Verb:
186 // Local helper functions ------------------------------------------------------
188 void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle)
191 int rx = SkMin32(SkScalarRound(rect.width()), size.width());
192 int ry = SkMin32(SkScalarRound(rect.height()), size.height());
194 ir.set(-rx, -ry, rx, ry);
195 switch (startAngle) {
197 ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom);
200 ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom);
203 ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop);
206 ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop);
214 path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false);
217 // -----------------------------------------------------------------------------
219 // This may be called with a NULL pointer to create a graphics context that has
221 void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
223 m_data = new GraphicsContextPlatformPrivate(gc);
224 setPaintingDisabled(!gc || !platformContext()->canvas());
227 void GraphicsContext::platformDestroy()
232 PlatformGraphicsContext* GraphicsContext::platformContext() const
234 ASSERT(!paintingDisabled());
235 return m_data->context();
238 // State saving ----------------------------------------------------------------
240 void GraphicsContext::savePlatformState()
242 if (paintingDisabled())
245 if (platformContext()->useGPU())
246 platformContext()->gpuCanvas()->save();
248 // Save our private State.
249 platformContext()->save();
252 void GraphicsContext::restorePlatformState()
254 if (paintingDisabled())
257 if (platformContext()->useGPU())
258 platformContext()->gpuCanvas()->restore();
260 // Restore our private State.
261 platformContext()->restore();
264 void GraphicsContext::beginTransparencyLayer(float opacity)
266 if (paintingDisabled())
269 // We need the "alpha" layer flag here because the base layer is opaque
270 // (the surface of the page) but layers on top may have transparent parts.
271 // Without explicitly setting the alpha flag, the layer will inherit the
272 // opaque setting of the base and some things won't work properly.
273 platformContext()->canvas()->saveLayerAlpha(
275 static_cast<unsigned char>(opacity * 255),
276 static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag |
277 SkCanvas::kFullColorLayer_SaveFlag));
280 void GraphicsContext::endTransparencyLayer()
282 if (paintingDisabled())
284 platformContext()->canvas()->restore();
287 // Graphics primitives ---------------------------------------------------------
289 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
291 if (paintingDisabled())
295 if (!isRectSkiaSafe(getCTM(), r))
298 platformContext()->prepareForSoftwareDraw();
300 path.addOval(r, SkPath::kCW_Direction);
301 // only perform the inset if we won't invert r
302 if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
303 // Adding one to the thickness doesn't make the border too thick as
304 // it's painted over afterwards. But without this adjustment the
305 // border appears a little anemic after anti-aliasing.
306 r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
307 path.addOval(r, SkPath::kCCW_Direction);
309 platformContext()->clipPathAntiAliased(path);
312 void GraphicsContext::clearPlatformShadow()
314 if (paintingDisabled())
316 platformContext()->setDrawLooper(0);
319 void GraphicsContext::clearRect(const FloatRect& rect)
321 if (paintingDisabled())
324 if (platformContext()->useGPU() && !platformContext()->canvasClipApplied()) {
325 platformContext()->prepareForHardwareDraw();
326 platformContext()->gpuCanvas()->clearRect(rect);
330 // Force a readback here (if we're using the GPU), since clearRect() is
331 // incompatible with mixed-mode rendering.
332 platformContext()->syncSoftwareCanvas();
335 if (!isRectSkiaSafe(getCTM(), r))
336 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
339 platformContext()->setupPaintForFilling(&paint);
340 paint.setXfermodeMode(SkXfermode::kClear_Mode);
341 platformContext()->canvas()->drawRect(r, paint);
344 void GraphicsContext::clip(const FloatRect& rect)
346 if (paintingDisabled())
350 if (!isRectSkiaSafe(getCTM(), r))
353 platformContext()->prepareForSoftwareDraw();
354 platformContext()->canvas()->clipRect(r);
357 void GraphicsContext::clip(const Path& path)
359 if (paintingDisabled())
362 const SkPath& p = *path.platformPath();
363 if (!isPathSkiaSafe(getCTM(), p))
366 platformContext()->prepareForSoftwareDraw();
367 platformContext()->clipPathAntiAliased(p);
370 void GraphicsContext::canvasClip(const Path& path)
372 if (paintingDisabled())
375 if (platformContext()->useGPU())
376 platformContext()->gpuCanvas()->clipPath(path);
378 const SkPath& p = *path.platformPath();
379 if (!isPathSkiaSafe(getCTM(), p))
382 platformContext()->canvasClipPath(p);
385 void GraphicsContext::clipOut(const IntRect& rect)
387 if (paintingDisabled())
391 if (!isRectSkiaSafe(getCTM(), r))
394 platformContext()->canvas()->clipRect(r, SkRegion::kDifference_Op);
397 void GraphicsContext::clipOut(const Path& p)
399 if (paintingDisabled())
402 if (platformContext()->useGPU())
403 platformContext()->gpuCanvas()->clipOut(p);
405 const SkPath& path = *p.platformPath();
406 if (!isPathSkiaSafe(getCTM(), path))
409 platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op);
412 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
414 if (paintingDisabled())
417 if (platformContext()->useGPU())
418 platformContext()->gpuCanvas()->clipPath(pathToClip);
420 SkPath path = *pathToClip.platformPath();
421 if (!isPathSkiaSafe(getCTM(), path))
424 path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
425 platformContext()->clipPathAntiAliased(path);
428 void GraphicsContext::concatCTM(const AffineTransform& affine)
430 if (paintingDisabled())
433 if (platformContext()->useGPU())
434 platformContext()->gpuCanvas()->concatCTM(affine);
436 platformContext()->canvas()->concat(affine);
439 void GraphicsContext::setCTM(const AffineTransform& affine)
441 if (paintingDisabled())
444 if (platformContext()->useGPU())
445 platformContext()->gpuCanvas()->setCTM(affine);
447 platformContext()->canvas()->setMatrix(affine);
450 void GraphicsContext::drawConvexPolygon(size_t numPoints,
451 const FloatPoint* points,
452 bool shouldAntialias)
454 if (paintingDisabled())
460 platformContext()->prepareForSoftwareDraw();
464 path.incReserve(numPoints);
465 path.moveTo(WebCoreFloatToSkScalar(points[0].x()),
466 WebCoreFloatToSkScalar(points[0].y()));
467 for (size_t i = 1; i < numPoints; i++) {
468 path.lineTo(WebCoreFloatToSkScalar(points[i].x()),
469 WebCoreFloatToSkScalar(points[i].y()));
472 if (!isPathSkiaSafe(getCTM(), path))
476 platformContext()->setupPaintForFilling(&paint);
477 platformContext()->canvas()->drawPath(path, paint);
479 if (strokeStyle() != NoStroke) {
481 platformContext()->setupPaintForStroking(&paint, 0, 0);
482 platformContext()->canvas()->drawPath(path, paint);
486 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
488 if (paintingDisabled())
494 // FIXME: IMPLEMENT!!
497 // This method is only used to draw the little circles used in lists.
498 void GraphicsContext::drawEllipse(const IntRect& elipseRect)
500 if (paintingDisabled())
503 SkRect rect = elipseRect;
504 if (!isRectSkiaSafe(getCTM(), rect))
507 platformContext()->prepareForSoftwareDraw();
509 platformContext()->setupPaintForFilling(&paint);
510 platformContext()->canvas()->drawOval(rect, paint);
512 if (strokeStyle() != NoStroke) {
514 platformContext()->setupPaintForStroking(&paint, &rect, 0);
515 platformContext()->canvas()->drawOval(rect, paint);
519 void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color)
524 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
526 if (paintingDisabled())
529 unsigned rectCount = rects.size();
533 platformContext()->prepareForSoftwareDraw();
534 SkRegion focusRingRegion;
535 const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.5);
536 for (unsigned i = 0; i < rectCount; i++) {
537 SkIRect r = rects[i];
538 r.inset(-focusRingOutset, -focusRingOutset);
539 focusRingRegion.op(r, SkRegion::kUnion_Op);
544 paint.setAntiAlias(true);
545 paint.setStyle(SkPaint::kStroke_Style);
547 paint.setColor(color.rgb());
548 paint.setStrokeWidth(focusRingOutset * 2);
549 paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref();
550 focusRingRegion.getBoundaryPath(&path);
551 platformContext()->canvas()->drawPath(path, paint);
554 // This is only used to draw borders.
555 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
557 if (paintingDisabled())
560 StrokeStyle penStyle = strokeStyle();
561 if (penStyle == NoStroke)
565 if (!isPointSkiaSafe(getCTM(), point1) || !isPointSkiaSafe(getCTM(), point2))
568 platformContext()->prepareForSoftwareDraw();
570 FloatPoint p1 = point1;
571 FloatPoint p2 = point2;
572 bool isVerticalLine = (p1.x() == p2.x());
573 int width = roundf(strokeThickness());
575 // We know these are vertical or horizontal lines, so the length will just
576 // be the sum of the displacement component vectors give or take 1 -
577 // probably worth the speed up of no square root, which also won't be exact.
578 FloatSize disp = p2 - p1;
579 int length = SkScalarRound(disp.width() + disp.height());
580 platformContext()->setupPaintForStroking(&paint, 0, length);
582 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
583 // Do a rect fill of our endpoints. This ensures we always have the
584 // appearance of being a border. We then draw the actual dotted/dashed line.
587 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
588 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
590 if (isVerticalLine) {
591 r1.offset(-width / 2, 0);
592 r2.offset(-width / 2, -width);
594 r1.offset(0, -width / 2);
595 r2.offset(-width, -width / 2);
598 fillPaint.setColor(paint.getColor());
599 platformContext()->canvas()->drawRect(r1, fillPaint);
600 platformContext()->canvas()->drawRect(r2, fillPaint);
603 adjustLineToPixelBoundaries(p1, p2, width, penStyle);
604 SkPoint pts[2] = { (SkPoint)p1, (SkPoint)p2 };
606 platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
609 void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, TextCheckingLineStyle style)
611 if (paintingDisabled())
614 platformContext()->prepareForSoftwareDraw();
616 // Create the pattern we'll use to draw the underline.
617 static SkBitmap* misspellBitmap = 0;
618 if (!misspellBitmap) {
619 // We use a 2-pixel-high misspelling indicator because that seems to be
620 // what WebKit is designed for, and how much room there is in a typical
622 const int rowPixels = 32; // Must be multiple of 4 for pattern below.
623 const int colPixels = 2;
624 misspellBitmap = new SkBitmap;
625 misspellBitmap->setConfig(SkBitmap::kARGB_8888_Config,
626 rowPixels, colPixels);
627 misspellBitmap->allocPixels();
629 misspellBitmap->eraseARGB(0, 0, 0, 0);
630 const uint32_t lineColor = 0xFFFF0000; // Opaque red.
631 const uint32_t antiColor = 0x60600000; // Semitransparent red.
633 // Pattern: X o o X o o X
635 uint32_t* row1 = misspellBitmap->getAddr32(0, 0);
636 uint32_t* row2 = misspellBitmap->getAddr32(0, 1);
637 for (int x = 0; x < rowPixels; x++) {
657 // Offset it vertically by 1 so that there's some space under the text.
658 SkScalar originX = WebCoreFloatToSkScalar(pt.x());
659 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
661 // Make a shader for the bitmap with an origin of the box we'll draw. This
662 // shader is refcounted and will have an initial refcount of 1.
663 SkShader* shader = SkShader::CreateBitmapShader(
664 *misspellBitmap, SkShader::kRepeat_TileMode,
665 SkShader::kRepeat_TileMode);
668 matrix.postTranslate(originX, originY);
669 shader->setLocalMatrix(matrix);
671 // Assign the shader to the paint & release our reference. The paint will
672 // now own the shader and the shader will be destroyed when the paint goes
675 paint.setShader(shader);
681 originX + WebCoreFloatToSkScalar(width),
682 originY + SkIntToScalar(misspellBitmap->height()));
683 platformContext()->canvas()->drawRect(rect, paint);
686 void GraphicsContext::drawLineForText(const FloatPoint& pt,
690 if (paintingDisabled())
696 platformContext()->prepareForSoftwareDraw();
698 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
700 r.fLeft = WebCoreFloatToSkScalar(pt.x());
701 r.fTop = WebCoreFloatToSkScalar(pt.y());
702 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
703 r.fBottom = r.fTop + SkIntToScalar(thickness);
706 platformContext()->setupPaintForFilling(&paint);
707 // Text lines are drawn using the stroke color.
708 paint.setColor(platformContext()->effectiveStrokeColor());
709 platformContext()->canvas()->drawRect(r, paint);
712 // Draws a filled rectangle with a stroked border.
713 void GraphicsContext::drawRect(const IntRect& rect)
715 if (paintingDisabled())
718 platformContext()->prepareForSoftwareDraw();
721 if (!isRectSkiaSafe(getCTM(), r)) {
722 // See the fillRect below.
723 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
726 platformContext()->drawRect(r);
729 void GraphicsContext::fillPath(const Path& pathToFill)
731 if (paintingDisabled())
734 // FIXME: add support to GLES2Canvas for more than just solid fills.
735 if (platformContext()->useGPU() && platformContext()->canAccelerate()) {
736 platformContext()->prepareForHardwareDraw();
737 platformContext()->gpuCanvas()->fillPath(pathToFill);
741 SkPath path = *pathToFill.platformPath();
742 if (!isPathSkiaSafe(getCTM(), path))
745 platformContext()->prepareForSoftwareDraw();
747 const GraphicsContextState& state = m_state;
748 path.setFillType(state.fillRule == RULE_EVENODD ?
749 SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
752 platformContext()->setupPaintForFilling(&paint);
754 platformContext()->canvas()->drawPath(path, paint);
757 void GraphicsContext::fillRect(const FloatRect& rect)
759 if (paintingDisabled())
763 if (!isRectSkiaSafe(getCTM(), r)) {
764 // See the other version of fillRect below.
765 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
768 if (platformContext()->useGPU() && platformContext()->canAccelerate()) {
769 platformContext()->prepareForHardwareDraw();
770 platformContext()->gpuCanvas()->fillRect(rect);
774 platformContext()->save();
776 platformContext()->prepareForSoftwareDraw();
779 platformContext()->setupPaintForFilling(&paint);
780 platformContext()->canvas()->drawRect(r, paint);
782 platformContext()->restore();
785 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
787 if (paintingDisabled())
790 if (platformContext()->useGPU() && platformContext()->canAccelerate()) {
791 platformContext()->prepareForHardwareDraw();
792 platformContext()->gpuCanvas()->fillRect(rect, color, colorSpace);
796 platformContext()->prepareForSoftwareDraw();
799 if (!isRectSkiaSafe(getCTM(), r)) {
800 // Special case when the rectangle overflows fixed point. This is a
801 // workaround to fix bug 1212844. When the input rectangle is very
802 // large, it can overflow Skia's internal fixed point rect. This
803 // should be fixable in Skia (since the output bitmap isn't that
804 // large), but until that is fixed, we try to handle it ourselves.
806 // We manually clip the rectangle to the current clip rect. This
807 // will prevent overflow. The rectangle will be transformed to the
808 // canvas' coordinate space before it is converted to fixed point
809 // so we are guaranteed not to overflow after doing this.
810 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
814 platformContext()->setupPaintCommon(&paint);
815 paint.setColor(color.rgb());
816 platformContext()->canvas()->drawRect(r, paint);
819 void GraphicsContext::fillRoundedRect(const IntRect& rect,
820 const IntSize& topLeft,
821 const IntSize& topRight,
822 const IntSize& bottomLeft,
823 const IntSize& bottomRight,
825 ColorSpace colorSpace)
827 if (paintingDisabled())
830 platformContext()->prepareForSoftwareDraw();
833 if (!isRectSkiaSafe(getCTM(), r))
835 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
837 if (topLeft.width() + topRight.width() > rect.width()
838 || bottomLeft.width() + bottomRight.width() > rect.width()
839 || topLeft.height() + bottomLeft.height() > rect.height()
840 || topRight.height() + bottomRight.height() > rect.height()) {
841 // Not all the radii fit, return a rect. This matches the behavior of
842 // Path::createRoundedRectangle. Without this we attempt to draw a round
843 // shadow for a square box.
844 fillRect(rect, color, colorSpace);
849 addCornerArc(&path, r, topRight, 270);
850 addCornerArc(&path, r, bottomRight, 0);
851 addCornerArc(&path, r, bottomLeft, 90);
852 addCornerArc(&path, r, topLeft, 180);
855 platformContext()->setupPaintForFilling(&paint);
856 paint.setColor(color.rgb());
857 platformContext()->canvas()->drawPath(path, paint);
860 AffineTransform GraphicsContext::getCTM() const
862 const SkMatrix& m = platformContext()->canvas()->getTotalMatrix();
863 return AffineTransform(SkScalarToDouble(m.getScaleX()),
864 SkScalarToDouble(m.getSkewY()),
865 SkScalarToDouble(m.getSkewX()),
866 SkScalarToDouble(m.getScaleY()),
867 SkScalarToDouble(m.getTranslateX()),
868 SkScalarToDouble(m.getTranslateY()));
871 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
876 void GraphicsContext::scale(const FloatSize& size)
878 if (paintingDisabled())
881 if (platformContext()->useGPU())
882 platformContext()->gpuCanvas()->scale(size);
884 platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()),
885 WebCoreFloatToSkScalar(size.height()));
888 void GraphicsContext::setAlpha(float alpha)
890 if (paintingDisabled())
893 if (platformContext()->useGPU())
894 platformContext()->gpuCanvas()->setAlpha(alpha);
896 platformContext()->setAlpha(alpha);
899 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
901 if (paintingDisabled())
904 if (platformContext()->useGPU())
905 platformContext()->gpuCanvas()->setCompositeOperation(op);
907 platformContext()->setXfermodeMode(WebCoreCompositeToSkiaComposite(op));
910 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
912 return platformContext()->interpolationQuality();
915 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality q)
917 platformContext()->setInterpolationQuality(q);
920 void GraphicsContext::setLineCap(LineCap cap)
922 if (paintingDisabled())
926 platformContext()->setLineCap(SkPaint::kButt_Cap);
929 platformContext()->setLineCap(SkPaint::kRound_Cap);
932 platformContext()->setLineCap(SkPaint::kSquare_Cap);
940 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
942 if (paintingDisabled())
945 // FIXME: This is lifted directly off SkiaSupport, lines 49-74
946 // so it is not guaranteed to work correctly.
947 size_t dashLength = dashes.size();
949 // If no dash is set, revert to solid stroke
950 // FIXME: do we need to set NoStroke in some cases?
951 platformContext()->setStrokeStyle(SolidStroke);
952 platformContext()->setDashPathEffect(0);
956 size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
957 SkScalar* intervals = new SkScalar[count];
959 for (unsigned int i = 0; i < count; i++)
960 intervals[i] = dashes[i % dashLength];
962 platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset));
967 void GraphicsContext::setLineJoin(LineJoin join)
969 if (paintingDisabled())
973 platformContext()->setLineJoin(SkPaint::kMiter_Join);
976 platformContext()->setLineJoin(SkPaint::kRound_Join);
979 platformContext()->setLineJoin(SkPaint::kBevel_Join);
987 void GraphicsContext::setMiterLimit(float limit)
989 if (paintingDisabled())
991 platformContext()->setMiterLimit(limit);
994 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
996 if (paintingDisabled())
999 if (platformContext()->useGPU())
1000 platformContext()->gpuCanvas()->setFillColor(color, colorSpace);
1002 platformContext()->setFillColor(color.rgb());
1005 void GraphicsContext::setPlatformFillGradient(Gradient* gradient)
1007 if (paintingDisabled())
1010 platformContext()->setFillShader(gradient->platformGradient());
1013 void GraphicsContext::setPlatformFillPattern(Pattern* pattern)
1015 if (paintingDisabled())
1018 platformContext()->setFillShader(pattern->platformPattern(getCTM()));
1021 void GraphicsContext::setPlatformShadow(const FloatSize& size,
1024 ColorSpace colorSpace)
1026 if (paintingDisabled())
1029 if (platformContext()->useGPU()) {
1030 GLES2Canvas* canvas = platformContext()->gpuCanvas();
1031 canvas->setShadowOffset(size);
1032 canvas->setShadowBlur(blurFloat);
1033 canvas->setShadowColor(color, colorSpace);
1034 canvas->setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms);
1037 // Detect when there's no effective shadow and clear the looper.
1038 if (!size.width() && !size.height() && !blurFloat) {
1039 platformContext()->setDrawLooper(0);
1043 double width = size.width();
1044 double height = size.height();
1045 double blur = blurFloat;
1047 uint32_t blurFlags = SkBlurDrawLooper::kHighQuality_BlurFlag |
1048 SkBlurDrawLooper::kOverrideColor_BlurFlag;
1050 if (m_state.shadowsIgnoreTransforms) {
1051 // Currently only the GraphicsContext associated with the
1052 // CanvasRenderingContext for HTMLCanvasElement have shadows ignore
1053 // Transforms. So with this flag set, we know this state is associated
1054 // with a CanvasRenderingContext.
1055 blurFlags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag;
1057 // CG uses natural orientation for Y axis, but the HTML5 canvas spec
1059 // So we now flip the height since it was flipped in
1060 // CanvasRenderingContext in order to work with CG.
1065 if (color.isValid())
1068 c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" apple shadow color.
1070 // TODO(tc): Should we have a max value for the blur? CG clamps at 1000.0
1071 // for perf reasons.
1072 SkDrawLooper* dl = new SkBlurDrawLooper(blur / 2, width, height, c, blurFlags);
1073 platformContext()->setDrawLooper(dl);
1077 void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor, ColorSpace colorSpace)
1079 if (paintingDisabled())
1082 platformContext()->setStrokeColor(strokecolor.rgb());
1085 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle stroke)
1087 if (paintingDisabled())
1090 platformContext()->setStrokeStyle(stroke);
1093 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1095 if (paintingDisabled())
1098 platformContext()->setStrokeThickness(thickness);
1101 void GraphicsContext::setPlatformStrokeGradient(Gradient* gradient)
1103 if (paintingDisabled())
1106 platformContext()->setStrokeShader(gradient->platformGradient());
1109 void GraphicsContext::setPlatformStrokePattern(Pattern* pattern)
1111 if (paintingDisabled())
1114 platformContext()->setStrokeShader(pattern->platformPattern(getCTM()));
1117 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1119 if (paintingDisabled())
1122 platformContext()->setTextDrawingMode(mode);
1125 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1129 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1131 if (paintingDisabled())
1134 platformContext()->setUseAntialiasing(enable);
1137 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
1139 if (paintingDisabled())
1142 platformContext()->prepareForSoftwareDraw();
1146 if (strokeStyle() == NoStroke) {
1147 // Stroke using the fill color.
1148 // TODO(brettw) is this really correct? It seems unreasonable.
1149 platformContext()->setupPaintForFilling(&paint);
1150 paint.setStyle(SkPaint::kStroke_Style);
1151 paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness()));
1153 platformContext()->setupPaintForStroking(&paint, 0, 0);
1155 // We do this before converting to scalar, so we don't overflow SkFixed.
1156 startAngle = fastMod(startAngle, 360);
1157 angleSpan = fastMod(angleSpan, 360);
1160 path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
1161 if (!isPathSkiaSafe(getCTM(), path))
1163 platformContext()->canvas()->drawPath(path, paint);
1166 void GraphicsContext::strokePath(const Path& pathToStroke)
1168 if (paintingDisabled())
1171 SkPath path = *pathToStroke.platformPath();
1172 if (!isPathSkiaSafe(getCTM(), path))
1175 platformContext()->prepareForSoftwareDraw();
1178 platformContext()->setupPaintForStroking(&paint, 0, 0);
1179 platformContext()->canvas()->drawPath(path, paint);
1182 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1184 if (paintingDisabled())
1187 if (!isRectSkiaSafe(getCTM(), rect))
1190 platformContext()->prepareForSoftwareDraw();
1193 platformContext()->setupPaintForStroking(&paint, 0, 0);
1194 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1195 platformContext()->canvas()->drawRect(rect, paint);
1198 void GraphicsContext::rotate(float angleInRadians)
1200 if (paintingDisabled())
1203 if (platformContext()->useGPU())
1204 platformContext()->gpuCanvas()->rotate(angleInRadians);
1206 platformContext()->canvas()->rotate(WebCoreFloatToSkScalar(
1207 angleInRadians * (180.0f / 3.14159265f)));
1210 void GraphicsContext::translate(float w, float h)
1212 if (paintingDisabled())
1215 if (platformContext()->useGPU())
1216 platformContext()->gpuCanvas()->translate(w, h);
1218 platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w),
1219 WebCoreFloatToSkScalar(h));
1222 void GraphicsContext::syncSoftwareCanvas()
1224 platformContext()->syncSoftwareCanvas();
1227 void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* framebuffer, const IntSize& size)
1229 platformContext()->setSharedGraphicsContext3D(context, framebuffer, size);
1232 void GraphicsContext::markDirtyRect(const IntRect& rect)
1234 platformContext()->markDirtyRect(rect);
1237 } // namespace WebCore