commit
eb805d0d75
11 changed files with 149 additions and 189 deletions
|
@ -42,10 +42,10 @@ type
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.push checks: off.}
|
{.push checks: off.}
|
||||||
|
|
||||||
proc min(a, b: uint32): uint32 {.inline, raises: [].} =
|
proc min(a, b: uint32): uint32 {.inline.} =
|
||||||
if a < b: a else: b
|
if a < b: a else: b
|
||||||
|
|
||||||
proc alphaFix(backdrop, source, mixed: ColorRGBA): ColorRGBA {.raises: [].} =
|
proc alphaFix(backdrop, source, mixed: ColorRGBA): ColorRGBA =
|
||||||
## After mixing an image, adjust its alpha value to be correct.
|
## After mixing an image, adjust its alpha value to be correct.
|
||||||
let
|
let
|
||||||
sa = source.a.uint32
|
sa = source.a.uint32
|
||||||
|
@ -68,7 +68,7 @@ proc alphaFix(backdrop, source, mixed: ColorRGBA): ColorRGBA {.raises: [].} =
|
||||||
result.b = (b div a div 255).uint8
|
result.b = (b div a div 255).uint8
|
||||||
result.a = a.uint8
|
result.a = a.uint8
|
||||||
|
|
||||||
proc alphaFix(backdrop, source, mixed: Color): Color {.raises: [].} =
|
proc alphaFix(backdrop, source, mixed: Color): Color =
|
||||||
## After mixing an image, adjust its alpha value to be correct.
|
## After mixing an image, adjust its alpha value to be correct.
|
||||||
result.a = (source.a + backdrop.a * (1.0 - source.a))
|
result.a = (source.a + backdrop.a * (1.0 - source.a))
|
||||||
if result.a == 0:
|
if result.a == 0:
|
||||||
|
@ -87,16 +87,16 @@ proc alphaFix(backdrop, source, mixed: Color): Color {.raises: [].} =
|
||||||
result.g /= result.a
|
result.g /= result.a
|
||||||
result.b /= result.a
|
result.b /= result.a
|
||||||
|
|
||||||
proc blendAlpha*(backdrop, source: uint8): uint8 {.inline, raises: [].} =
|
proc blendAlpha*(backdrop, source: uint8): uint8 {.inline.} =
|
||||||
## Blends alphas of backdrop, source.
|
## Blends alphas of backdrop, source.
|
||||||
source + ((backdrop.uint32 * (255 - source)) div 255).uint8
|
source + ((backdrop.uint32 * (255 - source)) div 255).uint8
|
||||||
|
|
||||||
proc screen(backdrop, source: uint32): uint8 {.inline, raises: [].} =
|
proc screen(backdrop, source: uint32): uint8 {.inline.} =
|
||||||
((backdrop + source).int32 - ((backdrop * source) div 255).int32).uint8
|
((backdrop + source).int32 - ((backdrop * source) div 255).int32).uint8
|
||||||
|
|
||||||
proc hardLight(
|
proc hardLight(
|
||||||
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint32
|
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint32
|
||||||
): uint8 {.inline, raises: [].} =
|
): uint8 {.inline.} =
|
||||||
if sourceColor * 2 <= sourceAlpha:
|
if sourceColor * 2 <= sourceAlpha:
|
||||||
((
|
((
|
||||||
2 * sourceColor * backdropColor +
|
2 * sourceColor * backdropColor +
|
||||||
|
@ -106,41 +106,41 @@ proc hardLight(
|
||||||
else:
|
else:
|
||||||
screen(backdropColor, sourceColor)
|
screen(backdropColor, sourceColor)
|
||||||
|
|
||||||
proc softLight(backdrop, source: float32): float32 {.inline, raises: [].} =
|
proc softLight(backdrop, source: float32): float32 {.inline.} =
|
||||||
## Pegtop
|
## Pegtop
|
||||||
(1 - 2 * source) * backdrop ^ 2 + 2 * source * backdrop
|
(1 - 2 * source) * backdrop ^ 2 + 2 * source * backdrop
|
||||||
|
|
||||||
proc `+`(c: Color, v: float32): Color {.inline, raises: [].} =
|
proc `+`(c: Color, v: float32): Color {.inline.} =
|
||||||
result.r = c.r + v
|
result.r = c.r + v
|
||||||
result.g = c.g + v
|
result.g = c.g + v
|
||||||
result.b = c.b + v
|
result.b = c.b + v
|
||||||
result.a = c.a + v
|
result.a = c.a + v
|
||||||
|
|
||||||
proc `+`(v: float32, c: Color): Color {.inline, raises: [].} =
|
proc `+`(v: float32, c: Color): Color {.inline.} =
|
||||||
c + v
|
c + v
|
||||||
|
|
||||||
proc `*`(c: Color, v: float32): Color {.inline, raises: [].} =
|
proc `*`(c: Color, v: float32): Color {.inline.} =
|
||||||
result.r = c.r * v
|
result.r = c.r * v
|
||||||
result.g = c.g * v
|
result.g = c.g * v
|
||||||
result.b = c.b * v
|
result.b = c.b * v
|
||||||
result.a = c.a * v
|
result.a = c.a * v
|
||||||
|
|
||||||
proc `/`(c: Color, v: float32): Color {.inline, raises: [].} =
|
proc `/`(c: Color, v: float32): Color {.inline.} =
|
||||||
result.r = c.r / v
|
result.r = c.r / v
|
||||||
result.g = c.g / v
|
result.g = c.g / v
|
||||||
result.b = c.b / v
|
result.b = c.b / v
|
||||||
result.a = c.a / v
|
result.a = c.a / v
|
||||||
|
|
||||||
proc `-`(c: Color, v: float32): Color {.inline, raises: [].} =
|
proc `-`(c: Color, v: float32): Color {.inline.} =
|
||||||
result.r = c.r - v
|
result.r = c.r - v
|
||||||
result.g = c.g - v
|
result.g = c.g - v
|
||||||
result.b = c.b - v
|
result.b = c.b - v
|
||||||
result.a = c.a - v
|
result.a = c.a - v
|
||||||
|
|
||||||
proc Lum(C: Color): float32 {.inline, raises: [].} =
|
proc Lum(C: Color): float32 {.inline.} =
|
||||||
0.3 * C.r + 0.59 * C.g + 0.11 * C.b
|
0.3 * C.r + 0.59 * C.g + 0.11 * C.b
|
||||||
|
|
||||||
proc ClipColor(C: var Color) {.inline, raises: [].} =
|
proc ClipColor(C: var Color) {.inline.} =
|
||||||
let
|
let
|
||||||
L = Lum(C)
|
L = Lum(C)
|
||||||
n = min([C.r, C.g, C.b])
|
n = min([C.r, C.g, C.b])
|
||||||
|
@ -150,22 +150,22 @@ proc ClipColor(C: var Color) {.inline, raises: [].} =
|
||||||
if x > 1:
|
if x > 1:
|
||||||
C = L + (((C - L) * (1 - L)) / (x - L))
|
C = L + (((C - L) * (1 - L)) / (x - L))
|
||||||
|
|
||||||
proc SetLum(C: Color, l: float32): Color {.inline, raises: [].} =
|
proc SetLum(C: Color, l: float32): Color {.inline.} =
|
||||||
let d = l - Lum(C)
|
let d = l - Lum(C)
|
||||||
result.r = C.r + d
|
result.r = C.r + d
|
||||||
result.g = C.g + d
|
result.g = C.g + d
|
||||||
result.b = C.b + d
|
result.b = C.b + d
|
||||||
ClipColor(result)
|
ClipColor(result)
|
||||||
|
|
||||||
proc Sat(C: Color): float32 {.inline, raises: [].} =
|
proc Sat(C: Color): float32 {.inline.} =
|
||||||
max([C.r, C.g, C.b]) - min([C.r, C.g, C.b])
|
max([C.r, C.g, C.b]) - min([C.r, C.g, C.b])
|
||||||
|
|
||||||
proc SetSat(C: Color, s: float32): Color {.inline, raises: [].} =
|
proc SetSat(C: Color, s: float32): Color {.inline.} =
|
||||||
let satC = Sat(C)
|
let satC = Sat(C)
|
||||||
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
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:
|
||||||
|
@ -179,7 +179,7 @@ proc blendNormal(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendDarken(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
proc blend(
|
proc blend(
|
||||||
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
||||||
): uint8 {.inline.} =
|
): uint8 {.inline.} =
|
||||||
|
@ -193,7 +193,7 @@ proc blendDarken(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendMultiply(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
proc blend(
|
proc blend(
|
||||||
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
||||||
): uint8 {.inline.} =
|
): uint8 {.inline.} =
|
||||||
|
@ -218,7 +218,7 @@ proc blendMultiply(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
# result = alphaFix(backdrop, source, result)
|
# result = alphaFix(backdrop, source, result)
|
||||||
# result = result.toPremultipliedAlpha()
|
# result = result.toPremultipliedAlpha()
|
||||||
|
|
||||||
proc blendColorBurn(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendColorBurn(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba()
|
backdrop = backdrop.rgba()
|
||||||
source = source.rgba()
|
source = source.rgba()
|
||||||
|
@ -235,7 +235,7 @@ proc blendColorBurn(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
blended.b = blend(backdrop.b, source.b)
|
blended.b = blend(backdrop.b, source.b)
|
||||||
result = alphaFix(backdrop, source, blended).rgbx()
|
result = alphaFix(backdrop, source, blended).rgbx()
|
||||||
|
|
||||||
proc blendLighten(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendLighten(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
proc blend(
|
proc blend(
|
||||||
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
backdropColor, backdropAlpha, sourceColor, sourceAlpha: uint8
|
||||||
): uint8 {.inline.} =
|
): uint8 {.inline.} =
|
||||||
|
@ -249,7 +249,7 @@ proc blendLighten(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
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)
|
||||||
|
@ -265,7 +265,7 @@ proc blendScreen(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
# result = alphaFix(backdrop, source, result)
|
# result = alphaFix(backdrop, source, result)
|
||||||
# result = result.toPremultipliedAlpha()
|
# result = result.toPremultipliedAlpha()
|
||||||
|
|
||||||
proc blendColorDodge(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendColorDodge(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba()
|
backdrop = backdrop.rgba()
|
||||||
source = source.rgba()
|
source = source.rgba()
|
||||||
|
@ -282,13 +282,13 @@ proc blendColorDodge(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
blended.b = blend(backdrop.b, source.b)
|
blended.b = blend(backdrop.b, source.b)
|
||||||
result = alphaFix(backdrop, source, blended).rgbx()
|
result = alphaFix(backdrop, source, blended).rgbx()
|
||||||
|
|
||||||
proc blendOverlay(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendSoftLight(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
# proc softLight(backdrop, source: int32): uint8 {.inline.} =
|
# proc softLight(backdrop, source: int32): uint8 {.inline.} =
|
||||||
# ## Pegtop
|
# ## Pegtop
|
||||||
# (
|
# (
|
||||||
|
@ -362,13 +362,13 @@ proc blendSoftLight(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
|
|
||||||
result = rgba.rgbx()
|
result = rgba.rgbx()
|
||||||
|
|
||||||
proc blendHardLight(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
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 +384,7 @@ proc blendDifference(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
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,56 +393,56 @@ proc blendExclusion(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
||||||
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendColor(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
blended = SetLum(source, Lum(backdrop))
|
blended = SetLum(source, Lum(backdrop))
|
||||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||||
|
|
||||||
proc blendLuminosity(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendLuminosity(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
blended = SetLum(backdrop, Lum(source))
|
blended = SetLum(backdrop, Lum(source))
|
||||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||||
|
|
||||||
proc blendHue(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendHue(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
blended = SetLum(SetSat(source, Sat(backdrop)), Lum(backdrop))
|
blended = SetLum(SetSat(source, Sat(backdrop)), Lum(backdrop))
|
||||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||||
|
|
||||||
proc blendSaturation(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendSaturation(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let
|
let
|
||||||
backdrop = backdrop.rgba().color
|
backdrop = backdrop.rgba().color
|
||||||
source = source.rgba().color
|
source = source.rgba().color
|
||||||
blended = SetLum(SetSat(backdrop, Sat(source)), Lum(backdrop))
|
blended = SetLum(SetSat(backdrop, Sat(source)), Lum(backdrop))
|
||||||
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
result = alphaFix(backdrop, source, blended).rgba.rgbx()
|
||||||
|
|
||||||
proc blendMask(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
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: ColorRGBX): ColorRGBX {.raises: [].} =
|
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 blendExcludeMask(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
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 = ((source.r * a) div 255).uint8
|
result.r = ((source.r * a) div 255).uint8
|
||||||
result.g = ((source.g * a) div 255).uint8
|
result.g = ((source.g * a) div 255).uint8
|
||||||
result.b = ((source.b * a) div 255).uint8
|
result.b = ((source.b * a) div 255).uint8
|
||||||
result.a = a.uint8
|
result.a = a.uint8
|
||||||
|
|
||||||
proc blendOverwrite(backdrop, source: ColorRGBX): ColorRGBX {.raises: [].} =
|
proc blendOverwrite(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
source
|
source
|
||||||
|
|
||||||
# proc blendWhite(backdrop, source: ColorRGBX): ColorRGBX =
|
# proc blendWhite(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
|
@ -475,21 +475,21 @@ proc blender*(blendMode: BlendMode): Blender {.raises: [].} =
|
||||||
of bmSubtractMask: blendSubtractMask
|
of bmSubtractMask: blendSubtractMask
|
||||||
of bmExcludeMask: blendExcludeMask
|
of bmExcludeMask: blendExcludeMask
|
||||||
|
|
||||||
proc maskNormal(backdrop, source: uint8): uint8 {.raises: [].} =
|
proc maskNormal(backdrop, source: uint8): uint8 =
|
||||||
## Blending masks
|
## Blending masks
|
||||||
blendAlpha(backdrop, source)
|
blendAlpha(backdrop, source)
|
||||||
|
|
||||||
proc maskMask(backdrop, source: uint8): uint8 {.raises: [].} =
|
proc maskMask(backdrop, source: uint8): uint8 =
|
||||||
## Masking masks
|
## Masking masks
|
||||||
((backdrop.uint32 * source) div 255).uint8
|
((backdrop.uint32 * source) div 255).uint8
|
||||||
|
|
||||||
proc maskSubtract(backdrop, source: uint8): uint8 {.raises: [].} =
|
proc maskSubtract(backdrop, source: uint8): uint8 =
|
||||||
((backdrop.uint32 * (255 - source)) div 255).uint8
|
((backdrop.uint32 * (255 - source)) div 255).uint8
|
||||||
|
|
||||||
proc maskExclude(backdrop, source: uint8): uint8 {.raises: [].} =
|
proc maskExclude(backdrop, source: uint8): uint8 =
|
||||||
max(backdrop, source) - min(backdrop, source)
|
max(backdrop, source) - min(backdrop, source)
|
||||||
|
|
||||||
proc maskOverwrite(backdrop, source: uint8): uint8 {.raises: [].} =
|
proc maskOverwrite(backdrop, source: uint8): uint8 =
|
||||||
source
|
source
|
||||||
|
|
||||||
proc masker*(blendMode: BlendMode): Masker {.raises: [PixieError].} =
|
proc masker*(blendMode: BlendMode): Masker {.raises: [PixieError].} =
|
||||||
|
@ -512,7 +512,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
MaskerSimd* = proc(blackdrop, source: M128i): M128i {.raises: [].}
|
MaskerSimd* = proc(blackdrop, source: M128i): M128i {.raises: [].}
|
||||||
## Function signature returned by maskerSimd.
|
## Function signature returned by maskerSimd.
|
||||||
|
|
||||||
proc blendNormalSimd(backdrop, source: M128i): M128i {.raises: [].} =
|
proc blendNormalSimd(backdrop, source: M128i): M128i =
|
||||||
let
|
let
|
||||||
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
|
@ -541,7 +541,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
||||||
)
|
)
|
||||||
|
|
||||||
proc blendMaskSimd(backdrop, source: M128i): M128i {.raises: [].} =
|
proc blendMaskSimd(backdrop, source: M128i): M128i =
|
||||||
let
|
let
|
||||||
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
alphaMask = mm_set1_epi32(cast[int32](0xff000000))
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
|
@ -562,7 +562,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
|
|
||||||
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))
|
||||||
|
|
||||||
proc blendOverwriteSimd(backdrop, source: M128i): M128i {.raises: [].} =
|
proc blendOverwriteSimd(backdrop, source: M128i): M128i =
|
||||||
source
|
source
|
||||||
|
|
||||||
proc blenderSimd*(blendMode: BlendMode): BlenderSimd {.raises: [PixieError].} =
|
proc blenderSimd*(blendMode: BlendMode): BlenderSimd {.raises: [PixieError].} =
|
||||||
|
@ -578,7 +578,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
## Is there a blend function for a given blend mode with SIMD support?
|
## Is there a blend function for a given blend mode with SIMD support?
|
||||||
blendMode in {bmNormal, bmMask, bmOverwrite}
|
blendMode in {bmNormal, bmMask, bmOverwrite}
|
||||||
|
|
||||||
proc maskNormalSimd(backdrop, source: M128i): M128i {.raises: [].} =
|
proc maskNormalSimd(backdrop, source: M128i): M128i =
|
||||||
## Blending masks
|
## Blending masks
|
||||||
let
|
let
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
|
@ -615,7 +615,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
|
|
||||||
mm_or_si128(blendedEven, mm_slli_epi16(blendedOdd, 8))
|
mm_or_si128(blendedEven, mm_slli_epi16(blendedOdd, 8))
|
||||||
|
|
||||||
proc maskMaskSimd(backdrop, source: M128i): M128i {.raises: [].} =
|
proc maskMaskSimd(backdrop, source: M128i): M128i =
|
||||||
let
|
let
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
div255 = mm_set1_epi16(cast[int16](0x8081))
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
|
|
|
@ -63,7 +63,7 @@ proc newContext*(width, height: int): Context {.inline, raises: [PixieError].} =
|
||||||
## Create a new Context that will draw to a new image of width and height.
|
## Create a new Context that will draw to a new image of width and height.
|
||||||
newContext(newImage(width, height))
|
newContext(newImage(width, height))
|
||||||
|
|
||||||
proc state(ctx: Context): ContextState {.raises: [PixieError].} =
|
proc state(ctx: Context): ContextState =
|
||||||
result.fillStyle = ctx.fillStyle
|
result.fillStyle = ctx.fillStyle
|
||||||
result.strokeStyle = ctx.strokeStyle
|
result.strokeStyle = ctx.strokeStyle
|
||||||
result.globalAlpha = ctx.globalAlpha
|
result.globalAlpha = ctx.globalAlpha
|
||||||
|
@ -130,9 +130,7 @@ proc restore*(ctx: Context) {.raises: [PixieError].} =
|
||||||
else: # Otherwise draw to the root image
|
else: # Otherwise draw to the root image
|
||||||
ctx.image.draw(poppedLayer)
|
ctx.image.draw(poppedLayer)
|
||||||
|
|
||||||
proc fill(
|
proc fill(ctx: Context, image: Image, path: Path, windingRule: WindingRule) =
|
||||||
ctx: Context, image: Image, path: Path, windingRule: WindingRule
|
|
||||||
) {.raises: [PixieError].} =
|
|
||||||
var image = image
|
var image = image
|
||||||
|
|
||||||
if ctx.globalAlpha != 1:
|
if ctx.globalAlpha != 1:
|
||||||
|
@ -150,7 +148,7 @@ proc fill(
|
||||||
ctx.layer.applyOpacity(ctx.globalAlpha)
|
ctx.layer.applyOpacity(ctx.globalAlpha)
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
proc stroke(ctx: Context, image: Image, path: Path) {.raises: [PixieError].} =
|
proc stroke(ctx: Context, image: Image, path: Path) =
|
||||||
var image = image
|
var image = image
|
||||||
|
|
||||||
if ctx.globalAlpha != 1:
|
if ctx.globalAlpha != 1:
|
||||||
|
@ -172,7 +170,7 @@ proc stroke(ctx: Context, image: Image, path: Path) {.raises: [PixieError].} =
|
||||||
ctx.layer.applyOpacity(ctx.globalAlpha)
|
ctx.layer.applyOpacity(ctx.globalAlpha)
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
proc newFont(ctx: Context): Font {.raises: [PixieError].} =
|
proc newFont(ctx: Context): Font =
|
||||||
if ctx.font == "":
|
if ctx.font == "":
|
||||||
raise newException(PixieError, "No font has been set on this Context")
|
raise newException(PixieError, "No font has been set on this Context")
|
||||||
|
|
||||||
|
@ -182,9 +180,7 @@ proc newFont(ctx: Context): Font {.raises: [PixieError].} =
|
||||||
result = newFont(ctx.typefaces.getOrDefault(ctx.font, nil))
|
result = newFont(ctx.typefaces.getOrDefault(ctx.font, nil))
|
||||||
result.size = ctx.fontSize
|
result.size = ctx.fontSize
|
||||||
|
|
||||||
proc fillText(
|
proc fillText(ctx: Context, image: Image, text: string, at: Vec2) =
|
||||||
ctx: Context, image: Image, text: string, at: Vec2
|
|
||||||
) {.raises: [PixieError].} =
|
|
||||||
let font = newFont(ctx)
|
let font = newFont(ctx)
|
||||||
|
|
||||||
# Canvas positions text relative to the alphabetic baseline by default
|
# Canvas positions text relative to the alphabetic baseline by default
|
||||||
|
@ -210,9 +206,7 @@ proc fillText(
|
||||||
ctx.layer.applyOpacity(ctx.globalAlpha)
|
ctx.layer.applyOpacity(ctx.globalAlpha)
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
proc strokeText(
|
proc strokeText(ctx: Context, image: Image, text: string, at: Vec2) =
|
||||||
ctx: Context, image: Image, text: string, at: Vec2
|
|
||||||
) {.raises: [PixieError].} =
|
|
||||||
let font = newFont(ctx)
|
let font = newFont(ctx)
|
||||||
|
|
||||||
# Canvas positions text relative to the alphabetic baseline by default
|
# Canvas positions text relative to the alphabetic baseline by default
|
||||||
|
@ -501,7 +495,7 @@ proc getLineDash*(ctx: Context): seq[float32] {.inline, raises: [].} =
|
||||||
proc setLineDash*(ctx: Context, lineDash: seq[float32]) {.inline, raises: [].} =
|
proc setLineDash*(ctx: Context, lineDash: seq[float32]) {.inline, raises: [].} =
|
||||||
ctx.lineDash = lineDash
|
ctx.lineDash = lineDash
|
||||||
|
|
||||||
proc getTransform*(ctx: Context): Mat3 {.inline, raises: []} =
|
proc getTransform*(ctx: Context): Mat3 {.inline, raises: [].} =
|
||||||
## Retrieves the current transform matrix being applied to the context.
|
## Retrieves the current transform matrix being applied to the context.
|
||||||
ctx.mat
|
ctx.mat
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ template failInvalid() =
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.push checks: off.}
|
{.push checks: off.}
|
||||||
|
|
||||||
proc decodeHeader(data: string): PngHeader {.raises: [PixieError].} =
|
proc decodeHeader(data: string): PngHeader =
|
||||||
result.width = data.readUint32(0).swap().int
|
result.width = data.readUint32(0).swap().int
|
||||||
result.height = data.readUint32(4).swap().int
|
result.height = data.readUint32(4).swap().int
|
||||||
result.bitDepth = data.readUint8(8)
|
result.bitDepth = data.readUint8(8)
|
||||||
|
@ -79,7 +79,7 @@ proc decodeHeader(data: string): PngHeader {.raises: [PixieError].} =
|
||||||
if result.interlaceMethod != 0:
|
if result.interlaceMethod != 0:
|
||||||
raise newException(PixieError, "Interlaced PNG not yet supported")
|
raise newException(PixieError, "Interlaced PNG not yet supported")
|
||||||
|
|
||||||
proc decodePalette(data: string): seq[ColorRGB] {.raises: [PixieError].} =
|
proc decodePalette(data: string): seq[ColorRGB] =
|
||||||
if data.len == 0 or data.len mod 3 != 0:
|
if data.len == 0 or data.len mod 3 != 0:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ proc decodePalette(data: string): seq[ColorRGB] {.raises: [PixieError].} =
|
||||||
|
|
||||||
proc unfilter(
|
proc unfilter(
|
||||||
uncompressed: string, height, rowBytes, bpp: int
|
uncompressed: string, height, rowBytes, bpp: int
|
||||||
): string {.raises: [].} =
|
): string =
|
||||||
result.setLen(uncompressed.len - height)
|
result.setLen(uncompressed.len - height)
|
||||||
|
|
||||||
template uncompressedIdx(x, y: int): int =
|
template uncompressedIdx(x, y: int): int =
|
||||||
|
@ -164,7 +164,7 @@ proc decodeImageData(
|
||||||
header: PngHeader,
|
header: PngHeader,
|
||||||
palette: seq[ColorRGB],
|
palette: seq[ColorRGB],
|
||||||
transparency, data: string
|
transparency, data: string
|
||||||
): seq[ColorRGBA] {.raises: [PixieError].} =
|
): seq[ColorRGBA] =
|
||||||
result.setLen(header.width * header.height)
|
result.setLen(header.width * header.height)
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
|
@ -33,12 +33,12 @@ type
|
||||||
template failInvalid() =
|
template failInvalid() =
|
||||||
raise newException(PixieError, "Invalid SVG data")
|
raise newException(PixieError, "Invalid SVG data")
|
||||||
|
|
||||||
proc attrOrDefault(node: XmlNode, name, default: string): string {.raises: [].} =
|
proc attrOrDefault(node: XmlNode, name, default: string): string =
|
||||||
result = node.attr(name)
|
result = node.attr(name)
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
result = default
|
result = default
|
||||||
|
|
||||||
proc initCtx(): Ctx {.raises: [PixieError].} =
|
proc initCtx(): Ctx =
|
||||||
result.display = true
|
result.display = true
|
||||||
try:
|
try:
|
||||||
result.fill = parseHtmlColor("black").rgbx
|
result.fill = parseHtmlColor("black").rgbx
|
||||||
|
@ -317,7 +317,7 @@ proc decodeCtxInternal(inherited: Ctx, node: XmlNode): Ctx =
|
||||||
else:
|
else:
|
||||||
failInvalidTransform(transform)
|
failInvalidTransform(transform)
|
||||||
|
|
||||||
proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx {.raises: [PixieError].} =
|
proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx =
|
||||||
try:
|
try:
|
||||||
decodeCtxInternal(inherited, node)
|
decodeCtxInternal(inherited, node)
|
||||||
except PixieError as e:
|
except PixieError as e:
|
||||||
|
@ -326,14 +326,14 @@ proc decodeCtx(inherited: Ctx, node: XmlNode): Ctx {.raises: [PixieError].} =
|
||||||
let e = getCurrentException()
|
let e = getCurrentException()
|
||||||
raise newException(PixieError, e.msg, e)
|
raise newException(PixieError, e.msg, e)
|
||||||
|
|
||||||
proc fill(img: Image, ctx: Ctx, path: Path) {.inline, raises: [PixieError].} =
|
proc fill(img: Image, ctx: Ctx, path: Path) {.inline.} =
|
||||||
if ctx.display and ctx.opacity > 0:
|
if ctx.display and ctx.opacity > 0:
|
||||||
let paint = newPaint(ctx.fill)
|
let paint = newPaint(ctx.fill)
|
||||||
if ctx.opacity != 1:
|
if ctx.opacity != 1:
|
||||||
paint.opacity = paint.opacity * ctx.opacity
|
paint.opacity = paint.opacity * ctx.opacity
|
||||||
img.fillPath(path, paint, ctx.transform, ctx.fillRule)
|
img.fillPath(path, paint, ctx.transform, ctx.fillRule)
|
||||||
|
|
||||||
proc stroke(img: Image, ctx: Ctx, path: Path) {.inline, raises: [PixieError].} =
|
proc stroke(img: Image, ctx: Ctx, path: Path) {.inline.} =
|
||||||
if ctx.display and ctx.opacity > 0:
|
if ctx.display and ctx.opacity > 0:
|
||||||
let paint = newPaint(ctx.stroke)
|
let paint = newPaint(ctx.stroke)
|
||||||
if ctx.opacity != 1:
|
if ctx.opacity != 1:
|
||||||
|
@ -556,9 +556,7 @@ proc drawInternal(img: Image, node: XmlNode, ctxStack: var seq[Ctx]) =
|
||||||
else:
|
else:
|
||||||
raise newException(PixieError, "Unsupported SVG tag: " & node.tag)
|
raise newException(PixieError, "Unsupported SVG tag: " & node.tag)
|
||||||
|
|
||||||
proc draw(
|
proc draw(img: Image, node: XmlNode, ctxStack: var seq[Ctx]) =
|
||||||
img: Image, node: XmlNode, ctxStack: var seq[Ctx]
|
|
||||||
) {.raises: [PixieError].} =
|
|
||||||
try:
|
try:
|
||||||
drawInternal(img, node, ctxStack)
|
drawInternal(img, node, ctxStack)
|
||||||
except PixieError as e:
|
except PixieError as e:
|
||||||
|
|
|
@ -341,27 +341,25 @@ template eofCheck(buf: string, readTo: int) =
|
||||||
template failUnsupported() =
|
template failUnsupported() =
|
||||||
raise newException(PixieError, "Unsupported font data")
|
raise newException(PixieError, "Unsupported font data")
|
||||||
|
|
||||||
proc readUint16Seq(buf: string, offset, len: int): seq[uint16] {.raises: [].} =
|
proc readUint16Seq(buf: string, offset, len: int): seq[uint16] =
|
||||||
result = newSeq[uint16](len)
|
result = newSeq[uint16](len)
|
||||||
for i in 0 ..< len:
|
for i in 0 ..< len:
|
||||||
result[i] = buf.readUint16(offset + i * 2).swap()
|
result[i] = buf.readUint16(offset + i * 2).swap()
|
||||||
|
|
||||||
proc readFixed32(buf: string, offset: int): float32 {.raises: [].} =
|
proc readFixed32(buf: string, offset: int): float32 =
|
||||||
## Packed 32-bit value with major and minor version numbers.
|
## Packed 32-bit value with major and minor version numbers.
|
||||||
ceil(buf.readInt32(offset).swap().float32 / 65536.0 * 100000.0) / 100000.0
|
ceil(buf.readInt32(offset).swap().float32 / 65536.0 * 100000.0) / 100000.0
|
||||||
|
|
||||||
proc readFixed16(buf: string, offset: int): float32 {.raises: [].} =
|
proc readFixed16(buf: string, offset: int): float32 =
|
||||||
## Reads 16-bit signed fixed number with the low 14 bits of fraction (2.14).
|
## Reads 16-bit signed fixed number with the low 14 bits of fraction (2.14).
|
||||||
buf.readInt16(offset).swap().float32 / 16384.0
|
buf.readInt16(offset).swap().float32 / 16384.0
|
||||||
|
|
||||||
proc readLongDateTime(buf: string, offset: int): float64 {.raises: [].} =
|
proc readLongDateTime(buf: string, offset: int): float64 =
|
||||||
## Date and time represented in number of seconds since 12:00 midnight,
|
## Date and time represented in number of seconds since 12:00 midnight,
|
||||||
## January 1, 1904, UTC.
|
## January 1, 1904, UTC.
|
||||||
buf.readInt64(offset).swap().float64 - 2082844800
|
buf.readInt64(offset).swap().float64 - 2082844800
|
||||||
|
|
||||||
proc parseCmapTable(
|
proc parseCmapTable(buf: string, offset: int): CmapTable =
|
||||||
buf: string, offset: int
|
|
||||||
): CmapTable {.raises: [PixieError].} =
|
|
||||||
var i = offset
|
var i = offset
|
||||||
buf.eofCheck(i + 4)
|
buf.eofCheck(i + 4)
|
||||||
|
|
||||||
|
@ -456,9 +454,7 @@ proc parseCmapTable(
|
||||||
# TODO implement other cmap platformIDs
|
# TODO implement other cmap platformIDs
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc parseHeadTable(
|
proc parseHeadTable(buf: string, offset: int): HeadTable =
|
||||||
buf: string, offset: int
|
|
||||||
): HeadTable {.raises: [PixieError].} =
|
|
||||||
buf.eofCheck(offset + 54)
|
buf.eofCheck(offset + 54)
|
||||||
|
|
||||||
result = HeadTable()
|
result = HeadTable()
|
||||||
|
@ -487,9 +483,7 @@ proc parseHeadTable(
|
||||||
if result.glyphDataFormat != 0:
|
if result.glyphDataFormat != 0:
|
||||||
failUnsupported()
|
failUnsupported()
|
||||||
|
|
||||||
proc parseHheaTable(
|
proc parseHheaTable(buf: string, offset: int): HheaTable =
|
||||||
buf: string, offset: int
|
|
||||||
): HheaTable {.raises: [PixieError].} =
|
|
||||||
buf.eofCheck(offset + 36)
|
buf.eofCheck(offset + 36)
|
||||||
|
|
||||||
result = HheaTable()
|
result = HheaTable()
|
||||||
|
@ -518,9 +512,7 @@ proc parseHheaTable(
|
||||||
failUnsupported()
|
failUnsupported()
|
||||||
result.numberOfHMetrics = buf.readUint16(offset + 34).swap()
|
result.numberOfHMetrics = buf.readUint16(offset + 34).swap()
|
||||||
|
|
||||||
proc parseMaxpTable(
|
proc parseMaxpTable(buf: string, offset: int): MaxpTable =
|
||||||
buf: string, offset: int
|
|
||||||
): MaxpTable {.raises: [PixieError].} =
|
|
||||||
buf.eofCheck(offset + 32)
|
buf.eofCheck(offset + 32)
|
||||||
|
|
||||||
result = MaxpTable()
|
result = MaxpTable()
|
||||||
|
@ -544,7 +536,7 @@ proc parseMaxpTable(
|
||||||
|
|
||||||
proc parseHmtxTable(
|
proc parseHmtxTable(
|
||||||
buf: string, offset: int, hhea: HheaTable, maxp: MaxpTable
|
buf: string, offset: int, hhea: HheaTable, maxp: MaxpTable
|
||||||
): HmtxTable {.raises: [PixieError].} =
|
): HmtxTable =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -565,9 +557,7 @@ proc parseHmtxTable(
|
||||||
result.leftSideBearings.add(buf.readInt16(i).swap())
|
result.leftSideBearings.add(buf.readInt16(i).swap())
|
||||||
i += 2
|
i += 2
|
||||||
|
|
||||||
proc parseNameTable(
|
proc parseNameTable(buf: string, offset: int): NameTable =
|
||||||
buf: string, offset: int
|
|
||||||
): NameTable {.raises: [PixieError].} =
|
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 6)
|
buf.eofCheck(i + 6)
|
||||||
|
@ -593,9 +583,7 @@ proc parseNameTable(
|
||||||
record.offset = buf.readUint16(i + 10).swap()
|
record.offset = buf.readUint16(i + 10).swap()
|
||||||
i += 12
|
i += 12
|
||||||
|
|
||||||
proc parseOS2Table(
|
proc parseOS2Table(buf: string, offset: int): OS2Table =
|
||||||
buf: string, offset: int
|
|
||||||
): OS2Table {.raises: [PixieError].} =
|
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 78)
|
buf.eofCheck(i + 78)
|
||||||
|
@ -659,7 +647,7 @@ proc parseOS2Table(
|
||||||
|
|
||||||
proc parseLocaTable(
|
proc parseLocaTable(
|
||||||
buf: string, offset: int, head: HeadTable, maxp: MaxpTable
|
buf: string, offset: int, head: HeadTable, maxp: MaxpTable
|
||||||
): LocaTable {.raises: [PixieError].} =
|
): LocaTable =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
result = LocaTable()
|
result = LocaTable()
|
||||||
|
@ -678,15 +666,13 @@ proc parseLocaTable(
|
||||||
|
|
||||||
proc parseGlyfTable(
|
proc parseGlyfTable(
|
||||||
buf: string, offset: int, loca: LocaTable
|
buf: string, offset: int, loca: LocaTable
|
||||||
): GlyfTable {.raises: [].} =
|
): GlyfTable =
|
||||||
result = GlyfTable()
|
result = GlyfTable()
|
||||||
result.offsets.setLen(loca.offsets.len)
|
result.offsets.setLen(loca.offsets.len)
|
||||||
for glyphId in 0 ..< loca.offsets.len:
|
for glyphId in 0 ..< loca.offsets.len:
|
||||||
result.offsets[glyphId] = offset.uint32 + loca.offsets[glyphId]
|
result.offsets[glyphId] = offset.uint32 + loca.offsets[glyphId]
|
||||||
|
|
||||||
proc parseKernTable(
|
proc parseKernTable(buf: string, offset: int): KernTable =
|
||||||
buf: string, offset: int
|
|
||||||
): KernTable {.raises: [PixieError].} =
|
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 2)
|
buf.eofCheck(i + 2)
|
||||||
|
@ -748,9 +734,7 @@ proc parseKernTable(
|
||||||
else:
|
else:
|
||||||
failUnsupported()
|
failUnsupported()
|
||||||
|
|
||||||
# proc parseLangSys(
|
# proc parseLangSys(buf: string, offset: int): LangSys =
|
||||||
# buf: string, offset: int
|
|
||||||
# ): LangSys {.raises: [PixieError].} =
|
|
||||||
# var i = offset
|
# var i = offset
|
||||||
|
|
||||||
# buf.eofCheck(i + 6)
|
# buf.eofCheck(i + 6)
|
||||||
|
@ -838,16 +822,14 @@ proc parseKernTable(
|
||||||
# result.featureRecords.add(featureRecord)
|
# result.featureRecords.add(featureRecord)
|
||||||
# i += 6
|
# i += 6
|
||||||
|
|
||||||
proc parseRangeRecord(
|
proc parseRangeRecord(buf: string, offset: int): RangeRecord =
|
||||||
buf: string, offset: int
|
|
||||||
): RangeRecord {.raises: [PixieError].} =
|
|
||||||
buf.eofCheck(offset + 6)
|
buf.eofCheck(offset + 6)
|
||||||
|
|
||||||
result.startGlyphID = buf.readUint16(offset + 0).swap()
|
result.startGlyphID = buf.readUint16(offset + 0).swap()
|
||||||
result.endGlyphID = buf.readUint16(offset + 2).swap()
|
result.endGlyphID = buf.readUint16(offset + 2).swap()
|
||||||
result.startCoverageIndex = buf.readUint16(offset + 4).swap()
|
result.startCoverageIndex = buf.readUint16(offset + 4).swap()
|
||||||
|
|
||||||
proc parseCoverage(buf: string, offset: int): Coverage {.raises: [PixieError].} =
|
proc parseCoverage(buf: string, offset: int): Coverage =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 4)
|
buf.eofCheck(i + 4)
|
||||||
|
@ -885,7 +867,7 @@ proc parseCoverage(buf: string, offset: int): Coverage {.raises: [PixieError].}
|
||||||
else:
|
else:
|
||||||
failUnsupported()
|
failUnsupported()
|
||||||
|
|
||||||
proc valueFormatSize(valueFormat: uint16): int {.raises: [].} =
|
proc valueFormatSize(valueFormat: uint16): int =
|
||||||
# countSetBits(valueFormat) * 2
|
# countSetBits(valueFormat) * 2
|
||||||
var
|
var
|
||||||
n = valueFormat
|
n = valueFormat
|
||||||
|
@ -897,7 +879,7 @@ proc valueFormatSize(valueFormat: uint16): int {.raises: [].} =
|
||||||
|
|
||||||
proc parseValueRecord(
|
proc parseValueRecord(
|
||||||
buf: string, offset: int, valueFormat: uint16
|
buf: string, offset: int, valueFormat: uint16
|
||||||
): ValueRecord {.raises: [PixieError].} =
|
): ValueRecord =
|
||||||
buf.eofCheck(offset + valueFormatSize(valueFormat))
|
buf.eofCheck(offset + valueFormatSize(valueFormat))
|
||||||
|
|
||||||
var i = offset
|
var i = offset
|
||||||
|
@ -928,7 +910,7 @@ proc parseValueRecord(
|
||||||
|
|
||||||
proc parsePairValueRecord(
|
proc parsePairValueRecord(
|
||||||
buf: string, offset: int, valueFormat1, valueFormat2: uint16
|
buf: string, offset: int, valueFormat1, valueFormat2: uint16
|
||||||
): PairValueRecord {.raises: [PixieError].} =
|
): PairValueRecord =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 2)
|
buf.eofCheck(i + 2)
|
||||||
|
@ -942,7 +924,7 @@ proc parsePairValueRecord(
|
||||||
|
|
||||||
proc parsePairSet(
|
proc parsePairSet(
|
||||||
buf: string, offset: int, valueFormat1, valueFormat2: uint16
|
buf: string, offset: int, valueFormat1, valueFormat2: uint16
|
||||||
): PairSet {.raises: [PixieError].} =
|
): PairSet =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 2)
|
buf.eofCheck(i + 2)
|
||||||
|
@ -961,7 +943,7 @@ proc parsePairSet(
|
||||||
|
|
||||||
proc parseClass2Record(
|
proc parseClass2Record(
|
||||||
buf: string, offset: int, valueFormat1, valueFormat2: uint16
|
buf: string, offset: int, valueFormat1, valueFormat2: uint16
|
||||||
): Class2Record {.raises: [PixieError].} =
|
): Class2Record =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(
|
buf.eofCheck(
|
||||||
|
@ -974,7 +956,7 @@ proc parseClass2Record(
|
||||||
|
|
||||||
proc parseClass1Record(
|
proc parseClass1Record(
|
||||||
buf: string, offset: int, valueFormat1, valueFormat2, class2Count: uint16
|
buf: string, offset: int, valueFormat1, valueFormat2, class2Count: uint16
|
||||||
): Class1Record {.raises: [PixieError].} =
|
): Class1Record =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
result.class2Records.setLen(class2Count.int)
|
result.class2Records.setLen(class2Count.int)
|
||||||
|
@ -985,14 +967,14 @@ proc parseClass1Record(
|
||||||
|
|
||||||
proc parseClassRangeRecord(
|
proc parseClassRangeRecord(
|
||||||
buf: string, offset: int
|
buf: string, offset: int
|
||||||
): ClassRangeRecord {.raises: [PixieError].} =
|
): ClassRangeRecord =
|
||||||
buf.eofCheck(offset + 6)
|
buf.eofCheck(offset + 6)
|
||||||
|
|
||||||
result.startGlyphID = buf.readUint16(offset + 0).swap()
|
result.startGlyphID = buf.readUint16(offset + 0).swap()
|
||||||
result.endGlyphID = buf.readUint16(offset + 2).swap()
|
result.endGlyphID = buf.readUint16(offset + 2).swap()
|
||||||
result.class = buf.readUint16(offset + 4).swap()
|
result.class = buf.readUint16(offset + 4).swap()
|
||||||
|
|
||||||
proc parseClassDef(buf: string, offset: int): ClassDef {.raises: [PixieError].} =
|
proc parseClassDef(buf: string, offset: int): ClassDef =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 2)
|
buf.eofCheck(i + 2)
|
||||||
|
@ -1024,7 +1006,7 @@ proc parseClassDef(buf: string, offset: int): ClassDef {.raises: [PixieError].}
|
||||||
else:
|
else:
|
||||||
failUnsupported()
|
failUnsupported()
|
||||||
|
|
||||||
proc parsePairPos(buf: string, offset: int): PairPos {.raises: [PixieError].} =
|
proc parsePairPos(buf: string, offset: int): PairPos =
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 4)
|
buf.eofCheck(i + 4)
|
||||||
|
@ -1157,9 +1139,7 @@ proc parsePairPos(buf: string, offset: int): PairPos {.raises: [PixieError].} =
|
||||||
else:
|
else:
|
||||||
failUnsupported()
|
failUnsupported()
|
||||||
|
|
||||||
proc parseLookup(
|
proc parseLookup(buf: string, offset: int, gpos: GposTable): Lookup =
|
||||||
buf: string, offset: int, gpos: GposTable
|
|
||||||
): Lookup {.raises: [PixieError].} =
|
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 6)
|
buf.eofCheck(i + 6)
|
||||||
|
@ -1185,9 +1165,7 @@ proc parseLookup(
|
||||||
pairPos.classPairAdjustments.len > 0:
|
pairPos.classPairAdjustments.len > 0:
|
||||||
gpos.lookupList.pairPosTables.add(pairPos)
|
gpos.lookupList.pairPosTables.add(pairPos)
|
||||||
|
|
||||||
proc parseLookupList(
|
proc parseLookupList(buf: string, offset: int, gpos: GposTable): LookupList =
|
||||||
buf: string, offset: int, gpos: GposTable
|
|
||||||
): LookupList {.raises: [PixieError].} =
|
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 2)
|
buf.eofCheck(i + 2)
|
||||||
|
@ -1202,9 +1180,7 @@ proc parseLookupList(
|
||||||
for lookupOffset in result.lookupoffsets:
|
for lookupOffset in result.lookupoffsets:
|
||||||
result.lookups.add(parseLookup(buf, offset + lookupOffset.int, gpos))
|
result.lookups.add(parseLookup(buf, offset + lookupOffset.int, gpos))
|
||||||
|
|
||||||
proc parseGposTable(
|
proc parseGposTable(buf: string, offset: int): GPOSTable =
|
||||||
buf: string, offset: int
|
|
||||||
): GPOSTable {.raises: [PixieError].} =
|
|
||||||
var i = offset
|
var i = offset
|
||||||
|
|
||||||
buf.eofCheck(i + 10)
|
buf.eofCheck(i + 10)
|
||||||
|
@ -1235,9 +1211,7 @@ proc parseGposTable(
|
||||||
result.lookupList =
|
result.lookupList =
|
||||||
parseLookupList(buf, offset + result.lookupListOffset.int, result)
|
parseLookupList(buf, offset + result.lookupListOffset.int, result)
|
||||||
|
|
||||||
proc parsePostTable(
|
proc parsePostTable(buf: string, offset: int): PostTable =
|
||||||
buf: string, offset: int
|
|
||||||
): PostTable {.raises: [PixieError].} =
|
|
||||||
buf.eofCheck(offset + 14)
|
buf.eofCheck(offset + 14)
|
||||||
|
|
||||||
result = PostTable()
|
result = PostTable()
|
||||||
|
@ -1247,14 +1221,14 @@ proc parsePostTable(
|
||||||
result.underlineThickness = buf.readInt16(offset + 10).swap()
|
result.underlineThickness = buf.readInt16(offset + 10).swap()
|
||||||
result.isFixedPitch = buf.readUint32(offset + 12).swap()
|
result.isFixedPitch = buf.readUint32(offset + 12).swap()
|
||||||
|
|
||||||
proc getGlyphId(opentype: OpenType, rune: Rune): uint16 {.inline, raises: [].} =
|
proc getGlyphId(opentype: OpenType, rune: Rune): uint16 =
|
||||||
result = opentype.cmap.runeToGlyphId.getOrDefault(rune, 0)
|
result = opentype.cmap.runeToGlyphId.getOrDefault(rune, 0)
|
||||||
|
|
||||||
proc parseGlyph(opentype: OpenType, glyphId: uint16): Path {.raises: [PixieError].}
|
proc parseGlyph(opentype: OpenType, glyphId: uint16): Path {.raises: [PixieError].}
|
||||||
|
|
||||||
proc parseGlyphPath(
|
proc parseGlyphPath(
|
||||||
buf: string, offset, numberOfContours: int
|
buf: string, offset, numberOfContours: int
|
||||||
): Path {.raises: [PixieError].} =
|
): Path =
|
||||||
if numberOfContours < 0:
|
if numberOfContours < 0:
|
||||||
raise newException(PixieError, "Glyph numberOfContours must be >= 0")
|
raise newException(PixieError, "Glyph numberOfContours must be >= 0")
|
||||||
|
|
||||||
|
@ -1387,9 +1361,7 @@ proc parseGlyphPath(
|
||||||
|
|
||||||
result.closePath()
|
result.closePath()
|
||||||
|
|
||||||
proc parseCompositeGlyph(
|
proc parseCompositeGlyph(opentype: OpenType, offset: int): Path =
|
||||||
opentype: OpenType, offset: int
|
|
||||||
): Path {.raises: [PixieError].} =
|
|
||||||
result = newPath()
|
result = newPath()
|
||||||
|
|
||||||
var
|
var
|
||||||
|
@ -1486,7 +1458,7 @@ proc parseCompositeGlyph(
|
||||||
|
|
||||||
proc parseGlyph(
|
proc parseGlyph(
|
||||||
opentype: OpenType, glyphId: uint16
|
opentype: OpenType, glyphId: uint16
|
||||||
): Path {.raises: [PixieError].} =
|
): Path =
|
||||||
if glyphId.int >= opentype.glyf.offsets.len:
|
if glyphId.int >= opentype.glyf.offsets.len:
|
||||||
raise newException(PixieError, "Invalid glyph ID " & $glyphId)
|
raise newException(PixieError, "Invalid glyph ID " & $glyphId)
|
||||||
|
|
||||||
|
@ -1516,7 +1488,7 @@ proc parseGlyph(
|
||||||
|
|
||||||
proc parseGlyph(
|
proc parseGlyph(
|
||||||
opentype: OpenType, rune: Rune
|
opentype: OpenType, rune: Rune
|
||||||
): Path {.inline, raises: [PixieError].} =
|
): Path {.inline.} =
|
||||||
opentype.parseGlyph(opentype.getGlyphId(rune))
|
opentype.parseGlyph(opentype.getGlyphId(rune))
|
||||||
|
|
||||||
proc getGlyphPath*(
|
proc getGlyphPath*(
|
||||||
|
|
|
@ -23,7 +23,7 @@ proc getKerningAdjustment*(
|
||||||
template failInvalid() =
|
template failInvalid() =
|
||||||
raise newException(PixieError, "Invalid SVG font data")
|
raise newException(PixieError, "Invalid SVG font data")
|
||||||
|
|
||||||
proc parseFloat(node: XmlNode, attr: string): float32 {.raises: [PixieError].} =
|
proc parseFloat(node: XmlNode, attr: string): float32 =
|
||||||
let value = node.attr(attr)
|
let value = node.attr(attr)
|
||||||
if value.len == 0:
|
if value.len == 0:
|
||||||
raise newException(PixieError, "SVG font missing attr " & attr)
|
raise newException(PixieError, "SVG font missing attr " & attr)
|
||||||
|
|
|
@ -76,19 +76,19 @@ proc lineHeight*(typeface: Typeface): float32 {.inline, raises: [].} =
|
||||||
## The default line height in font units.
|
## The default line height in font units.
|
||||||
typeface.ascent - typeface.descent + typeface.lineGap
|
typeface.ascent - typeface.descent + typeface.lineGap
|
||||||
|
|
||||||
proc underlinePosition(typeface: Typeface): float32 {.raises: [].} =
|
proc underlinePosition(typeface: Typeface): float32 =
|
||||||
if typeface.opentype != nil:
|
if typeface.opentype != nil:
|
||||||
result = typeface.opentype.post.underlinePosition.float32
|
result = typeface.opentype.post.underlinePosition.float32
|
||||||
|
|
||||||
proc underlineThickness(typeface: Typeface): float32 {.raises: [].} =
|
proc underlineThickness(typeface: Typeface): float32 =
|
||||||
if typeface.opentype != nil:
|
if typeface.opentype != nil:
|
||||||
result = typeface.opentype.post.underlineThickness.float32
|
result = typeface.opentype.post.underlineThickness.float32
|
||||||
|
|
||||||
proc strikeoutPosition(typeface: Typeface): float32 {.raises: [].} =
|
proc strikeoutPosition(typeface: Typeface): float32 =
|
||||||
if typeface.opentype != nil:
|
if typeface.opentype != nil:
|
||||||
result = typeface.opentype.os2.yStrikeoutPosition.float32
|
result = typeface.opentype.os2.yStrikeoutPosition.float32
|
||||||
|
|
||||||
proc strikeoutThickness(typeface: Typeface): float32 {.raises: [].} =
|
proc strikeoutThickness(typeface: Typeface): float32 =
|
||||||
if typeface.opentype != nil:
|
if typeface.opentype != nil:
|
||||||
result = typeface.opentype.os2.yStrikeoutSize.float32
|
result = typeface.opentype.os2.yStrikeoutSize.float32
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ proc newSpan*(text: string, font: Font): Span {.raises: [].} =
|
||||||
result.text = text
|
result.text = text
|
||||||
result.font = font
|
result.font = font
|
||||||
|
|
||||||
proc convertTextCase(runes: var seq[Rune], textCase: TextCase) {.raises: [].} =
|
proc convertTextCase(runes: var seq[Rune], textCase: TextCase) =
|
||||||
case textCase:
|
case textCase:
|
||||||
of tcNormal:
|
of tcNormal:
|
||||||
discard
|
discard
|
||||||
|
@ -169,7 +169,7 @@ proc convertTextCase(runes: var seq[Rune], textCase: TextCase) {.raises: [].} =
|
||||||
rune = rune.toUpper()
|
rune = rune.toUpper()
|
||||||
prevRune = rune
|
prevRune = rune
|
||||||
|
|
||||||
proc canWrap(rune: Rune): bool {.inline, raises: [].} =
|
proc canWrap(rune: Rune): bool {.inline.} =
|
||||||
rune == Rune(32) or rune.isWhiteSpace()
|
rune == Rune(32) or rune.isWhiteSpace()
|
||||||
|
|
||||||
proc typeset*(
|
proc typeset*(
|
||||||
|
@ -447,7 +447,7 @@ proc textUber(
|
||||||
miterLimit = defaultMiterLimit,
|
miterLimit = defaultMiterLimit,
|
||||||
dashes: seq[float32] = @[],
|
dashes: seq[float32] = @[],
|
||||||
stroke: static[bool] = false
|
stroke: static[bool] = false
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
var line: int
|
var line: int
|
||||||
for spanIndex, (start, stop) in arrangement.spans:
|
for spanIndex, (start, stop) in arrangement.spans:
|
||||||
let
|
let
|
||||||
|
|
|
@ -884,10 +884,10 @@ proc draw*(
|
||||||
|
|
||||||
proc drawTiled*(
|
proc drawTiled*(
|
||||||
dst, src: Image, mat: Mat3, blendMode = bmNormal
|
dst, src: Image, mat: Mat3, blendMode = bmNormal
|
||||||
) {.raises: [PixieError]} =
|
) {.raises: [PixieError].} =
|
||||||
dst.drawCorrect(src, mat, true, blendMode)
|
dst.drawCorrect(src, mat, true, blendMode)
|
||||||
|
|
||||||
proc resize*(srcImage: Image, width, height: int): Image {.raises: [PixieError]} =
|
proc resize*(srcImage: Image, width, height: int): Image {.raises: [PixieError].} =
|
||||||
## Resize an image to a given height and width.
|
## Resize an image to a given height and width.
|
||||||
if width == srcImage.width and height == srcImage.height:
|
if width == srcImage.width and height == srcImage.height:
|
||||||
result = srcImage.copy()
|
result = srcImage.copy()
|
||||||
|
@ -904,7 +904,7 @@ proc resize*(srcImage: Image, width, height: int): Image {.raises: [PixieError]}
|
||||||
|
|
||||||
proc shadow*(
|
proc shadow*(
|
||||||
image: Image, offset: Vec2, spread, blur: float32, color: SomeColor
|
image: Image, offset: Vec2, spread, blur: float32, color: SomeColor
|
||||||
): Image {.raises: [PixieError]} =
|
): Image {.raises: [PixieError].} =
|
||||||
## 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
|
let
|
||||||
mask = image.newMask()
|
mask = image.newMask()
|
||||||
|
@ -916,7 +916,7 @@ proc shadow*(
|
||||||
result.fill(color)
|
result.fill(color)
|
||||||
result.draw(shifted, blendMode = bmMask)
|
result.draw(shifted, blendMode = bmMask)
|
||||||
|
|
||||||
proc superImage*(image: Image, x, y, w, h: int): Image {.raises: [PixieError]} =
|
proc superImage*(image: Image, x, y, w, h: int): Image {.raises: [PixieError].} =
|
||||||
## Either cuts a sub image or returns a super image with padded transparency.
|
## Either cuts a sub image or returns a super image with padded transparency.
|
||||||
if x >= 0 and x + w <= image.width and y >= 0 and y + h <= image.height:
|
if x >= 0 and x + w <= image.width and y >= 0 and y + h <= image.height:
|
||||||
result = image.subImage(x, y, w, h)
|
result = image.subImage(x, y, w, h)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import chroma, vmath
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
import nimsimd/sse2
|
import nimsimd/sse2
|
||||||
|
|
||||||
proc gaussianKernel*(radius: int): seq[uint32] =
|
proc gaussianKernel*(radius: int): seq[uint32] {.raises: [].} =
|
||||||
## Compute lookup table for 1d Gaussian kernel.
|
## Compute lookup table for 1d Gaussian kernel.
|
||||||
## Values are [0, 255] * 1024.
|
## Values are [0, 255] * 1024.
|
||||||
result.setLen(radius * 2 + 1)
|
result.setLen(radius * 2 + 1)
|
||||||
|
@ -23,7 +23,7 @@ proc gaussianKernel*(radius: int): seq[uint32] =
|
||||||
for i, f in floats:
|
for i, f in floats:
|
||||||
result[i] = round(f * 255 * 1024).uint32
|
result[i] = round(f * 255 * 1024).uint32
|
||||||
|
|
||||||
proc applyOpacity*(color: ColorRGBX, opacity: float32): ColorRGBX =
|
proc applyOpacity*(color: ColorRGBX, opacity: float32): ColorRGBX {.raises: [].} =
|
||||||
if opacity == 0:
|
if opacity == 0:
|
||||||
rgbx(0, 0, 0, 0)
|
rgbx(0, 0, 0, 0)
|
||||||
else:
|
else:
|
||||||
|
@ -35,7 +35,7 @@ proc applyOpacity*(color: ColorRGBX, opacity: float32): ColorRGBX =
|
||||||
a = ((color.a * x) div 255).uint8
|
a = ((color.a * x) div 255).uint8
|
||||||
rgbx(r, g, b, a)
|
rgbx(r, g, b, a)
|
||||||
|
|
||||||
proc toStraightAlpha*(data: var seq[ColorRGBA | ColorRGBX]) =
|
proc toStraightAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} =
|
||||||
## 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:
|
||||||
|
@ -46,7 +46,7 @@ proc toStraightAlpha*(data: var seq[ColorRGBA | ColorRGBX]) =
|
||||||
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 | ColorRGBX]) =
|
proc toPremultipliedAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} =
|
||||||
## 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):
|
||||||
|
@ -94,7 +94,7 @@ proc toPremultipliedAlpha*(data: var seq[ColorRGBA | ColorRGBX]) =
|
||||||
data[j] = c
|
data[j] = c
|
||||||
|
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
proc packAlphaValues*(v: M128i): M128i {.inline.} =
|
proc packAlphaValues*(v: M128i): M128i {.inline, raises: [].} =
|
||||||
## Shuffle the alpha values for these 4 colors to the first 4 bytes
|
## Shuffle the alpha values for these 4 colors to the first 4 bytes
|
||||||
result = mm_srli_epi32(v, 24)
|
result = mm_srli_epi32(v, 24)
|
||||||
let
|
let
|
||||||
|
@ -105,7 +105,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
result = mm_or_si128(mm_or_si128(result, i), mm_or_si128(j, k))
|
result = mm_or_si128(mm_or_si128(result, i), mm_or_si128(j, k))
|
||||||
result = mm_and_si128(result, first32)
|
result = mm_and_si128(result, first32)
|
||||||
|
|
||||||
proc unpackAlphaValues*(v: M128i): M128i {.inline.} =
|
proc unpackAlphaValues*(v: M128i): M128i {.inline, raises: [].} =
|
||||||
## Unpack the first 32 bits into 4 rgba(0, 0, 0, value)
|
## Unpack the first 32 bits into 4 rgba(0, 0, 0, value)
|
||||||
let mask = cast[M128i]([uint8.high.uint64, 0])
|
let mask = cast[M128i]([uint8.high.uint64, 0])
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ converter parseSomePaint*(
|
||||||
elif type(paint) is Paint:
|
elif type(paint) is Paint:
|
||||||
paint
|
paint
|
||||||
|
|
||||||
proc toLineSpace(at, to, point: Vec2): float32 {.inline, raises: [].} =
|
proc toLineSpace(at, to, point: Vec2): float32 {.inline.} =
|
||||||
## Convert position on to where it would fall on a line between at and to.
|
## Convert position on to where it would fall on a line between at and to.
|
||||||
let
|
let
|
||||||
d = to - at
|
d = to - at
|
||||||
|
@ -73,7 +73,7 @@ proc toLineSpace(at, to, point: Vec2): float32 {.inline, raises: [].} =
|
||||||
|
|
||||||
proc gradientPut(
|
proc gradientPut(
|
||||||
image: Image, paint: Paint, x, y: int, t: float32, stops: seq[ColorStop]
|
image: Image, paint: Paint, x, y: int, t: float32, stops: seq[ColorStop]
|
||||||
) {.raises: [].} =
|
) =
|
||||||
## Put an gradient color based on `t` - where are we related to a line.
|
## Put an gradient color based on `t` - where are we related to a line.
|
||||||
var index = -1
|
var index = -1
|
||||||
for i, stop in stops:
|
for i, stop in stops:
|
||||||
|
@ -100,7 +100,7 @@ proc gradientPut(
|
||||||
color.a *= paint.opacity
|
color.a *= paint.opacity
|
||||||
image.setRgbaUnsafe(x, y, color.rgbx())
|
image.setRgbaUnsafe(x, y, color.rgbx())
|
||||||
|
|
||||||
proc fillGradientLinear(image: Image, paint: Paint) {.raises: [PixieError].} =
|
proc fillGradientLinear(image: Image, paint: Paint) =
|
||||||
## Fills a linear gradient.
|
## Fills a linear gradient.
|
||||||
|
|
||||||
if paint.gradientHandlePositions.len != 2:
|
if paint.gradientHandlePositions.len != 2:
|
||||||
|
@ -122,7 +122,7 @@ proc fillGradientLinear(image: Image, paint: Paint) {.raises: [PixieError].} =
|
||||||
t = toLineSpace(at, to, xy)
|
t = toLineSpace(at, to, xy)
|
||||||
image.gradientPut(paint, x, y, t, paint.gradientStops)
|
image.gradientPut(paint, x, y, t, paint.gradientStops)
|
||||||
|
|
||||||
proc fillGradientRadial(image: Image, paint: Paint) {.raises: [PixieError].} =
|
proc fillGradientRadial(image: Image, paint: Paint) =
|
||||||
## Fills a radial gradient.
|
## Fills a radial gradient.
|
||||||
|
|
||||||
if paint.gradientHandlePositions.len != 3:
|
if paint.gradientHandlePositions.len != 3:
|
||||||
|
@ -153,7 +153,7 @@ proc fillGradientRadial(image: Image, paint: Paint) {.raises: [PixieError].} =
|
||||||
t = (mat * xy).length()
|
t = (mat * xy).length()
|
||||||
image.gradientPut(paint, x, y, t, paint.gradientStops)
|
image.gradientPut(paint, x, y, t, paint.gradientStops)
|
||||||
|
|
||||||
proc fillGradientAngular(image: Image, paint: Paint) {.raises: [PixieError].} =
|
proc fillGradientAngular(image: Image, paint: Paint) =
|
||||||
## Fills an angular gradient.
|
## Fills an angular gradient.
|
||||||
|
|
||||||
if paint.gradientHandlePositions.len != 3:
|
if paint.gradientHandlePositions.len != 3:
|
||||||
|
|
|
@ -50,19 +50,19 @@ proc newPath*(): Path {.raises: [].} =
|
||||||
## Create a new Path.
|
## Create a new Path.
|
||||||
Path()
|
Path()
|
||||||
|
|
||||||
proc pixelScale(transform: Mat3): float32 {.raises: [].} =
|
proc pixelScale(transform: Mat3): float32 =
|
||||||
## What is the largest scale factor of this transform?
|
## What is the largest scale factor of this transform?
|
||||||
max(
|
max(
|
||||||
vec2(transform[0, 0], transform[0, 1]).length,
|
vec2(transform[0, 0], transform[0, 1]).length,
|
||||||
vec2(transform[1, 0], transform[1, 1]).length
|
vec2(transform[1, 0], transform[1, 1]).length
|
||||||
)
|
)
|
||||||
|
|
||||||
proc isRelative(kind: PathCommandKind): bool {.inline, raises: [].} =
|
proc isRelative(kind: PathCommandKind): bool {.inline.} =
|
||||||
kind in {
|
kind in {
|
||||||
RMove, RLine, TQuad, RTQuad, RHLine, RVLine, RCubic, RSCubic, RQuad, RArc
|
RMove, RLine, TQuad, RTQuad, RHLine, RVLine, RCubic, RSCubic, RQuad, RArc
|
||||||
}
|
}
|
||||||
|
|
||||||
proc parameterCount(kind: PathCommandKind): int {.raises: [].} =
|
proc parameterCount(kind: PathCommandKind): int =
|
||||||
## Returns number of parameters a path command has.
|
## Returns number of parameters a path command has.
|
||||||
case kind:
|
case kind:
|
||||||
of Close: 0
|
of Close: 0
|
||||||
|
@ -635,7 +635,7 @@ proc polygon*(
|
||||||
|
|
||||||
proc commandsToShapes(
|
proc commandsToShapes(
|
||||||
path: Path, closeSubpaths = false, pixelScale: float32 = 1.0
|
path: Path, closeSubpaths = false, pixelScale: float32 = 1.0
|
||||||
): seq[seq[Vec2]] {.raises: [PixieError].} =
|
): seq[seq[Vec2]] =
|
||||||
## Converts SVG-like commands to sequences of vectors.
|
## Converts SVG-like commands to sequences of vectors.
|
||||||
var
|
var
|
||||||
start, at: Vec2
|
start, at: Vec2
|
||||||
|
@ -999,7 +999,7 @@ proc commandsToShapes(
|
||||||
|
|
||||||
proc shapesToSegments(
|
proc shapesToSegments(
|
||||||
shapes: seq[seq[Vec2]]
|
shapes: seq[seq[Vec2]]
|
||||||
): seq[(Segment, int16)] {.raises: [].} =
|
): seq[(Segment, int16)] =
|
||||||
## Converts the shapes into a set of filtered segments with winding value.
|
## Converts the shapes into a set of filtered segments with winding value.
|
||||||
for shape in shapes:
|
for shape in shapes:
|
||||||
for segment in shape.segments:
|
for segment in shape.segments:
|
||||||
|
@ -1014,7 +1014,7 @@ proc shapesToSegments(
|
||||||
|
|
||||||
result.add((segment, winding))
|
result.add((segment, winding))
|
||||||
|
|
||||||
proc requiresAntiAliasing(segments: seq[(Segment, int16)]): bool {.raises: [].} =
|
proc requiresAntiAliasing(segments: seq[(Segment, int16)]): bool =
|
||||||
## Returns true if the fill requires antialiasing.
|
## Returns true if the fill requires antialiasing.
|
||||||
|
|
||||||
template hasFractional(v: float32): bool =
|
template hasFractional(v: float32): bool =
|
||||||
|
@ -1029,13 +1029,13 @@ proc requiresAntiAliasing(segments: seq[(Segment, int16)]): bool {.raises: [].}
|
||||||
# AA is required if all segments are not vertical or have fractional > 0
|
# AA is required if all segments are not vertical or have fractional > 0
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc transform(shapes: var seq[seq[Vec2]], transform: Mat3) {.raises: [].} =
|
proc transform(shapes: var seq[seq[Vec2]], transform: Mat3) =
|
||||||
if transform != mat3():
|
if transform != mat3():
|
||||||
for shape in shapes.mitems:
|
for shape in shapes.mitems:
|
||||||
for vec in shape.mitems:
|
for vec in shape.mitems:
|
||||||
vec = transform * vec
|
vec = transform * vec
|
||||||
|
|
||||||
proc computeBounds(segments: seq[(Segment, int16)]): Rect {.raises: [].} =
|
proc computeBounds(segments: seq[(Segment, int16)]): Rect =
|
||||||
## Compute the bounds of the segments.
|
## Compute the bounds of the segments.
|
||||||
var
|
var
|
||||||
xMin = float32.high
|
xMin = float32.high
|
||||||
|
@ -1067,7 +1067,7 @@ proc computeBounds*(
|
||||||
|
|
||||||
proc partitionSegments(
|
proc partitionSegments(
|
||||||
segments: seq[(Segment, int16)], top, height: int
|
segments: seq[(Segment, int16)], top, height: int
|
||||||
): Partitioning {.raises: [].} =
|
): Partitioning =
|
||||||
## Puts segments into the height partitions they intersect with.
|
## Puts segments into the height partitions they intersect with.
|
||||||
let
|
let
|
||||||
maxPartitions = max(1, height div 10).uint32
|
maxPartitions = max(1, height div 10).uint32
|
||||||
|
@ -1091,9 +1091,7 @@ proc partitionSegments(
|
||||||
for i in atPartition .. toPartition:
|
for i in atPartition .. toPartition:
|
||||||
result.partitions[i].add((segment, winding))
|
result.partitions[i].add((segment, winding))
|
||||||
|
|
||||||
proc getIndexForY(
|
proc getIndexForY(partitioning: Partitioning, y: int): uint32 {.inline.} =
|
||||||
partitioning: Partitioning, y: int
|
|
||||||
): uint32 {.inline, raises: [].} =
|
|
||||||
if partitioning.partitionHeight == 0 or partitioning.partitions.len == 1:
|
if partitioning.partitionHeight == 0 or partitioning.partitions.len == 1:
|
||||||
0.uint32
|
0.uint32
|
||||||
else:
|
else:
|
||||||
|
@ -1104,7 +1102,7 @@ proc getIndexForY(
|
||||||
|
|
||||||
proc insertionSort(
|
proc insertionSort(
|
||||||
a: var seq[(float32, int16)], lo, hi: int
|
a: var seq[(float32, int16)], lo, hi: int
|
||||||
) {.inline, raises: [].} =
|
) {.inline.} =
|
||||||
for i in lo + 1 .. hi:
|
for i in lo + 1 .. hi:
|
||||||
var
|
var
|
||||||
j = i - 1
|
j = i - 1
|
||||||
|
@ -1114,7 +1112,7 @@ proc insertionSort(
|
||||||
dec j
|
dec j
|
||||||
dec k
|
dec k
|
||||||
|
|
||||||
proc sort(a: var seq[(float32, int16)], inl, inr: int) {.raises: [].} =
|
proc sort(a: var seq[(float32, int16)], inl, inr: int) =
|
||||||
## Quicksort + insertion sort, in-place and faster than standard lib sort.
|
## Quicksort + insertion sort, in-place and faster than standard lib sort.
|
||||||
let n = inr - inl + 1
|
let n = inr - inl + 1
|
||||||
if n < 32:
|
if n < 32:
|
||||||
|
@ -1138,7 +1136,7 @@ proc sort(a: var seq[(float32, int16)], inl, inr: int) {.raises: [].} =
|
||||||
|
|
||||||
proc shouldFill(
|
proc shouldFill(
|
||||||
windingRule: WindingRule, count: int
|
windingRule: WindingRule, count: int
|
||||||
): bool {.inline, raises: [].} =
|
): bool {.inline.} =
|
||||||
## Should we fill based on the current winding rule and count?
|
## Should we fill based on the current winding rule and count?
|
||||||
case windingRule:
|
case windingRule:
|
||||||
of wrNonZero:
|
of wrNonZero:
|
||||||
|
@ -1152,7 +1150,7 @@ iterator walk(
|
||||||
windingRule: WindingRule,
|
windingRule: WindingRule,
|
||||||
y: int,
|
y: int,
|
||||||
size: Vec2
|
size: Vec2
|
||||||
): (float32, float32, int32) {.raises: [].} =
|
): (float32, float32, int32) =
|
||||||
var
|
var
|
||||||
prevAt: float32
|
prevAt: float32
|
||||||
count: int32
|
count: int32
|
||||||
|
@ -1184,7 +1182,7 @@ proc computeCoverages(
|
||||||
aa: bool,
|
aa: bool,
|
||||||
partitioning: Partitioning,
|
partitioning: Partitioning,
|
||||||
windingRule: WindingRule
|
windingRule: WindingRule
|
||||||
) {.inline, raises: [].} =
|
) {.inline.} =
|
||||||
let
|
let
|
||||||
quality = if aa: 5 else: 1 # Must divide 255 cleanly (1, 3, 5, 15, 17, 51, 85)
|
quality = if aa: 5 else: 1 # Must divide 255 cleanly (1, 3, 5, 15, 17, 51, 85)
|
||||||
sampleCoverage = (255 div quality).uint8
|
sampleCoverage = (255 div quality).uint8
|
||||||
|
@ -1254,9 +1252,7 @@ proc computeCoverages(
|
||||||
for j in i ..< fillStart + fillLen:
|
for j in i ..< fillStart + fillLen:
|
||||||
coverages[j - startX] += sampleCoverage
|
coverages[j - startX] += sampleCoverage
|
||||||
|
|
||||||
proc clearUnsafe(
|
proc clearUnsafe(target: Image | Mask, startX, startY, toX, toY: int) =
|
||||||
target: Image | Mask, startX, startY, toX, toY: int
|
|
||||||
) {.raises: [].} =
|
|
||||||
## Clears data from [start, to).
|
## Clears data from [start, to).
|
||||||
if startX == target.width or startY == target.height:
|
if startX == target.width or startY == target.height:
|
||||||
return
|
return
|
||||||
|
@ -1274,7 +1270,7 @@ proc fillCoverage(
|
||||||
startX, y: int,
|
startX, y: int,
|
||||||
coverages: seq[uint8],
|
coverages: seq[uint8],
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
var x = startX
|
var x = startX
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
if blendMode.hasSimdBlender():
|
if blendMode.hasSimdBlender():
|
||||||
|
@ -1361,7 +1357,7 @@ proc fillCoverage(
|
||||||
startX, y: int,
|
startX, y: int,
|
||||||
coverages: seq[uint8],
|
coverages: seq[uint8],
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
var x = startX
|
var x = startX
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
if blendMode.hasSimdMasker():
|
if blendMode.hasSimdMasker():
|
||||||
|
@ -1404,7 +1400,7 @@ proc fillHits(
|
||||||
numHits: int,
|
numHits: int,
|
||||||
windingRule: WindingRule,
|
windingRule: WindingRule,
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
let blender = blendMode.blender()
|
let blender = blendMode.blender()
|
||||||
var filledTo: int
|
var filledTo: int
|
||||||
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, image.wh):
|
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, image.wh):
|
||||||
|
@ -1452,7 +1448,7 @@ proc fillHits(
|
||||||
numHits: int,
|
numHits: int,
|
||||||
windingRule: WindingRule,
|
windingRule: WindingRule,
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
let masker = blendMode.masker()
|
let masker = blendMode.masker()
|
||||||
var filledTo: int
|
var filledTo: int
|
||||||
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, mask.wh):
|
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, mask.wh):
|
||||||
|
@ -1496,7 +1492,7 @@ proc fillShapes(
|
||||||
color: SomeColor,
|
color: SomeColor,
|
||||||
windingRule: WindingRule,
|
windingRule: WindingRule,
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
# Figure out the total bounds of all the shapes,
|
# Figure out the total bounds of all the shapes,
|
||||||
# rasterize only within the total bounds
|
# rasterize only within the total bounds
|
||||||
let
|
let
|
||||||
|
@ -1554,7 +1550,7 @@ proc fillShapes(
|
||||||
shapes: seq[seq[Vec2]],
|
shapes: seq[seq[Vec2]],
|
||||||
windingRule: WindingRule,
|
windingRule: WindingRule,
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) {.raises: [PixieError].} =
|
) =
|
||||||
# Figure out the total bounds of all the shapes,
|
# Figure out the total bounds of all the shapes,
|
||||||
# rasterize only within the total bounds
|
# rasterize only within the total bounds
|
||||||
let
|
let
|
||||||
|
@ -1592,11 +1588,11 @@ proc fillShapes(
|
||||||
mask.clearUnsafe(0, 0, 0, startY)
|
mask.clearUnsafe(0, 0, 0, startY)
|
||||||
mask.clearUnsafe(0, pathHeight, 0, mask.height)
|
mask.clearUnsafe(0, pathHeight, 0, mask.height)
|
||||||
|
|
||||||
proc miterLimitToAngle*(limit: float32): float32 {.inline, raises: [].} =
|
proc miterLimitToAngle*(limit: float32): float32 {.inline.} =
|
||||||
## Converts miter-limit-ratio to miter-limit-angle.
|
## Converts miter-limit-ratio to miter-limit-angle.
|
||||||
arcsin(1 / limit) * 2
|
arcsin(1 / limit) * 2
|
||||||
|
|
||||||
proc angleToMiterLimit*(angle: float32): float32 {.inline, raises: [].} =
|
proc angleToMiterLimit*(angle: float32): float32 {.inline.} =
|
||||||
## Converts miter-limit-angle to miter-limit-ratio.
|
## Converts miter-limit-angle to miter-limit-ratio.
|
||||||
1 / sin(angle / 2)
|
1 / sin(angle / 2)
|
||||||
|
|
||||||
|
@ -1607,7 +1603,7 @@ proc strokeShapes(
|
||||||
lineJoin: LineJoin,
|
lineJoin: LineJoin,
|
||||||
miterLimit: float32,
|
miterLimit: float32,
|
||||||
dashes: seq[float32]
|
dashes: seq[float32]
|
||||||
): seq[seq[Vec2]] {.raises: [PixieError].} =
|
): seq[seq[Vec2]] =
|
||||||
if strokeWidth <= 0:
|
if strokeWidth <= 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1743,7 +1739,7 @@ proc strokeShapes(
|
||||||
|
|
||||||
proc parseSomePath(
|
proc parseSomePath(
|
||||||
path: SomePath, closeSubpaths: bool, pixelScale: float32 = 1.0
|
path: SomePath, closeSubpaths: bool, pixelScale: float32 = 1.0
|
||||||
): seq[seq[Vec2]] {.inline, raises: [PixieError].} =
|
): seq[seq[Vec2]] {.inline.} =
|
||||||
## Given SomePath, parse it in different ways.
|
## Given SomePath, parse it in different ways.
|
||||||
when type(path) is string:
|
when type(path) is string:
|
||||||
parsePath(path).commandsToShapes(closeSubpaths, pixelScale)
|
parsePath(path).commandsToShapes(closeSubpaths, pixelScale)
|
||||||
|
@ -1908,7 +1904,7 @@ proc overlaps(
|
||||||
shapes: seq[seq[Vec2]],
|
shapes: seq[seq[Vec2]],
|
||||||
test: Vec2,
|
test: Vec2,
|
||||||
windingRule: WindingRule
|
windingRule: WindingRule
|
||||||
): bool {.raises: [].} =
|
): bool =
|
||||||
var hits: seq[(float32, int16)]
|
var hits: seq[(float32, int16)]
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
Loading…
Reference in a new issue