///|
/// Helper to parse an integer from an ASCII string safely.
fn parse_ascii_int(s : String) -> Int? {
let len = s.length()
if len == 0 {
return None
}
let mut res = 0
let mut is_neg = false
let mut start = 0
if s[0] == '-' {
is_neg = true
start = 1
} else if s[0] == '+' {
start = 1
}
if start >= len {
return None
}
for i in start..= 48 && c <= 57 {
res = res * 10 + (c - 48)
} else {
return None
}
}
if is_neg {
Some(-res)
} else {
Some(res)
}
}
///|
/// Exports a 24-bit ColorImage to NetPBM PPM (P3 ASCII format) string.
pub fn ColorImage::to_ppm(self : ColorImage) -> String {
let buf = StringBuilder()
buf.write_string("P3\n")
buf.write_string(self.width.to_string())
buf.write_string(" ")
buf.write_string(self.height.to_string())
buf.write_string("\n255\n")
for y in 0.. Image? {
let lines = content.split("\n")
let tokens : Array[String] = []
for line in lines {
let trimmed = line.trim().to_owned()
// Skip comments
if !trimmed.has_prefix("#") && trimmed.length() > 0 {
let parts = trimmed.split(" ")
for p in parts {
let pt = p.trim().to_owned()
if pt.length() > 0 {
tokens.push(pt)
}
}
}
}
if tokens.length() < 4 || tokens[0] != "P2" {
return None
}
let width = match parse_ascii_int(tokens[1]) {
Some(w) => w
None => return None
}
let height = match parse_ascii_int(tokens[2]) {
Some(h) => h
None => return None
}
if width <= 0 || height <= 0 {
return None
}
let img = Image::new(width, height)
let expected_pixels = width * height
let offset = 4 // After P2, width, height, max_val
for i in 0.. {
let x = i % width
let y = i / width
img.set(x, y, v)
}
None => ()
}
}
}
Some(img)
}
///|
/// Parses an ASCII PPM (P3) string into a 24-bit ColorImage.
pub fn parse_ppm_ascii(content : String) -> ColorImage? {
let lines = content.split("\n")
let tokens : Array[String] = []
for line in lines {
let trimmed = line.trim().to_owned()
if !trimmed.has_prefix("#") && trimmed.length() > 0 {
let parts = trimmed.split(" ")
for p in parts {
let pt = p.trim().to_owned()
if pt.length() > 0 {
tokens.push(pt)
}
}
}
}
if tokens.length() < 4 || tokens[0] != "P3" {
return None
}
let width = match parse_ascii_int(tokens[1]) {
Some(w) => w
None => return None
}
let height = match parse_ascii_int(tokens[2]) {
Some(h) => h
None => return None
}
if width <= 0 || height <= 0 {
return None
}
let img = ColorImage::new(width, height)
let expected_pixels = width * height
let offset = 4
for i in 0.. v
None => 0
}
let g = match parse_ascii_int(tokens[tok_idx + 1]) {
Some(v) => v
None => 0
}
let b = match parse_ascii_int(tokens[tok_idx + 2]) {
Some(v) => v
None => 0
}
let x = i % width
let y = i / width
img.set(x, y, ColorRGB::new(r, g, b))
}
}
Some(img)
}