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