OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008 Apple Inc. All rights reserved. |
3 * Copyright (C) 2015 Google Inc. All rights reserved. | 3 * Copyright (C) 2015 Google Inc. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 PassRefPtr<Image> CSSGradientValue::image(const LayoutObject& layoutObject, | 71 PassRefPtr<Image> CSSGradientValue::image(const LayoutObject& layoutObject, |
72 const IntSize& size) { | 72 const IntSize& size) { |
73 if (size.isEmpty()) | 73 if (size.isEmpty()) |
74 return nullptr; | 74 return nullptr; |
75 | 75 |
76 bool cacheable = isCacheable(); | 76 bool cacheable = isCacheable(); |
77 if (cacheable) { | 77 if (cacheable) { |
78 if (!clients().contains(&layoutObject)) | 78 if (!clients().contains(&layoutObject)) |
79 return nullptr; | 79 return nullptr; |
80 | 80 |
81 // Need to look up our size. Create a string of width*height to use as a ha
sh key. | 81 // Need to look up our size. Create a string of width*height to use as a |
| 82 // hash key. |
82 Image* result = getImage(&layoutObject, size); | 83 Image* result = getImage(&layoutObject, size); |
83 if (result) | 84 if (result) |
84 return result; | 85 return result; |
85 } | 86 } |
86 | 87 |
87 // We need to create an image. | 88 // We need to create an image. |
88 RefPtr<Gradient> gradient; | 89 RefPtr<Gradient> gradient; |
89 | 90 |
90 const ComputedStyle* rootStyle = | 91 const ComputedStyle* rootStyle = |
91 layoutObject.document().documentElement()->computedStyle(); | 92 layoutObject.document().documentElement()->computedStyle(); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 GradientStop() : offset(0), specified(false) {} | 124 GradientStop() : offset(0), specified(false) {} |
124 }; | 125 }; |
125 | 126 |
126 static void replaceColorHintsWithColorStops( | 127 static void replaceColorHintsWithColorStops( |
127 Vector<GradientStop>& stops, | 128 Vector<GradientStop>& stops, |
128 const HeapVector<CSSGradientColorStop, 2>& cssGradientStops) { | 129 const HeapVector<CSSGradientColorStop, 2>& cssGradientStops) { |
129 // This algorithm will replace each color interpolation hint with 9 regular | 130 // This algorithm will replace each color interpolation hint with 9 regular |
130 // color stops. The color values for the new color stops will be calculated | 131 // color stops. The color values for the new color stops will be calculated |
131 // using the color weighting formula defined in the spec. The new color | 132 // using the color weighting formula defined in the spec. The new color |
132 // stops will be positioned in such a way that all the pixels between the two | 133 // stops will be positioned in such a way that all the pixels between the two |
133 // user defined color stops have color values close to the interpolation curve
. | 134 // user defined color stops have color values close to the interpolation |
| 135 // curve. |
134 // If the hint is closer to the left color stop, add 2 stops to the left and | 136 // If the hint is closer to the left color stop, add 2 stops to the left and |
135 // 6 to the right, else add 6 stops to the left and 2 to the right. | 137 // 6 to the right, else add 6 stops to the left and 2 to the right. |
136 // The color stops on the side with more space start midway because | 138 // The color stops on the side with more space start midway because |
137 // the curve approximates a line in that region. | 139 // the curve approximates a line in that region. |
138 // Using this aproximation, it is possible to discern the color steps when | 140 // Using this aproximation, it is possible to discern the color steps when |
139 // the gradient is large. If this becomes an issue, we can consider improving | 141 // the gradient is large. If this becomes an issue, we can consider improving |
140 // the algorithm, or adding support for color interpolation hints to skia shad
ers. | 142 // the algorithm, or adding support for color interpolation hints to skia |
| 143 // shaders. |
141 | 144 |
142 int indexOffset = 0; | 145 int indexOffset = 0; |
143 | 146 |
144 // The first and the last color stops cannot be color hints. | 147 // The first and the last color stops cannot be color hints. |
145 for (size_t i = 1; i < cssGradientStops.size() - 1; ++i) { | 148 for (size_t i = 1; i < cssGradientStops.size() - 1; ++i) { |
146 if (!cssGradientStops[i].isHint()) | 149 if (!cssGradientStops[i].isHint()) |
147 continue; | 150 continue; |
148 | 151 |
149 // The current index of the stops vector. | 152 // The current index of the stops vector. |
150 size_t x = i + indexOffset; | 153 size_t x = i + indexOffset; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
190 newStops[7].offset = offset + rightDist / 3; | 193 newStops[7].offset = offset + rightDist / 3; |
191 newStops[8].offset = offset + rightDist * 2 / 3; | 194 newStops[8].offset = offset + rightDist * 2 / 3; |
192 } else { | 195 } else { |
193 newStops[0].offset = offsetLeft + leftDist / 3; | 196 newStops[0].offset = offsetLeft + leftDist / 3; |
194 newStops[1].offset = offsetLeft + leftDist * 2 / 3; | 197 newStops[1].offset = offsetLeft + leftDist * 2 / 3; |
195 for (size_t y = 0; y < 7; ++y) | 198 for (size_t y = 0; y < 7; ++y) |
196 newStops[y + 2].offset = offset + rightDist * y / 13; | 199 newStops[y + 2].offset = offset + rightDist * y / 13; |
197 } | 200 } |
198 | 201 |
199 // calculate colors for the new color hints. | 202 // calculate colors for the new color hints. |
200 // The color weighting for the new color stops will be pointRelativeOffset^(
ln(0.5)/ln(hintRelativeOffset)). | 203 // The color weighting for the new color stops will be |
| 204 // pointRelativeOffset^(ln(0.5)/ln(hintRelativeOffset)). |
201 float hintRelativeOffset = leftDist / totalDist; | 205 float hintRelativeOffset = leftDist / totalDist; |
202 for (size_t y = 0; y < 9; ++y) { | 206 for (size_t y = 0; y < 9; ++y) { |
203 float pointRelativeOffset = (newStops[y].offset - offsetLeft) / totalDist; | 207 float pointRelativeOffset = (newStops[y].offset - offsetLeft) / totalDist; |
204 float weighting = | 208 float weighting = |
205 powf(pointRelativeOffset, logf(.5f) / logf(hintRelativeOffset)); | 209 powf(pointRelativeOffset, logf(.5f) / logf(hintRelativeOffset)); |
206 newStops[y].color = blend(leftColor, rightColor, weighting); | 210 newStops[y].color = blend(leftColor, rightColor, weighting); |
207 } | 211 } |
208 | 212 |
209 // Replace the color hint with the new color stops. | 213 // Replace the color hint with the new color stops. |
210 stops.remove(x); | 214 stops.remove(x); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
252 if (gradient->spreadMethod() == SpreadMethodRepeat) | 256 if (gradient->spreadMethod() == SpreadMethodRepeat) |
253 return true; | 257 return true; |
254 | 258 |
255 // Degenerate stops | 259 // Degenerate stops |
256 if (stops.first().offset < 0 || stops.last().offset > 1) | 260 if (stops.first().offset < 0 || stops.last().offset > 1) |
257 return true; | 261 return true; |
258 | 262 |
259 return false; | 263 return false; |
260 } | 264 } |
261 | 265 |
262 // Redistribute the stops such that they fully cover [0 , 1] and add them to the
gradient. | 266 // Redistribute the stops such that they fully cover [0 , 1] and add them to the |
| 267 // gradient. |
263 static bool normalizeAndAddStops(const Vector<GradientStop>& stops, | 268 static bool normalizeAndAddStops(const Vector<GradientStop>& stops, |
264 Gradient* gradient) { | 269 Gradient* gradient) { |
265 ASSERT(stops.size() > 1); | 270 ASSERT(stops.size() > 1); |
266 | 271 |
267 const float firstOffset = stops.first().offset; | 272 const float firstOffset = stops.first().offset; |
268 const float lastOffset = stops.last().offset; | 273 const float lastOffset = stops.last().offset; |
269 const float span = lastOffset - firstOffset; | 274 const float span = lastOffset - firstOffset; |
270 | 275 |
271 if (fabs(span) < std::numeric_limits<float>::epsilon()) { | 276 if (fabs(span) < std::numeric_limits<float>::epsilon()) { |
272 // All stops are coincident -> use a single clamped offset value. | 277 // All stops are coincident -> use a single clamped offset value. |
273 const float clampedOffset = std::min(std::max(firstOffset, 0.f), 1.f); | 278 const float clampedOffset = std::min(std::max(firstOffset, 0.f), 1.f); |
274 | 279 |
275 // For repeating gradients, a coincident stop set defines a solid-color imag
e with the color | 280 // For repeating gradients, a coincident stop set defines a solid-color |
276 // of the last color-stop in the rule. | 281 // image with the color of the last color-stop in the rule. |
277 // For non-repeating gradients, both the first color and the last color can
be significant | 282 // For non-repeating gradients, both the first color and the last color can |
278 // (padding on both sides of the offset). | 283 // be significant (padding on both sides of the offset). |
279 if (gradient->spreadMethod() != SpreadMethodRepeat) | 284 if (gradient->spreadMethod() != SpreadMethodRepeat) |
280 gradient->addColorStop(clampedOffset, stops.first().color); | 285 gradient->addColorStop(clampedOffset, stops.first().color); |
281 gradient->addColorStop(clampedOffset, stops.last().color); | 286 gradient->addColorStop(clampedOffset, stops.last().color); |
282 | 287 |
283 return false; | 288 return false; |
284 } | 289 } |
285 | 290 |
286 ASSERT(span > 0); | 291 ASSERT(span > 0); |
287 | 292 |
288 for (size_t i = 0; i < stops.size(); ++i) { | 293 for (size_t i = 0; i < stops.size(); ++i) { |
289 const float normalizedOffset = (stops[i].offset - firstOffset) / span; | 294 const float normalizedOffset = (stops[i].offset - firstOffset) / span; |
290 | 295 |
291 // stop offsets should be monotonically increasing in [0 , 1] | 296 // stop offsets should be monotonically increasing in [0 , 1] |
292 ASSERT(normalizedOffset >= 0 && normalizedOffset <= 1); | 297 ASSERT(normalizedOffset >= 0 && normalizedOffset <= 1); |
293 ASSERT(i == 0 || | 298 ASSERT(i == 0 || |
294 normalizedOffset >= (stops[i - 1].offset - firstOffset) / span); | 299 normalizedOffset >= (stops[i - 1].offset - firstOffset) / span); |
295 | 300 |
296 gradient->addColorStop(normalizedOffset, stops[i].color); | 301 gradient->addColorStop(normalizedOffset, stops[i].color); |
297 } | 302 } |
298 | 303 |
299 return true; | 304 return true; |
300 } | 305 } |
301 | 306 |
302 // Collapse all negative-offset stops to 0 and compute an interpolated color val
ue for that point. | 307 // Collapse all negative-offset stops to 0 and compute an interpolated color |
| 308 // value for that point. |
303 static void clampNegativeOffsets(Vector<GradientStop>& stops) { | 309 static void clampNegativeOffsets(Vector<GradientStop>& stops) { |
304 float lastNegativeOffset = 0; | 310 float lastNegativeOffset = 0; |
305 | 311 |
306 for (size_t i = 0; i < stops.size(); ++i) { | 312 for (size_t i = 0; i < stops.size(); ++i) { |
307 const float currentOffset = stops[i].offset; | 313 const float currentOffset = stops[i].offset; |
308 if (currentOffset >= 0) { | 314 if (currentOffset >= 0) { |
309 if (i > 0) { | 315 if (i > 0) { |
310 // We found the negative -> positive offset transition: compute an inter
polated | 316 // We found the negative -> positive offset transition: compute an |
311 // color value for 0 and use it with the last clamped stop. | 317 // interpolated color value for 0 and use it with the last clamped stop. |
312 ASSERT(lastNegativeOffset < 0); | 318 ASSERT(lastNegativeOffset < 0); |
313 float lerpRatio = | 319 float lerpRatio = |
314 -lastNegativeOffset / (currentOffset - lastNegativeOffset); | 320 -lastNegativeOffset / (currentOffset - lastNegativeOffset); |
315 stops[i - 1].color = | 321 stops[i - 1].color = |
316 blend(stops[i - 1].color, stops[i].color, lerpRatio); | 322 blend(stops[i - 1].color, stops[i].color, lerpRatio); |
317 } | 323 } |
318 | 324 |
319 break; | 325 break; |
320 } | 326 } |
321 | 327 |
(...skipping 27 matching lines...) Expand all Loading... |
349 ASSERT(firstOffset <= lastOffset); | 355 ASSERT(firstOffset <= lastOffset); |
350 | 356 |
351 // Radial offsets are relative to the [0 , endRadius] segment. | 357 // Radial offsets are relative to the [0 , endRadius] segment. |
352 float adjustedR0 = gradient->endRadius() * firstOffset; | 358 float adjustedR0 = gradient->endRadius() * firstOffset; |
353 float adjustedR1 = gradient->endRadius() * lastOffset; | 359 float adjustedR1 = gradient->endRadius() * lastOffset; |
354 ASSERT(adjustedR0 <= adjustedR1); | 360 ASSERT(adjustedR0 <= adjustedR1); |
355 | 361 |
356 // Unlike linear gradients (where we can adjust the points arbitrarily), | 362 // Unlike linear gradients (where we can adjust the points arbitrarily), |
357 // we cannot let our radii turn negative here. | 363 // we cannot let our radii turn negative here. |
358 if (adjustedR0 < 0) { | 364 if (adjustedR0 < 0) { |
359 // For the non-repeat case, this can never happen: clampNegativeOffsets() en
sures we don't | 365 // For the non-repeat case, this can never happen: clampNegativeOffsets() |
360 // have to deal with negative offsets at this point. | 366 // ensures we don't have to deal with negative offsets at this point. |
361 ASSERT(gradient->spreadMethod() == SpreadMethodRepeat); | 367 ASSERT(gradient->spreadMethod() == SpreadMethodRepeat); |
362 | 368 |
363 // When in repeat mode, we deal with it by repositioning both radii in the p
ositive domain - | 369 // When in repeat mode, we deal with it by repositioning both radii in the |
364 // shifting them by a multiple of the radius span (which is the period of ou
r repeating | 370 // positive domain - shifting them by a multiple of the radius span (which |
365 // gradient -> hence no visible side effects). | 371 // is the period of our repeating gradient -> hence no visible side |
| 372 // effects). |
366 const float radiusSpan = adjustedR1 - adjustedR0; | 373 const float radiusSpan = adjustedR1 - adjustedR0; |
367 const float shiftToPositive = radiusSpan * ceilf(-adjustedR0 / radiusSpan); | 374 const float shiftToPositive = radiusSpan * ceilf(-adjustedR0 / radiusSpan); |
368 adjustedR0 += shiftToPositive; | 375 adjustedR0 += shiftToPositive; |
369 adjustedR1 += shiftToPositive; | 376 adjustedR1 += shiftToPositive; |
370 } | 377 } |
371 ASSERT(adjustedR0 >= 0); | 378 ASSERT(adjustedR0 >= 0); |
372 ASSERT(adjustedR1 >= adjustedR0); | 379 ASSERT(adjustedR1 >= adjustedR0); |
373 | 380 |
374 gradient->setStartRadius(adjustedR0); | 381 gradient->setStartRadius(adjustedR0); |
375 gradient->setEndRadius(adjustedR1); | 382 gradient->setEndRadius(adjustedR1); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
419 length = stop.m_position->cssCalcValue() | 426 length = stop.m_position->cssCalcValue() |
420 ->toCalcValue(conversionData) | 427 ->toCalcValue(conversionData) |
421 ->evaluate(gradientLength); | 428 ->evaluate(gradientLength); |
422 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0; | 429 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0; |
423 } else { | 430 } else { |
424 ASSERT_NOT_REACHED(); | 431 ASSERT_NOT_REACHED(); |
425 stops[i].offset = 0; | 432 stops[i].offset = 0; |
426 } | 433 } |
427 stops[i].specified = true; | 434 stops[i].specified = true; |
428 } else { | 435 } else { |
429 // If the first color-stop does not have a position, its position defaults
to 0%. | 436 // If the first color-stop does not have a position, its position defaults |
430 // If the last color-stop does not have a position, its position defaults
to 100%. | 437 // to 0%. If the last color-stop does not have a position, its position |
| 438 // defaults to 100%. |
431 if (!i) { | 439 if (!i) { |
432 stops[i].offset = 0; | 440 stops[i].offset = 0; |
433 stops[i].specified = true; | 441 stops[i].specified = true; |
434 } else if (numStops > 1 && i == numStops - 1) { | 442 } else if (numStops > 1 && i == numStops - 1) { |
435 stops[i].offset = 1; | 443 stops[i].offset = 1; |
436 stops[i].specified = true; | 444 stops[i].specified = true; |
437 } | 445 } |
438 } | 446 } |
439 | 447 |
440 // If a color-stop has a position that is less than the specified position o
f any | 448 // If a color-stop has a position that is less than the specified position |
441 // color-stop before it in the list, its position is changed to be equal to
the | 449 // of any color-stop before it in the list, its position is changed to be |
442 // largest specified position of any color-stop before it. | 450 // equal to the largest specified position of any color-stop before it. |
443 if (stops[i].specified && i > 0) { | 451 if (stops[i].specified && i > 0) { |
444 size_t prevSpecifiedIndex; | 452 size_t prevSpecifiedIndex; |
445 for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; | 453 for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; |
446 --prevSpecifiedIndex) { | 454 --prevSpecifiedIndex) { |
447 if (stops[prevSpecifiedIndex].specified) | 455 if (stops[prevSpecifiedIndex].specified) |
448 break; | 456 break; |
449 } | 457 } |
450 | 458 |
451 if (stops[i].offset < stops[prevSpecifiedIndex].offset) | 459 if (stops[i].offset < stops[prevSpecifiedIndex].offset) |
452 stops[i].offset = stops[prevSpecifiedIndex].offset; | 460 stops[i].offset = stops[prevSpecifiedIndex].offset; |
453 } | 461 } |
454 } | 462 } |
455 | 463 |
456 ASSERT(stops.first().specified && stops.last().specified); | 464 ASSERT(stops.first().specified && stops.last().specified); |
457 | 465 |
458 // If any color-stop still does not have a position, then, for each run of adj
acent | 466 // If any color-stop still does not have a position, then, for each run of |
459 // color-stops without positions, set their positions so that they are evenly
spaced | 467 // adjacent color-stops without positions, set their positions so that they |
460 // between the preceding and following color-stops with positions. | 468 // are evenly spaced between the preceding and following color-stops with |
| 469 // positions. |
461 if (numStops > 2) { | 470 if (numStops > 2) { |
462 size_t unspecifiedRunStart = 0; | 471 size_t unspecifiedRunStart = 0; |
463 bool inUnspecifiedRun = false; | 472 bool inUnspecifiedRun = false; |
464 | 473 |
465 for (size_t i = 0; i < numStops; ++i) { | 474 for (size_t i = 0; i < numStops; ++i) { |
466 if (!stops[i].specified && !inUnspecifiedRun) { | 475 if (!stops[i].specified && !inUnspecifiedRun) { |
467 unspecifiedRunStart = i; | 476 unspecifiedRunStart = i; |
468 inUnspecifiedRun = true; | 477 inUnspecifiedRun = true; |
469 } else if (stops[i].specified && inUnspecifiedRun) { | 478 } else if (stops[i].specified && inUnspecifiedRun) { |
470 size_t unspecifiedRunEnd = i; | 479 size_t unspecifiedRunEnd = i; |
(...skipping 12 matching lines...) Expand all Loading... |
483 inUnspecifiedRun = false; | 492 inUnspecifiedRun = false; |
484 } | 493 } |
485 } | 494 } |
486 } | 495 } |
487 | 496 |
488 ASSERT(stops.size() == m_stops.size()); | 497 ASSERT(stops.size() == m_stops.size()); |
489 if (hasHints) { | 498 if (hasHints) { |
490 replaceColorHintsWithColorStops(stops, m_stops); | 499 replaceColorHintsWithColorStops(stops, m_stops); |
491 } | 500 } |
492 | 501 |
493 // At this point we have a fully resolved set of stops. Time to perform adjust
ments for | 502 // At this point we have a fully resolved set of stops. Time to perform |
494 // repeat gradients and degenerate values if needed. | 503 // adjustments for repeat gradients and degenerate values if needed. |
495 if (requiresStopsNormalization(stops, gradient)) { | 504 if (requiresStopsNormalization(stops, gradient)) { |
496 // Negative offsets are only an issue for non-repeating radial gradients: li
near gradient | 505 // Negative offsets are only an issue for non-repeating radial gradients: |
497 // points can be repositioned arbitrarily, and for repeating radial gradient
s we shift | 506 // linear gradient points can be repositioned arbitrarily, and for repeating |
498 // the radii into equivalent positive values. | 507 // radial gradients we shift the radii into equivalent positive values. |
499 if (isRadialGradientValue() && !m_repeating) | 508 if (isRadialGradientValue() && !m_repeating) |
500 clampNegativeOffsets(stops); | 509 clampNegativeOffsets(stops); |
501 | 510 |
502 if (normalizeAndAddStops(stops, gradient)) { | 511 if (normalizeAndAddStops(stops, gradient)) { |
503 if (isLinearGradientValue()) { | 512 if (isLinearGradientValue()) { |
504 adjustGradientPointsForOffsetRange(gradient, stops.first().offset, | 513 adjustGradientPointsForOffsetRange(gradient, stops.first().offset, |
505 stops.last().offset); | 514 stops.last().offset); |
506 } else { | 515 } else { |
507 adjustGradientRadiiForOffsetRange(gradient, stops.first().offset, | 516 adjustGradientRadiiForOffsetRange(gradient, stops.first().offset, |
508 stops.last().offset); | 517 stops.last().offset); |
509 } | 518 } |
510 } else { | 519 } else { |
511 // Normalization failed because the stop set is coincident. | 520 // Normalization failed because the stop set is coincident. |
512 } | 521 } |
513 } else { | 522 } else { |
514 // No normalization required, just add the current stops. | 523 // No normalization required, just add the current stops. |
515 for (const auto& stop : stops) | 524 for (const auto& stop : stops) |
516 gradient->addColorStop(stop.offset, stop.color); | 525 gradient->addColorStop(stop.offset, stop.color); |
517 } | 526 } |
518 } | 527 } |
519 | 528 |
520 static float positionFromValue(const CSSValue* value, | 529 static float positionFromValue(const CSSValue* value, |
521 const CSSToLengthConversionData& conversionData, | 530 const CSSToLengthConversionData& conversionData, |
522 const IntSize& size, | 531 const IntSize& size, |
523 bool isHorizontal) { | 532 bool isHorizontal) { |
524 int origin = 0; | 533 int origin = 0; |
525 int sign = 1; | 534 int sign = 1; |
526 int edgeDistance = isHorizontal ? size.width() : size.height(); | 535 int edgeDistance = isHorizontal ? size.width() : size.height(); |
527 | 536 |
528 // In this case the center of the gradient is given relative to an edge in the
form of: | 537 // In this case the center of the gradient is given relative to an edge in the |
529 // [ top | bottom | right | left ] [ <percentage> | <length> ]. | 538 // form of: [ top | bottom | right | left ] [ <percentage> | <length> ]. |
530 if (value->isValuePair()) { | 539 if (value->isValuePair()) { |
531 const CSSValuePair& pair = toCSSValuePair(*value); | 540 const CSSValuePair& pair = toCSSValuePair(*value); |
532 CSSValueID originID = toCSSIdentifierValue(pair.first()).getValueID(); | 541 CSSValueID originID = toCSSIdentifierValue(pair.first()).getValueID(); |
533 value = &pair.second(); | 542 value = &pair.second(); |
534 | 543 |
535 if (originID == CSSValueRight || originID == CSSValueBottom) { | 544 if (originID == CSSValueRight || originID == CSSValueBottom) { |
536 // For right/bottom, the offset is relative to the far edge. | 545 // For right/bottom, the offset is relative to the far edge. |
537 origin = edgeDistance; | 546 origin = edgeDistance; |
538 sign = -1; | 547 sign = -1; |
539 } | 548 } |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
723 result.append(' '); | 732 result.append(' '); |
724 if (stop.m_position) | 733 if (stop.m_position) |
725 result.append(stop.m_position->cssText()); | 734 result.append(stop.m_position->cssText()); |
726 } | 735 } |
727 } | 736 } |
728 | 737 |
729 result.append(')'); | 738 result.append(')'); |
730 return result.toString(); | 739 return result.toString(); |
731 } | 740 } |
732 | 741 |
733 // Compute the endpoints so that a gradient of the given angle covers a box of t
he given size. | 742 // Compute the endpoints so that a gradient of the given angle covers a box of |
| 743 // the given size. |
734 static void endPointsFromAngle(float angleDeg, | 744 static void endPointsFromAngle(float angleDeg, |
735 const IntSize& size, | 745 const IntSize& size, |
736 FloatPoint& firstPoint, | 746 FloatPoint& firstPoint, |
737 FloatPoint& secondPoint, | 747 FloatPoint& secondPoint, |
738 CSSGradientType type) { | 748 CSSGradientType type) { |
739 // Prefixed gradients use "polar coordinate" angles, rather than "bearing" ang
les. | 749 // Prefixed gradients use "polar coordinate" angles, rather than "bearing" |
| 750 // angles. |
740 if (type == CSSPrefixedLinearGradient) | 751 if (type == CSSPrefixedLinearGradient) |
741 angleDeg = 90 - angleDeg; | 752 angleDeg = 90 - angleDeg; |
742 | 753 |
743 angleDeg = fmodf(angleDeg, 360); | 754 angleDeg = fmodf(angleDeg, 360); |
744 if (angleDeg < 0) | 755 if (angleDeg < 0) |
745 angleDeg += 360; | 756 angleDeg += 360; |
746 | 757 |
747 if (!angleDeg) { | 758 if (!angleDeg) { |
748 firstPoint.set(0, size.height()); | 759 firstPoint.set(0, size.height()); |
749 secondPoint.set(0, 0); | 760 secondPoint.set(0, 0); |
(...skipping 15 matching lines...) Expand all Loading... |
765 if (angleDeg == 270) { | 776 if (angleDeg == 270) { |
766 firstPoint.set(size.width(), 0); | 777 firstPoint.set(size.width(), 0); |
767 secondPoint.set(0, 0); | 778 secondPoint.set(0, 0); |
768 return; | 779 return; |
769 } | 780 } |
770 | 781 |
771 // angleDeg is a "bearing angle" (0deg = N, 90deg = E), | 782 // angleDeg is a "bearing angle" (0deg = N, 90deg = E), |
772 // but tan expects 0deg = E, 90deg = N. | 783 // but tan expects 0deg = E, 90deg = N. |
773 float slope = tan(deg2rad(90 - angleDeg)); | 784 float slope = tan(deg2rad(90 - angleDeg)); |
774 | 785 |
775 // We find the endpoint by computing the intersection of the line formed by th
e slope, | 786 // We find the endpoint by computing the intersection of the line formed by |
776 // and a line perpendicular to it that intersects the corner. | 787 // the slope, and a line perpendicular to it that intersects the corner. |
777 float perpendicularSlope = -1 / slope; | 788 float perpendicularSlope = -1 / slope; |
778 | 789 |
779 // Compute start corner relative to center, in Cartesian space (+y = up). | 790 // Compute start corner relative to center, in Cartesian space (+y = up). |
780 float halfHeight = size.height() / 2; | 791 float halfHeight = size.height() / 2; |
781 float halfWidth = size.width() / 2; | 792 float halfWidth = size.width() / 2; |
782 FloatPoint endCorner; | 793 FloatPoint endCorner; |
783 if (angleDeg < 90) | 794 if (angleDeg < 90) |
784 endCorner.set(halfWidth, halfHeight); | 795 endCorner.set(halfWidth, halfHeight); |
785 else if (angleDeg < 180) | 796 else if (angleDeg < 180) |
786 endCorner.set(halfWidth, -halfHeight); | 797 endCorner.set(halfWidth, -halfHeight); |
787 else if (angleDeg < 270) | 798 else if (angleDeg < 270) |
788 endCorner.set(-halfWidth, -halfHeight); | 799 endCorner.set(-halfWidth, -halfHeight); |
789 else | 800 else |
790 endCorner.set(-halfWidth, halfHeight); | 801 endCorner.set(-halfWidth, halfHeight); |
791 | 802 |
792 // Compute c (of y = mx + c) using the corner point. | 803 // Compute c (of y = mx + c) using the corner point. |
793 float c = endCorner.y() - perpendicularSlope * endCorner.x(); | 804 float c = endCorner.y() - perpendicularSlope * endCorner.x(); |
794 float endX = c / (slope - perpendicularSlope); | 805 float endX = c / (slope - perpendicularSlope); |
795 float endY = perpendicularSlope * endX + c; | 806 float endY = perpendicularSlope * endX + c; |
796 | 807 |
797 // We computed the end point, so set the second point, | 808 // We computed the end point, so set the second point, taking into account the |
798 // taking into account the moved origin and the fact that we're in drawing spa
ce (+y = down). | 809 // moved origin and the fact that we're in drawing space (+y = down). |
799 secondPoint.set(halfWidth + endX, halfHeight - endY); | 810 secondPoint.set(halfWidth + endX, halfHeight - endY); |
800 // Reflect around the center for the start point. | 811 // Reflect around the center for the start point. |
801 firstPoint.set(halfWidth - endX, halfHeight + endY); | 812 firstPoint.set(halfWidth - endX, halfHeight + endY); |
802 } | 813 } |
803 | 814 |
804 PassRefPtr<Gradient> CSSLinearGradientValue::createGradient( | 815 PassRefPtr<Gradient> CSSLinearGradientValue::createGradient( |
805 const CSSToLengthConversionData& conversionData, | 816 const CSSToLengthConversionData& conversionData, |
806 const IntSize& size, | 817 const IntSize& size, |
807 const LayoutObject& object) { | 818 const LayoutObject& object) { |
808 ASSERT(!size.isEmpty()); | 819 ASSERT(!size.isEmpty()); |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1085 else | 1096 else |
1086 result = radius->computeLength<float>(conversionData); | 1097 result = radius->computeLength<float>(conversionData); |
1087 | 1098 |
1088 return clampTo<float>(std::max(result, 0.0f)); | 1099 return clampTo<float>(std::max(result, 0.0f)); |
1089 } | 1100 } |
1090 | 1101 |
1091 namespace { | 1102 namespace { |
1092 | 1103 |
1093 enum EndShapeType { CircleEndShape, EllipseEndShape }; | 1104 enum EndShapeType { CircleEndShape, EllipseEndShape }; |
1094 | 1105 |
1095 // Compute the radius to the closest/farthest side (depending on the compare fun
ctor). | 1106 // Compute the radius to the closest/farthest side (depending on the compare |
| 1107 // functor). |
1096 FloatSize radiusToSide(const FloatPoint& point, | 1108 FloatSize radiusToSide(const FloatPoint& point, |
1097 const FloatSize& size, | 1109 const FloatSize& size, |
1098 EndShapeType shape, | 1110 EndShapeType shape, |
1099 bool (*compare)(float, float)) { | 1111 bool (*compare)(float, float)) { |
1100 float dx1 = clampTo<float>(fabs(point.x())); | 1112 float dx1 = clampTo<float>(fabs(point.x())); |
1101 float dy1 = clampTo<float>(fabs(point.y())); | 1113 float dy1 = clampTo<float>(fabs(point.y())); |
1102 float dx2 = clampTo<float>(fabs(point.x() - size.width())); | 1114 float dx2 = clampTo<float>(fabs(point.x() - size.width())); |
1103 float dy2 = clampTo<float>(fabs(point.y() - size.height())); | 1115 float dy2 = clampTo<float>(fabs(point.y() - size.height())); |
1104 | 1116 |
1105 float dx = compare(dx1, dx2) ? dx1 : dx2; | 1117 float dx = compare(dx1, dx2) ? dx1 : dx2; |
1106 float dy = compare(dy1, dy2) ? dy1 : dy2; | 1118 float dy = compare(dy1, dy2) ? dy1 : dy2; |
1107 | 1119 |
1108 if (shape == CircleEndShape) | 1120 if (shape == CircleEndShape) |
1109 return compare(dx, dy) ? FloatSize(dx, dx) : FloatSize(dy, dy); | 1121 return compare(dx, dy) ? FloatSize(dx, dx) : FloatSize(dy, dy); |
1110 | 1122 |
1111 ASSERT(shape == EllipseEndShape); | 1123 ASSERT(shape == EllipseEndShape); |
1112 return FloatSize(dx, dy); | 1124 return FloatSize(dx, dy); |
1113 } | 1125 } |
1114 | 1126 |
1115 // Compute the radius of an ellipse with center at 0,0 which passes through p, a
nd has | 1127 // Compute the radius of an ellipse with center at 0,0 which passes through p, |
1116 // width/height given by aspectRatio. | 1128 // and has width/height given by aspectRatio. |
1117 inline FloatSize ellipseRadius(const FloatPoint& p, float aspectRatio) { | 1129 inline FloatSize ellipseRadius(const FloatPoint& p, float aspectRatio) { |
1118 // If the aspectRatio is 0 or infinite, the ellipse is completely flat. | 1130 // If the aspectRatio is 0 or infinite, the ellipse is completely flat. |
1119 // TODO(sashab): Implement Degenerate Radial Gradients, see crbug.com/635727. | 1131 // TODO(sashab): Implement Degenerate Radial Gradients, see crbug.com/635727. |
1120 if (aspectRatio == 0 || std::isinf(aspectRatio)) | 1132 if (aspectRatio == 0 || std::isinf(aspectRatio)) |
1121 return FloatSize(0, 0); | 1133 return FloatSize(0, 0); |
1122 | 1134 |
1123 // x^2/a^2 + y^2/b^2 = 1 | 1135 // x^2/a^2 + y^2/b^2 = 1 |
1124 // a/b = aspectRatio, b = a/aspectRatio | 1136 // a/b = aspectRatio, b = a/aspectRatio |
1125 // a = sqrt(x^2 + y^2/(1/r^2)) | 1137 // a = sqrt(x^2 + y^2/(1/r^2)) |
1126 float a = sqrtf(p.x() * p.x() + p.y() * p.y() * aspectRatio * aspectRatio); | 1138 float a = sqrtf(p.x() * p.x() + p.y() * p.y() * aspectRatio * aspectRatio); |
1127 return FloatSize(clampTo<float>(a), clampTo<float>(a / aspectRatio)); | 1139 return FloatSize(clampTo<float>(a), clampTo<float>(a / aspectRatio)); |
1128 } | 1140 } |
1129 | 1141 |
1130 // Compute the radius to the closest/farthest corner (depending on the compare f
unctor). | 1142 // Compute the radius to the closest/farthest corner (depending on the compare |
| 1143 // functor). |
1131 FloatSize radiusToCorner(const FloatPoint& point, | 1144 FloatSize radiusToCorner(const FloatPoint& point, |
1132 const FloatSize& size, | 1145 const FloatSize& size, |
1133 EndShapeType shape, | 1146 EndShapeType shape, |
1134 bool (*compare)(float, float)) { | 1147 bool (*compare)(float, float)) { |
1135 const FloatRect rect(FloatPoint(), size); | 1148 const FloatRect rect(FloatPoint(), size); |
1136 const FloatPoint corners[] = {rect.minXMinYCorner(), rect.maxXMinYCorner(), | 1149 const FloatPoint corners[] = {rect.minXMinYCorner(), rect.maxXMinYCorner(), |
1137 rect.maxXMaxYCorner(), rect.minXMaxYCorner()}; | 1150 rect.maxXMaxYCorner(), rect.minXMaxYCorner()}; |
1138 | 1151 |
1139 unsigned cornerIndex = 0; | 1152 unsigned cornerIndex = 0; |
1140 float distance = (point - corners[cornerIndex]).diagonalLength(); | 1153 float distance = (point - corners[cornerIndex]).diagonalLength(); |
1141 for (unsigned i = 1; i < WTF_ARRAY_LENGTH(corners); ++i) { | 1154 for (unsigned i = 1; i < WTF_ARRAY_LENGTH(corners); ++i) { |
1142 float newDistance = (point - corners[i]).diagonalLength(); | 1155 float newDistance = (point - corners[i]).diagonalLength(); |
1143 if (compare(newDistance, distance)) { | 1156 if (compare(newDistance, distance)) { |
1144 cornerIndex = i; | 1157 cornerIndex = i; |
1145 distance = newDistance; | 1158 distance = newDistance; |
1146 } | 1159 } |
1147 } | 1160 } |
1148 | 1161 |
1149 if (shape == CircleEndShape) | 1162 if (shape == CircleEndShape) |
1150 return FloatSize(distance, distance); | 1163 return FloatSize(distance, distance); |
1151 | 1164 |
1152 ASSERT(shape == EllipseEndShape); | 1165 ASSERT(shape == EllipseEndShape); |
1153 // If the end shape is an ellipse, the gradient-shape has the same ratio of wi
dth to height | 1166 // If the end shape is an ellipse, the gradient-shape has the same ratio of |
1154 // that it would if closest-side or farthest-side were specified, as appropria
te. | 1167 // width to height that it would if closest-side or farthest-side were |
| 1168 // specified, as appropriate. |
1155 const FloatSize sideRadius = | 1169 const FloatSize sideRadius = |
1156 radiusToSide(point, size, EllipseEndShape, compare); | 1170 radiusToSide(point, size, EllipseEndShape, compare); |
1157 | 1171 |
1158 return ellipseRadius(FloatPoint(corners[cornerIndex] - point), | 1172 return ellipseRadius(FloatPoint(corners[cornerIndex] - point), |
1159 sideRadius.aspectRatio()); | 1173 sideRadius.aspectRatio()); |
1160 } | 1174 } |
1161 | 1175 |
1162 } // anonymous namespace | 1176 } // anonymous namespace |
1163 | 1177 |
1164 PassRefPtr<Gradient> CSSRadialGradientValue::createGradient( | 1178 PassRefPtr<Gradient> CSSRadialGradientValue::createGradient( |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1303 visitor->trace(m_firstRadius); | 1317 visitor->trace(m_firstRadius); |
1304 visitor->trace(m_secondRadius); | 1318 visitor->trace(m_secondRadius); |
1305 visitor->trace(m_shape); | 1319 visitor->trace(m_shape); |
1306 visitor->trace(m_sizingBehavior); | 1320 visitor->trace(m_sizingBehavior); |
1307 visitor->trace(m_endHorizontalSize); | 1321 visitor->trace(m_endHorizontalSize); |
1308 visitor->trace(m_endVerticalSize); | 1322 visitor->trace(m_endVerticalSize); |
1309 CSSGradientValue::traceAfterDispatch(visitor); | 1323 CSSGradientValue::traceAfterDispatch(visitor); |
1310 } | 1324 } |
1311 | 1325 |
1312 } // namespace blink | 1326 } // namespace blink |
OLD | NEW |