Merge pull request #106200 from BlueCube3310/image-16-u16

Image: Implement 16-bit unorm and uint formats
This commit is contained in:
Thaddeus Crews 2025-09-23 12:08:46 -05:00
commit cd3a6c88fd
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
13 changed files with 914 additions and 63 deletions

View file

@ -78,6 +78,14 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"ASTC_4x4_HDR",
"ASTC_8x8",
"ASTC_8x8_HDR",
"R16",
"RG16",
"RGB16",
"RGBA16",
"R16Int",
"RG16Int",
"RGB16Int",
"RGBA16Int",
};
// External VRAM compression function pointers.
@ -201,6 +209,22 @@ int Image::get_format_pixel_size(Format p_format) {
return 1;
case FORMAT_ASTC_8x8_HDR:
return 1;
case FORMAT_R16:
return 2;
case FORMAT_RG16:
return 4;
case FORMAT_RGB16:
return 6;
case FORMAT_RGBA16:
return 8;
case FORMAT_R16I:
return 2;
case FORMAT_RG16I:
return 4;
case FORMAT_RGB16I:
return 6;
case FORMAT_RGBA16I:
return 8;
case FORMAT_MAX: {
}
}
@ -759,6 +783,78 @@ void Image::convert(Format p_new_format) {
case FORMAT_RGBAF | (FORMAT_RGBF << 8):
_convert_fast<uint32_t, 4, 3, 0x00000000, 0x3F800000>(mip_width, mip_height, (const uint32_t *)rptr, (uint32_t *)wptr);
break;
case FORMAT_R16 | (FORMAT_RG16 << 8):
_convert_fast<uint16_t, 1, 2, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_R16 | (FORMAT_RGB16 << 8):
_convert_fast<uint16_t, 1, 3, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_R16 | (FORMAT_RGBA16 << 8):
_convert_fast<uint16_t, 1, 4, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RG16 | (FORMAT_R16 << 8):
_convert_fast<uint16_t, 2, 1, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RG16 | (FORMAT_RGB16 << 8):
_convert_fast<uint16_t, 2, 3, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RG16 | (FORMAT_RGBA16 << 8):
_convert_fast<uint16_t, 2, 4, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGB16 | (FORMAT_R16 << 8):
_convert_fast<uint16_t, 3, 1, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGB16 | (FORMAT_RG16 << 8):
_convert_fast<uint16_t, 3, 2, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGB16 | (FORMAT_RGBA16 << 8):
_convert_fast<uint16_t, 3, 4, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGBA16 | (FORMAT_R16 << 8):
_convert_fast<uint16_t, 4, 1, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGBA16 | (FORMAT_RG16 << 8):
_convert_fast<uint16_t, 4, 2, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGBA16 | (FORMAT_RGB16 << 8):
_convert_fast<uint16_t, 4, 3, 0x0000, 0xFFFF>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_R16I | (FORMAT_RG16I << 8):
_convert_fast<uint16_t, 1, 2, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_R16I | (FORMAT_RGB16I << 8):
_convert_fast<uint16_t, 1, 3, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_R16I | (FORMAT_RGBA16I << 8):
_convert_fast<uint16_t, 1, 4, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RG16I | (FORMAT_R16I << 8):
_convert_fast<uint16_t, 2, 1, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RG16I | (FORMAT_RGB16I << 8):
_convert_fast<uint16_t, 2, 3, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RG16I | (FORMAT_RGBA16I << 8):
_convert_fast<uint16_t, 2, 4, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGB16I | (FORMAT_R16I << 8):
_convert_fast<uint16_t, 3, 1, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGB16I | (FORMAT_RG16I << 8):
_convert_fast<uint16_t, 3, 2, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGB16I | (FORMAT_RGBA16I << 8):
_convert_fast<uint16_t, 3, 4, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGBA16I | (FORMAT_R16I << 8):
_convert_fast<uint16_t, 4, 1, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGBA16I | (FORMAT_RG16I << 8):
_convert_fast<uint16_t, 4, 2, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
case FORMAT_RGBA16I | (FORMAT_RGB16I << 8):
_convert_fast<uint16_t, 4, 3, 0x0000, 0x0001>(mip_width, mip_height, (const uint16_t *)rptr, (uint16_t *)wptr);
break;
}
}
@ -769,6 +865,11 @@ Image::Format Image::get_format() const {
return format;
}
enum ImageScaleType {
IMAGE_SCALING_INT,
IMAGE_SCALING_FLOAT,
};
static double _bicubic_interp_kernel(double x) {
x = Math::abs(x);
@ -783,7 +884,7 @@ static double _bicubic_interp_kernel(double x) {
return bc;
}
template <int CC, typename T>
template <int CC, typename T, ImageScaleType TYPE>
static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
// get source image size
int width = p_src_width;
@ -845,7 +946,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
const T *__restrict p = ((T *)p_src) + (oy2 * p_src_width + ox2) * CC;
for (int i = 0; i < CC; i++) {
if constexpr (sizeof(T) == 2) { //half float
if constexpr (sizeof(T) == 2 && TYPE == IMAGE_SCALING_FLOAT) { //half float
color[i] = Math::half_to_float(p[i]);
} else {
color[i] += p[i] * k2;
@ -857,8 +958,12 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
for (int i = 0; i < CC; i++) {
if constexpr (sizeof(T) == 1) { //byte
dst[i] = CLAMP(Math::fast_ftoi(color[i]), 0, 255);
} else if constexpr (sizeof(T) == 2) { //half float
dst[i] = Math::make_half_float(color[i]);
} else if constexpr (sizeof(T) == 2) {
if constexpr (TYPE == IMAGE_SCALING_FLOAT) {
dst[i] = Math::make_half_float(color[i]); //half float
} else {
dst[i] = CLAMP(Math::fast_ftoi(color[i]), 0, 65535); // uint16
}
} else {
dst[i] = color[i];
}
@ -867,7 +972,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
}
}
template <int CC, typename T>
template <int CC, typename T, ImageScaleType TYPE>
static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
constexpr uint32_t FRAC_BITS = 8;
constexpr uint32_t FRAC_LEN = (1 << FRAC_BITS);
@ -915,23 +1020,40 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict
uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS);
interp >>= FRAC_BITS;
p_dst[i * p_dst_width * CC + j * CC + l] = uint8_t(interp);
} else if constexpr (sizeof(T) == 2) { //half float
} else if constexpr (sizeof(T) == 2) {
if constexpr (TYPE == IMAGE_SCALING_FLOAT) { //half float
float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS);
float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS);
const T *src = ((const T *)p_src);
T *dst = ((T *)p_dst);
float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS);
float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS);
const T *src = ((const T *)p_src);
T *dst = ((T *)p_dst);
float p00 = Math::half_to_float(src[y_ofs_up + src_xofs_left + l]);
float p10 = Math::half_to_float(src[y_ofs_up + src_xofs_right + l]);
float p01 = Math::half_to_float(src[y_ofs_down + src_xofs_left + l]);
float p11 = Math::half_to_float(src[y_ofs_down + src_xofs_right + l]);
float p00 = Math::half_to_float(src[y_ofs_up + src_xofs_left + l]);
float p10 = Math::half_to_float(src[y_ofs_up + src_xofs_right + l]);
float p01 = Math::half_to_float(src[y_ofs_down + src_xofs_left + l]);
float p11 = Math::half_to_float(src[y_ofs_down + src_xofs_right + l]);
float interp_up = p00 + (p10 - p00) * xofs_frac;
float interp_down = p01 + (p11 - p01) * xofs_frac;
float interp = interp_up + ((interp_down - interp_up) * yofs_frac);
float interp_up = p00 + (p10 - p00) * xofs_frac;
float interp_down = p01 + (p11 - p01) * xofs_frac;
float interp = interp_up + ((interp_down - interp_up) * yofs_frac);
dst[i * p_dst_width * CC + j * CC + l] = Math::make_half_float(interp);
} else { //uint16
float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS);
float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS);
const T *src = ((const T *)p_src);
T *dst = ((T *)p_dst);
dst[i * p_dst_width * CC + j * CC + l] = Math::make_half_float(interp);
float p00 = src[y_ofs_up + src_xofs_left + l];
float p10 = src[y_ofs_up + src_xofs_right + l];
float p01 = src[y_ofs_down + src_xofs_left + l];
float p11 = src[y_ofs_down + src_xofs_right + l];
float interp_up = p00 + (p10 - p00) * xofs_frac;
float interp_down = p01 + (p11 - p01) * xofs_frac;
float interp = interp_up + ((interp_down - interp_up) * yofs_frac);
dst[i * p_dst_width * CC + j * CC + l] = uint16_t(interp);
}
} else if constexpr (sizeof(T) == 4) { //float
float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS);
@ -982,7 +1104,7 @@ static float _lanczos(float p_x) {
return Math::abs(p_x) >= LANCZOS_TYPE ? 0 : Math::sincn(p_x) * Math::sincn(p_x / LANCZOS_TYPE);
}
template <int CC, typename T>
template <int CC, typename T, ImageScaleType TYPE>
static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
int32_t src_width = p_src_width;
int32_t src_height = p_src_height;
@ -1023,7 +1145,7 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
const T *__restrict src_data = ((const T *)p_src) + (buffer_y * src_width + target_x) * CC;
for (uint32_t i = 0; i < CC; i++) {
if constexpr (sizeof(T) == 2) { //half float
if constexpr (sizeof(T) == 2 && TYPE == IMAGE_SCALING_FLOAT) { //half float
pixel[i] += Math::half_to_float(src_data[i]) * lanczos_val;
} else {
pixel[i] += src_data[i] * lanczos_val;
@ -1082,8 +1204,13 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
if constexpr (sizeof(T) == 1) { //byte
dst_data[i] = CLAMP(Math::fast_ftoi(pixel[i]), 0, 255);
} else if constexpr (sizeof(T) == 2) { //half float
dst_data[i] = Math::make_half_float(pixel[i]);
} else if constexpr (sizeof(T) == 2) {
if constexpr (TYPE == IMAGE_SCALING_FLOAT) { //half float
dst_data[i] = Math::make_half_float(pixel[i]);
} else { //uint16
dst_data[i] = CLAMP(Math::fast_ftoi(pixel[i]), 0, 65535);
}
} else { // float
dst_data[i] = pixel[i];
}
@ -1226,6 +1353,21 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
_scale_nearest<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
} else if (format >= FORMAT_R16 && format <= FORMAT_RGBA16I) {
switch (get_format_pixel_size(format)) {
case 2:
_scale_nearest<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 4:
_scale_nearest<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 6:
_scale_nearest<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 8:
_scale_nearest<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
}
} break;
@ -1270,46 +1412,61 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) {
switch (get_format_pixel_size(format)) {
case 1:
_scale_bilinear<1, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<1, uint8_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 2:
_scale_bilinear<2, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<2, uint8_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 3:
_scale_bilinear<3, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<3, uint8_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 4:
_scale_bilinear<4, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<4, uint8_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
}
} else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) {
switch (get_format_pixel_size(format)) {
case 4:
_scale_bilinear<1, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<1, float, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 8:
_scale_bilinear<2, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<2, float, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 12:
_scale_bilinear<3, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<3, float, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 16:
_scale_bilinear<4, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<4, float, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
}
} else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) {
switch (get_format_pixel_size(format)) {
case 2:
_scale_bilinear<1, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<1, uint16_t, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 4:
_scale_bilinear<2, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<2, uint16_t, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 6:
_scale_bilinear<3, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<3, uint16_t, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 8:
_scale_bilinear<4, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
_scale_bilinear<4, uint16_t, IMAGE_SCALING_FLOAT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
}
} else if (format >= FORMAT_R16 && format <= FORMAT_RGBA16I) {
switch (get_format_pixel_size(format)) {
case 2:
_scale_bilinear<1, uint16_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 4:
_scale_bilinear<2, uint16_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 6:
_scale_bilinear<3, uint16_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
case 8:
_scale_bilinear<4, uint16_t, IMAGE_SCALING_INT>(src_ptr, w_ptr, src_width, src_height, p_width, p_height);
break;
}
}
@ -1326,46 +1483,61 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) {
switch (get_format_pixel_size(format)) {
case 1:
_scale_cubic<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<1, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 2:
_scale_cubic<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<2, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 3:
_scale_cubic<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<3, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 4:
_scale_cubic<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<4, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
} else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) {
switch (get_format_pixel_size(format)) {
case 4:
_scale_cubic<1, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<1, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 8:
_scale_cubic<2, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<2, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 12:
_scale_cubic<3, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<3, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 16:
_scale_cubic<4, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<4, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
} else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) {
switch (get_format_pixel_size(format)) {
case 2:
_scale_cubic<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<1, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 4:
_scale_cubic<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<2, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 6:
_scale_cubic<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<3, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 8:
_scale_cubic<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_cubic<4, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
} else if (format >= FORMAT_R16 && format <= FORMAT_RGBA16I) {
switch (get_format_pixel_size(format)) {
case 2:
_scale_cubic<1, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 4:
_scale_cubic<2, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 6:
_scale_cubic<3, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 8:
_scale_cubic<4, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
}
@ -1374,46 +1546,61 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) {
switch (get_format_pixel_size(format)) {
case 1:
_scale_lanczos<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<1, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 2:
_scale_lanczos<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<2, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 3:
_scale_lanczos<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<3, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 4:
_scale_lanczos<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<4, uint8_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
} else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) {
switch (get_format_pixel_size(format)) {
case 4:
_scale_lanczos<1, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<1, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 8:
_scale_lanczos<2, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<2, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 12:
_scale_lanczos<3, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<3, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 16:
_scale_lanczos<4, float>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<4, float, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
} else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) {
switch (get_format_pixel_size(format)) {
case 2:
_scale_lanczos<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<1, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 4:
_scale_lanczos<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<2, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 6:
_scale_lanczos<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<3, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 8:
_scale_lanczos<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height);
_scale_lanczos<4, uint16_t, IMAGE_SCALING_FLOAT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
} else if (format >= FORMAT_R16 && format <= FORMAT_RGBA16I) {
switch (get_format_pixel_size(format)) {
case 2:
_scale_lanczos<1, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 4:
_scale_lanczos<2, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 6:
_scale_lanczos<3, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
case 8:
_scale_lanczos<4, uint16_t, IMAGE_SCALING_INT>(r_ptr, w_ptr, width, height, p_width, p_height);
break;
}
}
@ -1871,6 +2058,30 @@ void Image::_generate_mipmap_from_format(Image::Format p_format, const uint8_t *
case Image::FORMAT_RGBE9995:
_generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(src_u32, dst_u32, p_width, p_height);
break;
case Image::FORMAT_R16:
case Image::FORMAT_R16I:
_generate_po2_mipmap<uint16_t, 1, false, Image::average_4_uint16, Image::renormalize_uint16>(src_u16, dst_u16, p_width, p_height);
break;
case Image::FORMAT_RG16:
case Image::FORMAT_RG16I:
_generate_po2_mipmap<uint16_t, 2, false, Image::average_4_uint16, Image::renormalize_uint16>(src_u16, dst_u16, p_width, p_height);
break;
case Image::FORMAT_RGB16:
case Image::FORMAT_RGB16I: {
if (p_renormalize) {
_generate_po2_mipmap<uint16_t, 3, true, Image::average_4_uint16, Image::renormalize_uint16>(src_u16, dst_u16, p_width, p_height);
} else {
_generate_po2_mipmap<uint16_t, 3, false, Image::average_4_uint16, Image::renormalize_uint16>(src_u16, dst_u16, p_width, p_height);
}
} break;
case Image::FORMAT_RGBA16:
case Image::FORMAT_RGBA16I: {
if (p_renormalize) {
_generate_po2_mipmap<uint16_t, 4, true, Image::average_4_uint16, Image::renormalize_uint16>(src_u16, dst_u16, p_width, p_height);
} else {
_generate_po2_mipmap<uint16_t, 4, false, Image::average_4_uint16, Image::renormalize_uint16>(src_u16, dst_u16, p_width, p_height);
}
} break;
default:
return;
@ -2468,6 +2679,17 @@ bool Image::is_invisible() const {
}
}
} break;
case FORMAT_RGBA16:
case FORMAT_RGBA16I: {
const int pixel_count = len / 8;
const uint16_t *pixeldata = reinterpret_cast<const uint16_t *>(data.ptr());
for (int i = 0; i < pixel_count; i++) {
if (pixeldata[i * 4 + 3] != 0) {
return false;
}
}
} break;
default: {
// Formats that are compressed or don't support alpha channels are presumed to be visible.
return false;
@ -2671,7 +2893,7 @@ bool Image::is_compressed() const {
}
bool Image::is_format_compressed(Format p_format) {
return p_format > FORMAT_RGBE9995;
return p_format > FORMAT_RGBE9995 && p_format < FORMAT_R16;
}
Error Image::decompress() {
@ -3252,6 +3474,50 @@ Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const {
case FORMAT_RGBE9995: {
return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]);
}
case FORMAT_R16: {
float r = ((uint16_t *)ptr)[ofs] / 65535.0f;
return Color(r, 0, 0, 1);
}
case FORMAT_RG16: {
float r = ((uint16_t *)ptr)[ofs * 2 + 0] / 65535.0f;
float g = ((uint16_t *)ptr)[ofs * 2 + 1] / 65535.0f;
return Color(r, g, 0, 1);
}
case FORMAT_RGB16: {
float r = ((uint16_t *)ptr)[ofs * 3 + 0] / 65535.0f;
float g = ((uint16_t *)ptr)[ofs * 3 + 1] / 65535.0f;
float b = ((uint16_t *)ptr)[ofs * 3 + 2] / 65535.0f;
return Color(r, g, b, 1);
}
case FORMAT_RGBA16: {
float r = ((uint16_t *)ptr)[ofs * 4 + 0] / 65535.0f;
float g = ((uint16_t *)ptr)[ofs * 4 + 1] / 65535.0f;
float b = ((uint16_t *)ptr)[ofs * 4 + 2] / 65535.0f;
float a = ((uint16_t *)ptr)[ofs * 4 + 3] / 65535.0f;
return Color(r, g, b, a);
}
case FORMAT_R16I: {
uint16_t r = ((uint16_t *)ptr)[ofs];
return Color(r, 0, 0, 1);
}
case FORMAT_RG16I: {
uint16_t r = ((uint16_t *)ptr)[ofs * 2 + 0];
uint16_t g = ((uint16_t *)ptr)[ofs * 2 + 1];
return Color(r, g, 0, 1);
}
case FORMAT_RGB16I: {
uint16_t r = ((uint16_t *)ptr)[ofs * 3 + 0];
uint16_t g = ((uint16_t *)ptr)[ofs * 3 + 1];
uint16_t b = ((uint16_t *)ptr)[ofs * 3 + 2];
return Color(r, g, b, 1);
}
case FORMAT_RGBA16I: {
uint16_t r = ((uint16_t *)ptr)[ofs * 4 + 0];
uint16_t g = ((uint16_t *)ptr)[ofs * 4 + 1];
uint16_t b = ((uint16_t *)ptr)[ofs * 4 + 2];
uint16_t a = ((uint16_t *)ptr)[ofs * 4 + 3];
return Color(r, g, b, a);
}
default: {
ERR_FAIL_V_MSG(Color(), "Can't get_pixel() on compressed image, sorry.");
@ -3344,6 +3610,42 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color)
case FORMAT_RGBE9995: {
((uint32_t *)ptr)[ofs] = p_color.to_rgbe9995();
} break;
case FORMAT_R16: {
((uint16_t *)ptr)[ofs] = uint16_t(CLAMP(p_color.r * 65535.0, 0, 65535));
} break;
case FORMAT_RG16: {
((uint16_t *)ptr)[ofs * 2 + 0] = uint16_t(CLAMP(p_color.r * 65535.0, 0, 65535));
((uint16_t *)ptr)[ofs * 2 + 1] = uint16_t(CLAMP(p_color.g * 65535.0, 0, 65535));
} break;
case FORMAT_RGB16: {
((uint16_t *)ptr)[ofs * 3 + 0] = uint16_t(CLAMP(p_color.r * 65535.0, 0, 65535));
((uint16_t *)ptr)[ofs * 3 + 1] = uint16_t(CLAMP(p_color.g * 65535.0, 0, 65535));
((uint16_t *)ptr)[ofs * 3 + 2] = uint16_t(CLAMP(p_color.b * 65535.0, 0, 65535));
} break;
case FORMAT_RGBA16: {
((uint16_t *)ptr)[ofs * 4 + 0] = uint16_t(CLAMP(p_color.r * 65535.0, 0, 65535));
((uint16_t *)ptr)[ofs * 4 + 1] = uint16_t(CLAMP(p_color.g * 65535.0, 0, 65535));
((uint16_t *)ptr)[ofs * 4 + 2] = uint16_t(CLAMP(p_color.b * 65535.0, 0, 65535));
((uint16_t *)ptr)[ofs * 4 + 3] = uint16_t(CLAMP(p_color.a * 65535.0, 0, 65535));
} break;
case FORMAT_R16I: {
((uint16_t *)ptr)[ofs] = uint16_t(CLAMP(p_color.r, 0, 65535));
} break;
case FORMAT_RG16I: {
((uint16_t *)ptr)[ofs * 2 + 0] = uint16_t(CLAMP(p_color.r, 0, 65535));
((uint16_t *)ptr)[ofs * 2 + 1] = uint16_t(CLAMP(p_color.g, 0, 65535));
} break;
case FORMAT_RGB16I: {
((uint16_t *)ptr)[ofs * 3 + 0] = uint16_t(CLAMP(p_color.r, 0, 65535));
((uint16_t *)ptr)[ofs * 3 + 1] = uint16_t(CLAMP(p_color.g, 0, 65535));
((uint16_t *)ptr)[ofs * 3 + 2] = uint16_t(CLAMP(p_color.b, 0, 65535));
} break;
case FORMAT_RGBA16I: {
((uint16_t *)ptr)[ofs * 4 + 0] = uint16_t(CLAMP(p_color.r, 0, 65535));
((uint16_t *)ptr)[ofs * 4 + 1] = uint16_t(CLAMP(p_color.g, 0, 65535));
((uint16_t *)ptr)[ofs * 4 + 2] = uint16_t(CLAMP(p_color.b, 0, 65535));
((uint16_t *)ptr)[ofs * 4 + 3] = uint16_t(CLAMP(p_color.a, 0, 65535));
} break;
default: {
ERR_FAIL_MSG("Can't set_pixel() on compressed image, sorry.");
@ -3419,11 +3721,11 @@ Image::UsedChannels Image::detect_used_channels(CompressSource p_source) const {
if (format == FORMAT_L8) {
return USED_CHANNELS_L; // Grayscale only cannot have any channel less.
} else if (format == FORMAT_R8 || format == FORMAT_RH || format == FORMAT_RF) {
} else if (format == FORMAT_R8 || format == FORMAT_RH || format == FORMAT_RF || format == FORMAT_R16 || format == FORMAT_R16I) {
return USED_CHANNELS_R; // Red only cannot have any channel less.
}
const bool supports_alpha = format == FORMAT_RGBA8 || format == FORMAT_RGBA4444 || format == FORMAT_RGBAH || format == FORMAT_RGBAF;
const bool supports_alpha = format == FORMAT_RGBA8 || format == FORMAT_RGBA4444 || format == FORMAT_RGBAH || format == FORMAT_RGBAF || format == FORMAT_RGBA16 || format == FORMAT_RGBA16I;
bool r = false, g = false, b = false, a = false, c = false;
const uint8_t *data_ptr = data.ptr();
@ -3657,6 +3959,14 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(FORMAT_ASTC_4x4_HDR);
BIND_ENUM_CONSTANT(FORMAT_ASTC_8x8);
BIND_ENUM_CONSTANT(FORMAT_ASTC_8x8_HDR);
BIND_ENUM_CONSTANT(FORMAT_R16);
BIND_ENUM_CONSTANT(FORMAT_RG16);
BIND_ENUM_CONSTANT(FORMAT_RGB16);
BIND_ENUM_CONSTANT(FORMAT_RGBA16);
BIND_ENUM_CONSTANT(FORMAT_R16I);
BIND_ENUM_CONSTANT(FORMAT_RG16I);
BIND_ENUM_CONSTANT(FORMAT_RGB16I);
BIND_ENUM_CONSTANT(FORMAT_RGBA16I);
BIND_ENUM_CONSTANT(FORMAT_MAX);
BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST);
@ -4070,6 +4380,22 @@ uint32_t Image::get_format_component_mask(Format p_format) {
return rgba;
case FORMAT_ASTC_8x8_HDR:
return rgba;
case FORMAT_R16:
return r;
case FORMAT_RG16:
return rg;
case FORMAT_RGB16:
return rgb;
case FORMAT_RGBA16:
return rgba;
case FORMAT_R16I:
return r;
case FORMAT_RG16I:
return rg;
case FORMAT_RGB16I:
return rgb;
case FORMAT_RGBA16I:
return rgba;
default:
ERR_PRINT("Unhandled format.");
return rgba;
@ -4213,6 +4539,10 @@ void Image::average_4_rgbe9995(uint32_t &p_out, const uint32_t &p_a, const uint3
p_out = ((Color::from_rgbe9995(p_a) + Color::from_rgbe9995(p_b) + Color::from_rgbe9995(p_c) + Color::from_rgbe9995(p_d)) * 0.25f).to_rgbe9995();
}
void Image::average_4_uint16(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d) {
p_out = static_cast<uint16_t>((p_a + p_b + p_c + p_d + 2) >> 2);
}
void Image::renormalize_uint8(uint8_t *p_rgb) {
Vector3 n(p_rgb[0] / 255.0, p_rgb[1] / 255.0, p_rgb[2] / 255.0);
n *= 2.0;
@ -4246,6 +4576,19 @@ void Image::renormalize_rgbe9995(uint32_t *p_rgb) {
// Never used.
}
void Image::renormalize_uint16(uint16_t *p_rgb) {
Vector3 n(p_rgb[0] / 65535.0, p_rgb[1] / 65535.0, p_rgb[2] / 65535.0);
n *= 2.0;
n -= Vector3(1, 1, 1);
n.normalize();
n += Vector3(1, 1, 1);
n *= 0.5;
n *= 65535;
p_rgb[0] = CLAMP(int(n.x), 0, 65535);
p_rgb[1] = CLAMP(int(n.y), 0, 65535);
p_rgb[2] = CLAMP(int(n.z), 0, 65535);
}
Image::Image(const uint8_t *p_mem_png_jpg, int p_len) {
width = 0;
height = 0;

View file

@ -111,6 +111,14 @@ public:
FORMAT_ASTC_4x4_HDR,
FORMAT_ASTC_8x8,
FORMAT_ASTC_8x8_HDR,
FORMAT_R16,
FORMAT_RG16,
FORMAT_RGB16,
FORMAT_RGBA16,
FORMAT_R16I,
FORMAT_RG16I,
FORMAT_RGB16I,
FORMAT_RGBA16I,
FORMAT_MAX
};
@ -282,10 +290,12 @@ private:
static void average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d);
static void average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d);
static void average_4_rgbe9995(uint32_t &p_out, const uint32_t &p_a, const uint32_t &p_b, const uint32_t &p_c, const uint32_t &p_d);
static void average_4_uint16(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d);
static void renormalize_uint8(uint8_t *p_rgb);
static void renormalize_float(float *p_rgb);
static void renormalize_half(uint16_t *p_rgb);
static void renormalize_rgbe9995(uint32_t *p_rgb);
static void renormalize_uint16(uint16_t *p_rgb);
public:
int get_width() const;