OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 19 matching lines...) Expand all Loading... |
30 #include "core/css/CSSCalculationValue.h" | 30 #include "core/css/CSSCalculationValue.h" |
31 #include "core/css/CSSToLengthConversionData.h" | 31 #include "core/css/CSSToLengthConversionData.h" |
32 #include "core/css/Pair.h" | 32 #include "core/css/Pair.h" |
33 #include "core/dom/NodeRenderStyle.h" | 33 #include "core/dom/NodeRenderStyle.h" |
34 #include "core/dom/TextLinkColors.h" | 34 #include "core/dom/TextLinkColors.h" |
35 #include "core/rendering/RenderObject.h" | 35 #include "core/rendering/RenderObject.h" |
36 #include "platform/geometry/IntSize.h" | 36 #include "platform/geometry/IntSize.h" |
37 #include "platform/graphics/Gradient.h" | 37 #include "platform/graphics/Gradient.h" |
38 #include "platform/graphics/GradientGeneratedImage.h" | 38 #include "platform/graphics/GradientGeneratedImage.h" |
39 #include "platform/graphics/Image.h" | 39 #include "platform/graphics/Image.h" |
| 40 #include "platform/graphics/skia/SkiaUtils.h" |
40 #include "wtf/text/StringBuilder.h" | 41 #include "wtf/text/StringBuilder.h" |
41 #include "wtf/text/WTFString.h" | 42 #include "wtf/text/WTFString.h" |
42 | 43 |
43 namespace blink { | 44 namespace blink { |
44 | 45 |
45 void CSSGradientColorStop::trace(Visitor* visitor) | 46 void CSSGradientColorStop::trace(Visitor* visitor) |
46 { | 47 { |
47 visitor->trace(m_position); | 48 visitor->trace(m_position); |
48 visitor->trace(m_color); | 49 visitor->trace(m_color); |
49 } | 50 } |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 | 108 |
108 GradientStop() | 109 GradientStop() |
109 : offset(0) | 110 : offset(0) |
110 , specified(false) | 111 , specified(false) |
111 { } | 112 { } |
112 }; | 113 }; |
113 | 114 |
114 PassRefPtrWillBeRawPtr<CSSGradientValue> CSSGradientValue::gradientWithStylesRes
olved(const TextLinkColors& textLinkColors, Color currentColor) | 115 PassRefPtrWillBeRawPtr<CSSGradientValue> CSSGradientValue::gradientWithStylesRes
olved(const TextLinkColors& textLinkColors, Color currentColor) |
115 { | 116 { |
116 bool derived = false; | 117 bool derived = false; |
117 for (unsigned i = 0; i < m_stops.size(); i++) | 118 for (auto& stop : m_stops) { |
118 if (m_stops[i].m_color->colorIsDerivedFromElement()) { | 119 if (!stop.isHint() && stop.m_color->colorIsDerivedFromElement()) { |
119 m_stops[i].m_colorIsDerivedFromElement = true; | 120 stop.m_colorIsDerivedFromElement = true; |
120 derived = true; | 121 derived = true; |
121 break; | 122 break; |
122 } | 123 } |
| 124 } |
123 | 125 |
124 RefPtrWillBeRawPtr<CSSGradientValue> result = nullptr; | 126 RefPtrWillBeRawPtr<CSSGradientValue> result = nullptr; |
125 if (!derived) | 127 if (!derived) |
126 result = this; | 128 result = this; |
127 else if (isLinearGradientValue()) | 129 else if (isLinearGradientValue()) |
128 result = toCSSLinearGradientValue(this)->clone(); | 130 result = toCSSLinearGradientValue(this)->clone(); |
129 else if (isRadialGradientValue()) | 131 else if (isRadialGradientValue()) |
130 result = toCSSRadialGradientValue(this)->clone(); | 132 result = toCSSRadialGradientValue(this)->clone(); |
131 else { | 133 else { |
132 ASSERT_NOT_REACHED(); | 134 ASSERT_NOT_REACHED(); |
133 return nullptr; | 135 return nullptr; |
134 } | 136 } |
135 | 137 |
136 for (unsigned i = 0; i < result->m_stops.size(); i++) | 138 for (auto& stop : result->m_stops) { |
137 result->m_stops[i].m_resolvedColor = textLinkColors.colorFromPrimitiveVa
lue(result->m_stops[i].m_color.get(), currentColor); | 139 if (!stop.isHint()) |
| 140 stop.m_resolvedColor = textLinkColors.colorFromPrimitiveValue(stop.m
_color.get(), currentColor); |
| 141 } |
138 | 142 |
139 return result.release(); | 143 return result.release(); |
140 } | 144 } |
141 | 145 |
| 146 static void replaceColorHintsWithColorStops(Vector<GradientStop>& stops, const V
ector<CSSGradientColorStop, 2>& cssGradientStops) |
| 147 { |
| 148 // This algorithm will replace each color interpolation hint with 9 regular |
| 149 // color stops. The color values for the new color stops will be calculated |
| 150 // using the color weighting formula defined in the spec. The new color |
| 151 // stops will be positioned in such a way that all the pixels between the tw
o |
| 152 // user defined color stops have color values close to the interpolation cur
ve. |
| 153 // If the hint is closer to the left color stop, add 2 stops to the left and |
| 154 // 6 to the right, else add 6 stops to the left and 2 to the right. |
| 155 // The color stops on the side with more space start midway because |
| 156 // the curve approximates a line in that region. |
| 157 // Using this aproximation, it is possible to discern the color steps when |
| 158 // the gradient is large. If this becomes an issue, we can consider improvin
g |
| 159 // the algorithm, or adding support for color interpolation hints to skia sh
aders. |
| 160 |
| 161 int indexOffset = 0; |
| 162 |
| 163 // The first and the last color stops cannot be color hints. |
| 164 for (size_t i = 1; i < cssGradientStops.size() - 1; ++i) { |
| 165 if (!cssGradientStops[i].isHint()) |
| 166 continue; |
| 167 |
| 168 // The current index of the stops vector. |
| 169 size_t x = i + indexOffset; |
| 170 ASSERT(x >= 1); |
| 171 |
| 172 // offsetLeft offset offsetRight |
| 173 // |-------------------|---------------------------------| |
| 174 // leftDist rightDist |
| 175 |
| 176 float offsetLeft = stops[x - 1].offset; |
| 177 float offsetRight = stops[x + 1].offset; |
| 178 float offset = stops[x].offset; |
| 179 float leftDist = offset - offsetLeft; |
| 180 float rightDist = offsetRight - offset; |
| 181 float totalDist = offsetRight - offsetLeft; |
| 182 |
| 183 Color leftColor = stops[x - 1].color; |
| 184 Color rightColor = stops[x + 1].color; |
| 185 |
| 186 ASSERT(offsetLeft <= offset && offset <= offsetRight); |
| 187 |
| 188 if (WebCoreFloatNearlyEqual(leftDist, rightDist)) { |
| 189 stops.remove(x); |
| 190 --indexOffset; |
| 191 continue; |
| 192 } |
| 193 |
| 194 if (WebCoreFloatNearlyEqual(leftDist, .0f)) { |
| 195 stops[x].color = rightColor; |
| 196 continue; |
| 197 } |
| 198 |
| 199 if (WebCoreFloatNearlyEqual(rightDist, .0f)) { |
| 200 stops[x].color = leftColor; |
| 201 continue; |
| 202 } |
| 203 |
| 204 GradientStop newStops[9]; |
| 205 // Position the new color stops. |
| 206 if (leftDist > rightDist) { |
| 207 for (size_t y = 0; y < 7; ++y) |
| 208 newStops[y].offset = offsetLeft + leftDist * (7 + y) / 13; |
| 209 newStops[7].offset = offset + rightDist / 3; |
| 210 newStops[8].offset = offset + rightDist * 2 / 3; |
| 211 } else { |
| 212 newStops[0].offset = offsetLeft + leftDist / 3; |
| 213 newStops[1].offset = offsetLeft + leftDist * 2 / 3; |
| 214 for (size_t y = 0; y < 7; ++y) |
| 215 newStops[y + 2].offset = offset + rightDist * y / 13; |
| 216 } |
| 217 |
| 218 // calculate colors for the new color hints. |
| 219 // The color weighting for the new color stops will be pointRelativeOffs
et^(ln(0.5)/ln(hintRelativeOffset)). |
| 220 float hintRelativeOffset = leftDist / totalDist; |
| 221 for (size_t y = 0; y < 9; ++y) { |
| 222 float pointRelativeOffset = (newStops[y].offset - offsetLeft) / tota
lDist; |
| 223 float weighting = powf(pointRelativeOffset, logf(.5f) / logf(hintRel
ativeOffset)); |
| 224 newStops[y].color = blend(leftColor, rightColor, weighting); |
| 225 } |
| 226 |
| 227 // Replace the color hint with the new color stops. |
| 228 stops.remove(x); |
| 229 stops.insert(x, newStops, 9); |
| 230 indexOffset += 8; |
| 231 } |
| 232 } |
| 233 |
142 void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionD
ata& conversionData, float maxLengthForRepeat) | 234 void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionD
ata& conversionData, float maxLengthForRepeat) |
143 { | 235 { |
144 if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDe
precatedRadialGradient) { | 236 if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDe
precatedRadialGradient) { |
145 sortStopsIfNeeded(); | 237 sortStopsIfNeeded(); |
146 | 238 |
147 for (unsigned i = 0; i < m_stops.size(); i++) { | 239 for (unsigned i = 0; i < m_stops.size(); i++) { |
148 const CSSGradientColorStop& stop = m_stops[i]; | 240 const CSSGradientColorStop& stop = m_stops[i]; |
149 | 241 |
150 float offset; | 242 float offset; |
151 if (stop.m_position->isPercentage()) | 243 if (stop.m_position->isPercentage()) |
152 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_P
ERCENTAGE) / 100; | 244 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_P
ERCENTAGE) / 100; |
153 else | 245 else |
154 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_N
UMBER); | 246 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_N
UMBER); |
155 | 247 |
156 gradient->addColorStop(offset, stop.m_resolvedColor); | 248 gradient->addColorStop(offset, stop.m_resolvedColor); |
157 } | 249 } |
158 | 250 |
159 return; | 251 return; |
160 } | 252 } |
161 | 253 |
162 size_t numStops = m_stops.size(); | 254 size_t numStops = m_stops.size(); |
163 | 255 |
164 Vector<GradientStop> stops(numStops); | 256 Vector<GradientStop> stops(numStops); |
165 | 257 |
166 float gradientLength = 0; | 258 float gradientLength = 0; |
167 bool computedGradientLength = false; | 259 bool computedGradientLength = false; |
168 | 260 |
| 261 bool hasHints = false; |
| 262 |
169 FloatPoint gradientStart = gradient->p0(); | 263 FloatPoint gradientStart = gradient->p0(); |
170 FloatPoint gradientEnd; | 264 FloatPoint gradientEnd; |
171 if (isLinearGradientValue()) | 265 if (isLinearGradientValue()) |
172 gradientEnd = gradient->p1(); | 266 gradientEnd = gradient->p1(); |
173 else if (isRadialGradientValue()) | 267 else if (isRadialGradientValue()) |
174 gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0); | 268 gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0); |
175 | 269 |
176 for (size_t i = 0; i < numStops; ++i) { | 270 for (size_t i = 0; i < numStops; ++i) { |
177 const CSSGradientColorStop& stop = m_stops[i]; | 271 const CSSGradientColorStop& stop = m_stops[i]; |
178 | 272 |
179 stops[i].color = stop.m_resolvedColor; | 273 if (stop.isHint()) |
| 274 hasHints = true; |
| 275 else |
| 276 stops[i].color = stop.m_resolvedColor; |
180 | 277 |
181 if (stop.m_position) { | 278 if (stop.m_position) { |
182 if (stop.m_position->isPercentage()) | 279 if (stop.m_position->isPercentage()) |
183 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveVal
ue::CSS_PERCENTAGE) / 100; | 280 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveVal
ue::CSS_PERCENTAGE) / 100; |
184 else if (stop.m_position->isLength() || stop.m_position->isCalculate
dPercentageWithLength()) { | 281 else if (stop.m_position->isLength() || stop.m_position->isCalculate
dPercentageWithLength()) { |
185 if (!computedGradientLength) { | 282 if (!computedGradientLength) { |
186 FloatSize gradientSize(gradientStart - gradientEnd); | 283 FloatSize gradientSize(gradientStart - gradientEnd); |
187 gradientLength = gradientSize.diagonalLength(); | 284 gradientLength = gradientSize.diagonalLength(); |
188 } | 285 } |
189 float length; | 286 float length; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 | 344 |
248 for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd;
++j) | 345 for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd;
++j) |
249 stops[j].offset = lastSpecifiedOffset + (j - unspecified
RunStart + 1) * delta; | 346 stops[j].offset = lastSpecifiedOffset + (j - unspecified
RunStart + 1) * delta; |
250 } | 347 } |
251 | 348 |
252 inUnspecifiedRun = false; | 349 inUnspecifiedRun = false; |
253 } | 350 } |
254 } | 351 } |
255 } | 352 } |
256 | 353 |
| 354 ASSERT(stops.size() == m_stops.size()); |
| 355 if (hasHints) { |
| 356 replaceColorHintsWithColorStops(stops, m_stops); |
| 357 numStops = stops.size(); |
| 358 } |
| 359 |
257 // If the gradient is repeating, repeat the color stops. | 360 // If the gradient is repeating, repeat the color stops. |
258 // We can't just push this logic down into the platform-specific Gradient co
de, | 361 // We can't just push this logic down into the platform-specific Gradient co
de, |
259 // because we have to know the extent of the gradient, and possible move the
end points. | 362 // because we have to know the extent of the gradient, and possible move the
end points. |
260 if (m_repeating && numStops > 1) { | 363 if (m_repeating && numStops > 1) { |
261 // If the difference in the positions of the first and last color-stops
is 0, | 364 // If the difference in the positions of the first and last color-stops
is 0, |
262 // the gradient defines a solid-color image with the color of the last c
olor-stop in the rule. | 365 // the gradient defines a solid-color image with the color of the last c
olor-stop in the rule. |
263 float gradientRange = stops[numStops - 1].offset - stops[0].offset; | 366 float gradientRange = stops[numStops - 1].offset - stops[0].offset; |
264 if (!gradientRange) { | 367 if (!gradientRange) { |
265 stops.first().offset = 0; | 368 stops.first().offset = 0; |
266 stops.first().color = stops.last().color; | 369 stops.first().color = stops.last().color; |
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
462 | 565 |
463 if (stop.m_position->isFontRelativeLength()) | 566 if (stop.m_position->isFontRelativeLength()) |
464 return false; | 567 return false; |
465 } | 568 } |
466 | 569 |
467 return true; | 570 return true; |
468 } | 571 } |
469 | 572 |
470 bool CSSGradientValue::knownToBeOpaque(const RenderObject*) const | 573 bool CSSGradientValue::knownToBeOpaque(const RenderObject*) const |
471 { | 574 { |
472 for (size_t i = 0; i < m_stops.size(); ++i) { | 575 for (auto& stop : m_stops) { |
473 if (m_stops[i].m_resolvedColor.hasAlpha()) | 576 if (!stop.isHint() && stop.m_resolvedColor.hasAlpha()) |
474 return false; | 577 return false; |
475 } | 578 } |
476 return true; | 579 return true; |
477 } | 580 } |
478 | 581 |
479 void CSSGradientValue::traceAfterDispatch(Visitor* visitor) | 582 void CSSGradientValue::traceAfterDispatch(Visitor* visitor) |
480 { | 583 { |
481 visitor->trace(m_firstX); | 584 visitor->trace(m_firstX); |
482 visitor->trace(m_firstY); | 585 visitor->trace(m_firstY); |
483 visitor->trace(m_secondX); | 586 visitor->trace(m_secondX); |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
573 wroteSomething = true; | 676 wroteSomething = true; |
574 } | 677 } |
575 | 678 |
576 if (wroteSomething) | 679 if (wroteSomething) |
577 result.appendLiteral(", "); | 680 result.appendLiteral(", "); |
578 | 681 |
579 for (unsigned i = 0; i < m_stops.size(); i++) { | 682 for (unsigned i = 0; i < m_stops.size(); i++) { |
580 const CSSGradientColorStop& stop = m_stops[i]; | 683 const CSSGradientColorStop& stop = m_stops[i]; |
581 if (i) | 684 if (i) |
582 result.appendLiteral(", "); | 685 result.appendLiteral(", "); |
583 result.append(stop.m_color->cssText()); | 686 if (stop.m_color) |
584 if (stop.m_position) { | 687 result.append(stop.m_color->cssText()); |
| 688 if (stop.m_color && stop.m_position) |
585 result.append(' '); | 689 result.append(' '); |
| 690 if (stop.m_position) |
586 result.append(stop.m_position->cssText()); | 691 result.append(stop.m_position->cssText()); |
587 } | |
588 } | 692 } |
589 | 693 |
590 } | 694 } |
591 | 695 |
592 result.append(')'); | 696 result.append(')'); |
593 return result.toString(); | 697 return result.toString(); |
594 } | 698 } |
595 | 699 |
596 // Compute the endpoints so that a gradient of the given angle covers a box of t
he given size. | 700 // Compute the endpoints so that a gradient of the given angle covers a box of t
he given size. |
597 static void endPointsFromAngle(float angleDeg, const IntSize& size, FloatPoint&
firstPoint, FloatPoint& secondPoint, CSSGradientType type) | 701 static void endPointsFromAngle(float angleDeg, const IntSize& size, FloatPoint&
firstPoint, FloatPoint& secondPoint, CSSGradientType type) |
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
895 wroteSomething = true; | 999 wroteSomething = true; |
896 } | 1000 } |
897 | 1001 |
898 if (wroteSomething) | 1002 if (wroteSomething) |
899 result.appendLiteral(", "); | 1003 result.appendLiteral(", "); |
900 | 1004 |
901 for (unsigned i = 0; i < m_stops.size(); i++) { | 1005 for (unsigned i = 0; i < m_stops.size(); i++) { |
902 const CSSGradientColorStop& stop = m_stops[i]; | 1006 const CSSGradientColorStop& stop = m_stops[i]; |
903 if (i) | 1007 if (i) |
904 result.appendLiteral(", "); | 1008 result.appendLiteral(", "); |
905 result.append(stop.m_color->cssText()); | 1009 if (stop.m_color) |
906 if (stop.m_position) { | 1010 result.append(stop.m_color->cssText()); |
| 1011 if (stop.m_color && stop.m_position) |
907 result.append(' '); | 1012 result.append(' '); |
| 1013 if (stop.m_position) |
908 result.append(stop.m_position->cssText()); | 1014 result.append(stop.m_position->cssText()); |
909 } | |
910 } | 1015 } |
911 | 1016 |
912 } | 1017 } |
913 | 1018 |
914 result.append(')'); | 1019 result.append(')'); |
915 return result.toString(); | 1020 return result.toString(); |
916 } | 1021 } |
917 | 1022 |
918 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, const CSS
ToLengthConversionData& conversionData, float* widthOrHeight) | 1023 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, const CSS
ToLengthConversionData& conversionData, float* widthOrHeight) |
919 { | 1024 { |
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1198 visitor->trace(m_firstRadius); | 1303 visitor->trace(m_firstRadius); |
1199 visitor->trace(m_secondRadius); | 1304 visitor->trace(m_secondRadius); |
1200 visitor->trace(m_shape); | 1305 visitor->trace(m_shape); |
1201 visitor->trace(m_sizingBehavior); | 1306 visitor->trace(m_sizingBehavior); |
1202 visitor->trace(m_endHorizontalSize); | 1307 visitor->trace(m_endHorizontalSize); |
1203 visitor->trace(m_endVerticalSize); | 1308 visitor->trace(m_endVerticalSize); |
1204 CSSGradientValue::traceAfterDispatch(visitor); | 1309 CSSGradientValue::traceAfterDispatch(visitor); |
1205 } | 1310 } |
1206 | 1311 |
1207 } // namespace blink | 1312 } // namespace blink |
OLD | NEW |