rakata_formats/tga/
writer.rs

1//! TGA (Targa) writer.
2
3use std::io::{Cursor, Write};
4
5use crate::binary::{write_u16, write_u8};
6
7use super::{validate_rgba_len, Tga, TgaBinaryError};
8
9/// Writes TGA data to a writer.
10///
11/// Output is always canonical: uncompressed true-color 32-bit BGRA with
12/// top-left origin and 8-bit alpha.
13#[cfg_attr(
14    feature = "tracing",
15    tracing::instrument(level = "debug", skip(writer, tga))
16)]
17pub fn write_tga<W: Write>(writer: &mut W, tga: &Tga) -> Result<(), TgaBinaryError> {
18    validate_rgba_len(tga.header.width, tga.header.height, &tga.rgba_pixels)?;
19    write_tga_canonical(writer, tga)
20}
21
22/// Serializes TGA data into a byte vector.
23#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(tga)))]
24pub fn write_tga_to_vec(tga: &Tga) -> Result<Vec<u8>, TgaBinaryError> {
25    let mut cursor = Cursor::new(Vec::new());
26    write_tga(&mut cursor, tga)?;
27    Ok(cursor.into_inner())
28}
29
30fn write_tga_canonical<W: Write>(writer: &mut W, tga: &Tga) -> Result<(), TgaBinaryError> {
31    let id_len = u8::try_from(tga.image_id.len())
32        .map_err(|_| TgaBinaryError::InvalidHeader("image_id is longer than 255 bytes".into()))?;
33
34    write_u8(writer, id_len)?;
35    write_u8(writer, 0)?; // no color map
36    write_u8(writer, 2)?; // uncompressed true-color
37
38    write_u16(writer, 0)?; // color_map_start
39    write_u16(writer, 0)?; // color_map_len
40    write_u8(writer, 0)?; // color_map_depth
41
42    write_u16(writer, 0)?; // x_origin
43    write_u16(writer, 0)?; // y_origin
44    write_u16(writer, tga.header.width)?;
45    write_u16(writer, tga.header.height)?;
46    write_u8(writer, 32)?; // pixel depth
47    write_u8(writer, 0x20 | 0x08)?; // top-left origin + 8-bit alpha
48    writer.write_all(&tga.image_id)?;
49    write_truecolor_uncompressed(writer, &tga.rgba_pixels, true)?;
50    Ok(())
51}
52
53fn write_truecolor_uncompressed<W: Write>(
54    writer: &mut W,
55    rgba_pixels: &[u8],
56    include_alpha: bool,
57) -> Result<(), TgaBinaryError> {
58    for rgba in rgba_pixels.chunks_exact(4) {
59        if include_alpha {
60            writer.write_all(&[rgba[2], rgba[1], rgba[0], rgba[3]])?;
61        } else {
62            writer.write_all(&[rgba[2], rgba[1], rgba[0]])?;
63        }
64    }
65    Ok(())
66}