about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/config/config.nim7
-rw-r--r--src/css/cascade.nim5
-rw-r--r--src/display/term.nim55
-rw-r--r--src/types/color.nim89
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