Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ mask.fillPath(
Q 180 120 100 180
Q 20 120 20 60
z
"""
""",
color(1, 1, 1, 1)
)
lines.draw(mask)
lines.draw(mask, blendMode = MaskBlend)
image.draw(lines)
```
![example output](examples/masking.png)
Expand Down Expand Up @@ -303,8 +304,8 @@ nim c -r [examples/blur.nim](examples/blur.nim)
let path = newPath()
path.polygon(vec2(100, 100), 70, sides = 6)

let mask = newMask(200, 200)
mask.fillPath(path)
let mask = newImage(200, 200)
mask.fillPath(path, color(1, 1, 1, 1))

blur.blur(20)
blur.draw(mask, blendMode = MaskBlend)
Expand Down
33 changes: 0 additions & 33 deletions bindings/bindings.nim
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,10 @@ exportRefObject Image:
applyOpacity(Image, float32)
invert(Image)
blur(Image, float32, Color)
newMask(Image)
resize(Image, int, int)
shadow(Image, Vec2, float32, float32, Color)
superImage
draw(Image, Image, Mat3, BlendMode)
draw(Image, Mask, Mat3, BlendMode)
fillGradient
fillText(Image, Font, string, Mat3, Vec2, HorizontalAlignment, VerticalAlignment)
fillText(Image, Arrangement, Mat3)
Expand All @@ -140,36 +138,6 @@ exportRefObject Image:
strokePath(Image, Path, Paint, Mat3, float32, LineCap, LineJoin, float32, seq[float32])
newContext(Image)

exportRefObject Mask:
fields:
width
height
constructor:
newMask(int, int)
procs:
writeFile(Mask, string)
copy(Mask)
getValue
setValue
fill(Mask, uint8)
minifyBy2(Mask, int)
magnifyBy2(Mask, int)
spread
ceil(Mask)
newImage(Mask)
applyOpacity(Mask, float32)
invert(Mask)
blur(Mask, float32, uint8)
resize(Mask, int, int)
draw(Mask, Mask, Mat3, BlendMode)
draw(Mask, Image, Mat3, BlendMode)
fillText(Mask, Font, string, Mat3, Vec2, HorizontalAlignment, VerticalAlignment)
fillText(Mask, Arrangement, Mat3)
strokeText(Mask, Font, string, Mat3, float32, Vec2, HorizontalAlignment, VerticalAlignment, LineCap, LineJoin, float32, seq[float32])
strokeText(Mask, Arrangement, Mat3, float32, LineCap, LineJoin, float32, seq[float32])
fillPath(Mask, Path, Mat3, WindingRule)
strokePath(Mask, Path, Mat3, float32, LineCap, LineJoin, float32, seq[float32])

exportRefObject Paint:
fields:
kind
Expand Down Expand Up @@ -320,7 +288,6 @@ exportProcs:
decodeImageDimensions
readImage
readImageDimensions
readmask
readTypeface
readFont
parsePath
Expand Down
4 changes: 2 additions & 2 deletions examples/blur.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ image.fill(rgba(255, 255, 255, 255))
let path = newPath()
path.polygon(vec2(100, 100), 70, sides = 6)

let mask = newMask(200, 200)
mask.fillPath(path)
let mask = newImage(200, 200)
mask.fillPath(path, color(1, 1, 1, 1))

blur.blur(20)
blur.draw(mask, blendMode = MaskBlend)
Expand Down
Binary file modified examples/blur.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/gradient.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/heart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/image_tiled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/line.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions examples/masking.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import pixie
let
image = newImage(200, 200)
lines = newImage(200, 200)
mask = newMask(200, 200)
mask = newImage(200, 200)

lines.fill(parseHtmlColor("#FC427B").rgba)
image.fill(rgba(255, 255, 255, 255))
Expand All @@ -23,9 +23,10 @@ mask.fillPath(
Q 180 120 100 180
Q 20 120 20 60
z
"""
""",
color(1, 1, 1, 1)
)
lines.draw(mask)
lines.draw(mask, blendMode = MaskBlend)
image.draw(lines)

image.writeFile("examples/masking.png")
Binary file modified examples/masking.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/rounded_rectangle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/square.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/text_spans.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/tiger.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 3 additions & 41 deletions src/pixie.nim
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import bumpy, chroma, flatty/binny, os, pixie/common, pixie/contexts,
pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpeg,
pixie/fileformats/png, pixie/fileformats/ppm, pixie/fileformats/qoi,
pixie/fileformats/svg, pixie/fonts, pixie/images, pixie/internal, pixie/masks, pixie/paints,
pixie/paths, strutils, vmath
pixie/fileformats/svg, pixie/fonts, pixie/images, pixie/internal,
pixie/paints, pixie/paths, strutils, vmath

export bumpy, chroma, common, contexts, fonts, images, masks, paints, paths, vmath
export bumpy, chroma, common, contexts, fonts, images, paints, paths, vmath

type
FileFormat* = enum
Expand Down Expand Up @@ -57,13 +57,6 @@ proc decodeImage*(data: string): Image {.raises: [PixieError].} =
else:
raise newException(PixieError, "Unsupported image file format")

proc decodeMask*(data: string): Mask {.raises: [PixieError].} =
## Loads a mask from memory.
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
newMask(decodePng(data).convertToImage())
else:
raise newException(PixieError, "Unsupported mask file format")

proc readImageDimensions*(
filePath: string
): ImageDimensions {.inline, raises: [PixieError].} =
Expand All @@ -80,13 +73,6 @@ proc readImage*(filePath: string): Image {.inline, raises: [PixieError].} =
except IOError as e:
raise newException(PixieError, e.msg, e)

proc readMask*(filePath: string): Mask {.raises: [PixieError].} =
## Loads a mask from a file.
try:
decodeMask(readFile(filePath))
except IOError as e:
raise newException(PixieError, e.msg, e)

proc encodeImage*(image: Image, fileFormat: FileFormat): string {.raises: [PixieError].} =
## Encodes an image into memory.
case fileFormat:
Expand All @@ -103,14 +89,6 @@ proc encodeImage*(image: Image, fileFormat: FileFormat): string {.raises: [Pixie
of PpmFormat:
image.encodePpm()

proc encodeMask*(mask: Mask, fileFormat: FileFormat): string {.raises: [PixieError].} =
## Encodes a mask into memory.
case fileFormat:
of PngFormat:
mask.encodePng()
else:
raise newException(PixieError, "Unsupported file format")

proc writeFile*(image: Image, filePath: string) {.raises: [PixieError].} =
## Writes an image to a file.
let fileFormat = case splitFile(filePath).ext.toLowerAscii():
Expand All @@ -127,22 +105,6 @@ proc writeFile*(image: Image, filePath: string) {.raises: [PixieError].} =
except IOError as e:
raise newException(PixieError, e.msg, e)

proc writeFile*(mask: Mask, filePath: string) {.raises: [PixieError].} =
## Writes a mask to a file.
let fileFormat = case splitFile(filePath).ext.toLowerAscii():
of ".png": PngFormat
of ".bmp": BmpFormat
of ".jpg", ".jpeg": JpegFormat
of ".qoi": QoiFormat
of ".ppm": PpmFormat
else:
raise newException(PixieError, "Unsupported file extension")

try:
writeFile(filePath, mask.encodeMask(fileFormat))
except IOError as e:
raise newException(PixieError, e.msg, e)

proc fill*(image: Image, paint: Paint) {.raises: [PixieError].} =
## Fills the image with the paint.
case paint.kind:
Expand Down
153 changes: 0 additions & 153 deletions src/pixie/blends.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import chroma, common, simd, std/math
type
Blender* = proc(backdrop, source: ColorRGBX): ColorRGBX {.gcsafe, raises: [].}
## Function signature returned by blender.
MaskBlender* = proc(backdrop, source: uint8): uint8 {.gcsafe, raises: [].}
## Function signature returned by maskBlender.

when defined(release):
{.push checks: off.}
Expand Down Expand Up @@ -414,54 +412,10 @@ proc blender*(blendMode: BlendMode): Blender {.raises: [].} =
of SubtractMaskBlend: subtractMaskBlender
of ExcludeMaskBlend: excludeMaskBlender

proc maskBlendNormal*(backdrop, source: uint8): uint8 {.inline.} =
## Normal blend masks
blendAlpha(backdrop, source)

proc maskBlendMask*(backdrop, source: uint8): uint8 {.inline.} =
## Mask blend masks
((backdrop.uint32 * source) div 255).uint8

proc maskBlendSubtract*(backdrop, source: uint8): uint8 {.inline.} =
## Subtract blend masks
((backdrop.uint32 * (255 - source)) div 255).uint8

proc maskBlendExclude*(backdrop, source: uint8): uint8 {.inline.} =
## Exclude blend masks
max(backdrop, source) - min(backdrop, source)

proc maskBlendNormalMaskBlender(backdrop, source: uint8): uint8 =
maskBlendNormal(backdrop, source)

proc maskBlendMaskMaskBlender(backdrop, source: uint8): uint8 =
maskBlendMask(backdrop, source)

proc maskBlendSubtractMaskBlender(backdrop, source: uint8): uint8 =
maskBlendSubtract(backdrop, source)

proc maskBlendExcludeMaskBlender(backdrop, source: uint8): uint8 =
maskBlendExclude(backdrop, source)

proc maskBlendOverwriteMaskBlender(backdrop, source: uint8): uint8 =
source

proc maskBlender*(blendMode: BlendMode): MaskBlender {.raises: [PixieError].} =
## Returns a blend masking function for a given blend masking mode.
case blendMode:
of NormalBlend: maskBlendNormalMaskBlender
of MaskBlend: maskBlendMaskMaskBlender
of OverwriteBlend: maskBlendOverwriteMaskBlender
of SubtractMaskBlend: maskBlendSubtractMaskBlender
of ExcludeMaskBlend: maskBlendExcludeMaskBlender
else:
raise newException(PixieError, "No masker for " & $blendMode)

when defined(amd64) and allowSimd:
type
BlenderSimd* = proc(blackdrop, source: M128i): M128i {.gcsafe, raises: [].}
## Function signature returned by blenderSimd.
MaskerSimd* = proc(blackdrop, source: M128i): M128i {.gcsafe, raises: [].}
## Function signature returned by maskerSimd.

proc blendNormalSimd*(backdrop, source: M128i): M128i {.inline.} =
let
Expand Down Expand Up @@ -535,112 +489,5 @@ when defined(amd64) and allowSimd:
## Is there a blend function for a given blend mode with SIMD support?
blendMode in {NormalBlend, MaskBlend, OverwriteBlend}

proc maskBlendNormalSimd*(backdrop, source: M128i): M128i {.inline.} =
## Blending masks
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
v255high = mm_set1_epi16(cast[int16](255.uint16 shl 8))
div255 = mm_set1_epi16(cast[int16](0x8081))

var
sourceEven = mm_slli_epi16(source, 8)
sourceOdd = mm_and_si128(source, oddMask)

let
evenK = mm_sub_epi16(v255high, sourceEven)
oddK = mm_sub_epi16(v255high, sourceOdd)

var
backdropEven = mm_slli_epi16(backdrop, 8)
backdropOdd = mm_and_si128(backdrop, oddMask)
backdropEven = mm_mulhi_epu16(backdropEven, evenK)
backdropOdd = mm_mulhi_epu16(backdropOdd, oddK)
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)

sourceEven = mm_srli_epi16(sourceEven, 8)
sourceOdd = mm_srli_epi16(sourceOdd, 8)

let
blendedEven = mm_add_epi16(sourceEven, backdropEven)
blendedOdd = mm_add_epi16(sourceOdd, backdropOdd)

mm_or_si128(blendedEven, mm_slli_epi16(blendedOdd, 8))

proc maskBlendMaskSimd*(backdrop, source: M128i): M128i =
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
div255 = mm_set1_epi16(cast[int16](0x8081))
sourceEven = mm_slli_epi16(source, 8)
sourceOdd = mm_and_si128(source, oddMask)

var
backdropEven = mm_slli_epi16(backdrop, 8)
backdropOdd = mm_and_si128(backdrop, oddMask)
backdropEven = mm_mulhi_epu16(backdropEven, sourceEven)
backdropOdd = mm_mulhi_epu16(backdropOdd, sourceOdd)
backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)

mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))

proc maskBlendSubtractSimd*(backdrop, source: M128i): M128i {.inline.} =
let
oddMask = mm_set1_epi16(cast[int16](0xff00))
vec255 = mm_set1_epi8(255)
div255 = mm_set1_epi16(cast[int16](0x8081))

let sourceMinus255 = mm_sub_epi8(vec255, source)

var
multiplierEven = mm_slli_epi16(sourceMinus255, 8)
multiplierOdd = mm_and_si128(sourceMinus255, oddMask)
backdropEven = mm_slli_epi16(backdrop, 8)
backdropOdd = mm_and_si128(backdrop, oddMask)

backdropEven = mm_mulhi_epu16(backdropEven, multiplierEven)
backdropOdd = mm_mulhi_epu16(backdropOdd, multiplierOdd)

backdropEven = mm_srli_epi16(mm_mulhi_epu16(backdropEven, div255), 7)
backdropOdd = mm_srli_epi16(mm_mulhi_epu16(backdropOdd, div255), 7)

mm_or_si128(backdropEven, mm_slli_epi16(backdropOdd, 8))

proc maskBlendExcludeSimd*(backdrop, source: M128i): M128i {.inline.} =
mm_sub_epi8(mm_max_epu8(backdrop, source), mm_min_epu8(backdrop, source))

proc maskBlendNormalSimdMaskBlender(backdrop, source: M128i): M128i =
maskBlendNormalSimd(backdrop, source)

proc maskBlendMaskSimdMaskBlender(backdrop, source: M128i): M128i =
maskBlendMaskSimd(backdrop, source)

proc maskBlendExcludeSimdMaskBlender(backdrop, source: M128i): M128i =
maskBlendExcludeSimd(backdrop, source)

proc maskBlendSubtractSimdMaskBlender(backdrop, source: M128i): M128i =
maskBlendSubtractSimd(backdrop, source)

proc maskBlenderSimd*(blendMode: BlendMode): MaskerSimd {.raises: [PixieError].} =
## Returns a blend masking function with SIMD support.
case blendMode:
of NormalBlend: maskBlendNormalSimdMaskBlender
of MaskBlend: maskBlendMaskSimdMaskBlender
of OverwriteBlend: overwriteSimdBlender
of SubtractMaskBlend: maskBlendSubtractSimdMaskBlender
of ExcludeMaskBlend: maskBlendExcludeSimdMaskBlender
else:
raise newException(PixieError, "No SIMD masker for " & $blendMode)

proc hasSimdMaskBlender*(blendMode: BlendMode): bool {.inline, raises: [].} =
## Is there a blend masking function with SIMD support?
blendMode in {
NormalBlend,
MaskBlend,
OverwriteBlend,
SubtractMaskBlend,
ExcludeMaskBlend
}

when defined(release):
{.pop.}
Loading