1 /*
2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials
14 * provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27 * OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "third_party/blink/renderer/core/layout/shapes/shape.h"
31
32 #include <algorithm>
33 #include <memory>
34 #include <utility>
35
36 #include "third_party/blink/public/platform/platform.h"
37 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
38 #include "third_party/blink/renderer/core/layout/shapes/box_shape.h"
39 #include "third_party/blink/renderer/core/layout/shapes/polygon_shape.h"
40 #include "third_party/blink/renderer/core/layout/shapes/raster_shape.h"
41 #include "third_party/blink/renderer/core/layout/shapes/rectangle_shape.h"
42 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
43 #include "third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h"
44 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
45 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
46 #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
47 #include "third_party/blink/renderer/platform/geometry/float_size.h"
48 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
49 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
50 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
51 #include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
52 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
53 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
54 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
55 #include "third_party/skia/include/core/SkSurface.h"
56
57 namespace blink {
58
CreateInsetShape(const FloatRoundedRect & bounds)59 static std::unique_ptr<Shape> CreateInsetShape(const FloatRoundedRect& bounds) {
60 DCHECK_GE(bounds.Rect().Width(), 0);
61 DCHECK_GE(bounds.Rect().Height(), 0);
62 return std::make_unique<BoxShape>(bounds);
63 }
64
CreateCircleShape(const FloatPoint & center,float radius)65 static std::unique_ptr<Shape> CreateCircleShape(const FloatPoint& center,
66 float radius) {
67 DCHECK_GE(radius, 0);
68 return std::make_unique<RectangleShape>(
69 FloatRect(center.X() - radius, center.Y() - radius, radius * 2,
70 radius * 2),
71 FloatSize(radius, radius));
72 }
73
CreateEllipseShape(const FloatPoint & center,const FloatSize & radii)74 static std::unique_ptr<Shape> CreateEllipseShape(const FloatPoint& center,
75 const FloatSize& radii) {
76 DCHECK_GE(radii.Width(), 0);
77 DCHECK_GE(radii.Height(), 0);
78 return std::make_unique<RectangleShape>(
79 FloatRect(center.X() - radii.Width(), center.Y() - radii.Height(),
80 radii.Width() * 2, radii.Height() * 2),
81 radii);
82 }
83
CreatePolygonShape(Vector<FloatPoint> vertices,WindRule fill_rule)84 static std::unique_ptr<Shape> CreatePolygonShape(Vector<FloatPoint> vertices,
85 WindRule fill_rule) {
86 return std::make_unique<PolygonShape>(std::move(vertices), fill_rule);
87 }
88
PhysicalRectToLogical(const FloatRect & rect,float logical_box_height,WritingMode writing_mode)89 static inline FloatRect PhysicalRectToLogical(const FloatRect& rect,
90 float logical_box_height,
91 WritingMode writing_mode) {
92 if (IsHorizontalWritingMode(writing_mode))
93 return rect;
94 if (IsFlippedBlocksWritingMode(writing_mode))
95 return FloatRect(rect.Y(), logical_box_height - rect.MaxX(), rect.Height(),
96 rect.Width());
97 return rect.TransposedRect();
98 }
99
PhysicalPointToLogical(const FloatPoint & point,float logical_box_height,WritingMode writing_mode)100 static inline FloatPoint PhysicalPointToLogical(const FloatPoint& point,
101 float logical_box_height,
102 WritingMode writing_mode) {
103 if (IsHorizontalWritingMode(writing_mode))
104 return point;
105 if (IsFlippedBlocksWritingMode(writing_mode))
106 return FloatPoint(point.Y(), logical_box_height - point.X());
107 return point.TransposedPoint();
108 }
109
PhysicalSizeToLogical(const FloatSize & size,WritingMode writing_mode)110 static inline FloatSize PhysicalSizeToLogical(const FloatSize& size,
111 WritingMode writing_mode) {
112 if (IsHorizontalWritingMode(writing_mode))
113 return size;
114 return size.TransposedSize();
115 }
116
CreateShape(const BasicShape * basic_shape,const LayoutSize & logical_box_size,WritingMode writing_mode,float margin)117 std::unique_ptr<Shape> Shape::CreateShape(const BasicShape* basic_shape,
118 const LayoutSize& logical_box_size,
119 WritingMode writing_mode,
120 float margin) {
121 DCHECK(basic_shape);
122
123 bool horizontal_writing_mode = IsHorizontalWritingMode(writing_mode);
124 float box_width = horizontal_writing_mode
125 ? logical_box_size.Width().ToFloat()
126 : logical_box_size.Height().ToFloat();
127 float box_height = horizontal_writing_mode
128 ? logical_box_size.Height().ToFloat()
129 : logical_box_size.Width().ToFloat();
130 std::unique_ptr<Shape> shape;
131
132 switch (basic_shape->GetType()) {
133 case BasicShape::kBasicShapeCircleType: {
134 const BasicShapeCircle* circle = To<BasicShapeCircle>(basic_shape);
135 FloatPoint center =
136 FloatPointForCenterCoordinate(circle->CenterX(), circle->CenterY(),
137 FloatSize(box_width, box_height));
138 float radius =
139 circle->FloatValueForRadiusInBox(FloatSize(box_width, box_height));
140 FloatPoint logical_center = PhysicalPointToLogical(
141 center, logical_box_size.Height().ToFloat(), writing_mode);
142
143 shape = CreateCircleShape(logical_center, radius);
144 break;
145 }
146
147 case BasicShape::kBasicShapeEllipseType: {
148 const BasicShapeEllipse* ellipse = To<BasicShapeEllipse>(basic_shape);
149 FloatPoint center =
150 FloatPointForCenterCoordinate(ellipse->CenterX(), ellipse->CenterY(),
151 FloatSize(box_width, box_height));
152 float radius_x = ellipse->FloatValueForRadiusInBox(ellipse->RadiusX(),
153 center.X(), box_width);
154 float radius_y = ellipse->FloatValueForRadiusInBox(
155 ellipse->RadiusY(), center.Y(), box_height);
156 FloatPoint logical_center = PhysicalPointToLogical(
157 center, logical_box_size.Height().ToFloat(), writing_mode);
158
159 shape = CreateEllipseShape(logical_center, FloatSize(radius_x, radius_y));
160 break;
161 }
162
163 case BasicShape::kBasicShapePolygonType: {
164 const BasicShapePolygon* polygon = To<BasicShapePolygon>(basic_shape);
165 const Vector<Length>& values = polygon->Values();
166 wtf_size_t values_size = values.size();
167 DCHECK(!(values_size % 2));
168 Vector<FloatPoint> vertices(values_size / 2);
169 for (wtf_size_t i = 0; i < values_size; i += 2) {
170 FloatPoint vertex(FloatValueForLength(values.at(i), box_width),
171 FloatValueForLength(values.at(i + 1), box_height));
172 vertices[i / 2] = PhysicalPointToLogical(
173 vertex, logical_box_size.Height().ToFloat(), writing_mode);
174 }
175 shape = CreatePolygonShape(std::move(vertices), polygon->GetWindRule());
176 break;
177 }
178
179 case BasicShape::kBasicShapeInsetType: {
180 const BasicShapeInset& inset = *To<BasicShapeInset>(basic_shape);
181 float left = FloatValueForLength(inset.Left(), box_width);
182 float top = FloatValueForLength(inset.Top(), box_height);
183 float right = FloatValueForLength(inset.Right(), box_width);
184 float bottom = FloatValueForLength(inset.Bottom(), box_height);
185 FloatRect rect(left, top, std::max<float>(box_width - left - right, 0),
186 std::max<float>(box_height - top - bottom, 0));
187 FloatRect logical_rect = PhysicalRectToLogical(
188 rect, logical_box_size.Height().ToFloat(), writing_mode);
189
190 FloatSize box_size(box_width, box_height);
191 FloatSize top_left_radius = PhysicalSizeToLogical(
192 FloatSizeForLengthSize(inset.TopLeftRadius(), box_size),
193 writing_mode);
194 FloatSize top_right_radius = PhysicalSizeToLogical(
195 FloatSizeForLengthSize(inset.TopRightRadius(), box_size),
196 writing_mode);
197 FloatSize bottom_left_radius = PhysicalSizeToLogical(
198 FloatSizeForLengthSize(inset.BottomLeftRadius(), box_size),
199 writing_mode);
200 FloatSize bottom_right_radius = PhysicalSizeToLogical(
201 FloatSizeForLengthSize(inset.BottomRightRadius(), box_size),
202 writing_mode);
203 FloatRoundedRect::Radii corner_radii(top_left_radius, top_right_radius,
204 bottom_left_radius,
205 bottom_right_radius);
206
207 FloatRoundedRect final_rect(logical_rect, corner_radii);
208 final_rect.ConstrainRadii();
209
210 shape = CreateInsetShape(final_rect);
211 break;
212 }
213
214 default:
215 NOTREACHED();
216 }
217
218 shape->writing_mode_ = writing_mode;
219 shape->margin_ = margin;
220
221 return shape;
222 }
223
CreateEmptyRasterShape(WritingMode writing_mode,float margin)224 std::unique_ptr<Shape> Shape::CreateEmptyRasterShape(WritingMode writing_mode,
225 float margin) {
226 std::unique_ptr<RasterShapeIntervals> intervals =
227 std::make_unique<RasterShapeIntervals>(0, 0);
228 std::unique_ptr<RasterShape> raster_shape =
229 std::make_unique<RasterShape>(std::move(intervals), IntSize());
230 raster_shape->writing_mode_ = writing_mode;
231 raster_shape->margin_ = margin;
232 return std::move(raster_shape);
233 }
234
ExtractImageData(Image * image,const IntSize & image_size,ArrayBufferContents & contents,RespectImageOrientationEnum respect_orientation)235 static bool ExtractImageData(Image* image,
236 const IntSize& image_size,
237 ArrayBufferContents& contents,
238 RespectImageOrientationEnum respect_orientation) {
239 if (!image)
240 return false;
241
242 CanvasColorParams color_params;
243 SkImageInfo info = SkImageInfo::Make(
244 image_size.Width(), image_size.Height(), color_params.GetSkColorType(),
245 color_params.GetSkAlphaType(),
246 color_params.GetSkColorSpaceForSkSurfaces());
247 sk_sp<SkSurface> surface =
248 SkSurface::MakeRaster(info, color_params.GetSkSurfaceProps());
249
250 if (!surface)
251 return false;
252
253 // FIXME: This is not totally correct but it is needed to prevent shapes
254 // that loads SVG Images during paint invalidations to mark layoutObjects
255 // for layout, which is not allowed. See https://crbug.com/429346
256 ImageObserverDisabler disabler(image);
257 PaintFlags flags;
258 FloatRect image_source_rect(FloatPoint(), FloatSize(image->Size()));
259 IntRect image_dest_rect(IntPoint(), image_size);
260 SkiaPaintCanvas canvas(surface->getCanvas());
261 canvas.clear(SK_ColorTRANSPARENT);
262
263 image->Draw(&canvas, flags, FloatRect(image_dest_rect), image_source_rect,
264 respect_orientation, Image::kDoNotClampImageToSourceRect,
265 Image::kSyncDecode);
266
267 size_t size_in_bytes;
268 if (!StaticBitmapImage::GetSizeInBytes(image_dest_rect, color_params)
269 .AssignIfValid(&size_in_bytes) ||
270 size_in_bytes > v8::TypedArray::kMaxLength) {
271 return false;
272 }
273 ArrayBufferContents result(size_in_bytes, 1, ArrayBufferContents::kNotShared,
274 ArrayBufferContents::kZeroInitialize);
275 if (result.DataLength() != size_in_bytes)
276 return false;
277 result.Transfer(contents);
278
279 return StaticBitmapImage::CopyToByteArray(
280 UnacceleratedStaticBitmapImage::Create(surface->makeImageSnapshot()),
281 base::span<uint8_t>(reinterpret_cast<uint8_t*>(contents.Data()),
282 contents.DataLength()),
283 image_dest_rect, color_params);
284 }
285
ExtractIntervalsFromImageData(ArrayBufferContents & contents,float threshold,const IntRect & image_rect,const IntRect & margin_rect)286 static std::unique_ptr<RasterShapeIntervals> ExtractIntervalsFromImageData(
287 ArrayBufferContents& contents,
288 float threshold,
289 const IntRect& image_rect,
290 const IntRect& margin_rect) {
291 DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(contents);
292 DOMUint8ClampedArray* pixel_array = DOMUint8ClampedArray::Create(
293 array_buffer, 0, array_buffer->ByteLengthAsSizeT());
294
295 unsigned pixel_array_offset = 3; // Each pixel is four bytes: RGBA.
296 uint8_t alpha_pixel_threshold = threshold * 255;
297
298 DCHECK_EQ(image_rect.Size().Area() * 4, pixel_array->lengthAsSizeT());
299
300 int min_buffer_y = std::max(0, margin_rect.Y() - image_rect.Y());
301 int max_buffer_y =
302 std::min(image_rect.Height(), margin_rect.MaxY() - image_rect.Y());
303
304 std::unique_ptr<RasterShapeIntervals> intervals =
305 std::make_unique<RasterShapeIntervals>(margin_rect.Height(),
306 -margin_rect.Y());
307
308 for (int y = min_buffer_y; y < max_buffer_y; ++y) {
309 int start_x = -1;
310 for (int x = 0; x < image_rect.Width(); ++x, pixel_array_offset += 4) {
311 uint8_t alpha = pixel_array->Item(pixel_array_offset);
312 bool alpha_above_threshold = alpha > alpha_pixel_threshold;
313 if (start_x == -1 && alpha_above_threshold) {
314 start_x = x;
315 } else if (start_x != -1 &&
316 (!alpha_above_threshold || x == image_rect.Width() - 1)) {
317 int end_x = alpha_above_threshold ? x + 1 : x;
318 intervals->IntervalAt(y + image_rect.Y())
319 .Unite(IntShapeInterval(start_x + image_rect.X(),
320 end_x + image_rect.X()));
321 start_x = -1;
322 }
323 }
324 }
325 return intervals;
326 }
327
IsValidRasterShapeSize(const IntSize & size)328 static bool IsValidRasterShapeSize(const IntSize& size) {
329 // Some platforms don't limit MaxDecodedImageBytes.
330 constexpr size_t size32_max_bytes = 0xFFFFFFFF / 4;
331 static const size_t max_image_size_bytes =
332 std::min(size32_max_bytes, Platform::Current()->MaxDecodedImageBytes());
333 return size.Area() * 4 < max_image_size_bytes;
334 }
335
CreateRasterShape(Image * image,float threshold,const LayoutRect & image_r,const LayoutRect & margin_r,WritingMode writing_mode,float margin,RespectImageOrientationEnum respect_orientation)336 std::unique_ptr<Shape> Shape::CreateRasterShape(
337 Image* image,
338 float threshold,
339 const LayoutRect& image_r,
340 const LayoutRect& margin_r,
341 WritingMode writing_mode,
342 float margin,
343 RespectImageOrientationEnum respect_orientation) {
344 IntRect image_rect = PixelSnappedIntRect(image_r);
345 IntRect margin_rect = PixelSnappedIntRect(margin_r);
346
347 if (!IsValidRasterShapeSize(margin_rect.Size()) ||
348 !IsValidRasterShapeSize(image_rect.Size())) {
349 return CreateEmptyRasterShape(writing_mode, margin);
350 }
351
352 ArrayBufferContents contents;
353 if (!ExtractImageData(image, image_rect.Size(), contents,
354 respect_orientation)) {
355 return CreateEmptyRasterShape(writing_mode, margin);
356 }
357
358 std::unique_ptr<RasterShapeIntervals> intervals =
359 ExtractIntervalsFromImageData(contents, threshold, image_rect,
360 margin_rect);
361 std::unique_ptr<RasterShape> raster_shape =
362 std::make_unique<RasterShape>(std::move(intervals), margin_rect.Size());
363 raster_shape->writing_mode_ = writing_mode;
364 raster_shape->margin_ = margin;
365 return std::move(raster_shape);
366 }
367
CreateLayoutBoxShape(const FloatRoundedRect & rounded_rect,WritingMode writing_mode,float margin)368 std::unique_ptr<Shape> Shape::CreateLayoutBoxShape(
369 const FloatRoundedRect& rounded_rect,
370 WritingMode writing_mode,
371 float margin) {
372 FloatRect rect(0, 0, rounded_rect.Rect().Width(),
373 rounded_rect.Rect().Height());
374 FloatRoundedRect bounds(rect, rounded_rect.GetRadii());
375 std::unique_ptr<Shape> shape = CreateInsetShape(bounds);
376 shape->writing_mode_ = writing_mode;
377 shape->margin_ = margin;
378
379 return shape;
380 }
381
382 } // namespace blink
383