// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "flutter/fml/logging.h" #include "impeller/geometry/gradient.h" namespace impeller { static void AppendColor(const Color& color, GradientData* data) { auto converted = color.ToR8G8B8A8(); data->color_bytes.push_back(converted[0]); data->color_bytes.push_back(converted[1]); data->color_bytes.push_back(converted[2]); data->color_bytes.push_back(converted[3]); } GradientData CreateGradientBuffer(const std::vector& colors, const std::vector& stops) { FML_DCHECK(stops.size() == colors.size()); uint32_t texture_size; if (stops.size() == 2) { texture_size = colors.size(); } else { auto minimum_delta = 1.0; for (size_t i = 1; i < stops.size(); i++) { auto value = stops[i] - stops[i - 1]; // Smaller than kEhCloseEnough if (value < 0.0001) { continue; } if (value < minimum_delta) { minimum_delta = value; } } // Avoid creating textures that are absurdly large due to stops that are // very close together. // TODO(jonahwilliams): this should use a platform specific max texture // size. texture_size = std::min( static_cast(std::round(1.0 / minimum_delta)) + 1, 1024u); } GradientData data = { .color_bytes = {}, .texture_size = texture_size, }; data.color_bytes.reserve(texture_size * 4); if (texture_size == colors.size() && colors.size() <= 1024) { for (auto i = 0u; i < colors.size(); i++) { AppendColor(colors[i], &data); } } else { Color previous_color = colors[0]; auto previous_stop = 0.0; auto previous_color_index = 0; // The first index is always equal to the first color, exactly. AppendColor(previous_color, &data); for (auto i = 1u; i < texture_size - 1; i++) { auto scaled_i = i / (texture_size - 1.0); Color next_color = colors[previous_color_index + 1]; auto next_stop = stops[previous_color_index + 1]; // We're almost exactly equal to the next stop. if (ScalarNearlyEqual(scaled_i, next_stop)) { AppendColor(next_color, &data); previous_color = next_color; previous_stop = next_stop; previous_color_index += 1; } else if (scaled_i < next_stop) { // We're still between the current stop and the next stop. auto t = (scaled_i - previous_stop) / (next_stop - previous_stop); auto mixed_color = Color::Lerp(previous_color, next_color, t); AppendColor(mixed_color, &data); } else { // We've slightly overshot the previous stop. previous_color = next_color; previous_stop = next_stop; previous_color_index += 1; next_color = colors[previous_color_index + 1]; auto next_stop = stops[previous_color_index + 1]; auto t = (scaled_i - previous_stop) / (next_stop - previous_stop); auto mixed_color = Color::Lerp(previous_color, next_color, t); AppendColor(mixed_color, &data); } } // The last index is always equal to the last color, exactly. AppendColor(colors.back(), &data); } return data; } } // namespace impeller