Commit 5678f008 authored by commit-queue@webkit.org's avatar commit-queue@webkit.org
Browse files

[CSS Exclusions] ExclusionShape API should use logical coordinates for input/output

https://bugs.webkit.org/show_bug.cgi?id=96156

Patch by Hans Muller <hmuller@adobe.com> on 2012-09-24
Reviewed by Dirk Schulze.

Source/WebCore:

Correct how ExclusionShapes deal with logical coordinates and enable
shape-inside exclusion layout for vertical writing-modes.

BasicShape's are defined in physical cooordinates, incoming line and box
dimensions are logical coordinates, and the ExclusionShape internals assume
that lines are aligned with the Y axis. The createExclusionShape() method
is responsible for converting the BasicShape to the internal coordinate
system when the writing-mode is vertical. Similarly, the getInclude,ExcludedIntervals()
methods are responsible for converting their logical line parameters to Y
values in the internal coordinate system. The min,maxYForLogicalLine()
methods do the conversion, based on the WritingMode the ExclusionShape
was created with. The getInclude,ExcludedIntervals() methods return the
logical left and right edges of line segments. No transformation is needed for this.

The ExclusionShape's internal coordinate system is essentially the
"logical" one, except that top/bottom always map to Y, no matter what
the writing-mode is.  This is just to simplify writing geometrical shape
algorithms, notably the complex ones for polygons. The bug report includes a
pair of diagrams that clarify how internal coordinates are related to logical
and physical coordinates.

Test: fast/exclusions/shape-inside/shape-inside-vertical-text.html

* rendering/ExclusionRectangle.cpp:
(WebCore::ExclusionRectangle::getExcludedIntervals): rename more accurately reflects return value
(WebCore::ExclusionRectangle::getIncludedIntervals): rename for consistency with "excluded" version
* rendering/ExclusionRectangle.h:
* rendering/ExclusionShape.cpp:
(WebCore):
(WebCore::ExclusionShape::createExclusionShape):
* rendering/ExclusionShape.h:
(LineSegment):
(WebCore::LineSegment::LineSegment): relocated from WrapShapeInfo.h
(WebCore):
(ExclusionShape):
(WebCore::ExclusionShape::minYForLogicalLine):
(WebCore::ExclusionShape::maxYForLogicalLine):
(WebCore::ExclusionShape::internalToLogicalBoundingBox):
* rendering/WrapShapeInfo.cpp:
(WebCore::WrapShapeInfo::isWrapShapeInfoEnabledForRenderBlock):
(WebCore::WrapShapeInfo::computeShapeSize): pass writingMode to createExclusionShape()
(WebCore::WrapShapeInfo::computeSegmentsForLine): removed short-circuit for vertical writing-modes
* rendering/WrapShapeInfo.h:
(WebCore):

LayoutTests:

Verify that shape-inside respects the writing-mode, notably the vertical writing-modes.

* fast/exclusions/shape-inside/shape-inside-vertical-text-expected.html: Added.
* fast/exclusions/shape-inside/shape-inside-vertical-text.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@129411 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 769514ed
2012-09-24 Hans Muller <hmuller@adobe.com>
[CSS Exclusions] ExclusionShape API should use logical coordinates for input/output
https://bugs.webkit.org/show_bug.cgi?id=96156
Reviewed by Dirk Schulze.
Verify that shape-inside respects the writing-mode, notably the vertical writing-modes.
* fast/exclusions/shape-inside/shape-inside-vertical-text-expected.html: Added.
* fast/exclusions/shape-inside/shape-inside-vertical-text.html: Added.
2012-09-24 Alec Flett <alecflett@chromium.org>
 
Rebaseline for r129389
<!DOCTYPE html>
<html>
<head>
<style>
#shape-inside-vertical-lr {
padding: 20px 35px 25px 15px;
width: 150px;
height: 175px;
-webkit-writing-mode: vertical-lr;
position: relative;
}
#shape-inside-vertical-rl {
padding: 20px 35px 25px 15px;
width: 150px;
height: 175px;
-webkit-writing-mode: vertical-rl;
position: relative;
}
#border {
position: absolute;
top: 13px;
left: 18px;
width: 150px;
height: 175px;
border: 2px solid blue;
}
</style>
</head>
<body>
<div id="shape-inside-vertical-lr">
<div id="border"></div>
This text should be contained by the blue square.
</div>
<div id="shape-inside-vertical-rl">
<div id="border"></div>
This text should be contained by the blue square.
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<script>
if (window.internals)
window.internals.settings.setCSSExclusionsEnabled(true);
</script>
<style>
#shape-inside-vertical-lr {
width: 200px;
height: 220px;
-webkit-shape-inside: rectangle(15px, 20px, 150px, 175px);
-webkit-writing-mode: vertical-lr;
position: relative;
}
#shape-inside-vertical-rl {
width: 200px;
height: 220px;
-webkit-shape-inside: rectangle(15px, 20px, 150px, 175px);
-webkit-writing-mode: vertical-rl;
position: relative;
}
#border {
position: absolute;
top: 13px;
left: 18px;
width: 150px;
height: 175px;
border: 2px solid blue;
}
</style>
</head>
<body>
<div id="shape-inside-vertical-lr">
<div id="border"></div>
This text should be contained by the blue square.
</div>
<div id="shape-inside-vertical-rl">
<div id="border"></div>
This text should be contained by the blue square.
</div>
</body>
</html>
2012-09-24 Hans Muller <hmuller@adobe.com>
[CSS Exclusions] ExclusionShape API should use logical coordinates for input/output
https://bugs.webkit.org/show_bug.cgi?id=96156
Reviewed by Dirk Schulze.
Correct how ExclusionShapes deal with logical coordinates and enable
shape-inside exclusion layout for vertical writing-modes.
BasicShape's are defined in physical cooordinates, incoming line and box
dimensions are logical coordinates, and the ExclusionShape internals assume
that lines are aligned with the Y axis. The createExclusionShape() method
is responsible for converting the BasicShape to the internal coordinate
system when the writing-mode is vertical. Similarly, the getInclude,ExcludedIntervals()
methods are responsible for converting their logical line parameters to Y
values in the internal coordinate system. The min,maxYForLogicalLine()
methods do the conversion, based on the WritingMode the ExclusionShape
was created with. The getInclude,ExcludedIntervals() methods return the
logical left and right edges of line segments. No transformation is needed for this.
The ExclusionShape's internal coordinate system is essentially the
"logical" one, except that top/bottom always map to Y, no matter what
the writing-mode is. This is just to simplify writing geometrical shape
algorithms, notably the complex ones for polygons. The bug report includes a
pair of diagrams that clarify how internal coordinates are related to logical
and physical coordinates.
Test: fast/exclusions/shape-inside/shape-inside-vertical-text.html
* rendering/ExclusionRectangle.cpp:
(WebCore::ExclusionRectangle::getExcludedIntervals): rename more accurately reflects return value
(WebCore::ExclusionRectangle::getIncludedIntervals): rename for consistency with "excluded" version
* rendering/ExclusionRectangle.h:
* rendering/ExclusionShape.cpp:
(WebCore):
(WebCore::ExclusionShape::createExclusionShape):
* rendering/ExclusionShape.h:
(LineSegment):
(WebCore::LineSegment::LineSegment): relocated from WrapShapeInfo.h
(WebCore):
(ExclusionShape):
(WebCore::ExclusionShape::minYForLogicalLine):
(WebCore::ExclusionShape::maxYForLogicalLine):
(WebCore::ExclusionShape::internalToLogicalBoundingBox):
* rendering/WrapShapeInfo.cpp:
(WebCore::WrapShapeInfo::isWrapShapeInfoEnabledForRenderBlock):
(WebCore::WrapShapeInfo::computeShapeSize): pass writingMode to createExclusionShape()
(WebCore::WrapShapeInfo::computeSegmentsForLine): removed short-circuit for vertical writing-modes
* rendering/WrapShapeInfo.h:
(WebCore):
2012-09-24 Tony Chang <tony@chromium.org>
 
Replace RenderMeter::updateLogicalHeight to RenderMeter::computeLogicalHeight
......@@ -40,10 +40,10 @@ static inline float ellipseXIntercept(float y, float rx, float ry)
return rx * sqrt(1 - (y*y) / (ry*ry));
}
void ExclusionRectangle::getOutsideIntervals(float y1, float y2, Vector<ExclusionInterval>& rv) const
void ExclusionRectangle::getExcludedIntervals(float logicalTop, float logicalBottom, SegmentList& result) const
{
if (y1 > y2)
std::swap(y1, y2);
float y1 = minYForLogicalLine(logicalTop, logicalBottom);
float y2 = maxYForLogicalLine(logicalTop, logicalBottom);
if (y2 < m_y || y1 >= m_y + m_height)
return;
......@@ -65,15 +65,15 @@ void ExclusionRectangle::getOutsideIntervals(float y1, float y2, Vector<Exclusio
}
}
rv.append(ExclusionInterval(x1, x2));
result.append(LineSegment(x1, x2));
}
void ExclusionRectangle::getInsideIntervals(float y1, float y2, Vector<ExclusionInterval>& rv) const
void ExclusionRectangle::getIncludedIntervals(float logicalTop, float logicalBottom, SegmentList& result) const
{
if (y1 > y2)
std::swap(y1, y2);
float y1 = minYForLogicalLine(logicalTop, logicalBottom);
float y2 = maxYForLogicalLine(logicalTop, logicalBottom);
if (y1 < m_y || y2 >= m_y + m_height)
if (y1 < m_y || y2 > m_y + m_height)
return;
float x1 = m_x;
......@@ -106,7 +106,7 @@ void ExclusionRectangle::getInsideIntervals(float y1, float y2, Vector<Exclusion
}
}
rv.append(ExclusionInterval(x1, x2));
result.append(LineSegment(x1, x2));
}
} // namespace WebCore
......@@ -31,6 +31,7 @@
#define ExclusionRectangle_h
#include "ExclusionShape.h"
#include "FloatSize.h"
#include <wtf/Assertions.h>
#include <wtf/Vector.h>
......@@ -38,20 +39,20 @@ namespace WebCore {
class ExclusionRectangle : public ExclusionShape {
public:
ExclusionRectangle(float x, float y, float width, float height, float rx = 0, float ry = 0)
ExclusionRectangle(const FloatRect& bounds, const FloatSize& radii)
: ExclusionShape()
, m_x(x)
, m_y(y)
, m_width(width)
, m_height(height)
, m_rx(rx)
, m_ry(ry)
, m_x(bounds.x())
, m_y(bounds.y())
, m_width(bounds.width())
, m_height(bounds.height())
, m_rx(radii.width())
, m_ry(radii.height())
{
}
virtual FloatRect shapeLogicalBoundingBox() const OVERRIDE { return FloatRect(m_x, m_y, m_width, m_height); }
virtual void getOutsideIntervals(float y1, float y2, Vector<ExclusionInterval>&) const OVERRIDE;
virtual void getInsideIntervals(float y1, float y2, Vector<ExclusionInterval>&) const OVERRIDE;
virtual FloatRect shapeLogicalBoundingBox() const OVERRIDE { return internalToLogicalBoundingBox(FloatRect(m_x, m_y, m_width, m_height)); }
virtual void getExcludedIntervals(float logicalTop, float logicalBottom, SegmentList&) const OVERRIDE;
virtual void getIncludedIntervals(float logicalTop, float logicalBottom, SegmentList&) const OVERRIDE;
private:
float m_x;
......
......@@ -32,6 +32,7 @@
#include "BasicShapeFunctions.h"
#include "ExclusionRectangle.h"
#include "FloatSize.h"
#include "LengthFunctions.h"
#include "NotImplemented.h"
#include "WindRule.h"
......@@ -41,66 +42,93 @@
namespace WebCore {
static PassOwnPtr<ExclusionShape> createExclusionRectangle(float x, float y, float width, float height, float rx, float ry)
static PassOwnPtr<ExclusionShape> createExclusionRectangle(const FloatRect& bounds, const FloatSize& radii)
{
ASSERT(width >= 0 && height >= 0 && rx >= 0 && ry >= 0);
return adoptPtr(new ExclusionRectangle(x, y, width, height, rx, ry));
ASSERT(bounds.width() >= 0 && bounds.height() >= 0 && radii.width() >= 0 && radii.height() >= 0);
return adoptPtr(new ExclusionRectangle(bounds, radii));
}
static PassOwnPtr<ExclusionShape> createExclusionCircle(float cx, float cy, float radius)
static PassOwnPtr<ExclusionShape> createExclusionCircle(const FloatPoint& center, float radius)
{
ASSERT(radius >= 0);
return adoptPtr(new ExclusionRectangle(cx - radius, cy - radius, cx + radius, cy + radius, radius, radius));
return adoptPtr(new ExclusionRectangle(FloatRect(center.x() - radius, center.y() - radius, radius*2, radius*2), FloatSize(radius, radius)));
}
static PassOwnPtr<ExclusionShape> createExclusionEllipse(float cx, float cy, float rx, float ry)
static PassOwnPtr<ExclusionShape> createExclusionEllipse(const FloatPoint& center, const FloatSize& radii)
{
ASSERT(rx >= 0 && ry >= 0);
return adoptPtr(new ExclusionRectangle(cx - rx, cy - ry, cx + rx, cy + ry, rx, ry));
ASSERT(radii.width() >= 0 && radii.height() >= 0);
return adoptPtr(new ExclusionRectangle(FloatRect(center.x() - radii.width(), center.y() - radii.height(), radii.width()*2, radii.height()*2), radii));
}
PassOwnPtr<ExclusionShape> ExclusionShape::createExclusionShape(const BasicShape* wrapShape, float borderBoxLogicalWidth, float borderBoxLogicalHeight)
// If the writingMode is vertical, then the BasicShape's (physical) x and y coordinates are swapped, so that
// line segments are parallel to the internal coordinate system's X axis.
PassOwnPtr<ExclusionShape> ExclusionShape::createExclusionShape(const BasicShape* basicShape, float logicalBoxWidth, float logicalBoxHeight, WritingMode writingMode)
{
if (!wrapShape)
if (!basicShape)
return nullptr;
switch (wrapShape->type()) {
bool horizontalWritingMode = isHorizontalWritingMode(writingMode);
float boxWidth = horizontalWritingMode ? logicalBoxWidth : logicalBoxHeight;
float boxHeight = horizontalWritingMode ? logicalBoxHeight : logicalBoxWidth;
OwnPtr<ExclusionShape> exclusionShape;
switch (basicShape->type()) {
case BasicShape::BASIC_SHAPE_RECTANGLE: {
const BasicShapeRectangle* rectangle = static_cast<const BasicShapeRectangle*>(wrapShape);
Length rx = rectangle->cornerRadiusX();
Length ry = rectangle->cornerRadiusY();
return createExclusionRectangle(
floatValueForLength(rectangle->x(), borderBoxLogicalWidth),
floatValueForLength(rectangle->y(), borderBoxLogicalHeight),
floatValueForLength(rectangle->width(), borderBoxLogicalWidth),
floatValueForLength(rectangle->height(), borderBoxLogicalHeight),
rx.isUndefined() ? 0 : floatValueForLength(rx, borderBoxLogicalWidth),
ry.isUndefined() ? 0 : floatValueForLength(ry, borderBoxLogicalHeight) );
const BasicShapeRectangle* rectangle = static_cast<const BasicShapeRectangle*>(basicShape);
float x = floatValueForLength(rectangle->x(), boxWidth);
float y = floatValueForLength(rectangle->y(), boxHeight);
float width = floatValueForLength(rectangle->width(), boxWidth);
float height = floatValueForLength(rectangle->height(), boxHeight);
Length radiusXLength = rectangle->cornerRadiusX();
Length radiusYLength = rectangle->cornerRadiusY();
float radiusX = radiusXLength.isUndefined() ? 0 : floatValueForLength(radiusXLength, boxWidth);
float radiusY = radiusYLength.isUndefined() ? 0 : floatValueForLength(radiusYLength, boxHeight);
exclusionShape = horizontalWritingMode
? createExclusionRectangle(FloatRect(x, y, width, height), FloatSize(radiusX, radiusY))
: createExclusionRectangle(FloatRect(y, x, height, width), FloatSize(radiusY, radiusX));
break;
}
case BasicShape::BASIC_SHAPE_CIRCLE: {
const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(wrapShape);
return createExclusionCircle(
floatValueForLength(circle->centerX(), borderBoxLogicalWidth),
floatValueForLength(circle->centerY(), borderBoxLogicalHeight),
floatValueForLength(circle->radius(), std::max(borderBoxLogicalHeight, borderBoxLogicalWidth)) );
const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape);
float centerX = floatValueForLength(circle->centerX(), boxWidth);
float centerY = floatValueForLength(circle->centerY(), boxHeight);
float radius = floatValueForLength(circle->radius(), std::max(boxHeight, boxWidth));
exclusionShape = horizontalWritingMode
? createExclusionCircle(FloatPoint(centerX, centerY), radius)
: createExclusionCircle(FloatPoint(centerY, centerX), radius);
break;
}
case BasicShape::BASIC_SHAPE_ELLIPSE: {
const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(wrapShape);
return createExclusionEllipse(
floatValueForLength(ellipse->centerX(), borderBoxLogicalWidth),
floatValueForLength(ellipse->centerY(), borderBoxLogicalHeight),
floatValueForLength(ellipse->radiusX(), borderBoxLogicalWidth),
floatValueForLength(ellipse->radiusY(), borderBoxLogicalHeight) );
const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape);
float centerX = floatValueForLength(ellipse->centerX(), boxWidth);
float centerY = floatValueForLength(ellipse->centerY(), boxHeight);
float radiusX = floatValueForLength(ellipse->radiusX(), boxWidth);
float radiusY = floatValueForLength(ellipse->radiusY(), boxHeight);
exclusionShape = horizontalWritingMode
? createExclusionEllipse(FloatPoint(centerX, centerY), FloatSize(radiusX, radiusY))
: createExclusionEllipse(FloatPoint(centerY, centerX), FloatSize(radiusY, radiusX));
break;
}
case BasicShape::BASIC_SHAPE_POLYGON:
notImplemented();
default:
ASSERT_NOT_REACHED();
}
ASSERT_NOT_REACHED();
return nullptr;
exclusionShape->m_logicalBoxWidth = logicalBoxWidth;
exclusionShape->m_logicalBoxHeight = logicalBoxHeight;
exclusionShape->m_writingMode = writingMode;
return exclusionShape.release();
}
} // namespace WebCore
......@@ -31,22 +31,51 @@
#define ExclusionShape_h
#include "BasicShapes.h"
#include "ExclusionInterval.h"
#include "FloatRect.h"
#include "WritingMode.h"
#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
namespace WebCore {
struct LineSegment {
float logicalLeft;
float logicalRight;
LineSegment(float logicalLeft, float logicalRight)
: logicalLeft(logicalLeft)
, logicalRight(logicalRight)
{
}
};
typedef Vector<LineSegment> SegmentList;
// A representation of a BasicShape that enables layout code to determine how to break a line up into segments
// that will fit within or around a shape. The line is defined by a pair of logical Y coordinates and the
// computed segments are returned as pairs of logical X coordinates. The BasicShape itself is defined in
// physical coordinates.
class ExclusionShape {
public:
static PassOwnPtr<ExclusionShape> createExclusionShape(const BasicShape*, float borderBoxLogicalWidth, float borderBoxLogicalHeight);
static PassOwnPtr<ExclusionShape> createExclusionShape(const BasicShape*, float logicalBoxWidth, float logicalBoxHeight, WritingMode);
virtual ~ExclusionShape() { }
virtual FloatRect shapeLogicalBoundingBox() const = 0;
virtual void getInsideIntervals(float logicalTop, float logicalBottom, Vector<ExclusionInterval>&) const = 0;
virtual void getOutsideIntervals(float logicalTop, float logicalBottom, Vector<ExclusionInterval>&) const = 0;
virtual void getIncludedIntervals(float logicalTop, float logicalBottom, SegmentList&) const = 0;
virtual void getExcludedIntervals(float logicalTop, float logicalBottom, SegmentList&) const = 0;
protected:
float minYForLogicalLine(float logicalTop, float logicalBottom) const { return (m_writingMode == RightToLeftWritingMode) ? m_logicalBoxHeight - logicalBottom : logicalTop; }
float maxYForLogicalLine(float logicalTop, float logicalBottom) const { return (m_writingMode == RightToLeftWritingMode) ? m_logicalBoxHeight - logicalTop : logicalBottom; }
FloatRect internalToLogicalBoundingBox(FloatRect r) const { return (m_writingMode == RightToLeftWritingMode) ? FloatRect(r.x(), m_logicalBoxHeight - r.maxY(), r.width(), r.height()) : r; }
private:
WritingMode m_writingMode;
float m_logicalBoxWidth;
float m_logicalBoxHeight;
};
} // namespace WebCore
......
......@@ -71,10 +71,6 @@ WrapShapeInfo* WrapShapeInfo::wrapShapeInfoForRenderBlock(const RenderBlock* blo
bool WrapShapeInfo::isWrapShapeInfoEnabledForRenderBlock(const RenderBlock* block)
{
// FIXME: Bug 89705: Enable shape inside for vertical writing modes
if (!block->isHorizontalWritingMode())
return false;
// FIXME: Bug 89707: Enable shape inside for non-rectangular shapes
BasicShape* shape = block->style()->wrapShapeInside();
return (shape && shape->type() == BasicShape::BASIC_SHAPE_RECTANGLE);
......@@ -107,7 +103,7 @@ void WrapShapeInfo::computeShapeSize(LayoutUnit logicalWidth, LayoutUnit logical
BasicShape* shape = m_block->style()->wrapShapeInside();
ASSERT(shape);
m_shape = ExclusionShape::createExclusionShape(shape, logicalWidth, logicalHeight);
m_shape = ExclusionShape::createExclusionShape(shape, logicalWidth, logicalHeight, m_block->style()->writingMode());
ASSERT(m_shape);
}
......@@ -118,15 +114,7 @@ bool WrapShapeInfo::computeSegmentsForLine(LayoutUnit lineTop)
if (lineState() == LINE_INSIDE_SHAPE) {
ASSERT(m_shape);
Vector<ExclusionInterval> intervals;
m_shape->getInsideIntervals(lineTop, lineTop, intervals); // FIXME: Bug 95479, workaround for now
for (size_t i = 0; i < intervals.size(); i++) {
LineSegment segment;
segment.logicalLeft = intervals[i].x1;
segment.logicalRight = intervals[i].x2;
m_segments.append(segment);
}
m_shape->getIncludedIntervals(lineTop, lineTop, m_segments); // FIXME: Bug 95479, workaround for now
}
return m_segments.size();
}
......
......@@ -42,14 +42,6 @@
namespace WebCore {
class RenderBlock;
class WrapShapeInfo;
struct LineSegment {
LayoutUnit logicalLeft;
LayoutUnit logicalRight;
};
typedef Vector<LineSegment> SegmentList;
class WrapShapeInfo {
WTF_MAKE_FAST_ALLOCATED;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment