@@ -16,6 +16,7 @@ pub mut:
1616 width int
1717 height int
1818 nr_channels int
19+ nr_mipmaps int
1920 ok bool
2021 data voidptr
2122 ext string
@@ -26,6 +27,13 @@ pub mut:
2627 texture_filter TextureFilter = .linear
2728}
2829
30+ @[params]
31+ pub struct ImageConfig {
32+ pub :
33+ texture_filter ? TextureFilter
34+ max_mipmaps int = 1 // a value below 1 means as many as possible
35+ }
36+
2937// destroy GPU resources associated with the image
3038fn (image &Image) destroy () {
3139 if image.ok {
@@ -38,17 +46,18 @@ fn (image &Image) destroy() {
3846 }
3947}
4048
41- // create_image creates an `Image` from `file`.
42- pub fn (mut ctx Context) create_image (file string ) ! Image {
43- return ctx.create_image_with_filter (file, ctx.config.texture_filter)
44- }
45-
4649// create_image_with_filter creates an `Image` from `file` with the requested texture filter.
50+ @[deprecated: 'use Context.create_image instead' ]
4751pub fn (mut ctx Context) create_image_with_filter (file string , texture_filter TextureFilter) ! Image {
52+ return ctx.create_image (file, texture_filter: texture_filter)!
53+ }
54+
55+ // create_image creates an `Image` from `file`.
56+ pub fn (mut ctx Context) create_image (file string , cfg ImageConfig) ! Image {
4857 if ! os.exists (file) {
4958 $if android {
5059 image_data := os.read_apk_asset (file)!
51- mut image := ctx.create_image_from_byte_array_with_filter (image_data, texture_filter )!
60+ mut image := ctx.create_image_from_byte_array (image_data, cfg )!
5261
5362 image.path = file
5463
@@ -57,15 +66,11 @@ pub fn (mut ctx Context) create_image_with_filter(file string, texture_filter Te
5766 return error ('image file "${file} " not found' )
5867 }
5968 }
60-
6169 $if macos {
6270 if ctx.native_rendering {
63- // return C.darwin_create_image(file)
6471 mut img := C.darwin_create_image (file)
65- img.texture_filter = texture_filter
72+ img.texture_filter = cfg. texture_filter or { ctx.config.texture_filter }
6673
67- // println('created macos image: ${img.path} w=${img.width}')
68- // C.printf('p = %p\n', img.data)
6974 img.id = ctx.image_cache.len
7075 unsafe {
7176 ctx.image_cache << img
@@ -74,33 +79,83 @@ pub fn (mut ctx Context) create_image_with_filter(file string, texture_filter Te
7479 }
7580 }
7681
77- if ! gfx.is_valid () {
78- // Sokol is not initialized yet, add stbi object to a queue/cache
79- // ctx.image_queue << file
80- mut img := load_image_with_filter (file, texture_filter)!
81- img.ok = false
82- img.id = ctx.image_cache.len
83- unsafe {
84- ctx.image_cache << img
85- }
86- return img
87- }
88- mut img := create_image (file, texture_filter)!
82+ mut img := load_image (file)!
8983 img.id = ctx.image_cache.len
84+ img.texture_filter = cfg.texture_filter or { ctx.config.texture_filter }
9085 unsafe {
9186 ctx.image_cache << img
9287 }
88+ if gfx.is_valid () {
89+ img.init_sokol_image ()
90+ }
9391 return img
9492}
9593
94+ // adapted from https://github.com/floooh/sokol/issues/102#issuecomment-2926566603
95+ fn sokol_mipmap (mut simg_desc gfx.ImageDesc, max_mipmaps int ) {
96+ mut levels := 0
97+ mut size := 0
98+ width := simg_desc.width
99+ height := simg_desc.height
100+ mipmaps := if max_mipmaps < gfx.sg_max_mipmaps && max_mipmaps > 0 {
101+ max_mipmaps
102+ } else {
103+ gfx.sg_max_mipmaps
104+ }
105+ for i in 1 .. mipmaps {
106+ w := width >> i
107+ h := height >> i
108+ if w < 1 || h < 1 { break
109+ }
110+ size + = w * h * 4 // 4 = img.nr_channels
111+ levels++
112+ }
113+ // use unsafe to access the data because we have to anyway
114+ mut target := unsafe { malloc (size) }
115+ mut src_width := width
116+ mut src_height := height
117+ for level in 1 .. levels {
118+ src := & u8 (simg_desc.data.subimage[0 ][level - 1 ].ptr)
119+ target_width := src_width / 2
120+ target_height := src_height / 2
121+ for x in 0 .. target_width {
122+ for y in 0 .. target_height {
123+ // channels RGBA
124+ chans := 4
125+ for ch in 0 .. 4 {
126+ // avererage colors in a 2x2 square over the source image where (x*2|y*2) is the lower left corner
127+ mut color := 0
128+ sx := x * 2
129+ sy := y * 2
130+ color + = unsafe { src[(sx + src_width * sy) * chans + ch] }
131+ color + = unsafe { src[(sx + src_width * (sy + 1 )) * chans + ch] }
132+ color + = unsafe { src[((sx + 1 ) + src_width * (sy + 1 )) * chans + ch] }
133+ color + = unsafe { src[((sx + 1 ) + src_width * sy) * chans + ch] }
134+ color / = 4
135+ unsafe {
136+ target[(x + y * target_width) * chans + ch] = u8 (color)
137+ }
138+ }
139+ }
140+ }
141+ src_width = target_width
142+ src_height = target_height
143+ simg_desc.data.subimage[0 ][level].ptr = target
144+ simg_desc.data.subimage[0 ][level].size = usize (target_width * target_height * 4 )
145+ unsafe {
146+ target + = target_width * target_height * 4
147+ }
148+ }
149+ simg_desc.num_mipmaps = levels
150+ }
151+
96152// init_sokol_image initializes this `Image` for use with the
97153// sokol graphical backend system.
98154pub fn (mut img Image) init_sokol_image () & Image {
99155 // println('\n init sokol image ${img.path} ok=${img.simg_ok}')
100156 mut img_desc := gfx.ImageDesc{
101- width: img.width
102- height: img.height
103- num_mipmaps: 0
157+ width: img.width
158+ height: img.height
104159 // wrap_u: .clamp_to_edge // XTODO SAMPLER
105160 // wrap_v: .clamp_to_edge
106161 label: & char (img.path.str)
@@ -123,14 +178,17 @@ pub fn (mut img Image) init_sokol_image() &Image {
123178 ptr: img.data
124179 size: img_size
125180 }
181+ sokol_mipmap (mut img_desc, img.nr_mipmaps)
182+ img.nr_mipmaps = img_desc.num_mipmaps
126183 img.simg = gfx.make_image (& img_desc)
127184 gfx_filter := img.texture_filter.gfx_filter ()
128185
129186 mut smp_desc := gfx.SamplerDesc{
130- min_filter: gfx_filter
131- mag_filter: gfx_filter
132- wrap_u: .clamp_to_edge
133- wrap_v: .clamp_to_edge
187+ min_filter: gfx_filter
188+ mag_filter: gfx_filter
189+ mipmap_filter: .linear
190+ wrap_u: .clamp_to_edge
191+ wrap_v: .clamp_to_edge
134192 }
135193
136194 img.ssmp = gfx.make_sampler (& smp_desc)
@@ -216,33 +274,16 @@ pub fn (mut ctx Context) create_image_with_size(file string, width int, height i
216274 if ! gfx.is_valid () {
217275 // Sokol is not initialized yet, add stbi object to a queue/cache
218276 // ctx.image_queue << file
219- mut img := load_image_with_filter (file, ctx.config.texture_filter) or { return Image{} }
277+ mut img := load_image (file) or { return Image{} }
278+ img.texture_filter = ctx.config.texture_filter
220279 img.width = width
221280 img.height = height
222281 img.ok = false
223282 img.id = ctx.image_cache.len
224283 ctx.image_cache << img
225284 return img
226285 }
227- mut img := create_image (file, ctx.config.texture_filter) or { return Image{} }
228- img.id = ctx.image_cache.len
229- ctx.image_cache << img
230- return img
231- }
232-
233- // create_image creates an `Image` from `file`.
234- //
235- // TODO: remove this
236- fn create_image (file string , texture_filter TextureFilter) ! Image {
237- mut img := load_image_with_filter (file, texture_filter)!
238- img.init_sokol_image ()
239- return img
240- }
241-
242- fn load_image_with_filter (file string , texture_filter TextureFilter) ! Image {
243- mut img := load_image (file)!
244- img.texture_filter = texture_filter
245- return img
286+ return ctx.create_image (file) or { Image{} }
246287}
247288
248289fn load_image (file string ) ! Image {
@@ -265,12 +306,7 @@ fn load_image(file string) !Image {
265306// memory buffer `buf` of size `bufsize`.
266307//
267308// See also: create_image_from_byte_array
268- pub fn (mut ctx Context) create_image_from_memory (buf & u8 , bufsize int ) ! Image {
269- return ctx.create_image_from_memory_with_filter (buf, bufsize, ctx.config.texture_filter)
270- }
271-
272- // create_image_from_memory_with_filter creates an `Image` from `buf` with the requested texture filter.
273- pub fn (mut ctx Context) create_image_from_memory_with_filter (buf & u8 , bufsize int , texture_filter TextureFilter) ! Image {
309+ pub fn (mut ctx Context) create_image_from_memory (buf & u8 , bufsize int , cfg ImageConfig) ! Image {
274310 stb_img := stbi.load_from_memory (buf, bufsize)!
275311 mut img := Image{
276312 width: stb_img.width
@@ -280,26 +316,33 @@ pub fn (mut ctx Context) create_image_from_memory_with_filter(buf &u8, bufsize i
280316 data: stb_img.data
281317 ext: stb_img.ext
282318 id: ctx.image_cache.len
283- texture_filter: texture_filter
319+ texture_filter: cfg. texture_filter or { ctx.config.texture_filter }
284320 }
285- if gfx.is_valid () && ! ctx.native_rendering {
321+ if gfx.is_valid () {
286322 img.init_sokol_image ()
287323 }
288324 ctx.image_cache << img
289325 return img
290326}
291327
328+ // create_image_from_memory_with_filter creates an `Image` from `buf` with the requested texture filter.
329+ @[deprecated: 'use Context.create_image_from_memory instead' ]
330+ pub fn (mut ctx Context) create_image_from_memory_with_filter (buf & u8 , bufsize int , texture_filter TextureFilter) ! Image {
331+ return ctx.create_image_from_memory (buf, bufsize, texture_filter: texture_filter)
332+ }
333+
292334// create_image_from_byte_array creates an `Image` from the
293335// byte array `b`.
294336//
295337// See also: create_image_from_memory
296- pub fn (mut ctx Context) create_image_from_byte_array (b []u8 ) ! Image {
297- return ctx.create_image_from_byte_array_with_filter (b, ctx.config.texture_filter )
338+ pub fn (mut ctx Context) create_image_from_byte_array (b []u8 , cfg ImageConfig ) ! Image {
339+ return ctx.create_image_from_memory (b.data, b.len, cfg )
298340}
299341
300342// create_image_from_byte_array_with_filter creates an `Image` from `b` with the requested texture filter.
343+ @[deprecated: 'use Context.create_image_from_byte_array instead' ]
301344pub fn (mut ctx Context) create_image_from_byte_array_with_filter (b []u8 , texture_filter TextureFilter) ! Image {
302- return ctx.create_image_from_memory_with_filter (b.data, b.len, texture_filter)
345+ return ctx.create_image_from_memory (b.data, b.len, texture_filter: texture_filter)
303346}
304347
305348pub struct StreamingImageConfig {
0 commit comments