Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Read by IntelliJ, Android Studio, VS Code and ktlint alike, which is what makes this
# the file that actually governs formatting.
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[*.{kt,kts}]
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
# Trailing commas keep the diff for a new parameter to a single added line, instead of
# also touching the line before it.
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true

[*.{yml,yaml,json}]
indent_size = 2

[*.md]
# Two trailing spaces are a hard line break in Markdown, so they must survive.
trim_trailing_whitespace = false

[*.bat]
end_of_line = crlf
32 changes: 32 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Every text file is stored with LF in the repository and checked out with LF, whatever
# operating system the contributor is on. Without this, a Windows checkout can commit CRLF
# and the next Linux contributor sees a whole-file diff that changes nothing.
* text=auto eol=lf

# Windows batch files are the exception: cmd.exe expects CRLF, so these are checked out
# with CRLF while still being stored as LF.
*.bat text eol=crlf
*.cmd text eol=crlf

# Marked binary so no line-ending normalisation is ever attempted on them. This matters:
# a CRLF pass that treats a jar or a PNG as text rewrites bytes inside the compressed
# stream and silently corrupts the file.
*.jar binary
*.klib binary
*.zip binary
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.webp binary
*.ico binary
*.ttf binary
*.otf binary
*.woff binary
*.woff2 binary
*.keystore binary
*.jks binary

# Generated, and noisy in review.
kotlin-js-store/** linguist-generated=true
colorpicker/api/** linguist-generated=true
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ public fun Int.toRgbColor(): RgbColor = RgbColor(

internal fun argb(alpha: Int, red: Int, green: Int, blue: Int): Int =
((alpha.coerceIn(0, 255) and 0xFF) shl 24) or
((red.coerceIn(0, 255) and 0xFF) shl 16) or
((green.coerceIn(0, 255) and 0xFF) shl 8) or
(blue.coerceIn(0, 255) and 0xFF)
((red.coerceIn(0, 255) and 0xFF) shl 16) or
((green.coerceIn(0, 255) and 0xFF) shl 8) or
(blue.coerceIn(0, 255) and 0xFF)

internal fun setAlphaComponent(color: Int, alpha: Int): Int =
(color and 0x00FFFFFF) or ((alpha.coerceIn(0, 255) and 0xFF) shl 24)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public fun String.toRgbColorOrNull(): RgbColor? {
append(char)
}
}

6 -> "FF$hex"
8 -> hex
else -> return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,29 @@ public fun HslColor.toRgb(): RgbColor {
val b: Float

when (hueSegment) {
0 -> { r = c + m; g = x + m; b = m }
1 -> { r = x + m; g = c + m; b = m }
2 -> { r = m; g = c + m; b = x + m }
3 -> { r = m; g = x + m; b = c + m }
4 -> { r = x + m; g = m; b = c + m }
else -> { r = c + m; g = m; b = x + m }
0 -> {
r = c + m; g = x + m; b = m
}

1 -> {
r = x + m; g = c + m; b = m
}

2 -> {
r = m; g = c + m; b = x + m
}

3 -> {
r = m; g = x + m; b = c + m
}

4 -> {
r = x + m; g = m; b = c + m
}

else -> {
r = c + m; g = m; b = x + m
}
}

return RgbColor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import codes.side.colorpicker.model.RgbColor
internal fun RgbColor.contrastColor(): RgbColor {
// WCAG relative luminance on linearized sRGB channels.
val luminance = 0.2126 * linearize(red.toDouble()) +
0.7152 * linearize(green.toDouble()) +
0.0722 * linearize(blue.toDouble())
0.7152 * linearize(green.toDouble()) +
0.0722 * linearize(blue.toDouble())
return if (luminance > 0.179) RgbColor.Black else RgbColor.White
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,29 @@ public class CmykColor(
// "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero.
/** Cyan channel in `0..1`. */
public val cyan: Float = cyan + 0f

/** Magenta channel in `0..1`. */
public val magenta: Float = magenta + 0f

/** Yellow channel in `0..1`. */
public val yellow: Float = yellow + 0f

/** Key (black) channel in `0..1`. */
public val key: Float = key + 0f
override val alpha: Float = alpha + 0f

/** [cyan] scaled to `0..100` percent and rounded to the nearest integer. */
public val intCyan: Int get() = (cyan * 100f).roundToInt()

/** [magenta] scaled to `0..100` percent and rounded to the nearest integer. */
public val intMagenta: Int get() = (magenta * 100f).roundToInt()

/** [yellow] scaled to `0..100` percent and rounded to the nearest integer. */
public val intYellow: Int get() = (yellow * 100f).roundToInt()

/** [key] scaled to `0..100` percent and rounded to the nearest integer. */
public val intKey: Int get() = (key * 100f).roundToInt()

/** [alpha] scaled to `0..255` and rounded to the nearest integer. */
public val intAlpha: Int get() = (alpha * 255f).roundToInt()

Expand All @@ -61,16 +68,17 @@ public class CmykColor(
yellow: Float = this.yellow,
key: Float = this.key,
alpha: Float = this.alpha,
): CmykColor = CmykColor(cyan = cyan, magenta = magenta, yellow = yellow, key = key, alpha = alpha)
): CmykColor =
CmykColor(cyan = cyan, magenta = magenta, yellow = yellow, key = key, alpha = alpha)

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is CmykColor) return false
return cyan == other.cyan &&
magenta == other.magenta &&
yellow == other.yellow &&
key == other.key &&
alpha == other.alpha
magenta == other.magenta &&
yellow == other.yellow &&
key == other.key &&
alpha == other.alpha
}

override fun hashCode(): Int {
Expand All @@ -88,6 +96,7 @@ public class CmykColor(
public companion object {
/** Opaque black. */
public val Black: CmykColor = CmykColor(0f, 0f, 0f, 1f)

/** Opaque white. */
public val White: CmykColor = CmykColor(0f, 0f, 0f, 0f)

Expand All @@ -96,7 +105,13 @@ public class CmykColor(
* [key] in `0..100` percent, [alpha] in `0..255`. Unlike the constructor,
* out-of-range values are clamped instead of throwing.
*/
public fun fromInt(cyan: Int, magenta: Int, yellow: Int, key: Int, alpha: Int = 255): CmykColor = CmykColor(
public fun fromInt(
cyan: Int,
magenta: Int,
yellow: Int,
key: Int,
alpha: Int = 255,
): CmykColor = CmykColor(
cyan = (cyan / 100f).coerceIn(0f, 1f),
magenta = (magenta / 100f).coerceIn(0f, 1f),
yellow = (yellow / 100f).coerceIn(0f, 1f),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,23 @@ public class HslColor(
// "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero.
/** Hue in degrees, in `0..360` (exclusive) after normalization. */
public val hue: Float = if (hue == 360f) 0f else hue + 0f

/** Saturation in `0..1`. */
public val saturation: Float = saturation + 0f

/** Lightness in `0..1`. */
public val lightness: Float = lightness + 0f
override val alpha: Float = alpha + 0f

/** [hue] in degrees, rounded to the nearest integer. */
public val intHue: Int get() = hue.roundToInt()

/** [saturation] scaled to `0..100` percent and rounded to the nearest integer. */
public val intSaturation: Int get() = (saturation * 100f).roundToInt()

/** [lightness] scaled to `0..100` percent and rounded to the nearest integer. */
public val intLightness: Int get() = (lightness * 100f).roundToInt()

/** [alpha] scaled to `0..255` and rounded to the nearest integer. */
public val intAlpha: Int get() = (alpha * 255f).roundToInt()

Expand All @@ -62,9 +67,9 @@ public class HslColor(
if (this === other) return true
if (other !is HslColor) return false
return hue == other.hue &&
saturation == other.saturation &&
lightness == other.lightness &&
alpha == other.alpha
saturation == other.saturation &&
lightness == other.lightness &&
alpha == other.alpha
}

override fun hashCode(): Int {
Expand All @@ -81,8 +86,10 @@ public class HslColor(
public companion object {
/** Opaque black. */
public val Black: HslColor = HslColor(hue = 0f, saturation = 0f, lightness = 0f)

/** Opaque white. */
public val White: HslColor = HslColor(hue = 0f, saturation = 0f, lightness = 1f)

/** Opaque pure red. */
public val Red: HslColor = HslColor(hue = 0f, saturation = 1f, lightness = 0.5f)

Expand All @@ -91,11 +98,12 @@ public class HslColor(
* [saturation] and [lightness] in `0..100` percent, [alpha] in `0..255`.
* Unlike the constructor, out-of-range values are clamped instead of throwing.
*/
public fun fromInt(hue: Int, saturation: Int, lightness: Int, alpha: Int = 255): HslColor = HslColor(
hue = hue.toFloat().coerceIn(0f, 360f),
saturation = (saturation / 100f).coerceIn(0f, 1f),
lightness = (lightness / 100f).coerceIn(0f, 1f),
alpha = (alpha / 255f).coerceIn(0f, 1f),
)
public fun fromInt(hue: Int, saturation: Int, lightness: Int, alpha: Int = 255): HslColor =
HslColor(
hue = hue.toFloat().coerceIn(0f, 360f),
saturation = (saturation / 100f).coerceIn(0f, 1f),
lightness = (lightness / 100f).coerceIn(0f, 1f),
alpha = (alpha / 255f).coerceIn(0f, 1f),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,23 @@ public class LabColor(
// "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero.
/** Lightness (L*) in `0..100`. */
public val l: Float = l + 0f

/** Green-red axis (a*) in `-128..127`; negative is green, positive is red. */
public val a: Float = a + 0f

/** Blue-yellow axis (b*) in `-128..127`; negative is blue, positive is yellow. */
public val b: Float = b + 0f
override val alpha: Float = alpha + 0f

/** [l] rounded to the nearest integer. */
public val intL: Int get() = l.roundToInt()

/** [a] rounded to the nearest integer. */
public val intA: Int get() = a.roundToInt()

/** [b] rounded to the nearest integer. */
public val intB: Int get() = b.roundToInt()

/** [alpha] scaled to `0..255` and rounded to the nearest integer. */
public val intAlpha: Int get() = (alpha * 255f).roundToInt()

Expand All @@ -60,9 +65,9 @@ public class LabColor(
if (this === other) return true
if (other !is LabColor) return false
return l == other.l &&
a == other.a &&
b == other.b &&
alpha == other.alpha
a == other.a &&
b == other.b &&
alpha == other.alpha
}

override fun hashCode(): Int {
Expand All @@ -79,6 +84,7 @@ public class LabColor(
public companion object {
/** Opaque black. */
public val Black: LabColor = LabColor(l = 0f, a = 0f, b = 0f)

/** Opaque white. */
public val White: LabColor = LabColor(l = 100f, a = 0f, b = 0f)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,23 @@ public class RgbColor(
// "+ 0f" normalizes -0.0f to 0.0f so equality can't split on signed zero.
/** Red channel in `0..1`. */
public val red: Float = red + 0f

/** Green channel in `0..1`. */
public val green: Float = green + 0f

/** Blue channel in `0..1`. */
public val blue: Float = blue + 0f
override val alpha: Float = alpha + 0f

/** [red] scaled to `0..255` and rounded to the nearest integer. */
public val intRed: Int get() = (red * 255f).roundToInt()

/** [green] scaled to `0..255` and rounded to the nearest integer. */
public val intGreen: Int get() = (green * 255f).roundToInt()

/** [blue] scaled to `0..255` and rounded to the nearest integer. */
public val intBlue: Int get() = (blue * 255f).roundToInt()

/** [alpha] scaled to `0..255` and rounded to the nearest integer. */
public val intAlpha: Int get() = (alpha * 255f).roundToInt()

Expand All @@ -60,9 +65,9 @@ public class RgbColor(
if (this === other) return true
if (other !is RgbColor) return false
return red == other.red &&
green == other.green &&
blue == other.blue &&
alpha == other.alpha
green == other.green &&
blue == other.blue &&
alpha == other.alpha
}

override fun hashCode(): Int {
Expand All @@ -79,12 +84,16 @@ public class RgbColor(
public companion object {
/** Opaque black. */
public val Black: RgbColor = RgbColor(0f, 0f, 0f)

/** Opaque white. */
public val White: RgbColor = RgbColor(1f, 1f, 1f)

/** Opaque pure red. */
public val Red: RgbColor = RgbColor(1f, 0f, 0f)

/** Opaque pure green. */
public val Green: RgbColor = RgbColor(0f, 1f, 0f)

/** Opaque pure blue. */
public val Blue: RgbColor = RgbColor(0f, 0f, 1f)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ internal val ColorPickerStateSaver = Saver<ColorPickerState, FloatArray>(
}
}
color?.let { ColorPickerState(it) }
}
},
)

/**
Expand Down
Loading