void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle) { // 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, WebKit 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 (penStyle == DottedStroke || penStyle == DashedStroke) { if (p1.x() == p2.x()) { p1.setY(p1.y() + strokeWidth); p2.setY(p2.y() - strokeWidth); } else { p1.setX(p1.x() + strokeWidth); p2.setX(p2.x() - strokeWidth); } } if (static_cast<int>(strokeWidth) % 2) { //odd if (p1.x() == p2.x()) { // We're a vertical line. Adjust our x. p1.setX(p1.x() + 0.5f); p2.setX(p2.x() + 0.5f); } else { // We're a horizontal line. Adjust our y. p1.setY(p1.y() + 0.5f); p2.setY(p2.y() + 0.5f); } } }
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; }
FloatPoint floatPointForCenterCoordinate(const BasicShapeCenterCoordinate& centerX, const BasicShapeCenterCoordinate& centerY, FloatSize boxSize) { FloatPoint p; float offset = floatValueForLength(centerX.length(), boxSize.width()); switch (centerX.keyword()) { case BasicShapeCenterCoordinate::None: case BasicShapeCenterCoordinate::Left: p.setX(offset); break; case BasicShapeCenterCoordinate::Right: p.setX(boxSize.width() - offset); break; default: ASSERT_NOT_REACHED(); } offset = floatValueForLength(centerY.length(), boxSize.height()); switch (centerY.keyword()) { case BasicShapeCenterCoordinate::None: case BasicShapeCenterCoordinate::Top: p.setY(offset); break; case BasicShapeCenterCoordinate::Bottom: p.setY(boxSize.height() - offset); break; default: ASSERT_NOT_REACHED(); } return p; }
// Adjusts 'point' to the nearest point inside rect, and leaves it unchanged if already inside. void adjustPointToRect(FloatPoint& point, const FloatRect& rect) { if (point.x() < rect.x()) point.setX(rect.x()); else if (point.x() > rect.maxX()) point.setX(rect.maxX()); if (point.y() < rect.y()) point.setY(rect.y()); else if (point.y() > rect.maxY()) point.setY(rect.maxY()); }
FloatPoint CSSGradientValue::resolvePoint(CSSPrimitiveValue* first, CSSPrimitiveValue* second, const IntSize& size, float zoomFactor) { FloatPoint result; if (first->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) result.setX(first->getFloatValue() * zoomFactor); else if (first->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) result.setX(first->getFloatValue() / 100.f * size.width()); if (second->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) result.setY(second->getFloatValue() * zoomFactor); else if (second->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) result.setY(second->getFloatValue() / 100.f * size.height()); return result; }
static void updateCurrentPoint(FloatPoint& currentPoint, const PathSegmentData& segment) { switch (segment.command) { case PathSegMoveToRel: case PathSegLineToRel: case PathSegCurveToCubicRel: case PathSegCurveToQuadraticRel: case PathSegArcRel: case PathSegLineToHorizontalRel: case PathSegLineToVerticalRel: case PathSegCurveToCubicSmoothRel: case PathSegCurveToQuadraticSmoothRel: currentPoint += segment.targetPoint; break; case PathSegMoveToAbs: case PathSegLineToAbs: case PathSegCurveToCubicAbs: case PathSegCurveToQuadraticAbs: case PathSegArcAbs: case PathSegCurveToCubicSmoothAbs: case PathSegCurveToQuadraticSmoothAbs: currentPoint = segment.targetPoint; break; case PathSegLineToHorizontalAbs: currentPoint.setX(segment.targetPoint.x()); break; case PathSegLineToVerticalAbs: currentPoint.setY(segment.targetPoint.y()); break; case PathSegClosePath: break; default: ASSERT_NOT_REACHED(); } }
void FullscreenVideoController::LayerClient::platformCALayerLayoutSublayersOfLayer(PlatformCALayer* layer) { ASSERT_ARG(layer, layer == m_parent->m_rootChild); HTMLVideoElement* videoElement = m_parent->m_videoElement.get(); if (!videoElement) return; PlatformCALayer* videoLayer = PlatformCALayer::platformCALayer(videoElement->platformLayer()); if (!videoLayer || videoLayer->superlayer() != layer) return; FloatRect layerBounds = layer->bounds(); FloatSize videoSize = videoElement->player()->naturalSize(); float scaleFactor; if (videoSize.aspectRatio() > layerBounds.size().aspectRatio()) scaleFactor = layerBounds.width() / videoSize.width(); else scaleFactor = layerBounds.height() / videoSize.height(); videoSize.scale(scaleFactor); // Calculate the centered position based on the videoBounds and layerBounds: FloatPoint videoPosition; FloatPoint videoOrigin; videoOrigin.setX((layerBounds.width() - videoSize.width()) * 0.5); videoOrigin.setY((layerBounds.height() - videoSize.height()) * 0.5); videoLayer->setPosition(videoOrigin); videoLayer->setBounds(FloatRect(FloatPoint(), videoSize)); }
void SVGGlyphToPathTranslator::advance() { do { if (m_glyph) { float advance = m_glyphBuffer.advanceAt(m_index).width(); if (m_isVerticalText) m_currentPoint.move(0, advance); else m_currentPoint.move(advance, 0); } ++m_index; if (m_index >= m_stoppingPoint || !m_glyphBuffer.fontDataAt(m_index)->isSVGFont()) break; m_glyph = m_glyphBuffer.glyphAt(m_index); if (!m_glyph) continue; m_svgGlyph = m_fontElement.svgGlyphForGlyph(m_glyph); ASSERT(!m_svgGlyph.isPartOfLigature); ASSERT(m_svgGlyph.tableEntry == m_glyph); SVGGlyphElement::inheritUnspecifiedAttributes(m_svgGlyph, &m_svgFontData); } while ((!m_glyph || m_svgGlyph.pathData.isEmpty()) && m_index < m_stoppingPoint); if (containsMorePaths() && m_isVerticalText) { m_glyphOrigin.setX(m_svgGlyph.verticalOriginX * m_scale); m_glyphOrigin.setY(m_svgGlyph.verticalOriginY * m_scale); } }
static float squaredDistanceToClosestPoint(const FloatRect& rect, const FloatPoint& point) { FloatPoint closestPoint; closestPoint.setX(std::max(std::min(point.x(), rect.maxX()), rect.x())); closestPoint.setY(std::max(std::min(point.y(), rect.maxY()), rect.y())); return (point - closestPoint).diagonalLengthSquared(); }
FloatPoint ShapeOutsideInfo::shapeToRendererPoint(FloatPoint point) const { FloatPoint result = FloatPoint(point.x() + logicalLeftOffset(), point.y() + logicalTopOffset()); if (m_renderer.style()->isFlippedBlocksWritingMode()) result.setY(m_renderer.logicalHeight() - result.y()); if (!m_renderer.style()->isHorizontalWritingMode()) result = result.transposedPoint(); return result; }
FloatPoint RenderSlider::mouseEventOffsetToThumb(MouseEvent* evt) { ASSERT(m_thumb && m_thumb->renderer()); FloatPoint localPoint = m_thumb->renderBox()->absoluteToLocal(evt->absoluteLocation(), false, true); IntRect thumbBounds = m_thumb->renderBox()->borderBoxRect(); FloatPoint offset; offset.setX(thumbBounds.x() + thumbBounds.width() / 2 - localPoint.x()); offset.setY(thumbBounds.y() + thumbBounds.height() / 2 - localPoint.y()); return offset; }
void SliderThumbElement::defaultEventHandler(Event* event) { if (!event->isMouseEvent()) { HTMLDivElement::defaultEventHandler(event); return; } MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); bool isLeftButton = mouseEvent->button() == LeftButton; const AtomicString& eventType = event->type(); if (eventType == eventNames().mousedownEvent && isLeftButton) { if (document()->frame() && renderer()) { RenderSlider* slider = toRenderSlider(renderer()->parent()); if (slider) { if (slider->mouseEventIsInThumb(mouseEvent)) { // We selected the thumb, we want the cursor to always stay at // the same position relative to the thumb. m_offsetToThumb = slider->mouseEventOffsetToThumb(mouseEvent); } else { // We are outside the thumb, move the thumb to the point were // we clicked. We'll be exactly at the center of the thumb. m_offsetToThumb.setX(0); m_offsetToThumb.setY(0); } m_inDragMode = true; document()->frame()->eventHandler()->setCapturingMouseEventsNode(m_shadowParent); event->setDefaultHandled(); return; } } } else if (eventType == eventNames().mouseupEvent && isLeftButton) { if (m_inDragMode) { if (Frame* frame = document()->frame()) frame->eventHandler()->setCapturingMouseEventsNode(0); m_inDragMode = false; event->setDefaultHandled(); return; } } else if (eventType == eventNames().mousemoveEvent) { if (m_inDragMode && renderer() && renderer()->parent()) { RenderSlider* slider = toRenderSlider(renderer()->parent()); if (slider) { FloatPoint curPoint = slider->absoluteToLocal(mouseEvent->absoluteLocation(), false, true); IntPoint eventOffset(curPoint.x() + m_offsetToThumb.x(), curPoint.y() + m_offsetToThumb.y()); slider->setValueForPosition(slider->positionForOffset(eventOffset)); event->setDefaultHandled(); return; } } } HTMLDivElement::defaultEventHandler(event); }
static void yAttrSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { INC_STATS("DOM.SVGPoint.y._set"); V8SVGPODTypeWrapper<FloatPoint>* wrapper = V8SVGPODTypeWrapper<FloatPoint>::toNative(info.Holder()); FloatPoint impInstance = *wrapper; FloatPoint* imp = &impInstance; float v = static_cast<float>(value->NumberValue()); imp->setY(v); wrapper->commitChange(*imp, V8Proxy::svgContext(wrapper)); return; }
static inline FloatPoint rightMostCornerToVector(const FloatRect& rect, const FloatSize& vector) { // Return the corner of the rectangle that if it is to the left of the vector // would mean all of the rectangle is to the left of the vector. // The vector here represents the side between two points in a clockwise convex polygon. // // Q XXX // QQQ XXX If the lower left corner of X is left of the vector that goes from the top corner of Q to // QQQ the right corner of Q, then all of X is left of the vector, and intersection impossible. // Q // FloatPoint point; if (vector.width() >= 0) point.setY(rect.maxY()); else point.setY(rect.y()); if (vector.height() >= 0) point.setX(rect.x()); else point.setX(rect.maxX()); return point; }
FloatPoint Path::currentPoint() const { if (m_path.countPoints() > 0) { SkPoint skResult; m_path.getLastPt(&skResult); FloatPoint result; result.setX(SkScalarToFloat(skResult.fX)); result.setY(SkScalarToFloat(skResult.fY)); return result; } // FIXME: Why does this return quietNaN? Other ports return 0,0. float quietNaN = std::numeric_limits<float>::quiet_NaN(); return FloatPoint(quietNaN, quietNaN); }
Path SVGGlyphToPathTranslator::nextPath() { if (m_isVerticalText) { m_glyphOrigin.setX(m_svgGlyph.verticalOriginX * m_scale); m_glyphOrigin.setY(m_svgGlyph.verticalOriginY * m_scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(m_currentPoint.x() + m_glyphOrigin.x(), m_currentPoint.y() + m_glyphOrigin.y()); glyphPathTransform.scale(m_scale, -m_scale); Path glyphPath = m_svgGlyph.pathData; glyphPath.transform(glyphPathTransform); incrementIndex(); return glyphPath; }
bool AnimationBase::computeTransformedExtentViaTransformList(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const { FloatRect floatBounds = bounds; FloatPoint transformOrigin; bool applyTransformOrigin = containsRotation(style.transform().operations()) || style.transform().affectedByTransformOrigin(); if (applyTransformOrigin) { float offsetX = style.transformOriginX().isPercent() ? rendererBox.x() : 0; float offsetY = style.transformOriginY().isPercent() ? rendererBox.y() : 0; transformOrigin.setX(floatValueForLength(style.transformOriginX(), rendererBox.width()) + offsetX); transformOrigin.setY(floatValueForLength(style.transformOriginY(), rendererBox.height()) + offsetY); // Ignore transformOriginZ because we'll bail if we encounter any 3D transforms. floatBounds.moveBy(-transformOrigin); } for (const auto& operation : style.transform().operations()) { if (operation->type() == TransformOperation::ROTATE) { // For now, just treat this as a full rotation. This could take angle into account to reduce inflation. floatBounds = boundsOfRotatingRect(floatBounds); } else { TransformationMatrix transform; operation->apply(transform, rendererBox.size()); if (!transform.isAffine()) return false; if (operation->type() == TransformOperation::MATRIX || operation->type() == TransformOperation::MATRIX_3D) { TransformationMatrix::Decomposed2Type toDecomp; transform.decompose2(toDecomp); // Any rotation prevents us from using a simple start/end rect union. if (toDecomp.angle) return false; } floatBounds = transform.mapRect(floatBounds); } } if (applyTransformOrigin) floatBounds.moveBy(transformOrigin); bounds = LayoutRect(floatBounds); return true; }
bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection) { float pxLength = p2.x() - p1.x(); float pyLength = p2.y() - p1.y(); float dxLength = d2.x() - d1.x(); float dyLength = d2.y() - d1.y(); float denom = pxLength * dyLength - pyLength * dxLength; if (!denom) return false; float param = ((d1.x() - p1.x()) * dyLength - (d1.y() - p1.y()) * dxLength) / denom; intersection.setX(p1.x() + param * pxLength); intersection.setY(p1.y() + param * pyLength); return true; }
void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE) return; RenderText& textRenderer = toRenderText(*flow.firstChild()); ASSERT(!textRenderer.firstTextBox()); bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled(); GraphicsContext& context = *paintInfo.context; const Font& font = style.font(); TextPaintStyle textPaintStyle = computeTextPaintStyle(textRenderer, style, paintInfo); GraphicsContextStateSaver stateSaver(context, textPaintStyle.strokeWidth > 0); updateGraphicsContext(context, textPaintStyle); LayoutRect paintRect = paintInfo.rect; paintRect.moveBy(-paintOffset); auto resolver = runResolver(flow, layout); auto range = resolver.rangeForRect(paintRect); for (auto it = range.begin(), end = range.end(); it != end; ++it) { const auto& run = *it; if (!run.rect().intersects(paintRect)) continue; TextRun textRun(run.text()); textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); FloatPoint textOrigin = run.baseline() + paintOffset; textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), flow.document().deviceScaleFactor())); context.drawText(font, textRun, textOrigin); if (debugBordersEnabled) paintDebugBorders(context, run.rect(), paintOffset); } }
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return; // We can only paint SVGFonts if a context is available. RenderObject* renderObject = renderObjectFromRun(run); ASSERT(renderObject); bool isVerticalText = false; if (RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject)) { RenderStyle* parentRenderObjectStyle = parentRenderObject->style(); ASSERT(parentRenderObjectStyle); isVerticalText = parentRenderObjectStyle->svgStyle().isVerticalWritingMode(); } float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); FloatPoint glyphOrigin; glyphOrigin.setX(svgFontData->horizontalOriginX() * scale); glyphOrigin.setY(svgFontData->horizontalOriginY() * scale); unsigned short resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; FloatPoint currentPoint = point; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); if (!glyph) continue; float advance = glyphBuffer.advanceAt(from + i); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData); // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (svgGlyph.pathData.isEmpty()) { if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); continue; } if (isVerticalText) { glyphOrigin.setX(svgGlyph.verticalOriginX * scale); glyphOrigin.setY(svgGlyph.verticalOriginY * scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); glyphPathTransform.scale(scale, -scale); Path glyphPath = svgGlyph.pathData; glyphPath.transform(glyphPathTransform); SVGRenderSupport::fillOrStrokePath(context, resourceMode, glyphPath); if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); } }
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return; // We can only paint SVGFonts if a context is available. RenderSVGResource* activePaintingResource = activePaintingResourceFromRun(run); RenderObject* renderObject = renderObjectFromRun(run); RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject); RenderStyle* parentRenderObjectStyle = 0; ASSERT(renderObject); if (!activePaintingResource) { // TODO: We're only supporting simple filled HTML text so far. RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource(); solidPaintingResource->setColor(context->fillColor()); activePaintingResource = solidPaintingResource; } bool isVerticalText = false; if (parentRenderObject) { parentRenderObjectStyle = parentRenderObject->style(); ASSERT(parentRenderObjectStyle); isVerticalText = parentRenderObjectStyle->svgStyle()->isVerticalWritingMode(); } float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); ASSERT(activePaintingResource); FloatPoint glyphOrigin; glyphOrigin.setX(svgFontData->horizontalOriginX() * scale); glyphOrigin.setY(svgFontData->horizontalOriginY() * scale); FloatPoint currentPoint = point; RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); if (!glyph) continue; float advance = glyphBuffer.advanceAt(from + i); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData); // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (svgGlyph.pathData.isEmpty()) { if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); continue; } context->save(); if (isVerticalText) { glyphOrigin.setX(svgGlyph.verticalOriginX * scale); glyphOrigin.setY(svgGlyph.verticalOriginY * scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); glyphPathTransform.scale(scale, -scale); Path glyphPath = svgGlyph.pathData; glyphPath.transform(glyphPathTransform); if (activePaintingResource->applyResource(parentRenderObject, parentRenderObjectStyle, context, resourceMode)) { if (renderObject && renderObject->isSVGInlineText()) { const RenderSVGInlineText* textRenderer = toRenderSVGInlineText(renderObject); context->setStrokeThickness(context->strokeThickness() * textRenderer->scalingFactor()); } activePaintingResource->postApplyResource(parentRenderObject, context, resourceMode, &glyphPath, 0); } context->restore(); if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); } }
GradientOpenVG* Gradient::platformGradient() { if (m_gradient) return m_gradient; m_gradient = new GradientOpenVG; m_gradient->m_radial = m_radial; m_gradient->m_transformation = m_gradientSpaceTransformation; switch (m_spreadMethod) { case SpreadMethodPad: m_gradient->m_spread = VG_COLOR_RAMP_SPREAD_PAD; break; case SpreadMethodReflect: m_gradient->m_spread = VG_COLOR_RAMP_SPREAD_REFLECT; break; case SpreadMethodRepeat: m_gradient->m_spread = VG_COLOR_RAMP_SPREAD_REPEAT; break; } if (m_stops.size() > 0) { if (!m_stopsSorted) { std::stable_sort(m_stops.begin(), m_stops.end(), compareStops); m_stopsSorted = true; } } if (!m_radial) { // linear gradient m_gradient->m_coordinates.resize(4); m_gradient->m_coordinates[0] = m_p0.x(); m_gradient->m_coordinates[1] = m_p0.y(); m_gradient->m_coordinates[2] = m_p1.x(); m_gradient->m_coordinates[3] = m_p1.y(); if (!m_stops.isEmpty()) { m_gradient->m_colorStops.resize(m_stops.size()*5); for (int i = 0; i < m_stops.size(); i++) { m_gradient->m_colorStops[i*5] = m_stops[i].stop; m_gradient->m_colorStops[i*5 + 1] = m_stops[i].red; m_gradient->m_colorStops[i*5 + 2] = m_stops[i].green; m_gradient->m_colorStops[i*5 + 3] = m_stops[i].blue; m_gradient->m_colorStops[i*5 + 4] = m_stops[i].alpha; } } return m_gradient; } // radial gradient // OpenVG takes centre, focus, radius. WebKit provides p0+r0, p1+r1. FloatPoint c0; FloatPoint c1; FloatPoint focus; double r0; double r1; bool reverseStops; // c0 should be the centre of the circle with the smaller radius r0 if (m_r0 < m_r1) { c0 = m_p0; c1 = m_p1; r0 = m_r0; r1 = m_r1; reverseStops = false; } else { c0 = m_p1; c1 = m_p0; r0 = m_r1; r1 = m_r0; reverseStops = true; } if (r0 == r1) { if (c0 == c1) return m_gradient; m_gradient->m_coordinates.resize(5); m_gradient->m_coordinates[0] = c1.x(); // centre m_gradient->m_coordinates[1] = c1.y(); m_gradient->m_coordinates[2] = c1.x(); // focal point m_gradient->m_coordinates[3] = c1.y(); m_gradient->m_coordinates[4] = r1; // radius of centre if (m_stops.size() < 1) { // if there's no stop, we should use transparent black m_gradient->m_colorStops.resize(5); m_gradient->m_colorStops[0] = 0; m_gradient->m_colorStops[1] = 0.0; m_gradient->m_colorStops[2] = 0.0; m_gradient->m_colorStops[3] = 0.0; m_gradient->m_colorStops[4] = 0.0; } else { int nStops = m_stops.size() > 1 ? 1 : 2; m_gradient->m_colorStops.resize(nStops * 5); m_gradient->m_colorStops[0] = 0; m_gradient->m_colorStops[1] = m_stops[0].red; m_gradient->m_colorStops[2] = m_stops[0].green; m_gradient->m_colorStops[3] = m_stops[0].blue; m_gradient->m_colorStops[4] = m_stops[0].alpha; if (nStops > 1) { m_gradient->m_colorStops[5] = 0; m_gradient->m_colorStops[6] = m_stops[nStops - 1].red; m_gradient->m_colorStops[7] = m_stops[nStops - 1].green; m_gradient->m_colorStops[8] = m_stops[nStops - 1].blue; m_gradient->m_colorStops[9] = m_stops[nStops - 1].alpha; } } return m_gradient; } double startOffset; bool emulateReflect = false; focus.setX((r1 * c0.x() - r0 * c1.x()) / (r1 - r0)); focus.setY((r1 * c0.y() - r0 * c1.y()) / (r1 - r0)); /* OpenVG doesn't support the inner circle and use instead a gradient focal. * That means we need to emulate the WebKit behaviour by processing the * WebKit gradient stops. * The SpreadMethodPad mode is quite easy to handle, * it's just a matter of stop position translation and calculation of * the corresponding OpenVG radial gradient focal. * The SpreadMethodReflect and SpreadMethodRepeat modes require a new * radial gradient, with a new outer circle equal to r1 - r0 in the SpreadMethodRepeat * case and 2 * (r1 - r0) in the SpreadMethodReflect case, and a new gradient stop * list that maps to the original WebKit stop list. */ if ((m_spreadMethod == SpreadMethodReflect || m_spreadMethod == SpreadMethodRepeat) && r0 > 0.0) { double rOrig = r1; if (m_spreadMethod == SpreadMethodReflect) { r1 = 2 * r1 - r0; emulateReflect = true; } startOffset = fmod(r1, r1 - r0) / (r1 - r0) - 1.0; double r = r1 - r0; /* New position of outer circle. */ double x = r * (c1.x() - focus.x()) / rOrig + focus.x(); double y = r * (c1.y() - focus.y()) / rOrig + focus.y(); c1.setX(x); c1.setY(y); r1 = r; r0 = 0.0; } else startOffset = r0 / r1; m_gradient->m_coordinates.resize(5); m_gradient->m_coordinates[0] = c1.x(); // centre m_gradient->m_coordinates[1] = c1.y(); m_gradient->m_coordinates[2] = focus.x(); // focal point m_gradient->m_coordinates[3] = focus.y(); m_gradient->m_coordinates[4] = r1; // radius of centre if (emulateReflect) m_gradient->m_spread = VG_COLOR_RAMP_SPREAD_REPEAT; if (m_stops.size() < 1) return m_gradient; if (m_stops.size() == 1) { m_gradient->m_colorStops.resize(5); m_gradient->m_colorStops[0] = m_stops[0].stop; m_gradient->m_colorStops[1] = m_stops[0].red; m_gradient->m_colorStops[2] = m_stops[0].green; m_gradient->m_colorStops[3] = m_stops[0].blue; m_gradient->m_colorStops[4] = m_stops[0].alpha; return m_gradient; } int nStops; Vector<ColorStop> actualStops; if (emulateReflect || reverseStops) { nStops = emulateReflect ? m_stops.size() * 2 - 2: m_stops.size(); actualStops.resize(nStops); for (int i = 0; i < nStops; i++) { if (reverseStops) { actualStops[i] = m_stops[nStops - i - 1]; actualStops[i].stop = 1.0 - actualStops[i].stop; } else actualStops[i] = m_stops[i]; if (emulateReflect) { actualStops[i].stop /= 2; if (i > 0 && i < (nStops - 1)) { if (reverseStops) { actualStops[i + nStops - 1] = m_stops[i]; actualStops[i + nStops - 1].stop = 0.5 + 0.5 * actualStops[i + nStops - 1].stop; } else { actualStops[i + nStops - 1] = m_stops[nStops - i - 1]; actualStops[i + nStops - 1].stop = 1 - 0.5 * actualStops[i + nStops - 1].stop; } } } } } else { nStops = m_stops.size(); actualStops = m_stops; } if (startOffset >= 0.0) { m_gradient->m_colorStops.resize(actualStops.size() * 5); for (int i = 0; i < nStops; i++) { double offset = startOffset + (1 - startOffset) * actualStops[i].stop; m_gradient->m_colorStops[i*5] = offset; m_gradient->m_colorStops[i*5 + 1] = actualStops[i].red; m_gradient->m_colorStops[i*5 + 2] = actualStops[i].green; m_gradient->m_colorStops[i*5 + 3] = actualStops[i].blue; m_gradient->m_colorStops[i*5 + 4] = actualStops[i].alpha; } return m_gradient; } bool found = FALSE; unsigned int offsetIndex; float startRed, startGreen, startBlue, startAlpha; float stopRed, stopGreen, stopBlue, stopAlpha; for (int i = 0; i < nStops; i++) { if (actualStops[i].stop >= -startOffset) { if (i > 0) { if (actualStops[i].stop != actualStops[i-1].stop) { double x0, x1; x0 = actualStops[i-1].stop; x1 = actualStops[i].stop; startRed = stopRed = actualStops[i-1].red + (actualStops[i].red - actualStops[i-1].red) * (-startOffset - x0) / (x1 - x0); startGreen = stopGreen = actualStops[i-1].green + (actualStops[i].green - actualStops[i-1].green) * (-startOffset - x0) / (x1 - x0); startBlue = stopBlue = actualStops[i-1].blue + (actualStops[i].blue - actualStops[i-1].blue) * (-startOffset - x0) / (x1 - x0); startAlpha = stopAlpha = actualStops[i-1].alpha + (actualStops[i].alpha - actualStops[i-1].alpha) * (-startOffset - x0) / (x1 - x0); } else { stopRed = actualStops[i-1].red; stopGreen = actualStops[i-1].green; stopBlue = actualStops[i-1].blue; stopAlpha = actualStops[i-1].alpha; startRed = actualStops[i].red; startGreen = actualStops[i].green; startBlue = actualStops[i].blue; startAlpha = actualStops[i].alpha; } } else { startRed = stopRed = actualStops[i].red; startGreen = stopGreen = actualStops[i].green; startBlue = stopBlue = actualStops[i].blue; startAlpha = stopAlpha = actualStops[i].alpha; } offsetIndex = i; found = true; break; } } if (!found) { offsetIndex = nStops - 1; startRed = stopRed = actualStops[offsetIndex].red; startGreen = stopGreen = actualStops[offsetIndex].green; startBlue = stopBlue = actualStops[offsetIndex].blue; startAlpha = stopAlpha = actualStops[offsetIndex].alpha; } m_gradient->m_colorStops.resize((nStops + 2) * 5); int stopNum = 0; m_gradient->m_colorStops[stopNum * 5] = 0; m_gradient->m_colorStops[stopNum * 5 + 1] = startRed; m_gradient->m_colorStops[stopNum * 5 + 2] = startGreen; m_gradient->m_colorStops[stopNum * 5 + 3] = startBlue; m_gradient->m_colorStops[stopNum * 5 + 4] = startAlpha; ++stopNum; for (int i = offsetIndex; i < nStops; i++) { m_gradient->m_colorStops[stopNum * 5] = actualStops[i].stop + startOffset; m_gradient->m_colorStops[stopNum * 5 + 1] = actualStops[i].red; m_gradient->m_colorStops[stopNum * 5 + 2] = actualStops[i].green; m_gradient->m_colorStops[stopNum * 5 + 3] = actualStops[i].blue; m_gradient->m_colorStops[stopNum * 5 + 4] = actualStops[i].alpha; ++stopNum; } for (int i = 0; i < offsetIndex; i++) { m_gradient->m_colorStops[stopNum * 5] = 1.0 + actualStops[i].stop + startOffset; m_gradient->m_colorStops[stopNum * 5 + 1] = actualStops[i].red; m_gradient->m_colorStops[stopNum * 5 + 2] = actualStops[i].green; m_gradient->m_colorStops[stopNum * 5 + 3] = actualStops[i].blue; m_gradient->m_colorStops[stopNum * 5 + 4] = actualStops[i].alpha; ++stopNum; } m_gradient->m_colorStops[stopNum * 5] = 0; m_gradient->m_colorStops[stopNum * 5 + 1] = stopRed; m_gradient->m_colorStops[stopNum * 5 + 2] = stopGreen; m_gradient->m_colorStops[stopNum * 5 + 3] = stopBlue; m_gradient->m_colorStops[stopNum * 5 + 4] = stopAlpha; return m_gradient; }
unsigned char FETurbulence::calculateTurbulenceValueForPoint(int channel, PaintingData& paintingData, const FloatPoint& point) { float tileWidth = paintingData.filterSize.width(); ASSERT(tileWidth > 0); float tileHeight = paintingData.filterSize.height(); ASSERT(tileHeight > 0); // Adjust the base frequencies if necessary for stitching. if (m_stitchTiles) { // When stitching tiled turbulence, the frequencies must be adjusted // so that the tile borders will be continuous. if (m_baseFrequencyX) { float lowFrequency = floorf(tileWidth * m_baseFrequencyX) / tileWidth; float highFrequency = ceilf(tileWidth * m_baseFrequencyX) / tileWidth; // BaseFrequency should be non-negative according to the standard. if (m_baseFrequencyX / lowFrequency < highFrequency / m_baseFrequencyX) m_baseFrequencyX = lowFrequency; else m_baseFrequencyX = highFrequency; } if (m_baseFrequencyY) { float lowFrequency = floorf(tileHeight * m_baseFrequencyY) / tileHeight; float highFrequency = ceilf(tileHeight * m_baseFrequencyY) / tileHeight; if (m_baseFrequencyY / lowFrequency < highFrequency / m_baseFrequencyY) m_baseFrequencyY = lowFrequency; else m_baseFrequencyY = highFrequency; } // Set up TurbulenceInitial stitch values. paintingData.width = roundf(tileWidth * m_baseFrequencyX); paintingData.wrapX = s_perlinNoise + paintingData.width; paintingData.height = roundf(tileHeight * m_baseFrequencyY); paintingData.wrapY = s_perlinNoise + paintingData.height; } float turbulenceFunctionResult = 0; FloatPoint noiseVector(point.x() * m_baseFrequencyX, point.y() * m_baseFrequencyY); float ratio = 1; for (int octave = 0; octave < m_numOctaves; ++octave) { if (m_type == FETURBULENCE_TYPE_FRACTALNOISE) turbulenceFunctionResult += noise2D(channel, paintingData, noiseVector) / ratio; else turbulenceFunctionResult += fabsf(noise2D(channel, paintingData, noiseVector)) / ratio; noiseVector.setX(noiseVector.x() * 2); noiseVector.setY(noiseVector.y() * 2); ratio *= 2; if (m_stitchTiles) { // Update stitch values. Subtracting s_perlinNoiseoise before the multiplication and // adding it afterward simplifies to subtracting it once. paintingData.width *= 2; paintingData.wrapX = 2 * paintingData.wrapX - s_perlinNoise; paintingData.height *= 2; paintingData.wrapY = 2 * paintingData.wrapY - s_perlinNoise; } } // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult * 255) + 255) / 2 by fractalNoise // and (turbulenceFunctionResult * 255) by turbulence. if (m_type == FETURBULENCE_TYPE_FRACTALNOISE) turbulenceFunctionResult = turbulenceFunctionResult * 0.5f + 0.5f; // Clamp result turbulenceFunctionResult = std::max(std::min(turbulenceFunctionResult, 1.f), 0.f); return static_cast<unsigned char>(turbulenceFunctionResult * 255); }
void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) { if (!fontElement) return; SVGTextRunWalkerDrawTextData data; FloatPoint currentPoint = point; float scale = convertEmUnitToPixel(size(), fontFaceElement->unitsPerEm(), 1.0f); SVGPaintServer* activePaintServer = run.activePaintServer(); // If renderObject is not set, we're dealing for HTML text rendered using SVG Fonts. if (!run.referencingRenderObject()) { ASSERT(!activePaintServer); // TODO: We're only supporting simple filled HTML text so far. SVGPaintServerSolid* solidPaintServer = SVGPaintServer::sharedSolidPaintServer(); solidPaintServer->setColor(context->fillColor()); activePaintServer = solidPaintServer; } ASSERT(activePaintServer); int charsConsumed; String glyphName; bool isVerticalText = false; float xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName); FloatPoint glyphOrigin; String language; // TODO: language matching & svg glyphs should be possible for HTML text, too. if (run.referencingRenderObject()) { isVerticalText = isVerticalWritingMode(run.referencingRenderObject()->style()->svgStyle()); if (SVGElement* element = static_cast<SVGElement*>(run.referencingRenderObject()->element())) language = element->getAttribute(XMLNames::langAttr); } if (!isVerticalText) { glyphOrigin.setX(fontData->horizontalOriginX() * scale); glyphOrigin.setY(fontData->horizontalOriginY() * scale); } data.extraCharsAvailable = 0; data.charsConsumed = 0; SVGTextRunWalker<SVGTextRunWalkerDrawTextData> runWalker(fontData, fontElement, data, drawTextUsingSVGFontCallback, drawTextMissingGlyphCallback); runWalker.walk(run, isVerticalText, language, from, to); SVGPaintTargetType targetType = context->textDrawingMode() == cTextStroke ? ApplyToStrokeTargetType : ApplyToFillTargetType; unsigned numGlyphs = data.glyphIdentifiers.size(); unsigned fallbackCharacterIndex = 0; for (unsigned i = 0; i < numGlyphs; ++i) { const SVGGlyphIdentifier& identifier = data.glyphIdentifiers[run.rtl() ? numGlyphs - i - 1 : i]; if (identifier.isValid) { // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (!identifier.pathData.isEmpty()) { context->save(); if (isVerticalText) { glyphOrigin.setX(identifier.verticalOriginX * scale); glyphOrigin.setY(identifier.verticalOriginY * scale); } context->translate(xStartOffset + currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); context->scale(FloatSize(scale, -scale)); context->beginPath(); context->addPath(identifier.pathData); if (activePaintServer->setup(context, run.referencingRenderObject(), targetType)) { // Spec: Any properties specified on a text elements which represents a length, such as the // 'stroke-width' property, might produce surprising results since the length value will be // processed in the coordinate system of the glyph. (TODO: What other lengths? miter-limit? dash-offset?) if (targetType == ApplyToStrokeTargetType && scale != 0.0f) context->setStrokeThickness(context->strokeThickness() / scale); activePaintServer->renderPath(context, run.referencingRenderObject(), targetType); activePaintServer->teardown(context, run.referencingRenderObject(), targetType); } context->restore(); } if (isVerticalText) currentPoint.move(0.0f, identifier.verticalAdvanceY * scale); else currentPoint.move(identifier.horizontalAdvanceX * scale, 0.0f); } else { // Handle system font fallback FontDescription fontDescription(context->font().fontDescription()); fontDescription.setFamily(FontFamily()); Font font(fontDescription, 0, 0); // spacing handled by SVG text code. font.update(context->font().fontSelector()); TextRun fallbackCharacterRun(run); fallbackCharacterRun.setText(&data.fallbackCharacters[run.rtl() ? data.fallbackCharacters.size() - fallbackCharacterIndex - 1 : fallbackCharacterIndex], 1); font.drawText(context, fallbackCharacterRun, currentPoint); if (isVerticalText) currentPoint.move(0.0f, font.floatWidth(fallbackCharacterRun)); else currentPoint.move(font.floatWidth(fallbackCharacterRun), 0.0f); fallbackCharacterIndex++; } } } }