| /* |
| * Copyright 2026 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/GradientBitmap.h" |
| |
| #include "include/core/SkAlphaType.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkColorType.h" |
| #include "include/core/SkImageInfo.h" |
| #include "include/core/SkRect.h" |
| #include "include/private/SkAssert.h" |
| #include "src/core/SkHalf.h" |
| |
| #include <cmath> |
| |
| namespace skgpu { |
| |
| bool EncodeGradientStopToHalf(float offset, float* mantissa, float* exponent) { |
| int exp; |
| float mant = std::frexp(offset, &exp); |
| |
| SkHalf halfE = SkFloatToHalf(static_cast<float>(exp)); |
| if (static_cast<int>(SkHalfToFloat(halfE)) != exp) { |
| return false; |
| } |
| |
| #if defined(SK_DEBUG) |
| SkHalf halfM = SkFloatToHalf(mant); |
| float restored = std::ldexp(SkHalfToFloat(halfM), exp); |
| SkASSERT(std::abs(restored - offset) < 0.001f); |
| #endif |
| |
| *mantissa = mant; |
| *exponent = static_cast<float>(exp); |
| return true; |
| } |
| |
| SkBitmap CreateGradientColorAndOffsetBitmap(int numStops, |
| const SkPMColor4f* colors, |
| const float* offsets) { |
| SkBitmap colorsAndOffsetsBitmap; |
| if (!colorsAndOffsetsBitmap.tryAllocPixels( |
| SkImageInfo::Make(numStops, 2, kRGBA_F16_SkColorType, kPremul_SkAlphaType))) { |
| return {}; |
| } |
| |
| for (int i = 0; i < numStops; i++) { |
| // TODO: there should be a way to directly set a premul pixel in a bitmap with |
| // a premul color. |
| SkColor4f unpremulColor = colors[i].unpremul(); |
| colorsAndOffsetsBitmap.erase(unpremulColor, SkIRect::MakeXYWH(i, 0, 1, 1)); |
| |
| float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops - 1); |
| SkASSERT(offset >= 0.0f && offset <= 1.0f); |
| |
| float mantissa, exponent; |
| if (!EncodeGradientStopToHalf(offset, &mantissa, &exponent)) { |
| return {}; |
| } |
| |
| // TODO: we're only using 2 of the f16s here. The encoding could be altered to better |
| // preserve precision. This encoding yields < 0.001f error for 2^20 evenly spaced stops. |
| colorsAndOffsetsBitmap.erase(SkColor4f{mantissa, exponent, 0, 1}, |
| SkIRect::MakeXYWH(i, 1, 1, 1)); |
| } |
| |
| return colorsAndOffsetsBitmap; |
| } |
| |
| } // namespace skgpu |