Merge pull request #136 from guzba/master

migrate public api to rgbx
This commit is contained in:
treeform 2021-02-25 16:13:50 -08:00 committed by GitHub
commit 22aace7a5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 86 additions and 118 deletions

View file

@ -190,7 +190,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
elif fill == "none": elif fill == "none":
result.fill = ColorRGBA() result.fill = ColorRGBA()
else: else:
result.fill = parseHtmlColor(fill).rgba.toPremultipliedAlpha() result.fill = parseHtmlColor(fill).rgba
if stroke == "": if stroke == "":
discard # Inherit discard # Inherit
@ -199,7 +199,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
elif stroke == "none": elif stroke == "none":
result.stroke = ColorRGBA() result.stroke = ColorRGBA()
else: else:
result.stroke = parseHtmlColor(stroke).rgba.toPremultipliedAlpha() result.stroke = parseHtmlColor(stroke).rgba
result.shouldStroke = true result.shouldStroke = true
if strokeWidth == "": if strokeWidth == "":

View file

@ -35,7 +35,7 @@ type
bmIntersectMask bmIntersectMask
bmExcludeMask bmExcludeMask
Blender* = proc(backdrop, source: ColorRGBA): ColorRGBA Blender* = proc(backdrop, source: ColorRGBX): ColorRGBX
## Function signature returned by blender. ## Function signature returned by blender.
Masker* = proc(backdrop, source: uint8): uint8 Masker* = proc(backdrop, source: uint8): uint8
## Function signature returned by masker. ## Function signature returned by masker.
@ -166,7 +166,7 @@ proc SetSat(C: Color, s: float32): Color {.inline.} =
if satC > 0: if satC > 0:
result = (C - min([C.r, C.g, C.b])) * s / satC result = (C - min([C.r, C.g, C.b])) * s / satC
proc blendNormal(backdrop, source: ColorRGBA): ColorRGBA = proc blendNormal(backdrop, source: ColorRGBX): ColorRGBX =
if backdrop.a == 0: if backdrop.a == 0:
return source return source
if source.a == 255: if source.a == 255:
@ -180,7 +180,7 @@ proc blendNormal(backdrop, source: ColorRGBA): ColorRGBA =
result.b = source.b + ((backdrop.b.uint32 * k) div 255).uint8 result.b = source.b + ((backdrop.b.uint32 * k) div 255).uint8
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
proc blendDarken(backdrop, source: ColorRGBA): ColorRGBA = proc blendDarken(backdrop, source: ColorRGBX): ColorRGBX =
proc blend( proc blend(
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8 backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
): uint8 {.inline.} = ): uint8 {.inline.} =
@ -194,7 +194,7 @@ proc blendDarken(backdrop, source: ColorRGBA): ColorRGBA =
result.b = blend(backdrop.b, backdrop.a, source.b, source.a) result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
proc blendMultiply(backdrop, source: ColorRGBA): ColorRGBA = proc blendMultiply(backdrop, source: ColorRGBX): ColorRGBX =
proc blend( proc blend(
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8 backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
): uint8 {.inline.} = ): uint8 {.inline.} =
@ -209,7 +209,7 @@ proc blendMultiply(backdrop, source: ColorRGBA): ColorRGBA =
result.b = blend(backdrop.b, backdrop.a, source.b, source.a) result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
# proc blendLinearBurn(backdrop, source: ColorRGBA): ColorRGBA = # proc blendLinearBurn(backdrop, source: ColorRGBX): ColorRGBX =
# let # let
# backdrop = backdrop.toStraightAlpha() # backdrop = backdrop.toStraightAlpha()
# source = source.toStraightAlpha() # source = source.toStraightAlpha()
@ -219,7 +219,7 @@ proc blendMultiply(backdrop, source: ColorRGBA): ColorRGBA =
# result = alphaFix(backdrop, source, result) # result = alphaFix(backdrop, source, result)
# result = result.toPremultipliedAlpha() # result = result.toPremultipliedAlpha()
proc blendColorBurn(backdrop, source: ColorRGBA): ColorRGBA = proc blendColorBurn(backdrop, source: ColorRGBX): ColorRGBX =
let let
backdrop = backdrop.toStraightAlpha() backdrop = backdrop.toStraightAlpha()
source = source.toStraightAlpha() source = source.toStraightAlpha()
@ -230,13 +230,13 @@ proc blendColorBurn(backdrop, source: ColorRGBA): ColorRGBA =
0 0
else: else:
255 - min(255, (255 * (255 - backdrop)) div source).uint8 255 - min(255, (255 * (255 - backdrop)) div source).uint8
result.r = blend(backdrop.r, source.r) var blended: ColorRGBA
result.g = blend(backdrop.g, source.g) blended.r = blend(backdrop.r, source.r)
result.b = blend(backdrop.b, source.b) blended.g = blend(backdrop.g, source.g)
result = alphaFix(backdrop, source, result) blended.b = blend(backdrop.b, source.b)
result = result.toPremultipliedAlpha() result = alphaFix(backdrop, source, blended).toPremultipliedAlpha()
proc blendLighten(backdrop, source: ColorRGBA): ColorRGBA = proc blendLighten(backdrop, source: ColorRGBX): ColorRGBX =
proc blend( proc blend(
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8 backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
): uint8 {.inline.} = ): uint8 {.inline.} =
@ -250,13 +250,13 @@ proc blendLighten(backdrop, source: ColorRGBA): ColorRGBA =
result.b = blend(backdrop.b, backdrop.a, source.b, source.a) result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
proc blendScreen(backdrop, source: ColorRGBA): ColorRGBA = proc blendScreen(backdrop, source: ColorRGBX): ColorRGBX =
result.r = screen(backdrop.r, source.r) result.r = screen(backdrop.r, source.r)
result.g = screen(backdrop.g, source.g) result.g = screen(backdrop.g, source.g)
result.b = screen(backdrop.b, source.b) result.b = screen(backdrop.b, source.b)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
# proc blendLinearDodge(backdrop, source: ColorRGBA): ColorRGBA = # proc blendLinearDodge(backdrop, source: ColorRGBX): ColorRGBX =
# let # let
# backdrop = backdrop.toStraightAlpha() # backdrop = backdrop.toStraightAlpha()
# source = source.toStraightAlpha() # source = source.toStraightAlpha()
@ -266,7 +266,7 @@ proc blendScreen(backdrop, source: ColorRGBA): ColorRGBA =
# result = alphaFix(backdrop, source, result) # result = alphaFix(backdrop, source, result)
# result = result.toPremultipliedAlpha() # result = result.toPremultipliedAlpha()
proc blendColorDodge(backdrop, source: ColorRGBA): ColorRGBA = proc blendColorDodge(backdrop, source: ColorRGBX): ColorRGBX =
let let
backdrop = backdrop.toStraightAlpha() backdrop = backdrop.toStraightAlpha()
source = source.toStraightAlpha() source = source.toStraightAlpha()
@ -277,19 +277,19 @@ proc blendColorDodge(backdrop, source: ColorRGBA): ColorRGBA =
255 255
else: else:
min(255, (255 * backdrop) div (255 - source)).uint8 min(255, (255 * backdrop) div (255 - source)).uint8
result.r = blend(backdrop.r, source.r) var blended: ColorRGBA
result.g = blend(backdrop.g, source.g) blended.r = blend(backdrop.r, source.r)
result.b = blend(backdrop.b, source.b) blended.g = blend(backdrop.g, source.g)
result = alphaFix(backdrop, source, result) blended.b = blend(backdrop.b, source.b)
result = result.toPremultipliedAlpha() result = alphaFix(backdrop, source, blended).toPremultipliedAlpha()
proc blendOverlay(backdrop, source: ColorRGBA): ColorRGBA = proc blendOverlay(backdrop, source: ColorRGBX): ColorRGBX =
result.r = hardLight(source.r, source.a, backdrop.r, backdrop.a) result.r = hardLight(source.r, source.a, backdrop.r, backdrop.a)
result.g = hardLight(source.g, source.a, backdrop.g, backdrop.a) result.g = hardLight(source.g, source.a, backdrop.g, backdrop.a)
result.b = hardLight(source.b, source.a, backdrop.b, backdrop.a) result.b = hardLight(source.b, source.a, backdrop.b, backdrop.a)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
proc blendSoftLight(backdrop, source: ColorRGBA): ColorRGBA = proc blendSoftLight(backdrop, source: ColorRGBX): ColorRGBX =
# proc softLight(backdrop, source: int32): uint8 {.inline.} = # proc softLight(backdrop, source: int32): uint8 {.inline.} =
# ## Pegtop # ## Pegtop
# ( # (
@ -301,6 +301,7 @@ proc blendSoftLight(backdrop, source: ColorRGBA): ColorRGBA =
backdrop = backdrop.toStraightAlpha() backdrop = backdrop.toStraightAlpha()
source = source.toStraightAlpha() source = source.toStraightAlpha()
var rgba: ColorRGBA
when defined(amd64) and not defined(pixieNoSimd): when defined(amd64) and not defined(pixieNoSimd):
let let
vb = mm_setr_ps( vb = mm_setr_ps(
@ -316,11 +317,11 @@ proc blendSoftLight(backdrop, source: ColorRGBA): ColorRGBA =
vm = ((v255 - v2 * vs) * vb * vb) / v255sq + (v2 * vs * vb) / v255 vm = ((v255 - v2 * vs) * vb * vb) / v255sq + (v2 * vs * vb) / v255
values = cast[array[4, uint32]](mm_cvtps_epi32(vm)) values = cast[array[4, uint32]](mm_cvtps_epi32(vm))
result.r = values[0].uint8 rgba.r = values[0].uint8
result.g = values[1].uint8 rgba.g = values[1].uint8
result.b = values[2].uint8 rgba.b = values[2].uint8
# proc alphaFix(backdrop, source, mixed: ColorRGBA): ColorRGBA {.inline.} = # proc alphaFix(backdrop, source, mixed: ColorRGBX): ColorRGBX {.inline.} =
# if backdrop.a == 0 and source.a == 0: # if backdrop.a == 0 and source.a == 0:
# return # return
# let # let
@ -345,10 +346,10 @@ proc blendSoftLight(backdrop, source: ColorRGBA): ColorRGBA =
mm_cvtps_epi32((t0 * vs + t1 * vm + t2 * vb) / va / v255) mm_cvtps_epi32((t0 * vs + t1 * vm + t2 * vb) / va / v255)
) )
result.r = final[0].uint8 rgba.r = final[0].uint8
result.g = final[1].uint8 rgba.g = final[1].uint8
result.b = final[2].uint8 rgba.b = final[2].uint8
result.a = a.uint8 rgba.a = a.uint8
else: else:
let let
b = backdrop.color b = backdrop.color
@ -358,17 +359,17 @@ proc blendSoftLight(backdrop, source: ColorRGBA): ColorRGBA =
blended.g = softLight(b.g, s.g) blended.g = softLight(b.g, s.g)
blended.b = softLight(b.b, s.b) blended.b = softLight(b.b, s.b)
blended = alphaFix(b, s, blended) blended = alphaFix(b, s, blended)
result = blended.rgba rgba = blended.rgba
result = result.toPremultipliedAlpha() result = rgba
proc blendHardLight(backdrop, source: ColorRGBA): ColorRGBA = proc blendHardLight(backdrop, source: ColorRGBX): ColorRGBX =
result.r = hardLight(backdrop.r, backdrop.a, source.r, source.a) result.r = hardLight(backdrop.r, backdrop.a, source.r, source.a)
result.g = hardLight(backdrop.g, backdrop.a, source.g, source.a) result.g = hardLight(backdrop.g, backdrop.a, source.g, source.a)
result.b = hardLight(backdrop.b, backdrop.a, source.b, source.a) result.b = hardLight(backdrop.b, backdrop.a, source.b, source.a)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
proc blendDifference(backdrop, source: ColorRGBA): ColorRGBA = proc blendDifference(backdrop, source: ColorRGBX): ColorRGBX =
proc blend( proc blend(
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8 backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
): uint8 {.inline.} = ): uint8 {.inline.} =
@ -384,7 +385,7 @@ proc blendDifference(backdrop, source: ColorRGBA): ColorRGBA =
result.b = blend(backdrop.b, backdrop.a, source.b, source.a) result.b = blend(backdrop.b, backdrop.a, source.b, source.a)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
proc blendExclusion(backdrop, source: ColorRGBA): ColorRGBA = proc blendExclusion(backdrop, source: ColorRGBX): ColorRGBX =
proc blend(backdrop, source: uint32): uint8 {.inline.} = proc blend(backdrop, source: uint32): uint8 {.inline.} =
let v = (backdrop + source).int32 - ((2 * backdrop * source) div 255).int32 let v = (backdrop + source).int32 - ((2 * backdrop * source) div 255).int32
max(0, v).uint8 max(0, v).uint8
@ -393,62 +394,62 @@ proc blendExclusion(backdrop, source: ColorRGBA): ColorRGBA =
result.b = blend(backdrop.b.uint32, source.b.uint32) result.b = blend(backdrop.b.uint32, source.b.uint32)
result.a = blendAlpha(backdrop.a, source.a) result.a = blendAlpha(backdrop.a, source.a)
proc blendColor(backdrop, source: ColorRGBA): ColorRGBA = proc blendColor(backdrop, source: ColorRGBX): ColorRGBX =
let let
backdrop = backdrop.toStraightAlpha().color backdrop = backdrop.toStraightAlpha().color
source = source.toStraightAlpha().color source = source.toStraightAlpha().color
blended = SetLum(source, Lum(backdrop)) blended = SetLum(source, Lum(backdrop))
result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha() result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha()
proc blendLuminosity(backdrop, source: ColorRGBA): ColorRGBA = proc blendLuminosity(backdrop, source: ColorRGBX): ColorRGBX =
let let
backdrop = backdrop.toStraightAlpha().color backdrop = backdrop.toStraightAlpha().color
source = source.toStraightAlpha().color source = source.toStraightAlpha().color
blended = SetLum(backdrop, Lum(source)) blended = SetLum(backdrop, Lum(source))
result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha() result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha()
proc blendHue(backdrop, source: ColorRGBA): ColorRGBA = proc blendHue(backdrop, source: ColorRGBX): ColorRGBX =
let let
backdrop = backdrop.toStraightAlpha().color backdrop = backdrop.toStraightAlpha().color
source = source.toStraightAlpha().color source = source.toStraightAlpha().color
blended = SetLum(SetSat(source, Sat(backdrop)), Lum(backdrop)) blended = SetLum(SetSat(source, Sat(backdrop)), Lum(backdrop))
result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha() result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha()
proc blendSaturation(backdrop, source: ColorRGBA): ColorRGBA = proc blendSaturation(backdrop, source: ColorRGBX): ColorRGBX =
let let
backdrop = backdrop.toStraightAlpha().color backdrop = backdrop.toStraightAlpha().color
source = source.toStraightAlpha().color source = source.toStraightAlpha().color
blended = SetLum(SetSat(backdrop, Sat(source)), Lum(backdrop)) blended = SetLum(SetSat(backdrop, Sat(source)), Lum(backdrop))
result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha() result = alphaFix(backdrop, source, blended).rgba.toPremultipliedAlpha()
proc blendMask(backdrop, source: ColorRGBA): ColorRGBA = proc blendMask(backdrop, source: ColorRGBX): ColorRGBX =
let k = source.a.uint32 let k = source.a.uint32
result.r = ((backdrop.r * k) div 255).uint8 result.r = ((backdrop.r * k) div 255).uint8
result.g = ((backdrop.g * k) div 255).uint8 result.g = ((backdrop.g * k) div 255).uint8
result.b = ((backdrop.b * k) div 255).uint8 result.b = ((backdrop.b * k) div 255).uint8
result.a = ((backdrop.a * k) div 255).uint8 result.a = ((backdrop.a * k) div 255).uint8
proc blendSubtractMask(backdrop, source: ColorRGBA): ColorRGBA = proc blendSubtractMask(backdrop, source: ColorRGBX): ColorRGBX =
let a = (backdrop.a.uint32 * (255 - source.a)) div 255 let a = (backdrop.a.uint32 * (255 - source.a)) div 255
result.r = ((backdrop.r * a) div 255).uint8 result.r = ((backdrop.r * a) div 255).uint8
result.g = ((backdrop.g * a) div 255).uint8 result.g = ((backdrop.g * a) div 255).uint8
result.b = ((backdrop.b * a) div 255).uint8 result.b = ((backdrop.b * a) div 255).uint8
result.a = a.uint8 result.a = a.uint8
proc blendIntersectMask(backdrop, source: ColorRGBA): ColorRGBA = proc blendIntersectMask(backdrop, source: ColorRGBX): ColorRGBX =
blendMask(backdrop, source) blendMask(backdrop, source)
proc blendExcludeMask(backdrop, source: ColorRGBA): ColorRGBA = proc blendExcludeMask(backdrop, source: ColorRGBX): ColorRGBX =
let a = max(backdrop.a, source.a).uint32 - min(backdrop.a, source.a) let a = max(backdrop.a, source.a).uint32 - min(backdrop.a, source.a)
result.r = ((backdrop.r * a) div 255).uint8 result.r = ((backdrop.r * a) div 255).uint8
result.g = ((backdrop.g * a) div 255).uint8 result.g = ((backdrop.g * a) div 255).uint8
result.b = ((backdrop.b * a) div 255).uint8 result.b = ((backdrop.b * a) div 255).uint8
result.a = a.uint8 result.a = a.uint8
proc blendOverwrite(backdrop, source: ColorRGBA): ColorRGBA = proc blendOverwrite(backdrop, source: ColorRGBX): ColorRGBX =
source source
# proc blendWhite(backdrop, source: ColorRGBA): ColorRGBA = # proc blendWhite(backdrop, source: ColorRGBX): ColorRGBX =
# ## For testing # ## For testing
# rgba(255, 255, 255, 255) # rgba(255, 255, 255, 255)

View file

@ -14,7 +14,7 @@ proc lerp*(a, b: uint8, t: float32): uint8 {.inline.} =
let t = round(t * 255).uint32 let t = round(t * 255).uint32
((a * (255 - t) + b * t) div 255).uint8 ((a * (255 - t) + b * t) div 255).uint8
proc lerp*(a, b: ColorRGBA, t: float32): ColorRGBA {.inline.} = proc lerp*(a, b: ColorRGBX, t: float32): ColorRGBX {.inline.} =
## Linearly interpolate between a and b using t. ## Linearly interpolate between a and b using t.
let x = round(t * 255).uint32 let x = round(t * 255).uint32
result.r = ((a.r.uint32 * (255 - x) + b.r.uint32 * x) div 255).uint8 result.r = ((a.r.uint32 * (255 - x) + b.r.uint32 * x) div 255).uint8
@ -28,36 +28,3 @@ func lerp*(a, b: Color, v: float32): Color {.inline.} =
result.g = lerp(a.g, b.g, v) result.g = lerp(a.g, b.g, v)
result.b = lerp(a.b, b.b, v) result.b = lerp(a.b, b.b, v)
result.a = lerp(a.a, b.a, v) result.a = lerp(a.a, b.a, v)
proc toPremultipliedAlpha*(c: ColorRGBA): ColorRGBA {.inline.} =
## Converts a color to premultiplied alpha from straight alpha.
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.a = c.a
proc toStraightAlpha*(c: ColorRGBA): ColorRGBA {.inline.} =
## Converts a color from premultiplied alpha to straight alpha.
result = c
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
proc toPremultipliedAlpha*(c: Color): Color {.inline.} =
## Converts a color to premultiplied alpha from straight alpha.
result.r = c.r * c.a
result.g = c.g * c.a
result.b = c.b * c.a
result.a = c.a
proc toStraightAlpha*(c: Color): Color {.inline.} =
## Converts a color from premultiplied alpha to straight alpha.
if c.a != 0 and c.a != 1:
result = c
else:
result.r = c.r / c.a
result.g = c.g / c.a
result.b = c.b / c.a
result.a = c.a

View file

@ -1,4 +1,4 @@
import chroma, flatty/binny, pixie/common, pixie/images, pixie/internal import chroma, flatty/binny, pixie/common, pixie/images
# See: https://en.wikipedia.org/wiki/BMP_file_format # See: https://en.wikipedia.org/wiki/BMP_file_format
@ -48,8 +48,6 @@ proc decodeBmp*(data: string): Image =
offset += 3 offset += 3
result[x, result.height - y - 1] = rgba result[x, result.height - y - 1] = rgba
result.data.toPremultipliedAlpha()
proc decodeBmp*(data: seq[uint8]): Image {.inline.} = proc decodeBmp*(data: seq[uint8]): Image {.inline.} =
## Decodes bitmap data into an Image. ## Decodes bitmap data into an Image.
decodeBmp(cast[string](data)) decodeBmp(cast[string](data))

View file

@ -407,11 +407,13 @@ proc decodePng*(data: seq[uint8]): Image =
if prevChunkType != "IEND": if prevChunkType != "IEND":
failInvalid() failInvalid()
var pixels = decodeImageData(header, palette, transparency, imageData)
pixels.toPremultipliedAlpha()
result = Image() result = Image()
result.width = header.width result.width = header.width
result.height = header.height result.height = header.height
result.data = decodeImageData(header, palette, transparency, imageData) result.data = cast[seq[ColorRGBX]](pixels)
result.data.toPremultipliedAlpha()
proc decodePng*(data: string): Image {.inline.} = proc decodePng*(data: string): Image {.inline.} =
## Decodes the PNG data into an Image. ## Decodes the PNG data into an Image.

View file

@ -81,7 +81,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
elif fill == "none": elif fill == "none":
result.fill = ColorRGBA() result.fill = ColorRGBA()
else: else:
result.fill = parseHtmlColor(fill).rgba.toPremultipliedAlpha() result.fill = parseHtmlColor(fill).rgba
if stroke == "": if stroke == "":
discard # Inherit discard # Inherit
@ -90,7 +90,7 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
elif stroke == "none": elif stroke == "none":
result.stroke = ColorRGBA() result.stroke = ColorRGBA()
else: else:
result.stroke = parseHtmlColor(stroke).rgba.toPremultipliedAlpha() result.stroke = parseHtmlColor(stroke).rgba
result.shouldStroke = true result.shouldStroke = true
if strokeWidth == "": if strokeWidth == "":

View file

@ -9,7 +9,7 @@ type
Image* = ref object Image* = ref object
## Image object that holds bitmap data in RGBA format. ## Image object that holds bitmap data in RGBA format.
width*, height*: int width*, height*: int
data*: seq[ColorRGBA] data*: seq[ColorRGBX]
when defined(release): when defined(release):
{.push checks: off.} {.push checks: off.}
@ -22,7 +22,7 @@ proc newImage*(width, height: int): Image =
result = Image() result = Image()
result.width = width result.width = width
result.height = height result.height = height
result.data = newSeq[ColorRGBA](width * height) result.data = newSeq[ColorRGBX](width * height)
proc wh*(image: Image): Vec2 {.inline.} = proc wh*(image: Image): Vec2 {.inline.} =
## Return with and height as a size vector. ## Return with and height as a size vector.
@ -44,31 +44,31 @@ proc inside*(image: Image, x, y: int): bool {.inline.} =
proc dataIndex*(image: Image, x, y: int): int {.inline.} = proc dataIndex*(image: Image, x, y: int): int {.inline.} =
image.width * y + x image.width * y + x
proc getRgbaUnsafe*(image: Image, x, y: int): ColorRGBA {.inline.} = proc getRgbaUnsafe*(image: Image, x, y: int): ColorRGBX {.inline.} =
## Gets a color from (x, y) coordinates. ## Gets a color from (x, y) coordinates.
## * No bounds checking * ## * No bounds checking *
## Make sure that x, y are in bounds. ## Make sure that x, y are in bounds.
## Failure in the assumptions will case unsafe memory reads. ## Failure in the assumptions will case unsafe memory reads.
result = image.data[image.width * y + x] result = image.data[image.width * y + x]
proc `[]`*(image: Image, x, y: int): ColorRGBA {.inline.} = proc `[]`*(image: Image, x, y: int): ColorRGBX {.inline.} =
## Gets a pixel at (x, y) or returns transparent black if outside of bounds. ## Gets a pixel at (x, y) or returns transparent black if outside of bounds.
if image.inside(x, y): if image.inside(x, y):
return image.getRgbaUnsafe(x, y) return image.getRgbaUnsafe(x, y)
proc setRgbaUnsafe*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} = proc setRgbaUnsafe*(image: Image, x, y: int, rgba: ColorRGBX) {.inline.} =
## Sets a color from (x, y) coordinates. ## Sets a color from (x, y) coordinates.
## * No bounds checking * ## * No bounds checking *
## Make sure that x, y are in bounds. ## Make sure that x, y are in bounds.
## Failure in the assumptions will case unsafe memory writes. ## Failure in the assumptions will case unsafe memory writes.
image.data[image.dataIndex(x, y)] = rgba image.data[image.dataIndex(x, y)] = rgba
proc `[]=`*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} = proc `[]=`*(image: Image, x, y: int, rgba: ColorRGBX) {.inline.} =
## Sets a pixel at (x, y) or does nothing if outside of bounds. ## Sets a pixel at (x, y) or does nothing if outside of bounds.
if image.inside(x, y): if image.inside(x, y):
image.setRgbaUnsafe(x, y, rgba) image.setRgbaUnsafe(x, y, rgba)
proc fillUnsafe*(data: var seq[ColorRGBA], rgba: ColorRGBA, start, len: int) = proc fillUnsafe*(data: var seq[ColorRGBX], rgba: ColorRGBX, start, len: int) =
## Fills the image data with the parameter color starting at index start and ## Fills the image data with the parameter color starting at index start and
## continuing for len indices. ## continuing for len indices.
@ -97,7 +97,7 @@ proc fillUnsafe*(data: var seq[ColorRGBA], rgba: ColorRGBA, start, len: int) =
for j in i ..< start + len: for j in i ..< start + len:
data[j] = rgba data[j] = rgba
proc fill*(image: Image, rgba: ColorRgba) {.inline.} = proc fill*(image: Image, rgba: ColorRGBX) {.inline.} =
## Fills the image with the parameter color. ## Fills the image with the parameter color.
fillUnsafe(image.data, rgba, 0, image.data.len) fillUnsafe(image.data, rgba, 0, image.data.len)
@ -187,7 +187,7 @@ proc minifyBy2*(image: Image, power = 1): Image =
c = src.getRgbaUnsafe(x * 2 + 1, y * 2 + 1) c = src.getRgbaUnsafe(x * 2 + 1, y * 2 + 1)
d = src.getRgbaUnsafe(x * 2 + 0, y * 2 + 1) d = src.getRgbaUnsafe(x * 2 + 0, y * 2 + 1)
let color = rgba( let color = rgbx(
((a.r.uint32 + b.r + c.r + d.r) div 4).uint8, ((a.r.uint32 + b.r + c.r + d.r) div 4).uint8,
((a.g.uint32 + b.g + c.g + d.g) div 4).uint8, ((a.g.uint32 + b.g + c.g + d.g) div 4).uint8,
((a.b.uint32 + b.b + c.b + d.b) div 4).uint8, ((a.b.uint32 + b.b + c.b + d.b) div 4).uint8,
@ -217,7 +217,7 @@ proc applyOpacity*(target: Image | Mask, opacity: float32) =
if opacity == 0: if opacity == 0:
when type(target) is Image: when type(target) is Image:
target.fill(rgba(0, 0, 0, 0)) target.fill(rgbx(0, 0, 0, 0))
else: else:
target.fill(0) target.fill(0)
return return
@ -458,7 +458,7 @@ proc newMask*(image: Image): Mask =
for j in i ..< image.data.len: for j in i ..< image.data.len:
result.data[j] = image.data[j].a result.data[j] = image.data[j].a
proc getRgbaSmooth*(image: Image, x, y: float32, wrapped = false): ColorRGBA = proc getRgbaSmooth*(image: Image, x, y: float32, wrapped = false): ColorRGBX =
## Gets a interpolated color with float point coordinates. ## Gets a interpolated color with float point coordinates.
## Pixes outside the image are transparent. ## Pixes outside the image are transparent.
let let
@ -473,7 +473,7 @@ proc getRgbaSmooth*(image: Image, x, y: float32, wrapped = false): ColorRGBA =
x1 = (x + 1) x1 = (x + 1)
y1 = (y + 1) y1 = (y + 1)
var x0y0, x1y0, x0y1, x1y1: ColorRGBA var x0y0, x1y0, x0y1, x1y1: ColorRGBX
if wrapped: if wrapped:
x0y0 = image.getRgbaUnsafe(x0 mod image.width, y0 mod image.height) x0y0 = image.getRgbaUnsafe(x0 mod image.width, y0 mod image.height)
x1y0 = image.getRgbaUnsafe(x1 mod image.width, y0 mod image.height) x1y0 = image.getRgbaUnsafe(x1 mod image.width, y0 mod image.height)
@ -642,7 +642,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
else: # b is a Mask else: # b is a Mask
let let
sample = b.getValueSmooth(xFloat, yFloat) sample = b.getValueSmooth(xFloat, yFloat)
blended = blender(backdrop, rgba(0, 0, 0, sample)) blended = blender(backdrop, rgbx(0, 0, 0, sample))
a.setRgbaUnsafe(x, y, blended) a.setRgbaUnsafe(x, y, blended)
else: # a is a Mask else: # a is a Mask
let backdrop = a.getValueUnsafe(x, y) let backdrop = a.getValueUnsafe(x, y)
@ -729,7 +729,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
else: # b is a Mask else: # b is a Mask
let let
sample = b.getValueUnsafe(xFloat.int, yFloat.int) sample = b.getValueUnsafe(xFloat.int, yFloat.int)
blended = blender(backdrop, rgba(0, 0, 0, sample)) blended = blender(backdrop, rgbx(0, 0, 0, sample))
a.setRgbaUnsafe(x, y, blended) a.setRgbaUnsafe(x, y, blended)
else: # a is a Mask else: # a is a Mask
let backdrop = a.getValueUnsafe(x, y) let backdrop = a.getValueUnsafe(x, y)
@ -812,7 +812,7 @@ proc shift*(target: Image | Mask, offset: Vec2) =
target.draw(copy, offset, bmOverwrite) # Draw copy at offset target.draw(copy, offset, bmOverwrite) # Draw copy at offset
proc shadow*( proc shadow*(
image: Image, offset: Vec2, spread, blur: float32, color: ColorRGBA image: Image, offset: Vec2, spread, blur: float32, color: ColorRGBX
): Image = ): Image =
## Create a shadow of the image with the offset, spread and blur. ## Create a shadow of the image with the offset, spread and blur.
let mask = image.newMask() let mask = image.newMask()

View file

@ -3,7 +3,7 @@ import chroma
when defined(amd64) and not defined(pixieNoSimd): when defined(amd64) and not defined(pixieNoSimd):
import nimsimd/sse2 import nimsimd/sse2
proc toStraightAlpha*(data: var seq[ColorRGBA]) = proc toStraightAlpha*(data: var seq[ColorRGBA | ColorRGBX]) =
## Converts an image from premultiplied alpha to straight alpha. ## Converts an image from premultiplied alpha to straight alpha.
## This is expensive for large images. ## This is expensive for large images.
for c in data.mitems: for c in data.mitems:
@ -14,7 +14,7 @@ proc toStraightAlpha*(data: var seq[ColorRGBA]) =
c.g = ((c.g.uint32 * multiplier) div 255).uint8 c.g = ((c.g.uint32 * multiplier) div 255).uint8
c.b = ((c.b.uint32 * multiplier) div 255).uint8 c.b = ((c.b.uint32 * multiplier) div 255).uint8
proc toPremultipliedAlpha*(data: var seq[ColorRGBA]) = proc toPremultipliedAlpha*(data: var seq[ColorRGBA | ColorRGBX]) =
## Converts an image to premultiplied alpha from straight alpha. ## Converts an image to premultiplied alpha from straight alpha.
var i: int var i: int
when defined(amd64) and not defined(pixieNoSimd): when defined(amd64) and not defined(pixieNoSimd):

View file

@ -1,4 +1,4 @@
import blends, chroma, common, images, vmath import blends, chroma, images, vmath
type type
PaintKind* = enum PaintKind* = enum
@ -13,7 +13,7 @@ type
## Paint used to fill paths. ## Paint used to fill paths.
case kind*: PaintKind case kind*: PaintKind
of pkSolid: of pkSolid:
color*: ColorRGBA ## Color to fill with. color*: ColorRGBX ## Color to fill with.
of pkImage, pkImageTiled: of pkImage, pkImageTiled:
image*: Image ## Image to fill with. image*: Image ## Image to fill with.
imageMat*: Mat3 ## Matrix of the filled image. imageMat*: Mat3 ## Matrix of the filled image.

View file

@ -1105,7 +1105,7 @@ template computeCoverages(
proc fillShapes( proc fillShapes(
image: Image, image: Image,
shapes: seq[seq[Vec2]], shapes: seq[seq[Vec2]],
color: ColorRGBA, color: ColorRGBX,
windingRule: WindingRule, windingRule: WindingRule,
blendMode: BlendMode blendMode: BlendMode
) = ) =
@ -1418,7 +1418,7 @@ proc parseSomePath(
proc fillPath*( proc fillPath*(
image: Image, image: Image,
path: SomePath, path: SomePath,
color: ColorRGBA, color: ColorRGBX,
windingRule = wrNonZero, windingRule = wrNonZero,
blendMode = bmNormal blendMode = bmNormal
) {.inline.} = ) {.inline.} =
@ -1428,7 +1428,7 @@ proc fillPath*(
proc fillPath*( proc fillPath*(
image: Image, image: Image,
path: SomePath, path: SomePath,
color: ColorRGBA, color: ColorRGBX,
transform: Vec2 | Mat3, transform: Vec2 | Mat3,
windingRule = wrNonZero, windingRule = wrNonZero,
blendMode = bmNormal blendMode = bmNormal
@ -1488,7 +1488,7 @@ proc fillPath*(
case paint.kind: case paint.kind:
of pkSolid: of pkSolid:
fill.fill(paint.color.toPremultipliedAlpha()) fill.fill(paint.color)
of pkImage: of pkImage:
fill.draw(paint.image, paint.imageMat) fill.draw(paint.image, paint.imageMat)
of pkImageTiled: of pkImageTiled:
@ -1520,7 +1520,7 @@ proc fillPath*(
proc strokePath*( proc strokePath*(
image: Image, image: Image,
path: SomePath, path: SomePath,
color: ColorRGBA, color: ColorRGBX,
strokeWidth = 1.0, strokeWidth = 1.0,
lineCap = lcButt, lineCap = lcButt,
lineJoin = ljMiter, lineJoin = ljMiter,
@ -1535,7 +1535,7 @@ proc strokePath*(
proc strokePath*( proc strokePath*(
image: Image, image: Image,
path: SomePath, path: SomePath,
color: ColorRGBA, color: ColorRGBX,
transform: Vec2 | Mat3, transform: Vec2 | Mat3,
strokeWidth = 1.0, strokeWidth = 1.0,
lineCap = lcButt, lineCap = lcButt,

View file

@ -30,10 +30,10 @@ block:
block: block:
let image = newImage(100, 100) let image = newImage(100, 100)
image.fill(rgba(200, 200, 200, 200)) image.fill(rgbx(200, 200, 200, 200))
image.applyOpacity(0.5) image.applyOpacity(0.5)
doAssert image[0, 0] == rgba(100, 100, 100, 100) doAssert image[0, 0] == rgbx(100, 100, 100, 100)
doAssert image[88, 88] == rgba(100, 100, 100, 100) doAssert image[88, 88] == rgbx(100, 100, 100, 100)
block: block:
let let
@ -101,6 +101,6 @@ block:
block: block:
let a = newImage(100, 100) let a = newImage(100, 100)
a.fill(rgba(50, 100, 150, 200)) a.fill(rgbx(50, 100, 150, 200))
a.invert() a.invert()
doAssert a[0, 0] == rgba(44, 33, 22, 55) doAssert a[0, 0] == rgbx(44, 33, 22, 55)