Skip to content

Commit a75ce22

Browse files
committed
library: Optimize color space usage
1 parent d53f219 commit a75ce22

File tree

5 files changed

+169
-127
lines changed

5 files changed

+169
-127
lines changed

example/src/commonMain/kotlin/component/OtherComponent.kt

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ import top.yukonga.miuix.kmp.basic.TextField
5858
import top.yukonga.miuix.kmp.icon.MiuixIcons
5959
import top.yukonga.miuix.kmp.icon.icons.useful.Like
6060
import top.yukonga.miuix.kmp.theme.MiuixTheme
61-
import top.yukonga.miuix.kmp.utils.ColorUtils.colorToHsv
62-
import top.yukonga.miuix.kmp.utils.ColorUtils.colorToOkLab
61+
import top.yukonga.miuix.kmp.utils.toHsv
62+
import top.yukonga.miuix.kmp.utils.toOkLab
6363
import top.yukonga.miuix.kmp.utils.PressFeedbackType
6464
import kotlin.math.round
6565

@@ -327,16 +327,16 @@ fun LazyListScope.otherComponent(
327327
modifier = Modifier.padding(bottom = 12.dp),
328328
verticalAlignment = Alignment.CenterVertically
329329
) {
330-
val hsv = colorToHsv(selectedColor)
330+
val hsv = selectedColor.toHsv()
331331
Text(
332332
text = "HEX: #${selectedColor.toArgb().toHexString(HexFormat.UpperCase)}" +
333333
"\nRGBA: ${(selectedColor.red * 255).toInt()}, " +
334334
"${(selectedColor.green * 255).toInt()}, " +
335335
"${(selectedColor.blue * 255).toInt()}, " +
336336
"${(round(selectedColor.alpha * 100) / 100.0)}" +
337-
"\nHSVA: ${(hsv[0]).toInt()}, " +
338-
"${(hsv[1] * 100).toInt()}%, " +
339-
"${(hsv[2] * 100).toInt()}%, " +
337+
"\nHSVA: ${(hsv.h).toInt()}, " +
338+
"${(hsv.s).toInt()}%, " +
339+
"${(hsv.v).toInt()}%, " +
340340
"${(round(selectedColor.alpha * 100) / 100.0)}",
341341
modifier = Modifier.weight(1f)
342342
)
@@ -366,16 +366,16 @@ fun LazyListScope.otherComponent(
366366
modifier = Modifier.padding(bottom = 12.dp),
367367
verticalAlignment = Alignment.CenterVertically
368368
) {
369-
val hsv = colorToHsv(selectedColor)
369+
val hsv = selectedColor.toHsv()
370370
Text(
371371
text = "HEX: #${selectedColor.toArgb().toHexString(HexFormat.UpperCase)}" +
372372
"\nRGBA: ${(selectedColor.red * 255).toInt()}, " +
373373
"${(selectedColor.green * 255).toInt()}, " +
374374
"${(selectedColor.blue * 255).toInt()}, " +
375375
"${(round(selectedColor.alpha * 100) / 100.0)}" +
376-
"\nHSVA: ${(hsv[0]).toInt()}, " +
377-
"${(hsv[1] * 100).toInt()}%, " +
378-
"${(hsv[2] * 100).toInt()}%, " +
376+
"\nHSVA: ${(hsv.h).toInt()}, " +
377+
"${(hsv.s).toInt()}%, " +
378+
"${(hsv.v).toInt()}%, " +
379379
"${(round(selectedColor.alpha * 100) / 100.0)}",
380380
modifier = Modifier.weight(1f)
381381
)
@@ -405,16 +405,16 @@ fun LazyListScope.otherComponent(
405405
modifier = Modifier.padding(bottom = 12.dp),
406406
verticalAlignment = Alignment.CenterVertically
407407
) {
408-
val okLab = colorToOkLab(selectedColor)
408+
val ok = selectedColor.toOkLab()
409409
Text(
410410
text = "HEX: #${selectedColor.toArgb().toHexString(HexFormat.UpperCase)}" +
411411
"\nRGBA: ${(selectedColor.red * 255).toInt()}, " +
412412
"${(selectedColor.green * 255).toInt()}, " +
413413
"${(selectedColor.blue * 255).toInt()}, " +
414414
"${(round(selectedColor.alpha * 100) / 100.0)}" +
415-
"\nOkLab: ${(okLab[0] * 1000).toInt() / 1000f}, " +
416-
"${(okLab[1] * 1000).toInt() / 1000f}, " +
417-
"${(okLab[2] * 1000).toInt() / 1000f} / " +
415+
"\nOkLab: ${((ok.l * 1000).toInt() / 1000.0)}, " +
416+
"${((ok.a * 1000).toInt() / 1000.0)}, " +
417+
"${((ok.b * 1000).toInt() / 1000.0)} / " +
418418
"${(round(selectedColor.alpha * 100) / 100.0)}",
419419
modifier = Modifier.weight(1f)
420420
)
@@ -444,16 +444,16 @@ fun LazyListScope.otherComponent(
444444
modifier = Modifier.padding(bottom = 12.dp),
445445
verticalAlignment = Alignment.CenterVertically
446446
) {
447-
val hsv = colorToHsv(selectedColor)
447+
val hsv = selectedColor.toHsv()
448448
Text(
449449
text = "HEX: #${selectedColor.toArgb().toHexString(HexFormat.UpperCase)}" +
450450
"\nRGBA: ${(selectedColor.red * 255).toInt()}, " +
451451
"${(selectedColor.green * 255).toInt()}, " +
452452
"${(selectedColor.blue * 255).toInt()}, " +
453453
"${(round(selectedColor.alpha * 100) / 100.0)}" +
454-
"\nHSVA: ${(hsv[0]).toInt()}, " +
455-
"${(hsv[1] * 100).toInt()}%, " +
456-
"${(hsv[2] * 100).toInt()}%, " +
454+
"\nHSVA: ${(hsv.h).toInt()}, " +
455+
"${(hsv.s).toInt()}%, " +
456+
"${(hsv.v).toInt()}%, " +
457457
"${(round(selectedColor.alpha * 100) / 100.0)}",
458458
modifier = Modifier.weight(1f)
459459
)

iosApp/iosApp/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<key>CFBundleShortVersionString</key>
1818
<string>1.0.4</string>
1919
<key>CFBundleVersion</key>
20-
<string>519</string>
20+
<string>521</string>
2121
<key>LSRequiresIPhoneOS</key>
2222
<true/>
2323
<key>CADisableMinimumFrameDurationOnPhone</key>

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ColorPalette.kt

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ import androidx.compose.ui.unit.dp
3737
import androidx.compose.ui.util.lerp
3838
import top.yukonga.miuix.kmp.utils.CapsuleShape
3939
import top.yukonga.miuix.kmp.utils.G2RoundedCornerShape
40+
import top.yukonga.miuix.kmp.utils.Hsv
41+
import top.yukonga.miuix.kmp.utils.toHsv
4042
import kotlin.math.abs
41-
import kotlin.math.max
4243
import kotlin.math.min
4344
import kotlin.math.roundToInt
4445

@@ -80,7 +81,10 @@ fun ColorPalette(
8081
LaunchedEffect(initialColor, rows, hueColumns, includeGrayColumn) {
8182
if (lastEmittedColor?.let { colorsEqualApprox(it, initialColor) } == true) return@LaunchedEffect
8283

83-
val (h, s, v) = colorToHsvLocal(initialColor)
84+
val hsvInit = initialColor.toHsv()
85+
val h = hsvInit.h.toFloat()
86+
val s = (hsvInit.s / 100.0).toFloat()
87+
val v = (hsvInit.v / 100.0).toFloat()
8488
val currentHSV = Triple(h, s, v)
8589
if (lastAcceptedHSV?.let { hsvEqualApprox(it, currentHSV) } == true) {
8690
alpha = initialColor.alpha
@@ -136,14 +140,17 @@ fun ColorPalette(
136140
selectedCol = c
137141
val base = cellColor(c, r, rowSV, grayV, hueColumns, includeGrayColumn)
138142
val newColor = base.copy(alpha = alpha)
139-
lastAcceptedHSV = colorToHsvLocal(base)
143+
lastAcceptedHSV = base.toHsv().let { Triple(it.h.toFloat(), (it.s / 100.0).toFloat(), (it.v / 100.0).toFloat()) }
140144
lastEmittedColor = newColor
141145
onColorChangedState.value(newColor)
142146
}
143147
)
144148

145149
val base = cellColor(selectedCol, selectedRow, rowSV, grayV, hueColumns, includeGrayColumn)
146-
val (h, s, v) = colorToHsvLocal(base)
150+
val hsvBase = base.toHsv()
151+
val h = hsvBase.h.toFloat()
152+
val s = (hsvBase.s / 100.0).toFloat()
153+
val v = (hsvBase.v / 100.0).toFloat()
147154

148155
HsvAlphaSlider(
149156
currentHue = h,
@@ -153,7 +160,7 @@ fun ColorPalette(
153160
onAlphaChanged = {
154161
alpha = it
155162
val newColor = base.copy(alpha = it)
156-
lastAcceptedHSV = colorToHsvLocal(base)
163+
lastAcceptedHSV = base.toHsv().let { Triple(it.h.toFloat(), (it.s / 100.0).toFloat(), (it.v / 100.0).toFloat()) }
157164
lastEmittedColor = newColor
158165
onColorChangedState.value(newColor)
159166
}
@@ -292,11 +299,11 @@ private fun cellColor(
292299
val totalColumns = hueColumns + if (includeGrayColumn) 1 else 0
293300
val (s, v) = rowSV[row]
294301
return if (includeGrayColumn && col == totalColumns - 1) {
295-
Color.hsv(0f, 0f, grayV[row])
302+
Hsv(0.0, (grayV[row] * 100.0), 0.0).toColor()
296303
} else {
297304
val step = 360f / hueColumns
298305
val h = (col * step) % 360f
299-
Color.hsv(h, s, v)
306+
Hsv(h.toDouble(), (v * 100.0), (s * 100.0)).toColor()
300307
}
301308
}
302309

@@ -326,26 +333,6 @@ private fun hsvEqualApprox(
326333
return dh <= epsH && abs(a.second - b.second) <= eps && abs(a.third - b.third) <= eps
327334
}
328335

329-
private fun colorToHsvLocal(color: Color): Triple<Float, Float, Float> {
330-
val r = color.red
331-
val g = color.green
332-
val b = color.blue
333-
334-
val max = max(r, max(g, b))
335-
val min = min(r, min(g, b))
336-
val delta = max - min
337-
338-
val h = when {
339-
delta == 0f -> 0f
340-
max == r -> ((60f * ((g - b) / delta) + 360f) % 360f)
341-
max == g -> (60f * ((b - r) / delta) + 120f)
342-
else -> (60f * ((r - g) / delta) + 240f)
343-
}
344-
val s = if (max == 0f) 0f else delta / max
345-
val v = max
346-
return Triple(h.coerceIn(0f, 360f), s.coerceIn(0f, 1f), v.coerceIn(0f, 1f))
347-
}
348-
349336
private fun <T> List<T>.indexOfMinBy(selector: (T) -> Float): Int {
350337
var idx = 0
351338
var minVal = Float.POSITIVE_INFINITY

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ColorPicker.kt

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ import androidx.compose.ui.unit.Dp
4141
import androidx.compose.ui.unit.dp
4242
import top.yukonga.miuix.kmp.utils.CapsuleShape
4343
import top.yukonga.miuix.kmp.utils.ColorUtils
44+
import top.yukonga.miuix.kmp.utils.Hsv
45+
import top.yukonga.miuix.kmp.utils.OkLab
46+
import top.yukonga.miuix.kmp.utils.toHsv
47+
import top.yukonga.miuix.kmp.utils.toOkLab
4448

4549
/**
4650
* A [ColorPicker] component with Miuix style that supports multiple color spaces.
@@ -117,14 +121,18 @@ fun HsvColorPicker(
117121
var currentValue by remember { mutableStateOf(0f) }
118122
var currentAlpha by remember { mutableStateOf(1f) }
119123

120-
val selectedColor = Color.hsv(currentHue, currentSaturation, currentValue, currentAlpha)
124+
val selectedColor = Hsv(
125+
h = currentHue.toDouble(),
126+
v = (currentValue * 100.0),
127+
s = (currentSaturation * 100.0)
128+
).toColor(currentAlpha)
121129

122130
LaunchedEffect(initialColor) {
123131
if (initialSetup) {
124-
val hsv = ColorUtils.colorToHsv(initialColor)
125-
currentHue = hsv[0]
126-
currentSaturation = hsv[1]
127-
currentValue = hsv[2]
132+
val hsv = initialColor.toHsv()
133+
currentHue = hsv.h.toFloat()
134+
currentSaturation = (hsv.s / 100.0).toFloat()
135+
currentValue = (hsv.v / 100.0).toFloat()
128136
currentAlpha = initialColor.alpha
129137
initialSetup = false
130138
}
@@ -230,8 +238,8 @@ fun HsvSaturationSlider(
230238
) {
231239
val saturationColors = remember(currentHue) {
232240
listOf(
233-
Color.hsv(currentHue, 0f, 1f, 1f),
234-
Color.hsv(currentHue, 1f, 1f, 1f)
241+
Hsv(currentHue.toDouble(), 100.0, 0.0).toColor(1f),
242+
Hsv(currentHue.toDouble(), 100.0, 100.0).toColor(1f)
235243
)
236244
}
237245
ColorSlider(
@@ -261,7 +269,7 @@ fun HsvValueSlider(
261269
hapticEffect: SliderDefaults.SliderHapticEffect = SliderDefaults.DefaultHapticEffect
262270
) {
263271
val valueColors = remember(currentHue, currentSaturation) {
264-
listOf(Color.Black, Color.hsv(currentHue, currentSaturation, 1f))
272+
listOf(Color.Black, Hsv(currentHue.toDouble(), 100.0, (currentSaturation * 100.0)).toColor())
265273
}
266274
ColorSlider(
267275
value = currentValue,
@@ -294,7 +302,7 @@ fun HsvAlphaSlider(
294302
val density = LocalDensity.current
295303

296304
val alphaColors = remember(currentHue, currentSaturation, currentValue) {
297-
val baseColor = Color.hsv(currentHue, currentSaturation, currentValue)
305+
val baseColor = Hsv(currentHue.toDouble(), (currentValue * 100.0), (currentSaturation * 100.0)).toColor()
298306
listOf(baseColor.copy(alpha = 0f), baseColor.copy(alpha = 1f))
299307
}
300308

@@ -567,16 +575,19 @@ fun OkLabColorPicker(
567575
var currentAlpha by remember { mutableStateOf(1f) }
568576

569577
val selectedColor = remember(currentL, currentA, currentB, currentAlpha) {
570-
val okLabColor = ColorUtils.createOkLabColor(currentL, currentA, currentB)
571-
ColorUtils.okLabToColor(okLabColor, currentAlpha)
578+
OkLab(
579+
l = (currentL * 100.0),
580+
a = (currentA / 0.4 * 100.0),
581+
b = (currentB / 0.4 * 100.0)
582+
).toColor(currentAlpha)
572583
}
573584

574585
LaunchedEffect(initialColor) {
575586
if (initialSetup) {
576-
val okLabColor = ColorUtils.colorToOkLab(initialColor)
577-
currentL = okLabColor[0]
578-
currentA = okLabColor[1]
579-
currentB = okLabColor[2]
587+
val ok = initialColor.toOkLab()
588+
currentL = (ok.l / 100.0).toFloat()
589+
currentA = ((ok.a / 100.0) * 0.4).toFloat()
590+
currentB = ((ok.b / 100.0) * 0.4).toFloat()
580591
currentAlpha = initialColor.alpha
581592
initialSetup = false
582593
}
@@ -657,8 +668,11 @@ fun OkLabLightnessSlider(
657668
val steps = 7
658669
(0..steps).map { i ->
659670
val l = i.toFloat() / steps.toFloat()
660-
val okLabColor = ColorUtils.createOkLabColor(l, currentA, currentB)
661-
ColorUtils.okLabToColor(okLabColor)
671+
OkLab(
672+
l = l * 100.0,
673+
a = (currentA / 0.4 * 100.0),
674+
b = (currentB / 0.4 * 100.0)
675+
).toColor()
662676
}
663677
}
664678
ColorSlider(
@@ -687,8 +701,11 @@ fun OkLabAChannelSlider(
687701
val steps = 8
688702
(0..steps).map { i ->
689703
val a = minA + (maxA - minA) * i.toFloat() / steps.toFloat()
690-
val okLabColor = ColorUtils.createOkLabColor(currentL, a, currentB)
691-
ColorUtils.okLabToColor(okLabColor)
704+
OkLab(
705+
l = currentL * 100.0,
706+
a = (a / 0.4 * 100.0),
707+
b = (currentB / 0.4 * 100.0)
708+
).toColor()
692709
}
693710
}
694711
ColorSlider(
@@ -719,8 +736,11 @@ fun OkLabBChannelSlider(
719736
val steps = 8
720737
(0..steps).map { i ->
721738
val b = minB + (maxB - minB) * i.toFloat() / steps.toFloat()
722-
val okLabColor = ColorUtils.createOkLabColor(currentL, currentA, b)
723-
ColorUtils.okLabToColor(okLabColor)
739+
OkLab(
740+
l = currentL * 100.0,
741+
a = (currentA / 0.4 * 100.0),
742+
b = (b / 0.4 * 100.0)
743+
).toColor()
724744
}
725745
}
726746
ColorSlider(
@@ -749,8 +769,11 @@ fun OkLabAlphaSlider(
749769
val density = LocalDensity.current
750770

751771
val alphaColors = remember(currentL, currentA, currentB) {
752-
val okLabColor = ColorUtils.createOkLabColor(currentL, currentA, currentB)
753-
val baseColor = ColorUtils.okLabToColor(okLabColor)
772+
val baseColor = OkLab(
773+
l = currentL * 100.0,
774+
a = (currentA / 0.4 * 100.0),
775+
b = (currentB / 0.4 * 100.0)
776+
).toColor()
754777
listOf(baseColor.copy(alpha = 0f), baseColor.copy(alpha = 1f))
755778
}
756779

0 commit comments

Comments
 (0)