Merge pull request #434 from guzba/master

decode png ptr+len api
This commit is contained in:
Andre von Houck 2022-06-10 08:46:04 +03:00 committed by GitHub
commit ab63f2e2e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 24 deletions

View file

@ -9,11 +9,10 @@ requires "nim >= 1.4.8"
requires "vmath >= 1.1.4"
requires "chroma >= 0.2.5"
requires "zippy >= 0.9.11"
requires "flatty >= 0.3.3"
requires "flatty >= 0.3.4"
requires "nimsimd >= 1.0.0"
requires "bumpy >= 1.1.1"
task bindings, "Generate bindings":
proc compile(libName: string, flags = "") =

View file

@ -67,7 +67,7 @@ proc decodeMask*(data: string): Mask {.raises: [PixieError].} =
proc readImageDimensions*(
filePath: string
): ImageDimensions {.inline, raises: [PixieError].} =
## Loads an image from a file.
## Decodes an image's dimensions from a file.
try:
decodeImageDimensions(readFile(filePath))
except IOError as e:

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
@ -268,7 +270,6 @@ proc decodeImageData(
result[i].a = 0
else:
# While we can read an extra byte safely, do so. Much faster.
# var rgba: ColorRGBA
for i in 0 ..< header.height * header.width - 1:
copyMem(result[i].addr, unfiltered[i * 3].unsafeAddr, 4)
result[i].a = 255
@ -342,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:
@ -357,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:
@ -385,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)
@ -394,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
@ -405,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:
@ -415,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:
@ -457,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":
@ -469,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].} =