import { internalRoundColorFloat } from "../internal.ts" import type { HslColorValue, HsvColorValue, LinearRgbColorValue, SrgbColorValue } from "../types.ts" import { internalGetLinearRgbChannelBySrgbChannel, internalGetSrgbChannelByLinearRgbChannel, internalNormalizeHslColorValue, internalNormalizeHsvColorValue, internalNormalizeLinearRgbColorValue, internalNormalizeSrgbColorValue, } from "./internal.ts" const internalGetHueByNormalizedRgbChannels = ( red: number, green: number, blue: number, delta: number, max: number, ): number => { if (delta === 0) { return 0 } let hue = 0 if (max === red) { hue = 60 * (((green - blue) / delta) % 6) } else if (max === green) { hue = 60 * ((blue - red) / delta + 2) } else { hue = 60 * ((red - green) / delta + 4) } if (hue < 0) { return hue + 360 } return hue } const internalGetNormalizedRgbChannelsByHue = ( hue: number, chroma: number, ): { red: number; green: number; blue: number } => { const segment = hue / 60 const secondary = chroma * (1 - Math.abs((segment % 2) - 1)) if (segment >= 0 && segment < 1) { return { red: chroma, green: secondary, blue: 0 } } if (segment >= 1 && segment < 2) { return { red: secondary, green: chroma, blue: 0 } } if (segment >= 2 && segment < 3) { return { red: 0, green: chroma, blue: secondary } } if (segment >= 3 && segment < 4) { return { red: 0, green: secondary, blue: chroma } } if (segment >= 4 && segment < 5) { return { red: secondary, green: 0, blue: chroma } } return { red: chroma, green: 0, blue: secondary } } /** * @description 将 Linear RGB 颜色值转换为 sRGB 颜色值。 * * @example * ``` * // Expect: { red: 255, green: 0, blue: 0, alpha: 1 } * const example1 = linearRgbColorValueToSrgbColorValue({ red: 1, green: 0, blue: 0, alpha: 1 }) * * // Expect: { red: 188, green: 188, blue: 188, alpha: 0.5 } * const example2 = linearRgbColorValueToSrgbColorValue({ red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5 }) * ``` */ export const linearRgbColorValueToSrgbColorValue = (color: LinearRgbColorValue): SrgbColorValue => { const normalizedColor = internalNormalizeLinearRgbColorValue(color) return internalNormalizeSrgbColorValue({ red: internalGetSrgbChannelByLinearRgbChannel(normalizedColor.red, "red"), green: internalGetSrgbChannelByLinearRgbChannel(normalizedColor.green, "green"), blue: internalGetSrgbChannelByLinearRgbChannel(normalizedColor.blue, "blue"), alpha: normalizedColor.alpha, }) } /** * @description 将 sRGB 颜色值转换为 Linear RGB 颜色值。 * * @example * ``` * // Expect: { red: 1, green: 0, blue: 0, alpha: 1 } * const example1 = srgbColorValueToLinearRgbColorValue({ red: 255, green: 0, blue: 0, alpha: 1 }) * * // Expect: { red: 0.215861, green: 0.215861, blue: 0.215861, alpha: 0.5 } * const example2 = srgbColorValueToLinearRgbColorValue({ red: 128, green: 128, blue: 128, alpha: 0.5 }) * ``` */ export const srgbColorValueToLinearRgbColorValue = (color: SrgbColorValue): LinearRgbColorValue => { const normalizedColor = internalNormalizeSrgbColorValue(color) return internalNormalizeLinearRgbColorValue({ red: internalGetLinearRgbChannelBySrgbChannel(normalizedColor.red), green: internalGetLinearRgbChannelBySrgbChannel(normalizedColor.green), blue: internalGetLinearRgbChannelBySrgbChannel(normalizedColor.blue), alpha: normalizedColor.alpha, }) } /** * @description 将 sRGB 颜色值转换为 HSL 颜色值。 * * @example * ``` * // Expect: { hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 } * const example1 = srgbColorValueToHslColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }) * * // Expect: { hue: 0, saturation: 0, lightness: 0.501961, alpha: 1 } * const example2 = srgbColorValueToHslColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }) * ``` */ export const srgbColorValueToHslColorValue = (color: SrgbColorValue): HslColorValue => { const normalizedColor = internalNormalizeSrgbColorValue(color) const red = normalizedColor.red / 255 const green = normalizedColor.green / 255 const blue = normalizedColor.blue / 255 const max = Math.max(red, green, blue) const min = Math.min(red, green, blue) const delta = max - min const lightness = (max + min) / 2 const saturation = delta === 0 ? 0 : delta / (1 - Math.abs(2 * lightness - 1)) const hue = internalGetHueByNormalizedRgbChannels(red, green, blue, delta, max) return internalNormalizeHslColorValue({ hue: internalRoundColorFloat(hue), saturation: internalRoundColorFloat(saturation), lightness: internalRoundColorFloat(lightness), alpha: normalizedColor.alpha, }) } /** * @description 将 HSL 颜色值转换为 sRGB 颜色值。 * * @example * ``` * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 } * const example1 = hslColorValueToSrgbColorValue({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 }) * * // Expect: { red: 128, green: 128, blue: 128, alpha: 1 } * const example2 = hslColorValueToSrgbColorValue({ hue: 0, saturation: 0, lightness: 0.501961, alpha: 1 }) * ``` */ export const hslColorValueToSrgbColorValue = (color: HslColorValue): SrgbColorValue => { const normalizedColor = internalNormalizeHslColorValue(color) const chroma = (1 - Math.abs(2 * normalizedColor.lightness - 1)) * normalizedColor.saturation const channels = internalGetNormalizedRgbChannelsByHue(normalizedColor.hue, chroma) const match = normalizedColor.lightness - chroma / 2 return internalNormalizeSrgbColorValue({ red: Math.round((channels.red + match) * 255), green: Math.round((channels.green + match) * 255), blue: Math.round((channels.blue + match) * 255), alpha: normalizedColor.alpha, }) } /** * @description 将 sRGB 颜色值转换为 HSV 颜色值。 * * @example * ``` * // Expect: { hue: 210, saturation: 0.666667, value: 0.6, alpha: 1 } * const example1 = srgbColorValueToHsvColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }) * * // Expect: { hue: 0, saturation: 0, value: 0.501961, alpha: 1 } * const example2 = srgbColorValueToHsvColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }) * ``` */ export const srgbColorValueToHsvColorValue = (color: SrgbColorValue): HsvColorValue => { const normalizedColor = internalNormalizeSrgbColorValue(color) const red = normalizedColor.red / 255 const green = normalizedColor.green / 255 const blue = normalizedColor.blue / 255 const max = Math.max(red, green, blue) const min = Math.min(red, green, blue) const delta = max - min const saturation = max === 0 ? 0 : delta / max const hue = internalGetHueByNormalizedRgbChannels(red, green, blue, delta, max) return internalNormalizeHsvColorValue({ hue: internalRoundColorFloat(hue), saturation: internalRoundColorFloat(saturation), value: internalRoundColorFloat(max), alpha: normalizedColor.alpha, }) } /** * @description 将 HSV 颜色值转换为 sRGB 颜色值。 * * @example * ``` * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 } * const example1 = hsvColorValueToSrgbColorValue({ hue: 210, saturation: 0.666667, value: 0.6, alpha: 1 }) * * // Expect: { red: 128, green: 128, blue: 128, alpha: 1 } * const example2 = hsvColorValueToSrgbColorValue({ hue: 0, saturation: 0, value: 0.501961, alpha: 1 }) * ``` */ export const hsvColorValueToSrgbColorValue = (color: HsvColorValue): SrgbColorValue => { const normalizedColor = internalNormalizeHsvColorValue(color) const chroma = normalizedColor.value * normalizedColor.saturation const channels = internalGetNormalizedRgbChannelsByHue(normalizedColor.hue, chroma) const match = normalizedColor.value - chroma return internalNormalizeSrgbColorValue({ red: Math.round((channels.red + match) * 255), green: Math.round((channels.green + match) * 255), blue: Math.round((channels.blue + match) * 255), alpha: normalizedColor.alpha, }) }