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