commit
bde0d2fd5c
4 changed files with 92 additions and 55 deletions
|
@ -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,19 +122,18 @@ 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).
|
||||||
|
|
|
@ -2126,7 +2126,9 @@ proc getGlyphId(opentype: OpenType, rune: Rune): uint16 =
|
||||||
proc hasGlyph*(opentype: OpenType, rune: Rune): bool =
|
proc hasGlyph*(opentype: OpenType, rune: Rune): bool =
|
||||||
rune in opentype.cmap.runeToGlyphId
|
rune in opentype.cmap.runeToGlyphId
|
||||||
|
|
||||||
proc parseGlyfGlyph(opentype: OpenType, glyphId: uint16): Path {.raises: [PixieError], gcsafe.}
|
proc parseGlyfGlyph(
|
||||||
|
opentype: OpenType, glyphId: uint16
|
||||||
|
): Path {.raises: [PixieError], gcsafe.}
|
||||||
|
|
||||||
proc parseGlyphPath(
|
proc parseGlyphPath(
|
||||||
buf: string, offset, numberOfContours: int
|
buf: string, offset, numberOfContours: int
|
||||||
|
|
|
@ -837,27 +837,37 @@ proc drawUber(
|
||||||
when type(a) is Image:
|
when type(a) is Image:
|
||||||
when type(b) is Image:
|
when type(b) is Image:
|
||||||
for q in [0, 4, 8, 12]:
|
for q in [0, 4, 8, 12]:
|
||||||
let sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
let
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, mm_setzero_si128())) != 0xffff:
|
sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
||||||
if (mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) and 0x8888) == 0x8888:
|
eqZer0 = mm_cmpeq_epi8(sourceVec, mm_setzero_si128())
|
||||||
|
if mm_movemask_epi8(eqZer0) != 0xffff:
|
||||||
|
let eq255 = mm_cmpeq_epi8(sourceVec, vec255)
|
||||||
|
if (mm_movemask_epi8(eq255) and 0x8888) == 0x8888:
|
||||||
mm_storeu_si128(a.data[a.dataIndex(x + q, y)].addr, sourceVec)
|
mm_storeu_si128(a.data[a.dataIndex(x + q, y)].addr, sourceVec)
|
||||||
else:
|
else:
|
||||||
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
let
|
||||||
|
backdropIdx = a.dataIndex(x + q, y)
|
||||||
|
backdropVec = mm_loadu_si128(a.data[backdropIdx].addr)
|
||||||
mm_storeu_si128(
|
mm_storeu_si128(
|
||||||
a.data[a.dataIndex(x + q, y)].addr,
|
a.data[backdropIdx].addr,
|
||||||
blendNormalInlineSimd(backdropVec, sourceVec)
|
blendNormalInlineSimd(backdropVec, sourceVec)
|
||||||
)
|
)
|
||||||
else: # b is a Mask
|
else: # b is a Mask
|
||||||
var values = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
|
var values = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
|
||||||
for q in [0, 4, 8, 12]:
|
for q in [0, 4, 8, 12]:
|
||||||
let sourceVec = unpackAlphaValues(values)
|
let
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, mm_setzero_si128())) != 0xffff:
|
sourceVec = unpackAlphaValues(values)
|
||||||
if (mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) and 0x8888) == 0x8888:
|
eqZer0 = mm_cmpeq_epi8(sourceVec, mm_setzero_si128())
|
||||||
|
if mm_movemask_epi8(eqZer0) != 0xffff:
|
||||||
|
let eq255 = mm_cmpeq_epi8(sourceVec, vec255)
|
||||||
|
if (mm_movemask_epi8(eq255) and 0x8888) == 0x8888:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
let
|
||||||
|
backdropIdx = a.dataIndex(x + q, y)
|
||||||
|
backdropVec = mm_loadu_si128(a.data[backdropIdx].addr)
|
||||||
mm_storeu_si128(
|
mm_storeu_si128(
|
||||||
a.data[a.dataIndex(x + q, y)].addr,
|
a.data[backdropIdx].addr,
|
||||||
blendNormalInlineSimd(backdropVec, sourceVec)
|
blendNormalInlineSimd(backdropVec, sourceVec)
|
||||||
)
|
)
|
||||||
# Shuffle 32 bits off for the next iteration
|
# Shuffle 32 bits off for the next iteration
|
||||||
|
@ -885,9 +895,14 @@ proc drawUber(
|
||||||
when type(a) is Image:
|
when type(a) is Image:
|
||||||
when type(b) is Image:
|
when type(b) is Image:
|
||||||
for q in [0, 4, 8, 12]:
|
for q in [0, 4, 8, 12]:
|
||||||
let sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
let
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, mm_setzero_si128())) == 0xffff:
|
sourceVec = mm_loadu_si128(b.data[b.dataIndex(sx + q, sy)].addr)
|
||||||
mm_storeu_si128(a.data[a.dataIndex(x + q, y)].addr, mm_setzero_si128())
|
eqZer0 = mm_cmpeq_epi8(sourceVec, mm_setzero_si128())
|
||||||
|
if mm_movemask_epi8(eqZer0) == 0xffff:
|
||||||
|
mm_storeu_si128(
|
||||||
|
a.data[a.dataIndex(x + q, y)].addr,
|
||||||
|
mm_setzero_si128()
|
||||||
|
)
|
||||||
elif mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) != 0xffff:
|
elif mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) != 0xffff:
|
||||||
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
||||||
mm_storeu_si128(
|
mm_storeu_si128(
|
||||||
|
@ -897,10 +912,16 @@ proc drawUber(
|
||||||
else: # b is a Mask
|
else: # b is a Mask
|
||||||
var values = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
|
var values = mm_loadu_si128(b.data[b.dataIndex(sx, sy)].addr)
|
||||||
for q in [0, 4, 8, 12]:
|
for q in [0, 4, 8, 12]:
|
||||||
let sourceVec = unpackAlphaValues(values)
|
let
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, mm_setzero_si128())) == 0xffff:
|
sourceVec = unpackAlphaValues(values)
|
||||||
mm_storeu_si128(a.data[a.dataIndex(x + q, y)].addr, mm_setzero_si128())
|
eqZer0 = mm_cmpeq_epi8(sourceVec, mm_setzero_si128())
|
||||||
elif (mm_movemask_epi8(mm_cmpeq_epi8(sourceVec, vec255)) and 0x8888) != 0x8888:
|
eq255 = mm_cmpeq_epi8(sourceVec, vec255)
|
||||||
|
if mm_movemask_epi8(eqZer0) == 0xffff:
|
||||||
|
mm_storeu_si128(
|
||||||
|
a.data[a.dataIndex(x + q, y)].addr,
|
||||||
|
mm_setzero_si128()
|
||||||
|
)
|
||||||
|
elif (mm_movemask_epi8(eq255) and 0x8888) != 0x8888:
|
||||||
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
let backdropVec = mm_loadu_si128(a.data[a.dataIndex(x + q, y)].addr)
|
||||||
mm_storeu_si128(
|
mm_storeu_si128(
|
||||||
a.data[a.dataIndex(x + q, y)].addr,
|
a.data[a.dataIndex(x + q, y)].addr,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue