diff --git a/src/pixie/fileformats/png.nim b/src/pixie/fileformats/png.nim index 2091675..79e3a14 100644 --- a/src/pixie/fileformats/png.nim +++ b/src/pixie/fileformats/png.nim @@ -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].} =