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

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

Powered by Google App Engine
This is Rietveld 408576698