aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2021-10-07 13:41:26 +0100
committergingerBill <bill@gingerbill.org>2021-10-07 13:41:26 +0100
commitfd9adaf1dedfec7cf3b08fccb56d3c538d85c355 (patch)
tree599fc2d44d10dcdfa6cb5db2aea09029238e6393
parentaf7008aa447edd71cf45d3b8e501bbd05db12a46 (diff)
parentd3a18fbe9a668e15758da5f19b80496614e3c835 (diff)
Merge branch 'master' of https://github.com/odin-lang/Odin
-rw-r--r--core/compress/common.odin13
-rw-r--r--core/image/common.odin135
-rw-r--r--core/image/png/example.odin61
-rw-r--r--core/image/png/helpers.odin94
-rw-r--r--core/image/png/png.odin331
-rw-r--r--core/sys/win32/general.odin69
-rw-r--r--core/sys/win32/kernel32.odin2
-rw-r--r--core/sys/win32/user32.odin2
-rw-r--r--core/sys/win32/winmm.odin2
-rw-r--r--tests/core/image/build.bat4
-rw-r--r--tests/core/image/test_core_image.odin51
11 files changed, 465 insertions, 299 deletions
diff --git a/core/compress/common.odin b/core/compress/common.odin
index 683679566..41f292b6f 100644
--- a/core/compress/common.odin
+++ b/core/compress/common.odin
@@ -1,5 +1,3 @@
-package compress
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -7,10 +5,11 @@ package compress
List of contributors:
Jeroen van Rijn: Initial implementation, optimization.
*/
+package compress
import "core:io"
-import "core:image"
import "core:bytes"
+import "core:runtime"
/*
These settings bound how much compression algorithms will allocate for their output buffer.
@@ -51,11 +50,8 @@ Error :: union {
ZLIB_Error,
GZIP_Error,
ZIP_Error,
- /*
- This is here because png.load will return a this type of error union,
- as it may involve an I/O error, a Deflate error, etc.
- */
- image.Error,
+
+ runtime.Allocator_Error,
}
General_Error :: enum {
@@ -69,7 +65,6 @@ General_Error :: enum {
Incompatible_Options,
Unimplemented,
-
/*
Memory errors
*/
diff --git a/core/image/common.odin b/core/image/common.odin
index f30febc26..3ec8e15be 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -1,16 +1,17 @@
-package image
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
- Made available under Odin's BSD-2 license.
+ Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation, optimization.
Ginger Bill: Cosmetic changes.
*/
+package image
import "core:bytes"
import "core:mem"
+import "core:compress"
+import "core:runtime"
Image :: struct {
width: int,
@@ -25,8 +26,11 @@ Image :: struct {
*/
background: Maybe([3]u16),
- metadata_ptr: rawptr,
- metadata_type: typeid,
+ metadata: Image_Metadata,
+}
+
+Image_Metadata :: union {
+ ^PNG_Info,
}
/*
@@ -112,31 +116,140 @@ Option :: enum {
}
Options :: distinct bit_set[Option]
-Error :: enum {
+Error :: union {
+ General_Image_Error,
+ PNG_Error,
+
+ compress.Error,
+ compress.General_Error,
+ compress.Deflate_Error,
+ compress.ZLIB_Error,
+ runtime.Allocator_Error,
+}
+
+General_Image_Error :: enum {
+ None = 0,
+ Invalid_Image_Dimensions,
+ Image_Dimensions_Too_Large,
+ Image_Does_Not_Adhere_to_Spec,
+}
+
+PNG_Error :: enum {
Invalid_PNG_Signature,
IHDR_Not_First_Chunk,
IHDR_Corrupt,
IDAT_Missing,
IDAT_Must_Be_Contiguous,
IDAT_Corrupt,
- PNG_Does_Not_Adhere_to_Spec,
+ IDAT_Size_Too_Large,
PLTE_Encountered_Unexpectedly,
PLTE_Invalid_Length,
TRNS_Encountered_Unexpectedly,
BKGD_Invalid_Length,
- Invalid_Image_Dimensions,
Unknown_Color_Type,
Invalid_Color_Bit_Depth_Combo,
Unknown_Filter_Method,
Unknown_Interlace_Method,
Requested_Channel_Not_Present,
Post_Processing_Error,
+ Invalid_Chunk_Length,
}
/*
- Functions to help with image buffer calculations
+ PNG-specific structs
*/
+PNG_Info :: struct {
+ header: PNG_IHDR,
+ chunks: [dynamic]PNG_Chunk,
+}
+
+PNG_Chunk_Header :: struct #packed {
+ length: u32be,
+ type: PNG_Chunk_Type,
+}
+
+PNG_Chunk :: struct #packed {
+ header: PNG_Chunk_Header,
+ data: []byte,
+ crc: u32be,
+}
+
+PNG_Chunk_Type :: enum u32be {
+ // IHDR must come first in a file
+ IHDR = 'I' << 24 | 'H' << 16 | 'D' << 8 | 'R',
+ // PLTE must precede the first IDAT chunk
+ PLTE = 'P' << 24 | 'L' << 16 | 'T' << 8 | 'E',
+ bKGD = 'b' << 24 | 'K' << 16 | 'G' << 8 | 'D',
+ tRNS = 't' << 24 | 'R' << 16 | 'N' << 8 | 'S',
+ IDAT = 'I' << 24 | 'D' << 16 | 'A' << 8 | 'T',
+
+ iTXt = 'i' << 24 | 'T' << 16 | 'X' << 8 | 't',
+ tEXt = 't' << 24 | 'E' << 16 | 'X' << 8 | 't',
+ zTXt = 'z' << 24 | 'T' << 16 | 'X' << 8 | 't',
+
+ iCCP = 'i' << 24 | 'C' << 16 | 'C' << 8 | 'P',
+ pHYs = 'p' << 24 | 'H' << 16 | 'Y' << 8 | 's',
+ gAMA = 'g' << 24 | 'A' << 16 | 'M' << 8 | 'A',
+ tIME = 't' << 24 | 'I' << 16 | 'M' << 8 | 'E',
+
+ sPLT = 's' << 24 | 'P' << 16 | 'L' << 8 | 'T',
+ sRGB = 's' << 24 | 'R' << 16 | 'G' << 8 | 'B',
+ hIST = 'h' << 24 | 'I' << 16 | 'S' << 8 | 'T',
+ cHRM = 'c' << 24 | 'H' << 16 | 'R' << 8 | 'M',
+ sBIT = 's' << 24 | 'B' << 16 | 'I' << 8 | 'T',
+
+ /*
+ eXIf tags are not part of the core spec, but have been ratified
+ in v1.5.0 of the PNG Ext register.
+
+ We will provide unprocessed chunks to the caller if `.return_metadata` is set.
+ Applications are free to implement an Exif decoder.
+ */
+ eXIf = 'e' << 24 | 'X' << 16 | 'I' << 8 | 'f',
+
+ // PNG files must end with IEND
+ IEND = 'I' << 24 | 'E' << 16 | 'N' << 8 | 'D',
+
+ /*
+ XCode sometimes produces "PNG" files that don't adhere to the PNG spec.
+ We recognize them only in order to avoid doing further work on them.
+
+ Some tools like PNG Defry may be able to repair them, but we're not
+ going to reward Apple for producing proprietary broken files purporting
+ to be PNGs by supporting them.
+
+ */
+ iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T',
+ CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I',
+}
+PNG_IHDR :: struct #packed {
+ width: u32be,
+ height: u32be,
+ bit_depth: u8,
+ color_type: PNG_Color_Type,
+ compression_method: u8,
+ filter_method: u8,
+ interlace_method: PNG_Interlace_Method,
+}
+PNG_IHDR_SIZE :: size_of(PNG_IHDR)
+#assert (PNG_IHDR_SIZE == 13)
+
+PNG_Color_Value :: enum u8 {
+ Paletted = 0, // 1 << 0 = 1
+ Color = 1, // 1 << 1 = 2
+ Alpha = 2, // 1 << 2 = 4
+}
+PNG_Color_Type :: distinct bit_set[PNG_Color_Value; u8]
+
+PNG_Interlace_Method :: enum u8 {
+ None = 0,
+ Adam7 = 1,
+}
+
+/*
+ Functions 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
return
@@ -145,7 +258,6 @@ compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes
/*
For when you have an RGB(A) image, but want a particular channel.
*/
-
Channel :: enum u8 {
R = 1,
G = 2,
@@ -207,8 +319,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
res.depth = img.depth
res.pixels = t
res.background = img.background
- res.metadata_ptr = img.metadata_ptr
- res.metadata_type = img.metadata_type
+ res.metadata = img.metadata
return res, true
}
diff --git a/core/image/png/example.odin b/core/image/png/example.odin
index e27ea9671..5e7dca4c8 100644
--- a/core/image/png/example.odin
+++ b/core/image/png/example.odin
@@ -1,9 +1,6 @@
-//+ignore
-package png
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
- Made available under Odin's BSD-2 license.
+ Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
@@ -11,8 +8,9 @@ package png
An example of how to use `load`.
*/
+//+ignore
+package png
-import "core:compress"
import "core:image"
// import "core:image/png"
import "core:bytes"
@@ -41,8 +39,8 @@ main :: proc() {
demo :: proc() {
file: string
- options := image.Options{} // {.return_metadata};
- err: compress.Error
+ options := image.Options{.return_metadata}
+ err: image.Error
img: ^image.Image
file = "../../../misc/logo-slim.png"
@@ -53,32 +51,33 @@ demo :: proc() {
if err != nil {
fmt.printf("Trying to read PNG file %v returned %v\n", file, err)
} else {
- v: ^Info
-
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)
+ if v, ok := img.metadata.(^image.PNG_Info); ok {
// 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)
- fmt.printf("[tIME]: %v\n", t)
+ if t, t_ok := core_time(c); t_ok {
+ fmt.printf("[tIME]: %v\n", t)
+ }
case .gAMA:
- fmt.printf("[gAMA]: %v\n", gamma(c))
+ if gama, gama_ok := gamma(c); gama_ok {
+ fmt.printf("[gAMA]: %v\n", gama)
+ }
case .pHYs:
- phys := phys(c)
- if phys.unit == .Meter {
- xm := f32(img.width) / f32(phys.ppu_x)
- ym := f32(img.height) / f32(phys.ppu_y)
- dpi_x, dpi_y := phys_to_dpi(phys)
- fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y)
- fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y)
- fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym)
- } else {
- fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y)
+ if phys, phys_ok := phys(c); phys_ok {
+ if phys.unit == .Meter {
+ xm := f32(img.width) / f32(phys.ppu_x)
+ ym := f32(img.height) / f32(phys.ppu_y)
+ dpi_x, dpi_y := phys_to_dpi(phys)
+ fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y)
+ fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y)
+ fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym)
+ } else {
+ 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)
@@ -93,8 +92,7 @@ demo :: proc() {
case .bKGD:
fmt.printf("[bKGD] %v\n", img.background)
case .eXIf:
- res, ok_exif := exif(c)
- if ok_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.
@@ -102,20 +100,17 @@ demo :: proc() {
fmt.printf("[eXIf] %v\n", res)
}
case .PLTE:
- plte, plte_ok := plte(c)
- if plte_ok {
+ if plte, plte_ok := plte(c); plte_ok {
fmt.printf("[PLTE] %v\n", plte)
} else {
fmt.printf("[PLTE] Error\n")
}
case .hIST:
- res, ok_hist := hist(c)
- if ok_hist {
+ if res, ok_hist := hist(c); ok_hist {
fmt.printf("[hIST] %v\n", res)
}
case .cHRM:
- res, ok_chrm := chrm(c)
- if ok_chrm {
+ if res, ok_chrm := chrm(c); ok_chrm {
fmt.printf("[cHRM] %v\n", res)
}
case .sPLT:
@@ -147,6 +142,8 @@ demo :: proc() {
}
}
+ fmt.printf("Done parsing metadata.\n")
+
if err == nil && .do_not_decompress_image not_in options && .info not_in options {
if ok := write_image_as_ppm("out.ppm", img); ok {
fmt.println("Saved decoded image.")
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index 4d8cebedf..ecc0183bc 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -1,5 +1,3 @@
-package png
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
@@ -10,6 +8,7 @@ package png
These are a few useful utility functions to work with PNG images.
*/
+package png
import "core:image"
import "core:compress/zlib"
@@ -34,15 +33,14 @@ destroy :: proc(img: ^Image) {
}
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
-
- See read_chunk.
- */
+ if v, ok := img.metadata.(^image.PNG_Info); ok {
+ for chunk in &v.chunks {
+ delete(chunk.data)
+ }
+ delete(v.chunks)
+ free(v)
+ }
free(img)
}
@@ -50,46 +48,50 @@ destroy :: proc(img: ^Image) {
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: image.PNG_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
-phys :: proc(c: Chunk) -> pHYs {
- assert(c.header.type == .pHYs)
- res := (^pHYs)(raw_data(c.data))^
- return res
+phys :: proc(c: image.PNG_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) {
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: image.PNG_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) {
- png_time := time(c)
- using png_time
- return coretime.datetime_to_time(
- int(year), int(month), int(day),
- int(hour), int(minute), int(second),
- )
+core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
+ 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: image.PNG_Chunk) -> (res: Text, ok: bool) {
+ assert(len(c.data) == int(c.header.length))
#partial switch c.header.type {
case .tEXt:
ok = true
@@ -191,7 +193,7 @@ text_destroy :: proc(text: Text) {
delete(text.text)
}
-iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) {
+iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
ok = true
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
@@ -227,10 +229,8 @@ iccp_destroy :: proc(i: iCCP) {
}
-srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
- ok = true
-
- if c.header.type != .sRGB || len(c.data) != 1 {
+srgb :: proc(c: image.PNG_Chunk) -> (res: sRGB, ok: bool) {
+ if c.header.type != .sRGB || len(c.data) != size_of(sRGB_Rendering_Intent) {
return {}, false
}
@@ -238,10 +238,10 @@ srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
if res.intent > max(sRGB_Rendering_Intent) {
ok = false; return
}
- return
+ return res, true
}
-plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
+plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) {
if c.header.type != .PLTE {
return {}, false
}
@@ -255,7 +255,7 @@ plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
return
}
-splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) {
+splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
if c.header.type != .sPLT {
return {}, false
}
@@ -306,7 +306,7 @@ splt_destroy :: proc(s: sPLT) {
delete(s.name)
}
-sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
+sbit :: proc(c: image.PNG_Chunk) -> (res: [4]u8, ok: bool) {
/*
Returns [4]u8 with the significant bits in each channel.
A channel will contain zero if not applicable to the PNG color type.
@@ -324,7 +324,7 @@ sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
}
-hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
+hist :: proc(c: image.PNG_Chunk) -> (res: hIST, ok: bool) {
if c.header.type != .hIST {
return {}, false
}
@@ -346,7 +346,7 @@ hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
return
}
-chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
+chrm :: proc(c: image.PNG_Chunk) -> (res: cHRM, ok: bool) {
ok = true
if c.header.length != size_of(cHRM_Raw) {
return {}, false
@@ -364,7 +364,7 @@ chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
return
}
-exif :: proc(c: Chunk) -> (res: Exif, ok: bool) {
+exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
ok = true
diff --git a/core/image/png/png.odin b/core/image/png/png.odin
index 6e9102aaa..f77bf7519 100644
--- a/core/image/png/png.odin
+++ b/core/image/png/png.odin
@@ -1,13 +1,12 @@
-package png
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
- Made available under Odin's BSD-2 license.
+ Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
*/
+package png
import "core:compress"
import "core:compress/zlib"
@@ -21,11 +20,29 @@ import "core:io"
import "core:mem"
import "core:intrinsics"
-Error :: compress.Error
-E_General :: compress.General_Error
-E_PNG :: image.Error
-E_Deflate :: compress.Deflate_Error
+/*
+ 67_108_864 pixels max by default.
+ Maximum allowed dimensions are capped at 65535 * 65535.
+*/
+MAX_DIMENSIONS :: min(#config(PNG_MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
+
+/*
+ Limit chunk sizes.
+ By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
+*/
+_MAX_IDAT_DEFAULT :: ( 8192 /* Width */ * 8192 /* Height */ * 2 /* 16-bit */) + 8192 /* Filter bytes */
+_MAX_IDAT :: (65535 /* Width */ * 65535 /* Height */ * 2 /* 16-bit */) + 65535 /* Filter bytes */
+MAX_IDAT_SIZE :: min(#config(PNG_MAX_IDAT_SIZE, _MAX_IDAT_DEFAULT), _MAX_IDAT)
+
+/*
+ For chunks other than IDAT with a variable size like `zTXT` and `eXIf`,
+ limit their size to 16 MiB each by default. Max of 256 MiB each.
+*/
+MAX_CHUNK_SIZE :: min(#config(PNG_MAX_CHUNK_SIZE, 16_777_216), 268_435_456)
+
+
+Error :: image.Error
Image :: image.Image
Options :: image.Options
@@ -34,95 +51,6 @@ Signature :: enum u64be {
PNG = 0x89 << 56 | 'P' << 48 | 'N' << 40 | 'G' << 32 | '\r' << 24 | '\n' << 16 | 0x1a << 8 | '\n',
}
-Info :: struct {
- header: IHDR,
- chunks: [dynamic]Chunk,
-}
-
-Chunk_Header :: struct #packed {
- length: u32be,
- type: Chunk_Type,
-}
-
-Chunk :: struct #packed {
- header: Chunk_Header,
- data: []byte,
- crc: u32be,
-}
-
-Chunk_Type :: enum u32be {
- // IHDR must come first in a file
- IHDR = 'I' << 24 | 'H' << 16 | 'D' << 8 | 'R',
- // PLTE must precede the first IDAT chunk
- PLTE = 'P' << 24 | 'L' << 16 | 'T' << 8 | 'E',
- bKGD = 'b' << 24 | 'K' << 16 | 'G' << 8 | 'D',
- tRNS = 't' << 24 | 'R' << 16 | 'N' << 8 | 'S',
- IDAT = 'I' << 24 | 'D' << 16 | 'A' << 8 | 'T',
-
- iTXt = 'i' << 24 | 'T' << 16 | 'X' << 8 | 't',
- tEXt = 't' << 24 | 'E' << 16 | 'X' << 8 | 't',
- zTXt = 'z' << 24 | 'T' << 16 | 'X' << 8 | 't',
-
- iCCP = 'i' << 24 | 'C' << 16 | 'C' << 8 | 'P',
- pHYs = 'p' << 24 | 'H' << 16 | 'Y' << 8 | 's',
- gAMA = 'g' << 24 | 'A' << 16 | 'M' << 8 | 'A',
- tIME = 't' << 24 | 'I' << 16 | 'M' << 8 | 'E',
-
- sPLT = 's' << 24 | 'P' << 16 | 'L' << 8 | 'T',
- sRGB = 's' << 24 | 'R' << 16 | 'G' << 8 | 'B',
- hIST = 'h' << 24 | 'I' << 16 | 'S' << 8 | 'T',
- cHRM = 'c' << 24 | 'H' << 16 | 'R' << 8 | 'M',
- sBIT = 's' << 24 | 'B' << 16 | 'I' << 8 | 'T',
-
- /*
- eXIf tags are not part of the core spec, but have been ratified
- in v1.5.0 of the PNG Ext register.
-
- We will provide unprocessed chunks to the caller if `.return_metadata` is set.
- Applications are free to implement an Exif decoder.
- */
- eXIf = 'e' << 24 | 'X' << 16 | 'I' << 8 | 'f',
-
- // PNG files must end with IEND
- IEND = 'I' << 24 | 'E' << 16 | 'N' << 8 | 'D',
-
- /*
- XCode sometimes produces "PNG" files that don't adhere to the PNG spec.
- We recognize them only in order to avoid doing further work on them.
-
- Some tools like PNG Defry may be able to repair them, but we're not
- going to reward Apple for producing proprietary broken files purporting
- to be PNGs by supporting them.
-
- */
- iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T',
- CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I',
-}
-
-IHDR :: struct #packed {
- width: u32be,
- height: u32be,
- bit_depth: u8,
- color_type: Color_Type,
- compression_method: u8,
- filter_method: u8,
- interlace_method: Interlace_Method,
-}
-IHDR_SIZE :: size_of(IHDR)
-#assert (IHDR_SIZE == 13)
-
-Color_Value :: enum u8 {
- Paletted = 0, // 1 << 0 = 1
- Color = 1, // 1 << 1 = 2
- Alpha = 2, // 1 << 2 = 4
-}
-Color_Type :: distinct bit_set[Color_Value; u8]
-
-Interlace_Method :: enum u8 {
- None = 0,
- Adam7 = 1,
-}
-
Row_Filter :: enum u8 {
None = 0,
Sub = 1,
@@ -135,22 +63,22 @@ PLTE_Entry :: [3]u8
PLTE :: struct #packed {
entries: [256]PLTE_Entry,
- used: u16,
+ used: u16,
}
hIST :: struct #packed {
entries: [256]u16,
- used: u16,
+ used: u16,
}
sPLT :: struct #packed {
- name: string,
- depth: u8,
+ name: string,
+ depth: u8,
entries: union {
[][4]u8,
[][4]u16,
},
- used: u16,
+ used: u16,
}
// Other chunks
@@ -223,14 +151,14 @@ Exif :: struct {
}
iCCP :: struct {
- name: string,
+ name: string,
profile: []u8,
}
sRGB_Rendering_Intent :: enum u8 {
- Perceptual = 0,
+ Perceptual = 0,
Relative_colorimetric = 1,
- Saturation = 2,
+ Saturation = 2,
Absolute_colorimetric = 3,
}
@@ -245,16 +173,30 @@ ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 }
// Implementation starts here
-read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
- ch, e := compress.read_data(ctx, Chunk_Header)
+read_chunk :: proc(ctx: ^$C) -> (chunk: image.PNG_Chunk, err: Error) {
+ ch, e := compress.read_data(ctx, image.PNG_Chunk_Header)
if e != .None {
- return {}, E_General.Stream_Too_Short
+ return {}, compress.General_Error.Stream_Too_Short
}
chunk.header = ch
+ /*
+ Sanity check chunk size
+ */
+ #partial switch ch.type {
+ case .IDAT:
+ if ch.length > MAX_IDAT_SIZE {
+ return {}, image.PNG_Error.IDAT_Size_Too_Large
+ }
+ case:
+ if ch.length > MAX_CHUNK_SIZE {
+ return {}, image.PNG_Error.Invalid_Chunk_Length
+ }
+ }
+
chunk.data, e = compress.read_slice(ctx, int(ch.length))
if e != .None {
- return {}, E_General.Stream_Too_Short
+ return {}, compress.General_Error.Stream_Too_Short
}
// Compute CRC over chunk type + data
@@ -264,39 +206,68 @@ read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
crc, e3 := compress.read_data(ctx, u32be)
if e3 != .None {
- return {}, E_General.Stream_Too_Short
+ return {}, compress.General_Error.Stream_Too_Short
}
chunk.crc = crc
if chunk.crc != u32be(computed_crc) {
- return {}, E_General.Checksum_Failed
+ return {}, compress.General_Error.Checksum_Failed
}
return chunk, nil
}
-read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
+copy_chunk :: proc(src: image.PNG_Chunk, allocator := context.allocator) -> (dest: image.PNG_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]image.PNG_Chunk, src: image.PNG_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 mem.Allocator_Error.Out_Of_Memory
+ }
+
+ return
+}
+
+read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
c, e := read_chunk(ctx)
if e != nil {
return {}, e
}
- header := (^IHDR)(raw_data(c.data))^
+ header := (^image.PNG_IHDR)(raw_data(c.data))^
// Validate IHDR
using header
- if width == 0 || height == 0 {
- return {}, E_PNG.Invalid_Image_Dimensions
+ if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS {
+ return {}, .Invalid_Image_Dimensions
}
if compression_method != 0 {
- return {}, E_General.Unknown_Compression_Method
+ return {}, compress.General_Error.Unknown_Compression_Method
}
if filter_method != 0 {
- return {}, E_PNG.Unknown_Filter_Method
+ return {}, .Unknown_Filter_Method
}
if interlace_method != .None && interlace_method != .Adam7 {
- return {}, E_PNG.Unknown_Interlace_Method
+ return {}, .Unknown_Interlace_Method
}
@@ -314,7 +285,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
}
}
if !allowed {
- return {}, E_PNG.Invalid_Color_Bit_Depth_Combo
+ return {}, .Invalid_Color_Bit_Depth_Combo
}
case 2, 4, 6:
/*
@@ -322,7 +293,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
Allowed bit depths: 8 and 16
*/
if bit_depth != 8 && bit_depth != 16 {
- return {}, E_PNG.Invalid_Color_Bit_Depth_Combo
+ return {}, .Invalid_Color_Bit_Depth_Combo
}
case 3:
/*
@@ -337,17 +308,17 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
}
}
if !allowed {
- return {}, E_PNG.Invalid_Color_Bit_Depth_Combo
+ return {}, .Invalid_Color_Bit_Depth_Combo
}
case:
- return {}, E_PNG.Unknown_Color_Type
+ return {}, .Unknown_Color_Type
}
return header, nil
}
-chunk_type_to_name :: proc(type: ^Chunk_Type) -> string {
+chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string {
t := transmute(^u8)type
return strings.string_from_ptr(t, 4)
}
@@ -377,7 +348,7 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
return load_from_slice(data, options)
} else {
img = new(Image)
- return img, E_General.File_Not_Found
+ return img, compress.General_Error.File_Not_Found
}
}
@@ -391,7 +362,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
if .alpha_drop_if_present in options && .alpha_add_if_missing in options {
- return {}, E_General.Incompatible_Options
+ return {}, compress.General_Error.Incompatible_Options
}
if .do_not_expand_channels in options {
@@ -402,27 +373,25 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
img = new(Image)
}
- info := new(Info)
- img.metadata_ptr = info
- img.metadata_type = typeid_of(Info)
+ info := new(image.PNG_Info)
+ img.metadata = info
signature, io_error := compress.read_data(ctx, Signature)
if io_error != .None || signature != .PNG {
- return img, E_PNG.Invalid_PNG_Signature
+ return img, .Invalid_PNG_Signature
}
idat: []u8
idat_b: bytes.Buffer
- idat_length := u32be(0)
defer bytes.buffer_destroy(&idat_b)
- c: Chunk
- ch: Chunk_Header
- e: io.Error
+ idat_length := u64(0)
- header: IHDR
+ c: image.PNG_Chunk
+ ch: image.PNG_Chunk_Header
+ e: io.Error
- info.chunks.allocator = context.temp_allocator
+ header: image.PNG_IHDR
// State to ensure correct chunk ordering.
seen_ihdr := false; first := true
@@ -433,7 +402,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
seen_iend := false
_plte := PLTE{}
- trns := Chunk{}
+ trns := image.PNG_Chunk{}
final_image_channels := 0
@@ -443,16 +412,16 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// Peek at next chunk's length and type.
// TODO: Some streams may not provide seek/read_at
- ch, e = compress.peek_data(ctx, Chunk_Header)
+ ch, e = compress.peek_data(ctx, image.PNG_Chunk_Header)
if e != .None {
- return img, E_General.Stream_Too_Short
+ return img, compress.General_Error.Stream_Too_Short
}
// name := chunk_type_to_name(&ch.type); // Only used for debug prints during development.
#partial switch ch.type {
case .IHDR:
if seen_ihdr || !first {
- return {}, E_PNG.IHDR_Not_First_Chunk
+ return {}, .IHDR_Not_First_Chunk
}
seen_ihdr = true
@@ -481,14 +450,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
if img.channels == 0 || img.depth == 0 {
- return {}, E_PNG.IHDR_Corrupt
+ return {}, .IHDR_Corrupt
}
img.width = int(header.width)
img.height = int(header.height)
using header
- h := IHDR{
+ h := image.PNG_IHDR{
width = width,
height = height,
bit_depth = bit_depth,
@@ -498,28 +467,30 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
interlace_method = interlace_method,
}
info.header = h
+
case .PLTE:
seen_plte = true
// PLTE must appear before IDAT and can't appear for color types 0, 4.
ct := transmute(u8)info.header.color_type
if seen_idat || ct == 0 || ct == 4 {
- return img, E_PNG.PLTE_Encountered_Unexpectedly
+ return img, .PLTE_Encountered_Unexpectedly
}
c = read_chunk(ctx) or_return
if c.header.length % 3 != 0 || c.header.length > 768 {
- return img, E_PNG.PLTE_Invalid_Length
+ return img, .PLTE_Invalid_Length
}
plte_ok: bool
_plte, plte_ok = plte(c)
if !plte_ok {
- return img, E_PNG.PLTE_Invalid_Length
+ return img, .PLTE_Invalid_Length
}
if .return_metadata in options {
- append(&info.chunks, c)
+ append_chunk(&info.chunks, c) or_return
}
+
case .IDAT:
// If we only want image metadata and don't want the pixel data, we can early out.
if .return_metadata not_in options && .do_not_decompress_image in options {
@@ -528,11 +499,11 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
// There must be at least 1 IDAT, contiguous if more.
if seen_idat {
- return img, E_PNG.IDAT_Must_Be_Contiguous
+ return img, .IDAT_Must_Be_Contiguous
}
if idat_length > 0 {
- return img, E_PNG.IDAT_Must_Be_Contiguous
+ return img, .IDAT_Must_Be_Contiguous
}
next := ch.type
@@ -540,22 +511,29 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
c = read_chunk(ctx) or_return
bytes.buffer_write(&idat_b, c.data)
- idat_length += c.header.length
+ idat_length += u64(c.header.length)
- ch, e = compress.peek_data(ctx, Chunk_Header)
+ if idat_length > MAX_IDAT_SIZE {
+ return {}, image.PNG_Error.IDAT_Size_Too_Large
+ }
+
+ ch, e = compress.peek_data(ctx, image.PNG_Chunk_Header)
if e != .None {
- return img, E_General.Stream_Too_Short
+ return img, compress.General_Error.Stream_Too_Short
}
next = ch.type
}
+
idat = bytes.buffer_to_bytes(&idat_b)
if int(idat_length) != len(idat) {
- return {}, E_PNG.IDAT_Corrupt
+ return {}, .IDAT_Corrupt
}
seen_idat = true
+
case .IEND:
c = read_chunk(ctx) or_return
seen_iend = true
+
case .bKGD:
// TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
@@ -563,14 +541,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
c = read_chunk(ctx) or_return
seen_bkgd = true
if .return_metadata in options {
- append(&info.chunks, c)
+ append_chunk(&info.chunks, c) or_return
}
ct := transmute(u8)info.header.color_type
switch ct {
case 3: // Indexed color
if c.header.length != 1 {
- return {}, E_PNG.BKGD_Invalid_Length
+ return {}, .BKGD_Invalid_Length
}
col := _plte.entries[c.data[0]]
img.background = [3]u16{
@@ -580,26 +558,27 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
case 0, 4: // Grayscale, with and without Alpha
if c.header.length != 2 {
- return {}, E_PNG.BKGD_Invalid_Length
+ return {}, .BKGD_Invalid_Length
}
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
img.background = [3]u16{col, col, col}
case 2, 6: // Color, with and without Alpha
if c.header.length != 6 {
- return {}, E_PNG.BKGD_Invalid_Length
+ return {}, .BKGD_Invalid_Length
}
col := mem.slice_data_cast([]u16be, c.data[:])
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
}
+
case .tRNS:
c = read_chunk(ctx) or_return
if .Alpha in info.header.color_type {
- return img, E_PNG.TRNS_Encountered_Unexpectedly
+ return img, .TRNS_Encountered_Unexpectedly
}
if .return_metadata in options {
- append(&info.chunks, c)
+ append_chunk(&info.chunks, c) or_return
}
/*
@@ -622,20 +601,20 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
}
trns = c
+
case .iDOT, .CbGI:
/*
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
- 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, .Image_Does_Not_Adhere_to_Spec
+
case:
// Unhandled type
c = read_chunk(ctx) or_return
-
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
@@ -648,7 +627,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
if !seen_idat {
- return img, E_PNG.IDAT_Missing
+ return img, .IDAT_Missing
}
/*
@@ -685,7 +664,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
buf_len := len(buf.buf)
if expected_size != buf_len {
- return {}, E_PNG.IDAT_Corrupt
+ return {}, .IDAT_Corrupt
}
/*
@@ -752,7 +731,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// We need to create a new image buffer
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
t := bytes.Buffer{}
- resize(&t.buf, dest_raw_size)
+ if !resize(&t.buf, dest_raw_size) {
+ return {}, mem.Allocator_Error.Out_Of_Memory
+ }
i := 0; j := 0
@@ -831,7 +812,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// We need to create a new image buffer
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 16)
t := bytes.Buffer{}
- resize(&t.buf, dest_raw_size)
+ if !resize(&t.buf, dest_raw_size) {
+ return {}, mem.Allocator_Error.Out_Of_Memory
+ }
p16 := mem.slice_data_cast([]u16, temp.buf[:])
o16 := mem.slice_data_cast([]u16, t.buf[:])
@@ -1028,7 +1011,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// We need to create a new image buffer
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
t := bytes.Buffer{}
- resize(&t.buf, dest_raw_size)
+ if !resize(&t.buf, dest_raw_size) {
+ return {}, mem.Allocator_Error.Out_Of_Memory
+ }
p := mem.slice_data_cast([]u8, temp.buf[:])
o := mem.slice_data_cast([]u8, t.buf[:])
@@ -1524,7 +1509,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
return
}
-defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, options: Options) -> (err: compress.Error) {
+defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IHDR, options: Options) -> (err: Error) {
input := bytes.buffer_to_bytes(filter_bytes)
width := int(header.width)
height := int(header.height)
@@ -1535,7 +1520,9 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
bytes_per_channel := depth == 16 ? 2 : 1
num_bytes := compute_buffer_size(width, height, channels, depth == 16 ? 16 : 8)
- resize(&img.pixels.buf, num_bytes)
+ if !resize(&img.pixels.buf, num_bytes) {
+ return mem.Allocator_Error.Out_Of_Memory
+ }
filter_ok: bool
@@ -1560,7 +1547,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
}
if !filter_ok {
// Caller will destroy buffer for us.
- return E_PNG.Unknown_Filter_Method
+ return .Unknown_Filter_Method
}
} else {
/*
@@ -1575,7 +1562,9 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
if x > 0 && y > 0 {
temp: bytes.Buffer
temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8)
- resize(&temp.buf, temp_len)
+ if !resize(&temp.buf, temp_len) {
+ return mem.Allocator_Error.Out_Of_Memory
+ }
params := Filter_Params{
src = input,
@@ -1598,7 +1587,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
if !filter_ok {
// Caller will destroy buffer for us.
- return E_PNG.Unknown_Filter_Method
+ return .Unknown_Filter_Method
}
t := temp.buf[:]
diff --git a/core/sys/win32/general.odin b/core/sys/win32/general.odin
index 51f8c7d5f..d53bf8a4f 100644
--- a/core/sys/win32/general.odin
+++ b/core/sys/win32/general.odin
@@ -99,6 +99,57 @@ Rect :: struct {
bottom: i32,
}
+Dev_Mode_A :: struct {
+ device_name: [32]u8,
+ spec_version: u16,
+ driver_version: u16,
+ size: u16,
+ driver_extra: u16,
+ fields: u32,
+ using _: struct #raw_union {
+ // Printer only fields.
+ using _: struct {
+ orientation: i16,
+ paper_size: i16,
+ paper_length: i16,
+ paper_width: i16,
+ scale: i16,
+ copies: i16,
+ default_source: i16,
+ print_quality: i16,
+ },
+ // Display only fields.
+ using _: struct {
+ position: Point,
+ display_orientation: u32,
+ display_fixed_output: u32,
+ },
+ },
+ color: i16,
+ duplex: i16,
+ y_resolution: i16,
+ tt_option: i16,
+ collate: i16,
+ form_name: [32]u8,
+ log_pixels: u16,
+ bits_per_pel: u32,
+ pels_width: u32,
+ pels_height: u32,
+ using _: struct #raw_union {
+ display_flags: u32,
+ nup: u32,
+ },
+ display_frequency: u32,
+ icm_method: u32,
+ icm_intent: u32,
+ media_type: u32,
+ dither_type: u32,
+ reserved_1: u32,
+ reserved_2: u32,
+ panning_width: u32,
+ panning_height: u32,
+}
+
Filetime :: struct {
lo, hi: u32,
}
@@ -364,6 +415,9 @@ MAPVK_VK_TO_CHAR :: 2
MAPVK_VSC_TO_VK_EX :: 3
//WinUser.h
+ENUM_CURRENT_SETTINGS :: u32(4294967295) // (DWORD)-1
+ENUM_REGISTRY_SETTINGS :: u32(4294967294) // (DWORD)-2
+
VK_LBUTTON :: 0x01
VK_RBUTTON :: 0x02
VK_CANCEL :: 0x03
@@ -877,7 +931,20 @@ FILE_GENERIC_EXECUTE :: 0x20000000
FILE_GENERIC_WRITE :: 0x40000000
FILE_GENERIC_READ :: 0x80000000
-FILE_APPEND_DATA :: 0x0004
+FILE_READ_DATA :: 0x0001
+FILE_LIST_DIRECTORY :: 0x0001
+FILE_WRITE_DATA :: 0x0002
+FILE_ADD_FILE :: 0x0002
+FILE_APPEND_DATA :: 0x0004
+FILE_ADD_SUBDIRECTORY :: 0x0004
+FILE_CREATE_PIPE_INSTANCE :: 0x0004
+FILE_READ_EA :: 0x0008
+FILE_WRITE_EA :: 0x0010
+FILE_EXECUTE :: 0x0020
+FILE_TRAVERSE :: 0x0020
+FILE_DELETE_CHILD :: 0x0040
+FILE_READ_ATTRIBUTES :: 0x0080
+FILE_WRITE_ATTRIBUTES :: 0x0100
STD_INPUT_HANDLE :: -10
STD_OUTPUT_HANDLE :: -11
diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin
index 187bcd06f..709964fb4 100644
--- a/core/sys/win32/kernel32.odin
+++ b/core/sys/win32/kernel32.odin
@@ -106,6 +106,8 @@ foreign kernel32 {
bytes_returned: ^u32, overlapped: ^Overlapped,
completion: rawptr) -> Bool ---
+ @(link_name="GetOverlappedResult") get_overlapped_result :: proc(file: Handle, overlapped: ^Overlapped, number_of_bytes_transferred: ^u32, wait: Bool) -> Bool ---
+
@(link_name="WideCharToMultiByte") wide_char_to_multi_byte :: proc(code_page: u32, flags: u32,
wchar_str: Wstring, wchar: i32,
multi_str: cstring, multi: i32,
diff --git a/core/sys/win32/user32.odin b/core/sys/win32/user32.odin
index 57aea88aa..593fdbb8e 100644
--- a/core/sys/win32/user32.odin
+++ b/core/sys/win32/user32.odin
@@ -201,6 +201,8 @@ foreign user32 {
@(link_name="MapVirtualKeyExA") map_virtual_key_ex_a :: proc(code, map_type: u32, hkl: HKL) -> u32 ---
@(link_name="EnumDisplayMonitors") enum_display_monitors :: proc(hdc: Hdc, rect: ^Rect, enum_proc: Monitor_Enum_Proc, lparam: Lparam) -> bool ---
+
+ @(link_name="EnumDisplaySettingsA") enum_display_settings_a :: proc(device_name: cstring, mode_number: u32, mode: ^Dev_Mode_A) -> Bool ---
}
@(default_calling_convention = "std")
diff --git a/core/sys/win32/winmm.odin b/core/sys/win32/winmm.odin
index 05d099a1a..0f567fbcc 100644
--- a/core/sys/win32/winmm.odin
+++ b/core/sys/win32/winmm.odin
@@ -6,5 +6,7 @@ foreign import "system:winmm.lib"
@(default_calling_convention = "std")
foreign winmm {
+ @(link_name="timeBeginPeriod") time_begin_period :: proc(period: u32) -> u32 ---
+
@(link_name="timeGetTime") time_get_time :: proc() -> u32 ---
}
diff --git a/tests/core/image/build.bat b/tests/core/image/build.bat
new file mode 100644
index 000000000..03ee6b9a5
--- /dev/null
+++ b/tests/core/image/build.bat
@@ -0,0 +1,4 @@
+@echo off
+pushd ..
+odin run image
+popd \ No newline at end of file
diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin
index 339faff58..155b69298 100644
--- a/tests/core/image/test_core_image.odin
+++ b/tests/core/image/test_core_image.odin
@@ -1,5 +1,3 @@
-package test_core_image
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -9,6 +7,7 @@ package test_core_image
A test suite for PNG.
*/
+package test_core_image
import "core:testing"
@@ -64,7 +63,7 @@ PNG_Test :: struct {
file: string,
tests: []struct {
options: image.Options,
- expected_error: compress.Error,
+ expected_error: image.Error,
dims: PNG_Dims,
hash: u32,
},
@@ -1198,37 +1197,37 @@ Corrupt_PNG_Tests := []PNG_Test{
{
"xs1n0g01", // signature byte 1 MSBit reset to zero
{
- {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000},
+ {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
},
},
{
"xs2n0g01", // signature byte 2 is a 'Q'
{
- {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000},
+ {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
},
},
{
"xs4n0g01", // signature byte 4 lowercase
{
- {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000},
+ {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
},
},
{
"xs7n0g01", // 7th byte a space instead of control-Z
{
- {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000},
+ {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
},
},
{
"xcrn0g04", // added cr bytes
{
- {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000},
+ {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
},
},
{
"xlfn0g04", // added lf bytes
{
- {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000},
+ {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
},
},
{
@@ -1240,37 +1239,37 @@ Corrupt_PNG_Tests := []PNG_Test{
{
"xc1n0g08", // color type 1
{
- {Default, I_Error.Unknown_Color_Type, {}, 0x_0000_0000},
+ {Default, .Unknown_Color_Type, {}, 0x_0000_0000},
},
},
{
"xc9n2c08", // color type 9
{
- {Default, I_Error.Unknown_Color_Type, {}, 0x_0000_0000},
+ {Default, .Unknown_Color_Type, {}, 0x_0000_0000},
},
},
{
"xd0n2c08", // bit-depth 0
{
- {Default, I_Error.Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
+ {Default, .Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
},
},
{
"xd3n2c08", // bit-depth 3
{
- {Default, I_Error.Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
+ {Default, .Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
},
},
{
"xd9n2c08", // bit-depth 99
{
- {Default, I_Error.Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
+ {Default, .Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
},
},
{
"xdtn0g01", // missing IDAT chunk
{
- {Default, I_Error.IDAT_Missing, {}, 0x_0000_0000},
+ {Default, .IDAT_Missing, {}, 0x_0000_0000},
},
},
{
@@ -1505,19 +1504,17 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
passed &= test.hash == hash
if .return_metadata in test.options {
- v: ^png.Info
- if img.metadata_ptr != nil && img.metadata_type == png.Info {
- v = (^png.Info)(img.metadata_ptr)
+ if v, ok := img.metadata.(^image.PNG_Info); ok {
for c in v.chunks {
#partial switch(c.header.type) {
case .gAMA:
switch(file.file) {
case "pp0n2c16", "pp0n6a08":
- gamma := png.gamma(c)
+ gamma, gamma_ok := png.gamma(c)
expected_gamma := f32(1.0)
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:
switch(file.file) {
@@ -1557,25 +1554,25 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
expect(t, expected_chrm == chrm && chrm_ok, error)
}
case .pHYs:
- phys := png.phys(c)
+ phys, phys_ok := png.phys(c)
phys_err := "%v test %v cHRM is %v, expected %v."
switch (file.file) {
case "cdfn2c08":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 4, unit = .Unknown}
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":
expected_phys := png.pHYs{ppu_x = 4, ppu_y = 1, unit = .Unknown}
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":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 1, unit = .Unknown}
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":
expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter}
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:
hist, hist_ok := png.hist(c)
@@ -1589,7 +1586,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
expect(t, hist.used == 256 && hist_ok, error)
}
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."
expected_time: png.tIME
@@ -1610,7 +1607,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)
- 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)
expect(t, core_time == expected_core && core_time_ok, error)