blob: 2bf31256396cc60acb6a76f8bc872a7bd81b6b09 [file] [edit]
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2011-2026 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// ----------------------------------------------------------------------------
/**
* @brief Functions for creating in-memory ASTC image structures.
*/
#include <cassert>
#include <cstring>
#include "astcenccli_internal.h"
/* See header for documentation. */
astcenc_image_ptr alloc_image(
unsigned int bitness,
unsigned int dim_x,
unsigned int dim_y,
unsigned int dim_z
) {
astcenc_image_ptr img { new astcenc_image };
// Initialize everything so that we can handle deletion of a partially
// constructed object if new[] throws a bad_alloc exception
img->dim_x = dim_x;
img->dim_y = dim_y;
img->dim_z = dim_z;
img->data_type = ASTCENC_TYPE_U8;
img->data = nullptr;
// Ensure this is done as a size_t to avoid overflow - calling code has
// checked that the product will fit in a size_t without overflowing.
size_t plane_components = static_cast<size_t>(dim_x) * dim_y * 4;
void** data = new void*[dim_z] {};
img->data = data;
if (bitness == 8)
{
img->data_type = ASTCENC_TYPE_U8;
for (unsigned int z = 0; z < dim_z; z++)
{
data[z] = new uint8_t[plane_components];
}
}
else if (bitness == 16)
{
img->data_type = ASTCENC_TYPE_F16;
for (unsigned int z = 0; z < dim_z; z++)
{
data[z] = new uint16_t[plane_components];
}
}
else // if (bitness == 32)
{
assert(bitness == 32);
img->data_type = ASTCENC_TYPE_F32;
for (unsigned int z = 0; z < dim_z; z++)
{
data[z] = new float[plane_components];
}
}
return img;
}
/* See header for documentation. */
void astcenc_image_deleter::operator()(
astcenc_image* img
) const {
if (img == nullptr)
{
return;
}
if (img->data)
{
for (unsigned int z = 0; z < img->dim_z; z++)
{
if (img->data_type == ASTCENC_TYPE_U8)
{
delete[] static_cast<uint8_t*>(img->data[z]);
}
else if (img->data_type == ASTCENC_TYPE_F16)
{
delete[] static_cast<uint16_t*>(img->data[z]);
}
else
{
assert(img->data_type == ASTCENC_TYPE_F32);
delete[] static_cast<float*>(img->data[z]);
}
}
delete[] img->data;
}
delete img;
}
/* See header for documentation. */
unsigned int determine_image_components(
const astcenc_image * img
) {
unsigned int dim_x = img->dim_x;
unsigned int dim_y = img->dim_y;
unsigned int dim_z = img->dim_z;
// Scan through the image data to determine how many color components the image has
bool is_luma = true;
bool has_alpha = false;
if (img->data_type == ASTCENC_TYPE_U8)
{
for (unsigned int z = 0; z < dim_z; z++)
{
uint8_t* data8 = static_cast<uint8_t*>(img->data[z]);
for (unsigned int y = 0; y < dim_y; y++)
{
for (unsigned int x = 0; x < dim_x; x++)
{
int r = data8[(4 * dim_x * y) + (4 * x )];
int g = data8[(4 * dim_x * y) + (4 * x + 1)];
int b = data8[(4 * dim_x * y) + (4 * x + 2)];
int a = data8[(4 * dim_x * y) + (4 * x + 3)];
is_luma = is_luma && (r == g) && (r == b);
has_alpha = has_alpha || (a != 0xFF);
}
}
}
}
else if (img->data_type == ASTCENC_TYPE_F16)
{
for (unsigned int z = 0; z < dim_z; z++)
{
uint16_t* data16 = static_cast<uint16_t*>(img->data[z]);
for (unsigned int y = 0; y < dim_y; y++)
{
for (unsigned int x = 0; x < dim_x; x++)
{
int r = data16[(4 * dim_x * y) + (4 * x )];
int g = data16[(4 * dim_x * y) + (4 * x + 1)];
int b = data16[(4 * dim_x * y) + (4 * x + 2)];
int a = data16[(4 * dim_x * y) + (4 * x + 3)];
is_luma = is_luma && (r == g) && (r == b);
has_alpha = has_alpha || ((a ^ 0xC3FF) != 0xFFFF);
// a ^ 0xC3FF returns FFFF if and only if the input is 1.0
}
}
}
}
else // if (img->data_type == ASTCENC_TYPE_F32)
{
assert(img->data_type == ASTCENC_TYPE_F32);
for (unsigned int z = 0; z < dim_z; z++)
{
float* data32 = static_cast<float*>(img->data[z]);
for (unsigned int y = 0; y < dim_y; y++)
{
for (unsigned int x = 0; x < dim_x; x++)
{
float r = data32[(4 * dim_x * y) + (4 * x )];
float g = data32[(4 * dim_x * y) + (4 * x + 1)];
float b = data32[(4 * dim_x * y) + (4 * x + 2)];
float a = data32[(4 * dim_x * y) + (4 * x + 3)];
is_luma = is_luma && (r == g) && (r == b);
has_alpha = has_alpha || (a != 1.0f);
}
}
}
}
return 1 + (is_luma == 0 ? 2 : 0) + (has_alpha ? 1 : 0);
}
/* See header for documentation. */
astcenc_image_ptr astc_img_from_floatx4_array(
const float* data,
unsigned int dim_x,
unsigned int dim_y,
bool y_flip
) {
auto img = alloc_image(16, dim_x, dim_y, 1);
for (unsigned int y = 0; y < dim_y; y++)
{
uint16_t* data16 = static_cast<uint16_t*>(img->data[0]);
unsigned int y_src = y_flip ? (dim_y - y - 1) : y;
const float* src = data + 4 * dim_x * y_src;
for (unsigned int x = 0; x < dim_x; x++)
{
vint4 colorf16 = float_to_float16(vfloat4(
src[4 * x ],
src[4 * x + 1],
src[4 * x + 2],
src[4 * x + 3]
));
data16[(4 * dim_x * y) + (4 * x )] = static_cast<uint16_t>(colorf16.lane<0>());
data16[(4 * dim_x * y) + (4 * x + 1)] = static_cast<uint16_t>(colorf16.lane<1>());
data16[(4 * dim_x * y) + (4 * x + 2)] = static_cast<uint16_t>(colorf16.lane<2>());
data16[(4 * dim_x * y) + (4 * x + 3)] = static_cast<uint16_t>(colorf16.lane<3>());
}
}
return img;
}
/* See header for documentation. */
astcenc_image_ptr astc_img_from_unorm8x4_array(
const uint8_t* data,
unsigned int dim_x,
unsigned int dim_y,
bool y_flip
) {
auto img = alloc_image(8, dim_x, dim_y, 1);
for (unsigned int y = 0; y < dim_y; y++)
{
uint8_t* data8 = static_cast<uint8_t*>(img->data[0]);
unsigned int y_src = y_flip ? (dim_y - y - 1) : y;
const uint8_t* src = data + 4 * dim_x * y_src;
for (unsigned int x = 0; x < dim_x; x++)
{
data8[(4 * dim_x * y) + (4 * x )] = src[4 * x ];
data8[(4 * dim_x * y) + (4 * x + 1)] = src[4 * x + 1];
data8[(4 * dim_x * y) + (4 * x + 2)] = src[4 * x + 2];
data8[(4 * dim_x * y) + (4 * x + 3)] = src[4 * x + 3];
}
}
return img;
}
/* See header for documentation. */
std::vector<float> floatx4_array_from_astc_img(
const astcenc_image* img,
bool y_flip,
unsigned int z_index
) {
size_t dim_x = img->dim_x;
size_t dim_y = img->dim_y;
std::vector<float> buf(4 * dim_x * dim_y);
float* buf_data = buf.data();
assert(z_index < img->dim_z);
if (img->data_type == ASTCENC_TYPE_U8)
{
uint8_t* data8 = static_cast<uint8_t*>(img->data[z_index]);
for (size_t y = 0; y < dim_y; y++)
{
size_t mod_y = y_flip ? dim_y - y - 1 : y;
float* dst = buf_data + y * dim_x * 4;
for (size_t x = 0; x < dim_x; x++)
{
dst[4 * x ] = data8[(4 * dim_x * mod_y) + (4 * x )] * (1.0f / 255.0f);
dst[4 * x + 1] = data8[(4 * dim_x * mod_y) + (4 * x + 1)] * (1.0f / 255.0f);
dst[4 * x + 2] = data8[(4 * dim_x * mod_y) + (4 * x + 2)] * (1.0f / 255.0f);
dst[4 * x + 3] = data8[(4 * dim_x * mod_y) + (4 * x + 3)] * (1.0f / 255.0f);
}
}
}
else if (img->data_type == ASTCENC_TYPE_F16)
{
uint16_t* data16 = static_cast<uint16_t*>(img->data[z_index]);
for (size_t y = 0; y < dim_y; y++)
{
size_t mod_y = y_flip ? dim_y - y - 1 : y;
float *dst = buf_data + y * dim_x * 4;
for (size_t x = 0; x < dim_x; x++)
{
vint4 colori(
data16[(4 * dim_x * mod_y) + (4 * x )],
data16[(4 * dim_x * mod_y) + (4 * x + 1)],
data16[(4 * dim_x * mod_y) + (4 * x + 2)],
data16[(4 * dim_x * mod_y) + (4 * x + 3)]
);
vfloat4 color = float16_to_float(colori);
store(color, dst + 4 * x);
}
}
}
else // if (img->data_type == ASTCENC_TYPE_F32)
{
assert(img->data_type == ASTCENC_TYPE_F32);
float* data32 = static_cast<float*>(img->data[z_index]);
for (size_t y = 0; y < dim_y; y++)
{
size_t mod_y = y_flip ? dim_y - y - 1 : y;
float *dst = buf_data + y * dim_x * 4;
for (size_t x = 0; x < dim_x; x++)
{
dst[4 * x ] = data32[(4 * dim_x * mod_y) + (4 * x )];
dst[4 * x + 1] = data32[(4 * dim_x * mod_y) + (4 * x + 1)];
dst[4 * x + 2] = data32[(4 * dim_x * mod_y) + (4 * x + 2)];
dst[4 * x + 3] = data32[(4 * dim_x * mod_y) + (4 * x + 3)];
}
}
}
return buf;
}