From 2232e09a273aa49f747011c8512ee13876efc881 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Thu, 24 Jun 2021 19:09:54 -0500 Subject: [PATCH] masks and images conversion --- src/pixie.nim | 39 +++++++++++++++++++++++++++++++++--- src/pixie/images.nim | 16 +++++++++++++++ tests/benchmark_images.nim | 11 +++++++++- tests/images/mask2image.png | Bin 0 -> 882 bytes tests/test_images.nim | 26 ++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/images/mask2image.png diff --git a/src/pixie.nim b/src/pixie.nim index b0d21e0..688a4f2 100644 --- a/src/pixie.nim +++ b/src/pixie.nim @@ -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, diff --git a/src/pixie/images.nim b/src/pixie/images.nim index e0f9bf0..bf174b7 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -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) diff --git a/tests/benchmark_images.nim b/tests/benchmark_images.nim index fc53bf6..6f761d7 100644 --- a/tests/benchmark_images.nim +++ b/tests/benchmark_images.nim @@ -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) diff --git a/tests/images/mask2image.png b/tests/images/mask2image.png new file mode 100644 index 0000000000000000000000000000000000000000..91ccc81ffaae664625a52dfa13139a9318138bd3 GIT binary patch literal 882 zcmeAS@N?(olHy`uVBq!ia0vp^DIm@<=viY8cTd#b?b|=^z-+eDYJ$Gw|>IrSaWSFv^UI=n5F2OJhy1886cr`dMwmS4F)(Qn(idZ)B~(ck1Bmw$J@ z{JmUsTTGn$k)_j4rbV%ZzS}2yU|ChV$X#H5FpVm{w z$5(7hj~4E}Z?IXH;acC!YOT~~mmem+D!UuLcUN;wFn|2gDXVAxymRIiXZF1_Vax}% z8(vm0etm2H%$v2wpR1VOTc-cXdZZ9)d+~aIv0$OG(z+vGR%NgMz2IH9R7O(0sLQnZ fn5kE!<&1cE@~(}{Wrtq?^AdxntDnm{r-UW|6Iz1$ literal 0 HcmV?d00001 diff --git a/tests/test_images.nim b/tests/test_images.nim index 5f237f0..0141093 100644 --- a/tests/test_images.nim +++ b/tests/test_images.nim @@ -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")