1 /*
2  * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 2010 Dirk Schulze <krit@webkit.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h"
23 
24 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
25 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
26 #include "third_party/blink/renderer/platform/heap/heap.h"
27 #include "third_party/blink/renderer/platform/transforms/affine_transform.h"
28 #include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
29 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
30 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
31 
32 namespace blink {
33 
SVGPreserveAspectRatio()34 SVGPreserveAspectRatio::SVGPreserveAspectRatio() {
35   SetDefault();
36 }
37 
SetDefault()38 void SVGPreserveAspectRatio::SetDefault() {
39   align_ = kSvgPreserveaspectratioXmidymid;
40   meet_or_slice_ = kSvgMeetorsliceMeet;
41 }
42 
Clone() const43 SVGPreserveAspectRatio* SVGPreserveAspectRatio::Clone() const {
44   auto* preserve_aspect_ratio = MakeGarbageCollected<SVGPreserveAspectRatio>();
45 
46   preserve_aspect_ratio->align_ = align_;
47   preserve_aspect_ratio->meet_or_slice_ = meet_or_slice_;
48 
49   return preserve_aspect_ratio;
50 }
51 
52 template <typename CharType>
ParseInternal(const CharType * & ptr,const CharType * end,bool validate)53 SVGParsingError SVGPreserveAspectRatio::ParseInternal(const CharType*& ptr,
54                                                       const CharType* end,
55                                                       bool validate) {
56   SVGPreserveAspectRatioType align = kSvgPreserveaspectratioXmidymid;
57   SVGMeetOrSliceType meet_or_slice = kSvgMeetorsliceMeet;
58 
59   SetAlign(align);
60   SetMeetOrSlice(meet_or_slice);
61 
62   const CharType* start = ptr;
63   if (!SkipOptionalSVGSpaces(ptr, end))
64     return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
65 
66   if (*ptr == 'n') {
67     if (!SkipToken(ptr, end, "none"))
68       return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
69     align = kSvgPreserveaspectratioNone;
70     SkipOptionalSVGSpaces(ptr, end);
71   } else if (*ptr == 'x') {
72     if ((end - ptr) < 8)
73       return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
74     if (ptr[1] != 'M' || ptr[4] != 'Y' || ptr[5] != 'M')
75       return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
76     if (ptr[2] == 'i') {
77       if (ptr[3] == 'n') {
78         if (ptr[6] == 'i') {
79           if (ptr[7] == 'n')
80             align = kSvgPreserveaspectratioXminymin;
81           else if (ptr[7] == 'd')
82             align = kSvgPreserveaspectratioXminymid;
83           else
84             return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
85                                    ptr - start);
86         } else if (ptr[6] == 'a' && ptr[7] == 'x') {
87           align = kSvgPreserveaspectratioXminymax;
88         } else {
89           return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
90                                  ptr - start);
91         }
92       } else if (ptr[3] == 'd') {
93         if (ptr[6] == 'i') {
94           if (ptr[7] == 'n')
95             align = kSvgPreserveaspectratioXmidymin;
96           else if (ptr[7] == 'd')
97             align = kSvgPreserveaspectratioXmidymid;
98           else
99             return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
100                                    ptr - start);
101         } else if (ptr[6] == 'a' && ptr[7] == 'x') {
102           align = kSvgPreserveaspectratioXmidymax;
103         } else {
104           return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
105                                  ptr - start);
106         }
107       } else {
108         return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
109                                ptr - start);
110       }
111     } else if (ptr[2] == 'a' && ptr[3] == 'x') {
112       if (ptr[6] == 'i') {
113         if (ptr[7] == 'n')
114           align = kSvgPreserveaspectratioXmaxymin;
115         else if (ptr[7] == 'd')
116           align = kSvgPreserveaspectratioXmaxymid;
117         else
118           return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
119                                  ptr - start);
120       } else if (ptr[6] == 'a' && ptr[7] == 'x') {
121         align = kSvgPreserveaspectratioXmaxymax;
122       } else {
123         return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
124                                ptr - start);
125       }
126     } else {
127       return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
128     }
129     ptr += 8;
130     SkipOptionalSVGSpaces(ptr, end);
131   } else {
132     return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
133   }
134 
135   if (ptr < end) {
136     if (*ptr == 'm') {
137       if (!SkipToken(ptr, end, "meet"))
138         return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
139                                ptr - start);
140       SkipOptionalSVGSpaces(ptr, end);
141     } else if (*ptr == 's') {
142       if (!SkipToken(ptr, end, "slice"))
143         return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
144                                ptr - start);
145       SkipOptionalSVGSpaces(ptr, end);
146       if (align != kSvgPreserveaspectratioNone)
147         meet_or_slice = kSvgMeetorsliceSlice;
148     }
149   }
150 
151   if (end != ptr && validate)
152     return SVGParsingError(SVGParseStatus::kTrailingGarbage, ptr - start);
153 
154   SetAlign(align);
155   SetMeetOrSlice(meet_or_slice);
156 
157   return SVGParseStatus::kNoError;
158 }
159 
SetValueAsString(const String & string)160 SVGParsingError SVGPreserveAspectRatio::SetValueAsString(const String& string) {
161   SetDefault();
162 
163   if (string.IsEmpty())
164     return SVGParseStatus::kNoError;
165 
166   if (string.Is8Bit()) {
167     const LChar* ptr = string.Characters8();
168     const LChar* end = ptr + string.length();
169     return ParseInternal(ptr, end, true);
170   }
171   const UChar* ptr = string.Characters16();
172   const UChar* end = ptr + string.length();
173   return ParseInternal(ptr, end, true);
174 }
175 
Parse(const LChar * & ptr,const LChar * end,bool validate)176 bool SVGPreserveAspectRatio::Parse(const LChar*& ptr,
177                                    const LChar* end,
178                                    bool validate) {
179   return ParseInternal(ptr, end, validate) == SVGParseStatus::kNoError;
180 }
181 
Parse(const UChar * & ptr,const UChar * end,bool validate)182 bool SVGPreserveAspectRatio::Parse(const UChar*& ptr,
183                                    const UChar* end,
184                                    bool validate) {
185   return ParseInternal(ptr, end, validate) == SVGParseStatus::kNoError;
186 }
187 
TransformRect(FloatRect & dest_rect,FloatRect & src_rect) const188 void SVGPreserveAspectRatio::TransformRect(FloatRect& dest_rect,
189                                            FloatRect& src_rect) const {
190   if (align_ == kSvgPreserveaspectratioNone)
191     return;
192 
193   FloatSize image_size = src_rect.Size();
194   float orig_dest_width = dest_rect.Width();
195   float orig_dest_height = dest_rect.Height();
196   switch (meet_or_slice_) {
197     case SVGPreserveAspectRatio::kSvgMeetorsliceUnknown:
198       break;
199     case SVGPreserveAspectRatio::kSvgMeetorsliceMeet: {
200       float width_to_height_multiplier = src_rect.Height() / src_rect.Width();
201       if (orig_dest_height > orig_dest_width * width_to_height_multiplier) {
202         dest_rect.SetHeight(orig_dest_width * width_to_height_multiplier);
203         switch (align_) {
204           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymid:
205           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
206           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
207             dest_rect.SetY(dest_rect.Y() + orig_dest_height / 2 -
208                            dest_rect.Height() / 2);
209             break;
210           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymax:
211           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
212           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
213             dest_rect.SetY(dest_rect.Y() + orig_dest_height -
214                            dest_rect.Height());
215             break;
216           default:
217             break;
218         }
219       }
220       if (orig_dest_width > orig_dest_height / width_to_height_multiplier) {
221         dest_rect.SetWidth(orig_dest_height / width_to_height_multiplier);
222         switch (align_) {
223           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymin:
224           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
225           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
226             dest_rect.SetX(dest_rect.X() + orig_dest_width / 2 -
227                            dest_rect.Width() / 2);
228             break;
229           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymin:
230           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
231           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
232             dest_rect.SetX(dest_rect.X() + orig_dest_width - dest_rect.Width());
233             break;
234           default:
235             break;
236         }
237       }
238       break;
239     }
240     case SVGPreserveAspectRatio::kSvgMeetorsliceSlice: {
241       float width_to_height_multiplier = src_rect.Height() / src_rect.Width();
242       // If the destination height is less than the height of the image we'll be
243       // drawing.
244       if (orig_dest_height < orig_dest_width * width_to_height_multiplier) {
245         float dest_to_src_multiplier = src_rect.Width() / dest_rect.Width();
246         src_rect.SetHeight(dest_rect.Height() * dest_to_src_multiplier);
247         switch (align_) {
248           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymid:
249           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
250           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
251             src_rect.SetY(src_rect.Y() + image_size.Height() / 2 -
252                           src_rect.Height() / 2);
253             break;
254           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymax:
255           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
256           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
257             src_rect.SetY(src_rect.Y() + image_size.Height() -
258                           src_rect.Height());
259             break;
260           default:
261             break;
262         }
263       }
264       // If the destination width is less than the width of the image we'll be
265       // drawing.
266       if (orig_dest_width < orig_dest_height / width_to_height_multiplier) {
267         float dest_to_src_multiplier = src_rect.Height() / dest_rect.Height();
268         src_rect.SetWidth(dest_rect.Width() * dest_to_src_multiplier);
269         switch (align_) {
270           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymin:
271           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
272           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
273             src_rect.SetX(src_rect.X() + image_size.Width() / 2 -
274                           src_rect.Width() / 2);
275             break;
276           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymin:
277           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
278           case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
279             src_rect.SetX(src_rect.X() + image_size.Width() - src_rect.Width());
280             break;
281           default:
282             break;
283         }
284       }
285       break;
286     }
287   }
288 }
289 
ComputeTransform(float logical_x,float logical_y,float logical_width,float logical_height,float physical_width,float physical_height) const290 AffineTransform SVGPreserveAspectRatio::ComputeTransform(
291     float logical_x,
292     float logical_y,
293     float logical_width,
294     float logical_height,
295     float physical_width,
296     float physical_height) const {
297   DCHECK(logical_width);
298   DCHECK(logical_height);
299   DCHECK(physical_width);
300   DCHECK(physical_height);
301 
302   AffineTransform transform;
303   if (align_ == kSvgPreserveaspectratioUnknown)
304     return transform;
305 
306   double extended_logical_x = logical_x;
307   double extended_logical_y = logical_y;
308   double extended_logical_width = logical_width;
309   double extended_logical_height = logical_height;
310   double extended_physical_width = physical_width;
311   double extended_physical_height = physical_height;
312   double logical_ratio = extended_logical_width / extended_logical_height;
313   double physical_ratio = extended_physical_width / extended_physical_height;
314 
315   if (align_ == kSvgPreserveaspectratioNone) {
316     transform.ScaleNonUniform(
317         extended_physical_width / extended_logical_width,
318         extended_physical_height / extended_logical_height);
319     transform.Translate(-extended_logical_x, -extended_logical_y);
320     return transform;
321   }
322 
323   if ((logical_ratio < physical_ratio &&
324        (meet_or_slice_ == kSvgMeetorsliceMeet)) ||
325       (logical_ratio >= physical_ratio &&
326        (meet_or_slice_ == kSvgMeetorsliceSlice))) {
327     transform.ScaleNonUniform(
328         extended_physical_height / extended_logical_height,
329         extended_physical_height / extended_logical_height);
330 
331     if (align_ == kSvgPreserveaspectratioXminymin ||
332         align_ == kSvgPreserveaspectratioXminymid ||
333         align_ == kSvgPreserveaspectratioXminymax)
334       transform.Translate(-extended_logical_x, -extended_logical_y);
335     else if (align_ == kSvgPreserveaspectratioXmidymin ||
336              align_ == kSvgPreserveaspectratioXmidymid ||
337              align_ == kSvgPreserveaspectratioXmidymax)
338       transform.Translate(-extended_logical_x - (extended_logical_width -
339                                                  extended_physical_width *
340                                                      extended_logical_height /
341                                                      extended_physical_height) /
342                                                     2,
343                           -extended_logical_y);
344     else
345       transform.Translate(-extended_logical_x - (extended_logical_width -
346                                                  extended_physical_width *
347                                                      extended_logical_height /
348                                                      extended_physical_height),
349                           -extended_logical_y);
350 
351     return transform;
352   }
353 
354   transform.ScaleNonUniform(extended_physical_width / extended_logical_width,
355                             extended_physical_width / extended_logical_width);
356 
357   if (align_ == kSvgPreserveaspectratioXminymin ||
358       align_ == kSvgPreserveaspectratioXmidymin ||
359       align_ == kSvgPreserveaspectratioXmaxymin)
360     transform.Translate(-extended_logical_x, -extended_logical_y);
361   else if (align_ == kSvgPreserveaspectratioXminymid ||
362            align_ == kSvgPreserveaspectratioXmidymid ||
363            align_ == kSvgPreserveaspectratioXmaxymid)
364     transform.Translate(-extended_logical_x,
365                         -extended_logical_y -
366                             (extended_logical_height -
367                              extended_physical_height * extended_logical_width /
368                                  extended_physical_width) /
369                                 2);
370   else
371     transform.Translate(-extended_logical_x,
372                         -extended_logical_y -
373                             (extended_logical_height -
374                              extended_physical_height * extended_logical_width /
375                                  extended_physical_width));
376 
377   return transform;
378 }
379 
ValueAsString() const380 String SVGPreserveAspectRatio::ValueAsString() const {
381   StringBuilder builder;
382 
383   const char* align_string = "";
384   switch (align_) {
385     case kSvgPreserveaspectratioNone:
386       align_string = "none";
387       break;
388     case kSvgPreserveaspectratioXminymin:
389       align_string = "xMinYMin";
390       break;
391     case kSvgPreserveaspectratioXmidymin:
392       align_string = "xMidYMin";
393       break;
394     case kSvgPreserveaspectratioXmaxymin:
395       align_string = "xMaxYMin";
396       break;
397     case kSvgPreserveaspectratioXminymid:
398       align_string = "xMinYMid";
399       break;
400     case kSvgPreserveaspectratioXmidymid:
401       align_string = "xMidYMid";
402       break;
403     case kSvgPreserveaspectratioXmaxymid:
404       align_string = "xMaxYMid";
405       break;
406     case kSvgPreserveaspectratioXminymax:
407       align_string = "xMinYMax";
408       break;
409     case kSvgPreserveaspectratioXmidymax:
410       align_string = "xMidYMax";
411       break;
412     case kSvgPreserveaspectratioXmaxymax:
413       align_string = "xMaxYMax";
414       break;
415     case kSvgPreserveaspectratioUnknown:
416       align_string = "unknown";
417       break;
418   }
419   builder.Append(align_string);
420 
421   const char* meet_or_slice_string = "";
422   switch (meet_or_slice_) {
423     default:
424     case kSvgMeetorsliceUnknown:
425       break;
426     case kSvgMeetorsliceMeet:
427       meet_or_slice_string = " meet";
428       break;
429     case kSvgMeetorsliceSlice:
430       meet_or_slice_string = " slice";
431       break;
432   }
433   builder.Append(meet_or_slice_string);
434   return builder.ToString();
435 }
436 
Add(SVGPropertyBase * other,SVGElement *)437 void SVGPreserveAspectRatio::Add(SVGPropertyBase* other, SVGElement*) {
438   NOTREACHED();
439 }
440 
CalculateAnimatedValue(const SVGAnimateElement & animation_element,float percentage,unsigned repeat_count,SVGPropertyBase * from_value,SVGPropertyBase * to_value,SVGPropertyBase *,SVGElement *)441 void SVGPreserveAspectRatio::CalculateAnimatedValue(
442     const SVGAnimateElement& animation_element,
443     float percentage,
444     unsigned repeat_count,
445     SVGPropertyBase* from_value,
446     SVGPropertyBase* to_value,
447     SVGPropertyBase*,
448     SVGElement*) {
449   NOTREACHED();
450 }
451 
CalculateDistance(SVGPropertyBase * to_value,SVGElement * context_element)452 float SVGPreserveAspectRatio::CalculateDistance(SVGPropertyBase* to_value,
453                                                 SVGElement* context_element) {
454   // No paced animations for SVGPreserveAspectRatio.
455   return -1;
456 }
457 
458 }  // namespace blink
459