Browse Source

[TGA] Add B&W and RLE color-mapped.

Jeroen van Rijn 3 years ago
parent
commit
3d4698debe
2 changed files with 92 additions and 12 deletions
  1. 35 4
      core/image/common.odin
  2. 57 8
      core/image/tga/tga.odin

+ 35 - 4
core/image/common.odin

@@ -407,14 +407,45 @@ New_TGA_Signature :: "TRUEVISION-XFILE.\x00"
 TGA_Footer :: struct #packed {
 	extension_area_offset:      u32le,
 	developer_directory_offset: u32le,
-	signature:                  [18]u8 `fmt:"s"`, // Should match signature if New TGA.
+	signature:                  [18]u8 `fmt:"s,0"`, // Should match signature if New TGA.
 }
 #assert(size_of(TGA_Footer) == 26)
 
+TGA_Extension :: struct #packed {
+	extension_size:          u16le,               // Size of this struct. If not 495 bytes it means it's an unsupported version.
+	author_name:             [41]u8  `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
+	author_comments:         [324]u8 `fmt:"s,0"`, // Author comments, formatted as 4 lines of 80 character lines, each zero terminated.
+	datetime:                struct {month, day, year, hour, minute, second: u16le},
+	job_name:                [41]u8  `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
+	job_time:                struct {hour, minute, second: u16le},
+	software_id:             [41]u8  `fmt:"s,0"`, // Software ID name, ASCII. Zero-terminated
+	software_version: struct #packed {
+		number: u16le, // Version number * 100
+		letter: u8 `fmt:"r"`,   // " " if not used
+	},
+	key_color:               [4]u8,    // ARGB key color used at time of production
+	aspect_ratio:            [2]u16le, // Numerator / Denominator
+	gamma:                   [2]u16le, // Numerator / Denominator, range should be 0.0..10.0
+	color_correction_offset: u32le,    // 0 if no color correction information
+	postage_stamp_offset:    u32le,    // 0 if no thumbnail
+	scanline_offset:         u32le,    // 0 if no scanline table
+	attributes:              TGA_Alpha_Kind,
+}
+#assert(size_of(TGA_Extension) == 495)
+
+TGA_Alpha_Kind :: enum u8 {
+	None,
+	Undefined_Ignore,
+	Undefined_Retain,
+	Useful,
+	Premultiplied,
+}
+
 TGA_Info :: struct {
-	header:   TGA_Header,
-	image_id: string,
-	footer:   Maybe(TGA_Footer),
+	header:    TGA_Header,
+	image_id:  string,
+	footer:    Maybe(TGA_Footer),
+	extension: Maybe(TGA_Extension),
 }
 
 // Function to help with image buffer calculations

+ 57 - 8
core/image/tga/tga.odin

@@ -24,6 +24,7 @@ Error   :: image.Error
 Image   :: image.Image
 Options :: image.Options
 
+GA_Pixel   :: image.GA_Pixel
 RGB_Pixel  :: image.RGB_Pixel
 RGBA_Pixel :: image.RGBA_Pixel
 
@@ -128,20 +129,33 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 	footer: image.TGA_Footer
 	have_valid_footer := false
 
+	extension: image.TGA_Extension
+	have_valid_extension := false
+
 	if filesize >= size_of(image.TGA_Header) + size_of(image.TGA_Footer) {
 		if f, f_err := compress.peek_data(ctx, image.TGA_Footer, filesize - i64(size_of(image.TGA_Footer))); f_err == .None {
 			if string(f.signature[:]) == image.New_TGA_Signature {
 				have_valid_footer = true
 				footer = f
+
+				if i64(footer.extension_area_offset) + i64(size_of(image.TGA_Extension)) < filesize {
+					if e, e_err := compress.peek_data(ctx, image.TGA_Extension, footer.extension_area_offset); e_err == .None {
+						if e.extension_size == size_of(image.TGA_Extension) {
+							have_valid_extension = true
+							extension = e
+						}
+					}
+				}
 			}
 		}
 	}
 
 	header := image.read_data(ctx, image.TGA_Header) or_return
-	
+
 	// Header checks
 	rle_encoding  := false
 	color_mapped  := false
+	black_white   := false
 	src_channels  := 0
 	dest_depth    := header.bits_per_pixel
 	dest_channels := 0
@@ -152,8 +166,18 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 		rle_encoding = true
 	case .Uncompressed_RGB:
 		// Intentionally blank
+	case .Uncompressed_Black_White:
+		black_white  = true
+		dest_depth   = 24
 	case .Uncompressed_Color_Mapped:
 		color_mapped = true
+	case .Compressed_Color_Mapped:
+		color_mapped = true
+		rle_encoding = true
+	case .Compressed_Black_White:
+		black_white  = true
+		rle_encoding = true
+		dest_depth   = 24
 
 	case:
 		return nil, .Unsupported_Format
@@ -176,16 +200,16 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 		src_channels  = 2
 		dest_channels = 3
 		if color_mapped {
-			return nil, .Unsupported_Format
+			src_channels = 1
 		}
 	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
+			src_channels = 1
 		}
 	case 24: // RGB8
-		src_channels  = 3 if !color_mapped else 1
+		src_channels  = 1 if (color_mapped || black_white) else 3
 		dest_channels = 3
 	case 32: // RGBA8
 		src_channels  = 4 if !color_mapped else 1
@@ -235,6 +259,16 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 
 	if color_mapped {
 		switch header.color_map_depth {
+		case 16:
+			for i in 0..<header.color_map_length {
+				if lut, lut_err := compress.read_data(ctx, GA_Pixel); lut_err != .None {
+					return img, .Corrupt
+				} else {
+					color_map[i].rg = lut
+					color_map[i].ba = 255
+				}
+			}
+
 		case 24:
 			for i in 0..<header.color_map_length {
 				if lut, lut_err := compress.read_data(ctx, RGB_Pixel); lut_err != .None {
@@ -263,6 +297,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 		if have_valid_footer {
 			info.footer = footer
 		}
+		if have_valid_extension {
+			info.extension = extension
+		}
 		img.metadata = info
 	}
 
@@ -312,15 +349,27 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 				}
 				switch src_channels {
 				case 1:
-					// Color mapped
-					pixel = color_map[src[0]].bgra
+					// Color-mapped or Black & White
+					if black_white {
+						pixel = {src[0], src[0], src[0], 255}
+					} else if header.color_map_depth == 24 {
+						pixel = color_map[src[0]].bgra
+					} else if header.color_map_depth == 16 {
+						lut := color_map[src[0]]
+						v := u16(lut.r) | u16(lut.g) << 8
+						b := u8( v        & 31) << 3
+						g := u8((v >>  5) & 31) << 3
+						r := u8((v >> 10) & 31) << 3
+						pixel = {r, g, b, 255}
+					}
+
 				case 2:
-					assert(dest_depth == 16)
-					v := int(src[0]) | int(src[1]) << 8
+					v := u16(src[0]) | u16(src[1]) << 8
 					b := u8( v        & 31) << 3
 					g := u8((v >>  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: