Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: Source/core/css/CSSGradientValue.cpp

Issue 631753002: Adding support for color interpolation hints to CSS gradients. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: NeedsRebaseline Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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"
f(malita) 2014/10/08 16:13:54 Is this used at all?
rosca 2014/10/08 17:18:51 Yes, it is used for WebCoreFloatNearlyEqual(...)
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
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.m_color && stop.m_color->colorIsDerivedFromElement()) {
f(malita) 2014/10/08 16:13:54 I think these checks would look better if we add a
rosca 2014/10/08 17:18:52 Sounds good. I added it inline.
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.m_color)
f(malita) 2014/10/08 16:13:54 !stop.isHint()?
rosca 2014/10/08 17:18:52 Done.
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 inline int interpolate(int v0, int v1, float p)
147 {
f(malita) 2014/10/08 16:13:54 ASSERT(p >= 0 && p <= 1)?
rosca 2014/10/08 17:18:52 Done.
148 return v0 + static_cast<int>(p * (v1 - v0));
149 }
150
151 static inline Color interpolate(Color c1, Color c2, float p)
152 {
153 int r = interpolate(c1.red(), c2.red(), p);
154 int g = interpolate(c1.green(), c2.green(), p);
155 int b = interpolate(c1.blue(), c2.blue(), p);
156 int a = interpolate(c1.alpha(), c2.alpha(), p);
157 return Color(r, g, b, a);
158 }
159
160 static void replaceColorHintsWithColorStops(Vector<GradientStop>& stops, const V ector<CSSGradientColorStop, 2>& cssGradientStops)
f(malita) 2014/10/08 16:13:54 Since cssGradientStops is always a CSSGradientValu
rosca 2014/10/08 17:18:52 The problem here is that "struct GradientStop" is
f(malita) 2014/10/08 17:34:30 Ah, you're right, I missed that part. Let's leave
161 {
162 // This algorithm will replace each color interpolation hint with 9 regular
163 // color stops. The color values for the new color stops will be calculated
164 // using the color weighting formula defined in the spec. The new color
165 // stops will be positioned in such a way that all the pixels between the tw o
166 // user defined color stops have color values close to the interpolation cur ve.
167 // If the hint is closer to the left color stop, add 2 stops to the left and
168 // 6 to the right, else add 6 stops to the left and 2 to the right.
169 // The color stops on the side with more space start midway because
170 // the curve approximates a line in that region.
171 // Using this aproximation, it is possible to discern the color steps when
172 // the gradient is large. If this becomes an issue, we can consider improvin g
173 // the algorithm, or adding support for color interpolation hints to skia sh aders.
174
175 int indexOffset = 0;
176
177 // The first and the last color stops cannot be color hints.
178 for (size_t i = 1; i < cssGradientStops.size() - 1; ++i) {
179 // Not a color interpolation hint.
180 if (cssGradientStops[i].m_color)
f(malita) 2014/10/08 16:13:54 !cssGradientStops[i].isHint()?
rosca 2014/10/08 17:18:52 Done.
181 continue;
182
183 // The current index of the stops vector.
184 size_t x = i + indexOffset;
f(malita) 2014/10/08 16:13:54 Let's ASSERT(x >= 1) since indexOffset can potenti
rosca 2014/10/08 17:18:51 Done.
185
186 // offset1 offset offset2
187 // |-------------------|---------------------------------|
188 // leftDist rightDist
189
190 float offset1 = stops[x - 1].offset;
191 float offset2 = stops[x + 1].offset;
192 float offset = stops[x].offset;
193 float leftDist = offset - offset1;
194 float rightDist = offset2 - offset;
195 float totalDist = offset2 - offset1;
196
197 Color leftColor = stops[x - 1].color;
198 Color rightColor = stops[x + 1].color;
199
200 ASSERT(offset1 <= offset && offset <= offset2);
201
202 // If the color hint is positioned exactly in the middle, we can remove it.
203 if (WebCoreFloatNearlyEqual(leftDist, rightDist)) {
f(malita) 2014/10/08 16:13:54 Should we also drop the hint if offset1 == offset2
rosca 2014/10/08 17:18:51 This expression (leftDist == rightDist) should alw
204 stops.remove(x);
205 --indexOffset;
206 continue;
207 }
208
209 // Check if the color hint coincides with the left color stop.
210 if (WebCoreFloatNearlyEqual(leftDist, .0f)) {
211 stops[x].color = rightColor;
212 continue;
213 }
214
215 // Check if the color hint coincides with the right color stop.
216 if (WebCoreFloatNearlyEqual(rightDist, .0f)) {
217 stops[x].color = leftColor;
218 continue;
219 }
220
221 GradientStop newStops[9];
222 // Position the new color stops.
223 if (leftDist > rightDist) {
224 for (size_t y = 0; y < 7; ++y)
225 newStops[y].offset = offset1 + leftDist * (7 + y) / 13;
226 newStops[7].offset = offset + rightDist / 3;
227 newStops[8].offset = offset + rightDist * 2 / 3;
228 } else {
229 newStops[0].offset = offset1 + leftDist / 3;
230 newStops[1].offset = offset1 + leftDist * 2 / 3;
231 for (size_t y = 0; y < 7; ++y)
232 newStops[y + 2].offset = offset + rightDist * y / 13;
233 }
234
235 // calculate colors for the new color hints.
236 // The color weighting for the new color stops will be pointRelativeOffs et^(ln(0.5)/ln(hintRelativeOffset)).
237 float hintRelativeOffset = leftDist / totalDist;
238 for (size_t y = 0; y < 9; ++y) {
239 float pointRelativeOffset = (newStops[y].offset - offset1) / totalDi st;
240 float weighting = powf(pointRelativeOffset, logf(.5f) / logf(hintRel ativeOffset));
241 newStops[y].color = interpolate(leftColor, rightColor, weighting);
242 }
243
244 // Replace the color hint with the new color stops.
245 stops.remove(x);
246 stops.insert(x, newStops, 9);
247 indexOffset += 8;
248 }
249 }
250
142 void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionD ata& conversionData, float maxLengthForRepeat) 251 void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionD ata& conversionData, float maxLengthForRepeat)
143 { 252 {
144 if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDe precatedRadialGradient) { 253 if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDe precatedRadialGradient) {
145 sortStopsIfNeeded(); 254 sortStopsIfNeeded();
146 255
147 for (unsigned i = 0; i < m_stops.size(); i++) { 256 for (unsigned i = 0; i < m_stops.size(); i++) {
148 const CSSGradientColorStop& stop = m_stops[i]; 257 const CSSGradientColorStop& stop = m_stops[i];
149 258
150 float offset; 259 float offset;
151 if (stop.m_position->isPercentage()) 260 if (stop.m_position->isPercentage())
152 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_P ERCENTAGE) / 100; 261 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_P ERCENTAGE) / 100;
153 else 262 else
154 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_N UMBER); 263 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_N UMBER);
155 264
156 gradient->addColorStop(offset, stop.m_resolvedColor); 265 gradient->addColorStop(offset, stop.m_resolvedColor);
157 } 266 }
158 267
159 return; 268 return;
160 } 269 }
161 270
162 size_t numStops = m_stops.size(); 271 size_t numStops = m_stops.size();
163 272
164 Vector<GradientStop> stops(numStops); 273 Vector<GradientStop> stops(numStops);
165 274
166 float gradientLength = 0; 275 float gradientLength = 0;
167 bool computedGradientLength = false; 276 bool computedGradientLength = false;
168 277
278 size_t numColorHints = 0;
f(malita) 2014/10/08 16:13:54 I don't think we really care about the number here
rosca 2014/10/08 17:18:51 Done.
279
169 FloatPoint gradientStart = gradient->p0(); 280 FloatPoint gradientStart = gradient->p0();
170 FloatPoint gradientEnd; 281 FloatPoint gradientEnd;
171 if (isLinearGradientValue()) 282 if (isLinearGradientValue())
172 gradientEnd = gradient->p1(); 283 gradientEnd = gradient->p1();
173 else if (isRadialGradientValue()) 284 else if (isRadialGradientValue())
174 gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0); 285 gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
175 286
176 for (size_t i = 0; i < numStops; ++i) { 287 for (size_t i = 0; i < numStops; ++i) {
177 const CSSGradientColorStop& stop = m_stops[i]; 288 const CSSGradientColorStop& stop = m_stops[i];
178 289
179 stops[i].color = stop.m_resolvedColor; 290 if (stop.m_color)
f(malita) 2014/10/08 16:13:54 !stop.isHint()?
rosca 2014/10/08 17:18:51 Done.
291 stops[i].color = stop.m_resolvedColor;
292 else
293 ++numColorHints;
f(malita) 2014/10/08 16:13:54 hasHints = true?
rosca 2014/10/08 17:18:51 Done.
180 294
181 if (stop.m_position) { 295 if (stop.m_position) {
182 if (stop.m_position->isPercentage()) 296 if (stop.m_position->isPercentage())
183 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveVal ue::CSS_PERCENTAGE) / 100; 297 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveVal ue::CSS_PERCENTAGE) / 100;
184 else if (stop.m_position->isLength() || stop.m_position->isCalculate dPercentageWithLength()) { 298 else if (stop.m_position->isLength() || stop.m_position->isCalculate dPercentageWithLength()) {
185 if (!computedGradientLength) { 299 if (!computedGradientLength) {
186 FloatSize gradientSize(gradientStart - gradientEnd); 300 FloatSize gradientSize(gradientStart - gradientEnd);
187 gradientLength = gradientSize.diagonalLength(); 301 gradientLength = gradientSize.diagonalLength();
188 } 302 }
189 float length; 303 float length;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 361
248 for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j) 362 for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
249 stops[j].offset = lastSpecifiedOffset + (j - unspecified RunStart + 1) * delta; 363 stops[j].offset = lastSpecifiedOffset + (j - unspecified RunStart + 1) * delta;
250 } 364 }
251 365
252 inUnspecifiedRun = false; 366 inUnspecifiedRun = false;
253 } 367 }
254 } 368 }
255 } 369 }
256 370
371 ASSERT(stops.size() == m_stops.size());
372 if (numColorHints) {
f(malita) 2014/10/08 16:13:54 hasHints?
rosca 2014/10/08 17:18:51 Done.
373 replaceColorHintsWithColorStops(stops, m_stops);
374 numStops = stops.size();
375 }
376
257 // If the gradient is repeating, repeat the color stops. 377 // 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, 378 // 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. 379 // because we have to know the extent of the gradient, and possible move the end points.
260 if (m_repeating && numStops > 1) { 380 if (m_repeating && numStops > 1) {
261 // If the difference in the positions of the first and last color-stops is 0, 381 // 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. 382 // 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; 383 float gradientRange = stops[numStops - 1].offset - stops[0].offset;
264 if (!gradientRange) { 384 if (!gradientRange) {
265 stops.first().offset = 0; 385 stops.first().offset = 0;
266 stops.first().color = stops.last().color; 386 stops.first().color = stops.last().color;
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 582
463 if (stop.m_position->isFontRelativeLength()) 583 if (stop.m_position->isFontRelativeLength())
464 return false; 584 return false;
465 } 585 }
466 586
467 return true; 587 return true;
468 } 588 }
469 589
470 bool CSSGradientValue::knownToBeOpaque(const RenderObject*) const 590 bool CSSGradientValue::knownToBeOpaque(const RenderObject*) const
471 { 591 {
472 for (size_t i = 0; i < m_stops.size(); ++i) { 592 for (auto& stop : m_stops) {
473 if (m_stops[i].m_resolvedColor.hasAlpha()) 593 if (stop.m_color && stop.m_resolvedColor.hasAlpha())
f(malita) 2014/10/08 16:13:54 !stop.isHint()?
rosca 2014/10/08 17:18:52 Done.
474 return false; 594 return false;
475 } 595 }
476 return true; 596 return true;
477 } 597 }
478 598
479 void CSSGradientValue::traceAfterDispatch(Visitor* visitor) 599 void CSSGradientValue::traceAfterDispatch(Visitor* visitor)
480 { 600 {
481 visitor->trace(m_firstX); 601 visitor->trace(m_firstX);
482 visitor->trace(m_firstY); 602 visitor->trace(m_firstY);
483 visitor->trace(m_secondX); 603 visitor->trace(m_secondX);
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
573 wroteSomething = true; 693 wroteSomething = true;
574 } 694 }
575 695
576 if (wroteSomething) 696 if (wroteSomething)
577 result.appendLiteral(", "); 697 result.appendLiteral(", ");
578 698
579 for (unsigned i = 0; i < m_stops.size(); i++) { 699 for (unsigned i = 0; i < m_stops.size(); i++) {
580 const CSSGradientColorStop& stop = m_stops[i]; 700 const CSSGradientColorStop& stop = m_stops[i];
581 if (i) 701 if (i)
582 result.appendLiteral(", "); 702 result.appendLiteral(", ");
583 result.append(stop.m_color->cssText()); 703 if (stop.m_color)
584 if (stop.m_position) { 704 result.append(stop.m_color->cssText());
705 if (stop.m_color && stop.m_position)
585 result.append(' '); 706 result.append(' ');
707 if (stop.m_position)
586 result.append(stop.m_position->cssText()); 708 result.append(stop.m_position->cssText());
587 }
588 } 709 }
589 710
590 } 711 }
591 712
592 result.append(')'); 713 result.append(')');
593 return result.toString(); 714 return result.toString();
594 } 715 }
595 716
596 // Compute the endpoints so that a gradient of the given angle covers a box of t he given size. 717 // 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) 718 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
895 wroteSomething = true; 1016 wroteSomething = true;
896 } 1017 }
897 1018
898 if (wroteSomething) 1019 if (wroteSomething)
899 result.appendLiteral(", "); 1020 result.appendLiteral(", ");
900 1021
901 for (unsigned i = 0; i < m_stops.size(); i++) { 1022 for (unsigned i = 0; i < m_stops.size(); i++) {
902 const CSSGradientColorStop& stop = m_stops[i]; 1023 const CSSGradientColorStop& stop = m_stops[i];
903 if (i) 1024 if (i)
904 result.appendLiteral(", "); 1025 result.appendLiteral(", ");
905 result.append(stop.m_color->cssText()); 1026 if (stop.m_color)
906 if (stop.m_position) { 1027 result.append(stop.m_color->cssText());
1028 if (stop.m_color && stop.m_position)
907 result.append(' '); 1029 result.append(' ');
1030 if (stop.m_position)
908 result.append(stop.m_position->cssText()); 1031 result.append(stop.m_position->cssText());
909 }
910 } 1032 }
911 1033
912 } 1034 }
913 1035
914 result.append(')'); 1036 result.append(')');
915 return result.toString(); 1037 return result.toString();
916 } 1038 }
917 1039
918 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, const CSS ToLengthConversionData& conversionData, float* widthOrHeight) 1040 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, const CSS ToLengthConversionData& conversionData, float* widthOrHeight)
919 { 1041 {
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after
1198 visitor->trace(m_firstRadius); 1320 visitor->trace(m_firstRadius);
1199 visitor->trace(m_secondRadius); 1321 visitor->trace(m_secondRadius);
1200 visitor->trace(m_shape); 1322 visitor->trace(m_shape);
1201 visitor->trace(m_sizingBehavior); 1323 visitor->trace(m_sizingBehavior);
1202 visitor->trace(m_endHorizontalSize); 1324 visitor->trace(m_endHorizontalSize);
1203 visitor->trace(m_endVerticalSize); 1325 visitor->trace(m_endVerticalSize);
1204 CSSGradientValue::traceAfterDispatch(visitor); 1326 CSSGradientValue::traceAfterDispatch(visitor);
1205 } 1327 }
1206 1328
1207 } // namespace blink 1329 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698