Browse Source

Add additional error checking to helpers.

Jeroen van Rijn 4 years ago
parent
commit
21c6d691d8

+ 2 - 0
core/compress/common.odin

@@ -11,6 +11,7 @@ package compress
 import "core:io"
 import "core:io"
 import "core:image"
 import "core:image"
 import "core:bytes"
 import "core:bytes"
+import "core:mem"
 
 
 /*
 /*
 	These settings bound how much compression algorithms will allocate for their output buffer.
 	These settings bound how much compression algorithms will allocate for their output buffer.
@@ -47,6 +48,7 @@ when size_of(uintptr) == 8 {
 
 
 Error :: union {
 Error :: union {
 	General_Error,
 	General_Error,
+	mem.Allocator_Error,
 	Deflate_Error,
 	Deflate_Error,
 	ZLIB_Error,
 	ZLIB_Error,
 	GZIP_Error,
 	GZIP_Error,

+ 1 - 0
core/image/common.odin

@@ -131,6 +131,7 @@ Error :: enum {
 	Unknown_Interlace_Method,
 	Unknown_Interlace_Method,
 	Requested_Channel_Not_Present,
 	Requested_Channel_Not_Present,
 	Post_Processing_Error,
 	Post_Processing_Error,
+	Invalid_Chunk_Length,
 }
 }
 
 
 /*
 /*

+ 77 - 76
core/image/png/example.odin

@@ -41,7 +41,7 @@ main :: proc() {
 demo :: proc() {
 demo :: proc() {
 	file: string
 	file: string
 
 
-	options := image.Options{} // {.return_metadata};
+	options := image.Options{.return_metadata}
 	err:       compress.Error
 	err:       compress.Error
 	img:      ^image.Image
 	img:      ^image.Image
 
 
@@ -53,23 +53,26 @@ demo :: proc() {
 	if err != nil {
 	if err != nil {
 		fmt.printf("Trying to read PNG file %v returned %v\n", file, err)
 		fmt.printf("Trying to read PNG file %v returned %v\n", file, err)
 	} else {
 	} else {
-		v: ^Info
-
 		fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth)
 		fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth)
-		if img.metadata_ptr != nil && img.metadata_type == Info {
-			v = (^Info)(img.metadata_ptr)
-
-			// Handle ancillary chunks as you wish.
-			// We provide helper functions for a few types.
-			for c in v.chunks {
-				#partial switch c.header.type {
-				case .tIME:
-					t, _ := core_time(c)
+
+		assert(img.metadata_ptr != nil && img.metadata_type == Info)
+
+		v := (^Info)(img.metadata_ptr)
+
+		// Handle ancillary chunks as you wish.
+		// We provide helper functions for a few types.
+		for c in v.chunks {
+			#partial switch c.header.type {
+			case .tIME:
+				if t, t_ok := core_time(c); t_ok {
 					fmt.printf("[tIME]: %v\n", t)
 					fmt.printf("[tIME]: %v\n", t)
-				case .gAMA:
-					fmt.printf("[gAMA]: %v\n", gamma(c))
-				case .pHYs:
-					phys := phys(c)
+				}
+			case .gAMA:
+				if gama, gama_ok := gamma(c); gama_ok {
+					fmt.printf("[gAMA]: %v\n", gama)
+				}
+			case .pHYs:
+				if phys, phys_ok := phys(c); phys_ok {
 					if phys.unit == .Meter {
 					if phys.unit == .Meter {
 						xm    := f32(img.width)  / f32(phys.ppu_x)
 						xm    := f32(img.width)  / f32(phys.ppu_x)
 						ym    := f32(img.height) / f32(phys.ppu_y)
 						ym    := f32(img.height) / f32(phys.ppu_y)
@@ -80,73 +83,71 @@ demo :: proc() {
 					} else {
 					} else {
 						fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y)
 						fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y)
 					}
 					}
-				case .iTXt, .zTXt, .tEXt:
-					res, ok_text := text(c)
-					if ok_text {
-						if c.header.type == .iTXt {
-							fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text)
-						} else {
-							fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text)
-						}
-					}
-					defer text_destroy(res)
-				case .bKGD:
-					fmt.printf("[bKGD] %v\n", img.background)
-				case .eXIf:
-					res, ok_exif := exif(c)
-					if ok_exif {
-						/*
-							Other than checking the signature and byte order, we don't handle Exif data.
-							If you wish to interpret it, pass it to an Exif parser.
-						*/
-						fmt.printf("[eXIf] %v\n", res)
-					}
-				case .PLTE:
-					plte, plte_ok := plte(c)
-					if plte_ok {
-						fmt.printf("[PLTE] %v\n", plte)
+				}
+			case .iTXt, .zTXt, .tEXt:
+				res, ok_text := text(c)
+				if ok_text {
+					if c.header.type == .iTXt {
+						fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text)
 					} else {
 					} else {
-						fmt.printf("[PLTE] Error\n")
-					}
-				case .hIST:
-					res, ok_hist := hist(c)
-					if ok_hist {
-						fmt.printf("[hIST] %v\n", res)
-					}
-				case .cHRM:
-					res, ok_chrm := chrm(c)
-					if ok_chrm {
-						fmt.printf("[cHRM] %v\n", res)
-					}
-				case .sPLT:
-					res, ok_splt := splt(c)
-					if ok_splt {
-						fmt.printf("[sPLT] %v\n", res)
-					}
-					splt_destroy(res)
-				case .sBIT:
-					if res, ok_sbit := sbit(c); ok_sbit {
-						fmt.printf("[sBIT] %v\n", res)
+						fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text)
 					}
 					}
-				case .iCCP:
-					res, ok_iccp := iccp(c)
-					if ok_iccp {
-						fmt.printf("[iCCP] %v\n", res)
-					}
-					iccp_destroy(res)
-				case .sRGB:
-					if res, ok_srgb := srgb(c); ok_srgb {
-						fmt.printf("[sRGB] Rendering intent: %v\n", res)
-					}
-				case:
-					type := c.header.type
-					name := chunk_type_to_name(&type)
-					fmt.printf("[%v]: %v\n", name, c.data)
 				}
 				}
+				defer text_destroy(res)
+			case .bKGD:
+				fmt.printf("[bKGD] %v\n", img.background)
+			case .eXIf:
+				if res, ok_exif := exif(c); ok_exif {
+					/*
+						Other than checking the signature and byte order, we don't handle Exif data.
+						If you wish to interpret it, pass it to an Exif parser.
+					*/
+					fmt.printf("[eXIf] %v\n", res)
+				}
+			case .PLTE:
+				if plte, plte_ok := plte(c); plte_ok {
+					fmt.printf("[PLTE] %v\n", plte)
+				} else {
+					fmt.printf("[PLTE] Error\n")
+				}
+			case .hIST:
+				if res, ok_hist := hist(c); ok_hist {
+					fmt.printf("[hIST] %v\n", res)
+				}
+			case .cHRM:
+				if res, ok_chrm := chrm(c); ok_chrm {
+					fmt.printf("[cHRM] %v\n", res)
+				}
+			case .sPLT:
+				res, ok_splt := splt(c)
+				if ok_splt {
+					fmt.printf("[sPLT] %v\n", res)
+				}
+				splt_destroy(res)
+			case .sBIT:
+				if res, ok_sbit := sbit(c); ok_sbit {
+					fmt.printf("[sBIT] %v\n", res)
+				}
+			case .iCCP:
+				res, ok_iccp := iccp(c)
+				if ok_iccp {
+					fmt.printf("[iCCP] %v\n", res)
+				}
+				iccp_destroy(res)
+			case .sRGB:
+				if res, ok_srgb := srgb(c); ok_srgb {
+					fmt.printf("[sRGB] Rendering intent: %v\n", res)
+				}
+			case:
+				type := c.header.type
+				name := chunk_type_to_name(&type)
+				fmt.printf("[%v]: %v\n", name, c.data)
 			}
 			}
 		}
 		}
 	}
 	}
 
 
+	fmt.printf("Done parsing metadata.\n")
+
 	if err == nil && .do_not_decompress_image not_in options && .info not_in options {
 	if err == nil && .do_not_decompress_image not_in options && .info not_in options {
 		if ok := write_image_as_ppm("out.ppm", img); ok {
 		if ok := write_image_as_ppm("out.ppm", img); ok {
 			fmt.println("Saved decoded image.")
 			fmt.println("Saved decoded image.")

+ 38 - 34
core/image/png/helpers.odin

@@ -34,15 +34,17 @@ destroy :: proc(img: ^Image) {
 	}
 	}
 
 
 	bytes.buffer_destroy(&img.pixels)
 	bytes.buffer_destroy(&img.pixels)
-	// Clean up Info.
-	free(img.metadata_ptr)
 
 
-	/*
-		We don't need to do anything for the individual chunks.
-		They're allocated on the temp allocator, as is info.chunks
+	assert(img.metadata_ptr != nil && img.metadata_type == Info)
+	v := (^Info)(img.metadata_ptr)
 
 
-		See read_chunk.
-	*/
+	for chunk in &v.chunks {
+		delete(chunk.data)
+	}
+	delete(v.chunks)
+
+	// Clean up Info.
+	free(img.metadata_ptr)
 	free(img)
 	free(img)
 }
 }
 
 
@@ -50,46 +52,50 @@ destroy :: proc(img: ^Image) {
 	Chunk helpers
 	Chunk helpers
 */
 */
 
 
-gamma :: proc(c: Chunk) -> f32 {
-	assert(c.header.type == .gAMA)
-	res := (^gAMA)(raw_data(c.data))^
-	when true {
-		// Returns the wrong result on old backend
-		// Fixed for -llvm-api
-		return f32(res.gamma_100k) / 100_000.0
-	} else {
-		return f32(u32(res.gamma_100k)) / 100_000.0
+gamma :: proc(c: Chunk) -> (res: f32, ok: bool) {
+	if c.header.type != .gAMA || len(c.data) != size_of(gAMA) {
+		return {}, false
 	}
 	}
+	gama := (^gAMA)(raw_data(c.data))^
+	return f32(gama.gamma_100k) / 100_000.0, true
 }
 }
 
 
 INCHES_PER_METER :: 1000.0 / 25.4
 INCHES_PER_METER :: 1000.0 / 25.4
 
 
-phys :: proc(c: Chunk) -> pHYs {
-	assert(c.header.type == .pHYs)
-	res := (^pHYs)(raw_data(c.data))^
-	return res
+phys :: proc(c: Chunk) -> (res: pHYs, ok: bool) {
+	if c.header.type != .pHYs || len(c.data) != size_of(pHYs) {
+		return {}, false
+	}
+
+	return (^pHYs)(raw_data(c.data))^, true 
 }
 }
 
 
 phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) {
 phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) {
 	return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER
 	return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER
 }
 }
 
 
-time :: proc(c: Chunk) -> tIME {
-	assert(c.header.type == .tIME)
-	res := (^tIME)(raw_data(c.data))^
-	return res
+time :: proc(c: Chunk) -> (res: tIME, ok: bool) {
+	if c.header.type != .tIME || len(c.data) != size_of(tIME) {
+		return {}, false
+	}
+
+	return (^tIME)(raw_data(c.data))^, true
 }
 }
 
 
 core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
 core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
-	png_time := time(c)
-	using png_time
-	return coretime.datetime_to_time(
-		int(year), int(month), int(day),
-		int(hour), int(minute), int(second),
-	)
+	if png_time, png_ok := time(c); png_ok {
+		using png_time
+		return coretime.datetime_to_time(
+			int(year), int(month), int(day),
+			int(hour), int(minute), int(second),
+		)
+	} else {
+		return {}, false
+	}
 }
 }
 
 
 text :: proc(c: Chunk) -> (res: Text, ok: bool) {
 text :: proc(c: Chunk) -> (res: Text, ok: bool) {
+	assert(len(c.data) == int(c.header.length))
 	#partial switch c.header.type {
 	#partial switch c.header.type {
 	case .tEXt:
 	case .tEXt:
 		ok = true
 		ok = true
@@ -228,9 +234,7 @@ iccp_destroy :: proc(i: iCCP) {
 }
 }
 
 
 srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
 srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
-	ok = true
-
-	if c.header.type != .sRGB || len(c.data) != 1 {
+	if c.header.type != .sRGB || len(c.data) != size_of(sRGB_Rendering_Intent) {
 		return {}, false
 		return {}, false
 	}
 	}
 
 
@@ -238,7 +242,7 @@ srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
 	if res.intent > max(sRGB_Rendering_Intent) {
 	if res.intent > max(sRGB_Rendering_Intent) {
 		ok = false; return
 		ok = false; return
 	}
 	}
-	return
+	return res, true
 }
 }
 
 
 plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
 plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {

+ 48 - 23
core/image/png/png.odin

@@ -100,13 +100,13 @@ Chunk_Type :: enum u32be {
 }
 }
 
 
 IHDR :: struct #packed {
 IHDR :: struct #packed {
-	width: u32be,
-	height: u32be,
-	bit_depth: u8,
-	color_type: Color_Type,
+	width:              u32be,
+	height:             u32be,
+	bit_depth:          u8,
+	color_type:         Color_Type,
 	compression_method: u8,
 	compression_method: u8,
-	filter_method: u8,
-	interlace_method: Interlace_Method,
+	filter_method:      u8,
+	interlace_method:   Interlace_Method,
 }
 }
 IHDR_SIZE :: size_of(IHDR)
 IHDR_SIZE :: size_of(IHDR)
 #assert (IHDR_SIZE == 13)
 #assert (IHDR_SIZE == 13)
@@ -135,22 +135,22 @@ PLTE_Entry    :: [3]u8
 
 
 PLTE :: struct #packed {
 PLTE :: struct #packed {
 	entries: [256]PLTE_Entry,
 	entries: [256]PLTE_Entry,
-	used: u16,
+	used:    u16,
 }
 }
 
 
 hIST :: struct #packed {
 hIST :: struct #packed {
 	entries: [256]u16,
 	entries: [256]u16,
-	used: u16,
+	used:    u16,
 }
 }
 
 
 sPLT :: struct #packed {
 sPLT :: struct #packed {
-	name: string,
-	depth: u8,
+	name:    string,
+	depth:   u8,
 	entries: union {
 	entries: union {
 		[][4]u8,
 		[][4]u8,
 		[][4]u16,
 		[][4]u16,
 	},
 	},
-	used: u16,
+	used:    u16,
 }
 }
 
 
 // Other chunks
 // Other chunks
@@ -223,14 +223,14 @@ Exif :: struct {
 }
 }
 
 
 iCCP :: struct {
 iCCP :: struct {
-	name: string,
+	name:    string,
 	profile: []u8,
 	profile: []u8,
 }
 }
 
 
 sRGB_Rendering_Intent :: enum u8 {
 sRGB_Rendering_Intent :: enum u8 {
-	Perceptual = 0,
+	Perceptual            = 0,
 	Relative_colorimetric = 1,
 	Relative_colorimetric = 1,
-	Saturation = 2,
+	Saturation            = 2,
 	Absolute_colorimetric = 3,
 	Absolute_colorimetric = 3,
 }
 }
 
 
@@ -274,6 +274,35 @@ read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
 	return chunk, nil
 	return chunk, nil
 }
 }
 
 
+copy_chunk :: proc(src: Chunk, allocator := context.allocator) -> (dest: Chunk, err: Error) {
+	if int(src.header.length) != len(src.data) {
+		return {}, .Invalid_Chunk_Length
+	}
+
+	dest.header = src.header
+	dest.crc    = src.crc
+	dest.data   = make([]u8, dest.header.length, allocator) or_return
+
+	copy(dest.data[:], src.data[:])
+	return
+}
+
+append_chunk :: proc(list: ^[dynamic]Chunk, src: Chunk, allocator := context.allocator) -> (err: Error) {
+	if int(src.header.length) != len(src.data) {
+		return .Invalid_Chunk_Length
+	}
+
+	c := copy_chunk(src, allocator) or_return
+	length := len(list)
+	append(list, c)
+	if len(list) != length + 1 {
+		// Resize during append failed.
+		return .Out_Of_Memory
+	}
+
+	return
+}
+
 read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
 read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
 	c, e := read_chunk(ctx)
 	c, e := read_chunk(ctx)
 	if e != nil {
 	if e != nil {
@@ -422,8 +451,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 
 
 	header:	IHDR
 	header:	IHDR
 
 
-	info.chunks.allocator = context.temp_allocator
-
 	// State to ensure correct chunk ordering.
 	// State to ensure correct chunk ordering.
 	seen_ihdr := false; first := true
 	seen_ihdr := false; first := true
 	seen_plte := false
 	seen_plte := false
@@ -518,7 +545,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 			}
 			}
 
 
 			if .return_metadata in options {
 			if .return_metadata in options {
-				append(&info.chunks, c)
+				append_chunk(&info.chunks, c) or_return
 			}
 			}
 		case .IDAT:
 		case .IDAT:
 			// If we only want image metadata and don't want the pixel data, we can early out.
 			// If we only want image metadata and don't want the pixel data, we can early out.
@@ -563,7 +590,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 			c = read_chunk(ctx) or_return
 			c = read_chunk(ctx) or_return
 			seen_bkgd = true
 			seen_bkgd = true
 			if .return_metadata in options {
 			if .return_metadata in options {
-				append(&info.chunks, c)
+				append_chunk(&info.chunks, c) or_return
 			}
 			}
 
 
 			ct := transmute(u8)info.header.color_type
 			ct := transmute(u8)info.header.color_type
@@ -599,7 +626,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 			}
 			}
 
 
 			if .return_metadata in options {
 			if .return_metadata in options {
-				append(&info.chunks, c)
+				append_chunk(&info.chunks, c) or_return
 			}
 			}
 
 
 			/*
 			/*
@@ -626,16 +653,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
 			/*
 			/*
 				iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
 				iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
 				We're not going to add support for it. If you have the misfortunte of coming
 				We're not going to add support for it. If you have the misfortunte of coming
-				across one of these files, use a utility to defry it.s
+				across one of these files, use a utility to defry it.
 			*/
 			*/
 			return img, E_PNG.PNG_Does_Not_Adhere_to_Spec
 			return img, E_PNG.PNG_Does_Not_Adhere_to_Spec
 		case:
 		case:
 			// Unhandled type
 			// Unhandled type
 			c = read_chunk(ctx) or_return
 			c = read_chunk(ctx) or_return
-
 			if .return_metadata in options {
 			if .return_metadata in options {
-				// NOTE: Chunk cata is currently allocated on the temp allocator.
-				append(&info.chunks, c)
+				append_chunk(&info.chunks, c) or_return
 			}
 			}
 
 
 			first = false
 			first = false

+ 9 - 9
tests/core/image/test_core_image.odin

@@ -1514,10 +1514,10 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
 							case .gAMA:
 							case .gAMA:
 								switch(file.file) {
 								switch(file.file) {
 								case "pp0n2c16", "pp0n6a08":
 								case "pp0n2c16", "pp0n6a08":
-									gamma := png.gamma(c)
+									gamma, gamma_ok := png.gamma(c)
 									expected_gamma := f32(1.0)
 									expected_gamma := f32(1.0)
 									error  = fmt.tprintf("%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma)
 									error  = fmt.tprintf("%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma)
-									expect(t, gamma == expected_gamma, error)
+									expect(t, gamma == expected_gamma && gamma_ok, error)
 								}
 								}
 							case .PLTE:
 							case .PLTE:
 								switch(file.file) {
 								switch(file.file) {
@@ -1557,25 +1557,25 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
 									expect(t, expected_chrm == chrm && chrm_ok, error)
 									expect(t, expected_chrm == chrm && chrm_ok, error)
 								}
 								}
 							case .pHYs:
 							case .pHYs:
-								phys     := png.phys(c)
+								phys, phys_ok := png.phys(c)
 								phys_err := "%v test %v cHRM is %v, expected %v."
 								phys_err := "%v test %v cHRM is %v, expected %v."
 								switch (file.file) {
 								switch (file.file) {
 								case "cdfn2c08":
 								case "cdfn2c08":
 									expected_phys := png.pHYs{ppu_x =    1, ppu_y =    4, unit = .Unknown}
 									expected_phys := png.pHYs{ppu_x =    1, ppu_y =    4, unit = .Unknown}
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
-									expect(t, expected_phys == phys, error)
+									expect(t, expected_phys == phys && phys_ok, error)
 								case "cdhn2c08":
 								case "cdhn2c08":
 									expected_phys := png.pHYs{ppu_x =    4, ppu_y =    1, unit = .Unknown}
 									expected_phys := png.pHYs{ppu_x =    4, ppu_y =    1, unit = .Unknown}
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
-									expect(t, expected_phys == phys, error)
+									expect(t, expected_phys == phys && phys_ok, error)
 								case "cdsn2c08":
 								case "cdsn2c08":
 									expected_phys := png.pHYs{ppu_x =    1, ppu_y =    1, unit = .Unknown}
 									expected_phys := png.pHYs{ppu_x =    1, ppu_y =    1, unit = .Unknown}
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
-									expect(t, expected_phys == phys, error)
+									expect(t, expected_phys == phys && phys_ok, error)
 								case "cdun2c08":
 								case "cdun2c08":
 									expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter}
 									expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter}
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
 									error  = fmt.tprintf(phys_err, file.file, count, phys, expected_phys)
-									expect(t, expected_phys == phys, error)
+									expect(t, expected_phys == phys && phys_ok, error)
 								}
 								}
 							case .hIST:
 							case .hIST:
 								hist, hist_ok := png.hist(c)
 								hist, hist_ok := png.hist(c)
@@ -1589,7 +1589,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
 									expect(t, hist.used == 256 && hist_ok, error)
 									expect(t, hist.used == 256 && hist_ok, error)
 								}
 								}
 							case .tIME:
 							case .tIME:
-								png_time := png.time(c)
+								png_time, png_time_ok := png.time(c)
 								time_err := "%v test %v tIME was %v, expected %v."
 								time_err := "%v test %v tIME was %v, expected %v."
 								expected_time: png.tIME
 								expected_time: png.tIME
 
 
@@ -1610,7 +1610,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
 
 
 								}
 								}
 								error  = fmt.tprintf(time_err, file.file, count, png_time, expected_time)
 								error  = fmt.tprintf(time_err, file.file, count, png_time, expected_time)
-								expect(t, png_time == expected_time, error)
+								expect(t, png_time  == expected_time && png_time_ok,  error)
 
 
 								error  = fmt.tprintf(time_core_err, file.file, count, core_time, expected_core)
 								error  = fmt.tprintf(time_core_err, file.file, count, core_time, expected_core)
 								expect(t, core_time == expected_core && core_time_ok, error)
 								expect(t, core_time == expected_core && core_time_ok, error)