blob: 1be06ed6e971c4fcc7bd215983d696843178a356 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2011-2020 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 loading/storing ASTC compressed images.
*/
#include "astc_codec_internals.h"
// conversion functions between the LNS representation and the FP16 representation.
float float_to_lns(float p)
{
if (astc::isnan(p) || p <= 1.0f / 67108864.0f)
{
// underflow or NaN value, return 0.
// We count underflow if the input value is smaller than 2^-26.
return 0;
}
if (fabsf(p) >= 65536.0f)
{
// overflow, return a +INF value
return 65535;
}
int expo;
float normfrac = frexpf(p, &expo);
float p1;
if (expo < -13)
{
// input number is smaller than 2^-14. In this case, multiply by 2^25.
p1 = p * 33554432.0f;
expo = 0;
}
else
{
expo += 14;
p1 = (normfrac - 0.5f) * 4096.0f;
}
if (p1 < 384.0f)
p1 *= 4.0f / 3.0f;
else if (p1 <= 1408.0f)
p1 += 128.0f;
else
p1 = (p1 + 512.0f) * (4.0f / 5.0f);
p1 += expo * 2048.0f;
return p1 + 1.0f;
}
uint16_t lns_to_sf16(uint16_t p)
{
uint16_t mc = p & 0x7FF;
uint16_t ec = p >> 11;
uint16_t mt;
if (mc < 512)
mt = 3 * mc;
else if (mc < 1536)
mt = 4 * mc - 512;
else
mt = 5 * mc - 2048;
uint16_t res = (ec << 10) | (mt >> 3);
if (res >= 0x7BFF)
res = 0x7BFF;
return res;
}
// conversion function from 16-bit LDR value to FP16.
// note: for LDR interpolation, it is impossible to get a denormal result;
// this simplifies the conversion.
// FALSE; we can receive a very small UNORM16 through the constant-block.
uint16_t unorm16_to_sf16(uint16_t p)
{
if (p == 0xFFFF)
return 0x3C00; // value of 1.0 .
if (p < 4)
return p << 8;
int lz = clz32(p) - 16;
p <<= (lz + 1);
p >>= 6;
p |= (14 - lz) << 10;
return p;
}
// helper function to initialize the work-data from the orig-data
void imageblock_initialize_work_from_orig(
imageblock* pb,
int pixelcount
) {
float *fptr = pb->orig_data;
for (int i = 0; i < pixelcount; i++)
{
if (pb->rgb_lns[i])
{
pb->data_r[i] = float_to_lns(fptr[0]);
pb->data_g[i] = float_to_lns(fptr[1]);
pb->data_b[i] = float_to_lns(fptr[2]);
}
else
{
pb->data_r[i] = fptr[0] * 65535.0f;
pb->data_g[i] = fptr[1] * 65535.0f;
pb->data_b[i] = fptr[2] * 65535.0f;
}
if (pb->alpha_lns[i])
{
pb->data_a[i] = float_to_lns(fptr[3]);
}
else
{
pb->data_a[i] = fptr[3] * 65535.0f;
}
fptr += 4;
}
}
// helper function to initialize the orig-data from the work-data
void imageblock_initialize_orig_from_work(
imageblock* pb,
int pixelcount
) {
float *fptr = pb->orig_data;
for (int i = 0; i < pixelcount; i++)
{
if (pb->rgb_lns[i])
{
fptr[0] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_r[i]));
fptr[1] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_g[i]));
fptr[2] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_b[i]));
}
else
{
fptr[0] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_r[i]));
fptr[1] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_g[i]));
fptr[2] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_b[i]));
}
if (pb->alpha_lns[i])
{
fptr[3] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_a[i]));
}
else
{
fptr[3] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_a[i]));
}
fptr += 4;
}
}
/*
For an imageblock, update its flags.
The updating is done based on data, not orig_data.
*/
void update_imageblock_flags(
imageblock* pb,
int xdim,
int ydim,
int zdim
) {
int i;
float red_min = 1e38f, red_max = -1e38f;
float green_min = 1e38f, green_max = -1e38f;
float blue_min = 1e38f, blue_max = -1e38f;
float alpha_min = 1e38f, alpha_max = -1e38f;
int texels_per_block = xdim * ydim * zdim;
int grayscale = 1;
for (i = 0; i < texels_per_block; i++)
{
float red = pb->data_r[i];
float green = pb->data_g[i];
float blue = pb->data_b[i];
float alpha = pb->data_a[i];
if (red < red_min)
red_min = red;
if (red > red_max)
red_max = red;
if (green < green_min)
green_min = green;
if (green > green_max)
green_max = green;
if (blue < blue_min)
blue_min = blue;
if (blue > blue_max)
blue_max = blue;
if (alpha < alpha_min)
alpha_min = alpha;
if (alpha > alpha_max)
alpha_max = alpha;
if (grayscale == 1 && (red != green || red != blue))
grayscale = 0;
}
pb->red_min = red_min;
pb->red_max = red_max;
pb->green_min = green_min;
pb->green_max = green_max;
pb->blue_min = blue_min;
pb->blue_max = blue_max;
pb->alpha_min = alpha_min;
pb->alpha_max = alpha_max;
pb->grayscale = grayscale;
}