This commit is contained in:
gingerBill
2021-06-23 14:55:53 +01:00
2 changed files with 24 additions and 211 deletions

View File

@@ -1,187 +0,0 @@
package bytes
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
`bytes.Buffer` type conversion helpers.
*/
import "core:intrinsics"
import "core:mem"
need_endian_conversion :: proc($FT: typeid, $TT: typeid) -> (res: bool) {
// true if platform endian
f: bool;
t: bool;
when ODIN_ENDIAN == "little" {
f = intrinsics.type_is_endian_platform(FT) || intrinsics.type_is_endian_little(FT);
t = intrinsics.type_is_endian_platform(TT) || intrinsics.type_is_endian_little(TT);
return f != t;
} else {
f = intrinsics.type_is_endian_platform(FT) || intrinsics.type_is_endian_big(FT);
t = intrinsics.type_is_endian_platform(TT) || intrinsics.type_is_endian_big(TT);
return f != t;
}
return;
}
/*
Input:
count: number of elements
$TT: destination type
$FT: source type
from_buffer: buffer to convert
force_convert: cast each element separately
Output:
res: Converted/created buffer of []TT.
backing: ^bytes.Buffer{} backing the converted data.
alloc: Buffer was freshly allocated because we couldn't convert in-place. Points to `from_buffer` if `false`.
err: True if we passed too few elements or allocation failed, etc.
If `from_buffer` is empty, the input type $FT is ignored and `create_buffer_of_type` is called to create a fresh buffer.
This helper will try to do as little work as possible, so if you're converting between two equally sized types,
and they have compatible endianness, the contents will simply be reinterpreted using `slice_data_cast`.
If you want each element to be converted in this case, set `force_convert` to `true`.
For example, converting `[]u8{0, 60}` from `[]f16` to `[]u16` will return `[15360]` when simply reinterpreted,
and `[1]` if force converted.
Should you for example want to promote `[]f16` to `[]f32` (or truncate `[]f32` to `[]f16`), the size of these elements
being different will result in a conversion anyway, so this flag is unnecessary in cases like these.
Example:
fmt.println("Convert []f16le (x2) to []f32 (x2).");
b := []u8{0, 60, 0, 60}; // == []f16{1.0, 1.0}
res, backing, had_to_allocate, err := bytes.buffer_convert_to_type(2, f32, f16le, b);
fmt.printf("res : %v\n", res); // [1.000, 1.000]
fmt.printf("backing : %v\n", backing); // &Buffer{buf = [0, 0, 128, 63, 0, 0, 128, 63], off = 0, last_read = Invalid}
fmt.printf("allocated: %v\n", had_to_allocate); // true
fmt.printf("err : %v\n", err); // false
if had_to_allocate { defer bytes.buffer_destroy(backing); }
fmt.println("\nConvert []f16le (x2) to []u16 (x2).");
res2: []u16;
res2, backing, had_to_allocate, err = bytes.buffer_convert_to_type(2, u16, f16le, b);
fmt.printf("res : %v\n", res2); // [15360, 15360]
fmt.printf("backing : %v\n", backing); // Buffer.buf points to `b` because it could be converted in-place.
fmt.printf("allocated: %v\n", had_to_allocate); // false
fmt.printf("err : %v\n", err); // false
if had_to_allocate { defer bytes.buffer_destroy(backing); }
fmt.println("\nConvert []f16le (x2) to []u16 (x2), force_convert=true.");
res2, backing, had_to_allocate, err = bytes.buffer_convert_to_type(2, u16, f16le, b, true);
fmt.printf("res : %v\n", res2); // [1, 1]
fmt.printf("backing : %v\n", backing); // Buffer.buf points to `b` because it could be converted in-place.
fmt.printf("allocated: %v\n", had_to_allocate); // false
fmt.printf("err : %v\n", err); // false
if had_to_allocate { defer bytes.buffer_destroy(backing); }
*/
buffer_convert_to_type :: proc(count: int, $TT: typeid, $FT: typeid, from_buffer: []u8, force_convert := false) -> (
res: []TT, backing: ^Buffer, alloc: bool, err: bool) {
backing = new(Buffer);
if len(from_buffer) > 0 {
/*
Check if we've been given enough input elements.
*/
from := mem.slice_data_cast([]FT, from_buffer);
if len(from) != count {
err = true;
return;
}
/*
We can early out if the types are exactly identical.
This needs to be `when`, or res = from will fail if the types are different.
*/
when FT == TT {
res = from;
buffer_init(backing, from_buffer);
return;
}
/*
We can do a data cast if in-size == out-size and no endian conversion is needed.
*/
convert := need_endian_conversion(FT, TT);
convert |= (size_of(TT) * count != len(from_buffer));
convert |= force_convert;
if !convert {
// It's just a data cast
res = mem.slice_data_cast([]TT, from_buffer);
buffer_init(backing, from_buffer);
if len(res) != count {
err = true;
}
return;
} else {
if size_of(TT) * count == len(from_buffer) {
/*
Same size, can do an in-place Endianness conversion.
If `force_convert`, this also handles the per-element cast instead of slice_data_cast.
*/
res = mem.slice_data_cast([]TT, from_buffer);
buffer_init(backing, from_buffer);
for v, i in from {
res[i] = TT(v);
}
} else {
/*
Result is a different size, we need to allocate an output buffer.
*/
size := size_of(TT) * count;
buffer_init_allocator(backing, size, size, context.allocator);
alloc = true;
res = mem.slice_data_cast([]TT, backing.buf[:]);
if len(res) != count {
err = true;
return;
}
for v, i in from {
res[i] = TT(v);
}
}
}
} else {
/*
The input buffer is empty, so we'll have to create a new one for []TT of length count.
*/
res, backing, err = buffer_create_of_type(count, TT);
alloc = true;
}
return;
}
buffer_create_of_type :: proc(count: int, $TT: typeid) -> (res: []TT, backing: ^Buffer, err: bool) {
backing = new(Buffer);
size := size_of(TT) * count;
buffer_init_allocator(backing, size, size, context.allocator);
res = mem.slice_data_cast([]TT, backing.buf[:]);
if len(res) != count {
err = true;
}
return;
}

View File

@@ -9,12 +9,12 @@ package png
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
An example of how to use `png.load`.
An example of how to use `load`.
*/
import "core:compress"
import "core:image"
import "core:image/png"
// import "core:image/png"
import "core:bytes"
import "core:fmt"
@@ -31,33 +31,33 @@ main :: proc() {
file = "../../../misc/logo-slim.png";
img, err = png.load(file, options);
defer png.destroy(img);
img, err = load(file, options);
defer destroy(img);
if err != nil {
fmt.printf("Trying to read PNG file %v returned %v\n", file, err);
} else {
v: ^png.Info;
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 == png.Info {
v = (^png.Info)(img.metadata_ptr);
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, _ := png.core_time(c);
t, _ := core_time(c);
fmt.printf("[tIME]: %v\n", t);
case .gAMA:
fmt.printf("[gAMA]: %v\n", png.gamma(c));
fmt.printf("[gAMA]: %v\n", gamma(c));
case .pHYs:
phys := png.phys(c);
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 := png.phys_to_dpi(phys);
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);
@@ -65,7 +65,7 @@ main :: proc() {
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 := png.text(c);
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);
@@ -73,11 +73,11 @@ main :: proc() {
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text);
}
}
defer png.text_destroy(res);
defer text_destroy(res);
case .bKGD:
fmt.printf("[bKGD] %v\n", img.background);
case .eXIf:
res, ok_exif := png.exif(c);
res, ok_exif := exif(c);
if ok_exif {
/*
Other than checking the signature and byte order, we don't handle Exif data.
@@ -86,45 +86,45 @@ main :: proc() {
fmt.printf("[eXIf] %v\n", res);
}
case .PLTE:
plte, plte_ok := png.plte(c);
plte, plte_ok := plte(c);
if plte_ok {
fmt.printf("[PLTE] %v\n", plte);
} else {
fmt.printf("[PLTE] Error\n");
}
case .hIST:
res, ok_hist := png.hist(c);
res, ok_hist := hist(c);
if ok_hist {
fmt.printf("[hIST] %v\n", res);
}
case .cHRM:
res, ok_chrm := png.chrm(c);
res, ok_chrm := chrm(c);
if ok_chrm {
fmt.printf("[cHRM] %v\n", res);
}
case .sPLT:
res, ok_splt := png.splt(c);
res, ok_splt := splt(c);
if ok_splt {
fmt.printf("[sPLT] %v\n", res);
}
png.splt_destroy(res);
splt_destroy(res);
case .sBIT:
if res, ok_sbit := png.sbit(c); ok_sbit {
if res, ok_sbit := sbit(c); ok_sbit {
fmt.printf("[sBIT] %v\n", res);
}
case .iCCP:
res, ok_iccp := png.iccp(c);
res, ok_iccp := iccp(c);
if ok_iccp {
fmt.printf("[iCCP] %v\n", res);
}
png.iccp_destroy(res);
iccp_destroy(res);
case .sRGB:
if res, ok_srgb := png.srgb(c); ok_srgb {
if res, ok_srgb := srgb(c); ok_srgb {
fmt.printf("[sRGB] Rendering intent: %v\n", res);
}
case:
type := c.header.type;
name := png.chunk_type_to_name(&type);
name := chunk_type_to_name(&type);
fmt.printf("[%v]: %v\n", name, c.data);
}
}