/*----------------------------------------------------------------------------*/ /** * This confidential and proprietary software may be used only as * authorised by a licensing agreement from ARM Limited * (C) COPYRIGHT 2011-2012 ARM Limited * ALL RIGHTS RESERVED * * The entire notice above must be reproduced on all authorised * copies and copies may only be made to the extent permitted * by a licensing agreement from ARM Limited. * * @brief Functions for managing ASTC codec images. */ /*----------------------------------------------------------------------------*/ #include #include "astc_codec_internals.h" #include "softfloat.h" #include #include // 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 (fabs(p) >= 65536.0f) { // overflow, return a +INF value return 65535; } int expo; float normfrac = frexp(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; } void imageblock_initialize_deriv_from_work_and_orig(imageblock * pb, int pixelcount) { int i; const float *fptr = pb->orig_data; const float *wptr = pb->work_data; float *dptr = pb->deriv_data; for (i = 0; i < pixelcount; i++) { // compute derivatives for RGB first if (pb->rgb_lns[i]) { float r = MAX(fptr[0], 6e-5f); float g = MAX(fptr[1], 6e-5f); float b = MAX(fptr[2], 6e-5f); float rderiv = (float_to_lns(r * 1.05f) - float_to_lns(r)) / (r * 0.05f); float gderiv = (float_to_lns(g * 1.05f) - float_to_lns(g)) / (g * 0.05f); float bderiv = (float_to_lns(b * 1.05f) - float_to_lns(b)) / (b * 0.05f); // the derivative may not actually take values smaller than 1/32 or larger than 2^25; // if it does, we clamp it. if (rderiv < (1.0f / 32.0f)) rderiv = (1.0f / 32.0f); else if (rderiv > 33554432.0f) rderiv = 33554432.0f; if (gderiv < (1.0f / 32.0f)) gderiv = (1.0f / 32.0f); else if (gderiv > 33554432.0f) gderiv = 33554432.0f; if (bderiv < (1.0f / 32.0f)) bderiv = (1.0f / 32.0f); else if (bderiv > 33554432.0f) bderiv = 33554432.0f; dptr[0] = rderiv; dptr[1] = gderiv; dptr[2] = bderiv; } else { dptr[0] = 65535.0f; dptr[1] = 65535.0f; dptr[2] = 65535.0f; } // then compute derivatives for Alpha if (pb->alpha_lns[i]) { float a = MAX(fptr[3], 6e-5f); float aderiv = (float_to_lns(a * 1.05f) - float_to_lns(a)) / (a * 0.05f); // the derivative may not actually take values smaller than 1/32 or larger than 2^25; // if it does, we clamp it. if (aderiv < (1.0f / 32.0f)) aderiv = (1.0f / 32.0f); else if (aderiv > 33554432.0f) aderiv = 33554432.0f; dptr[3] = aderiv; } else { dptr[3] = 65535.0f; } fptr += 4; wptr += 4; dptr += 4; } } // helper function to initialize the work-data from the orig-data void imageblock_initialize_work_from_orig(imageblock * pb, int pixelcount) { int i; float *fptr = pb->orig_data; float *wptr = pb->work_data; for (i = 0; i < pixelcount; i++) { if (pb->rgb_lns[i]) { wptr[0] = float_to_lns(fptr[0]); wptr[1] = float_to_lns(fptr[1]); wptr[2] = float_to_lns(fptr[2]); } else { wptr[0] = fptr[0] * 65535.0f; wptr[1] = fptr[1] * 65535.0f; wptr[2] = fptr[2] * 65535.0f; } if (pb->alpha_lns[i]) { wptr[3] = float_to_lns(fptr[3]); } else { wptr[3] = fptr[3] * 65535.0f; } fptr += 4; wptr += 4; } imageblock_initialize_deriv_from_work_and_orig(pb, pixelcount); } // helper function to initialize the orig-data from the work-data void imageblock_initialize_orig_from_work(imageblock * pb, int pixelcount) { int i; float *fptr = pb->orig_data; float *wptr = pb->work_data; for (i = 0; i < pixelcount; i++) { if (pb->rgb_lns[i]) { fptr[0] = sf16_to_float(lns_to_sf16((uint16_t) wptr[0])); fptr[1] = sf16_to_float(lns_to_sf16((uint16_t) wptr[1])); fptr[2] = sf16_to_float(lns_to_sf16((uint16_t) wptr[2])); } else { fptr[0] = sf16_to_float(unorm16_to_sf16((uint16_t) wptr[0])); fptr[1] = sf16_to_float(unorm16_to_sf16((uint16_t) wptr[1])); fptr[2] = sf16_to_float(unorm16_to_sf16((uint16_t) wptr[2])); } if (pb->alpha_lns[i]) { fptr[3] = sf16_to_float(lns_to_sf16((uint16_t) wptr[3])); } else { fptr[3] = sf16_to_float(unorm16_to_sf16((uint16_t) wptr[3])); } fptr += 4; wptr += 4; } imageblock_initialize_deriv_from_work_and_orig(pb, pixelcount); } /* For an imageblock, update its flags. The updating is done based on work_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->work_data[4 * i]; float green = pb->work_data[4 * i + 1]; float blue = pb->work_data[4 * i + 2]; float alpha = pb->work_data[4 * i + 3]; 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; }