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