Skip to content

Commit 0be3323

Browse files
committed
fixup! Perform degamma and gamma conversions on user request
1 parent 8df604a commit 0be3323

File tree

5 files changed

+133
-33
lines changed

5 files changed

+133
-33
lines changed

examples/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fn main() {
2828
println!("Downsampling started!");
2929
let params = Parameters {
3030
// Input stb Image is gamma-corrected (i.e. expects to be passed through a CRT with exponent 2.2)
31-
degamma: true,
31+
degamma: false,
3232
// Output image is PNG which must be stored with a gamma of 1/2.2
3333
gamma: true,
3434
};

src/ispc/downsample_ispc.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,54 @@ fn bindgen_test_layout_Image() {
117117
)
118118
);
119119
}
120+
#[repr(C)]
121+
#[repr(align(16))]
122+
#[derive(Debug, Copy, Clone)]
123+
pub struct FloatImage {
124+
pub data: *mut f32,
125+
pub __bindgen_padding_0: u64,
126+
pub size: uint32_t2,
127+
}
128+
#[test]
129+
fn bindgen_test_layout_FloatImage() {
130+
const UNINIT: ::std::mem::MaybeUninit<FloatImage> = ::std::mem::MaybeUninit::uninit();
131+
let ptr = UNINIT.as_ptr();
132+
assert_eq!(
133+
::std::mem::size_of::<FloatImage>(),
134+
32usize,
135+
concat!("Size of: ", stringify!(FloatImage))
136+
);
137+
assert_eq!(
138+
::std::mem::align_of::<FloatImage>(),
139+
16usize,
140+
concat!("Alignment of ", stringify!(FloatImage))
141+
);
142+
assert_eq!(
143+
unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize },
144+
0usize,
145+
concat!(
146+
"Offset of field: ",
147+
stringify!(FloatImage),
148+
"::",
149+
stringify!(data)
150+
)
151+
);
152+
assert_eq!(
153+
unsafe { ::std::ptr::addr_of!((*ptr).size) as usize - ptr as usize },
154+
16usize,
155+
concat!(
156+
"Offset of field: ",
157+
stringify!(FloatImage),
158+
"::",
159+
stringify!(size)
160+
)
161+
);
162+
}
120163
extern "C" {
121164
pub fn resample(
122165
params: *const Parameters,
123166
src: *const Image,
167+
degamma: *mut FloatImage,
124168
dst: *mut Image,
125169
num_channels: u8,
126170
);

src/ispc/kernels/image.ispc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ struct Image {
22
uniform uint8* data;
33
uniform uint<2> size;
44
};
5+
6+
struct FloatImage {
7+
uniform float* data;
8+
uniform uint<2> size;
9+
};

src/ispc/kernels/lanczos3.ispc

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,53 @@ static inline float byte_to_float(uint8 b/*, uniform bool degamma*/) {
4747
return (float)b * inv_255;
4848
}
4949

50-
static inline uint8 float_to_byte(float d, bool gamma) {
50+
static inline uint8 float_to_byte(float d, uniform bool gamma) {
5151
if (gamma) {
5252
d = pow(d, DEGAMMA);
5353
}
5454
int b = d * 255;
5555
return clamp(b, 0, 255);
5656
}
5757

58-
static inline float<4> resample_internal(const uniform Image src_image, const float<2> uv, const uniform uint8 num_channels) {
58+
template<typename IT>
59+
static float<4> sample_image(const uniform IT &image, const int<2> coord, const uniform uint8 num_channels) {
60+
return 0.0f;
61+
}
62+
63+
template<>
64+
static float<4> sample_image<Image>(const uniform Image &image, const int<2> coord, const uniform uint8 num_channels) {
65+
float<4> col = 0.0;
66+
int x = clamp(coord.x, 0, image.size.x - 1);
67+
int y = clamp(coord.y, 0, image.size.y - 1);
68+
int addr = (x + y * image.size.x) * num_channels;
69+
70+
col[0] = byte_to_float(image.data[addr + 0]);
71+
col[1] = byte_to_float(image.data[addr + 1]);
72+
col[2] = byte_to_float(image.data[addr + 2]);
73+
if (num_channels == 4)
74+
col[3] = byte_to_float(image.data[addr + 3]);
75+
76+
return col;
77+
}
78+
79+
template<>
80+
static float<4> sample_image<FloatImage>(const uniform FloatImage &image, const int<2> coord, const uniform uint8 num_channels) {
81+
float<4> col = 0.0;
82+
int x = clamp(coord.x, 0, image.size.x - 1);
83+
int y = clamp(coord.y, 0, image.size.y - 1);
84+
int addr = (x + y * image.size.x) * num_channels;
85+
86+
col[0] = image.data[addr + 0];
87+
col[1] = image.data[addr + 1];
88+
col[2] = image.data[addr + 2];
89+
if (num_channels == 4)
90+
col[3] = image.data[addr + 3];
91+
92+
return col;
93+
}
94+
95+
template<typename IT>
96+
static inline float<4> resample_internal(const uniform IT src_image, const float<2> uv, const uniform uint8 num_channels) {
5997
float<4> col = 0.0;
6098
uniform float weight = 0.0;
6199
// Truncate floating point coordinate to integer:
@@ -72,61 +110,57 @@ static inline float<4> resample_internal(const uniform Image src_image, const fl
72110
const uniform float w = wx * wy;
73111
const uniform int<2> texel_offset = {x, y};
74112

75-
int<2> src_kernel_coord = src_coord + texel_offset;
113+
int<2> c = src_coord + texel_offset;
76114
// TODO: Let the user specify a boundary mode!
77115
// https://github.com/Traverse-Research/ispc-downsampler/issues/25#issuecomment-1584915050
78-
src_kernel_coord.x = clamp(src_kernel_coord.x, 0, src_image.size.x - 1);
79-
src_kernel_coord.y = clamp(src_kernel_coord.y, 0, src_image.size.y - 1);
80-
81-
const int addr = (src_kernel_coord.x + src_kernel_coord.y * src_image.size.x) * num_channels;
116+
// TODO: For some obscure reason this must happen in sample_image() or the whole thing segfaults because
117+
// values become <0 !?!?
118+
// c.x = clamp(c.x, 0, src_image.size.x - 1);
119+
// c.y = clamp(c.y, 0, src_image.size.y - 1);
82120

83-
float<4> texel;
84-
texel.x = byte_to_float(src_image.data[addr + 0]);
85-
texel.y = byte_to_float(src_image.data[addr + 1]);
86-
texel.z = byte_to_float(src_image.data[addr + 2]);
87-
if (num_channels >= 4)
88-
texel.w = byte_to_float(src_image.data[addr + 3]);
89-
90-
col += w * texel;
91121
weight += w;
122+
col += w * sample_image<IT>(src_image, c, num_channels);
92123
}
93124
}
94125
col /= weight;
95126
return col;
96-
97127
}
98128

99129
export void resample(
100-
uniform const Parameters *uniform params,
101-
uniform const Image *uniform src,
102-
uniform Image *uniform dst,
130+
uniform const Parameters &params,
131+
uniform const Image &src,
132+
uniform FloatImage &degamma,
133+
uniform Image &dst,
103134
// Passed separately because it should be the same between input and output:
104135
uniform uint8 num_channels
105136
) {
106-
const uniform float<2> inv_target_size = 1.0f / dst->size;
137+
const uniform float<2> inv_target_size = 1.0f / dst.size;
107138

108-
if (params->degamma) {
109-
foreach_tiled(y = 0 ... src->size.y, x = 0 ... src->size.x)
139+
if (params.degamma) {
140+
foreach_tiled(y = 0 ... src.size.y, x = 0 ... src.size.x)
110141
{
111-
uint p = (x + y * src->size.x) * num_channels;
142+
uint p = (x + y * src.size.x) * num_channels;
112143
for (uniform int i = 0; i < num_channels; i++) {
113144
uint c = p + i;
114-
// TODO: This texture should be writeonly!
115-
src->data[c] = float_to_byte(pow(byte_to_float(src->data[c]), GAMMA), false);
145+
degamma.data[c] = pow(byte_to_float(src.data[c]), GAMMA);
116146
}
117147
}
118148
}
119149

120-
foreach_tiled (y = 0 ... dst->size.y, x = 0 ... dst->size.x) {
150+
foreach_tiled (y = 0 ... dst.size.y, x = 0 ... dst.size.x) {
121151
float<2> uv = {x, y};
122152
// Use the center of each pixel, not the top-left:
123153
uv += 0.5f;
124154
// Convert to uniform space:
125155
uv *= inv_target_size;
126156

127-
const float<4> col = resample_internal(*src, uv, num_channels);
157+
float<4> col;
158+
if (params.degamma)
159+
col = resample_internal(degamma, uv, num_channels);
160+
else
161+
col = resample_internal(src, uv, num_channels);
128162

129163
for (uniform int i = 0; i < num_channels; i++)
130-
dst->data[(x + y * dst->size.x) * num_channels + i] = float_to_byte(col[i], params->gamma);
164+
dst.data[(x + y * dst.size.x) * num_channels + i] = float_to_byte(col[i], params.gamma);
131165
}
132166
}

src/lib.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,25 @@ pub fn downsample(
100100

101101
let num_channels = src.format.num_channels();
102102

103-
let src = ispc::downsample_ispc::Image {
103+
let src_raw = ispc::downsample_ispc::Image {
104104
data: src.pixels.as_ptr() as *mut _,
105105
__bindgen_padding_0: 0,
106-
// TODO: Use the builtin type when ISPC 1.22 is released
107-
// https://github.com/ispc/ispc/issues/2650
108106
size: ispc::downsample_ispc::uint32_t2 {
109107
v: [src.width, src.height],
110108
},
111109
};
112110

111+
let mut degamma = params.degamma.then(|| {
112+
let mut degamma = vec![0f32; (src.width * src.height * num_channels as u32) as usize];
113+
ispc::downsample_ispc::FloatImage {
114+
data: degamma.as_mut_ptr(),
115+
__bindgen_padding_0: 0,
116+
size: ispc::downsample_ispc::uint32_t2 {
117+
v: [src.width, src.height],
118+
},
119+
}
120+
});
121+
113122
let mut output = vec![0; (target_width * target_height * num_channels as u32) as usize];
114123

115124
let mut dst = ispc::downsample_ispc::Image {
@@ -120,7 +129,15 @@ pub fn downsample(
120129
},
121130
};
122131

123-
unsafe { ispc::downsample_ispc::resample(&params.to_ispc(), &src, &mut dst, num_channels) }
132+
unsafe {
133+
ispc::downsample_ispc::resample(
134+
&params.to_ispc(),
135+
&src_raw,
136+
degamma.as_mut().map_or(std::ptr::null_mut(), |x| x),
137+
&mut dst,
138+
num_channels,
139+
)
140+
}
124141

125142
output
126143
}

0 commit comments

Comments
 (0)