// 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; }