decode png has ptr+len proc

This commit is contained in:
Ryan Oldenburg 2022-06-09 18:04:37 -05:00
parent 7ce724c0fd
commit 58a7e3bcd0

View file

@ -27,7 +27,8 @@ template failInvalid() =
when defined(release):
{.push checks: off.}
proc decodeHeader(data: string): PngHeader =
proc decodeHeader(data: pointer): PngHeader =
let data = cast[ptr UncheckedArray[uint8]](data)
result.width = data.readUint32(0).swap().int
result.height = data.readUint32(4).swap().int
result.bitDepth = data.readUint8(8)
@ -78,18 +79,19 @@ proc decodeHeader(data: string): PngHeader =
# Not yet supported:
if result.bitDepth == 16:
raise newException(PixieError, "PNG 16 bit depth not yet supported")
raise newException(PixieError, "PNG 16 bit depth not supported yet")
if result.interlaceMethod != 0:
raise newException(PixieError, "Interlaced PNG not yet supported")
raise newException(PixieError, "Interlaced PNG not supported yet")
proc decodePalette(data: string): seq[ColorRGB] =
if data.len == 0 or data.len mod 3 != 0:
proc decodePalette(data: pointer, len: int): seq[ColorRGB] =
if len == 0 or len mod 3 != 0:
failInvalid()
result.setLen(data.len div 3)
result.setLen(len div 3)
for i in 0 ..< data.len div 3:
let data = cast[ptr UncheckedArray[uint8]](data)
for i in 0 ..< len div 3:
copyMem(result[i].addr, data[i * 3].unsafeAddr, 3)
proc unfilter(
@ -165,7 +167,7 @@ proc unfilter(
discard # Not possible, parseHeader validates
proc decodeImageData(
data: string,
data: ptr UncheckedArray[uint8],
header: PngHeader,
palette: seq[ColorRGB],
transparency: string,
@ -183,7 +185,7 @@ proc decodeImageData(
for (start, len) in idats:
let op = imageData.len
imageData.setLen(imageData.len + len)
copyMem(imageData[op].addr, data[start].unsafeAddr, len)
copyMem(imageData[op].addr, data[start].addr, len)
try: uncompress(imageData) except ZippyError: failInvalid()
else:
let
@ -341,12 +343,14 @@ proc newImage*(png: Png): Image {.raises: [PixieError].} =
result.data.toPremultipliedAlpha()
proc decodePngDimensions*(
data: string
data: pointer, len: int
): ImageDimensions {.raises: [PixieError].} =
## Decodes the PNG dimensions.
if data.len < (8 + (8 + 13 + 4) + 4): # Magic bytes + IHDR + IEND
if len < (8 + (8 + 13 + 4) + 4): # Magic bytes + IHDR + IEND
failInvalid()
let data = cast[ptr UncheckedArray[uint8]](data)
# PNG file signature
let signature = cast[array[8, uint8]](data.readUint64(0))
if signature != pngSignature:
@ -356,15 +360,23 @@ proc decodePngDimensions*(
if data.readUint32(8).swap() != 13 or data.readStr(12, 4) != "IHDR":
failInvalid()
let header = decodeHeader(data[16 ..< 16 + 13])
let header = decodeHeader(data[16].addr)
result.width = header.width
result.height = header.height
proc decodePng*(data: string): Png {.raises: [PixieError].} =
proc decodePngDimensions*(
data: string
): ImageDimensions {.inline, raises: [PixieError].} =
## Decodes the PNG dimensions.
decodePngDimensions(data.cstring, data.len)
proc decodePng*(data: pointer, len: int): Png {.raises: [PixieError].} =
## Decodes the PNG data.
if data.len < (8 + (8 + 13 + 4) + 4): # Magic bytes + IHDR + IEND
if len < (8 + (8 + 13 + 4) + 4): # Magic bytes + IHDR + IEND
failInvalid()
let data = cast[ptr UncheckedArray[uint8]](data)
# PNG file signature
let signature = cast[array[8, uint8]](data.readUint64(0))
if signature != pngSignature:
@ -384,7 +396,7 @@ proc decodePng*(data: string): Png {.raises: [PixieError].} =
data.readStr(pos + 4, 4) != "IHDR":
failInvalid()
inc(pos, 8)
header = decodeHeader(data[pos ..< pos + 13])
header = decodeHeader(data[pos].addr)
prevChunkType = "IHDR"
inc(pos, 13)
@ -393,7 +405,7 @@ proc decodePng*(data: string): Png {.raises: [PixieError].} =
inc(pos, 4) # CRC
while true:
if pos + 8 > data.len:
if pos + 8 > len:
failInvalid()
let
@ -404,7 +416,7 @@ proc decodePng*(data: string): Png {.raises: [PixieError].} =
if chunkLen > high(int32).int:
failInvalid()
if pos + chunkLen + 4 > data.len:
if pos + chunkLen + 4 > len:
failInvalid()
case chunkType:
@ -414,12 +426,12 @@ proc decodePng*(data: string): Png {.raises: [PixieError].} =
inc counts.PLTE
if counts.PLTE > 1 or counts.IDAT > 0 or counts.tRNS > 0:
failInvalid()
palette = decodePalette(data[pos ..< pos + chunkLen])
palette = decodePalette(data[pos].addr, chunkLen)
of "tRNS":
inc counts.tRNS
if counts.tRNS > 1 or counts.IDAT > 0:
failInvalid()
transparency = data[pos ..< pos + chunkLen]
transparency = data.readStr(pos, chunkLen)
case header.colorType:
of 0:
if transparency.len != 2:
@ -456,7 +468,7 @@ proc decodePng*(data: string): Png {.raises: [PixieError].} =
prevChunkType = chunkType
if pos == data.len or prevChunkType == "IEND":
if pos == len or prevChunkType == "IEND":
break
if prevChunkType != "IEND":
@ -468,6 +480,10 @@ proc decodePng*(data: string): Png {.raises: [PixieError].} =
result.channels = 4
result.data = decodeImageData(data, header, palette, transparency, idats)
proc decodePng*(data: string): Png {.inline, raises: [PixieError].} =
## Decodes the PNG data.
decodePng(data.cstring, data.len)
proc encodePng*(
width, height, channels: int, data: pointer, len: int
): string {.raises: [PixieError].} =