diff --git a/src/pixie.nim b/src/pixie.nim index a3e4fe3..d14049a 100644 --- a/src/pixie.nim +++ b/src/pixie.nim @@ -49,8 +49,39 @@ proc decodeImageDimensions*( ## Decodes an image's dimensions from memory. decodeImageDimensions(data.cstring, data.len) +template compare_as(T: typedesc, p,q: pointer): bool = + cast[ptr T](p)[] == cast[ptr T](q)[] + +proc decodeImage*(data: pointer, len: int): Image {.raises: [PixieError].} = + ## Loads an image from memory, from a pointer and a length. + if len > 8 and compare_as(uint64, data, pngSignature.addr): + decodePng(data, len).convertToImage() + elif len > 2 and compare_as(uint16, data, jpegStartOfImage.addr): + decodeJpeg(data, len) + elif len > 2 and compare_as(array[2,char], data, bmpSignature.cstring): + decodeBmp(data, len) + elif len > 5 and + compare_as(array[5,char], data, xmlSignature.cstring) or + compare_as(array[4,char], data, svgSignature.cstring): + # TODO: avoid allocating/initializing string + var s = newStringOfCap(len) + s.setLen len + copyMem(s.cstring, data, len) + newImage(parseSvg(s)) + elif len > 6 and compare_as(array[6,char], data, gifSignatures[0].cstring) or + compare_as(array[6,char], data, gifSignatures[1].cstring): + newImage(decodeGif(data, len)) + # TODO + # elif len > (14+8) and compare_as(array[4,char], data, qoiSignature.cstring): + # decodeQoi(data, len).convertToImage() + # elif len > 9 and compare_as(array[2,char], data, ppmSignatures[0].cstring) or + # compare_as(array[2,char], data, ppmSignatures[1].cstring): + # decodePpm(data, len) + else: + raise newException(PixieError, "Unsupported image file format") + proc decodeImage*(data: string): Image {.raises: [PixieError].} = - ## Loads an image from memory. + ## Loads an image from memory as a string. if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature): decodePng(data).convertToImage() elif data.len > 2 and data.readUint16(0) == cast[uint16](jpegStartOfImage): diff --git a/src/pixie/fileformats/bmp.nim b/src/pixie/fileformats/bmp.nim index 65d7c05..27798de 100644 --- a/src/pixie/fileformats/bmp.nim +++ b/src/pixie/fileformats/bmp.nim @@ -227,6 +227,17 @@ proc decodeBmp*(data: string): Image {.raises: [PixieError].} = decodeDib(data[14].unsafeAddr, data.len - 14) +proc decodeBmp*(data: pointer, len: int): Image {.raises: [PixieError].} = + ## Decodes bitmap data into an image. + if len < 14: + failInvalid() + + # BMP Header + if cast[ptr int16](data)[] != cast[ptr int16]("BM".cstring)[]: + failInvalid() + + decodeDib(cast[ptr UncheckedArray[char]](data)[14].addr, len - 14) + proc decodeBmpDimensions*( data: pointer, len: int ): ImageDimensions {.raises: [PixieError].} = diff --git a/src/pixie/fileformats/gif.nim b/src/pixie/fileformats/gif.nim index 7adfa78..adf4635 100644 --- a/src/pixie/fileformats/gif.nim +++ b/src/pixie/fileformats/gif.nim @@ -22,12 +22,18 @@ template failInvalid() = when defined(release): {.push checks: off.} -proc decodeGif*(data: string): Gif {.raises: [PixieError].} = +template compare_as(T: typedesc, p,q: pointer): bool = + cast[ptr T](p)[] == cast[ptr T](q)[] + +proc decodeGif*(data: pointer, len: int): Gif {.raises: [PixieError].} = + let data = cast[ptr UncheckedArray[uint8]](data) ## Decodes GIF data. - if data.len < 13: + if len < 13: failInvalid() - if data[0 .. 5] notin gifSignatures: + if not (len > 6 and + compare_as(array[6,char], data, gifSignatures[0].cstring) or + compare_as(array[6,char], data, gifSignatures[1].cstring)): raise newException(PixieError, "Invalid GIF file signature") result = Gif() @@ -49,7 +55,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = var pos = 13 - if pos + globalColorTableSize * 3 > data.len: + if pos + globalColorTableSize * 3 > len: failInvalid() var @@ -69,7 +75,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = proc skipSubBlocks() = while true: # Skip data sub-blocks - if pos + 1 > data.len: + if pos + 1 > len: failInvalid() let subBlockSize = data.readUint8(pos).int @@ -82,7 +88,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = var controlExtension: ControlExtension while true: - if pos + 1 > data.len: + if pos + 1 > len: failInvalid() let blockType = data.readUint8(pos) @@ -90,7 +96,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = case blockType: of 0x2c: # Image - if pos + 9 > data.len: + if pos + 9 > len: failInvalid() let @@ -108,7 +114,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = if imageWidth > screenWidth or imageHeight > screenHeight: raise newException(PixieError, "Invalid GIF frame dimensions") - if pos + localColorTableSize * 3 > data.len: + if pos + localColorTableSize * 3 > len: failInvalid() var localColorTable: seq[ColorRGBX] @@ -123,7 +129,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = ) pos += 3 - if pos + 1 > data.len: + if pos + 1 > len: failInvalid() let minCodeSize = data.readUint8(pos).int @@ -135,7 +141,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = # The image data is contained in a sequence of sub-blocks var lzwDataBlocks: seq[(int, int)] # (offset, len) while true: - if pos + 1 > data.len: + if pos + 1 > len: failInvalid() let subBlockSize = data.readUint8(pos).int @@ -144,7 +150,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = if subBlockSize == 0: break - if pos + subBlockSize > data.len: + if pos + subBlockSize > len: failInvalid() lzwDataBlocks.add((pos, subBlockSize)) @@ -307,7 +313,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = controlExtension = ControlExtension() of 0x21: # Extension - if pos + 1 > data.len: + if pos + 1 > len: failInvalid() let extensionType = data.readUint8(pos + 0) @@ -316,7 +322,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = case extensionType: of 0xf9: # Graphic Control Extension - if pos + 1 > data.len: + if pos + 1 > len: failInvalid() let blockSize = data.readUint8(pos).int @@ -325,7 +331,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = if blockSize != 4: failInvalid() - if pos + blockSize > data.len: + if pos + blockSize > len: failInvalid() controlExtension.fields = data.readUint8(pos + 0) @@ -344,7 +350,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = of 0xff: # Application Specific - if pos + 1 > data.len: + if pos + 1 > len: failInvalid() let blockSize = data.readUint8(pos).int @@ -353,7 +359,7 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = if blockSize != 11: failInvalid() - if pos + blockSize > data.len: + if pos + blockSize > len: failInvalid() pos += blockSize @@ -378,6 +384,9 @@ proc decodeGif*(data: string): Gif {.raises: [PixieError].} = for interval in result.intervals: result.duration += interval +proc decodeGif*(data: string): Gif {.raises: [PixieError].} = + return decodeGif(data.cstring, data.len) + proc decodeGifDimensions*( data: pointer, len: int ): ImageDimensions {.raises: [PixieError].} = diff --git a/src/pixie/fileformats/jpeg.nim b/src/pixie/fileformats/jpeg.nim index 4a41a98..6695daf 100644 --- a/src/pixie/fileformats/jpeg.nim +++ b/src/pixie/fileformats/jpeg.nim @@ -1092,12 +1092,12 @@ proc buildImage(state: var DecoderState): Image = else: failInvalid("invalid orientation") -proc decodeJpeg*(data: string): Image {.raises: [PixieError].} = +proc decodeJpeg*(data: pointer, len: int): Image {.raises: [PixieError].} = ## Decodes the JPEG into an Image. var state = DecoderState() - state.buffer = cast[ptr UncheckedArray[uint8]](data.cstring) - state.len = data.len + state.buffer = cast[ptr UncheckedArray[uint8]](data) + state.len = len while true: if state.readUint8() != 0xFF: @@ -1157,6 +1157,10 @@ proc decodeJpeg*(data: string): Image {.raises: [PixieError].} = state.buildImage() +proc decodeJpeg*(data: string): Image {.raises: [PixieError].} = + ## Decodes the JPEG into an Image. + decodeJpeg(data.cstring, data.len) + proc decodeJpegDimensions*( data: pointer, len: int ): ImageDimensions {.raises: [PixieError].} =