bool ExclusionPolygon::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const { float minIntervalTop = minLogicalIntervalTop; float minIntervalHeight = minLogicalIntervalSize.height(); float minIntervalWidth = minLogicalIntervalSize.width(); const FloatPolygon& polygon = shapePaddingBounds(); const FloatRect boundingBox = polygon.boundingBox(); if (minIntervalWidth > boundingBox.width()) return false; float minY = std::max(boundingBox.y(), minIntervalTop); float maxY = minY + minIntervalHeight; if (maxY > boundingBox.maxY()) return false; Vector<const FloatPolygonEdge*> edges; polygon.overlappingEdges(minIntervalTop, boundingBox.maxY(), edges); float dx = minIntervalWidth / 2; float dy = minIntervalHeight / 2; Vector<OffsetPolygonEdge> offsetEdges; for (unsigned i = 0; i < edges.size(); ++i) { const FloatPolygonEdge& edge = *(edges[i]); const FloatPoint& vertex0 = edge.previousEdge().vertex1(); const FloatPoint& vertex1 = edge.vertex1(); const FloatPoint& vertex2 = edge.vertex2(); Vector<OffsetPolygonEdge> offsetEdgeBuffer; if (vertex2.y() > vertex1.y() ? vertex2.x() >= vertex1.x() : vertex1.x() >= vertex2.x()) { offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, -dy))); offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, dy))); } else { offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, dy))); offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, -dy))); } if (isReflexVertex(vertex0, vertex1, vertex2)) { if (vertex2.x() <= vertex1.x() && vertex0.x() <= vertex1.x()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(dx, -dy), FloatSize(dx, dy))); else if (vertex2.x() >= vertex1.x() && vertex0.x() >= vertex1.x()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(-dx, dy))); if (vertex2.y() <= vertex1.y() && vertex0.y() <= vertex1.y()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, dy), FloatSize(dx, dy))); else if (vertex2.y() >= vertex1.y() && vertex0.y() >= vertex1.y()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(dx, -dy))); } for (unsigned j = 0; j < offsetEdgeBuffer.size(); ++j) if (offsetEdgeBuffer[j].maxY() >= minY) offsetEdges.append(offsetEdgeBuffer[j]); } offsetEdges.append(OffsetPolygonEdge(polygon, minIntervalTop, FloatSize(0, dy))); FloatPoint offsetEdgesIntersection; FloatRect firstFitRect; bool firstFitFound = false; for (unsigned i = 0; i < offsetEdges.size() - 1; ++i) { for (unsigned j = i + 1; j < offsetEdges.size(); ++j) { if (offsetEdges[i].intersection(offsetEdges[j], offsetEdgesIntersection)) { FloatPoint potentialFirstFitLocation(offsetEdgesIntersection.x() - dx, offsetEdgesIntersection.y() - dy); FloatRect potentialFirstFitRect(potentialFirstFitLocation, minLogicalIntervalSize); if ((offsetEdges[i].basis() == OffsetPolygonEdge::LineTop || offsetEdges[j].basis() == OffsetPolygonEdge::LineTop || potentialFirstFitLocation.y() >= minIntervalTop) && (!firstFitFound || aboveOrToTheLeft(potentialFirstFitRect, firstFitRect)) && polygon.contains(offsetEdgesIntersection) && firstFitRectInPolygon(polygon, potentialFirstFitRect, offsetEdges[i].edgeIndex(), offsetEdges[j].edgeIndex())) { firstFitFound = true; firstFitRect = potentialFirstFitRect; } } } } if (firstFitFound) result = LayoutUnit::fromFloatCeil(firstFitRect.y()); return firstFitFound; }
void Path::moveTo(const FloatPoint& point) { CGPathMoveToPoint(m_path, 0, point.x(), point.y()); }
void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p) { CGPathAddQuadCurveToPoint(m_path, 0, cp.x(), cp.y(), p.x(), p.y()); }
void SVGDocument::startPan(const FloatPoint& start) { if (rootElement()) m_translate = FloatPoint(start.x() - rootElement()->currentTranslate().x(), rootElement()->currentTranslate().y() + start.y()); }
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { cairo_t* cr = context->platformContext(); cairo_save(cr); cairo_set_scaled_font(cr, font->platformData().scaledFont()); GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); float offset = 0.0f; for (int i = 0; i < numGlyphs; i++) { glyphs[i].x = offset; glyphs[i].y = 0.0f; offset += glyphBuffer.advanceAt(from + i); } Color fillColor = context->fillColor(); // Synthetic Oblique if(font->platformData().syntheticOblique()) { cairo_matrix_t mat = {1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, point.x(), point.y()}; cairo_transform(cr, &mat); } else { cairo_translate(cr, point.x(), point.y()); } // Text shadow, inspired by FontMac FloatSize shadowOffset; float shadowBlur = 0; Color shadowColor; bool hasShadow = context->textDrawingMode() & cTextFill && context->getShadow(shadowOffset, shadowBlur, shadowColor); // TODO: Blur support if (hasShadow) { // Disable graphics context shadows (not yet implemented) and paint them manually context->clearShadow(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); cairo_save(cr); float red, green, blue, alpha; shadowFillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); #if ENABLE(FILTERS) cairo_text_extents_t extents; cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents); FloatRect rect(FloatPoint(), FloatSize(extents.width, extents.height)); IntSize shadowBufferSize; FloatRect shadowRect; float radius = 0; context->calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowOffset, shadowBlur); // Draw shadow into a new ImageBuffer OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize); GraphicsContext* shadowContext = shadowBuffer->context(); cairo_t* shadowCr = shadowContext->platformContext(); cairo_translate(shadowCr, radius, extents.height + radius); cairo_set_scaled_font(shadowCr, font->platformData().scaledFont()); cairo_show_glyphs(shadowCr, glyphs, numGlyphs); if (font->syntheticBoldOffset()) { cairo_save(shadowCr); cairo_translate(shadowCr, font->syntheticBoldOffset(), 0); cairo_show_glyphs(shadowCr, glyphs, numGlyphs); cairo_restore(shadowCr); } cairo_translate(cr, 0.0, -extents.height); context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius); #else cairo_translate(cr, shadowOffset.width(), shadowOffset.height()); cairo_show_glyphs(cr, glyphs, numGlyphs); if (font->syntheticBoldOffset()) { cairo_save(cr); cairo_translate(cr, font->syntheticBoldOffset(), 0); cairo_show_glyphs(cr, glyphs, numGlyphs); cairo_restore(cr); } #endif cairo_restore(cr); } if (context->textDrawingMode() & cTextFill) { if (context->fillGradient()) { cairo_set_source(cr, context->fillGradient()->platformGradient()); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else if (context->fillPattern()) { AffineTransform affine; cairo_pattern_t* pattern = context->fillPattern()->createPlatformPattern(affine); cairo_set_source(cr, pattern); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } cairo_pattern_destroy(pattern); } else { float red, green, blue, alpha; fillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); } cairo_show_glyphs(cr, glyphs, numGlyphs); if (font->syntheticBoldOffset()) { cairo_save(cr); cairo_translate(cr, font->syntheticBoldOffset(), 0); cairo_show_glyphs(cr, glyphs, numGlyphs); cairo_restore(cr); } } // Prevent running into a long computation within cairo. If the stroke width is // twice the size of the width of the text we will not ask cairo to stroke // the text as even one single stroke would cover the full wdth of the text. // See https://bugs.webkit.org/show_bug.cgi?id=33759. if (context->textDrawingMode() & cTextStroke && context->strokeThickness() < 2 * offset) { if (context->strokeGradient()) { cairo_set_source(cr, context->strokeGradient()->platformGradient()); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else if (context->strokePattern()) { AffineTransform affine; cairo_pattern_t* pattern = context->strokePattern()->createPlatformPattern(affine); cairo_set_source(cr, pattern); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } cairo_pattern_destroy(pattern); } else { Color strokeColor = context->strokeColor(); float red, green, blue, alpha; strokeColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); } cairo_glyph_path(cr, glyphs, numGlyphs); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); } // Re-enable the platform shadow we disabled earlier if (hasShadow) context->setShadow(shadowOffset, shadowBlur, shadowColor, DeviceColorSpace); cairo_restore(cr); }
static PassOwnPtr<Shape> createEllipseShape(const FloatPoint& center, const FloatSize& radii) { ASSERT(radii.width() >= 0 && radii.height() >= 0); return adoptPtr(new RectangleShape(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii)); }
void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise) { // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle if (isfinite(sa) && isfinite(ea)) CGPathAddArc(m_path, 0, p.x(), p.y(), r, sa, ea, clockwise); }
void Path::addLineTo(const FloatPoint& p) { m_path->lineTo(SkFloatToScalar(p.x()), SkFloatToScalar(p.y())); }
void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) { m_path->quadTo( SkFloatToScalar(cp.x()), SkFloatToScalar(cp.y()), SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y())); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); switch(fontDescription().fontSmoothing()) { case Antialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = false; break; } case SubpixelAntialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = true; break; } case NoSmoothing: { graphicsContext->setShouldAntialias(false); shouldUseFontSmoothing = false; break; } case AutoSmoothing: { // For the AutoSmooth case, don't do anything! Keep the default settings. break; } default: ASSERT_NOT_REACHED(); } if (font->platformData().useGDI() && !shouldUseFontSmoothing) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); const FontPlatformData& platformData = font->platformData(); CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } CGContextSetTextMatrix(cgContext, matrix); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); CGContextSetFontSize(cgContext, platformData.size()); wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); FloatSize shadowOffset; float shadowBlur; Color shadowColor; graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor); bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; if (hasSimpleShadow) { // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. graphicsContext->clearShadow(); Color fillColor = graphicsContext->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); graphicsContext->setFillColor(shadowFillColor, ColorSpaceDeviceRGB); CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width(), point.y() + translation.height() + shadowOffset.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB); } CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } if (hasSimpleShadow) graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB); wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
void Path::moveTo(const FloatPoint& point) { m_path->moveTo(SkFloatToScalar(point.x()), SkFloatToScalar(point.y())); }
static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { Color fillColor = graphicsContext->fillColor(); bool drawIntoBitmap = false; int drawingMode = graphicsContext->textDrawingMode(); if (drawingMode == cTextFill) { if (!fillColor.alpha()) return; drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer(); if (!drawIntoBitmap) { FloatSize offset; float blur; Color color; graphicsContext->getShadow(offset, blur, color); drawIntoBitmap = offset.width() || offset.height() || blur; } } // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances. Vector<int, 2048> gdiAdvances; int totalWidth = 0; for (int i = 0; i < numGlyphs; i++) { gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i))); totalWidth += gdiAdvances[i]; } HDC hdc = 0; OwnPtr<GraphicsContext::WindowsBitmap> bitmap; IntRect textRect; if (!drawIntoBitmap) hdc = graphicsContext->getWindowsContext(textRect, true, false); if (!hdc) { drawIntoBitmap = true; // We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges. // FIXME: Can get glyphs' optical bounds (even from CG) to get this right. int lineGap = font->lineGap(); textRect = IntRect(point.x() - (font->ascent() + font->descent()) / 2, point.y() - font->ascent() - lineGap, totalWidth + font->ascent() + font->descent(), font->lineSpacing()); bitmap.set(graphicsContext->createWindowsBitmap(textRect.size())); memset(bitmap->buffer(), 255, bitmap->bufferLength()); hdc = bitmap->hdc(); XFORM xform; xform.eM11 = 1.0f; xform.eM12 = 0.0f; xform.eM21 = 0.0f; xform.eM22 = 1.0f; xform.eDx = -textRect.x(); xform.eDy = -textRect.y(); SetWorldTransform(hdc, &xform); } SelectObject(hdc, font->platformData().hfont()); // Set the correct color. if (drawIntoBitmap) SetTextColor(hdc, RGB(0, 0, 0)); else SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue())); SetBkMode(hdc, TRANSPARENT); SetTextAlign(hdc, TA_LEFT | TA_BASELINE); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); if (translation.width() || translation.height()) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = 1.0; xform.eDx = translation.width(); xform.eDy = translation.height(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); } if (drawingMode == cTextFill) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 0; xform.eM22 = 1.0; xform.eDx = point.x(); xform.eDy = point.y(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); if (font->syntheticBoldOffset()) { xform.eM21 = 0; xform.eDx = font->syntheticBoldOffset(); xform.eDy = 0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); } } else { XFORM xform; GetWorldTransform(hdc, &xform); AffineTransform hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); CGAffineTransform initialGlyphTransform = hdcTransform.isInvertible() ? hdcTransform.inverse() : CGAffineTransformIdentity; if (font->platformData().syntheticOblique()) initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0)); initialGlyphTransform.tx = 0; initialGlyphTransform.ty = 0; CGContextRef cgContext = graphicsContext->platformContext(); CGContextSaveGState(cgContext); BOOL fontSmoothingEnabled = false; SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0); CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled); CGContextScaleCTM(cgContext, 1.0, -1.0); CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height())); for (unsigned i = 0; i < numGlyphs; ++i) { RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); CGContextSaveGState(cgContext); CGContextConcatCTM(cgContext, initialGlyphTransform); if (drawingMode & cTextFill) { CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } if (drawingMode & cTextStroke) { CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } CGContextRestoreGState(cgContext); CGContextTranslateCTM(cgContext, gdiAdvances[i], 0); } CGContextRestoreGState(cgContext); } if (drawIntoBitmap) { UInt8* buffer = bitmap->buffer(); unsigned bufferLength = bitmap->bufferLength(); for (unsigned i = 0; i < bufferLength; i += 4) { // Use green, which is always in the middle. UInt8 alpha = (255 - buffer[i + 1]) * fillColor.alpha() / 255; buffer[i] = fillColor.blue(); buffer[i + 1] = fillColor.green(); buffer[i + 2] = fillColor.red(); buffer[i + 3] = alpha; } graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.topLeft()); } else graphicsContext->releaseWindowsContext(hdc, textRect, true, false); }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { graphicsContext->save(); wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); wxFont* wxfont = font->getWxFont(); graphicsContext->setFillColor(graphicsContext->fillColor()); CGContextRef cgContext = static_cast<CGContextRef>(dc->GetGraphicsContext()->GetNativeContext()); CGFontRef cgFont; #ifdef wxOSX_USE_CORE_TEXT && wxOSX_USE_CORE_TEXT cgFont = CTFontCopyGraphicsFont((CTFontRef)font->OSXGetCTFont(), NULL); #else ATSFontRef fontRef; fontRef = FMGetATSFontRefFromFont(wxfont->MacGetATSUFontID()); if (fontRef) cgFont = CGFontCreateWithPlatformFont((void*)&fontRef); #endif CGContextSetFont(cgContext, cgFont); CGContextSetFontSize(cgContext, wxfont->GetPointSize()); CGFloat red, green, blue, alpha; graphicsContext->fillColor().getRGBA(red, green, blue, alpha); CGContextSetRGBFillColor(cgContext, red, green, blue, alpha); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; CGContextSetTextMatrix(cgContext, matrix); CGContextSetTextPosition(cgContext, point.x(), point.y()); const FloatSize* advanceSizes = static_cast<const FloatSize*>(glyphBuffer.advances(from)); int size = glyphBuffer.size() - from; CGSize sizes[size]; CGGlyph glyphs[numGlyphs]; // if the function doesn't exist, we're probably on tiger and need to grab the // function under its old name, CGFontGetGlyphsForUnicodes if (!CGFontGetGlyphsForUnichars) CGFontGetGlyphsForUnichars = (CGFontGetGlyphsForUnicharsPtr)dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); // Let's make sure we got the function under one name or another! ASSERT(CGFontGetGlyphsForUnichars); CGFontGetGlyphsForUnichars(cgFont, glyphBuffer.glyphs(from), glyphs, numGlyphs); for (int i = 0; i < size; i++) { FloatSize fsize = advanceSizes[i]; sizes[i] = CGSizeMake(fsize.width(), fsize.height()); } CGContextShowGlyphsWithAdvances(cgContext, glyphs, sizes, numGlyphs); if (cgFont) CGFontRelease(cgFont); graphicsContext->restore(); }
static inline float leftSide(const FloatPoint& vertex1, const FloatPoint& vertex2, const FloatPoint& point) { return ((point.x() - vertex1.x()) * (vertex2.y() - vertex1.y())) - ((vertex2.x() - vertex1.x()) * (point.y() - vertex1.y())); }
void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { if (isEmpty()) return; cairo_t* cr = platformPath()->context(); double x0, y0; cairo_get_current_point(cr, &x0, &y0); FloatPoint p0(x0, y0); // Draw only a straight line to p1 if any of the points are equal or the radius is zero // or the points are collinear (triangle that the points form has area of zero value). if ((p1.x() == p0.x() && p1.y() == p0.y()) || (p1.x() == p2.x() && p1.y() == p2.y()) || !radius || !areaOfTriangleFormedByPoints(p0, p1, p2)) { cairo_line_to(cr, p1.x(), p1.y()); return; } FloatPoint p1p0((p0.x() - p1.x()),(p0.y() - p1.y())); FloatPoint p1p2((p2.x() - p1.x()),(p2.y() - p1.y())); float p1p0_length = sqrtf(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); float p1p2_length = sqrtf(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); // all points on a line logic if (cos_phi == -1) { cairo_line_to(cr, p1.x(), p1.y()); return; } if (cos_phi == 1) { // add infinite far away point unsigned int max_length = 65535; double factor_max = max_length / p1p0_length; FloatPoint ep((p0.x() + factor_max * p1p0.x()), (p0.y() + factor_max * p1p0.y())); cairo_line_to(cr, ep.x(), ep.y()); return; } float tangent = radius / tan(acos(cos_phi) / 2); float factor_p1p0 = tangent / p1p0_length; FloatPoint t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); FloatPoint orth_p1p0(p1p0.y(), -p1p0.x()); float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); float factor_ra = radius / orth_p1p0_length; // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); if (cos_alpha < 0.f) orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); FloatPoint p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); // calculate angles for addArc orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); float sa = acos(orth_p1p0.x() / orth_p1p0_length); if (orth_p1p0.y() < 0.f) sa = 2 * piDouble - sa; // anticlockwise logic bool anticlockwise = false; float factor_p1p2 = tangent / p1p2_length; FloatPoint t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); FloatPoint orth_p1p2((t_p1p2.x() - p.x()),(t_p1p2.y() - p.y())); float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); float ea = acos(orth_p1p2.x() / orth_p1p2_length); if (orth_p1p2.y() < 0) ea = 2 * piDouble - ea; if ((sa > ea) && ((sa - ea) < piDouble)) anticlockwise = true; if ((sa < ea) && ((ea - sa) > piDouble)) anticlockwise = true; cairo_line_to(cr, t_p1p0.x(), t_p1p0.y()); addArc(p, radius, sa, ea, anticlockwise); }
void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep) { m_path->cubicTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()), SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()), SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y())); }
static PassOwnPtr<Shape> createCircleShape(const FloatPoint& center, float radius) { ASSERT(radius >= 0); return adoptPtr(new RectangleShape(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius))); }
void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { m_path->arcTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()), SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()), SkFloatToScalar(radius)); }
void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { CGPathAddArcToPoint(m_path, 0, p1.x(), p1.y(), p2.x(), p2.y(), radius); }
bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection) { float pOffset = 0; float pSlope = findSlope(p1, p2, pOffset); float dOffset = 0; float dSlope = findSlope(d1, d2, dOffset); if (dSlope == pSlope) return false; if (pSlope == std::numeric_limits<float>::infinity()) { intersection.setX(p1.x()); intersection.setY(dSlope * intersection.x() + dOffset); return true; } if (dSlope == std::numeric_limits<float>::infinity()) { intersection.setX(d1.x()); intersection.setY(pSlope * intersection.x() + pOffset); return true; } // Find x at intersection, where ys overlap; x = (c' - c) / (m - m') intersection.setX((dOffset - pOffset) / (pSlope - dSlope)); intersection.setY(pSlope * intersection.x() + pOffset); return true; }
// This works by converting the SVG arc to "simple" beziers. // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter bool SVGPathParser::decomposeArcToCubic(float angle, float rx, float ry, FloatPoint& point1, FloatPoint& point2, bool largeArcFlag, bool sweepFlag) { FloatSize midPointDistance = point1 - point2; midPointDistance.scale(0.5f); AffineTransform pointTransform; pointTransform.rotate(-angle); FloatPoint transformedMidPoint = pointTransform.mapPoint(FloatPoint(midPointDistance.width(), midPointDistance.height())); float squareRx = rx * rx; float squareRy = ry * ry; float squareX = transformedMidPoint.x() * transformedMidPoint.x(); float squareY = transformedMidPoint.y() * transformedMidPoint.y(); // Check if the radii are big enough to draw the arc, scale radii if not. // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii float radiiScale = squareX / squareRx + squareY / squareRy; if (radiiScale > 1) { rx *= sqrtf(radiiScale); ry *= sqrtf(radiiScale); } pointTransform.makeIdentity(); pointTransform.scale(1 / rx, 1 / ry); pointTransform.rotate(-angle); point1 = pointTransform.mapPoint(point1); point2 = pointTransform.mapPoint(point2); FloatSize delta = point2 - point1; float d = delta.width() * delta.width() + delta.height() * delta.height(); float scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); float scaleFactor = sqrtf(scaleFactorSquared); if (sweepFlag == largeArcFlag) scaleFactor = -scaleFactor; delta.scale(scaleFactor); FloatPoint centerPoint = point1 + point2; centerPoint.scale(0.5f, 0.5f); centerPoint.move(-delta.height(), delta.width()); float theta1 = FloatPoint(point1 - centerPoint).slopeAngleRadians(); float theta2 = FloatPoint(point2 - centerPoint).slopeAngleRadians(); float thetaArc = theta2 - theta1; if (thetaArc < 0 && sweepFlag) thetaArc += 2 * piFloat; else if (thetaArc > 0 && !sweepFlag) thetaArc -= 2 * piFloat; pointTransform.makeIdentity(); pointTransform.rotate(angle); pointTransform.scale(rx, ry); // Some results of atan2 on some platform implementations are not exact enough. So that we get more // cubic curves than expected here. Adding 0.001f reduces the count of sgements to the correct count. int segments = ceilf(fabsf(thetaArc / (piOverTwoFloat + 0.001f))); for (int i = 0; i < segments; ++i) { float startTheta = theta1 + i * thetaArc / segments; float endTheta = theta1 + (i + 1) * thetaArc / segments; float t = (8 / 6.f) * tanf(0.25f * (endTheta - startTheta)); if (!std::isfinite(t)) return false; float sinStartTheta = sinf(startTheta); float cosStartTheta = cosf(startTheta); float sinEndTheta = sinf(endTheta); float cosEndTheta = cosf(endTheta); point1 = FloatPoint(cosStartTheta - t * sinStartTheta, sinStartTheta + t * cosStartTheta); point1.move(centerPoint.x(), centerPoint.y()); FloatPoint targetPoint = FloatPoint(cosEndTheta, sinEndTheta); targetPoint.move(centerPoint.x(), centerPoint.y()); point2 = targetPoint; point2.move(t * sinEndTheta, -t * cosEndTheta); m_consumer->curveToCubic(pointTransform.mapPoint(point1), pointTransform.mapPoint(point2), pointTransform.mapPoint(targetPoint), AbsoluteCoordinates); } return true; }
// Helper functions static inline FloatPoint blendFloatPoint(const FloatPoint& a, const FloatPoint& b, float progress) { return FloatPoint(blend(a.x(), b.x(), progress), blend(a.y(), b.y(), progress)); }
void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const { if (to < 0) to = run.length(); QPainter *p = ctx->platformContext(); if (ctx->textDrawingMode() & cTextFill) { if (ctx->fillGradient()) { QBrush brush(*ctx->fillGradient()->platformGradient()); brush.setTransform(ctx->fillGradient()->gradientSpaceTransform()); p->setPen(QPen(brush, 0)); } else if (ctx->fillPattern()) { TransformationMatrix affine; p->setPen(QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0)); } else p->setPen(QColor(ctx->fillColor())); } if (ctx->textDrawingMode() & cTextStroke) { if (ctx->strokeGradient()) { QBrush brush(*ctx->strokeGradient()->platformGradient()); brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform()); p->setPen(QPen(brush, ctx->strokeThickness())); } else if (ctx->strokePattern()) { TransformationMatrix affine; p->setPen(QPen(QBrush(ctx->strokePattern()->createPlatformPattern(affine)), ctx->strokeThickness())); } else p->setPen(QPen(QColor(ctx->strokeColor()), ctx->strokeThickness())); } const QString string = fixSpacing(qstring(run)); // text shadow IntSize shadowSize; int shadowBlur; Color shadowColor; bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor); if (from > 0 || to < run.length()) { QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); float x1 = line.cursorToX(from); float x2 = line.cursorToX(to); if (x2 < x1) qSwap(x1, x2); QFontMetrics fm(font()); int ascent = fm.ascent(); QRectF clip(point.x() + x1, point.y() - ascent, x2 - x1, fm.height()); if (hasShadow) { // TODO: when blur support is added, the clip will need to account // for the blur radius qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0; if (shadowSize.width() > 0) dx2 = shadowSize.width(); else dx1 = -shadowSize.width(); if (shadowSize.height() > 0) dy2 = shadowSize.height(); else dy1 = -shadowSize.height(); // expand the clip rect to include the text shadow as well clip.adjust(dx1, dx2, dy1, dy2); } p->save(); p->setClipRect(clip.toRect()); QPointF pt(point.x(), point.y() - ascent); if (hasShadow) { p->save(); p->setPen(QColor(shadowColor)); p->translate(shadowSize.width(), shadowSize.height()); line.draw(p, pt); p->restore(); } line.draw(p, pt); p->restore(); return; } p->setFont(font()); QPointF pt(point.x(), point.y()); int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; if (hasShadow) { // TODO: text shadow blur support p->save(); p->setPen(QColor(shadowColor)); p->translate(shadowSize.width(), shadowSize.height()); p->drawText(pt, string, flags, run.padding()); p->restore(); } if (ctx->textDrawingMode() & cTextStroke) { QPainterPath path; path.addText(pt, font(), string); p->strokePath(path, p->pen()); } if (ctx->textDrawingMode() & cTextFill) p->drawText(pt, string, flags, run.padding()); }
void RenderCombineText::adjustTextOrigin(FloatPoint& textOrigin, const FloatRect& boxRect) const { if (m_isCombined) textOrigin.move(boxRect.height() / 2 - ceilf(m_combinedTextWidth) / 2, style().font().pixelSize()); }
void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/) { if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(&renderer()) || renderer().style()->visibility() != VISIBLE || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len) return; ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); LayoutUnit logicalLeftSide = logicalLeftVisualOverflow(); LayoutUnit logicalRightSide = logicalRightVisualOverflow(); LayoutUnit logicalStart = logicalLeftSide + (isHorizontal() ? paintOffset.x() : paintOffset.y()); LayoutUnit logicalExtent = logicalRightSide - logicalLeftSide; LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY(); LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y(); // When subpixel font scaling is enabled text runs are positioned at // subpixel boundaries on the x-axis and thus there is no reason to // snap the x value. We still round the y-axis to ensure consistent // line heights. LayoutPoint adjustedPaintOffset = RuntimeEnabledFeatures::subpixelFontScalingEnabled() ? LayoutPoint(paintOffset.x(), paintOffset.y().round()) : roundedIntPoint(paintOffset); if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart) return; bool isPrinting = textRenderer().document().printing(); // Determine whether or not we're selected. bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; if (!haveSelection && paintInfo.phase == PaintPhaseSelection) // When only painting the selection, don't bother to paint if there is none. return; if (m_truncation != cNoTruncation) { if (renderer().containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection()) { // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin // at which we start drawing text. // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is: // |Hello|CBA| -> |...He|CBA| // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing // farther to the right. // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the // truncated string i.e. |Hello|CBA| -> |...lo|CBA| LayoutUnit widthOfVisibleText = toRenderText(renderer()).width(m_start, m_truncation, textPos(), isLeftToRightDirection() ? LTR : RTL, isFirstLineStyle()); LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText; // FIXME: The hit testing logic also needs to take this translation into account. LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0); adjustedPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize()); } } GraphicsContext* context = paintInfo.context; RenderObject& rendererToUse = renderer(); RenderStyle* styleToUse = rendererToUse.style(isFirstLineStyle()); adjustedPaintOffset.move(0, styleToUse->isHorizontalWritingMode() ? 0 : -logicalHeight()); FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.move(adjustedPaintOffset.x().toFloat(), adjustedPaintOffset.y().toFloat()); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), logicalHeight())); RenderCombineText* combinedText = styleToUse->hasTextCombine() && textRenderer().isCombineText() && toRenderCombineText(textRenderer()).isCombined() ? &toRenderCombineText(textRenderer()) : 0; bool shouldRotate = !isHorizontal() && !combinedText; if (shouldRotate) context->concatCTM(rotation(boxRect, Clockwise)); // Determine whether or not we have composition underlines to draw. bool containsComposition = renderer().node() && renderer().frame()->inputMethodController().compositionNode() == renderer().node(); bool useCustomUnderlines = containsComposition && renderer().frame()->inputMethodController().compositionUsesCustomUnderlines(); // Determine the text colors and selection colors. Color textFillColor; Color textStrokeColor; Color emphasisMarkColor; float textStrokeWidth = styleToUse->textStrokeWidth(); // Text shadows are disabled when printing. http://crbug.com/258321 const ShadowList* textShadow = (context->printing() || paintInfo.forceBlackText()) ? 0 : styleToUse->textShadow(); if (paintInfo.forceBlackText()) { textFillColor = Color::black; textStrokeColor = Color::black; emphasisMarkColor = Color::black; } else { textFillColor = rendererToUse.resolveColor(styleToUse, CSSPropertyWebkitTextFillColor); bool forceBackgroundToWhite = false; if (isPrinting) { if (styleToUse->printColorAdjust() == PrintColorAdjustEconomy) forceBackgroundToWhite = true; if (textRenderer().document().settings() && textRenderer().document().settings()->shouldPrintBackgrounds()) forceBackgroundToWhite = false; } // Make the text fill color legible against a white background if (forceBackgroundToWhite) textFillColor = correctedTextColor(textFillColor, Color::white); textStrokeColor = rendererToUse.resolveColor(styleToUse, CSSPropertyWebkitTextStrokeColor); // Make the text stroke color legible against a white background if (forceBackgroundToWhite) textStrokeColor = correctedTextColor(textStrokeColor, Color::white); emphasisMarkColor = rendererToUse.resolveColor(styleToUse, CSSPropertyWebkitTextEmphasisColor); // Make the text stroke color legible against a white background if (forceBackgroundToWhite) emphasisMarkColor = correctedTextColor(emphasisMarkColor, Color::white); } bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection); bool paintSelectedTextSeparately = false; Color selectionFillColor = textFillColor; Color selectionStrokeColor = textStrokeColor; Color selectionEmphasisMarkColor = emphasisMarkColor; float selectionStrokeWidth = textStrokeWidth; const ShadowList* selectionShadow = textShadow; if (haveSelection) { // Check foreground color first. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer().selectionForegroundColor(); if (foreground != selectionFillColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionFillColor = foreground; } Color emphasisMarkForeground = paintInfo.forceBlackText() ? Color::black : renderer().selectionEmphasisMarkColor(); if (emphasisMarkForeground != selectionEmphasisMarkColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionEmphasisMarkColor = emphasisMarkForeground; } if (RenderStyle* pseudoStyle = renderer().getCachedPseudoStyle(SELECTION)) { // Text shadows are disabled when printing. http://crbug.com/258321 const ShadowList* shadow = (context->printing() || paintInfo.forceBlackText()) ? 0 : pseudoStyle->textShadow(); if (shadow != selectionShadow) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionShadow = shadow; } float strokeWidth = pseudoStyle->textStrokeWidth(); if (strokeWidth != selectionStrokeWidth) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionStrokeWidth = strokeWidth; } Color stroke = paintInfo.forceBlackText() ? Color::black : rendererToUse.resolveColor(pseudoStyle, CSSPropertyWebkitTextStrokeColor); if (stroke != selectionStrokeColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionStrokeColor = stroke; } } } // Set our font. const Font& font = styleToUse->font(); FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); if (combinedText) combinedText->adjustTextOrigin(textOrigin, boxRect); // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection // and composition highlights. if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) { if (containsComposition) { paintCompositionBackgrounds(context, boxOrigin, styleToUse, font, useCustomUnderlines); } paintDocumentMarkers(context, boxOrigin, styleToUse, font, true); if (haveSelection && !useCustomUnderlines) paintSelection(context, boxOrigin, styleToUse, font, selectionFillColor); } // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only). int length = m_len; int maximumLength; StringView string; if (!combinedText) { string = textRenderer().text().createView(); if (static_cast<unsigned>(length) != string.length() || m_start) string.narrow(m_start, length); maximumLength = textRenderer().textLength() - m_start; } else { combinedText->getStringToRender(m_start, string, length); maximumLength = length; } StringBuilder charactersWithHyphen; TextRun textRun = constructTextRun(styleToUse, font, string, maximumLength, hasHyphen() ? &charactersWithHyphen : 0); if (hasHyphen()) length = textRun.length(); int sPos = 0; int ePos = 0; if (paintSelectedTextOnly || paintSelectedTextSeparately) selectionStartEnd(sPos, ePos); if (m_truncation != cNoTruncation) { sPos = min<int>(sPos, m_truncation); ePos = min<int>(ePos, m_truncation); length = m_truncation; } int emphasisMarkOffset = 0; TextEmphasisPosition emphasisMarkPosition; bool hasTextEmphasis = getEmphasisMarkPosition(styleToUse, emphasisMarkPosition); const AtomicString& emphasisMark = hasTextEmphasis ? styleToUse->textEmphasisMarkString() : nullAtom; if (!emphasisMark.isEmpty()) emphasisMarkOffset = emphasisMarkPosition == TextEmphasisPositionOver ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark); if (!paintSelectedTextOnly) { // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side // effect, so only when we know we're stroking, do a save/restore. GraphicsContextStateSaver stateSaver(*context, textStrokeWidth > 0); updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth); if (!paintSelectedTextSeparately || ePos <= sPos) { // FIXME: Truncate right-to-left text correctly. paintTextWithShadows(context, rendererToUse, font, textRun, nullAtom, 0, 0, length, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); } else { paintTextWithShadows(context, rendererToUse, font, textRun, nullAtom, 0, ePos, sPos, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); } if (!emphasisMark.isEmpty()) { updateGraphicsContext(context, emphasisMarkColor, textStrokeColor, textStrokeWidth); DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1)); TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun; FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin; if (combinedText) context->concatCTM(rotation(boxRect, Clockwise)); int startOffset = 0; int endOffset = length; int paintRunLength = length; if (combinedText) { startOffset = 0; endOffset = objectReplacementCharacterTextRun.length(); paintRunLength = endOffset; } else if (paintSelectedTextSeparately && ePos > sPos) { startOffset = ePos; endOffset = sPos; } // FIXME: Truncate right-to-left text correctly. paintTextWithShadows(context, rendererToUse, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, startOffset, endOffset, paintRunLength, emphasisMarkTextOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); if (combinedText) context->concatCTM(rotation(boxRect, Counterclockwise)); } } if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) { // paint only the text that is selected GraphicsContextStateSaver stateSaver(*context, selectionStrokeWidth > 0); updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth); paintTextWithShadows(context, rendererToUse, font, textRun, nullAtom, 0, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal()); if (!emphasisMark.isEmpty()) { updateGraphicsContext(context, selectionEmphasisMarkColor, textStrokeColor, textStrokeWidth); DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1)); TextRun& emphasisMarkTextRun = combinedText ? objectReplacementCharacterTextRun : textRun; FloatPoint emphasisMarkTextOrigin = combinedText ? FloatPoint(boxOrigin.x() + boxRect.width() / 2, boxOrigin.y() + font.fontMetrics().ascent()) : textOrigin; if (combinedText) context->concatCTM(rotation(boxRect, Clockwise)); int startOffset = combinedText ? 0 : sPos; int endOffset = combinedText ? objectReplacementCharacterTextRun.length() : ePos; int paintRunLength = combinedText ? endOffset : length; paintTextWithShadows(context, rendererToUse, combinedText ? combinedText->originalFont() : font, emphasisMarkTextRun, emphasisMark, emphasisMarkOffset, startOffset, endOffset, paintRunLength, emphasisMarkTextOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal()); if (combinedText) context->concatCTM(rotation(boxRect, Counterclockwise)); } } // Paint decorations TextDecoration textDecorations = styleToUse->textDecorationsInEffect(); if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) { updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth); if (combinedText) context->concatCTM(rotation(boxRect, Clockwise)); paintDecoration(context, boxOrigin, textDecorations, textShadow); if (combinedText) context->concatCTM(rotation(boxRect, Counterclockwise)); } if (paintInfo.phase == PaintPhaseForeground) { paintDocumentMarkers(context, boxOrigin, styleToUse, font, false); // Paint custom underlines for compositions. if (useCustomUnderlines) { const Vector<CompositionUnderline>& underlines = renderer().frame()->inputMethodController().customCompositionUnderlines(); CompositionUnderlineRangeFilter filter(underlines, start(), end()); for (CompositionUnderlineRangeFilter::ConstIterator it = filter.begin(); it != filter.end(); ++it) { if (it->color == Color::transparent) continue; paintCompositionUnderline(context, boxOrigin, *it); } } } if (shouldRotate) context->concatCTM(rotation(boxRect, Counterclockwise)); }
void Path::addLineTo(const FloatPoint& p) { cairo_t* cr = platformPath()->context(); cairo_line_to(cr, p.x(), p.y()); }
void Path::addLineTo(const FloatPoint& p) { CGPathAddLineToPoint(m_path, 0, p.x(), p.y()); }
static inline float areaOfTriangleFormedByPoints(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3) { return p1.x() * (p2.y() - p3.y()) + p2.x() * (p3.y() - p1.y()) + p3.x() * (p1.y() - p2.y()); }
void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p) { CGPathAddCurveToPoint(m_path, 0, cp1.x(), cp1.y(), cp2.x(), cp2.y(), p.x(), p.y()); }
static void computeXIntersections(const FloatPolygon& polygon, float y, bool isMinY, Vector<ExclusionInterval>& result) { Vector<const FloatPolygonEdge*> edges; if (!polygon.overlappingEdges(y, y, edges)) return; Vector<EdgeIntersection> intersections; EdgeIntersection intersection; for (unsigned i = 0; i < edges.size(); ++i) { if (computeXIntersection(edges[i], y, intersection) && intersection.type != VertexYBoth) intersections.append(intersection); } if (intersections.size() < 2) return; std::sort(intersections.begin(), intersections.end(), WebCore::compareEdgeIntersectionX); unsigned index = 0; int windCount = 0; bool inside = false; while (index < intersections.size()) { const EdgeIntersection& thisIntersection = intersections[index]; if (index + 1 < intersections.size()) { const EdgeIntersection& nextIntersection = intersections[index + 1]; if ((thisIntersection.point.x() == nextIntersection.point.x()) && (thisIntersection.type == VertexMinY || thisIntersection.type == VertexMaxY)) { if (thisIntersection.type == nextIntersection.type) { // Skip pairs of intersections whose types are VertexMaxY,VertexMaxY and VertexMinY,VertexMinY. index += 2; } else { // Replace pairs of intersections whose types are VertexMinY,VertexMaxY or VertexMaxY,VertexMinY with one intersection. ++index; } continue; } } const FloatPolygonEdge& thisEdge = *thisIntersection.edge; bool evenOddCrossing = !windCount; if (polygon.fillRule() == RULE_EVENODD) { windCount += (thisEdge.vertex2().y() > thisEdge.vertex1().y()) ? 1 : -1; evenOddCrossing = evenOddCrossing || !windCount; } if (evenOddCrossing) { bool edgeCrossing = thisIntersection.type == Normal; if (!edgeCrossing) { FloatPoint prevVertex; FloatPoint thisVertex; FloatPoint nextVertex; if (getVertexIntersectionVertices(thisIntersection, prevVertex, thisVertex, nextVertex)) { if (nextVertex.y() == y) edgeCrossing = (isMinY) ? prevVertex.y() > y : prevVertex.y() < y; else if (prevVertex.y() == y) edgeCrossing = (isMinY) ? nextVertex.y() > y : nextVertex.y() < y; else edgeCrossing = true; } } if (edgeCrossing) inside = appendIntervalX(thisIntersection.point.x(), inside, result); } ++index; } }