@@ -187,194 +187,5 @@ + (CALayer *)gradientLayerWithSize:(CGSize)size gradient:(const RadialGradient &
187187 return gradientLayer;
188188}
189189
190- // https://drafts.csswg.org/css-images-4/#color-stop-fixup
191- static std::vector<ProcessedColorStop> getFixedColorStops (
192- const std::vector<ColorStop> &colorStops,
193- CGFloat gradientLineLength)
194- {
195- std::vector<ProcessedColorStop> fixedColorStops (colorStops.size ());
196- bool hasNullPositions = false ;
197- auto maxPositionSoFar = resolveColorStopPosition (colorStops[0 ].position , gradientLineLength);
198- if (!maxPositionSoFar.has_value ()) {
199- maxPositionSoFar = 0 .0f ;
200- }
201-
202- for (size_t i = 0 ; i < colorStops.size (); i++) {
203- const auto &colorStop = colorStops[i];
204- auto newPosition = resolveColorStopPosition (colorStop.position , gradientLineLength);
205-
206- if (!newPosition.has_value ()) {
207- // Step 1:
208- // If the first color stop does not have a position,
209- // set its position to 0%. If the last color stop does not have a position,
210- // set its position to 100%.
211- if (i == 0 ) {
212- newPosition = 0 .0f ;
213- } else if (i == colorStops.size () - 1 ) {
214- newPosition = 1 .0f ;
215- }
216- }
217-
218- // Step 2:
219- // If a color stop or transition hint has a position
220- // that is less than the specified position of any color stop or transition hint
221- // before it in the list, set its position to be equal to the
222- // largest specified position of any color stop or transition hint before it.
223- if (newPosition.has_value ()) {
224- newPosition = std::max (newPosition.value (), maxPositionSoFar.value ());
225- fixedColorStops[i] = ProcessedColorStop{colorStop.color , newPosition};
226- maxPositionSoFar = newPosition;
227- } else {
228- hasNullPositions = true ;
229- }
230- }
231-
232- // Step 3:
233- // If any color stop still does not have a position,
234- // then, for each run of adjacent color stops without positions,
235- // set their positions so that they are evenly spaced between the preceding and
236- // following color stops with positions.
237- if (hasNullPositions) {
238- size_t lastDefinedIndex = 0 ;
239- for (size_t i = 1 ; i < fixedColorStops.size (); i++) {
240- auto endPosition = fixedColorStops[i].position ;
241- if (endPosition.has_value ()) {
242- size_t unpositionedStops = i - lastDefinedIndex - 1 ;
243- if (unpositionedStops > 0 ) {
244- auto startPosition = fixedColorStops[lastDefinedIndex].position ;
245- if (startPosition.has_value ()) {
246- auto increment = (endPosition.value () - startPosition.value ()) / (unpositionedStops + 1 );
247- for (size_t j = 1 ; j <= unpositionedStops; j++) {
248- fixedColorStops[lastDefinedIndex + j] =
249- ProcessedColorStop{colorStops[lastDefinedIndex + j].color , startPosition.value () + increment * j};
250- }
251- }
252- }
253- lastDefinedIndex = i;
254- }
255- }
256- }
257-
258- return fixedColorStops;
259- }
260-
261- static std::optional<Float> resolveColorStopPosition (ValueUnit position, CGFloat gradientLineLength)
262- {
263- if (position.unit == UnitType::Point) {
264- return position.resolve (0 .0f ) / gradientLineLength;
265- }
266-
267- if (position.unit == UnitType::Percent) {
268- return position.resolve (1 .0f );
269- }
270-
271- return std::nullopt ;
272- }
273-
274- // Spec: https://drafts.csswg.org/css-images-4/#coloring-gradient-line (Refer transition hint section)
275- // Browsers add 9 intermediate color stops when a transition hint is present
276- // Algorithm is referred from Blink engine
277- // [source](https://github.com/chromium/chromium/blob/a296b1bad6dc1ed9d751b7528f7ca2134227b828/third_party/blink/renderer/core/css/css_gradient_value.cc#L240).
278- static std::vector<ProcessedColorStop> processColorTransitionHints (const std::vector<ProcessedColorStop> &originalStops)
279- {
280- auto colorStops = std::vector<ProcessedColorStop>(originalStops);
281- int indexOffset = 0 ;
282-
283- for (size_t i = 1 ; i < originalStops.size () - 1 ; ++i) {
284- // Skip if not a color hint
285- if (originalStops[i].color ) {
286- continue ;
287- }
288-
289- size_t x = i + indexOffset;
290- if (x < 1 ) {
291- continue ;
292- }
293-
294- auto offsetLeft = colorStops[x - 1 ].position .value ();
295- auto offsetRight = colorStops[x + 1 ].position .value ();
296- auto offset = colorStops[x].position .value ();
297- auto leftDist = offset - offsetLeft;
298- auto rightDist = offsetRight - offset;
299- auto totalDist = offsetRight - offsetLeft;
300- SharedColor leftSharedColor = colorStops[x - 1 ].color ;
301- SharedColor rightSharedColor = colorStops[x + 1 ].color ;
302-
303- if (facebook::react::floatEquality (leftDist, rightDist)) {
304- colorStops.erase (colorStops.begin () + x);
305- --indexOffset;
306- continue ;
307- }
308-
309- if (facebook::react::floatEquality (leftDist, .0f )) {
310- colorStops[x].color = rightSharedColor;
311- continue ;
312- }
313-
314- if (facebook::react::floatEquality (rightDist, .0f )) {
315- colorStops[x].color = leftSharedColor;
316- continue ;
317- }
318-
319- std::vector<ProcessedColorStop> newStops;
320- newStops.reserve (9 );
321-
322- // Position the new color stops
323- if (leftDist > rightDist) {
324- for (int y = 0 ; y < 7 ; ++y) {
325- ProcessedColorStop newStop{SharedColor (), offsetLeft + leftDist * ((7 .0f + y) / 13 .0f )};
326- newStops.push_back (newStop);
327- }
328- ProcessedColorStop stop1{SharedColor (), offset + rightDist * (1 .0f / 3 .0f )};
329- ProcessedColorStop stop2{SharedColor (), offset + rightDist * (2 .0f / 3 .0f )};
330- newStops.push_back (stop1);
331- newStops.push_back (stop2);
332- } else {
333- ProcessedColorStop stop1{SharedColor (), offsetLeft + leftDist * (1 .0f / 3 .0f )};
334- ProcessedColorStop stop2{SharedColor (), offsetLeft + leftDist * (2 .0f / 3 .0f )};
335- newStops.push_back (stop1);
336- newStops.push_back (stop2);
337- for (int y = 0 ; y < 7 ; ++y) {
338- ProcessedColorStop newStop{SharedColor (), offset + rightDist * (y / 13 .0f )};
339- newStops.push_back (newStop);
340- }
341- }
342-
343- // calculate colors for the new color hints.
344- // The color weighting for the new color stops will be
345- // pointRelativeOffset^(ln(0.5)/ln(hintRelativeOffset)).
346- auto hintRelativeOffset = leftDist / totalDist;
347- const auto logRatio = log (0.5 ) / log (hintRelativeOffset);
348- auto leftColor = RCTUIColorFromSharedColor (leftSharedColor);
349- auto rightColor = RCTUIColorFromSharedColor (rightSharedColor);
350- NSArray <NSNumber *> *inputRange = @[ @0.0 , @1.0 ];
351- NSArray <UIColor *> *outputRange = @[ leftColor, rightColor ];
352-
353- for (auto &newStop : newStops) {
354- auto pointRelativeOffset = (newStop.position .value () - offsetLeft) / totalDist;
355- auto weighting = pow (pointRelativeOffset, logRatio);
356-
357- if (!std::isfinite (weighting) || std::isnan (weighting)) {
358- continue ;
359- }
360-
361- auto interpolatedColor = RCTInterpolateColorInRange (weighting, inputRange, outputRange);
362-
363- auto alpha = (interpolatedColor >> 24 ) & 0xFF ;
364- auto red = (interpolatedColor >> 16 ) & 0xFF ;
365- auto green = (interpolatedColor >> 8 ) & 0xFF ;
366- auto blue = interpolatedColor & 0xFF ;
367-
368- newStop.color = facebook::react::colorFromRGBA (red, green, blue, alpha);
369- }
370-
371- // Replace the color hint with new color stops
372- colorStops.erase (colorStops.begin () + x);
373- colorStops.insert (colorStops.begin () + x, newStops.begin (), newStops.end ());
374- indexOffset += 8 ;
375- }
376-
377- return colorStops;
378- }
379190
380191@end
0 commit comments