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/character_visitor.h"
29 #include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
30 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
31 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
32
33 namespace blink {
34
SVGPreserveAspectRatio()35 SVGPreserveAspectRatio::SVGPreserveAspectRatio() {
36 SetDefault();
37 }
38
SetDefault()39 void SVGPreserveAspectRatio::SetDefault() {
40 align_ = kSvgPreserveaspectratioXmidymid;
41 meet_or_slice_ = kSvgMeetorsliceMeet;
42 }
43
Clone() const44 SVGPreserveAspectRatio* SVGPreserveAspectRatio::Clone() const {
45 auto* preserve_aspect_ratio = MakeGarbageCollected<SVGPreserveAspectRatio>();
46
47 preserve_aspect_ratio->align_ = align_;
48 preserve_aspect_ratio->meet_or_slice_ = meet_or_slice_;
49
50 return preserve_aspect_ratio;
51 }
52
53 template <typename CharType>
ParseInternal(const CharType * & ptr,const CharType * end,bool validate)54 SVGParsingError SVGPreserveAspectRatio::ParseInternal(const CharType*& ptr,
55 const CharType* end,
56 bool validate) {
57 SVGPreserveAspectRatioType align = kSvgPreserveaspectratioXmidymid;
58 SVGMeetOrSliceType meet_or_slice = kSvgMeetorsliceMeet;
59
60 SetAlign(align);
61 SetMeetOrSlice(meet_or_slice);
62
63 const CharType* start = ptr;
64 if (!SkipOptionalSVGSpaces(ptr, end))
65 return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
66
67 if (*ptr == 'n') {
68 if (!SkipToken(ptr, end, "none"))
69 return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
70 align = kSvgPreserveaspectratioNone;
71 SkipOptionalSVGSpaces(ptr, end);
72 } else if (*ptr == 'x') {
73 if ((end - ptr) < 8)
74 return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
75 if (ptr[1] != 'M' || ptr[4] != 'Y' || ptr[5] != 'M')
76 return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
77 if (ptr[2] == 'i') {
78 if (ptr[3] == 'n') {
79 if (ptr[6] == 'i') {
80 if (ptr[7] == 'n')
81 align = kSvgPreserveaspectratioXminymin;
82 else if (ptr[7] == 'd')
83 align = kSvgPreserveaspectratioXminymid;
84 else
85 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
86 ptr - start);
87 } else if (ptr[6] == 'a' && ptr[7] == 'x') {
88 align = kSvgPreserveaspectratioXminymax;
89 } else {
90 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
91 ptr - start);
92 }
93 } else if (ptr[3] == 'd') {
94 if (ptr[6] == 'i') {
95 if (ptr[7] == 'n')
96 align = kSvgPreserveaspectratioXmidymin;
97 else if (ptr[7] == 'd')
98 align = kSvgPreserveaspectratioXmidymid;
99 else
100 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
101 ptr - start);
102 } else if (ptr[6] == 'a' && ptr[7] == 'x') {
103 align = kSvgPreserveaspectratioXmidymax;
104 } else {
105 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
106 ptr - start);
107 }
108 } else {
109 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
110 ptr - start);
111 }
112 } else if (ptr[2] == 'a' && ptr[3] == 'x') {
113 if (ptr[6] == 'i') {
114 if (ptr[7] == 'n')
115 align = kSvgPreserveaspectratioXmaxymin;
116 else if (ptr[7] == 'd')
117 align = kSvgPreserveaspectratioXmaxymid;
118 else
119 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
120 ptr - start);
121 } else if (ptr[6] == 'a' && ptr[7] == 'x') {
122 align = kSvgPreserveaspectratioXmaxymax;
123 } else {
124 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
125 ptr - start);
126 }
127 } else {
128 return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
129 }
130 ptr += 8;
131 SkipOptionalSVGSpaces(ptr, end);
132 } else {
133 return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start);
134 }
135
136 if (ptr < end) {
137 if (*ptr == 'm') {
138 if (!SkipToken(ptr, end, "meet"))
139 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
140 ptr - start);
141 SkipOptionalSVGSpaces(ptr, end);
142 } else if (*ptr == 's') {
143 if (!SkipToken(ptr, end, "slice"))
144 return SVGParsingError(SVGParseStatus::kExpectedEnumeration,
145 ptr - start);
146 SkipOptionalSVGSpaces(ptr, end);
147 if (align != kSvgPreserveaspectratioNone)
148 meet_or_slice = kSvgMeetorsliceSlice;
149 }
150 }
151
152 if (end != ptr && validate)
153 return SVGParsingError(SVGParseStatus::kTrailingGarbage, ptr - start);
154
155 SetAlign(align);
156 SetMeetOrSlice(meet_or_slice);
157
158 return SVGParseStatus::kNoError;
159 }
160
SetValueAsString(const String & string)161 SVGParsingError SVGPreserveAspectRatio::SetValueAsString(const String& string) {
162 SetDefault();
163
164 if (string.IsEmpty())
165 return SVGParseStatus::kNoError;
166
167 return WTF::VisitCharacters(string, [&](const auto* chars, unsigned length) {
168 return ParseInternal(chars, chars + length, true);
169 });
170 }
171
Parse(const LChar * & ptr,const LChar * end,bool validate)172 bool SVGPreserveAspectRatio::Parse(const LChar*& ptr,
173 const LChar* end,
174 bool validate) {
175 return ParseInternal(ptr, end, validate) == SVGParseStatus::kNoError;
176 }
177
Parse(const UChar * & ptr,const UChar * end,bool validate)178 bool SVGPreserveAspectRatio::Parse(const UChar*& ptr,
179 const UChar* end,
180 bool validate) {
181 return ParseInternal(ptr, end, validate) == SVGParseStatus::kNoError;
182 }
183
TransformRect(FloatRect & dest_rect,FloatRect & src_rect) const184 void SVGPreserveAspectRatio::TransformRect(FloatRect& dest_rect,
185 FloatRect& src_rect) const {
186 if (align_ == kSvgPreserveaspectratioNone)
187 return;
188
189 FloatSize image_size = src_rect.Size();
190 float orig_dest_width = dest_rect.Width();
191 float orig_dest_height = dest_rect.Height();
192 switch (meet_or_slice_) {
193 case SVGPreserveAspectRatio::kSvgMeetorsliceUnknown:
194 break;
195 case SVGPreserveAspectRatio::kSvgMeetorsliceMeet: {
196 float width_to_height_multiplier = src_rect.Height() / src_rect.Width();
197 if (orig_dest_height > orig_dest_width * width_to_height_multiplier) {
198 dest_rect.SetHeight(orig_dest_width * width_to_height_multiplier);
199 switch (align_) {
200 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymid:
201 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
202 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
203 dest_rect.SetY(dest_rect.Y() + orig_dest_height / 2 -
204 dest_rect.Height() / 2);
205 break;
206 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymax:
207 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
208 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
209 dest_rect.SetY(dest_rect.Y() + orig_dest_height -
210 dest_rect.Height());
211 break;
212 default:
213 break;
214 }
215 }
216 if (orig_dest_width > orig_dest_height / width_to_height_multiplier) {
217 dest_rect.SetWidth(orig_dest_height / width_to_height_multiplier);
218 switch (align_) {
219 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymin:
220 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
221 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
222 dest_rect.SetX(dest_rect.X() + orig_dest_width / 2 -
223 dest_rect.Width() / 2);
224 break;
225 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymin:
226 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
227 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
228 dest_rect.SetX(dest_rect.X() + orig_dest_width - dest_rect.Width());
229 break;
230 default:
231 break;
232 }
233 }
234 break;
235 }
236 case SVGPreserveAspectRatio::kSvgMeetorsliceSlice: {
237 float width_to_height_multiplier = src_rect.Height() / src_rect.Width();
238 // If the destination height is less than the height of the image we'll be
239 // drawing.
240 if (orig_dest_height < orig_dest_width * width_to_height_multiplier) {
241 float dest_to_src_multiplier = src_rect.Width() / dest_rect.Width();
242 src_rect.SetHeight(dest_rect.Height() * dest_to_src_multiplier);
243 switch (align_) {
244 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymid:
245 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
246 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
247 src_rect.SetY(src_rect.Y() + image_size.Height() / 2 -
248 src_rect.Height() / 2);
249 break;
250 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXminymax:
251 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
252 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
253 src_rect.SetY(src_rect.Y() + image_size.Height() -
254 src_rect.Height());
255 break;
256 default:
257 break;
258 }
259 }
260 // If the destination width is less than the width of the image we'll be
261 // drawing.
262 if (orig_dest_width < orig_dest_height / width_to_height_multiplier) {
263 float dest_to_src_multiplier = src_rect.Height() / dest_rect.Height();
264 src_rect.SetWidth(dest_rect.Width() * dest_to_src_multiplier);
265 switch (align_) {
266 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymin:
267 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymid:
268 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmidymax:
269 src_rect.SetX(src_rect.X() + image_size.Width() / 2 -
270 src_rect.Width() / 2);
271 break;
272 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymin:
273 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymid:
274 case SVGPreserveAspectRatio::kSvgPreserveaspectratioXmaxymax:
275 src_rect.SetX(src_rect.X() + image_size.Width() - src_rect.Width());
276 break;
277 default:
278 break;
279 }
280 }
281 break;
282 }
283 }
284 }
285
ComputeTransform(const FloatRect & view_box,const FloatSize & viewport_size) const286 AffineTransform SVGPreserveAspectRatio::ComputeTransform(
287 const FloatRect& view_box,
288 const FloatSize& viewport_size) const {
289 DCHECK(!view_box.IsEmpty());
290 DCHECK(!viewport_size.IsEmpty());
291 DCHECK_NE(align_, kSvgPreserveaspectratioUnknown);
292
293 double extended_logical_x = view_box.X();
294 double extended_logical_y = view_box.Y();
295 double extended_logical_width = view_box.Width();
296 double extended_logical_height = view_box.Height();
297 double extended_physical_width = viewport_size.Width();
298 double extended_physical_height = viewport_size.Height();
299
300 AffineTransform transform;
301 if (align_ == kSvgPreserveaspectratioNone) {
302 transform.ScaleNonUniform(
303 extended_physical_width / extended_logical_width,
304 extended_physical_height / extended_logical_height);
305 transform.Translate(-extended_logical_x, -extended_logical_y);
306 return transform;
307 }
308
309 double logical_ratio = extended_logical_width / extended_logical_height;
310 double physical_ratio = extended_physical_width / extended_physical_height;
311 if ((logical_ratio < physical_ratio &&
312 (meet_or_slice_ == kSvgMeetorsliceMeet)) ||
313 (logical_ratio >= physical_ratio &&
314 (meet_or_slice_ == kSvgMeetorsliceSlice))) {
315 transform.ScaleNonUniform(
316 extended_physical_height / extended_logical_height,
317 extended_physical_height / extended_logical_height);
318
319 if (align_ == kSvgPreserveaspectratioXminymin ||
320 align_ == kSvgPreserveaspectratioXminymid ||
321 align_ == kSvgPreserveaspectratioXminymax)
322 transform.Translate(-extended_logical_x, -extended_logical_y);
323 else if (align_ == kSvgPreserveaspectratioXmidymin ||
324 align_ == kSvgPreserveaspectratioXmidymid ||
325 align_ == kSvgPreserveaspectratioXmidymax)
326 transform.Translate(-extended_logical_x - (extended_logical_width -
327 extended_physical_width *
328 extended_logical_height /
329 extended_physical_height) /
330 2,
331 -extended_logical_y);
332 else
333 transform.Translate(-extended_logical_x - (extended_logical_width -
334 extended_physical_width *
335 extended_logical_height /
336 extended_physical_height),
337 -extended_logical_y);
338
339 return transform;
340 }
341
342 transform.ScaleNonUniform(extended_physical_width / extended_logical_width,
343 extended_physical_width / extended_logical_width);
344
345 if (align_ == kSvgPreserveaspectratioXminymin ||
346 align_ == kSvgPreserveaspectratioXmidymin ||
347 align_ == kSvgPreserveaspectratioXmaxymin)
348 transform.Translate(-extended_logical_x, -extended_logical_y);
349 else if (align_ == kSvgPreserveaspectratioXminymid ||
350 align_ == kSvgPreserveaspectratioXmidymid ||
351 align_ == kSvgPreserveaspectratioXmaxymid)
352 transform.Translate(-extended_logical_x,
353 -extended_logical_y -
354 (extended_logical_height -
355 extended_physical_height * extended_logical_width /
356 extended_physical_width) /
357 2);
358 else
359 transform.Translate(-extended_logical_x,
360 -extended_logical_y -
361 (extended_logical_height -
362 extended_physical_height * extended_logical_width /
363 extended_physical_width));
364
365 return transform;
366 }
367
ValueAsString() const368 String SVGPreserveAspectRatio::ValueAsString() const {
369 StringBuilder builder;
370
371 const char* align_string = "";
372 switch (align_) {
373 case kSvgPreserveaspectratioNone:
374 align_string = "none";
375 break;
376 case kSvgPreserveaspectratioXminymin:
377 align_string = "xMinYMin";
378 break;
379 case kSvgPreserveaspectratioXmidymin:
380 align_string = "xMidYMin";
381 break;
382 case kSvgPreserveaspectratioXmaxymin:
383 align_string = "xMaxYMin";
384 break;
385 case kSvgPreserveaspectratioXminymid:
386 align_string = "xMinYMid";
387 break;
388 case kSvgPreserveaspectratioXmidymid:
389 align_string = "xMidYMid";
390 break;
391 case kSvgPreserveaspectratioXmaxymid:
392 align_string = "xMaxYMid";
393 break;
394 case kSvgPreserveaspectratioXminymax:
395 align_string = "xMinYMax";
396 break;
397 case kSvgPreserveaspectratioXmidymax:
398 align_string = "xMidYMax";
399 break;
400 case kSvgPreserveaspectratioXmaxymax:
401 align_string = "xMaxYMax";
402 break;
403 case kSvgPreserveaspectratioUnknown:
404 align_string = "unknown";
405 break;
406 }
407 builder.Append(align_string);
408
409 const char* meet_or_slice_string = "";
410 switch (meet_or_slice_) {
411 default:
412 case kSvgMeetorsliceUnknown:
413 break;
414 case kSvgMeetorsliceMeet:
415 meet_or_slice_string = " meet";
416 break;
417 case kSvgMeetorsliceSlice:
418 meet_or_slice_string = " slice";
419 break;
420 }
421 builder.Append(meet_or_slice_string);
422 return builder.ToString();
423 }
424
Add(const SVGPropertyBase * other,const SVGElement *)425 void SVGPreserveAspectRatio::Add(const SVGPropertyBase* other,
426 const SVGElement*) {
427 NOTREACHED();
428 }
429
CalculateAnimatedValue(const SMILAnimationEffectParameters &,float percentage,unsigned repeat_count,const SVGPropertyBase * from_value,const SVGPropertyBase * to_value,const SVGPropertyBase *,const SVGElement *)430 void SVGPreserveAspectRatio::CalculateAnimatedValue(
431 const SMILAnimationEffectParameters&,
432 float percentage,
433 unsigned repeat_count,
434 const SVGPropertyBase* from_value,
435 const SVGPropertyBase* to_value,
436 const SVGPropertyBase*,
437 const SVGElement*) {
438 NOTREACHED();
439 }
440
CalculateDistance(const SVGPropertyBase * to_value,const SVGElement * context_element) const441 float SVGPreserveAspectRatio::CalculateDistance(
442 const SVGPropertyBase* to_value,
443 const SVGElement* context_element) const {
444 // No paced animations for SVGPreserveAspectRatio.
445 return -1;
446 }
447
448 } // namespace blink
449