Coverage Report

Created: 2026-07-25 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/astc-encoder/Source/astcenc_image.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// ----------------------------------------------------------------------------
3
// Copyright 2011-2026 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 creating in-memory ASTC image structures.
20
 */
21
22
#include <cassert>
23
#include <cstring>
24
25
#include "astcenc_internal.h"
26
27
/**
28
 * @brief Loader pipeline function type for data fetch from memory.
29
 */
30
using pixel_loader = vfloat4(*)(const void*, size_t);
31
32
/**
33
 * @brief Loader pipeline function type for swizzling data in a vector.
34
 */
35
using pixel_swizzler = vfloat4(*)(vfloat4, const astcenc_swizzle&);
36
37
/**
38
 * @brief Loader pipeline function type for converting data in a vector to LNS.
39
 */
40
using pixel_converter = vfloat4(*)(vfloat4, vmask4);
41
42
/**
43
 * @brief Load an 8-bit UNORM texel from a data array.
44
 *
45
 * @param data          The data pointer.
46
 * @param base_offset   The index offset to the start of the pixel.
47
 */
48
static vfloat4 load_texel_u8(
49
  const void* data,
50
  size_t base_offset
51
22.8k
) {
52
22.8k
  const uint8_t* data8 = static_cast<const uint8_t*>(data);
53
22.8k
  return int_to_float(vint4(data8 + base_offset)) / 255.0f;
54
22.8k
}
55
56
/**
57
 * @brief Load a 16-bit fp16 texel from a data array.
58
 *
59
 * @param data          The data pointer.
60
 * @param base_offset   The index offset to the start of the pixel.
61
 */
62
static vfloat4 load_texel_f16(
63
  const void* data,
64
  size_t base_offset
65
0
) {
66
0
  const uint16_t* data16 = static_cast<const uint16_t*>(data);
67
0
  int r = data16[base_offset    ];
68
0
  int g = data16[base_offset + 1];
69
0
  int b = data16[base_offset + 2];
70
0
  int a = data16[base_offset + 3];
71
0
  return float16_to_float(vint4(r, g, b, a));
72
0
}
73
74
/**
75
 * @brief Load a 32-bit float texel from a data array.
76
 *
77
 * @param data          The data pointer.
78
 * @param base_offset   The index offset to the start of the pixel.
79
 */
80
static vfloat4 load_texel_f32(
81
  const void* data,
82
  size_t base_offset
83
0
) {
84
0
  const float* data32 = static_cast<const float*>(data);
85
0
  return vfloat4(data32 + base_offset);
86
0
}
87
88
/**
89
 * @brief Dummy no-op swizzle function.
90
 *
91
 * @param data   The source RGBA vector to swizzle.
92
 * @param swz    The swizzle to use.
93
 */
94
static vfloat4 swz_texel_skip(
95
  vfloat4 data,
96
  const astcenc_swizzle& swz
97
22.8k
) {
98
22.8k
  (void)swz;
99
22.8k
  return data;
100
22.8k
}
101
102
/**
103
 * @brief Swizzle a texel into a new arrangement.
104
 *
105
 * @param data   The source RGBA vector to swizzle.
106
 * @param swz    The swizzle to use.
107
 */
108
static vfloat4 swz_texel(
109
  vfloat4 data,
110
  const astcenc_swizzle& swz
111
0
) {
112
0
  ASTCENC_ALIGNAS float datas[6];
113
114
0
  storea(data, datas);
115
0
  datas[ASTCENC_SWZ_0] = 0.0f;
116
0
  datas[ASTCENC_SWZ_1] = 1.0f;
117
118
0
  return vfloat4(datas[swz.r], datas[swz.g], datas[swz.b], datas[swz.a]);
119
0
}
120
121
/**
122
 * @brief Encode a texel that is entirely LDR linear.
123
 *
124
 * Out-of-range inputs will be clamped to the valid UNORM range.
125
 *
126
 * @param data       The RGBA data to encode.
127
 * @param lns_mask   The mask for the HDR channels that need LNS encoding.
128
 */
129
static vfloat4 encode_texel_unorm(
130
  vfloat4 data,
131
  vmask4 lns_mask
132
22.8k
) {
133
22.8k
  (void)lns_mask;
134
22.8k
  vfloat4 raw_value = data * 65535.0f;
135
136
  // Unorm data must be in 0-1 range, so clamp because data can come from an
137
  // unconstrained float. This will replace NaNs with zero.
138
22.8k
  return clamp(0.0f, 65535.0f, raw_value);
139
22.8k
}
140
141
/**
142
 * @brief Encode a texel that includes at least some HDR LNS texels.
143
 *
144
 * Out-of-range inputs will be clamped to the valid LNS or UNORM range,
145
 * depending on @c lns_mask.
146
 *
147
 * @param data       The RGBA data to encode.
148
 * @param lns_mask   The mask for the HDR channels that need LNS encoding.
149
 */
150
static vfloat4 encode_texel_lns(
151
  vfloat4 data,
152
  vmask4 lns_mask
153
22.8k
) {
154
22.8k
  vfloat4 datav_unorm = encode_texel_unorm(data, lns_mask);
155
156
  // Out-of-range inputs are clamped inside float_to_lns
157
22.8k
  vfloat4 datav_lns = float_to_lns(data);
158
22.8k
  return select(datav_unorm, datav_lns, lns_mask);
159
22.8k
}
160
161
/* See header for documentation. */
162
void load_image_block(
163
  astcenc_profile decode_mode,
164
  const astcenc_image& img,
165
  image_block& blk,
166
  const block_size_descriptor& bsd,
167
  size_t pos_x,
168
  size_t pos_y,
169
  size_t pos_z,
170
  const astcenc_swizzle& swz
171
972
) {
172
972
  size_t size_x = img.dim_x;
173
972
  size_t size_y = img.dim_y;
174
972
  size_t size_z = img.dim_z;
175
176
972
  blk.pos_x = pos_x;
177
972
  blk.pos_y = pos_y;
178
972
  blk.pos_z = pos_z;
179
180
  // True if any non-identity swizzle
181
972
  bool needs_swz = (swz.r != ASTCENC_SWZ_R) || (swz.g != ASTCENC_SWZ_G) ||
182
972
                   (swz.b != ASTCENC_SWZ_B) || (swz.a != ASTCENC_SWZ_A);
183
184
972
  vfloat4 data_min(1e38f);
185
972
  vfloat4 data_mean(0.0f);
186
972
  vfloat4 data_mean_scale(1.0f / static_cast<float>(bsd.texel_count));
187
972
  vfloat4 data_max(-1e38f);
188
972
  vmask4 grayscalev(true);
189
972
  size_t idx = 0;
190
191
  // This works because we impose the same choice everywhere during encode
192
972
  uint8_t rgb_lns = (decode_mode == ASTCENC_PRF_HDR) ||
193
972
                    (decode_mode == ASTCENC_PRF_HDR_RGB_LDR_A) ? 1 : 0;
194
972
  uint8_t a_lns = decode_mode == ASTCENC_PRF_HDR ? 1 : 0;
195
972
  vint4 use_lns(rgb_lns, rgb_lns, rgb_lns, a_lns);
196
972
  vmask4 lns_mask = use_lns != vint4::zero();
197
198
  // Set up the function pointers for loading pipeline as needed
199
972
  pixel_loader loader = load_texel_u8;
200
972
  if (img.data_type == ASTCENC_TYPE_F16)
201
0
  {
202
0
    loader = load_texel_f16;
203
0
  }
204
972
  else if  (img.data_type == ASTCENC_TYPE_F32)
205
0
  {
206
0
    loader = load_texel_f32;
207
0
  }
208
209
972
  pixel_swizzler swizzler = swz_texel_skip;
210
972
  if (needs_swz)
211
0
  {
212
0
    swizzler = swz_texel;
213
0
  }
214
215
972
  pixel_converter converter = encode_texel_unorm;
216
972
  if (any(lns_mask))
217
972
  {
218
972
    converter = encode_texel_lns;
219
972
  }
220
221
1.94k
  for (size_t z = 0; z < bsd.dim_z; z++)
222
972
  {
223
972
    size_t zi = astc::min(pos_z + z, size_z - 1);
224
972
    void* plane = img.data[zi];
225
226
5.31k
    for (size_t y = 0; y < bsd.dim_y; y++)
227
4.33k
    {
228
4.33k
      size_t yi = astc::min(pos_y + y, size_y - 1);
229
230
27.1k
      for (size_t x = 0; x < bsd.dim_x; x++)
231
22.8k
      {
232
22.8k
        size_t xi = astc::min(pos_x + x, size_x - 1);
233
234
22.8k
        vfloat4 datav = loader(plane, (4 * size_x * yi) + (4 * xi));
235
22.8k
        datav = swizzler(datav, swz);
236
22.8k
        datav = converter(datav, lns_mask);
237
238
        // Compute block metadata
239
22.8k
        data_min = min(data_min, datav);
240
22.8k
        data_mean += datav * data_mean_scale;
241
22.8k
        data_max = max(data_max, datav);
242
243
22.8k
        grayscalev = grayscalev & (datav.swz<0,0,0,0>() == datav.swz<1,1,2,2>());
244
245
22.8k
        blk.data_r[idx] = datav.lane<0>();
246
22.8k
        blk.data_g[idx] = datav.lane<1>();
247
22.8k
        blk.data_b[idx] = datav.lane<2>();
248
22.8k
        blk.data_a[idx] = datav.lane<3>();
249
250
22.8k
        blk.rgb_lns[idx] = rgb_lns;
251
22.8k
        blk.alpha_lns[idx] = a_lns;
252
253
22.8k
        idx++;
254
22.8k
      }
255
4.33k
    }
256
972
  }
257
258
  // Reverse the encoding so we store origin block in the original format
259
972
  vfloat4 data_enc = blk.texel(0);
260
972
  vfloat4 data_enc_unorm = data_enc / 65535.0f;
261
972
  vfloat4 data_enc_lns = vfloat4::zero();
262
263
972
  if (rgb_lns || a_lns)
264
972
  {
265
972
    data_enc_lns = float16_to_float(lns_to_sf16(float_to_int(data_enc)));
266
972
  }
267
268
972
  blk.origin_texel = select(data_enc_unorm, data_enc_lns, lns_mask);
269
270
  // Store block metadata
271
972
  blk.data_min = data_min;
272
972
  blk.data_mean = data_mean;
273
972
  blk.data_max = data_max;
274
972
  blk.grayscale = all(grayscalev);
275
972
}
276
277
/* See header for documentation. */
278
void load_image_block_fast_ldr(
279
  astcenc_profile decode_mode,
280
  const astcenc_image& img,
281
  image_block& blk,
282
  const block_size_descriptor& bsd,
283
  size_t pos_x,
284
  size_t pos_y,
285
  size_t pos_z,
286
  const astcenc_swizzle& swz
287
1.08k
) {
288
1.08k
  (void)swz;
289
1.08k
  (void)decode_mode;
290
291
1.08k
  size_t size_x = img.dim_x;
292
1.08k
  size_t size_y = img.dim_y;
293
294
1.08k
  blk.pos_x = pos_x;
295
1.08k
  blk.pos_y = pos_y;
296
1.08k
  blk.pos_z = pos_z;
297
298
1.08k
  vfloat4 data_min(1e38f);
299
1.08k
  vfloat4 data_mean = vfloat4::zero();
300
1.08k
  vfloat4 data_max(-1e38f);
301
1.08k
  vmask4 grayscalev(true);
302
1.08k
  size_t idx = 0;
303
304
1.08k
  const uint8_t* plane = static_cast<const uint8_t*>(img.data[0]);
305
6.17k
  for (size_t y = pos_y; y < pos_y + bsd.dim_y; y++)
306
5.09k
  {
307
5.09k
    size_t yi = astc::min(y, size_y - 1);
308
309
33.7k
    for (size_t x = pos_x; x < pos_x + bsd.dim_x; x++)
310
28.6k
    {
311
28.6k
      size_t xi = astc::min(x, size_x - 1);
312
313
28.6k
      vint4 datavi = vint4(plane + (4 * size_x * yi) + (4 * xi));
314
28.6k
      vfloat4 datav = int_to_float(datavi) * (65535.0f / 255.0f);
315
316
      // Compute block metadata
317
28.6k
      data_min = min(data_min, datav);
318
28.6k
      data_mean += datav;
319
28.6k
      data_max = max(data_max, datav);
320
321
28.6k
      grayscalev = grayscalev & (datav.swz<0,0,0,0>() == datav.swz<1,1,2,2>());
322
323
28.6k
      blk.data_r[idx] = datav.lane<0>();
324
28.6k
      blk.data_g[idx] = datav.lane<1>();
325
28.6k
      blk.data_b[idx] = datav.lane<2>();
326
28.6k
      blk.data_a[idx] = datav.lane<3>();
327
328
28.6k
      idx++;
329
28.6k
    }
330
5.09k
  }
331
332
  // Reverse the encoding so we store origin block in the original format
333
1.08k
  blk.origin_texel = blk.texel(0) / 65535.0f;
334
335
  // Store block metadata
336
1.08k
  blk.rgb_lns[0] = 0;
337
1.08k
  blk.alpha_lns[0] = 0;
338
1.08k
  blk.data_min = data_min;
339
1.08k
  blk.data_mean = data_mean / static_cast<float>(bsd.texel_count);
340
1.08k
  blk.data_max = data_max;
341
1.08k
  blk.grayscale = all(grayscalev);
342
1.08k
}
343
344
/* See header for documentation. */
345
void store_image_block(
346
  astcenc_image& img,
347
  const image_block& blk,
348
  const block_size_descriptor& bsd,
349
  size_t pos_x,
350
  size_t pos_y,
351
  size_t pos_z,
352
  const astcenc_swizzle& swz
353
3.84k
) {
354
3.84k
  size_t size_x = img.dim_x;
355
3.84k
  size_t start_x = pos_x;
356
3.84k
  size_t end_x = astc::min(size_x, pos_x + bsd.dim_x);
357
3.84k
  size_t count_x = end_x - start_x;
358
3.84k
  size_t nudge_x = bsd.dim_x - count_x;
359
360
3.84k
  size_t size_y = img.dim_y;
361
3.84k
  size_t start_y = pos_y;
362
3.84k
  size_t end_y = astc::min(size_y, pos_y + bsd.dim_y);
363
3.84k
  size_t count_y = end_y - start_y;
364
3.84k
  size_t nudge_y = (bsd.dim_y - count_y) * bsd.dim_x;
365
366
3.84k
  size_t size_z = img.dim_z;
367
3.84k
  size_t start_z = pos_z;
368
3.84k
  size_t end_z = astc::min(size_z, pos_z + bsd.dim_z);
369
370
3.84k
  size_t idx = 0;
371
372
  // True if any non-identity swizzle
373
3.84k
  bool needs_swz = (swz.r != ASTCENC_SWZ_R) || (swz.g != ASTCENC_SWZ_G) ||
374
3.84k
                   (swz.b != ASTCENC_SWZ_B) || (swz.a != ASTCENC_SWZ_A);
375
376
  // True if any swizzle uses Z reconstruct
377
3.84k
  bool needs_z = (swz.r == ASTCENC_SWZ_Z) || (swz.g == ASTCENC_SWZ_Z) ||
378
3.84k
                 (swz.b == ASTCENC_SWZ_Z) || (swz.a == ASTCENC_SWZ_Z);
379
380
3.84k
  if (img.data_type == ASTCENC_TYPE_U8)
381
3.84k
  {
382
7.69k
    for (size_t z = start_z; z < end_z; z++)
383
3.84k
    {
384
      // Fetch the image plane
385
3.84k
      uint8_t* data8 = static_cast<uint8_t*>(img.data[z]);
386
387
33.0k
      for (size_t y = start_y; y < end_y; y++)
388
29.2k
      {
389
29.2k
        uint8_t* data8_row = data8 + (4 * size_x * y) + (4 * start_x);
390
391
107k
        for (size_t x = 0; x < count_x; x += ASTCENC_SIMD_WIDTH)
392
77.9k
        {
393
77.9k
          size_t max_texels = ASTCENC_SIMD_WIDTH;
394
77.9k
          size_t used_texels = astc::min(count_x - x, max_texels);
395
396
          // Unaligned load as rows are not always SIMD_WIDTH long
397
77.9k
          vfloat data_r(blk.data_r + idx);
398
77.9k
          vfloat data_g(blk.data_g + idx);
399
77.9k
          vfloat data_b(blk.data_b + idx);
400
77.9k
          vfloat data_a(blk.data_a + idx);
401
402
          // Clamp values to [0.0, 1.0] range before unorm conversion
403
          //   - Values > 1.0 are possible for all HDR blocks
404
          //   - Values < 0.0 are possible for HDR void-extent blocks
405
77.9k
          vint data_ri = float_to_int_rtn(clampzo(data_r) * 255.0f);
406
77.9k
          vint data_gi = float_to_int_rtn(clampzo(data_g) * 255.0f);
407
77.9k
          vint data_bi = float_to_int_rtn(clampzo(data_b) * 255.0f);
408
77.9k
          vint data_ai = float_to_int_rtn(clampzo(data_a) * 255.0f);
409
410
77.9k
          if (needs_swz)
411
0
          {
412
0
            vint swizzle_table[7];
413
0
            swizzle_table[ASTCENC_SWZ_0] = vint(0);
414
0
            swizzle_table[ASTCENC_SWZ_1] = vint(255);
415
0
            swizzle_table[ASTCENC_SWZ_R] = data_ri;
416
0
            swizzle_table[ASTCENC_SWZ_G] = data_gi;
417
0
            swizzle_table[ASTCENC_SWZ_B] = data_bi;
418
0
            swizzle_table[ASTCENC_SWZ_A] = data_ai;
419
420
0
            if (needs_z)
421
0
            {
422
0
              vfloat data_x = (data_r * vfloat(2.0f)) - vfloat(1.0f);
423
0
              vfloat data_y = (data_a * vfloat(2.0f)) - vfloat(1.0f);
424
0
              vfloat data_z = vfloat(1.0f) - (data_x * data_x) - (data_y * data_y);
425
0
              data_z = max(data_z, 0.0f);
426
0
              data_z = (sqrt(data_z) * vfloat(0.5f)) + vfloat(0.5f);
427
428
0
              swizzle_table[ASTCENC_SWZ_Z] = float_to_int_rtn(min(data_z, 1.0f) * 255.0f);
429
0
            }
430
431
0
            data_ri = swizzle_table[swz.r];
432
0
            data_gi = swizzle_table[swz.g];
433
0
            data_bi = swizzle_table[swz.b];
434
0
            data_ai = swizzle_table[swz.a];
435
0
          }
436
437
          // Errors are NaN encoded - convert to magenta error color
438
          // Branch is OK here - it is almost never true so predicts well
439
77.9k
          vmask nan_mask = data_r != data_r;
440
77.9k
          if (any(nan_mask))
441
20.8k
          {
442
20.8k
            data_ri = select(data_ri, vint(0xFF), nan_mask);
443
20.8k
            data_gi = select(data_gi, vint(0x00), nan_mask);
444
20.8k
            data_bi = select(data_bi, vint(0xFF), nan_mask);
445
20.8k
            data_ai = select(data_ai, vint(0xFF), nan_mask);
446
20.8k
          }
447
448
77.9k
          vint data_rgbai = interleave_rgba8(data_ri, data_gi, data_bi, data_ai);
449
          // Static cast must be safe, as used_texels must be less than vector length
450
77.9k
          vmask store_mask = vint::lane_id() < vint(static_cast<int>(used_texels));
451
77.9k
          store_lanes_masked(data8_row, data_rgbai, store_mask);
452
453
77.9k
          data8_row += ASTCENC_SIMD_WIDTH * 4;
454
77.9k
          idx += used_texels;
455
77.9k
        }
456
29.2k
        idx += nudge_x;
457
29.2k
      }
458
3.84k
      idx += nudge_y;
459
3.84k
    }
460
3.84k
  }
461
0
  else if (img.data_type == ASTCENC_TYPE_F16)
462
0
  {
463
0
    for (size_t z = start_z; z < end_z; z++)
464
0
    {
465
      // Fetch the image plane
466
0
      uint16_t* data16 = static_cast<uint16_t*>(img.data[z]);
467
468
0
      for (size_t y = start_y; y < end_y; y++)
469
0
      {
470
0
        uint16_t* data16_row = data16 + (4 * size_x * y) + (4 * start_x);
471
472
0
        for (size_t x = 0; x < count_x; x++)
473
0
        {
474
0
          vint4 color;
475
476
          // NaNs are handled inline - no need to special case
477
0
          if (needs_swz)
478
0
          {
479
0
            float data[7];
480
0
            data[ASTCENC_SWZ_0] = 0.0f;
481
0
            data[ASTCENC_SWZ_1] = 1.0f;
482
0
            data[ASTCENC_SWZ_R] = blk.data_r[idx];
483
0
            data[ASTCENC_SWZ_G] = blk.data_g[idx];
484
0
            data[ASTCENC_SWZ_B] = blk.data_b[idx];
485
0
            data[ASTCENC_SWZ_A] = blk.data_a[idx];
486
487
0
            if (needs_z)
488
0
            {
489
0
              float xN = (data[0] * 2.0f) - 1.0f;
490
0
              float yN = (data[3] * 2.0f) - 1.0f;
491
0
              float zN = 1.0f - xN * xN - yN * yN;
492
0
              if (zN < 0.0f)
493
0
              {
494
0
                zN = 0.0f;
495
0
              }
496
0
              data[ASTCENC_SWZ_Z] = (astc::sqrt(zN) * 0.5f) + 0.5f;
497
0
            }
498
499
0
            vfloat4 colorf(data[swz.r], data[swz.g], data[swz.b], data[swz.a]);
500
0
            color = float_to_float16(colorf);
501
0
          }
502
0
          else
503
0
          {
504
0
            vfloat4 colorf = blk.texel(idx);
505
0
            color = float_to_float16(colorf);
506
0
          }
507
508
          // TODO: Vectorize with store N shorts?
509
0
          data16_row[0] = static_cast<uint16_t>(color.lane<0>());
510
0
          data16_row[1] = static_cast<uint16_t>(color.lane<1>());
511
0
          data16_row[2] = static_cast<uint16_t>(color.lane<2>());
512
0
          data16_row[3] = static_cast<uint16_t>(color.lane<3>());
513
0
          data16_row += 4;
514
0
          idx++;
515
0
        }
516
0
        idx += nudge_x;
517
0
      }
518
0
      idx += nudge_y;
519
0
    }
520
0
  }
521
0
  else // if (img.data_type == ASTCENC_TYPE_F32)
522
0
  {
523
0
    assert(img.data_type == ASTCENC_TYPE_F32);
524
525
0
    for (size_t z = start_z; z < end_z; z++)
526
0
    {
527
      // Fetch the image plane
528
0
      float* data32 = static_cast<float*>(img.data[z]);
529
530
0
      for (size_t y = start_y; y < end_y; y++)
531
0
      {
532
0
        float* data32_row = data32 + (4 * size_x * y) + (4 * start_x);
533
534
0
        for (size_t x = 0; x < count_x; x++)
535
0
        {
536
0
          vfloat4 color = blk.texel(idx);
537
538
          // NaNs are handled inline - no need to special case
539
0
          if (needs_swz)
540
0
          {
541
0
            float data[7];
542
0
            data[ASTCENC_SWZ_0] = 0.0f;
543
0
            data[ASTCENC_SWZ_1] = 1.0f;
544
0
            data[ASTCENC_SWZ_R] = color.lane<0>();
545
0
            data[ASTCENC_SWZ_G] = color.lane<1>();
546
0
            data[ASTCENC_SWZ_B] = color.lane<2>();
547
0
            data[ASTCENC_SWZ_A] = color.lane<3>();
548
549
0
            if (needs_z)
550
0
            {
551
0
              float xN = (data[0] * 2.0f) - 1.0f;
552
0
              float yN = (data[3] * 2.0f) - 1.0f;
553
0
              float zN = 1.0f - xN * xN - yN * yN;
554
0
              if (zN < 0.0f)
555
0
              {
556
0
                zN = 0.0f;
557
0
              }
558
0
              data[ASTCENC_SWZ_Z] = (astc::sqrt(zN) * 0.5f) + 0.5f;
559
0
            }
560
561
0
            color = vfloat4(data[swz.r], data[swz.g], data[swz.b], data[swz.a]);
562
0
          }
563
564
0
          store(color, data32_row);
565
0
          data32_row += 4;
566
0
          idx++;
567
0
        }
568
0
        idx += nudge_x;
569
0
      }
570
0
      idx += nudge_y;
571
0
    }
572
0
  }
573
3.84k
}