mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 22:58:46 +00:00
Merge branch 'odin-lang:master' into master
This commit is contained in:
@@ -60,6 +60,8 @@ clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
|
|||||||
set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
|
set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
|
||||||
if e, ok := c.entries[key]; ok {
|
if e, ok := c.entries[key]; ok {
|
||||||
e.value = value
|
e.value = value
|
||||||
|
_pop_node(c, e)
|
||||||
|
_push_front_node(c, e)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,10 +69,14 @@ set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Alloc
|
|||||||
e.key = key
|
e.key = key
|
||||||
e.value = value
|
e.value = value
|
||||||
|
|
||||||
_push_front_node(c, e)
|
assert(c.count <= c.capacity)
|
||||||
if c.count > c.capacity {
|
if c.count == c.capacity {
|
||||||
_remove_node(c, c.tail)
|
_remove_node(c, c.tail)
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
c.count += 1
|
||||||
|
}
|
||||||
|
_push_front_node(c, e)
|
||||||
|
|
||||||
c.entries[key] = e
|
c.entries[key] = e
|
||||||
return nil
|
return nil
|
||||||
@@ -122,6 +128,7 @@ remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_remove_node(c, e)
|
_remove_node(c, e)
|
||||||
|
c.count -= 1
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,8 +150,6 @@ _remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
|
|||||||
node.prev = nil
|
node.prev = nil
|
||||||
node.next = nil
|
node.next = nil
|
||||||
|
|
||||||
c.count -= 1
|
|
||||||
|
|
||||||
delete_key(&c.entries, node.key)
|
delete_key(&c.entries, node.key)
|
||||||
|
|
||||||
_call_on_remove(c, node)
|
_call_on_remove(c, node)
|
||||||
@@ -171,8 +176,6 @@ _push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
|||||||
c.tail = e
|
c.tail = e
|
||||||
}
|
}
|
||||||
e.prev = nil
|
e.prev = nil
|
||||||
|
|
||||||
c.count += 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -180,6 +183,12 @@ _pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
|||||||
if e == nil {
|
if e == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if c.head == e {
|
||||||
|
c.head = e.next
|
||||||
|
}
|
||||||
|
if c.tail == e {
|
||||||
|
c.tail = e.prev
|
||||||
|
}
|
||||||
if e.prev != nil {
|
if e.prev != nil {
|
||||||
e.prev.next = e.next
|
e.prev.next = e.next
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,6 +320,20 @@ QOI_Info :: struct {
|
|||||||
header: QOI_Header,
|
header: QOI_Header,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TGA_Header :: struct #packed {
|
||||||
|
id_length: u8,
|
||||||
|
color_map_type: u8,
|
||||||
|
data_type_code: u8,
|
||||||
|
color_map_origin: u16le,
|
||||||
|
color_map_length: u16le,
|
||||||
|
color_map_depth: u8,
|
||||||
|
origin: [2]u16le,
|
||||||
|
dimensions: [2]u16le,
|
||||||
|
bits_per_pixel: u8,
|
||||||
|
image_descriptor: u8,
|
||||||
|
}
|
||||||
|
#assert(size_of(TGA_Header) == 18)
|
||||||
|
|
||||||
// Function to help with image buffer calculations
|
// Function to help with image buffer calculations
|
||||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
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
|
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// package tga implements a TGA image writer for 8-bit RGB and RGBA images.
|
||||||
|
package tga
|
||||||
|
|
||||||
|
import "core:mem"
|
||||||
|
import "core:image"
|
||||||
|
import "core:compress"
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
Error :: image.Error
|
||||||
|
General :: compress.General_Error
|
||||||
|
Image :: image.Image
|
||||||
|
Options :: image.Options
|
||||||
|
|
||||||
|
RGB_Pixel :: image.RGB_Pixel
|
||||||
|
RGBA_Pixel :: image.RGBA_Pixel
|
||||||
|
|
||||||
|
save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
if img == nil {
|
||||||
|
return .Invalid_Input_Image
|
||||||
|
}
|
||||||
|
|
||||||
|
if output == nil {
|
||||||
|
return .Invalid_Output
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels := img.width * img.height
|
||||||
|
if pixels == 0 || pixels > image.MAX_DIMENSIONS || img.width > 65535 || img.height > 65535 {
|
||||||
|
return .Invalid_Input_Image
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our TGA writer supports only 8-bit images with 3 or 4 channels.
|
||||||
|
if img.depth != 8 || img.channels < 3 || img.channels > 4 {
|
||||||
|
return .Invalid_Input_Image
|
||||||
|
}
|
||||||
|
|
||||||
|
if img.channels * pixels != len(img.pixels.buf) {
|
||||||
|
return .Invalid_Input_Image
|
||||||
|
}
|
||||||
|
|
||||||
|
written := 0
|
||||||
|
|
||||||
|
// Calculate and allocate necessary space.
|
||||||
|
necessary := pixels * img.channels + size_of(image.TGA_Header)
|
||||||
|
|
||||||
|
if !resize(&output.buf, necessary) {
|
||||||
|
return General.Resize_Failed
|
||||||
|
}
|
||||||
|
|
||||||
|
header := image.TGA_Header{
|
||||||
|
data_type_code = 0x02, // Color, uncompressed.
|
||||||
|
dimensions = {u16le(img.width), u16le(img.height)},
|
||||||
|
bits_per_pixel = u8(img.depth * img.channels),
|
||||||
|
image_descriptor = 1 << 5, // Origin is top left.
|
||||||
|
}
|
||||||
|
header_bytes := transmute([size_of(image.TGA_Header)]u8)header
|
||||||
|
|
||||||
|
copy(output.buf[written:], header_bytes[:])
|
||||||
|
written += size_of(image.TGA_Header)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Encode loop starts here.
|
||||||
|
*/
|
||||||
|
if img.channels == 3 {
|
||||||
|
pix := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
|
||||||
|
out := mem.slice_data_cast([]RGB_Pixel, output.buf[written:])
|
||||||
|
for p, i in pix {
|
||||||
|
out[i] = p.bgr
|
||||||
|
}
|
||||||
|
} else if img.channels == 4 {
|
||||||
|
pix := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
|
||||||
|
out := mem.slice_data_cast([]RGBA_Pixel, output.buf[written:])
|
||||||
|
for p, i in pix {
|
||||||
|
out[i] = p.bgra
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
defer bytes.buffer_destroy(out)
|
||||||
|
|
||||||
|
save_to_memory(out, img, options) or_return
|
||||||
|
write_ok := os.write_entire_file(output, out.buf[:])
|
||||||
|
|
||||||
|
return nil if write_ok else General.Cannot_Open_File
|
||||||
|
}
|
||||||
|
|
||||||
|
save :: proc{save_to_memory, save_to_file}
|
||||||
@@ -10,6 +10,53 @@ _ :: builtin
|
|||||||
_ :: bits
|
_ :: bits
|
||||||
_ :: mem
|
_ :: mem
|
||||||
|
|
||||||
|
/*
|
||||||
|
Turn a pointer and a length into a slice.
|
||||||
|
*/
|
||||||
|
from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
|
||||||
|
return ([^]T)(ptr)[:count]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Turn a pointer and a length into a byte slice.
|
||||||
|
*/
|
||||||
|
bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
|
||||||
|
return ([^]byte)(ptr)[:byte_count]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Turn a slice into a byte slice.
|
||||||
|
|
||||||
|
See `slice.reinterpret` to go the other way.
|
||||||
|
*/
|
||||||
|
to_bytes :: proc "contextless" (s: []$T) -> []byte {
|
||||||
|
return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Turn a slice of one type, into a slice of another type.
|
||||||
|
|
||||||
|
Only converts the type and length of the slice itself.
|
||||||
|
The length is rounded down to the nearest whole number of items.
|
||||||
|
|
||||||
|
```
|
||||||
|
large_items := []i64{1, 2, 3, 4}
|
||||||
|
small_items := slice.reinterpret([]i32, large_items)
|
||||||
|
assert(len(small_items) == 8)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
2, 0, 0, 0}
|
||||||
|
large_items := slice.reinterpret([]i64, small_items)
|
||||||
|
assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
|
||||||
|
bytes := to_bytes(s)
|
||||||
|
n := len(bytes) / size_of(U)
|
||||||
|
return ([^]U)(raw_data(bytes))[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
swap :: proc(array: $T/[]$E, a, b: int) {
|
swap :: proc(array: $T/[]$E, a, b: int) {
|
||||||
when size_of(E) > 8 {
|
when size_of(E) > 8 {
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user