From d0109db23b742ac1a7ed44e0c47be86aad37336b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 28 Aug 2022 19:41:42 +0200 Subject: [PATCH 1/2] [TGA] Add support for Top-Right and Bottom-Right origins. --- core/image/common.odin | 9 +++++++-- core/image/tga/tga.odin | 32 ++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/core/image/common.odin b/core/image/common.odin index c79344445..7c8df3d57 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -379,8 +379,13 @@ QOI_Info :: struct { } TGA_Data_Type :: enum u8 { - Uncompressed_RGB = 2, - Compressed_RBB = 10, + No_Image_Data = 0, + Uncompressed_Color_Mapped = 1, + Uncompressed_RGB = 2, + Uncompressed_Black_White = 3, + Compressed_Color_Mapped = 9, + Compressed_RGB = 10, + Compressed_Black_White = 11, } TGA_Header :: struct #packed { diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin index d257d06ff..af5b07ef5 100644 --- a/core/image/tga/tga.odin +++ b/core/image/tga/tga.odin @@ -145,9 +145,23 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a rle_encoding := false switch header.data_type_code { - case .Compressed_RBB: rle_encoding = true + // Supported formats: RGB(A), RGB(A) RLE + case .Compressed_RGB: + rle_encoding = true case .Uncompressed_RGB: - case: return nil, .Unsupported_Format + + case .No_Image_Data: + return nil, .Unsupported_Format + case .Uncompressed_Color_Mapped: + return nil, .Unsupported_Format + case .Uncompressed_Black_White: + return nil, .Unsupported_Format + case .Compressed_Color_Mapped: + return nil, .Unsupported_Format + case .Compressed_Black_White: + return nil, .Unsupported_Format + case: + return nil, .Unsupported_Format } if header.bits_per_pixel != 24 && header.bits_per_pixel != 32 { @@ -208,7 +222,8 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return img, .Unable_To_Allocate_Or_Resize } - origin_is_topleft := header.image_descriptor & IMAGE_DESCRIPTOR_TOPLEFT_MASK != 0 + origin_is_top := header.image_descriptor & IMAGE_DESCRIPTOR_TOP_MASK != 0 + origin_is_left := header.image_descriptor & IMAGE_DESCRIPTOR_RIGHT_MASK == 0 rle_repetition_count := 0 read_pixel := true is_packet_rle := false @@ -216,10 +231,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a pixel: [4]u8 stride := img.width * img.channels - line := 0 if origin_is_topleft else img.height - 1 + line := 0 if origin_is_top else img.height - 1 for _ in 0.. Date: Sun, 28 Aug 2022 21:48:51 +0200 Subject: [PATCH 2/2] [TGA] Add B5G5R5 15- and 16-bit support. --- core/image/tga/tga.odin | 141 ++++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 42 deletions(-) diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin index af5b07ef5..d8b200b67 100644 --- a/core/image/tga/tga.odin +++ b/core/image/tga/tga.odin @@ -17,8 +17,6 @@ import "core:bytes" import "core:os" import "core:compress" import "core:strings" -import "core:fmt" -_ :: fmt // TODO: alpha_premultiply support @@ -142,29 +140,58 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a header := image.read_data(ctx, image.TGA_Header) or_return // Header checks - rle_encoding := false + rle_encoding := false + color_mapped := false + src_channels := 0 + dest_depth := header.bits_per_pixel + dest_channels := 0 - switch header.data_type_code { - // Supported formats: RGB(A), RGB(A) RLE - case .Compressed_RGB: - rle_encoding = true - case .Uncompressed_RGB: + #partial switch header.data_type_code { + // Supported formats: RGB(A), RGB(A) RLE + case .Compressed_RGB: + rle_encoding = true + case .Uncompressed_RGB: + // Intentionally blank + case .Uncompressed_Color_Mapped: + color_mapped = true - case .No_Image_Data: - return nil, .Unsupported_Format - case .Uncompressed_Color_Mapped: - return nil, .Unsupported_Format - case .Uncompressed_Black_White: - return nil, .Unsupported_Format - case .Compressed_Color_Mapped: - return nil, .Unsupported_Format - case .Compressed_Black_White: - return nil, .Unsupported_Format - case: - return nil, .Unsupported_Format + case: + return nil, .Unsupported_Format } - if header.bits_per_pixel != 24 && header.bits_per_pixel != 32 { + if color_mapped { + if header.color_map_type != 1 { + return nil, .Unsupported_Format + } + dest_depth = header.color_map_depth + + // Expect LUT entry index to be 8 bits + if header.bits_per_pixel != 8 || header.color_map_origin != 0 || header.color_map_length > 256 { + return nil, .Unsupported_Format + } + } + + switch dest_depth { + case 15: // B5G5R5 + src_channels = 2 + dest_channels = 3 + if color_mapped { + return nil, .Unsupported_Format + } + case 16: // B5G5R5A1 + src_channels = 2 + dest_channels = 3 // Alpha bit is dodgy in TGA, so we ignore it. + if color_mapped { + return nil, .Unsupported_Format + } + case 24: // RGB8 + src_channels = 3 if !color_mapped else 1 + dest_channels = 3 + case 32: // RGBA8 + src_channels = 4 if !color_mapped else 1 + dest_channels = 4 + + case: return nil, .Unsupported_Format } @@ -184,9 +211,8 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a destroy(img) } - src_channels := int(header.bits_per_pixel) / 8 img.which = .TGA - img.channels = 4 if .alpha_add_if_missing in options else src_channels + img.channels = 4 if .alpha_add_if_missing in options else dest_channels img.channels = 3 if .alpha_drop_if_present in options else img.channels img.depth = 8 @@ -196,7 +222,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a // Read Image ID if present image_id := "" if _id, e := compress.read_slice(ctx, int(header.id_length)); e != .None { - return nil, .Corrupt + return img, .Corrupt } else { if .return_metadata in options { id := strings.trim_right_null(string(_id)) @@ -204,6 +230,32 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a } } + color_map := make([]RGBA_Pixel, header.color_map_length) + defer delete(color_map) + + if color_mapped { + switch header.color_map_depth { + case 24: + for i in 0..> 5) & 31) << 3 + r := u8((v >> 10) & 31) << 3 + pixel = {r, g, b, 255} + case 3: + pixel = {src[2], src[1], src[0], 255} + case 4: + pixel = {src[2], src[1], src[0], src[3]} + case: + return img, .Corrupt } } // Write pixel - copy(img.pixels.buf[offset:], pixel[:img.channels]) - offset += img.channels if origin_is_left else -img.channels + copy(img.pixels.buf[offset:], pixel[:dest_channels]) + offset += dest_channels if origin_is_left else -dest_channels rle_repetition_count -= 1 } line += 1 if origin_is_top else -1