Merge branch 'pr2'

This commit is contained in:
Ryan Oldenburg 2022-06-14 15:51:45 -05:00
commit 2fc42d2973
7 changed files with 56 additions and 24 deletions

View file

@ -8,7 +8,7 @@ srcDir = "src"
requires "nim >= 1.4.8" requires "nim >= 1.4.8"
requires "vmath >= 1.1.4" requires "vmath >= 1.1.4"
requires "chroma >= 0.2.5" requires "chroma >= 0.2.5"
requires "zippy >= 0.9.11" requires "zippy >= 0.10.0"
requires "flatty >= 0.3.4" requires "flatty >= 0.3.4"
requires "nimsimd >= 1.0.0" requires "nimsimd >= 1.0.0"
requires "bumpy >= 1.1.1" requires "bumpy >= 1.1.1"

View file

@ -40,7 +40,7 @@ proc decodeImageDimensions*(
proc decodeImage*(data: string): Image {.raises: [PixieError].} = proc decodeImage*(data: string): Image {.raises: [PixieError].} =
## Loads an image from 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):
newImage(decodePng(data)) decodePng(data).convertToImage()
elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage): elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage):
decodeJpeg(data) decodeJpeg(data)
elif data.len > 2 and data.readStr(0, 2) == bmpSignature: elif data.len > 2 and data.readStr(0, 2) == bmpSignature:
@ -51,7 +51,7 @@ proc decodeImage*(data: string): Image {.raises: [PixieError].} =
elif data.len > 6 and data.readStr(0, 6) in gifSignatures: elif data.len > 6 and data.readStr(0, 6) in gifSignatures:
decodeGif(data) decodeGif(data)
elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature: elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature:
newImage(decodeQoi(data)) decodeQoi(data).convertToImage()
elif data.len > 9 and data.readStr(0, 2) in ppmSignatures: elif data.len > 9 and data.readStr(0, 2) in ppmSignatures:
decodePpm(data) decodePpm(data)
else: else:
@ -60,7 +60,7 @@ proc decodeImage*(data: string): Image {.raises: [PixieError].} =
proc decodeMask*(data: string): Mask {.raises: [PixieError].} = proc decodeMask*(data: string): Mask {.raises: [PixieError].} =
## Loads a mask from memory. ## Loads a mask from memory.
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature): if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
newMask(newImage(decodePng(data))) newMask(decodePng(data).convertToImage())
else: else:
raise newException(PixieError, "Unsupported mask file format") raise newException(PixieError, "Unsupported mask file format")

View file

@ -21,8 +21,8 @@ type
template failInvalid() = template failInvalid() =
raise newException(PixieError, "Invalid PNG buffer, unable to load") raise newException(PixieError, "Invalid PNG buffer, unable to load")
# template failCRC() = template failCRC() =
# raise newException(PixieError, "CRC check failed") raise newException(PixieError, "CRC check failed")
when defined(release): when defined(release):
{.push checks: off.} {.push checks: off.}
@ -338,10 +338,24 @@ proc decodeImageData(
discard # Not possible, parseHeader validates discard # Not possible, parseHeader validates
proc newImage*(png: Png): Image {.raises: [PixieError].} = proc newImage*(png: Png): Image {.raises: [PixieError].} =
## Creates a new Image from the PNG.
result = newImage(png.width, png.height) result = newImage(png.width, png.height)
copyMem(result.data[0].addr, png.data[0].addr, png.data.len * 4) copyMem(result.data[0].addr, png.data[0].addr, png.data.len * 4)
result.data.toPremultipliedAlpha() result.data.toPremultipliedAlpha()
proc convertToImage*(png: Png): Image {.raises: [].} =
## Converts a PNG into an Image by moving the data. This is faster but can
## only be done once.
type Movable = ref object
width, height, channels: int
data: seq[ColorRGBX]
result = Image()
result.width = png.width
result.height = png.height
result.data = move cast[Movable](png).data
result.data.toPremultipliedAlpha()
proc decodePngDimensions*( proc decodePngDimensions*(
data: pointer, len: int data: pointer, len: int
): ImageDimensions {.raises: [PixieError].} = ): ImageDimensions {.raises: [PixieError].} =
@ -400,8 +414,9 @@ proc decodePng*(data: pointer, len: int): Png {.raises: [PixieError].} =
prevChunkType = "IHDR" prevChunkType = "IHDR"
inc(pos, 13) inc(pos, 13)
# if crc32(data[pos - 17 ..< pos]) != read32be(data, pos): let headerCrc = crc32(data[pos - 17].addr, 17)
# failCRC() if headerCrc != data.readUint32(pos).swap():
failCRC()
inc(pos, 4) # CRC inc(pos, 4) # CRC
while true: while true:
@ -462,8 +477,9 @@ proc decodePng*(data: pointer, len: int): Png {.raises: [PixieError].} =
inc(pos, chunkLen) inc(pos, chunkLen)
# if crc32(data[pos - chunkLen - 4 ..< pos]) != read32be(data, pos): let chunkCrc = crc32(data[pos - chunkLen - 4].addr, chunkLen + 4)
# failCRC() if chunkCrc != data.readUint32(pos).swap():
failCRC()
inc(pos, 4) # CRC inc(pos, 4) # CRC
prevChunkType = chunkType prevChunkType = chunkType
@ -523,7 +539,7 @@ proc encodePng*(
result.add(0.char) result.add(0.char)
result.add(0.char) result.add(0.char)
result.add(0.char) result.add(0.char)
result.addUint32(crc32(result[result.len - 17 ..< result.len]).swap()) result.addUint32(crc32(result[result.len - 17].addr, 17).swap())
# Add IDAT # Add IDAT
# Add room for 1 byte before each row for the filter type. # Add room for 1 byte before each row for the filter type.
@ -556,14 +572,15 @@ proc encodePng*(
result.addUint32(compressed.len.uint32.swap()) result.addUint32(compressed.len.uint32.swap())
result.add("IDAT") result.add("IDAT")
result.add(compressed) result.add(compressed)
result.addUint32( result.addUint32(crc32(
crc32(result[result.len - compressed.len - 4 ..< result.len]).swap() result[result.len - compressed.len - 4].addr,
) compressed.len + 4
).swap())
# Add IEND # Add IEND
result.addUint32(0) result.addUint32(0)
result.add("IEND") result.add("IEND")
result.addUint32(crc32(result[result.len - 4 ..< result.len]).swap()) result.addUint32(crc32(result[result.len - 4].addr, 4).swap())
proc encodePng*(png: Png): string {.raises: [PixieError].} = proc encodePng*(png: Png): string {.raises: [PixieError].} =
encodePng(png.width, png.height, 4, png.data[0].addr, png.data.len * 4) encodePng(png.width, png.height, 4, png.data[0].addr, png.data.len * 4)

View file

@ -30,11 +30,25 @@ proc hash(p: ColorRGBA): int =
(p.r.int * 3 + p.g.int * 5 + p.b.int * 7 + p.a.int * 11) mod indexLen (p.r.int * 3 + p.g.int * 5 + p.b.int * 7 + p.a.int * 11) mod indexLen
proc newImage*(qoi: Qoi): Image = proc newImage*(qoi: Qoi): Image =
## Converts raw QOI data to `Image`. ## Creates a new Image from the QOI.
result = newImage(qoi.width, qoi.height) result = newImage(qoi.width, qoi.height)
copyMem(result.data[0].addr, qoi.data[0].addr, qoi.data.len * 4) copyMem(result.data[0].addr, qoi.data[0].addr, qoi.data.len * 4)
result.data.toPremultipliedAlpha() result.data.toPremultipliedAlpha()
proc convertToImage*(qoi: Qoi): Image {.raises: [].} =
## Converts a QOI into an Image by moving the data. This is faster but can
## only be done once.
type Movable = ref object
width, height, channels: int
colorspace: Colorspace
data: seq[ColorRGBX]
result = Image()
result.width = qoi.width
result.height = qoi.height
result.data = move cast[Movable](qoi).data
result.data.toPremultipliedAlpha()
proc decodeQoi*(data: string): Qoi {.raises: [PixieError].} = proc decodeQoi*(data: string): Qoi {.raises: [PixieError].} =
## Decompress QOI file format data. ## Decompress QOI file format data.
if data.len <= 14 or data[0 .. 3] != qoiSignature: if data.len <= 14 or data[0 .. 3] != qoiSignature:

View file

@ -254,7 +254,8 @@ proc spread*(mask: Mask, spread: float32) {.raises: [PixieError].} =
let spread = round(spread).int let spread = round(spread).int
if spread == 0: if spread == 0:
return return
elif spread > 0:
if spread > 0:
# Spread in the X direction. Store with dimensions swapped for reading later. # Spread in the X direction. Store with dimensions swapped for reading later.
let spreadX = newMask(mask.height, mask.width) let spreadX = newMask(mask.height, mask.width)

View file

@ -1320,9 +1320,9 @@ proc clearUnsafe(target: Image | Mask, startX, startY, toX, toY: int) =
start = target.dataIndex(startX, startY) start = target.dataIndex(startX, startY)
len = target.dataIndex(toX, toY) - start len = target.dataIndex(toX, toY) - start
when type(target) is Image: when type(target) is Image:
target.data.fillUnsafe(rgbx(0, 0, 0, 0), start, len) fillUnsafe(target.data, rgbx(0, 0, 0, 0), start, len)
else: # target is Mask else: # target is Mask
target.data.fillUnsafe(0, start, len) fillUnsafe(target.data, 0, start, len)
proc fillCoverage( proc fillCoverage(
image: Image, image: Image,

View file

@ -13,12 +13,12 @@ block:
timeIt "pixie decode": timeIt "pixie decode":
discard decodePng(data) discard decodePng(data)
timeIt "pixie decode + alpha":
discard decodePng(data).convertToImage()
timeIt "pixie encode": timeIt "pixie encode":
discard encodePng(decodedPng) discard encodePng(decodedPng)
timeIt "pixie decode + alpha":
discard newImage(decodePng(data))
timeIt "pixie encode + alpha": timeIt "pixie encode + alpha":
discard encodePng(decodedImage) discard encodePng(decodedImage)
@ -55,9 +55,9 @@ block:
block: block:
timeIt "cairo decode": timeIt "cairo decode":
discard imageSurfaceCreateFromPng(filePath) discard imageSurfaceCreateFromPng(filePath.cstring)
let decoded = imageSurfaceCreateFromPng(filePath) let decoded = imageSurfaceCreateFromPng(filePath.cstring)
timeIt "cairo encode": timeIt "cairo encode":
var write: WriteFunc = var write: WriteFunc =
proc(closure: pointer, data: cstring, len: int32): Status {.cdecl.} = proc(closure: pointer, data: cstring, len: int32): Status {.cdecl.} =