1 // SPDX-License-Identifier: Apache-2.0
2 // ----------------------------------------------------------------------------
3 // Copyright 2011-2020 Arm Limited
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 // use this file except in compliance with the License. You may obtain a copy
7 // of the License at:
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 // License for the specific language governing permissions and limitations
15 // under the License.
16 // ----------------------------------------------------------------------------
17 
18 /**
19  * @brief Functions for loading/storing ASTC compressed images.
20  */
21 
22 
23 #include "astc_codec_internals.h"
24 
25 // conversion functions between the LNS representation and the FP16 representation.
float_to_lns(float p)26 float float_to_lns(float p)
27 {
28 	if (astc::isnan(p) || p <= 1.0f / 67108864.0f)
29 	{
30 		// underflow or NaN value, return 0.
31 		// We count underflow if the input value is smaller than 2^-26.
32 		return 0;
33 	}
34 
35 	if (fabsf(p) >= 65536.0f)
36 	{
37 		// overflow, return a +INF value
38 		return 65535;
39 	}
40 
41 	int expo;
42 	float normfrac = frexpf(p, &expo);
43 	float p1;
44 	if (expo < -13)
45 	{
46 		// input number is smaller than 2^-14. In this case, multiply by 2^25.
47 		p1 = p * 33554432.0f;
48 		expo = 0;
49 	}
50 	else
51 	{
52 		expo += 14;
53 		p1 = (normfrac - 0.5f) * 4096.0f;
54 	}
55 
56 	if (p1 < 384.0f)
57 		p1 *= 4.0f / 3.0f;
58 	else if (p1 <= 1408.0f)
59 		p1 += 128.0f;
60 	else
61 		p1 = (p1 + 512.0f) * (4.0f / 5.0f);
62 
63 	p1 += expo * 2048.0f;
64 	return p1 + 1.0f;
65 }
66 
lns_to_sf16(uint16_t p)67 uint16_t lns_to_sf16(uint16_t p)
68 {
69 	uint16_t mc = p & 0x7FF;
70 	uint16_t ec = p >> 11;
71 	uint16_t mt;
72 	if (mc < 512)
73 		mt = 3 * mc;
74 	else if (mc < 1536)
75 		mt = 4 * mc - 512;
76 	else
77 		mt = 5 * mc - 2048;
78 
79 	uint16_t res = (ec << 10) | (mt >> 3);
80 	if (res >= 0x7BFF)
81 		res = 0x7BFF;
82 	return res;
83 }
84 
85 // conversion function from 16-bit LDR value to FP16.
86 // note: for LDR interpolation, it is impossible to get a denormal result;
87 // this simplifies the conversion.
88 // FALSE; we can receive a very small UNORM16 through the constant-block.
unorm16_to_sf16(uint16_t p)89 uint16_t unorm16_to_sf16(uint16_t p)
90 {
91 	if (p == 0xFFFF)
92 		return 0x3C00;			// value of 1.0 .
93 	if (p < 4)
94 		return p << 8;
95 
96 	int lz = clz32(p) - 16;
97 	p <<= (lz + 1);
98 	p >>= 6;
99 	p |= (14 - lz) << 10;
100 	return p;
101 }
102 
103 // helper function to initialize the work-data from the orig-data
imageblock_initialize_work_from_orig(imageblock * pb,int pixelcount)104 void imageblock_initialize_work_from_orig(
105 	imageblock* pb,
106 	int pixelcount
107 ) {
108 	float *fptr = pb->orig_data;
109 
110 	for (int i = 0; i < pixelcount; i++)
111 	{
112 		if (pb->rgb_lns[i])
113 		{
114 			pb->data_r[i] = float_to_lns(fptr[0]);
115 			pb->data_g[i] = float_to_lns(fptr[1]);
116 			pb->data_b[i] = float_to_lns(fptr[2]);
117 		}
118 		else
119 		{
120 			pb->data_r[i] = fptr[0] * 65535.0f;
121 			pb->data_g[i] = fptr[1] * 65535.0f;
122 			pb->data_b[i] = fptr[2] * 65535.0f;
123 		}
124 
125 		if (pb->alpha_lns[i])
126 		{
127 			pb->data_a[i] = float_to_lns(fptr[3]);
128 		}
129 		else
130 		{
131 			pb->data_a[i] = fptr[3] * 65535.0f;
132 		}
133 
134 		fptr += 4;
135 	}
136 }
137 
138 // helper function to initialize the orig-data from the work-data
imageblock_initialize_orig_from_work(imageblock * pb,int pixelcount)139 void imageblock_initialize_orig_from_work(
140 	imageblock* pb,
141 	int pixelcount
142 ) {
143 	float *fptr = pb->orig_data;
144 
145 	for (int i = 0; i < pixelcount; i++)
146 	{
147 		if (pb->rgb_lns[i])
148 		{
149 			fptr[0] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_r[i]));
150 			fptr[1] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_g[i]));
151 			fptr[2] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_b[i]));
152 		}
153 		else
154 		{
155 			fptr[0] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_r[i]));
156 			fptr[1] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_g[i]));
157 			fptr[2] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_b[i]));
158 		}
159 
160 		if (pb->alpha_lns[i])
161 		{
162 			fptr[3] = sf16_to_float(lns_to_sf16((uint16_t)pb->data_a[i]));
163 		}
164 		else
165 		{
166 			fptr[3] = sf16_to_float(unorm16_to_sf16((uint16_t)pb->data_a[i]));
167 		}
168 
169 		fptr += 4;
170 	}
171 }
172 
173 /*
174    For an imageblock, update its flags.
175    The updating is done based on data, not orig_data.
176 */
update_imageblock_flags(imageblock * pb,int xdim,int ydim,int zdim)177 void update_imageblock_flags(
178 	imageblock* pb,
179 	int xdim,
180 	int ydim,
181 	int zdim
182 ) {
183 	int i;
184 	float red_min = 1e38f, red_max = -1e38f;
185 	float green_min = 1e38f, green_max = -1e38f;
186 	float blue_min = 1e38f, blue_max = -1e38f;
187 	float alpha_min = 1e38f, alpha_max = -1e38f;
188 
189 	int texels_per_block = xdim * ydim * zdim;
190 
191 	int grayscale = 1;
192 
193 	for (i = 0; i < texels_per_block; i++)
194 	{
195 		float red = pb->data_r[i];
196 		float green = pb->data_g[i];
197 		float blue = pb->data_b[i];
198 		float alpha = pb->data_a[i];
199 		if (red < red_min)
200 			red_min = red;
201 		if (red > red_max)
202 			red_max = red;
203 		if (green < green_min)
204 			green_min = green;
205 		if (green > green_max)
206 			green_max = green;
207 		if (blue < blue_min)
208 			blue_min = blue;
209 		if (blue > blue_max)
210 			blue_max = blue;
211 		if (alpha < alpha_min)
212 			alpha_min = alpha;
213 		if (alpha > alpha_max)
214 			alpha_max = alpha;
215 
216 		if (grayscale == 1 && (red != green || red != blue))
217 			grayscale = 0;
218 	}
219 
220 	pb->red_min = red_min;
221 	pb->red_max = red_max;
222 	pb->green_min = green_min;
223 	pb->green_max = green_max;
224 	pb->blue_min = blue_min;
225 	pb->blue_max = blue_max;
226 	pb->alpha_min = alpha_min;
227 	pb->alpha_max = alpha_max;
228 	pb->grayscale = grayscale;
229 }
230 
231