masks and images conversion
This commit is contained in:
parent
ac1bd9e64c
commit
2232e09a27
5 changed files with 88 additions and 4 deletions
|
@ -33,7 +33,7 @@ converter autoPremultipliedAlpha*(c: ColorRGBA): ColorRGBX {.inline.} =
|
||||||
c.rgbx()
|
c.rgbx()
|
||||||
|
|
||||||
proc decodeImage*(data: string | seq[uint8]): Image =
|
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):
|
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
|
||||||
decodePng(data)
|
decodePng(data)
|
||||||
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpgStartOfImage):
|
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpgStartOfImage):
|
||||||
|
@ -48,10 +48,21 @@ proc decodeImage*(data: string | seq[uint8]): Image =
|
||||||
else:
|
else:
|
||||||
raise newException(PixieError, "Unsupported image file format")
|
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 =
|
proc readImage*(filePath: string): Image =
|
||||||
## Loads an image from a file.
|
## Loads an image from a file.
|
||||||
decodeImage(readFile(filePath))
|
decodeImage(readFile(filePath))
|
||||||
|
|
||||||
|
proc readMask*(filePath: string): Mask =
|
||||||
|
## Loads a mask from a file.
|
||||||
|
decodeMask(readFile(filePath))
|
||||||
|
|
||||||
proc encodeImage*(image: Image, fileFormat: FileFormat): string =
|
proc encodeImage*(image: Image, fileFormat: FileFormat): string =
|
||||||
## Encodes an image into memory.
|
## Encodes an image into memory.
|
||||||
case fileFormat:
|
case fileFormat:
|
||||||
|
@ -62,7 +73,15 @@ proc encodeImage*(image: Image, fileFormat: FileFormat): string =
|
||||||
of ffBmp:
|
of ffBmp:
|
||||||
image.encodeBmp()
|
image.encodeBmp()
|
||||||
of ffGif:
|
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) =
|
proc writeFile*(image: Image, filePath: string, fileFormat: FileFormat) =
|
||||||
## Writes an image to a file.
|
## Writes an image to a file.
|
||||||
|
@ -75,9 +94,23 @@ proc writeFile*(image: Image, filePath: string) =
|
||||||
of ".bmp": ffBmp
|
of ".bmp": ffBmp
|
||||||
of ".jpg", ".jpeg": ffJpg
|
of ".jpg", ".jpeg": ffJpg
|
||||||
else:
|
else:
|
||||||
raise newException(PixieError, "Unsupported image file extension")
|
raise newException(PixieError, "Unsupported file extension")
|
||||||
image.writeFile(filePath, fileformat)
|
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*(
|
proc fillRect*(
|
||||||
mask: Mask,
|
mask: Mask,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
|
|
|
@ -24,6 +24,22 @@ proc newImage*(width, height: int): Image =
|
||||||
result.height = height
|
result.height = height
|
||||||
result.data = newSeq[ColorRGBX](width * 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.} =
|
proc wh*(image: Image): Vec2 {.inline.} =
|
||||||
## Return with and height as a size vector.
|
## Return with and height as a size vector.
|
||||||
vec2(image.width.float32, image.height.float32)
|
vec2(image.width.float32, image.height.float32)
|
||||||
|
|
|
@ -73,12 +73,21 @@ block:
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
timeIt "newMask":
|
timeIt "newMask(image)":
|
||||||
let mask = image.newMask()
|
let mask = image.newMask()
|
||||||
doAssert mask[0, 0] == image[0, 0].a
|
doAssert mask[0, 0] == image[0, 0].a
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
|
block:
|
||||||
|
let mask = image.newMask()
|
||||||
|
|
||||||
|
timeIt "newImage(mask)":
|
||||||
|
let image = newImage(mask)
|
||||||
|
doAssert mask[0, 0] == image[0, 0].a
|
||||||
|
|
||||||
|
reset()
|
||||||
|
|
||||||
timeIt "blur":
|
timeIt "blur":
|
||||||
image.blur(40)
|
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.fillRect(rect(25, 25, 50, 50))
|
||||||
ctx.image.blur(20, rgba(0, 0, 0, 255))
|
ctx.image.blur(20, rgba(0, 0, 0, 255))
|
||||||
ctx.image.writeFile("tests/images/imageblur20oob.png")
|
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