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