Commit c2dc9a55 authored by Fredrik Söderqvist's avatar Fredrik Söderqvist Committed by Chromium LUCI CQ

Fix zoomed SVGs in border-image

When getting the image from the StyleImage we would always specify a
multiplier (zoom) of one while specifying an object size which had a
different effective zoom. This would mess up the slice calculations for
SVG images under zoom and mess up the source rectangles.

Instead get an image with the appropriate (effective) zoom while also
computing the size of the StyleImage under no zoom (==CSS pixels). Use
these two sizes to derive a (or "the", if it's a raster image) pixel
ratio to apply to the image slice values (which is in CSS pixels).

Improves some cases in the TC from crbug.com/1134145 as well.

Bug: 561519, 596075, 1134145
Change-Id: I744151d40bc806930744cae95530b12496da7387
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2565535
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Reviewed-by: default avatarStephen Chenney <schenney@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833452}
parent 32b93848
......@@ -22,9 +22,21 @@ static int ComputeEdgeWidth(const BorderImageLength& border_slice,
return ValueForLength(border_slice.length(), LayoutUnit(box_extent)).Floor();
}
static int ComputeEdgeSlice(const Length& slice, int maximum) {
return std::min<int>(maximum,
ValueForLength(slice, LayoutUnit(maximum)).Floor());
static int ComputeEdgeSlice(const Length& slice,
float slice_scale,
int maximum) {
int resolved;
// If the slice is a <number> (stored as a fixed Length), scale it by the
// slice scale to get to the same space as the image.
if (slice.IsFixed()) {
LayoutUnit slice_value(slice.Value());
slice_value *= slice_scale;
resolved = slice_value.Round();
} else {
DCHECK(slice.IsPercent());
resolved = ValueForLength(slice, LayoutUnit(maximum)).Floor();
}
return std::min<int>(maximum, resolved);
}
// Scale the width of the |start| and |end| edges using |scale_factor|.
......@@ -48,6 +60,8 @@ static void ScaleEdgeWidths(NinePieceImageGrid::Edge& start,
NinePieceImageGrid::NinePieceImageGrid(const NinePieceImage& nine_piece_image,
IntSize image_size,
const FloatSize& slice_scale,
float zoom,
IntRect border_image_area,
const IntRectOutsets& border_widths,
PhysicalBoxSides sides_to_include)
......@@ -56,38 +70,49 @@ NinePieceImageGrid::NinePieceImageGrid(const NinePieceImage& nine_piece_image,
horizontal_tile_rule_(nine_piece_image.HorizontalRule()),
vertical_tile_rule_(nine_piece_image.VerticalRule()),
fill_(nine_piece_image.Fill()) {
top_.slice = ComputeEdgeSlice(nine_piece_image.ImageSlices().Top(),
const LengthBox& image_slices = nine_piece_image.ImageSlices();
top_.slice = ComputeEdgeSlice(image_slices.Top(), slice_scale.Height(),
image_size.Height());
right_.slice = ComputeEdgeSlice(nine_piece_image.ImageSlices().Right(),
right_.slice = ComputeEdgeSlice(image_slices.Right(), slice_scale.Width(),
image_size.Width());
bottom_.slice = ComputeEdgeSlice(nine_piece_image.ImageSlices().Bottom(),
bottom_.slice = ComputeEdgeSlice(image_slices.Bottom(), slice_scale.Height(),
image_size.Height());
left_.slice = ComputeEdgeSlice(nine_piece_image.ImageSlices().Left(),
left_.slice = ComputeEdgeSlice(image_slices.Left(), slice_scale.Width(),
image_size.Width());
// TODO(fs): Compute edge widths to LayoutUnit, and then only round to
// integer at the end - after (potential) compensation for overlapping edges.
top_.width = sides_to_include.top
? ComputeEdgeWidth(nine_piece_image.BorderSlices().Top(),
border_widths.Top(), top_.slice,
border_image_area.Height())
: 0;
right_.width = sides_to_include.right
? ComputeEdgeWidth(nine_piece_image.BorderSlices().Right(),
border_widths.Right(), right_.slice,
border_image_area.Width())
: 0;
// |Edge::slice| is in image-local units (physical pixels for raster images),
// but when using it to resolve 'auto' for border-image-widths we want it to
// be in zoomed CSS pixels, so divide by |slice_scale| and multiply by zoom.
const FloatSize auto_slice_adjustment(zoom / slice_scale.Width(),
zoom / slice_scale.Height());
const BorderImageLengthBox& border_slices = nine_piece_image.BorderSlices();
top_.width =
sides_to_include.top
? ComputeEdgeWidth(border_slices.Top(), border_widths.Top(),
top_.slice * auto_slice_adjustment.Height(),
border_image_area.Height())
: 0;
right_.width =
sides_to_include.right
? ComputeEdgeWidth(border_slices.Right(), border_widths.Right(),
right_.slice * auto_slice_adjustment.Width(),
border_image_area.Width())
: 0;
bottom_.width =
sides_to_include.bottom
? ComputeEdgeWidth(nine_piece_image.BorderSlices().Bottom(),
border_widths.Bottom(), bottom_.slice,
? ComputeEdgeWidth(border_slices.Bottom(), border_widths.Bottom(),
bottom_.slice * auto_slice_adjustment.Height(),
border_image_area.Height())
: 0;
left_.width = sides_to_include.left
? ComputeEdgeWidth(nine_piece_image.BorderSlices().Left(),
border_widths.Left(), left_.slice,
border_image_area.Width())
: 0;
left_.width =
sides_to_include.left
? ComputeEdgeWidth(border_slices.Left(), border_widths.Left(),
left_.slice * auto_slice_adjustment.Width(),
border_image_area.Width())
: 0;
// The spec says: Given Lwidth as the width of the border image area, Lheight
// as its height, and Wside as the border image width offset for the side, let
......@@ -307,10 +332,7 @@ void NinePieceImageGrid::SetDrawInfoMiddle(NinePieceDrawInfo& draw_info) const {
}
NinePieceImageGrid::NinePieceDrawInfo NinePieceImageGrid::GetNinePieceDrawInfo(
NinePiece piece,
float image_scale_factor) const {
DCHECK_NE(image_scale_factor, 0);
NinePiece piece) const {
NinePieceDrawInfo draw_info;
draw_info.is_corner_piece =
piece == kTopLeftPiece || piece == kTopRightPiece ||
......@@ -323,15 +345,6 @@ NinePieceImageGrid::NinePieceDrawInfo NinePieceImageGrid::GetNinePieceDrawInfo(
else
SetDrawInfoMiddle(draw_info);
if (image_scale_factor != 1) {
// The nine piece grid is computed in unscaled image coordinates but must be
// drawn using scaled image coordinates.
draw_info.source.Scale(image_scale_factor);
// Compensate for source scaling by scaling down the individual tiles.
draw_info.tile_scale.Scale(1 / image_scale_factor);
}
return draw_info;
}
......
......@@ -64,6 +64,8 @@ class CORE_EXPORT NinePieceImageGrid {
public:
NinePieceImageGrid(const NinePieceImage&,
IntSize image_size,
const FloatSize& slice_scale,
float zoom,
IntRect border_image_area,
const IntRectOutsets& border_widths,
PhysicalBoxSides sides_to_include = PhysicalBoxSides());
......@@ -85,7 +87,7 @@ class CORE_EXPORT NinePieceImageGrid {
ENinePieceImageRule vertical;
} tile_rule;
};
NinePieceDrawInfo GetNinePieceDrawInfo(NinePiece, float) const;
NinePieceDrawInfo GetNinePieceDrawInfo(NinePiece) const;
struct Edge {
DISALLOW_NEW();
......
......@@ -34,11 +34,12 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting_NoDrawables) {
IntRect border_image_area(0, 0, 100, 100);
IntRectOutsets border_widths(0, 0, 0, 0);
NinePieceImageGrid grid = NinePieceImageGrid(
nine_piece, image_size, border_image_area, border_widths);
NinePieceImageGrid grid =
NinePieceImageGrid(nine_piece, image_size, FloatSize(1, 1), 1,
border_image_area, border_widths);
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece, 1);
grid.GetNinePieceDrawInfo(piece);
EXPECT_FALSE(draw_info.is_drawable);
}
}
......@@ -53,11 +54,12 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting_AllDrawable) {
IntRect border_image_area(0, 0, 100, 100);
IntRectOutsets border_widths(10, 10, 10, 10);
NinePieceImageGrid grid = NinePieceImageGrid(
nine_piece, image_size, border_image_area, border_widths);
NinePieceImageGrid grid =
NinePieceImageGrid(nine_piece, image_size, FloatSize(1, 1), 1,
border_image_area, border_widths);
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece, 1);
grid.GetNinePieceDrawInfo(piece);
EXPECT_TRUE(draw_info.is_drawable);
}
}
......@@ -72,11 +74,12 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting_NoFillMiddleNotDrawable) {
IntRect border_image_area(0, 0, 100, 100);
IntRectOutsets border_widths(10, 10, 10, 10);
NinePieceImageGrid grid = NinePieceImageGrid(
nine_piece, image_size, border_image_area, border_widths);
NinePieceImageGrid grid =
NinePieceImageGrid(nine_piece, image_size, FloatSize(1, 1), 1,
border_image_area, border_widths);
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece, 1);
grid.GetNinePieceDrawInfo(piece);
if (piece != kMiddlePiece)
EXPECT_TRUE(draw_info.is_drawable);
else
......@@ -103,11 +106,12 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting_TopLeftDrawable) {
};
for (const auto& test_case : test_cases) {
NinePieceImageGrid grid = NinePieceImageGrid(
nine_piece, image_size, border_image_area, test_case.border_widths);
NinePieceImageGrid grid =
NinePieceImageGrid(nine_piece, image_size, FloatSize(1, 1), 1,
border_image_area, test_case.border_widths);
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece, 1);
grid.GetNinePieceDrawInfo(piece);
if (piece == kTopLeftPiece)
EXPECT_EQ(draw_info.is_drawable, test_case.expected_is_drawable);
}
......@@ -127,11 +131,12 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting_ScaleDownBorder) {
// down and corner pieces cover the entire border image area.
nine_piece.SetBorderSlices(BorderImageLengthBox(6));
NinePieceImageGrid grid = NinePieceImageGrid(
nine_piece, image_size, border_image_area, border_widths);
NinePieceImageGrid grid =
NinePieceImageGrid(nine_piece, image_size, FloatSize(1, 1), 1,
border_image_area, border_widths);
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece, 1);
grid.GetNinePieceDrawInfo(piece);
if (draw_info.is_corner_piece)
EXPECT_EQ(draw_info.destination.Size(), FloatSize(50, 50));
else
......@@ -144,16 +149,16 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting_ScaleDownBorder) {
BorderImageLength bottom_right(20);
nine_piece.SetBorderSlices(
BorderImageLengthBox(top_left, bottom_right, bottom_right, top_left));
grid = NinePieceImageGrid(nine_piece, image_size, border_image_area,
border_widths);
grid = NinePieceImageGrid(nine_piece, image_size, FloatSize(1, 1), 1,
border_image_area, border_widths);
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(kTopLeftPiece, 1);
grid.GetNinePieceDrawInfo(kTopLeftPiece);
EXPECT_EQ(draw_info.destination.Size(), FloatSize(33, 33));
draw_info = grid.GetNinePieceDrawInfo(kTopRightPiece, 1);
draw_info = grid.GetNinePieceDrawInfo(kTopRightPiece);
EXPECT_EQ(draw_info.destination.Size(), FloatSize(67, 33));
draw_info = grid.GetNinePieceDrawInfo(kBottomLeftPiece, 1);
draw_info = grid.GetNinePieceDrawInfo(kBottomLeftPiece);
EXPECT_EQ(draw_info.destination.Size(), FloatSize(33, 67));
draw_info = grid.GetNinePieceDrawInfo(kBottomRightPiece, 1);
draw_info = grid.GetNinePieceDrawInfo(kBottomRightPiece);
EXPECT_EQ(draw_info.destination.Size(), FloatSize(67, 67));
// Set border slices that overlap in one dimension but not in the other, and
......@@ -163,18 +168,18 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting_ScaleDownBorder) {
BorderImageLength left_right(Length::Fixed(11));
nine_piece.SetBorderSlices(
BorderImageLengthBox(top_bottom, left_right, top_bottom, left_right));
grid = NinePieceImageGrid(nine_piece, image_size, border_image_area,
border_widths);
grid = NinePieceImageGrid(nine_piece, image_size, FloatSize(1, 1), 1,
border_image_area, border_widths);
NinePieceImageGrid::NinePieceDrawInfo tl_info =
grid.GetNinePieceDrawInfo(kTopLeftPiece, 1);
grid.GetNinePieceDrawInfo(kTopLeftPiece);
EXPECT_EQ(tl_info.destination.Size(), FloatSize(6, 50));
// The top-right, bottom-left and bottom-right pieces are the same size as
// the top-left piece.
draw_info = grid.GetNinePieceDrawInfo(kTopRightPiece, 1);
draw_info = grid.GetNinePieceDrawInfo(kTopRightPiece);
EXPECT_EQ(tl_info.destination.Size(), draw_info.destination.Size());
draw_info = grid.GetNinePieceDrawInfo(kBottomLeftPiece, 1);
draw_info = grid.GetNinePieceDrawInfo(kBottomLeftPiece);
EXPECT_EQ(tl_info.destination.Size(), draw_info.destination.Size());
draw_info = grid.GetNinePieceDrawInfo(kBottomRightPiece, 1);
draw_info = grid.GetNinePieceDrawInfo(kBottomRightPiece);
EXPECT_EQ(tl_info.destination.Size(), draw_info.destination.Size());
}
......@@ -356,11 +361,11 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting) {
nine_piece.SetVerticalRule((ENinePieceImageRule)test_case.vertical_rule);
NinePieceImageGrid grid = NinePieceImageGrid(
nine_piece, test_case.image_size, test_case.border_image_area,
test_case.border_widths);
nine_piece, test_case.image_size, FloatSize(1, 1), 1,
test_case.border_image_area, test_case.border_widths);
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece, 1);
grid.GetNinePieceDrawInfo(piece);
EXPECT_EQ(test_case.pieces[piece].is_drawable, draw_info.is_drawable);
if (!test_case.pieces[piece].is_drawable)
continue;
......@@ -395,5 +400,70 @@ TEST_F(NinePieceImageGridTest, NinePieceImagePainting) {
}
}
TEST_F(NinePieceImageGridTest, NinePieceImagePainting_Zoomed) {
NinePieceImage nine_piece;
nine_piece.SetImage(GeneratedImage());
// Image slices are specified in CSS pixels.
nine_piece.SetImageSlices(LengthBox(10, 10, 10, 10));
nine_piece.SetFill(true);
IntSize image_size(50, 50);
IntRect border_image_area(0, 0, 200, 200);
IntRectOutsets border_widths(20, 20, 20, 20);
NinePieceImageGrid grid =
NinePieceImageGrid(nine_piece, image_size, FloatSize(2, 2), 2,
border_image_area, border_widths);
struct {
bool is_drawable;
bool is_corner_piece;
FloatRect destination;
FloatRect source;
float tile_scale_horizontal;
float tile_scale_vertical;
ENinePieceImageRule horizontal_rule;
ENinePieceImageRule vertical_rule;
} expected_pieces[kMaxPiece] = {
{true, true, FloatRect(0, 0, 20, 20), FloatRect(0, 0, 20, 20), 0, 0,
kStretchImageRule, kStretchImageRule},
{true, true, FloatRect(0, 180, 20, 20), FloatRect(0, 30, 20, 20), 0, 0,
kStretchImageRule, kStretchImageRule},
{true, false, FloatRect(0, 20, 20, 160), FloatRect(0, 20, 20, 10), 1, 1,
kStretchImageRule, kStretchImageRule},
{true, true, FloatRect(180, 0, 20, 20), FloatRect(30, 0, 20, 20), 0, 0,
kStretchImageRule, kStretchImageRule},
{true, true, FloatRect(180, 180, 20, 20), FloatRect(30, 30, 20, 20), 0, 0,
kStretchImageRule, kStretchImageRule},
{true, false, FloatRect(180, 20, 20, 160), FloatRect(30, 20, 20, 10), 1,
1, kStretchImageRule, kStretchImageRule},
{true, false, FloatRect(20, 0, 160, 20), FloatRect(20, 0, 10, 20), 1, 1,
kStretchImageRule, kStretchImageRule},
{true, false, FloatRect(20, 180, 160, 20), FloatRect(20, 30, 10, 20), 1,
1, kStretchImageRule, kStretchImageRule},
{true, false, FloatRect(20, 20, 160, 160), FloatRect(20, 20, 10, 10), 16,
16, kStretchImageRule, kStretchImageRule},
};
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece);
EXPECT_TRUE(draw_info.is_drawable);
const auto& expected = expected_pieces[piece];
EXPECT_EQ(draw_info.destination, expected.destination);
EXPECT_EQ(draw_info.source, expected.source);
if (expected.is_corner_piece)
continue;
EXPECT_FLOAT_EQ(draw_info.tile_scale.Width(),
expected.tile_scale_horizontal);
EXPECT_FLOAT_EQ(draw_info.tile_scale.Height(),
expected.tile_scale_vertical);
EXPECT_EQ(draw_info.tile_rule.vertical, expected.vertical_rule);
EXPECT_EQ(draw_info.tile_rule.horizontal, expected.horizontal_rule);
}
}
} // namespace
} // namespace blink
......@@ -80,18 +80,35 @@ void PaintPieces(GraphicsContext& context,
const ComputedStyle& style,
const NinePieceImage& nine_piece_image,
Image* image,
IntSize image_size,
const FloatSize& unzoomed_image_size,
PhysicalBoxSides sides_to_include) {
// |image_size| is in the image's native resolution and |slice_scale| defines
// the effective size of a CSS pixel in the image.
FloatSize image_size = image->SizeAsFloat(kRespectImageOrientation);
// Compute the scale factor to apply to the slice values by relating the
// zoomed size to the "unzoomed" (CSS pixel) size. For raster images this
// should match any DPR scale while for generated images it should match the
// effective zoom. (Modulo imprecisions introduced by the computation.) This
// scale should in theory be uniform.
FloatSize slice_scale(image_size.Width() / unzoomed_image_size.Width(),
image_size.Height() / unzoomed_image_size.Height());
// TODO(fs): Use FloatSize here to avoid additional rounding (leave that to
// NinePieceImageGrid if needed). For narrow slices the rounding can introduce
// large errors (fairly visible in the TC in crbug.com/596075 when zooming).
IntSize rounded_image_size = RoundedIntSize(image_size);
IntRectOutsets border_widths(style.BorderTopWidth(), style.BorderRightWidth(),
style.BorderBottomWidth(),
style.BorderLeftWidth());
NinePieceImageGrid grid(nine_piece_image, image_size,
PixelSnappedIntRect(border_image_rect), border_widths,
sides_to_include);
NinePieceImageGrid grid(
nine_piece_image, rounded_image_size, slice_scale, style.EffectiveZoom(),
PixelSnappedIntRect(border_image_rect), border_widths, sides_to_include);
ScopedInterpolationQuality interpolation_quality_scope(
context, style.GetInterpolationQuality());
for (NinePiece piece = kMinPiece; piece < kMaxPiece; ++piece) {
NinePieceImageGrid::NinePieceDrawInfo draw_info = grid.GetNinePieceDrawInfo(
piece, nine_piece_image.GetImage()->ImageScaleFactor());
NinePieceImageGrid::NinePieceDrawInfo draw_info =
grid.GetNinePieceDrawInfo(piece);
if (draw_info.is_drawable) {
if (draw_info.is_corner_piece) {
......@@ -125,10 +142,11 @@ void PaintPieces(GraphicsContext& context,
FloatSize tile_spacing(h_tile->spacing, v_tile->spacing);
// TODO(cavalcantii): see crbug.com/662507.
base::Optional<ScopedInterpolationQuality> interpolation_quality_scope;
base::Optional<ScopedInterpolationQuality>
interpolation_quality_override;
if (draw_info.tile_rule.horizontal == kRoundImageRule ||
draw_info.tile_rule.vertical == kRoundImageRule)
interpolation_quality_scope.emplace(context, kInterpolationMedium);
interpolation_quality_override.emplace(context, kInterpolationMedium);
context.DrawImageTiled(image, draw_info.destination, draw_info.source,
tile_scale_factor, tile_phase, tile_spacing);
......@@ -165,35 +183,33 @@ bool NinePieceImagePainter::Paint(GraphicsContext& graphics_context,
rect_with_outsets.Expand(style.ImageOutsets(nine_piece_image));
PhysicalRect border_image_rect = rect_with_outsets;
// NinePieceImage returns the image slices without effective zoom applied and
// thus we compute the nine piece grid on top of the image in unzoomed
// coordinates.
//
// FIXME: The default object size passed to imageSize() should be scaled by
// the zoom factor passed in. In this case it means that borderImageRect
// should be passed in compensated by effective zoom, since the scale factor
// is one. For generated images, the actual image data (gradient stops, etc.)
// are scaled to effective zoom instead so we must take care not to cause
// scale of them again.
IntSize image_size = RoundedIntSize(
style_image->ImageSize(document, 1, FloatSize(border_image_rect.size),
kRespectImageOrientation));
// Resolve the image size for any image that may need it (for example
// generated or SVG), then get an image using that size. This will yield an
// image with either "native" size (raster images) or size scaled by effective
// zoom.
const FloatSize default_object_size(border_image_rect.size);
FloatSize image_size =
style_image->ImageSize(document, style.EffectiveZoom(),
default_object_size, kRespectImageOrientation);
scoped_refptr<Image> image =
style_image->GetImage(observer, document, style, FloatSize(image_size));
style_image->GetImage(observer, document, style, image_size);
if (!image)
return true;
// Resolve the image size again, this time with a size-multiplier of one, to
// yield the size in CSS pixels. This is the unit/scale we expect the
// 'border-image-slice' values to be in.
FloatSize unzoomed_image_size = style_image->ImageSize(
document, 1, default_object_size.ScaledBy(1 / style.EffectiveZoom()),
kRespectImageOrientation);
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage",
"data",
inspector_paint_image_event::Data(node, *style_image,
FloatRect(image->Rect()),
FloatRect(border_image_rect)));
ScopedInterpolationQuality interpolation_quality_scope(
graphics_context, style.GetInterpolationQuality());
PaintPieces(graphics_context, border_image_rect, style, nine_piece_image,
image.get(), image_size, sides_to_include);
image.get(), unzoomed_image_size, sides_to_include);
return true;
}
......
<!DOCTYPE html>
<style>
body { margin: 0 }
.ref {
width: 80px;
height: 80px;
background-color: green
}
.spacer {
height: 80px
}
svg {
display: block
}
</style>
<svg xmlns="http://www.w3.org/2000/svg" width="160" viewBox="0 0 2 2">
<polygon points="0,1 1,0 2,1 1,2" fill="green"/>
</svg>
<div class="ref"></div>
<div class="spacer"></div>
<div class="ref"></div>
<!DOCTYPE html>
<style>
body {
margin: 0;
zoom: 4
}
div {
border-style: solid;
border-width: 20px;
width: 0
}
.t1 {
/* SVG paints a beveled rectangle. */
border-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="3" height="3"><polygon points="0,1 1,0 2,0 3,1, 3,2 2,3 1,3 0,2" fill="green"/></svg>') 1
}
.t2 {
/* SVG fills top left corner of nine piece image given a 40x40 default object size. */
border-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="40"><rect width="20" height="20" fill="green"/></svg>') 20;
}
.t3 {
/* SVG fills top left corner of nine piece image. */
border-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 3" width="30" height="30"><rect x="0" y="0" width="1" height="1" fill="green"/></svg>') 10;
width: 40px;
height: 40px;
border-bottom-width: 0;
border-right-width: 0
}
</style>
<div class="t1"></div>
<div class="t2"></div>
<div class="t3"></div>
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