Merge pull request #240 from guzba/master
png always strings, updated deps, masters fix
This commit is contained in:
commit
c6ddb73673
8 changed files with 60 additions and 70 deletions
|
@ -8,8 +8,8 @@ srcDir = "src"
|
||||||
requires "nim >= 1.2.6"
|
requires "nim >= 1.2.6"
|
||||||
requires "vmath >= 1.0.8"
|
requires "vmath >= 1.0.8"
|
||||||
requires "chroma >= 0.2.5"
|
requires "chroma >= 0.2.5"
|
||||||
requires "zippy >= 0.5.12"
|
requires "zippy >= 0.6.0"
|
||||||
requires "flatty >= 0.1.3"
|
requires "flatty >= 0.2.2"
|
||||||
requires "nimsimd >= 1.0.0"
|
requires "nimsimd >= 1.0.0"
|
||||||
requires "bumpy >= 1.0.3"
|
requires "bumpy >= 1.0.3"
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ proc decodeGif*(data: string): Image =
|
||||||
|
|
||||||
# Turn full lzw data into bit stream.
|
# Turn full lzw data into bit stream.
|
||||||
var
|
var
|
||||||
bs = initBitStream(cast[seq[uint8]](lzwData))
|
bs = initBitStream(lzwData)
|
||||||
bitSize = lzwMinBitSize + 1
|
bitSize = lzwMinBitSize + 1
|
||||||
currentCodeTableMax = (1 shl (bitSize)) - 1
|
currentCodeTableMax = (1 shl (bitSize)) - 1
|
||||||
codeLast: int = -1
|
codeLast: int = -1
|
||||||
|
|
|
@ -23,14 +23,14 @@ template failInvalid() =
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.push checks: off.}
|
{.push checks: off.}
|
||||||
|
|
||||||
proc decodeHeader(data: seq[uint8]): PngHeader =
|
proc decodeHeader(data: string): PngHeader =
|
||||||
result.width = data.readUint32(0).swap().int
|
result.width = data.readUint32(0).swap().int
|
||||||
result.height = data.readUint32(4).swap().int
|
result.height = data.readUint32(4).swap().int
|
||||||
result.bitDepth = data[8]
|
result.bitDepth = data.readUint8(8)
|
||||||
result.colorType = data[9]
|
result.colorType = data.readUint8(9)
|
||||||
result.compressionMethod = data[10]
|
result.compressionMethod = data.readUint8(10)
|
||||||
result.filterMethod = data[11]
|
result.filterMethod = data.readUint8(11)
|
||||||
result.interlaceMethod = data[12]
|
result.interlaceMethod = data.readUint8(12)
|
||||||
|
|
||||||
if result.width == 0 or result.width > int32.high.int:
|
if result.width == 0 or result.width > int32.high.int:
|
||||||
raise newException(PixieError, "Invalid PNG width")
|
raise newException(PixieError, "Invalid PNG width")
|
||||||
|
@ -79,7 +79,7 @@ proc decodeHeader(data: seq[uint8]): PngHeader =
|
||||||
if result.interlaceMethod != 0:
|
if result.interlaceMethod != 0:
|
||||||
raise newException(PixieError, "Interlaced PNG not yet supported")
|
raise newException(PixieError, "Interlaced PNG not yet supported")
|
||||||
|
|
||||||
proc decodePalette(data: seq[uint8]): seq[ColorRGB] =
|
proc decodePalette(data: string): seq[ColorRGB] =
|
||||||
if data.len == 0 or data.len mod 3 != 0:
|
if data.len == 0 or data.len mod 3 != 0:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
|
|
||||||
|
@ -88,9 +88,7 @@ proc decodePalette(data: seq[uint8]): seq[ColorRGB] =
|
||||||
for i in 0 ..< data.len div 3:
|
for i in 0 ..< data.len div 3:
|
||||||
result[i] = cast[ptr ColorRGB](data[i * 3].unsafeAddr)[]
|
result[i] = cast[ptr ColorRGB](data[i * 3].unsafeAddr)[]
|
||||||
|
|
||||||
proc unfilter(
|
proc unfilter(uncompressed: string, height, rowBytes, bpp: int): string =
|
||||||
uncompressed: seq[uint8], height, rowBytes, bpp: int
|
|
||||||
): seq[uint8] =
|
|
||||||
result.setLen(uncompressed.len - height)
|
result.setLen(uncompressed.len - height)
|
||||||
|
|
||||||
template uncompressedIdx(x, y: int): int =
|
template uncompressedIdx(x, y: int): int =
|
||||||
|
@ -101,7 +99,7 @@ proc unfilter(
|
||||||
|
|
||||||
# Unfilter the image data
|
# Unfilter the image data
|
||||||
for y in 0 ..< height:
|
for y in 0 ..< height:
|
||||||
let filterType = uncompressed[uncompressedIdx(0, y)]
|
let filterType = uncompressed.readUint8(uncompressedIdx(0, y))
|
||||||
case filterType:
|
case filterType:
|
||||||
of 0: # None
|
of 0: # None
|
||||||
copyMem(
|
copyMem(
|
||||||
|
@ -111,31 +109,31 @@ proc unfilter(
|
||||||
)
|
)
|
||||||
of 1: # Sub
|
of 1: # Sub
|
||||||
for x in 0 ..< rowBytes:
|
for x in 0 ..< rowBytes:
|
||||||
var value = uncompressed[uncompressedIdx(x + 1, y)]
|
var value = uncompressed.readUint8(uncompressedIdx(x + 1, y))
|
||||||
if x - bpp >= 0:
|
if x - bpp >= 0:
|
||||||
value += result[unfiteredIdx(x - bpp, y)]
|
value += result.readUint8(unfiteredIdx(x - bpp, y))
|
||||||
result[unfiteredIdx(x, y)] = value
|
result[unfiteredIdx(x, y)] = value.char
|
||||||
of 2: # Up
|
of 2: # Up
|
||||||
for x in 0 ..< rowBytes:
|
for x in 0 ..< rowBytes:
|
||||||
var value = uncompressed[uncompressedIdx(x + 1, y)]
|
var value = uncompressed.readUint8(uncompressedIdx(x + 1, y))
|
||||||
if y - 1 >= 0:
|
if y - 1 >= 0:
|
||||||
value += result[unfiteredIdx(x, y - 1)]
|
value += result.readUint8(unfiteredIdx(x, y - 1))
|
||||||
result[unfiteredIdx(x, y)] = value
|
result[unfiteredIdx(x, y)] = value.char
|
||||||
of 3: # Average
|
of 3: # Average
|
||||||
for x in 0 ..< rowBytes:
|
for x in 0 ..< rowBytes:
|
||||||
var
|
var
|
||||||
value = uncompressed[uncompressedIdx(x + 1, y)]
|
value = uncompressed.readUint8(uncompressedIdx(x + 1, y))
|
||||||
left, up: int
|
left, up: int
|
||||||
if x - bpp >= 0:
|
if x - bpp >= 0:
|
||||||
left = result[unfiteredIdx(x - bpp, y)].int
|
left = result[unfiteredIdx(x - bpp, y)].int
|
||||||
if y - 1 >= 0:
|
if y - 1 >= 0:
|
||||||
up = result[unfiteredIdx(x, y - 1)].int
|
up = result[unfiteredIdx(x, y - 1)].int
|
||||||
value += ((left + up) div 2).uint8
|
value += ((left + up) div 2).uint8
|
||||||
result[unfiteredIdx(x, y)] = value
|
result[unfiteredIdx(x, y)] = value.char
|
||||||
of 4: # Paeth
|
of 4: # Paeth
|
||||||
for x in 0 ..< rowBytes:
|
for x in 0 ..< rowBytes:
|
||||||
var
|
var
|
||||||
value = uncompressed[uncompressedIdx(x + 1, y)]
|
value = uncompressed.readUint8(uncompressedIdx(x + 1, y))
|
||||||
left, up, upLeft: int
|
left, up, upLeft: int
|
||||||
if x - bpp >= 0:
|
if x - bpp >= 0:
|
||||||
left = result[unfiteredIdx(x - bpp, y)].int
|
left = result[unfiteredIdx(x - bpp, y)].int
|
||||||
|
@ -156,14 +154,14 @@ proc unfilter(
|
||||||
else:
|
else:
|
||||||
c
|
c
|
||||||
value += paethPredictor(up, left, upLeft).uint8
|
value += paethPredictor(up, left, upLeft).uint8
|
||||||
result[unfiteredIdx(x, y)] = value
|
result[unfiteredIdx(x, y)] = value.char
|
||||||
else:
|
else:
|
||||||
discard # Not possible, parseHeader validates
|
discard # Not possible, parseHeader validates
|
||||||
|
|
||||||
proc decodeImageData(
|
proc decodeImageData(
|
||||||
header: PngHeader,
|
header: PngHeader,
|
||||||
palette: seq[ColorRGB],
|
palette: seq[ColorRGB],
|
||||||
transparency, data: seq[uint8]
|
transparency, data: string
|
||||||
): seq[ColorRGBA] =
|
): seq[ColorRGBA] =
|
||||||
result.setLen(header.width * header.height)
|
result.setLen(header.width * header.height)
|
||||||
|
|
||||||
|
@ -199,7 +197,7 @@ proc decodeImageData(
|
||||||
var bytePos, bitPos: int
|
var bytePos, bitPos: int
|
||||||
for y in 0 ..< header.height:
|
for y in 0 ..< header.height:
|
||||||
for x in 0 ..< header.width:
|
for x in 0 ..< header.width:
|
||||||
var value = unfiltered[bytePos]
|
var value = unfiltered.readUint8(bytePos)
|
||||||
case header.bitDepth:
|
case header.bitDepth:
|
||||||
of 1:
|
of 1:
|
||||||
value = (value shr (7 - bitPos)) and 1
|
value = (value shr (7 - bitPos)) and 1
|
||||||
|
@ -234,9 +232,9 @@ proc decodeImageData(
|
||||||
of 2:
|
of 2:
|
||||||
var special: ColorRGBA
|
var special: ColorRGBA
|
||||||
if transparency.len == 6: # Need to apply transparency check, slower.
|
if transparency.len == 6: # Need to apply transparency check, slower.
|
||||||
special.r = transparency[1]
|
special.r = transparency.readUint8(1)
|
||||||
special.g = transparency[3]
|
special.g = transparency.readUint8(3)
|
||||||
special.b = transparency[5]
|
special.b = transparency.readUint8(5)
|
||||||
special.a = 255
|
special.a = 255
|
||||||
|
|
||||||
# While we can read an extra byte safely, do so. Much faster.
|
# While we can read an extra byte safely, do so. Much faster.
|
||||||
|
@ -264,7 +262,7 @@ proc decodeImageData(
|
||||||
var bytePos, bitPos: int
|
var bytePos, bitPos: int
|
||||||
for y in 0 ..< header.height:
|
for y in 0 ..< header.height:
|
||||||
for x in 0 ..< header.width:
|
for x in 0 ..< header.width:
|
||||||
var value = unfiltered[bytePos]
|
var value = unfiltered.readUint8(bytePos)
|
||||||
case header.bitDepth:
|
case header.bitDepth:
|
||||||
of 1:
|
of 1:
|
||||||
value = (value shr (7 - bitPos)) and 1
|
value = (value shr (7 - bitPos)) and 1
|
||||||
|
@ -290,7 +288,7 @@ proc decodeImageData(
|
||||||
rgb = palette[value]
|
rgb = palette[value]
|
||||||
transparency =
|
transparency =
|
||||||
if transparency.len > value.int:
|
if transparency.len > value.int:
|
||||||
transparency[value]
|
transparency.readUint8(value.int)
|
||||||
else:
|
else:
|
||||||
255
|
255
|
||||||
result[x + y * header.width] = ColorRGBA(
|
result[x + y * header.width] = ColorRGBA(
|
||||||
|
@ -305,10 +303,10 @@ proc decodeImageData(
|
||||||
for i in 0 ..< header.height * header.width:
|
for i in 0 ..< header.height * header.width:
|
||||||
let bytePos = i * 2
|
let bytePos = i * 2
|
||||||
result[i] = ColorRGBA(
|
result[i] = ColorRGBA(
|
||||||
r: unfiltered[bytePos],
|
r: unfiltered.readUint8(bytePos),
|
||||||
g: unfiltered[bytePos],
|
g: unfiltered.readUint8(bytePos),
|
||||||
b: unfiltered[bytePos],
|
b: unfiltered.readUint8(bytePos),
|
||||||
a: unfiltered[bytePos + 1]
|
a: unfiltered.readUint8(bytePos + 1)
|
||||||
)
|
)
|
||||||
of 6:
|
of 6:
|
||||||
for i in 0 ..< header.height * header.width:
|
for i in 0 ..< header.height * header.width:
|
||||||
|
@ -316,7 +314,7 @@ proc decodeImageData(
|
||||||
else:
|
else:
|
||||||
discard # Not possible, parseHeader validates
|
discard # Not possible, parseHeader validates
|
||||||
|
|
||||||
proc decodePng*(data: seq[uint8]): Image =
|
proc decodePng*(data: string): Image =
|
||||||
## Decodes the PNG data into an Image.
|
## Decodes the PNG data into an Image.
|
||||||
|
|
||||||
if data.len < (8 + (8 + 13 + 4) + 4): # Magic bytes + IHDR + IEND
|
if data.len < (8 + (8 + 13 + 4) + 4): # Magic bytes + IHDR + IEND
|
||||||
|
@ -332,7 +330,7 @@ proc decodePng*(data: seq[uint8]): Image =
|
||||||
counts = ChunkCounts()
|
counts = ChunkCounts()
|
||||||
header: PngHeader
|
header: PngHeader
|
||||||
palette: seq[ColorRGB]
|
palette: seq[ColorRGB]
|
||||||
transparency, imageData: seq[uint8]
|
transparency, imageData: string
|
||||||
prevChunkType: string
|
prevChunkType: string
|
||||||
|
|
||||||
# First chunk must be IHDR
|
# First chunk must be IHDR
|
||||||
|
@ -401,8 +399,7 @@ proc decodePng*(data: seq[uint8]): Image =
|
||||||
if chunkLen != 0:
|
if chunkLen != 0:
|
||||||
failInvalid()
|
failInvalid()
|
||||||
else:
|
else:
|
||||||
let bytes = cast[seq[uint8]](chunkType)
|
if (chunkType.readUint8(0) and 0b00100000) == 0:
|
||||||
if (bytes[0] and 0b00100000) == 0:
|
|
||||||
raise newException(
|
raise newException(
|
||||||
PixieError, "Unrecognized PNG critical chunk " & chunkType
|
PixieError, "Unrecognized PNG critical chunk " & chunkType
|
||||||
)
|
)
|
||||||
|
@ -429,13 +426,7 @@ proc decodePng*(data: seq[uint8]): Image =
|
||||||
result.height = header.height
|
result.height = header.height
|
||||||
result.data = cast[seq[ColorRGBX]](pixels)
|
result.data = cast[seq[ColorRGBX]](pixels)
|
||||||
|
|
||||||
proc decodePng*(data: string): Image {.inline.} =
|
proc encodePng*(width, height, channels: int, data: pointer, len: int): string =
|
||||||
## Decodes the PNG data into an Image.
|
|
||||||
decodePng(cast[seq[uint8]](data))
|
|
||||||
|
|
||||||
proc encodePng*(
|
|
||||||
width, height, channels: int, data: pointer, len: int
|
|
||||||
): seq[uint8] =
|
|
||||||
## Encodes the image data into the PNG file format.
|
## Encodes the image data into the PNG file format.
|
||||||
## If data points to RGBA data, it is assumed to be straight alpha.
|
## If data points to RGBA data, it is assumed to be straight alpha.
|
||||||
|
|
||||||
|
@ -449,32 +440,36 @@ proc encodePng*(
|
||||||
raise newException(PixieError, "Invalid PNG data size")
|
raise newException(PixieError, "Invalid PNG data size")
|
||||||
|
|
||||||
let colorType = case channels:
|
let colorType = case channels:
|
||||||
of 1: 0.uint8
|
of 1: 0.char
|
||||||
of 2: 4
|
of 2: 4.char
|
||||||
of 3: 2
|
of 3: 2.char
|
||||||
of 4: 6
|
of 4: 6.char
|
||||||
else:
|
else:
|
||||||
raise newException(PixieError, "Invalid PNG number of channels")
|
raise newException(PixieError, "Invalid PNG number of channels")
|
||||||
|
|
||||||
let data = cast[ptr UncheckedArray[uint8]](data)
|
let data = cast[ptr UncheckedArray[uint8]](data)
|
||||||
|
|
||||||
# Add the PNG file signature
|
# Add the PNG file signature
|
||||||
result.add(pngSignature)
|
for c in pngSignature:
|
||||||
|
result.add(c.char)
|
||||||
|
|
||||||
# Add IHDR
|
# Add IHDR
|
||||||
result.addUint32(13.uint32.swap())
|
result.addUint32(13.uint32.swap())
|
||||||
result.addStr("IHDR")
|
result.add("IHDR")
|
||||||
result.addUint32(width.uint32.swap())
|
result.addUint32(width.uint32.swap())
|
||||||
result.addUint32(height.uint32.swap())
|
result.addUint32(height.uint32.swap())
|
||||||
result.add(8.uint8)
|
result.add(8.char)
|
||||||
result.add([colorType, 0, 0, 0])
|
result.add(colorType)
|
||||||
|
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 ..< result.len]).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.
|
||||||
var filtered = newSeq[uint8](width * height * channels + height)
|
var filtered = newString(width * height * channels + height)
|
||||||
for y in 0 ..< height:
|
for y in 0 ..< height:
|
||||||
filtered[y * width * channels + y] = 3 # Average
|
filtered[y * width * channels + y] = 3.char # Average
|
||||||
for x in 0 ..< width * channels:
|
for x in 0 ..< width * channels:
|
||||||
# Move through the image data byte-by-byte
|
# Move through the image data byte-by-byte
|
||||||
let
|
let
|
||||||
|
@ -486,7 +481,7 @@ proc encodePng*(
|
||||||
if y - 1 >= 0:
|
if y - 1 >= 0:
|
||||||
up = data[(y - 1) * width * channels + x].int
|
up = data[(y - 1) * width * channels + x].int
|
||||||
let avg = ((left + up) div 2).uint8
|
let avg = ((left + up) div 2).uint8
|
||||||
filtered[filteredPos] = data[dataPos] - avg
|
filtered[filteredPos] = (data[dataPos] - avg).char
|
||||||
|
|
||||||
let compressed =
|
let compressed =
|
||||||
try:
|
try:
|
||||||
|
@ -499,7 +494,7 @@ proc encodePng*(
|
||||||
raise newException(PixieError, "Compressed PNG image data too large")
|
raise newException(PixieError, "Compressed PNG image data too large")
|
||||||
|
|
||||||
result.addUint32(compressed.len.uint32.swap())
|
result.addUint32(compressed.len.uint32.swap())
|
||||||
result.add(cast[seq[uint8]]("IDAT"))
|
result.add("IDAT")
|
||||||
result.add(compressed)
|
result.add(compressed)
|
||||||
result.addUint32(
|
result.addUint32(
|
||||||
crc32(result[result.len - compressed.len - 4 ..< result.len]).swap()
|
crc32(result[result.len - compressed.len - 4 ..< result.len]).swap()
|
||||||
|
@ -507,7 +502,7 @@ proc encodePng*(
|
||||||
|
|
||||||
# Add IEND
|
# Add IEND
|
||||||
result.addUint32(0)
|
result.addUint32(0)
|
||||||
result.addStr("IEND")
|
result.add("IEND")
|
||||||
result.addUint32(crc32(result[result.len - 4 ..< result.len]).swap())
|
result.addUint32(crc32(result[result.len - 4 ..< result.len]).swap())
|
||||||
|
|
||||||
proc encodePng*(image: Image): string =
|
proc encodePng*(image: Image): string =
|
||||||
|
@ -519,9 +514,7 @@ proc encodePng*(image: Image): string =
|
||||||
)
|
)
|
||||||
var copy = image.data
|
var copy = image.data
|
||||||
copy.toStraightAlpha()
|
copy.toStraightAlpha()
|
||||||
cast[string](encodePng(
|
encodePng(image.width, image.height, 4, copy[0].addr, copy.len * 4)
|
||||||
image.width, image.height, 4, copy[0].addr, copy.len * 4
|
|
||||||
))
|
|
||||||
|
|
||||||
proc encodePng*(mask: Mask): string =
|
proc encodePng*(mask: Mask): string =
|
||||||
## Encodes the mask data into the PNG file format.
|
## Encodes the mask data into the PNG file format.
|
||||||
|
@ -530,9 +523,7 @@ proc encodePng*(mask: Mask): string =
|
||||||
PixieError,
|
PixieError,
|
||||||
"Mask has no data (are height and width 0?)"
|
"Mask has no data (are height and width 0?)"
|
||||||
)
|
)
|
||||||
cast[string](encodePng(
|
encodePng(mask.width, mask.height, 1, mask.data[0].addr, mask.data.len)
|
||||||
mask.width, mask.height, 1, mask.data[0].addr, mask.data.len
|
|
||||||
))
|
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -6,10 +6,10 @@ let
|
||||||
data = readFile(filePath)
|
data = readFile(filePath)
|
||||||
|
|
||||||
timeIt "pixie decode":
|
timeIt "pixie decode":
|
||||||
keep decodePng(cast[seq[uint8]](data))
|
keep decodePng(data)
|
||||||
|
|
||||||
timeIt "pixie encode":
|
timeIt "pixie encode":
|
||||||
let decoded = decodePng(cast[seq[uint8]](data))
|
let decoded = decodePng(data)
|
||||||
keep encodePng(decoded).len
|
keep encodePng(decoded).len
|
||||||
|
|
||||||
timeIt "nimPNG decode":
|
timeIt "nimPNG decode":
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
|
@ -45,9 +45,8 @@ block:
|
||||||
doAssert $path == "M1 2 L3 4 H5 V6 C0 0 0 0 0 0 Q1 1 1 1 T2 2 A7 7 7 7 7 7 7 Z"
|
doAssert $path == "M1 2 L3 4 H5 V6 C0 0 0 0 0 0 Q1 1 1 1 T2 2 A7 7 7 7 7 7 7 Z"
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let pathStr = "M 0.1E-10 0.1e10 L2+2 L3-3 L0.1E+10-1"
|
||||||
pathStr = "M 0.1E-10 0.1e10 L2+2 L3-3 L0.1E+10-1"
|
discard parsePath(pathStr)
|
||||||
path = parsePath(pathStr)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
|
Loading…
Reference in a new issue