ppm consistent style pass

This commit is contained in:
Ryan Oldenburg 2022-01-20 20:04:13 -06:00
parent 2caed5f5ae
commit be57c71c3c
2 changed files with 50 additions and 35 deletions

View file

@ -16,9 +16,10 @@ proc decodeHeader(data: string): PpmHeader {.raises: [PixieError].} =
if data.len <= 10: # Each part + whitespace if data.len <= 10: # Each part + whitespace
raise newException(PixieError, "Invalid PPM file header") raise newException(PixieError, "Invalid PPM file header")
var commentMode, readWhitespace: bool var
var i, readFields: int commentMode, readWhitespace: bool
var field: string i, readFields: int
field: string
while readFields < 4: while readFields < 4:
let c = readUint8(data, i).char let c = readUint8(data, i).char
if c == '#': if c == '#':
@ -41,7 +42,8 @@ proc decodeHeader(data: string): PpmHeader {.raises: [PixieError].} =
result.maxVal = parseInt(field) result.maxVal = parseInt(field)
else: else:
discard discard
except ValueError: failInvalid() except ValueError:
failInvalid()
field = "" field = ""
elif not (c in Whitespace): elif not (c in Whitespace):
field.add(c) field.add(c)
@ -54,49 +56,64 @@ proc decodeP6Data(data: string, maxVal: int): seq[ColorRGBX] {.raises: [].} =
let needsUint16 = maxVal > 0xFF let needsUint16 = maxVal > 0xFF
result = newSeq[ColorRGBX]( result = newSeq[ColorRGBX](
if needsUint16: data.len div 6 if needsUint16:
else: data.len div 3 data.len div 6
else:
data.len div 3
) )
# Let's calculate the real maximum value multiplier. # Let's calculate the real maximum value multiplier.
# rgbx() accepts a maximum value of 0xFF. Most of the time, # rgbx() accepts a maximum value of 255. Most of the time,
# maxVal is set to 0xFF as well, so in most cases it is 1 # maxVal is set to 255 as well, so in most cases it is 1
let valueMultiplier = 0xFF / maxVal let valueMultiplier = (255 / maxVal).float32
# if comparison in for loops is expensive, so let's unroll it # if comparison in for loops is expensive, so let's unroll it
if not needsUint16: if not needsUint16:
for i in 0 ..< result.len: for i in 0 ..< result.len:
let let
red = (readUint8(data, i + (i * 2)).float * valueMultiplier + 0.5).uint8 red = data.readUint8(i + (i * 2)).float32
green = (readUint8(data, i + 1 + (i * 2)).float * valueMultiplier + 0.5).uint8 green = data.readUint8(i + 1 + (i * 2)).float32
blue = (readUint8(data, i + 2 + (i * 2)).float * valueMultiplier + 0.5).uint8 blue = data.readUint8(i + 2 + (i * 2)).float32
result[i] = rgbx(red, green, blue, 0xFF) result[i] = rgbx(
(red * valueMultiplier + 0.5).uint8,
(green * valueMultiplier + 0.5).uint8,
(blue * valueMultiplier + 0.5).uint8,
255
)
else: else:
for i in 0 ..< result.len: for i in 0 ..< result.len:
let let
red = (readUint16(data, i + (i * 5)).swap.float * valueMultiplier + 0.5).uint8 red = data.readUint16(i + (i * 5)).swap.float32
green = (readUint16(data, i + 2 + (i * 5)).swap.float * valueMultiplier + 0.5).uint8 green = data.readUint16(i + 2 + (i * 5)).swap.float32
blue = (readUint16(data, i + 4 + (i * 5)).swap.float * valueMultiplier + 0.5).uint8 blue = data.readUint16(i + 4 + (i * 5)).swap.float32
result[i] = rgbx(red, green, blue, 0xFF) result[i] = rgbx(
(red * valueMultiplier + 0.5).uint8,
(green * valueMultiplier + 0.5).uint8,
(blue * valueMultiplier + 0.5).uint8,
255
)
proc decodeP3Data(data: string, maxVal: int): seq[ColorRGBX] {.raises: [PixieError].} = proc decodeP3Data(data: string, maxVal: int): seq[ColorRGBX] {.raises: [PixieError].} =
let needsUint16 = maxVal > 0xFF let
let maxLen = ( needsUint16 = maxVal > 0xFF
if needsUint16: data.splitWhitespace.len * 2 maxLen =
else: data.splitWhitespace.len if needsUint16:
) data.splitWhitespace.len * 2
var p6data = newStringOfCap(maxLen) else:
data.splitWhitespace.len
var p6data = newStringOfCap(maxLen)
try: try:
if not needsUint16: if not needsUint16:
for line in data.splitLines(): for line in data.splitLines():
for sample in line.split('#', 1)[0].splitWhitespace(): for sample in line.split('#', 1)[0].splitWhitespace():
p6data.add(parseInt(sample).chr) p6data.add(parseInt(sample).char)
else: else:
for line in data.splitLines(): for line in data.splitLines():
for sample in line.split('#', 1)[0].splitWhitespace(): for sample in line.split('#', 1)[0].splitWhitespace():
p6data.addUint16(parseInt(sample).uint16.swap) p6data.addUint16(parseInt(sample).uint16.swap)
except ValueError: failInvalid() except ValueError:
failInvalid()
result = decodeP6Data(p6data, maxVal) result = decodeP6Data(p6data, maxVal)
@ -105,20 +122,20 @@ proc decodePpm*(data: string): Image {.raises: [PixieError].} =
let header = decodeHeader(data) let header = decodeHeader(data)
if not (header.version in ppmSignatures): failInvalid() if not (header.version in ppmSignatures):
if 0 > header.maxVal or header.maxVal > 0xFFFF: failInvalid() failInvalid()
if 0 > header.maxVal or header.maxVal > 0xFFFF:
failInvalid()
result = newImage(header.width, header.height) result = newImage(header.width, header.height)
result.data = ( result.data = (
if header.version == "P3": if header.version == "P3":
decodeP3Data(data[header.dataOffset .. ^1], header.maxVal) decodeP3Data(data[header.dataOffset .. ^1], header.maxVal)
else: decodeP6Data(data[header.dataOffset .. ^1], header.maxVal) else:
decodeP6Data(data[header.dataOffset .. ^1], header.maxVal)
) )
proc decodePpm*(data: seq[uint8]): Image {.inline, raises: [PixieError].} =
## Decodes Portable Pixel Map data into an Image.
decodePpm(cast[string](data))
proc encodePpm*(image: Image): string {.raises: [].} = proc encodePpm*(image: Image): string {.raises: [].} =
## Encodes an image into the PPM file format (version P6). ## Encodes an image into the PPM file format (version P6).

View file

@ -7,9 +7,7 @@ block:
)) ))
writeFile("tests/fileformats/ppm/feep." & $format & ".ppm", encodePpm(image)) writeFile("tests/fileformats/ppm/feep." & $format & ".ppm", encodePpm(image))
let image = decodePpm(readFile( let image = decodePpm(readFile("tests/fileformats/ppm/feep.p3.hidepth.master.ppm"))
"tests/fileformats/ppm/feep.p3.hidepth.master.ppm"
))
writeFile("tests/fileformats/ppm/feep.p3.hidepth.ppm", encodePpm(image)) writeFile("tests/fileformats/ppm/feep.p3.hidepth.ppm", encodePpm(image))
# produced output should be identical to P6 master # produced output should be identical to P6 master