diff options
| author | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2022-04-18 23:28:34 +0200 |
|---|---|---|
| committer | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2022-04-18 23:28:34 +0200 |
| commit | fdd24f787fd6df5c1f3339d6317c76a1bafe60f5 (patch) | |
| tree | 08644f9a4b25fde2240535d24a5aa2ef228d7034 /core | |
| parent | df4a0c62add28af74ac8c926b3a6e0612296ccd1 (diff) | |
[image/tga] Writer for RGB(A) 8-bit images.
Diffstat (limited to 'core')
| -rw-r--r-- | core/image/common.odin | 14 | ||||
| -rw-r--r-- | core/image/tga/tga.odin | 103 |
2 files changed, 117 insertions, 0 deletions
diff --git a/core/image/common.odin b/core/image/common.odin index 2e7bca17e..8c77ec48a 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -320,6 +320,20 @@ QOI_Info :: struct { header: QOI_Header, } +TGA_Header :: struct #packed { + id_length: u8, + color_map_type: u8, + data_type_code: u8, + color_map_origin: u16le, + color_map_length: u16le, + color_map_depth: u8, + origin: [2]u16le, + dimensions: [2]u16le, + bits_per_pixel: u8, + image_descriptor: u8, +} +#assert(size_of(TGA_Header) == 18) + // Function to help with image buffer calculations compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) { size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin new file mode 100644 index 000000000..3c860cb62 --- /dev/null +++ b/core/image/tga/tga.odin @@ -0,0 +1,103 @@ +/* + Copyright 2022 Jeroen van Rijn <nom@duclavier.com>. + Made available under Odin's BSD-3 license. + + List of contributors: + Jeroen van Rijn: Initial implementation. +*/ + + +// package tga implements a TGA image writer for 8-bit RGB and RGBA images. +package tga + +import "core:mem" +import "core:image" +import "core:compress" +import "core:bytes" +import "core:os" + +Error :: image.Error +General :: compress.General_Error +Image :: image.Image +Options :: image.Options + +RGB_Pixel :: image.RGB_Pixel +RGBA_Pixel :: image.RGBA_Pixel + +save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + if img == nil { + return .Invalid_Input_Image + } + + if output == nil { + return .Invalid_Output + } + + pixels := img.width * img.height + if pixels == 0 || pixels > image.MAX_DIMENSIONS || img.width > 65535 || img.height > 65535 { + return .Invalid_Input_Image + } + + // Our TGA writer supports only 8-bit images with 3 or 4 channels. + if img.depth != 8 || img.channels < 3 || img.channels > 4 { + return .Invalid_Input_Image + } + + if img.channels * pixels != len(img.pixels.buf) { + return .Invalid_Input_Image + } + + written := 0 + + // Calculate and allocate necessary space. + necessary := pixels * img.channels + size_of(image.TGA_Header) + + if !resize(&output.buf, necessary) { + return General.Resize_Failed + } + + header := image.TGA_Header{ + data_type_code = 0x02, // Color, uncompressed. + dimensions = {u16le(img.width), u16le(img.height)}, + bits_per_pixel = u8(img.depth * img.channels), + image_descriptor = 1 << 5, // Origin is top left. + } + header_bytes := transmute([size_of(image.TGA_Header)]u8)header + + copy(output.buf[written:], header_bytes[:]) + written += size_of(image.TGA_Header) + + /* + Encode loop starts here. + */ + if img.channels == 3 { + pix := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGB_Pixel, output.buf[written:]) + for p, i in pix { + out[i] = p.bgr + } + } else if img.channels == 4 { + pix := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:]) + out := mem.slice_data_cast([]RGBA_Pixel, output.buf[written:]) + for p, i in pix { + out[i] = p.bgra + } + } + return nil +} + +save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + out := &bytes.Buffer{} + defer bytes.buffer_destroy(out) + + save_to_memory(out, img, options) or_return + write_ok := os.write_entire_file(output, out.buf[:]) + + return nil if write_ok else General.Cannot_Open_File +} + +save :: proc{save_to_memory, save_to_file}
\ No newline at end of file |