diff --git a/.jules/bolt.md b/.jules/bolt.md index 2d59eae..8832205 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,6 @@ ## 2024-07-14 - Optimize Hex Color Parsing and Formatting in KMP Hot Paths **Learning:** In Kotlin Multiplatform hot paths (such as hex color serialization and parsing), manual character array manipulation and bitwise shifts are substantially faster (up to 25x faster for serialization and 10x faster for parsing) than using standard library string methods like `toString(16).padStart()`, `uppercase()`, or `substring().toLong(16).toInt()`. This approach avoids platform-specific string allocation overhead. **Action:** When working in KMP hot paths, avoid standard library string manipulations that instantiate multiple objects per call. Favor `CharArray`, manual index iteration, bitwise shifts, and `concatToString()` to minimize allocations and latency. +## 2024-07-15 - Optimize Discrete Math Conversions with Precomputed Look-Up Tables +**Learning:** In Kotlin Multiplatform, when mathematical operations depend strictly on a small, discrete domain (e.g., converting 8-bit color components 0-255 from sRGB to linear space), utilizing pre-computed arrays or lookup tables (like `DoubleArray(256)`) instead of redundant on-the-fly computation (divisions, conditionals, `.pow()`) significantly improves performance in hot paths like HCT or XYZ conversions. +**Action:** Identify expensive math operations inside loops or frequently called utility functions. If the input domain is small and bounded (e.g., 0-255 or 0-360), replace the math with a pre-computed array/list lookup. diff --git a/halogen-core/src/commonMain/kotlin/halogen/color/ColorUtils.kt b/halogen-core/src/commonMain/kotlin/halogen/color/ColorUtils.kt index c4b7893..2e0fd5d 100644 --- a/halogen-core/src/commonMain/kotlin/halogen/color/ColorUtils.kt +++ b/halogen-core/src/commonMain/kotlin/halogen/color/ColorUtils.kt @@ -85,15 +85,23 @@ internal object ColorUtils { return 116.0 * labF(y / 100.0) - 16.0 } - fun linearized(rgbComponent: Int): Double { + // ⚡ Bolt: Optimize sRGB to linear conversion with a Look-Up Table (LUT). + // Pre-computing values for the discrete 0-255 domain avoids expensive floating-point + // math and Math.pow() calls in hot paths (e.g., color conversions), drastically + // reducing processing time. + private val LINEARIZED_LUT: DoubleArray = DoubleArray(256) { rgbComponent -> val normalized = rgbComponent / 255.0 - return if (normalized <= 0.040449936) { + if (normalized <= 0.040449936) { normalized / 12.92 * 100.0 } else { ((normalized + 0.055) / 1.055).pow(2.4) * 100.0 } } + fun linearized(rgbComponent: Int): Double { + return LINEARIZED_LUT[rgbComponent] + } + fun delinearized(rgbComponent: Double): Int { val normalized = rgbComponent / 100.0 val delinearized: Double = if (normalized <= 0.0031308) {