v / vlib / stbi / stbi.c.v
269 lines · 237 sloc · 9.99 KB · 77e978cae58fd2c701bd0cd73168dc28baa2a21f
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module stbi
6
7import os
8
9@[if trace_stbi_allocations ?]
10fn trace_allocation(message string) {
11 eprintln(message)
12}
13
14@[export: 'stbi__callback_malloc']
15fn cb_malloc(s usize) voidptr {
16 res := unsafe { malloc(isize(s)) }
17 trace_allocation('> stbi__callback_malloc: ${s} => ${ptr_str(res)}')
18 return res
19}
20
21@[export: 'stbi__callback_realloc']
22fn cb_realloc(p voidptr, s usize) voidptr {
23 res := unsafe { v_realloc(p, isize(s)) }
24 trace_allocation('> stbi__callback_realloc: ${ptr_str(p)} , ${s} => ${ptr_str(res)}')
25 return res
26}
27
28@[export: 'stbi__callback_free']
29fn cb_free(p voidptr) {
30 trace_allocation('> stbi__callback_free: ${ptr_str(p)}')
31 unsafe { free(p) }
32}
33
34#flag -I @VEXEROOT/thirdparty/stb_image
35#include "stb_image.h"
36#include "stb_image_write.h"
37#include "stb_image_resize2.h"
38#include "stb_v_header.h"
39#flag @VEXEROOT/thirdparty/stb_image/stbi.o
40
41// Image represents an image loaded from file or memory, or an image, produced after resizing
42pub struct Image {
43pub mut:
44 width int // the width in pixels in the .data
45 height int // the height in pixels in the .data
46 nr_channels int // the number of color channels in the .data
47 ok bool // if the image was loaded successfully
48 data &u8 = unsafe { nil } // the actual data/pixels in the image, after reading and potentially converting the image
49 ext string // the extension of the file, from which the image was loaded
50 //
51 original_nr_channels int // when loaded from memory/disk, this field will contain the original number of channels, based on the data, prior to any conversions. Use only as metadata, not for further conversions.
52}
53
54//-----------------------------------------------------------------------------
55//
56// Configuration functions
57//
58//-----------------------------------------------------------------------------
59fn C.stbi_set_flip_vertically_on_load(should_flip i32)
60fn C.stbi_flip_vertically_on_write(flag i32)
61fn C.set_png_compression_level(level i32)
62fn C.write_force_png_filter(level i32)
63fn C.write_tga_with_rle(level i32)
64
65pub fn set_flip_vertically_on_load(val bool) {
66 C.stbi_set_flip_vertically_on_load(val)
67}
68
69pub fn set_flip_vertically_on_write(val bool) {
70 C.stbi_flip_vertically_on_write(val)
71}
72
73// set_png_compression_level set the PNG compression level during the writing process
74// defaults to 8; set to higher for more compression
75pub fn set_png_compression_level(level int) {
76 C.set_png_compression_level(level)
77}
78
79// write_force_png_filter defaults to -1; set to 0..5 to force a filter mode
80// the filter algorithms that can be applied before compression. The purpose of these filters is to prepare the image data for optimum compression.
81// Type Name
82//
83// 0 None
84// 1 Sub
85// 2 Up
86// 3 Average
87// 4 Paeth
88pub fn write_force_png_filter(level int) {
89 C.write_force_png_filter(level)
90}
91
92// stbi_write_tga_with_rle enable/disable the TGA RLE during the writing process
93// defaults to true; set to false to disable RLE in tga
94pub fn write_tga_with_rle(flag bool) {
95 C.write_tga_with_rle(if flag { 1 } else { 0 })
96}
97
98//-----------------------------------------------------------------------------
99//
100// Utility functions
101//
102//-----------------------------------------------------------------------------
103fn C.stbi_image_free(retval_from_stbi_load &u8)
104
105pub fn (img &Image) free() {
106 C.stbi_image_free(img.data)
107}
108
109//-----------------------------------------------------------------------------
110//
111// Load functions
112//
113//-----------------------------------------------------------------------------
114fn C.stbi_load(filename &char, x &int, y &int, channels_in_file &int, desired_channels i32) &u8
115fn C.stbi_load_from_file(f voidptr, x &int, y &int, channels_in_file &int, desired_channels i32) &u8
116fn C.stbi_load_from_memory(buffer &u8, len i32, x &int, y &int, channels_in_file &int, desired_channels i32) &u8
117
118pub const C.STBI_rgb_alpha int
119
120@[params]
121pub struct LoadParams {
122pub:
123 desired_channels int = C.STBI_rgb_alpha // 4 by default (RGBA); desired_channels is the number of color channels, that will be used for representing the image in memory. If set to 0, stbi will figure out the number of channels, based on the original image data.
124}
125
126// load loads an image from `path`
127// If you do not pass desired_channels: explicitly, it will default to 4 (RGBA),
128// The image, will get converted into that internal format, no matter what it was on disk.
129// Use desired_channels:0, if you need to keep the channels of the image on disk.
130// Note that displaying such an image, with gg/sokol later, can be a problem.
131// Converting/resizing it, should work fine though.
132pub fn load(path string, params LoadParams) !Image {
133 ext := path.all_after_last('.')
134 $if windows {
135 $if clang {
136 // stb's file-based loader crashes on Windows when compiled with clang.
137 // Read the file through V and decode the in-memory bytes instead.
138 file_bytes := os.read_bytes(path) or {
139 return error('stbi_image failed to load from "${path}"')
140 }
141 mut img := load_from_memory(file_bytes.data, file_bytes.len, params) or {
142 return error('stbi_image failed to load from "${path}"')
143 }
144 img.ext = ext
145 return img
146 }
147 }
148 mut res := Image{
149 ok: true
150 ext: ext
151 nr_channels: params.desired_channels
152 }
153 res.data = C.stbi_load(&char(path.str), &res.width, &res.height, &res.original_nr_channels,
154 params.desired_channels)
155 if params.desired_channels == 0 {
156 res.nr_channels = res.original_nr_channels
157 }
158 if isnil(res.data) {
159 return error('stbi_image failed to load from "${path}"')
160 }
161 return res
162}
163
164// load_from_memory load an image from a memory buffer
165// If you do not pass desired_channels: explicitly, it will default to 4 (RGBA),
166// and the image will get converted into that internal format, no matter what it was originally.
167// Use desired_channels:0, if you need to keep the channels of the image as they were.
168// Note that displaying such an image, with gg/sokol later, can be a problem.
169// Converting/resizing it, should work fine though.
170pub fn load_from_memory(buf &u8, bufsize int, params LoadParams) !Image {
171 mut res := Image{
172 ok: true
173 nr_channels: params.desired_channels
174 }
175 res.data = C.stbi_load_from_memory(buf, bufsize, &res.width, &res.height,
176 &res.original_nr_channels, params.desired_channels)
177 if params.desired_channels == 0 {
178 res.nr_channels = res.original_nr_channels
179 }
180 if isnil(res.data) {
181 return error('stbi_image failed to load from memory')
182 }
183 return res
184}
185
186//-----------------------------------------------------------------------------
187//
188// Resize functions
189//
190//-----------------------------------------------------------------------------
191fn C.stbir_resize_uint8_linear(input_pixels &u8, input_w i32, input_h i32, input_stride_in_bytes i32, output_pixels &u8,
192 output_w i32, output_h i32, output_stride_in_bytes i32, num_channels i32) i32
193
194// resize_uint8 resizes `img` to dimensions of `output_w` and `output_h`
195pub fn resize_uint8(img &Image, output_w int, output_h int) !Image {
196 mut res := Image{
197 ok: true
198 ext: img.ext
199 width: output_w
200 height: output_h
201 nr_channels: img.nr_channels
202 original_nr_channels: img.original_nr_channels // preserve the metadata of the original, during resizes
203 }
204
205 res.data = cb_malloc(usize(output_w * output_h * img.nr_channels))
206 if res.data == 0 {
207 return error('stbi_image failed to resize file')
208 }
209
210 if 0 == C.stbir_resize_uint8_linear(img.data, img.width, img.height, 0, res.data, output_w,
211 output_h, 0, img.nr_channels) {
212 return error('stbi_image failed to resize file')
213 }
214 return res
215}
216
217//-----------------------------------------------------------------------------
218//
219// Write functions
220//
221//-----------------------------------------------------------------------------
222fn C.stbi_write_png(filename &char, w i32, h i32, nr_channels i32, buffer &u8, stride_in_bytes i32) i32
223fn C.stbi_write_bmp(filename &char, w i32, h i32, nr_channels i32, buffer &u8) i32
224fn C.stbi_write_tga(filename &char, w i32, h i32, nr_channels i32, buffer &u8) i32
225fn C.stbi_write_jpg(filename &char, w i32, h i32, nr_channels i32, buffer &u8, quality i32) i32
226
227// fn C.stbi_write_hdr(filename &char, w int, h int, nr_channels int, buffer &u8) int // buffer &u8 => buffer &f32
228
229// stbi_write_png writes a PNG file to `path`.
230// `nr_channels` is the number of channels in `buf`.
231// `row_stride_in_bytes` is usually `w * nr_channels`.
232pub fn stbi_write_png(path string, w int, h int, nr_channels int, buf &u8, row_stride_in_bytes int) ! {
233 if 0 == C.stbi_write_png(&char(path.str), w, h, nr_channels, buf, row_stride_in_bytes) {
234 return error('stbi_image failed to write png file to "${path}"')
235 }
236}
237
238// stbi_write_bmp writes a BMP file to `path`.
239// `nr_channels` is the number of channels in `buf`.
240pub fn stbi_write_bmp(path string, w int, h int, nr_channels int, buf &u8) ! {
241 if 0 == C.stbi_write_bmp(&char(path.str), w, h, nr_channels, buf) {
242 return error('stbi_image failed to write bmp file to "${path}"')
243 }
244}
245
246// stbi_write_tga writes a TGA file to `path`.
247// `nr_channels` is the number of channels in `buf`.
248pub fn stbi_write_tga(path string, w int, h int, nr_channels int, buf &u8) ! {
249 if 0 == C.stbi_write_tga(&char(path.str), w, h, nr_channels, buf) {
250 return error('stbi_image failed to write tga file to "${path}"')
251 }
252}
253
254// stbi_write_jpg writes a JPG file to `path`.
255// `nr_channels` is the number of channels in `buf`.
256// `quality` controls the JPG compression quality and must be between 1 and 100.
257pub fn stbi_write_jpg(path string, w int, h int, nr_channels int, buf &u8, quality int) ! {
258 if 0 == C.stbi_write_jpg(&char(path.str), w, h, nr_channels, buf, quality) {
259 return error('stbi_image failed to write jpg file to "${path}"')
260 }
261}
262
263/*
264pub fn stbi_write_hdr(path string, w int, h int, nr_channels int, buf &u8) ! {
265 if 0 == C.stbi_write_hdr(&char(path.str), w , h , nr_channels , buf){
266 return error('stbi_image failed to write hdr file to "${path}"')
267 }
268}
269*/
270