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 "GraphicsContextPlatformPrivate.h"
35 #include "GraphicsContextPrivate.h"
37 #include "FloatRect.h"
39 #include "ImageBuffer.h"
41 #include "NativeImageSkia.h"
42 #include "NotImplemented.h"
43 #include "PlatformContextSkia.h"
44 #include "TransformationMatrix.h"
47 #include "SkBlurDrawLooper.h"
48 #include "SkCornerPathEffect.h"
49 #include "skia/ext/platform_canvas.h"
50 #include "SkiaUtils.h"
54 #include <wtf/Assertions.h>
55 #include <wtf/MathExtras.h>
63 // "Seatbelt" functions ------------------------------------------------------
65 // These functions check certain graphics primitives for being "safe".
66 // Skia has historically crashed when sent crazy data. These functions do
67 // additional checking to prevent crashes.
69 // Ideally, all of these would be fixed in the graphics layer and we would not
70 // have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA
71 // flag to check the graphics layer.
72 #define ENSURE_VALUE_SAFETY_FOR_SKIA
74 static bool isCoordinateSkiaSafe(float coord)
76 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
77 // First check for valid floats.
85 // Skia uses 16.16 fixed point and 26.6 fixed point in various places. If
86 // the transformed point exceeds 15 bits, we just declare that it's
87 // unreasonable to catch both of these cases.
88 static const int maxPointMagnitude = 32767;
89 if (coord > maxPointMagnitude || coord < -maxPointMagnitude)
98 static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt)
100 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
101 // Now check for points that will overflow. We check the *transformed*
102 // points since this is what will be rasterized.
104 transform.mapPoints(&xPt, &pt, 1);
105 return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY);
111 static bool isRectSkiaSafe(const SkMatrix& transform, const SkRect& rc)
113 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
114 SkPoint topleft = {rc.fLeft, rc.fTop};
115 SkPoint bottomright = {rc.fRight, rc.fBottom};
116 return isPointSkiaSafe(transform, topleft) && isPointSkiaSafe(transform, bottomright);
122 bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path)
124 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
125 SkPoint current_points[4];
126 SkPath::Iter iter(path, false);
127 for (SkPath::Verb verb = iter.next(current_points);
128 verb != SkPath::kDone_Verb;
129 verb = iter.next(current_points)) {
131 case SkPath::kMove_Verb:
132 // This move will be duplicated in the next verb, so we can ignore.
134 case SkPath::kLine_Verb:
135 // iter.next returns 2 points.
136 if (!isPointSkiaSafe(transform, current_points[0])
137 || !isPointSkiaSafe(transform, current_points[1]))
140 case SkPath::kQuad_Verb:
141 // iter.next returns 3 points.
142 if (!isPointSkiaSafe(transform, current_points[0])
143 || !isPointSkiaSafe(transform, current_points[1])
144 || !isPointSkiaSafe(transform, current_points[2]))
147 case SkPath::kCubic_Verb:
148 // iter.next returns 4 points.
149 if (!isPointSkiaSafe(transform, current_points[0])
150 || !isPointSkiaSafe(transform, current_points[1])
151 || !isPointSkiaSafe(transform, current_points[2])
152 || !isPointSkiaSafe(transform, current_points[3]))
155 case SkPath::kClose_Verb:
156 case SkPath::kDone_Verb:
167 // Local helper functions ------------------------------------------------------
169 void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle)
172 int rx = SkMin32(SkScalarRound(rect.width()), size.width());
173 int ry = SkMin32(SkScalarRound(rect.height()), size.height());
175 ir.set(-rx, -ry, rx, ry);
176 switch (startAngle) {
178 ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom);
181 ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom);
184 ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop);
187 ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop);
195 path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false);
198 inline int fastMod(int value, int max)
200 int sign = SkExtractSign(value);
202 value = SkApplySign(value, sign);
205 return SkApplySign(value, sign);
208 inline float square(float n)
215 // -----------------------------------------------------------------------------
217 // This may be called with a NULL pointer to create a graphics context that has
219 GraphicsContext::GraphicsContext(PlatformGraphicsContext* gc)
220 : m_common(createGraphicsContextPrivate())
221 , m_data(new GraphicsContextPlatformPrivate(gc))
223 setPaintingDisabled(!gc || !platformContext()->canvas());
226 GraphicsContext::~GraphicsContext()
229 this->destroyGraphicsContextPrivate(m_common);
232 PlatformGraphicsContext* GraphicsContext::platformContext() const
234 ASSERT(!paintingDisabled());
235 return m_data->context();
238 // State saving ----------------------------------------------------------------
240 void GraphicsContext::savePlatformState()
242 if (paintingDisabled())
245 // Save our private State.
246 platformContext()->save();
249 void GraphicsContext::restorePlatformState()
251 if (paintingDisabled())
254 // Restore our private State.
255 platformContext()->restore();
258 void GraphicsContext::beginTransparencyLayer(float opacity)
260 if (paintingDisabled())
263 // We need the "alpha" layer flag here because the base layer is opaque
264 // (the surface of the page) but layers on top may have transparent parts.
265 // Without explicitly setting the alpha flag, the layer will inherit the
266 // opaque setting of the base and some things won't work properly.
267 platformContext()->canvas()->saveLayerAlpha(
269 static_cast<unsigned char>(opacity * 255),
270 static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag |
271 SkCanvas::kFullColorLayer_SaveFlag));
274 void GraphicsContext::endTransparencyLayer()
276 if (paintingDisabled())
280 platformContext()->canvas()->getTopPlatformDevice().
281 fixupAlphaBeforeCompositing();
283 platformContext()->canvas()->restore();
286 // Graphics primitives ---------------------------------------------------------
288 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
290 if (paintingDisabled())
294 if (!isRectSkiaSafe(getCTM(), r))
298 path.addOval(r, SkPath::kCW_Direction);
299 // only perform the inset if we won't invert r
300 if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
301 r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness));
302 path.addOval(r, SkPath::kCCW_Direction);
304 platformContext()->canvas()->clipPath(path);
307 void GraphicsContext::addPath(const Path& path)
309 if (paintingDisabled())
311 platformContext()->addPath(*path.platformPath());
314 void GraphicsContext::beginPath()
316 if (paintingDisabled())
318 platformContext()->beginPath();
321 void GraphicsContext::clearPlatformShadow()
323 if (paintingDisabled())
325 platformContext()->setDrawLooper(0);
328 void GraphicsContext::clearRect(const FloatRect& rect)
330 if (paintingDisabled())
334 if (!isRectSkiaSafe(getCTM(), r))
335 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
338 platformContext()->setupPaintForFilling(&paint);
339 paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode);
340 platformContext()->canvas()->drawRect(r, paint);
343 void GraphicsContext::clip(const FloatRect& rect)
345 if (paintingDisabled())
349 if (!isRectSkiaSafe(getCTM(), r))
352 platformContext()->canvas()->clipRect(r);
355 void GraphicsContext::clip(const Path& path)
357 if (paintingDisabled())
360 const SkPath& p = *path.platformPath();
361 if (!isPathSkiaSafe(getCTM(), p))
364 platformContext()->canvas()->clipPath(p);
367 void GraphicsContext::clipOut(const IntRect& rect)
369 if (paintingDisabled())
373 if (!isRectSkiaSafe(getCTM(), r))
376 platformContext()->canvas()->clipRect(r, SkRegion::kDifference_Op);
379 void GraphicsContext::clipOut(const Path& p)
381 if (paintingDisabled())
384 const SkPath& path = *p.platformPath();
385 if (!isPathSkiaSafe(getCTM(), path))
388 platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op);
391 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
393 if (paintingDisabled())
397 if (!isRectSkiaSafe(getCTM(), oval))
401 path.addOval(oval, SkPath::kCCW_Direction);
402 platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op);
405 void GraphicsContext::clipPath(WindRule clipRule)
407 if (paintingDisabled())
410 SkPath path = platformContext()->currentPathInLocalCoordinates();
411 path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
412 platformContext()->canvas()->clipPath(path);
415 void GraphicsContext::clipToImageBuffer(const FloatRect& rect,
416 const ImageBuffer* imageBuffer)
418 if (paintingDisabled())
421 #if defined(__linux__) || PLATFORM(WIN_OS)
422 platformContext()->beginLayerClippedToImage(rect, imageBuffer);
426 void GraphicsContext::concatCTM(const TransformationMatrix& xform)
428 if (paintingDisabled())
430 platformContext()->canvas()->concat(xform);
433 void GraphicsContext::drawConvexPolygon(size_t numPoints,
434 const FloatPoint* points,
435 bool shouldAntialias)
437 if (paintingDisabled())
445 path.incReserve(numPoints);
446 path.moveTo(WebCoreFloatToSkScalar(points[0].x()),
447 WebCoreFloatToSkScalar(points[0].y()));
448 for (size_t i = 1; i < numPoints; i++) {
449 path.lineTo(WebCoreFloatToSkScalar(points[i].x()),
450 WebCoreFloatToSkScalar(points[i].y()));
453 if (!isPathSkiaSafe(getCTM(), path))
457 if (fillColor().alpha() > 0) {
458 platformContext()->setupPaintForFilling(&paint);
459 platformContext()->canvas()->drawPath(path, paint);
462 if (strokeStyle() != NoStroke) {
464 platformContext()->setupPaintForStroking(&paint, 0, 0);
465 platformContext()->canvas()->drawPath(path, paint);
469 // This method is only used to draw the little circles used in lists.
470 void GraphicsContext::drawEllipse(const IntRect& elipseRect)
472 if (paintingDisabled())
475 SkRect rect = elipseRect;
476 if (!isRectSkiaSafe(getCTM(), rect))
480 if (fillColor().alpha() > 0) {
481 platformContext()->setupPaintForFilling(&paint);
482 platformContext()->canvas()->drawOval(rect, paint);
485 if (strokeStyle() != NoStroke) {
487 platformContext()->setupPaintForStroking(&paint, &rect, 0);
488 platformContext()->canvas()->drawOval(rect, paint);
492 void GraphicsContext::drawFocusRing(const Color& color)
494 if (paintingDisabled())
497 const Vector<IntRect>& rects = focusRingRects();
498 unsigned rectCount = rects.size();
502 SkRegion focusRingRegion;
503 const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.5);
504 for (unsigned i = 0; i < rectCount; i++) {
505 SkIRect r = rects[i];
506 r.inset(-focusRingOutset, -focusRingOutset);
507 focusRingRegion.op(r, SkRegion::kUnion_Op);
512 paint.setAntiAlias(true);
513 paint.setStyle(SkPaint::kStroke_Style);
515 paint.setColor(focusRingColor().rgb());
516 paint.setStrokeWidth(focusRingOutset * 2);
517 paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref();
518 focusRingRegion.getBoundaryPath(&path);
519 platformContext()->canvas()->drawPath(path, paint);
522 // This is only used to draw borders.
523 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
525 if (paintingDisabled())
528 StrokeStyle penStyle = strokeStyle();
529 if (penStyle == NoStroke)
533 SkPoint pts[2] = { (SkPoint)point1, (SkPoint)point2 };
534 if (!isPointSkiaSafe(getCTM(), pts[0]) || !isPointSkiaSafe(getCTM(), pts[1]))
537 // We know these are vertical or horizontal lines, so the length will just
538 // be the sum of the displacement component vectors give or take 1 -
539 // probably worth the speed up of no square root, which also won't be exact.
540 SkPoint disp = pts[1] - pts[0];
541 int length = SkScalarRound(disp.fX + disp.fY);
543 platformContext()->setupPaintForStroking(&paint, 0, length));
545 // "Borrowed" this comment and idea from GraphicsContextCG.cpp
546 // For odd widths, we add in 0.5 to the appropriate x/y so that the float
547 // arithmetic works out. For example, with a border width of 3, KHTML will
548 // pass us (y1+y2)/2, e.g., (50+53)/2 = 103/2 = 51 when we want 51.5. It is
549 // always true that an even width gave us a perfect position, but an odd
550 // width gave us a position that is off by exactly 0.5.
551 bool isVerticalLine = pts[0].fX == pts[1].fX;
553 if (width & 1) { // Odd.
554 if (isVerticalLine) {
555 pts[0].fX = pts[0].fX + SK_ScalarHalf;
556 pts[1].fX = pts[0].fX;
557 } else { // Horizontal line
558 pts[0].fY = pts[0].fY + SK_ScalarHalf;
559 pts[1].fY = pts[0].fY;
562 platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
565 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt,
569 if (paintingDisabled())
572 // Create the pattern we'll use to draw the underline.
573 static SkBitmap* misspellBitmap = 0;
574 if (!misspellBitmap) {
575 // We use a 2-pixel-high misspelling indicator because that seems to be
576 // what WebKit is designed for, and how much room there is in a typical
578 const int rowPixels = 32; // Must be multiple of 4 for pattern below.
579 const int colPixels = 2;
580 misspellBitmap = new SkBitmap;
581 misspellBitmap->setConfig(SkBitmap::kARGB_8888_Config,
582 rowPixels, colPixels);
583 misspellBitmap->allocPixels();
585 misspellBitmap->eraseARGB(0, 0, 0, 0);
586 const uint32_t lineColor = 0xFFFF0000; // Opaque red.
587 const uint32_t antiColor = 0x60600000; // Semitransparent red.
589 // Pattern: X o o X o o X
591 uint32_t* row1 = misspellBitmap->getAddr32(0, 0);
592 uint32_t* row2 = misspellBitmap->getAddr32(0, 1);
593 for (int x = 0; x < rowPixels; x++) {
613 // Offset it vertically by 1 so that there's some space under the text.
614 SkScalar originX = SkIntToScalar(pt.x());
615 SkScalar originY = SkIntToScalar(pt.y()) + 1;
617 // Make a shader for the bitmap with an origin of the box we'll draw. This
618 // shader is refcounted and will have an initial refcount of 1.
619 SkShader* shader = SkShader::CreateBitmapShader(
620 *misspellBitmap, SkShader::kRepeat_TileMode,
621 SkShader::kRepeat_TileMode);
624 matrix.postTranslate(originX, originY);
625 shader->setLocalMatrix(matrix);
627 // Assign the shader to the paint & release our reference. The paint will
628 // now own the shader and the shader will be destroyed when the paint goes
631 paint.setShader(shader);
637 originX + SkIntToScalar(width),
638 originY + SkIntToScalar(misspellBitmap->height()));
639 platformContext()->canvas()->drawRect(rect, paint);
642 void GraphicsContext::drawLineForText(const IntPoint& pt,
646 if (paintingDisabled())
652 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
654 r.fLeft = SkIntToScalar(pt.x());
655 r.fTop = SkIntToScalar(pt.y());
656 r.fRight = r.fLeft + SkIntToScalar(width);
657 r.fBottom = r.fTop + SkIntToScalar(thickness);
660 platformContext()->setupPaintForFilling(&paint);
661 // Text lines are drawn using the stroke color.
662 paint.setColor(platformContext()->effectiveStrokeColor());
663 platformContext()->canvas()->drawRect(r, paint);
666 // Draws a filled rectangle with a stroked border.
667 void GraphicsContext::drawRect(const IntRect& rect)
669 if (paintingDisabled())
673 if (!isRectSkiaSafe(getCTM(), r))
674 // See the fillRect below.
675 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
677 platformContext()->drawRect(r);
680 void GraphicsContext::fillPath()
682 if (paintingDisabled())
685 SkPath path = platformContext()->currentPathInLocalCoordinates();
686 if (!isPathSkiaSafe(getCTM(), path))
689 const GraphicsContextState& state = m_common->state;
690 ColorSpace colorSpace = state.fillColorSpace;
692 if (colorSpace == SolidColorSpace && !fillColor().alpha())
695 path.setFillType(state.fillRule == RULE_EVENODD ?
696 SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
699 platformContext()->setupPaintForFilling(&paint);
701 if (colorSpace == PatternColorSpace) {
702 SkShader* pat = state.fillPattern->createPlatformPattern(getCTM());
703 paint.setShader(pat);
705 } else if (colorSpace == GradientColorSpace)
706 paint.setShader(state.fillGradient->platformGradient());
708 platformContext()->canvas()->drawPath(path, paint);
711 void GraphicsContext::fillRect(const FloatRect& rect)
713 if (paintingDisabled())
717 if (!isRectSkiaSafe(getCTM(), r))
718 // See the other version of fillRect below.
719 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
721 const GraphicsContextState& state = m_common->state;
722 ColorSpace colorSpace = state.fillColorSpace;
724 if (colorSpace == SolidColorSpace && !fillColor().alpha())
728 platformContext()->setupPaintForFilling(&paint);
730 if (colorSpace == PatternColorSpace) {
731 SkShader* pat = state.fillPattern->createPlatformPattern(getCTM());
732 paint.setShader(pat);
734 } else if (colorSpace == GradientColorSpace)
735 paint.setShader(state.fillGradient->platformGradient());
737 platformContext()->canvas()->drawRect(r, paint);
740 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
742 if (paintingDisabled())
749 if (!isRectSkiaSafe(getCTM(), r)) {
750 // Special case when the rectangle overflows fixed point. This is a
751 // workaround to fix bug 1212844. When the input rectangle is very
752 // large, it can overflow Skia's internal fixed point rect. This
753 // should be fixable in Skia (since the output bitmap isn't that
754 // large), but until that is fixed, we try to handle it ourselves.
756 // We manually clip the rectangle to the current clip rect. This
757 // will prevent overflow. The rectangle will be transformed to the
758 // canvas' coordinate space before it is converted to fixed point
759 // so we are guaranteed not to overflow after doing this.
760 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
764 platformContext()->setupPaintCommon(&paint);
765 paint.setColor(color.rgb());
766 platformContext()->canvas()->drawRect(r, paint);
769 void GraphicsContext::fillRoundedRect(const IntRect& rect,
770 const IntSize& topLeft,
771 const IntSize& topRight,
772 const IntSize& bottomLeft,
773 const IntSize& bottomRight,
776 if (paintingDisabled())
780 if (!isRectSkiaSafe(getCTM(), r))
782 ClipRectToCanvas(*platformContext()->canvas(), r, &r);
784 if (topLeft.width() + topRight.width() > rect.width()
785 || bottomLeft.width() + bottomRight.width() > rect.width()
786 || topLeft.height() + bottomLeft.height() > rect.height()
787 || topRight.height() + bottomRight.height() > rect.height()) {
788 // Not all the radii fit, return a rect. This matches the behavior of
789 // Path::createRoundedRectangle. Without this we attempt to draw a round
790 // shadow for a square box.
791 fillRect(rect, color);
796 addCornerArc(&path, r, topRight, 270);
797 addCornerArc(&path, r, bottomRight, 0);
798 addCornerArc(&path, r, bottomLeft, 90);
799 addCornerArc(&path, r, topLeft, 180);
802 platformContext()->setupPaintForFilling(&paint);
803 platformContext()->canvas()->drawPath(path, paint);
806 TransformationMatrix GraphicsContext::getCTM() const
808 const SkMatrix& m = platformContext()->canvas()->getTotalMatrix();
809 return TransformationMatrix(SkScalarToDouble(m.getScaleX()), // a
810 SkScalarToDouble(m.getSkewY()), // b
811 SkScalarToDouble(m.getSkewX()), // c
812 SkScalarToDouble(m.getScaleY()), // d
813 SkScalarToDouble(m.getTranslateX()), // e
814 SkScalarToDouble(m.getTranslateY())); // f
817 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
819 // This logic is copied from GraphicsContextCG, eseidel 5/05/08
821 // It is not enough just to round to pixels in device space. The rotation
822 // part of the affine transform matrix to device space can mess with this
823 // conversion if we have a rotating image like the hands of the world clock
824 // widget. We just need the scale, so we get the affine transform matrix and
825 // extract the scale.
827 const SkMatrix& deviceMatrix = platformContext()->canvas()->getTotalMatrix();
828 if (deviceMatrix.isIdentity())
831 float deviceScaleX = sqrtf(square(deviceMatrix.getScaleX())
832 + square(deviceMatrix.getSkewY()));
833 float deviceScaleY = sqrtf(square(deviceMatrix.getSkewX())
834 + square(deviceMatrix.getScaleY()));
836 FloatPoint deviceOrigin(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
837 FloatPoint deviceLowerRight((rect.x() + rect.width()) * deviceScaleX,
838 (rect.y() + rect.height()) * deviceScaleY);
840 deviceOrigin.setX(roundf(deviceOrigin.x()));
841 deviceOrigin.setY(roundf(deviceOrigin.y()));
842 deviceLowerRight.setX(roundf(deviceLowerRight.x()));
843 deviceLowerRight.setY(roundf(deviceLowerRight.y()));
845 // Don't let the height or width round to 0 unless either was originally 0
846 if (deviceOrigin.y() == deviceLowerRight.y() && rect.height() != 0)
847 deviceLowerRight.move(0, 1);
848 if (deviceOrigin.x() == deviceLowerRight.x() && rect.width() != 0)
849 deviceLowerRight.move(1, 0);
851 FloatPoint roundedOrigin(deviceOrigin.x() / deviceScaleX,
852 deviceOrigin.y() / deviceScaleY);
853 FloatPoint roundedLowerRight(deviceLowerRight.x() / deviceScaleX,
854 deviceLowerRight.y() / deviceScaleY);
855 return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
858 void GraphicsContext::scale(const FloatSize& size)
860 if (paintingDisabled())
862 platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()),
863 WebCoreFloatToSkScalar(size.height()));
866 void GraphicsContext::setAlpha(float alpha)
868 if (paintingDisabled())
870 platformContext()->setAlpha(alpha);
873 void GraphicsContext::setCompositeOperation(CompositeOperator op)
875 if (paintingDisabled())
877 platformContext()->setPorterDuffMode(WebCoreCompositeToSkiaComposite(op));
880 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
885 void GraphicsContext::setLineCap(LineCap cap)
887 if (paintingDisabled())
891 platformContext()->setLineCap(SkPaint::kButt_Cap);
894 platformContext()->setLineCap(SkPaint::kRound_Cap);
897 platformContext()->setLineCap(SkPaint::kSquare_Cap);
905 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
907 if (paintingDisabled())
910 // FIXME: This is lifted directly off SkiaSupport, lines 49-74
911 // so it is not guaranteed to work correctly.
912 size_t dashLength = dashes.size();
916 size_t count = (dashLength % 2) == 0 ? dashLength : dashLength * 2;
917 SkScalar* intervals = new SkScalar[count];
919 for (unsigned int i = 0; i < count; i++)
920 intervals[i] = dashes[i % dashLength];
922 platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset));
927 void GraphicsContext::setLineJoin(LineJoin join)
929 if (paintingDisabled())
933 platformContext()->setLineJoin(SkPaint::kMiter_Join);
936 platformContext()->setLineJoin(SkPaint::kRound_Join);
939 platformContext()->setLineJoin(SkPaint::kBevel_Join);
947 void GraphicsContext::setMiterLimit(float limit)
949 if (paintingDisabled())
951 platformContext()->setMiterLimit(limit);
954 void GraphicsContext::setPlatformFillColor(const Color& color)
956 if (paintingDisabled())
958 platformContext()->setFillColor(color.rgb());
961 void GraphicsContext::setPlatformShadow(const IntSize& size,
965 if (paintingDisabled())
968 double width = size.width();
969 double height = size.height();
970 double blur = blurInt;
972 // TODO(tc): This still does not address the issue that shadows
973 // within canvas elements should ignore transforms.
974 if (m_common->state.shadowsIgnoreTransforms) {
975 // Currently only the GraphicsContext associated with the
976 // CanvasRenderingContext for HTMLCanvasElement have shadows ignore
977 // Transforms. So with this flag set, we know this state is associated
978 // with a CanvasRenderingContext.
979 // CG uses natural orientation for Y axis, but the HTML5 canvas spec
981 // So we now flip the height since it was flipped in
982 // CanvasRenderingContext in order to work with CG.
990 c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" apple shadow color.
992 // TODO(tc): Should we have a max value for the blur? CG clamps at 1000.0
994 SkDrawLooper* dl = new SkBlurDrawLooper(blur / 2, width, height, c);
995 platformContext()->setDrawLooper(dl);
999 void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor)
1001 if (paintingDisabled())
1004 platformContext()->setStrokeColor(strokecolor.rgb());
1007 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& stroke)
1009 if (paintingDisabled())
1012 platformContext()->setStrokeStyle(stroke);
1015 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1017 if (paintingDisabled())
1020 platformContext()->setStrokeThickness(thickness);
1023 void GraphicsContext::setPlatformTextDrawingMode(int mode)
1025 if (paintingDisabled())
1028 platformContext()->setTextDrawingMode(mode);
1031 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1035 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1037 if (paintingDisabled())
1040 platformContext()->setUseAntialiasing(enable);
1043 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
1045 if (paintingDisabled())
1050 if (strokeStyle() == NoStroke) {
1051 // Stroke using the fill color.
1052 // TODO(brettw) is this really correct? It seems unreasonable.
1053 platformContext()->setupPaintForFilling(&paint);
1054 paint.setStyle(SkPaint::kStroke_Style);
1055 paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness()));
1057 platformContext()->setupPaintForStroking(&paint, 0, 0);
1059 // We do this before converting to scalar, so we don't overflow SkFixed.
1060 startAngle = fastMod(startAngle, 360);
1061 angleSpan = fastMod(angleSpan, 360);
1064 path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
1065 if (!isPathSkiaSafe(getCTM(), path))
1067 platformContext()->canvas()->drawPath(path, paint);
1070 void GraphicsContext::strokePath()
1072 if (paintingDisabled())
1075 SkPath path = platformContext()->currentPathInLocalCoordinates();
1076 if (!isPathSkiaSafe(getCTM(), path))
1079 const GraphicsContextState& state = m_common->state;
1080 ColorSpace colorSpace = state.strokeColorSpace;
1082 if (colorSpace == SolidColorSpace && !strokeColor().alpha())
1086 platformContext()->setupPaintForStroking(&paint, 0, 0);
1088 if (colorSpace == PatternColorSpace) {
1089 SkShader* pat = state.strokePattern->createPlatformPattern(getCTM());
1090 paint.setShader(pat);
1092 } else if (colorSpace == GradientColorSpace)
1093 paint.setShader(state.strokeGradient->platformGradient());
1095 platformContext()->canvas()->drawPath(path, paint);
1098 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1100 if (paintingDisabled())
1103 if (!isRectSkiaSafe(getCTM(), rect))
1106 const GraphicsContextState& state = m_common->state;
1107 ColorSpace colorSpace = state.strokeColorSpace;
1109 if (colorSpace == SolidColorSpace && !strokeColor().alpha())
1113 platformContext()->setupPaintForStroking(&paint, 0, 0);
1114 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1116 if (colorSpace == PatternColorSpace) {
1117 SkShader* pat = state.strokePattern->createPlatformPattern(getCTM());
1118 paint.setShader(pat);
1120 } else if (colorSpace == GradientColorSpace)
1121 paint.setShader(state.strokeGradient->platformGradient());
1123 platformContext()->canvas()->drawRect(rect, paint);
1126 void GraphicsContext::rotate(float angleInRadians)
1128 if (paintingDisabled())
1131 platformContext()->canvas()->rotate(WebCoreFloatToSkScalar(
1132 angleInRadians * (180.0f / 3.14159265f)));
1135 void GraphicsContext::translate(float w, float h)
1137 if (paintingDisabled())
1140 platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w),
1141 WebCoreFloatToSkScalar(h));
1144 } // namespace WebCore