aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorftphikari <ftphikari@gmail.com>2022-04-19 06:00:30 +0300
committerGitHub <noreply@github.com>2022-04-19 06:00:30 +0300
commit240fb9b953eeabb07ff7bb75efa301da8c136d11 (patch)
treecd2eb9604f2e209db6f7816055d2e7ec732545e9 /core
parent4997a43763ca708700a9ec7171332c95095ad091 (diff)
parentd99ba9c073bcddce25a92d5af9afd59711df29d6 (diff)
Merge branch 'odin-lang:master' into master
Diffstat (limited to 'core')
-rw-r--r--core/container/lru/lru_cache.odin21
-rw-r--r--core/image/common.odin14
-rw-r--r--core/image/tga/tga.odin103
-rw-r--r--core/slice/slice.odin47
4 files changed, 179 insertions, 6 deletions
diff --git a/core/container/lru/lru_cache.odin b/core/container/lru/lru_cache.odin
index f8e6f7b46..81f0142b0 100644
--- a/core/container/lru/lru_cache.odin
+++ b/core/container/lru/lru_cache.odin
@@ -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 {
if e, ok := c.entries[key]; ok {
e.value = value
+ _pop_node(c, e)
+ _push_front_node(c, e)
return nil
}
@@ -67,10 +69,14 @@ set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Alloc
e.key = key
e.value = value
- _push_front_node(c, e)
- if c.count > c.capacity {
+ assert(c.count <= c.capacity)
+ if c.count == c.capacity {
_remove_node(c, c.tail)
}
+ else {
+ c.count += 1
+ }
+ _push_front_node(c, e)
c.entries[key] = e
return nil
@@ -122,6 +128,7 @@ remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
return false
}
_remove_node(c, e)
+ c.count -= 1
return true
}
@@ -143,8 +150,6 @@ _remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
node.prev = nil
node.next = nil
- c.count -= 1
-
delete_key(&c.entries, node.key)
_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
}
e.prev = nil
-
- c.count += 1
}
@(private)
@@ -180,6 +183,12 @@ _pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
if e == nil {
return
}
+ if c.head == e {
+ c.head = e.next
+ }
+ if c.tail == e {
+ c.tail = e.prev
+ }
if e.prev != nil {
e.prev.next = e.next
}
diff --git a/core/image/common.odin b/core/image/common.odin
index 2e7bca17e..8c77ec48a 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -320,6 +320,20 @@ QOI_Info :: struct {
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
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
diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin
new file mode 100644
index 000000000..3c860cb62
--- /dev/null
+++ b/core/image/tga/tga.odin
@@ -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} \ No newline at end of file
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index 520e3e1e0..b8fb29ab3 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -10,6 +10,53 @@ _ :: builtin
_ :: bits
_ :: 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) {
when size_of(E) > 8 {