diff options
-rw-r--r-- | src/config/config.nim | 7 | ||||
-rw-r--r-- | src/css/cascade.nim | 5 | ||||
-rw-r--r-- | src/display/term.nim | 55 | ||||
-rw-r--r-- | src/types/color.nim | 89 |
4 files changed, 88 insertions, 68 deletions
diff --git a/src/config/config.nim b/src/config/config.nim index 3144b6fa..cb42d6af 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -41,7 +41,7 @@ type formatmode*: Option[FormatMode] noformatmode*: FormatMode altscreen*: Option[bool] - mincontrast*: float + mincontrast*: int editor*: string tmpdir*: string siteconf: seq[StaticSiteConfig] @@ -220,10 +220,7 @@ proc parseConfig(config: Config, dir: string, t: TomlValue) = of "double-width-ambiguous": config.ambiguous_double = v.b of "minimum-contrast": - if v.vt == VALUE_INTEGER: - config.mincontrast = float(v.i) - else: - config.mincontrast = float(v.f) + config.mincontrast = int(v.i) of "force-clear": config.forceclear = v.b of "emulate-overline": config.emulateoverline = v.b of "external": diff --git a/src/css/cascade.nim b/src/css/cascade.nim index cfb34e4e..4c782ded 100644 --- a/src/css/cascade.nim +++ b/src/css/cascade.nim @@ -109,6 +109,10 @@ func calcPresentationalHints(element: Element): CSSComputedValues = of "center", "middle": set_cv(PROPERTY_TEXT_ALIGN, textalign, TEXT_ALIGN_CHA_CENTER) of "left": set_cv(PROPERTY_TEXT_ALIGN, textalign, TEXT_ALIGN_CHA_LEFT) of "right": set_cv(PROPERTY_TEXT_ALIGN, textalign, TEXT_ALIGN_CHA_RIGHT) + template map_text = + let c = parseLegacyColor(element.attr("text")) + if c.isSome: + set_cv(PROPERTY_COLOR, color, c.get) case element.tagType of TAG_DIV: @@ -132,6 +136,7 @@ func calcPresentationalHints(element: Element): CSSComputedValues = map_width of TAG_BODY: map_bgcolor + map_text of TAG_TEXTAREA: let textarea = HTMLTextAreaElement(element) set_cv(PROPERTY_WIDTH, length, CSSLength(unit: UNIT_CH, num: float64(textarea.cols))) diff --git a/src/display/term.nim b/src/display/term.nim index 5312bad2..073b5dfc 100644 --- a/src/display/term.nim +++ b/src/display/term.nim @@ -44,7 +44,7 @@ type canvas: FixedGrid pcanvas: FixedGrid attrs*: WindowAttributes - mincontrast: float + mincontrast: int colormode: ColorMode formatmode: FormatMode smcup: bool @@ -196,31 +196,15 @@ proc disableAltScreen(term: Terminal): string = else: return RMCUP() -proc distance(a, b: CellColor): float = - let a = if a.rgb: - a.rgbcolor +proc getRGB(a: CellColor, bg: bool): RGBColor = + if a.rgb: + return a.rgbcolor elif a == defaultColor: - ColorsRGB["black"] - else: - ANSIColorMap[a.color mod 10] - let b = if b.rgb: - b.rgbcolor - elif b == defaultColor: - ColorsRGB["white"] - else: - ANSIColorMap[b.color mod 10] - sqrt(float((a.r - b.r) ^ 2 + (a.g - b.b) ^ 2 + (a.g - b.g) ^ 2)) - -proc invert(color: CellColor, bg: bool): CellColor = - if color == defaultColor: if bg: - return ColorsRGB["white"].cellColor() + return ColorsRGB["black"] else: - return ColorsRGB["black"].cellColor() - elif color.rgb: - return RGBColor(0xFFFFFF - cast[uint32](color.rgbcolor)).cellColor() - else: - return RGBColor(0xFFFFFF - uint32(ANSIColorMap[color.color mod 10])).cellColor() + return ColorsRGB["white"] + return ANSIColorMap[a.color mod 10] # Use euclidian distance to quantize RGB colors. proc approximateANSIColor(rgb: RGBColor, exclude = -1): int = @@ -236,6 +220,24 @@ proc approximateANSIColor(rgb: RGBColor, exclude = -1): int = a = b return n +# Return a fgcolor contrasted to the background by contrast. +proc correctContrast(bgcolor, fgcolor: CellColor, contrast: int): CellColor = + let cfgcolor = fgcolor + let bgcolor = getRGB(bgcolor, true) + let fgcolor = getRGB(fgcolor, false) + let bgY = bgcolor.Y + let fgY = fgcolor.Y + let diff = abs(bgY - fgY) + if diff < contrast: + let newrgb = if bgY > contrast: + YUV(bgY - contrast, fgcolor.U, fgcolor.V) + else: + YUV(bgY + contrast, fgcolor.U, fgcolor.V) + if cfgcolor.rgb: + return newrgb.cellColor() + return ColorsANSIFg[approximateANSIColor(newrgb)] + return cfgcolor + proc processFormat*(term: Terminal, format: var Format, cellf: Format): string = for flag in FormatFlags: if flag in term.formatmode: @@ -243,11 +245,6 @@ proc processFormat*(term: Terminal, format: var Format, cellf: Format): string = result &= term.endFormat(flag) var cellf = cellf - if term.mincontrast >= 0 and distance(cellf.bgcolor, cellf.fgcolor) <= term.mincontrast: - cellf.fgcolor = invert(cellf.fgcolor, false) - if distance(cellf.bgcolor, cellf.fgcolor) <= term.mincontrast: - cellf.fgcolor = defaultColor - cellf.bgcolor = defaultColor case term.colormode of ANSI, EIGHT_BIT: if cellf.bgcolor.rgb: @@ -275,6 +272,8 @@ proc processFormat*(term: Terminal, format: var Format, cellf: Format): string = cellf.bgcolor = defaultColor of TRUE_COLOR: discard + cellf.fgcolor = correctContrast(cellf.bgcolor, cellf.fgcolor, term.mincontrast) + if cellf.fgcolor != format.fgcolor: var color = cellf.fgcolor if color.rgb: diff --git a/src/types/color.nim b/src/types/color.nim index dbd2e4fd..1f07d59b 100644 --- a/src/types/color.nim +++ b/src/types/color.nim @@ -208,6 +208,60 @@ const ColorsRGB* = { "rebeccapurple": 0x663399, }.map((a) => (a[0], RGBColor(a[1]))).toTable() +func r*(c: RGBAColor): int = + return int(uint32(c) shr 16 and 0xff) + +func g*(c: RGBAColor): int = + return int(uint32(c) shr 8 and 0xff) + +func b*(c: RGBAColor): int = + return int(uint32(c) and 0xff) + +func a*(c: RGBAColor): int = + return int(uint32(c) shr 24 and 0xff) + +func rgb*(r, g, b: int): RGBColor = + return RGBColor((r shl 16) or (g shl 8) or b) + +func `==`*(a, b: RGBAColor): bool {.borrow.} + +func r*(c: RGBColor): int = + return int(uint32(c) shr 16 and 0xff) + +func g*(c: RGBColor): int = + return int(uint32(c) shr 8 and 0xff) + +func b*(c: RGBColor): int = + return int(uint32(c) and 0xff) + +# see https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms893078(v=msdn.10) +func Y*(c: RGBColor): int = + return ((66 * c.r + 129 * c.g + 25 * c.b + 128) shr 8) + 16 + +func U*(c: RGBColor): int = + return ((-38 * c.r - 74 * c.g + 112 * c.b + 128) shr 8) + 128 + +func V*(c: RGBColor): int = + return ((112 * c.r - 94 * c.g - 18 * c.b + 128) shr 8) + 128 + +func YUV*(Y, U, V: int): RGBColor = + let C = Y - 16 + let D = U - 128 + let E = V - 128 + let r = max(min((298 * C + 409 * E + 128) shr 8, 255), 0) + let g = max(min((298 * C - 100 * D - 208 * E + 128) shr 8, 255), 0) + let b = max(min((298 * C + 516 * D + 128) shr 8, 255), 0) + return rgb(r, g, b) + +func rgba*(r, g, b, a: int): RGBAColor = + return RGBAColor((uint32(a) shl 24) or (uint32(r) shl 16) or (uint32(g) shl 8) or uint32(b)) + +template `$`*(color: CellColor): string = + if color.rgb: + "r" & $color.rgbcolor.r & "g" & $color.rgbcolor.g & "b" & $color.rgbcolor.b + else: + "tcolor" & $color.n + func parseHexColor*(s: string): Option[RGBAColor] = for c in s: if hexValue(c) == -1: return none(RGBAColor) @@ -283,38 +337,3 @@ func parseLegacyColor*(s: string): Option[RGBColor] = (hexValue(c2[0]) shl 12) or (hexValue(c2[1]) shl 8) or (hexValue(c3[0]) shl 4) or hexValue(c3[1]) return some(RGBColor(c)) - -func r*(c: RGBAColor): int = - return int(uint32(c) shr 16 and 0xff) - -func g*(c: RGBAColor): int = - return int(uint32(c) shr 8 and 0xff) - -func b*(c: RGBAColor): int = - return int(uint32(c) and 0xff) - -func a*(c: RGBAColor): int = - return int(uint32(c) shr 24 and 0xff) - -func rgb*(r, g, b: int): RGBColor = - return RGBColor((r shl 16) or (g shl 8) or b) - -func `==`*(a, b: RGBAColor): bool {.borrow.} - -func r*(c: RGBColor): int = - return int(uint32(c) shr 16 and 0xff) - -func g*(c: RGBColor): int = - return int(uint32(c) shr 8 and 0xff) - -func b*(c: RGBColor): int = - return int(uint32(c) and 0xff) - -func rgba*(r, g, b, a: int): RGBAColor = - return RGBAColor((uint32(a) shl 24) or (uint32(r) shl 16) or (uint32(g) shl 8) or uint32(b)) - -template `$`*(color: CellColor): string = - if color.rgb: - "r" & $color.rgbcolor.r & "g" & $color.rgbcolor.g & "b" & $color.rgbcolor.b - else: - "tcolor" & $color.color |