commit
4e5b2cd91e
6 changed files with 98 additions and 21 deletions
|
@ -33,7 +33,7 @@ converter autoPremultipliedAlpha*(c: ColorRGBA): ColorRGBX {.inline.} =
|
|||
c.rgbx()
|
||||
|
||||
proc decodeImage*(data: string | seq[uint8]): Image =
|
||||
## Loads an image from a memory.
|
||||
## Loads an image from memory.
|
||||
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
|
||||
decodePng(data)
|
||||
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpgStartOfImage):
|
||||
|
@ -48,10 +48,21 @@ proc decodeImage*(data: string | seq[uint8]): Image =
|
|||
else:
|
||||
raise newException(PixieError, "Unsupported image file format")
|
||||
|
||||
proc decodeMask*(data: string | seq[uint8]): Mask =
|
||||
## Loads a mask from memory.
|
||||
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
|
||||
newMask(decodePng(data))
|
||||
else:
|
||||
raise newException(PixieError, "Unsupported mask file format")
|
||||
|
||||
proc readImage*(filePath: string): Image =
|
||||
## Loads an image from a file.
|
||||
decodeImage(readFile(filePath))
|
||||
|
||||
proc readMask*(filePath: string): Mask =
|
||||
## Loads a mask from a file.
|
||||
decodeMask(readFile(filePath))
|
||||
|
||||
proc encodeImage*(image: Image, fileFormat: FileFormat): string =
|
||||
## Encodes an image into memory.
|
||||
case fileFormat:
|
||||
|
@ -62,7 +73,15 @@ proc encodeImage*(image: Image, fileFormat: FileFormat): string =
|
|||
of ffBmp:
|
||||
image.encodeBmp()
|
||||
of ffGif:
|
||||
raise newException(PixieError, "Unsupported image format")
|
||||
raise newException(PixieError, "Unsupported file format")
|
||||
|
||||
proc encodeMask*(mask: Mask, fileFormat: FileFormat): string =
|
||||
## Encodes a mask into memory.
|
||||
case fileFormat:
|
||||
of ffPng:
|
||||
mask.encodePng()
|
||||
else:
|
||||
raise newException(PixieError, "Unsupported file format")
|
||||
|
||||
proc writeFile*(image: Image, filePath: string, fileFormat: FileFormat) =
|
||||
## Writes an image to a file.
|
||||
|
@ -75,9 +94,23 @@ proc writeFile*(image: Image, filePath: string) =
|
|||
of ".bmp": ffBmp
|
||||
of ".jpg", ".jpeg": ffJpg
|
||||
else:
|
||||
raise newException(PixieError, "Unsupported image file extension")
|
||||
raise newException(PixieError, "Unsupported file extension")
|
||||
image.writeFile(filePath, fileformat)
|
||||
|
||||
proc writeFile*(mask: Mask, filePath: string, fileFormat: FileFormat) =
|
||||
## Writes an mask to a file.
|
||||
writeFile(filePath, mask.encodeMask(fileFormat))
|
||||
|
||||
proc writeFile*(mask: Mask, filePath: string) =
|
||||
## Writes a mask to a file.
|
||||
let fileFormat = case splitFile(filePath).ext.toLowerAscii():
|
||||
of ".png": ffPng
|
||||
of ".bmp": ffBmp
|
||||
of ".jpg", ".jpeg": ffJpg
|
||||
else:
|
||||
raise newException(PixieError, "Unsupported file extension")
|
||||
mask.writeFile(filePath, fileformat)
|
||||
|
||||
proc fillRect*(
|
||||
mask: Mask,
|
||||
rect: Rect,
|
||||
|
|
|
@ -24,6 +24,22 @@ proc newImage*(width, height: int): Image =
|
|||
result.height = height
|
||||
result.data = newSeq[ColorRGBX](width * height)
|
||||
|
||||
proc newImage*(mask: Mask): Image =
|
||||
result = newImage(mask.width, mask.height)
|
||||
var i: int
|
||||
when defined(amd64) and not defined(pixieNoSimd):
|
||||
for _ in countup(0, mask.data.len - 16, 4):
|
||||
let values = mm_loadu_si128(mask.data[i].addr)
|
||||
var alphas = unpackAlphaValues(values)
|
||||
alphas = mm_or_si128(alphas, mm_srli_epi32(alphas, 8))
|
||||
alphas = mm_or_si128(alphas, mm_srli_epi32(alphas, 16))
|
||||
mm_storeu_si128(result.data[i].addr, alphas)
|
||||
i += 4
|
||||
|
||||
for i in i ..< mask.data.len:
|
||||
let v = mask.data[i]
|
||||
result.data[i] = rgbx(v, v, v, v)
|
||||
|
||||
proc wh*(image: Image): Vec2 {.inline.} =
|
||||
## Return with and height as a size vector.
|
||||
vec2(image.width.float32, image.height.float32)
|
||||
|
|
|
@ -95,25 +95,18 @@ when defined(amd64) and not defined(pixieNoSimd):
|
|||
|
||||
proc unpackAlphaValues*(v: M128i): M128i {.inline.} =
|
||||
## Unpack the first 32 bits into 4 rgba(0, 0, 0, value)
|
||||
let
|
||||
first32 = cast[M128i]([uint32.high, 0, 0, 0]) # First 32 bits
|
||||
alphaMask = mm_set1_epi32(cast[int32](0xff000000)) # Only `a`
|
||||
|
||||
result = mm_shuffle_epi32(v, MM_SHUFFLE(0, 0, 0, 0))
|
||||
let mask = cast[M128i]([uint8.high.uint64, 0])
|
||||
|
||||
var
|
||||
i = mm_and_si128(result, first32)
|
||||
j = mm_and_si128(result, mm_slli_si128(first32, 4))
|
||||
k = mm_and_si128(result, mm_slli_si128(first32, 8))
|
||||
l = mm_and_si128(result, mm_slli_si128(first32, 12))
|
||||
i = mm_and_si128(v, mask)
|
||||
j = mm_and_si128(v, mm_slli_si128(mask, 1))
|
||||
k = mm_and_si128(v, mm_slli_si128(mask, 2))
|
||||
l = mm_and_si128(v, mm_slli_si128(mask, 3))
|
||||
|
||||
# Shift the values to `a`
|
||||
# Shift the values to uint32 `a`
|
||||
i = mm_slli_si128(i, 3)
|
||||
j = mm_slli_si128(j, 2)
|
||||
k = mm_slli_si128(k, 1)
|
||||
# l = mm_slli_si128(l, 0)
|
||||
j = mm_slli_si128(j, 6)
|
||||
k = mm_slli_si128(k, 9)
|
||||
l = mm_slli_si128(l, 12)
|
||||
|
||||
result = mm_and_si128(
|
||||
mm_or_si128(mm_or_si128(i, j), mm_or_si128(k, l)),
|
||||
alphaMask
|
||||
)
|
||||
result = mm_or_si128(mm_or_si128(i, j), mm_or_si128(k, l))
|
||||
|
|
|
@ -73,12 +73,21 @@ block:
|
|||
|
||||
reset()
|
||||
|
||||
timeIt "newMask":
|
||||
timeIt "newMask(image)":
|
||||
let mask = image.newMask()
|
||||
doAssert mask[0, 0] == image[0, 0].a
|
||||
|
||||
reset()
|
||||
|
||||
block:
|
||||
let mask = image.newMask()
|
||||
|
||||
timeIt "newImage(mask)":
|
||||
let image = newImage(mask)
|
||||
doAssert mask[0, 0] == image[0, 0].a
|
||||
|
||||
reset()
|
||||
|
||||
timeIt "blur":
|
||||
image.blur(40)
|
||||
|
||||
|
|
BIN
tests/images/mask2image.png
Normal file
BIN
tests/images/mask2image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 882 B |
|
@ -138,3 +138,29 @@ block:
|
|||
ctx.fillRect(rect(25, 25, 50, 50))
|
||||
ctx.image.blur(20, rgba(0, 0, 0, 255))
|
||||
ctx.image.writeFile("tests/images/imageblur20oob.png")
|
||||
|
||||
block: # Test conversion between image and mask
|
||||
let
|
||||
originalImage = newImage(100, 100)
|
||||
originalMask = newMask(100, 100)
|
||||
|
||||
var p: Path
|
||||
p.rect(10, 10, 80, 80)
|
||||
|
||||
originalImage.fillPath(p, rgba(255, 0, 0, 255))
|
||||
originalMask.fillPath(p)
|
||||
|
||||
# Converting an image to a mask == a mask of the same fill
|
||||
doAssert newMask(originalImage).data == originalMask.data
|
||||
|
||||
# Converting a mask to an image == converting an image to a mask as an image
|
||||
doAssert newImage(newMask(originalImage)).data == newImage(originalMask).data
|
||||
|
||||
block:
|
||||
var p: Path
|
||||
p.roundedRect(10, 10, 80, 80, 10, 10, 10, 10)
|
||||
|
||||
let image = newImage(100, 100)
|
||||
image.fillPath(p, rgba(255, 0, 0, 255))
|
||||
|
||||
newImage(newMask(image)).writeFile("tests/images/mask2image.png")
|
||||
|
|
Loading…
Reference in a new issue