diff --git a/chroma.nimble b/chroma.nimble index 107c68a..683516b 100644 --- a/chroma.nimble +++ b/chroma.nimble @@ -1,10 +1,8 @@ -# Package -version = "0.2.5" +version = "0.2.6" author = "Andre von Houck" description = "Everything you want to do with colors" license = "MIT" srcDir = "src" -# Deps requires "nim >= 1.0.0" diff --git a/src/chroma.nim b/src/chroma.nim index 337846c..6f5d6d4 100644 --- a/src/chroma.nim +++ b/src/chroma.nim @@ -3,7 +3,7 @@ ## import chroma/colortypes, chroma/distance, chroma/names, chroma/temperature, - chroma/transformations, hashes, strutils, tables + chroma/transformations, std/hashes, std/math, std/strutils, std/tables export colortypes, distance, temperature, transformations @@ -197,9 +197,9 @@ proc toHtmlRgb*(c: Color): string = ## * blue -> rgb(0,0,255) ## * white -> rgb(255,255,255) "rgb(" & - $int(c.r * 255) & ", " & - $int(c.g * 255) & ", " & - $int(c.b * 255) & + $round(c.r * 255).int & ", " & + $round(c.g * 255).int & ", " & + $round(c.b * 255).int & ")" proc parseHtmlRgba*(text: string): Color = @@ -230,9 +230,9 @@ proc toHtmlRgba*(c: Color): string = ## * blue -> rgb(0,0,255) ## * white -> rgb(255,255,255) "rgba(" & - $int(c.r * 255) & ", " & - $int(c.g * 255) & ", " & - $int(c.b * 255) & ", " & + $round(c.r * 255).int & ", " & + $round(c.g * 255).int & ", " & + $round(c.b * 255).int & ", " & $c.a & ")" @@ -336,16 +336,16 @@ proc mixCMYK*(colorA, colorB: Color): Color = proc mix*(a, b: ColorRGB): ColorRGB = ## Mixes two ColorRGB colors together using simple average. - result.r = a.r div 2 + b.r div 2 - result.g = a.g div 2 + b.g div 2 - result.b = a.b div 2 + b.b div 2 + result.r = ((a.r.uint32 + b.r) div 2).uint8 + result.g = ((a.g.uint32 + b.g) div 2).uint8 + result.b = ((a.b.uint32 + b.b) div 2).uint8 proc mix*(a, b: ColorRGBA): ColorRGBA = ## Mixes two ColorRGBA colors together using simple average. - result.r = a.r div 2 + b.r div 2 - result.g = a.g div 2 + b.g div 2 - result.b = a.b div 2 + b.b div 2 - result.a = a.a div 2 + b.a div 2 + result.r = ((a.r.uint32 + b.r) div 2).uint8 + result.g = ((a.g.uint32 + b.g) div 2).uint8 + result.b = ((a.b.uint32 + b.b) div 2).uint8 + result.a = ((a.a.uint32 + b.a) div 2).uint8 func distance*(c1, c2: SomeColor): float32 = ## A distance function based on CIEDE2000 color difference formula diff --git a/src/chroma/transformations.nim b/src/chroma/transformations.nim index 9f2aa41..17b5902 100644 --- a/src/chroma/transformations.nim +++ b/src/chroma/transformations.nim @@ -12,7 +12,7 @@ ## and link to a mirror of the C file: ## - https://github.com/cran/colorspace/blob/master/src/colorspace.c -import colortypes, math +import colortypes, std/math proc rgba*(c: ColorRGBX): ColorRGBA {.inline.} = ## Convert a premultiplied alpha RGBA to a straight alpha RGBA. @@ -21,16 +21,16 @@ proc rgba*(c: ColorRGBX): ColorRGBA {.inline.} = result.b = c.b result.a = c.a if result.a != 0 and result.a != 255: - let multiplier = ((255 / c.a.float32) * 255).uint32 - result.r = ((result.r.uint32 * multiplier) div 255).uint8 - result.g = ((result.g.uint32 * multiplier) div 255).uint8 - result.b = ((result.b.uint32 * multiplier) div 255).uint8 + let multiplier = round((255 / c.a.float32) * 255).uint32 + result.r = ((result.r.uint32 * multiplier + 127) div 255).uint8 + result.g = ((result.g.uint32 * multiplier + 127) div 255).uint8 + result.b = ((result.b.uint32 * multiplier + 127) div 255).uint8 proc rgbx*(c: ColorRGBA): ColorRGBX {.inline.} = ## Convert a straight alpha RGBA to a premultiplied alpha RGBA. - result.r = ((c.r.uint32 * c.a.uint32) div 255).uint8 - result.g = ((c.g.uint32 * c.a.uint32) div 255).uint8 - result.b = ((c.b.uint32 * c.a.uint32) div 255).uint8 + result.r = ((c.r.uint32 * c.a.uint32 + 127) div 255).uint8 + result.g = ((c.g.uint32 * c.a.uint32 + 127) div 255).uint8 + result.b = ((c.b.uint32 * c.a.uint32 + 127) div 255).uint8 result.a = c.a proc rgbx*(c: ColorRGB): ColorRGBX {.inline.} = @@ -42,9 +42,9 @@ proc rgbx*(c: ColorRGB): ColorRGBX {.inline.} = proc rgb*(c: Color): ColorRGB {.inline.} = ## Convert Color to ColorRGB - result.r = uint8(c.r * 255) - result.g = uint8(c.g * 255) - result.b = uint8(c.b * 255) + result.r = round(c.r * 255).uint8 + result.g = round(c.g * 255).uint8 + result.b = round(c.b * 255).uint8 proc color*(c: ColorRGB): Color {.inline.} = ## Convert ColorRGB to Color @@ -55,10 +55,10 @@ proc color*(c: ColorRGB): Color {.inline.} = proc rgba*(c: Color): ColorRGBA {.inline.} = ## Convert Color to ColorRGBA - result.r = uint8(c.r * 255) - result.g = uint8(c.g * 255) - result.b = uint8(c.b * 255) - result.a = uint8(c.a * 255) + result.r = round(c.r * 255).uint8 + result.g = round(c.g * 255).uint8 + result.b = round(c.b * 255).uint8 + result.a = round(c.a * 255).uint8 proc color*(c: ColorRGBA): Color {.inline.} = ## Convert ColorRGBA to Color diff --git a/tests/test_colors.nim b/tests/test_colors.nim index 7405fad..e2cdcf3 100644 --- a/tests/test_colors.nim +++ b/tests/test_colors.nim @@ -1,4 +1,4 @@ -import chroma, chroma/transformations, macros, sequtils, strutils, unittest +import chroma, chroma/transformations, std/macros, std/sequtils, std/strutils, std/unittest, std/math when not defined(js): import parsecsv @@ -397,6 +397,30 @@ suite "temperature": doAssert fromTemperature(6500).almostEqual(color(1.0, 0.9753435850143433, 0.9935365319252014, 1.0)) # LCD or CRT screen doAssert fromTemperature(15000).almostEqual(color(0.7009154558181763, 0.7981580495834351, 1.0, 1.0)) # Clear blue poleward sky +suite "premultiplied alpha": + test "rgba -> rgbx": + for a in 0.uint8 .. 255: + for r in 0.uint8 .. 255: + doAssert rgba(r, 0, 0, a).rgbx == rgbx( + round(r.float32 * a.float32 / 255).uint8, + 0, + 0, + a + ) + + test "rgbx -> rgba": + for a in 0.uint8 .. 255: + for r in 0.uint8 .. 255: + let + rgbx = rgba(r, 0, 0, a).rgbx + multiplier = round((255 / rgbx.a.float32) * 255).uint32 + doAssert rgbx.rgba == rgba( + ((rgbx.r * multiplier + 127) div 255).uint8, + 0, + 0, + rgbx.a.uint8 + ) + when false: # example in readme: import chroma