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

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: offset1->offsetLeft 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 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
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
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
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
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
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
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