void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) return; cairo_t* cr = platformContext()->cr(); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); // The rectangular clip function is traditionally not expected to // antialias. If we don't force antialiased clipping here, // edge fringe artifacts may occur at the layer edges // when a transformation is applied to the GraphicsContext // while drawing the transformed layer. cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr); cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); cairo_set_antialias(cr, savedAntialiasRule); m_data->clip(rect); }
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) { // This logic is copied from GraphicsContextCG, eseidel 5/05/08 // It is not enough just to round to pixels in device space. The rotation // part of the affine transform matrix to device space can mess with this // conversion if we have a rotating image like the hands of the world clock // widget. We just need the scale, so we get the affine transform matrix and // extract the scale. const SkMatrix& deviceMatrix = platformContext()->canvas()->getTotalMatrix(); if (deviceMatrix.isIdentity()) return rect; float deviceScaleX = sqrtf(square(deviceMatrix.getScaleX()) + square(deviceMatrix.getSkewY())); float deviceScaleY = sqrtf(square(deviceMatrix.getSkewX()) + square(deviceMatrix.getScaleY())); FloatPoint deviceOrigin(rect.x() * deviceScaleX, rect.y() * deviceScaleY); FloatPoint deviceLowerRight((rect.x() + rect.width()) * deviceScaleX, (rect.y() + rect.height()) * deviceScaleY); deviceOrigin.setX(roundf(deviceOrigin.x())); deviceOrigin.setY(roundf(deviceOrigin.y())); deviceLowerRight.setX(roundf(deviceLowerRight.x())); deviceLowerRight.setY(roundf(deviceLowerRight.y())); // Don't let the height or width round to 0 unless either was originally 0 if (deviceOrigin.y() == deviceLowerRight.y() && rect.height()) deviceLowerRight.move(0, 1); if (deviceOrigin.x() == deviceLowerRight.x() && rect.width()) deviceLowerRight.move(1, 0); FloatPoint roundedOrigin(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY); FloatPoint roundedLowerRight(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY); return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin); }
void GraphicsContext::restorePlatformState() { if (paintingDisabled()) return; platformContext()->restore(); platformContext()->setFillColor(m_state.fillColor.rgb()); if (hasShadow()) setPlatformShadow(m_state.shadowOffset, m_state.shadowBlur, m_state.shadowColor, m_state.shadowColorSpace); else clearPlatformShadow(); platformContext()->setStrokeColor(m_state.strokeColor.rgb()); platformContext()->setStrokeStyle(static_cast<BlackBerry::Platform::Graphics::StrokeStyle>(m_state.strokeStyle)); platformContext()->setStrokeThickness(m_state.strokeThickness); platformContext()->setTextDrawingMode(m_state.textDrawingMode); }
void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) { if (paintingDisabled()) return; if (numPoints <= 1) return; cairo_t* cr = platformContext()->cr(); cairo_new_path(cr); cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr); cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); addConvexPolygonToContext(cr, numPoints, points); cairo_clip(cr); cairo_set_antialias(cr, savedAntialiasRule); cairo_set_fill_rule(cr, savedFillRule); }
void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) { if (paintingDisabled()) return; CFURLRef urlRef = link.createCFURL(); if (urlRef) { CGContextRef context = platformContext(); // Get the bounding box to handle clipping. CGRect box = CGContextGetClipBoundingBox(context); IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height); IntRect rect = destRect; rect.intersect(intBox); CGPDFContextSetURLForRect(context, urlRef, CGRectApplyAffineTransform(rect, CGContextGetCTM(context))); CFRelease(urlRef); } }
void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth) { if (paintingDisabled()) return; CGContextRef context = platformContext(); if (m_state.strokeGradient) { CGContextSaveGState(context); setStrokeThickness(lineWidth); CGContextAddRect(context, r); CGContextReplacePathWithStrokedPath(context); CGContextClip(context); m_state.strokeGradient->paint(this); CGContextRestoreGState(context); return; } if (m_state.strokePattern) applyStrokePattern(); CGContextStrokeRectWithWidth(context, r, lineWidth); }
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) { if (paintingDisabled()) return; cairo_t* cr = platformContext()->cr(); clip(rect); Path p; FloatRect r(rect); // Add outer ellipse p.addEllipse(r); // Add inner ellipse r.inflate(-thickness); p.addEllipse(r); appendWebCorePathToCairoContext(cr, p); cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); }
void GraphicsContext::clearRect(const FloatRect& rect) { if (paintingDisabled()) return; if (platformContext()->useGPU() && !platformContext()->canvasClipApplied()) { platformContext()->prepareForHardwareDraw(); platformContext()->gpuCanvas()->clearRect(rect); return; } // Force a readback here (if we're using the GPU), since clearRect() is // incompatible with mixed-mode rendering. platformContext()->syncSoftwareCanvas(); SkRect r = rect; if (!isRectSkiaSafe(getCTM(), r)) ClipRectToCanvas(*platformContext()->canvas(), r, &r); SkPaint paint; platformContext()->setupPaintForFilling(&paint); paint.setXfermodeMode(SkXfermode::kClear_Mode); platformContext()->canvas()->drawRect(r, paint); }
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) { // It is not enough just to round to pixels in device space. The rotation part of the // affine transform matrix to device space can mess with this conversion if we have a // rotating image like the hands of the world clock widget. We just need the scale, so // we get the affine transform matrix and extract the scale. if (m_data->m_userToDeviceTransformKnownToBeIdentity) return rect; CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext()); if (CGAffineTransformIsIdentity(deviceMatrix)) { m_data->m_userToDeviceTransformKnownToBeIdentity = true; return rect; } float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b); float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d); CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY); CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX, (rect.y() + rect.height()) * deviceScaleY); deviceOrigin.x = roundf(deviceOrigin.x); deviceOrigin.y = roundf(deviceOrigin.y); deviceLowerRight.x = roundf(deviceLowerRight.x); deviceLowerRight.y = roundf(deviceLowerRight.y); // Don't let the height or width round to 0 unless either was originally 0 if (deviceOrigin.y == deviceLowerRight.y && rect.height()) deviceLowerRight.y += 1; if (deviceOrigin.x == deviceLowerRight.x && rect.width()) deviceLowerRight.x += 1; FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY); FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY); return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin); }
// Draws a filled rectangle with a stroked border. void GraphicsContext::drawRect(const IntRect& rect) { if (paintingDisabled()) return; ASSERT(!rect.isEmpty()); cairo_t* cr = platformContext()->cr(); cairo_save(cr); fillRectWithColor(cr, rect, fillColor()); if (strokeStyle() != NoStroke) { setSourceRGBAFromColor(cr, strokeColor()); FloatRect r(rect); r.inflate(-.5f); cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height()); cairo_set_line_width(cr, 1.0); cairo_stroke(cr); } cairo_restore(cr); }
// This method is only used to draw the little circles used in lists. void GraphicsContext::drawEllipse(const IntRect& elipseRect) { if (paintingDisabled()) return; SkRect rect = elipseRect; SkPaint paint; platformContext()->setupPaintForFilling(&paint); platformContext()->canvas()->drawOval(rect, paint); platformContext()->didDrawBounded(rect, paint); if (strokeStyle() != NoStroke) { paint.reset(); platformContext()->setupPaintForStroking(&paint, &rect, 0); platformContext()->canvas()->drawOval(rect, paint); platformContext()->didDrawBounded(rect, paint); } }
void GraphicsContext::clearRect(const FloatRect& rect) { if (paintingDisabled()) return; if (platformContext()->useGPU()) { platformContext()->prepareForHardwareDraw(); platformContext()->gpuCanvas()->clearRect(rect); return; } platformContext()->prepareForSoftwareDraw(); SkRect r = rect; if (!isRectSkiaSafe(getCTM(), r)) ClipRectToCanvas(*platformContext()->canvas(), r, &r); SkPaint paint; platformContext()->setupPaintForFilling(&paint); paint.setXfermodeMode(SkXfermode::kClear_Mode); platformContext()->canvas()->drawRect(r, paint); }
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode) { FloatRect result; double x = frect.x(); double y = frect.y(); cairo_t* cr = platformContext()->cr(); cairo_user_to_device(cr, &x, &y); x = round(x); y = round(y); cairo_device_to_user(cr, &x, &y); result.setX(narrowPrecisionToFloat(x)); result.setY(narrowPrecisionToFloat(y)); // We must ensure width and height are at least 1 (or -1) when // we're given float values in the range between 0 and 1 (or -1 and 0). double width = frect.width(); double height = frect.height(); cairo_user_to_device_distance(cr, &width, &height); if (width > -1 && width < 0) width = -1; else if (width > 0 && width < 1) width = 1; else width = round(width); if (height > -1 && width < 0) height = -1; else if (height > 0 && height < 1) height = 1; else height = round(height); cairo_device_to_user_distance(cr, &width, &height); result.setWidth(narrowPrecisionToFloat(width)); result.setHeight(narrowPrecisionToFloat(height)); return result; }
void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style) { if (paintingDisabled()) return; cairo_t* cr = platformContext()->cr(); cairo_save(cr); switch (style) { case DocumentMarkerSpellingLineStyle: cairo_set_source_rgb(cr, 1, 0, 0); break; case DocumentMarkerGrammarLineStyle: cairo_set_source_rgb(cr, 0, 1, 0); break; default: cairo_restore(cr); return; } drawErrorUnderline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness); cairo_restore(cr); }
void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode) { if (paintingDisabled()) return; // Wow, wish CG had used bits here. CGContextRef context = platformContext(); switch (mode) { case TextModeInvisible: CGContextSetTextDrawingMode(context, kCGTextInvisible); break; case TextModeFill: CGContextSetTextDrawingMode(context, kCGTextFill); break; case TextModeStroke: CGContextSetTextDrawingMode(context, kCGTextStroke); break; case TextModeFill | TextModeStroke: CGContextSetTextDrawingMode(context, kCGTextFillStroke); break; case TextModeClip: CGContextSetTextDrawingMode(context, kCGTextClip); break; case TextModeFill | TextModeClip: CGContextSetTextDrawingMode(context, kCGTextFillClip); break; case TextModeStroke | TextModeClip: CGContextSetTextDrawingMode(context, kCGTextStrokeClip); break; case TextModeFill | TextModeStroke | TextModeClip: CGContextSetTextDrawingMode(context, kCGTextFillStrokeClip); break; default: break; } }
void GraphicsContext::setPlatformTextDrawingMode(int mode) { if (paintingDisabled()) return; // Wow, wish CG had used bits here. CGContextRef context = platformContext(); switch (mode) { case cTextInvisible: // Invisible CGContextSetTextDrawingMode(context, kCGTextInvisible); break; case cTextFill: // Fill CGContextSetTextDrawingMode(context, kCGTextFill); break; case cTextStroke: // Stroke CGContextSetTextDrawingMode(context, kCGTextStroke); break; case 3: // Fill | Stroke CGContextSetTextDrawingMode(context, kCGTextFillStroke); break; case cTextClip: // Clip CGContextSetTextDrawingMode(context, kCGTextClip); break; case 5: // Fill | Clip CGContextSetTextDrawingMode(context, kCGTextFillClip); break; case 6: // Stroke | Clip CGContextSetTextDrawingMode(context, kCGTextStrokeClip); break; case 7: // Fill | Stroke | Clip CGContextSetTextDrawingMode(context, kCGTextFillStrokeClip); break; default: break; } }
void GraphicsContext::strokePath(const Path& path) { if (paintingDisabled()) return; CGContextRef context = platformContext(); CGContextBeginPath(context); CGContextAddPath(context, path.platformPath()); if (m_state.strokeGradient) { CGContextSaveGState(context); CGContextReplacePathWithStrokedPath(context); CGContextClip(context); CGContextConcatCTM(context, m_state.strokeGradient->gradientSpaceTransform()); m_state.strokeGradient->paint(this); CGContextRestoreGState(context); return; } if (m_state.strokePattern) applyStrokePattern(); CGContextStrokePath(context); }
// This is only used to draw borders. void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) { if (paintingDisabled()) return; if (strokeStyle() == NoStroke) return; float width = strokeThickness(); FloatPoint p1 = point1; FloatPoint p2 = point2; bool isVerticalLine = (p1.x() == p2.x()); // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic // works out. For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g., // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { if (isVerticalLine) { p1.move(0, width); p2.move(0, -width); } else { p1.move(width, 0); p2.move(-width, 0); } } if (((int)width) % 2) { if (isVerticalLine) { // We're a vertical line. Adjust our x. p1.move(0.5f, 0.0f); p2.move(0.5f, 0.0f); } else { // We're a horizontal line. Adjust our y. p1.move(0.0f, 0.5f); p2.move(0.0f, 0.5f); } } int patWidth = 0; switch (strokeStyle()) { case NoStroke: case SolidStroke: break; case DottedStroke: patWidth = (int)width; break; case DashedStroke: patWidth = 3 * (int)width; break; } CGContextRef context = platformContext(); if (shouldAntialias()) CGContextSetShouldAntialias(context, false); if (patWidth) { CGContextSaveGState(context); // Do a rect fill of our endpoints. This ensures we always have the // appearance of being a border. We then draw the actual dotted/dashed line. setCGFillColor(context, strokeColor(), strokeColorSpace()); // The save/restore make it safe to mutate the fill color here without setting it back to the old color. if (isVerticalLine) { CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width)); CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width)); } else { CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width)); CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width)); } // Example: 80 pixels with a width of 30 pixels. // Remainder is 20. The maximum pixels of line we could paint // will be 50 pixels. int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width; int remainder = distance % patWidth; int coverage = distance - remainder; int numSegments = coverage / patWidth; float patternOffset = 0.0f; // Special case 1px dotted borders for speed. if (patWidth == 1) patternOffset = 1.0f; else { bool evenNumberOfSegments = !(numSegments % 2); if (remainder) evenNumberOfSegments = !evenNumberOfSegments; if (evenNumberOfSegments) { if (remainder) { patternOffset += patWidth - remainder; patternOffset += remainder / 2; } else patternOffset = patWidth / 2; } else { if (remainder) patternOffset = (patWidth - remainder)/2; } } const CGFloat dottedLine[2] = { patWidth, patWidth }; CGContextSetLineDash(context, patternOffset, dottedLine, 2); } CGContextBeginPath(context); CGContextMoveToPoint(context, p1.x(), p1.y()); CGContextAddLineToPoint(context, p2.x(), p2.y()); CGContextStrokePath(context); if (patWidth) CGContextRestoreGState(context); if (shouldAntialias()) CGContextSetShouldAntialias(context, true); }
AffineTransform GraphicsContext::getCTM() const { CGAffineTransform t = CGContextGetCTM(platformContext()); return AffineTransform(t.a, t.b, t.c, t.d, t.tx, t.ty); }
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size()); }
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) { cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset); }
void GraphicsContext::clearRect(const FloatRect& r) { if (paintingDisabled()) return; CGContextClearRect(platformContext(), r); }
void GraphicsContext::setAlpha(float alpha) { if (paintingDisabled()) return; CGContextSetAlpha(platformContext(), alpha); }
void GraphicsContext::setMiterLimit(float limit) { if (paintingDisabled()) return; CGContextSetMiterLimit(platformContext(), limit); }
void GraphicsContext::clearPlatformShadow() { if (paintingDisabled()) return; CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0); }
void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) { if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f) return; CGContextRef context = platformContext(); CGContextSaveGState(context); CGContextBeginPath(context); CGContextSetShouldAntialias(context, false); int x = rect.x(); int y = rect.y(); float w = (float)rect.width(); float h = (float)rect.height(); float scaleFactor = h / w; float reverseScaleFactor = w / h; if (w != h) scale(FloatSize(1, scaleFactor)); float hRadius = w / 2; float vRadius = h / 2; float fa = startAngle; float falen = fa + angleSpan; float start = -fa * piFloat / 180.0f; float end = -falen * piFloat / 180.0f; CGContextAddArc(context, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, start, end, true); if (w != h) scale(FloatSize(1, reverseScaleFactor)); float width = strokeThickness(); int patWidth = 0; switch (strokeStyle()) { case DottedStroke: patWidth = (int)(width / 2); break; case DashedStroke: patWidth = 3 * (int)(width / 2); break; default: break; } if (patWidth) { // Example: 80 pixels with a width of 30 pixels. // Remainder is 20. The maximum pixels of line we could paint // will be 50 pixels. int distance; if (hRadius == vRadius) distance = static_cast<int>((piFloat * hRadius) / 2.0f); else // We are elliptical and will have to estimate the distance distance = static_cast<int>((piFloat * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0f)) / 2.0f); int remainder = distance % patWidth; int coverage = distance - remainder; int numSegments = coverage / patWidth; float patternOffset = 0.0f; // Special case 1px dotted borders for speed. if (patWidth == 1) patternOffset = 1.0f; else { bool evenNumberOfSegments = !(numSegments % 2); if (remainder) evenNumberOfSegments = !evenNumberOfSegments; if (evenNumberOfSegments) { if (remainder) { patternOffset += patWidth - remainder; patternOffset += remainder / 2.0f; } else patternOffset = patWidth / 2.0f; } else { if (remainder) patternOffset = (patWidth - remainder) / 2.0f; } } const CGFloat dottedLine[2] = { patWidth, patWidth }; CGContextSetLineDash(context, patternOffset, dottedLine, 2); } CGContextStrokePath(context); CGContextRestoreGState(context); }
void GraphicsContext::setPlatformShouldSmoothFonts(bool enable) { if (paintingDisabled()) return; CGContextSetShouldSmoothFonts(platformContext(), enable); }
void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) { if (paintingDisabled()) return; setCGFillColor(platformContext(), color, colorSpace); }
void GraphicsContext::setPlatformStrokeThickness(float thickness) { if (paintingDisabled()) return; CGContextSetLineWidth(platformContext(), thickness); }
void GraphicsContext::setAlpha(float alpha) { platformContext()->setGlobalAlpha(alpha); }