mirror of
https://github.com/vlang/v.git
synced 2025-08-04 02:07:28 -04:00
stbi: fix stbi.Image's nr_channels
property, after a forced conversion on load (#22528)
This commit is contained in:
parent
1fb9fec7fa
commit
04bd58596f
@ -36,14 +36,17 @@ fn cb_free(p voidptr) {
|
|||||||
#include "stb_v_header.h"
|
#include "stb_v_header.h"
|
||||||
#flag @VEXEROOT/thirdparty/stb_image/stbi.o
|
#flag @VEXEROOT/thirdparty/stb_image/stbi.o
|
||||||
|
|
||||||
|
// Image represents an image loaded from file or memory, or an image, produced after resizing
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
pub mut:
|
pub mut:
|
||||||
width int
|
width int // the width in pixels in the .data
|
||||||
height int
|
height int // the height in pixels in the .data
|
||||||
nr_channels int
|
nr_channels int // the number of color channels in the .data
|
||||||
ok bool
|
ok bool // if the image was loaded successfully
|
||||||
data &u8 = unsafe { nil }
|
data &u8 = unsafe { nil } // the actual data/pixels in the image, after reading and potentially converting the image
|
||||||
ext string
|
ext string // the extension of the file, from which the image was loaded
|
||||||
|
//
|
||||||
|
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.
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -113,21 +116,27 @@ fn C.stbi_load_from_memory(buffer &u8, len int, x &int, y &int, channels_in_file
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct LoadParams {
|
pub struct LoadParams {
|
||||||
pub:
|
pub:
|
||||||
// the number of channels you expect the image to have.
|
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.
|
||||||
// If set to 0 stbi will figure out the correct number of channels
|
|
||||||
desired_channels int = C.STBI_rgb_alpha
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load load an image from a path
|
// load loads an image from `path`
|
||||||
|
// If you do not pass desired_channels: explicitly, it will default to 4 (RGBA),
|
||||||
|
// The image, will get converted into that internal format, no matter what it was on disk.
|
||||||
|
// Use desired_channels:0, if you need to keep the channels of the image on disk.
|
||||||
|
// Note that displaying such an image, with gg/sokol later, can be a problem.
|
||||||
|
// Converting/resizing it, should work fine though.
|
||||||
pub fn load(path string, params LoadParams) !Image {
|
pub fn load(path string, params LoadParams) !Image {
|
||||||
ext := path.all_after_last('.')
|
ext := path.all_after_last('.')
|
||||||
mut res := Image{
|
mut res := Image{
|
||||||
ok: true
|
ok: true
|
||||||
ext: ext
|
ext: ext
|
||||||
|
nr_channels: params.desired_channels
|
||||||
}
|
}
|
||||||
res.data = C.stbi_load(&char(path.str), &res.width, &res.height, &res.nr_channels,
|
res.data = C.stbi_load(&char(path.str), &res.width, &res.height, &res.original_nr_channels,
|
||||||
params.desired_channels)
|
params.desired_channels)
|
||||||
|
if params.desired_channels == 0 {
|
||||||
|
res.nr_channels = res.original_nr_channels
|
||||||
|
}
|
||||||
if isnil(res.data) {
|
if isnil(res.data) {
|
||||||
return error('stbi_image failed to load from "${path}"')
|
return error('stbi_image failed to load from "${path}"')
|
||||||
}
|
}
|
||||||
@ -135,12 +144,21 @@ pub fn load(path string, params LoadParams) !Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load_from_memory load an image from a memory buffer
|
// load_from_memory load an image from a memory buffer
|
||||||
|
// If you do not pass desired_channels: explicitly, it will default to 4 (RGBA),
|
||||||
|
// and the image will get converted into that internal format, no matter what it was originally.
|
||||||
|
// Use desired_channels:0, if you need to keep the channels of the image as they were.
|
||||||
|
// Note that displaying such an image, with gg/sokol later, can be a problem.
|
||||||
|
// Converting/resizing it, should work fine though.
|
||||||
pub fn load_from_memory(buf &u8, bufsize int, params LoadParams) !Image {
|
pub fn load_from_memory(buf &u8, bufsize int, params LoadParams) !Image {
|
||||||
mut res := Image{
|
mut res := Image{
|
||||||
ok: true
|
ok: true
|
||||||
|
nr_channels: params.desired_channels
|
||||||
}
|
}
|
||||||
res.data = C.stbi_load_from_memory(buf, bufsize, &res.width, &res.height, &res.nr_channels,
|
res.data = C.stbi_load_from_memory(buf, bufsize, &res.width, &res.height, &res.original_nr_channels,
|
||||||
params.desired_channels)
|
params.desired_channels)
|
||||||
|
if params.desired_channels == 0 {
|
||||||
|
res.nr_channels = res.original_nr_channels
|
||||||
|
}
|
||||||
if isnil(res.data) {
|
if isnil(res.data) {
|
||||||
return error('stbi_image failed to load from memory')
|
return error('stbi_image failed to load from memory')
|
||||||
}
|
}
|
||||||
@ -158,11 +176,12 @@ fn C.stbir_resize_uint8_linear(input_pixels &u8, input_w int, input_h int, input
|
|||||||
// resize_uint8 resizes `img` to dimensions of `output_w` and `output_h`
|
// resize_uint8 resizes `img` to dimensions of `output_w` and `output_h`
|
||||||
pub fn resize_uint8(img &Image, output_w int, output_h int) !Image {
|
pub fn resize_uint8(img &Image, output_w int, output_h int) !Image {
|
||||||
mut res := Image{
|
mut res := Image{
|
||||||
ok: true
|
ok: true
|
||||||
ext: img.ext
|
ext: img.ext
|
||||||
width: output_w
|
width: output_w
|
||||||
height: output_h
|
height: output_h
|
||||||
nr_channels: img.nr_channels
|
nr_channels: img.nr_channels
|
||||||
|
original_nr_channels: img.original_nr_channels // preserve the metadata of the original, during resizes
|
||||||
}
|
}
|
||||||
|
|
||||||
res.data = cb_malloc(usize(output_w * output_h * img.nr_channels))
|
res.data = cb_malloc(usize(output_w * output_h * img.nr_channels))
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import stbi
|
import stbi
|
||||||
|
|
||||||
|
const vroot = @VEXEROOT
|
||||||
const tfolder = os.join_path(os.vtmp_dir(), 'stbi')
|
const tfolder = os.join_path(os.vtmp_dir(), 'stbi')
|
||||||
|
const logo_path = os.join_path(vroot, 'examples/assets/logo.png')
|
||||||
|
const background_path = os.join_path(vroot, 'examples/flappylearning/assets/img/background.png')
|
||||||
|
|
||||||
fn testsuite_begin() {
|
fn testsuite_begin() {
|
||||||
os.mkdir_all(tfolder) or {}
|
os.mkdir_all(tfolder) or {}
|
||||||
@ -12,10 +15,8 @@ fn testsuite_end() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_stbi_read_write() {
|
fn test_stbi_read_write() {
|
||||||
vroot := @VEXEROOT
|
println('Source path: ${logo_path}')
|
||||||
path := os.join_path(vroot, 'examples', 'assets', 'logo.png')
|
d_s := stbi.load(logo_path) or { panic(err) }
|
||||||
println('Source path: ${path}')
|
|
||||||
d_s := stbi.load(path) or { panic(err) }
|
|
||||||
println('Image source data:\n ${d_s}')
|
println('Image source data:\n ${d_s}')
|
||||||
|
|
||||||
out_path := os.join_path(tfolder, 'test.png')
|
out_path := os.join_path(tfolder, 'test.png')
|
||||||
@ -30,9 +31,10 @@ fn test_stbi_read_write() {
|
|||||||
assert d_s.width == d_d.width
|
assert d_s.width == d_d.width
|
||||||
assert d_s.height == d_d.height
|
assert d_s.height == d_d.height
|
||||||
assert d_s.nr_channels == d_d.nr_channels
|
assert d_s.nr_channels == d_d.nr_channels
|
||||||
|
assert d_s.original_nr_channels == 4
|
||||||
|
|
||||||
mut v_s := &u32(d_s.data)
|
mut v_s := unsafe { &u32(d_s.data) }
|
||||||
mut v_d := &u32(d_d.data)
|
mut v_d := unsafe { &u32(d_d.data) }
|
||||||
mut delta := i64(0)
|
mut delta := i64(0)
|
||||||
for index in 0 .. (d_d.width * d_d.width) {
|
for index in 0 .. (d_d.width * d_d.width) {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -44,15 +46,14 @@ fn test_stbi_read_write() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_stbi_resize() {
|
fn test_stbi_resize() {
|
||||||
vroot := @VEXEROOT
|
println('Source path: ${logo_path}')
|
||||||
path := os.join_path(vroot, 'examples', 'assets', 'logo.png')
|
d_s := stbi.load(logo_path) or { panic(err) }
|
||||||
println('Source path: ${path}')
|
|
||||||
d_s := stbi.load(path) or { panic(err) }
|
|
||||||
println('Image source data:\n ${d_s}')
|
println('Image source data:\n ${d_s}')
|
||||||
|
|
||||||
new_width, new_height := 100, 100
|
new_width, new_height := 100, 100
|
||||||
|
|
||||||
d_r := stbi.resize_uint8(d_s, new_width, new_height) or { panic(err) }
|
d_r := stbi.resize_uint8(d_s, new_width, new_height) or { panic(err) }
|
||||||
|
assert d_r.original_nr_channels == 4
|
||||||
println('Resized Image source data:\n ${d_s}')
|
println('Resized Image source data:\n ${d_s}')
|
||||||
|
|
||||||
out_path := os.join_path(tfolder, 'test.png')
|
out_path := os.join_path(tfolder, 'test.png')
|
||||||
@ -67,5 +68,24 @@ fn test_stbi_resize() {
|
|||||||
assert d_d.width == new_width
|
assert d_d.width == new_width
|
||||||
assert d_d.height == new_height
|
assert d_d.height == new_height
|
||||||
assert d_d.nr_channels == d_r.nr_channels
|
assert d_d.nr_channels == d_r.nr_channels
|
||||||
|
assert d_d.original_nr_channels == 4
|
||||||
os.rm(out_path) or {}
|
os.rm(out_path) or {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_load_image_with_channels_different_than_4() {
|
||||||
|
img := stbi.load(background_path)!
|
||||||
|
assert img.nr_channels == 4, 'by default, stbi.load should convert images to 4 channels'
|
||||||
|
assert img.original_nr_channels == 3, 'the default, should not affect the img.original_nr_channels field; it should be based solely on the original image data on disk'
|
||||||
|
|
||||||
|
img_resized := stbi.resize_uint8(img, 128, 128)!
|
||||||
|
assert img_resized.nr_channels == 4
|
||||||
|
assert img_resized.original_nr_channels == 3
|
||||||
|
|
||||||
|
img3 := stbi.load(background_path, desired_channels: 0)!
|
||||||
|
assert img3.nr_channels == 3, 'stbi.load, with desired_channels: 0, should return an image, without any conversion. the nr_channels should be determined by the image data'
|
||||||
|
assert img.original_nr_channels == 3, 'the default, should not affect the img.original_nr_channels field; it should be based solely on the original image data on disk'
|
||||||
|
|
||||||
|
img3_resized := stbi.resize_uint8(img3, 128, 128)!
|
||||||
|
assert img3_resized.nr_channels == 3
|
||||||
|
assert img3_resized.original_nr_channels == 3
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user