• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PredatorCZ / PreCore / 529

02 Aug 2024 04:58PM UTC coverage: 53.356% (-0.04%) from 53.394%
529

push

github

PredatorCZ
improve texel data process

111 of 202 new or added lines in 1 file covered. (54.95%)

17 existing lines in 1 file now uncovered.

4237 of 7941 relevant lines covered (53.36%)

10451.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.42
/src/app/texel.cpp
1
#define QOI_IMPLEMENTATION
2
#define QOI_NO_STDIO
3

4
#include "spike/app/texel.hpp"
5
#include "bc7decomp.h"
6
#include "pvr_decompress.hpp"
7
#include "qoi.h"
8
#include "spike/app/context.hpp"
9
#include "spike/crypto/crc32.hpp"
10
#include "spike/format/DDS.hpp"
11
#include "spike/gpu/BlockDecoder.inl"
12
#include "spike/gpu/addr_ps3.hpp"
13
#include "spike/io/binwritter_stream.hpp"
14
#include "spike/reflect/reflector.hpp"
15
#include "spike/uni/format.hpp"
16
#include "spike/util/endian.hpp"
17
#include <sstream>
18
#include <variant>
19

20
bool BlockCompression(TexelInputFormatType fmt) {
×
21
  using F = TexelInputFormatType;
22
  switch (fmt) {
×
23
  case F::BC1:
24
  case F::BC2:
25
  case F::BC3:
26
  case F::BC4:
27
  case F::BC5:
28
  case F::BC7:
29
    return true;
30
  default:
×
31
    return false;
×
32
  }
33
};
34

35
TexelContextFormat OutputFormat() {
×
36
  return mainSettings.texelSettings.outputFormat;
56✔
37
}
38

39
bool IsFormatSupported(TexelContextFormat ofmt, TexelInputFormatType fmt) {
128✔
40
  using F = TexelInputFormatType;
41

42
  switch (fmt) {
128✔
43
    // Not supported
44
  case F::PVRTC2:
45
  case F::PVRTC4:
46
  case F::ETC1:
47
    return true;
48

49
    // DDS, DDS_Legacy only
50
  case F::BC1:
17✔
51
  case F::BC2:
52
  case F::BC3:
53
    return ofmt == TexelContextFormat::UPNG ||
54
           ofmt == TexelContextFormat::QOI ||
17✔
55
           ofmt == TexelContextFormat::QOI_BMP;
17✔
56

57
    // DDS only
58
  case F::BC4:
8✔
59
  case F::BC5:
60
  case F::BC7:
61
  case F::RG8:
62
  case F::RGBA16:
63
  case F::BC6:
64
  case F::RGB9E5:
65
    return ofmt != TexelContextFormat::DDS;
8✔
66

67
    // BMP, DDS only
68
  case F::RGB10A2:
4✔
69
  case F::RGB5A1:
70
    return ofmt == TexelContextFormat::UPNG ||
4✔
71
           ofmt == TexelContextFormat::QOI ||
72
           ofmt == TexelContextFormat::DDS_Legacy;
4✔
73

74
    // BMP only
75
  case F::P8:
×
76
  case F::P4:
77
    return ofmt != TexelContextFormat::QOI_BMP;
×
78

79
  // DDS, DDS_Legacy, BMP only
80
  case F::RGBA4:
6✔
81
  case F::R5G6B5:
82
    return ofmt == TexelContextFormat::UPNG || ofmt == TexelContextFormat::QOI;
6✔
83

84
  // DDS, DDS_Legacy, BMP, UPNG only
85
  case F::R8:
4✔
86
    return ofmt == TexelContextFormat::QOI;
4✔
87

88
    // Supported for all
89
  case F::RGBA8:
90
  case F::INVALID:
91
    return false;
92

93
  // QOI, DDS_Legacy, UPNG only
94
  case F::RGB8:
2✔
95
    return ofmt == TexelContextFormat::DDS;
2✔
96
  }
97

98
  return false;
99
}
100

NEW
101
bool MustSwap(TexelInputFormatType fmt, bool shouldSwap) {
×
102
  using F = TexelInputFormatType;
103

NEW
104
  switch (fmt) {
×
105
  case F::R5G6B5:
106
  case F::RGBA4:
107
  case F::RGB10A2:
108
  case F::RGB5A1:
109
    return shouldSwap;
NEW
110
  default:
×
NEW
111
    return false;
×
112
    break;
113
  }
114
}
115

NEW
116
bool MustSwizzle(TexelSwizzle swizzle, uint32 numChannels) {
×
117
  if (swizzle.a != TexelSwizzleType::Alpha ||
73✔
118
      swizzle.r != TexelSwizzleType::Red ||
73✔
NEW
119
      swizzle.g != TexelSwizzleType::Green) {
×
120
    return true;
121
  }
122

123
  if (numChannels > 2) {
73✔
124
    return swizzle.b != TexelSwizzleType::Blue &&
73✔
125
           swizzle.b != TexelSwizzleType::DeriveZOrBlue;
73✔
126
  }
127

NEW
128
  return swizzle.b != TexelSwizzleType::Blue;
×
129
};
130

131
uint32 GetBPT(TexelInputFormatType fmt) {
23✔
132
  using F = TexelInputFormatType;
133

134
  switch (fmt) {
23✔
135
  case F::PVRTC2:
136
  case F::PVRTC4:
137
  case F::BC2:
138
  case F::BC3:
139
  case F::BC5:
140
  case F::BC7:
141
  case F::BC6:
142
    return 16;
143

144
  case F::ETC1:
3✔
145
  case F::BC1:
146
  case F::BC4:
147
  case F::RGBA16:
148
    return 8;
3✔
149

150
  case F::RG8:
4✔
151
  case F::RGB5A1:
152
  case F::RGBA4:
153
  case F::R5G6B5:
154
    return 2;
4✔
155

156
  case F::RGB10A2:
8✔
157
  case F::RGBA8:
158
  case F::RGB9E5:
159
    return 4;
8✔
160

161
  case F::P8:
5✔
162
  case F::P4:
163
  case F::R8:
164
    return 1;
5✔
165
  case F::RGB8:
×
166
    return 3;
×
167
  case F::INVALID:
168
    return 0;
169
  }
170

171
  return 0;
172
}
173

174
TexelDataLayout NewTexelContextImpl::ComputeTraditionalDataLayout(
×
175
    TexelInputFormatType *typeOverrides) {
176
  uint32 mipCount = std::max(ctx.numMipmaps, uint8(1));
×
177
  uint32 width = ctx.width;
×
178
  uint32 height = ctx.height;
×
179
  uint32 depth = std::max(ctx.depth, uint16(1));
×
180
  uint32 numFaces = std::max(ctx.numFaces, int8(1));
×
181

182
  TexelDataLayout retVal{};
×
183

184
  for (uint32 m = 0; m < mipCount; m++) {
×
185
    uint32 _width = width;
186
    uint32 _height = height;
187
    TexelInputFormatType type =
188
        typeOverrides ? typeOverrides[m] : ctx.baseFormat.type;
×
189
    uint32 bpt = GetBPT(type);
×
190

191
    if (BlockCompression(type)) {
192
      _width = (_width + 3) / 4;
×
193
      _height = (_height + 3) / 4;
×
194
    }
195

196
    retVal.mipSizes[m] = depth * _width * _height * bpt;
×
197
    retVal.mipOffsets[m] = retVal.mipGroupSize;
×
198
    retVal.mipGroupSize += retVal.mipSizes[m];
×
199
    width = std::max(1U, width / 2);
×
200
    height = std::max(1U, height / 2);
×
201
    depth = std::max(1U, depth / 2);
×
202
  }
203

204
  retVal.groupSize = retVal.mipGroupSize * numFaces;
×
205
  return retVal;
×
206
}
207

208
bool BMPFallback(TexelInputFormatType fmt) {
×
209
  using F = TexelInputFormatType;
210

211
  switch (fmt) {
×
212
  case F::RGB10A2:
213
  case F::RGB5A1:
214
  case F::P8:
215
  case F::P4:
216
  case F::RGBA4:
217
  case F::R5G6B5:
218
  case F::R8:
219
    return OutputFormat() == TexelContextFormat::QOI_BMP;
×
220

221
  default:
222
    return false;
223
  }
224

225
  return false;
226
}
227

228
#pragma pack(2)
229
struct BMPHeader {
×
230
  uint16 bfType = 0x4D42; // BM
231
  uint32 bfSize;
232
  uint16 bfReserved1 = 0;
233
  uint16 bfReserved2 = 0;
234
  uint32 bfOffBits = 54;
235
};
236
#pragma pack()
237

238
struct BMPInfoHeader {
×
239
  uint32 biSize = 40;
240
  uint32 biWidth;
241
  uint32 biHeight;
242
  uint16 biPlanes = 1;
243
  uint16 biBitCount = 32;
244
  uint32 biCompression = 0;
245
  uint32 biSizeImage;
246
  uint32 biXPelsPerMeter = 0;
247
  uint32 biYPelsPerMeter = 0;
248
  uint32 biClrUsed = 0;
249
  uint32 biClrImportant = 0;
250
};
251

252
union BMPMask {
253
  struct {
254
    uint32 red;
255
    uint32 green;
256
    uint32 blue;
257
    uint32 alpha;
258
  };
259
  uint32 data[4];
260
};
261

NEW
262
BMPMask SwizzleMask(BMPMask bits, TexelSwizzle swizzle) {
×
263
  uint32 currentOffset = 0;
264
  BMPMask retVal{};
×
265

266
  for (uint32 c = 0; c < 4; c++) {
×
NEW
267
    uint32 swizzleIndex = swizzle.types[c] > TexelSwizzleType::Alpha
×
NEW
268
                              ? c
×
269
                              : uint32(swizzle.types[c]);
270

271
    uint32 numBits = bits.data[swizzleIndex];
×
272
    uint32 mask = (1 << numBits) - 1;
×
273

274
    retVal.data[c] = mask << currentOffset;
×
275
    currentOffset += numBits;
×
276
  }
277

278
  return retVal;
×
279
}
280

281
BMPMask FillBmpFormat(NewTexelContextCreate ctx, TexelInputFormat fmt,
×
282
                      BMPInfoHeader &info) {
283
  using F = TexelInputFormatType;
284
  info.biWidth = ctx.width;
×
285
  info.biHeight = ctx.height;
×
286

287
  switch (fmt.type) {
×
288
  case F::RGB10A2:
×
289
    info.biBitCount = 32;
×
290
    info.biCompression = 6;
×
291
    return SwizzleMask({{.red = 10, .green = 10, .blue = 10, .alpha = 2}},
×
292
                       fmt.swizzle);
×
293
  case F::RGB5A1:
×
294
    info.biBitCount = 16;
×
295
    info.biCompression = 6;
×
296
    return SwizzleMask({{.red = 4, .green = 4, .blue = 4, .alpha = 1}},
×
297
                       fmt.swizzle);
×
298
  case F::RGBA4:
×
299
    info.biBitCount = 16;
×
300
    info.biCompression = 6;
×
301
    return SwizzleMask({{.red = 4, .green = 4, .blue = 4, .alpha = 4}},
×
302
                       fmt.swizzle);
×
303
  case F::R5G6B5:
×
304
    info.biBitCount = 16;
×
305
    info.biCompression = 3;
×
306
    return SwizzleMask({{.red = 5, .green = 6, .blue = 5, .alpha = 0}},
×
307
                       fmt.swizzle);
×
308
  default:
×
309
    return {};
×
310
  }
311

312
  return {};
313
}
314

315
DDS MakeDDS(NewTexelContextCreate ctx) {
28✔
316
  DDS dds;
317
  dds.width = ctx.width;
28✔
318
  dds.height = ctx.height;
28✔
319
  if (ctx.baseFormat.premultAlpha) {
28✔
320
    dds.alphaMode = dds.AlphaMode_Premultiplied;
×
321
  }
322

323
  dds.arraySize = ctx.arraySize;
28✔
324

325
  if (ctx.depth > 1) {
28✔
326
    dds.caps01 += dds.Caps01Flags_Volume;
327
    dds.flags += dds.Flags_Depth;
328
    dds.dimension = dds.Dimension_3D;
1✔
329
    dds.depth = ctx.depth;
1✔
330
  } else if (ctx.numFaces > 0) {
27✔
331
    dds.caps01 += dds.Caps01Flags_CubeMap;
332
    dds.miscFlag += dds.MiscFlag_CubeTexture;
333

334
    for (int32 i = 0; i < ctx.numFaces; i++) {
14✔
335
      dds.caps01 += static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
12✔
336
    }
337
  }
338

339
  if (mainSettings.texelSettings.processMipMaps) {
28✔
340
    dds.NumMipmaps(ctx.numMipmaps);
5✔
341
  }
342

343
  return dds;
28✔
344
}
345

346
void SetDDSFormat(DDS &dds, TexelInputFormat fmt) {
×
347
  dds = DDSFormat_DX10;
348
  switch (fmt.type) {
×
349
    using F = TexelInputFormatType;
350
  case F::BC1:
×
351
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC1_UNORM + fmt.srgb);
×
352
    break;
×
353

354
  case F::BC2:
×
355
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC2_UNORM + fmt.srgb);
×
356
    break;
×
357

358
  case F::BC3:
×
359
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC3_UNORM + fmt.srgb);
×
360
    break;
×
361

362
  case F::BC4:
×
363
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC4_UNORM + fmt.snorm);
×
364
    break;
×
365

366
  case F::BC5:
×
367
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC5_UNORM + fmt.snorm);
×
368
    break;
×
369

370
  case F::BC7:
×
371
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC7_UNORM + fmt.srgb);
×
372
    break;
×
373

374
  case F::RGBA4:
×
375
    dds.dxgiFormat = DXGI_FORMAT_B4G4R4A4_UNORM;
×
376
    break;
×
377

378
  case F::R5G6B5:
×
379
    dds.dxgiFormat = DXGI_FORMAT_B5G6R5_UNORM;
×
380
    break;
×
381

382
  case F::RGB5A1:
×
383
    dds.dxgiFormat = DXGI_FORMAT_B5G5R5A1_UNORM;
×
384
    break;
×
385

386
  case F::RGB10A2:
×
387
    dds.dxgiFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
×
388
    break;
×
389

390
  case F::RG8:
×
391
    dds.dxgiFormat =
×
392
        fmt.snorm ? DXGI_FORMAT_R8G8_SNORM : DXGI_FORMAT_R8G8_UNORM;
×
393
    break;
×
394

395
  case F::R8:
×
396
    dds.dxgiFormat = fmt.snorm ? DXGI_FORMAT_R8_SNORM : DXGI_FORMAT_R8_UNORM;
×
397
    break;
×
398

399
  case F::RGBA8:
×
400
  case F::P8:
401
  case F::P4:
402
  case F::PVRTC2:
403
  case F::PVRTC4:
404
  case F::ETC1:
405
  case F::RGB8:
406
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM + fmt.srgb);
×
407
    break;
×
408

409
  case F::RGBA16:
×
410
    dds.dxgiFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
×
411
    break;
×
412

413
  case F::BC6:
×
414
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC6H_UF16 + fmt.snorm);
×
415
    break;
×
416

417
  case F::RGB9E5:
×
418
    dds.dxgiFormat = DXGI_FORMAT_R9G9B9E5_SHAREDEXP;
×
419
    break;
×
420

421
  case F::INVALID:
×
422
    dds.dxgiFormat = DXGI_FORMAT_UNKNOWN;
×
423
    break;
×
424
  }
425
}
426

427
void SetDDSLegacyFormat(DDS &dds, TexelInputFormatType fmt) {
28✔
428
  switch (fmt) {
28✔
429
    using F = TexelInputFormatType;
430
  case F::BC1:
431
    dds = DDSFormat_DXT1;
432
    break;
433

434
  case F::BC2:
435
    dds = DDSFormat_DXT3;
436
    break;
437

438
  case F::BC3:
439
    dds = DDSFormat_DXT5;
440
    break;
441

442
  case F::BC4:
443
  case F::R8:
444
    dds = DDSFormat_L8;
445
    break;
446

447
  case F::RGBA4:
448
    dds = DDSFormat_A4R4G4B4;
449
    break;
450

451
  case F::R5G6B5:
452
    dds = DDSFormat_R5G6B5;
453
    break;
454

455
  case F::RGB8:
456
    dds = DDSFormat_R8G8B8;
457
    break;
458

459
  case F::RGBA8:
460
  case F::BC7:
461
  case F::RGB5A1:
462
  case F::RGB10A2:
463
  case F::P8:
464
  case F::P4:
465
  case F::PVRTC2:
466
  case F::PVRTC4:
467
  case F::ETC1:
468
  case F::RGBA16:
469
  case F::BC6:
470
  case F::RGB9E5:
471
    dds = DDSFormat_A8R8G8B8;
472
    break;
473

474
  case F::BC5:
475
  case F::RG8:
476
    dds = DDSFormat_R8G8B8;
477
    break;
478

479
  case F::INVALID:
480
    break;
481
  }
482
}
28✔
483

NEW
484
uint8 DesiredQOIChannels(TexelInputFormatType fmt) {
×
485
  switch (fmt) {
×
486
    using F = TexelInputFormatType;
487
  case F::BC4:
488
  case F::R8:
489
  case F::R5G6B5:
490
  case F::BC5:
491
  case F::RG8:
492
  case F::RGB8:
493
    return 3;
494

495
  case F::BC1:
496
  case F::BC2:
497
  case F::BC3:
498
  case F::RGBA4:
499
  case F::RGBA8:
500
  case F::BC7:
501
  case F::RGB5A1:
502
  case F::RGB10A2:
503
  case F::P8:
504
  case F::P4:
505
  case F::PVRTC2:
506
  case F::PVRTC4:
507
  case F::ETC1:
508
  case F::RGBA16:
509
  case F::BC6:
510
  case F::RGB9E5:
511
    return 4;
512

513
  case F::INVALID:
×
514
    return 0;
×
515
  }
516

517
  return 4;
518
}
519

520
enum class PngColorType : uint8 {
521
  Gray = 0,
522
  RGB = 2,
523
  Palette = 3,
524
  GrayAlpha = 4,
525
  RGBA = 6,
526
};
527

528
uint32 GetPngChannels(PngColorType fmt) {
×
529
  switch (fmt) {
530
  case PngColorType::Gray:
531
    return 1;
532
  case PngColorType::GrayAlpha:
533
    return 2;
534
  case PngColorType::RGB:
535
    return 3;
536
  case PngColorType::RGBA:
537
    return 4;
538
  }
539

540
  return 0;
541
}
542

NEW
543
PngColorType DesiredPngColorType(TexelInputFormatType fmt) {
×
544
  switch (fmt) {
×
545
    using F = TexelInputFormatType;
546
  case F::R8:
547
  case F::BC4:
548
    return PngColorType::Gray;
549

550
  case F::BC5:
×
551
  case F::RG8:
552
    // return PngColorType::GrayAlpha;
553

554
  case F::R5G6B5:
555
  case F::RGB8:
556
    return PngColorType::RGB;
×
557

558
  case F::BC1:
559
  case F::BC2:
560
  case F::BC3:
561
  case F::RGBA4:
562
  case F::RGBA8:
563
  case F::BC7:
564
  case F::RGB5A1:
565
  case F::RGB10A2:
566
  case F::P8:
567
  case F::P4:
568
  case F::PVRTC2:
569
  case F::PVRTC4:
570
  case F::ETC1:
571
  case F::RGBA16:
572
  case F::BC6:
573
  case F::RGB9E5:
574
    return PngColorType::RGBA;
575

576
  case F::INVALID:
577
    return PngColorType::RGBA;
578
  }
579

580
  return PngColorType::RGBA;
581
}
582

NEW
583
PngColorType DesiredPngColorType(PngColorType type, TexelSwizzle &swizzle) {
×
NEW
584
  switch (type) {
×
NEW
585
  case PngColorType::RGBA: {
×
NEW
586
    if (swizzle.r == swizzle.g && swizzle.g == swizzle.b) {
×
NEW
587
      if (swizzle.a == TexelSwizzleType::White) {
×
588
        return PngColorType::Gray;
589
      }
590

NEW
591
      swizzle.g = swizzle.a;
×
NEW
592
      return PngColorType::GrayAlpha;
×
593
    }
NEW
594
    if (swizzle.a == TexelSwizzleType::White) {
×
NEW
595
      return PngColorType::RGB;
×
596
    }
597

598
    return type;
599
  }
600

NEW
601
  case PngColorType::RGB: {
×
NEW
602
    if (swizzle.r == swizzle.g && swizzle.g == swizzle.b) {
×
NEW
603
      if (swizzle.a == TexelSwizzleType::White) {
×
604
        return PngColorType::Gray;
605
      }
606

NEW
607
      swizzle.g = swizzle.a;
×
NEW
608
      return PngColorType::GrayAlpha;
×
609
    }
610

NEW
611
    if (swizzle.a != TexelSwizzleType::White) {
×
NEW
612
      return PngColorType::RGBA;
×
613
    }
614

615
    return type;
616
  }
617
  }
618

619
  return type;
620
}
621

622
/*
623
PngColorType DesiredPngColorType(PngColorType type, TexelSwizzle swizzle) {
624
  switch (type) {
625
  case PngColorType::Gray: {
626
    int8 factors[4]{0, 0, 0, 0};
627

628
    for (uint32 f = 0; f < 4; f++) {
629
      switch (swizzle.types[f]) {
630
      case TexelSwizzleType::Red:
631
        factors[f] = 1;
632
        break;
633
      case TexelSwizzleType::RedInverted:
634
        factors[f] = -1;
635
        break;
636

637
      default:
638
        break;
639
      }
640
    }
641

642
    if ((factors[0] == factors[1] &&
643
         factors[1] == factors[2]) ||           // rgb == red || redinverted
644
        (factors[0] == 0 && factors[1] == 0) || // r == red || redinverted
645
        (factors[0] == 0 && factors[2] == 0) || // g == red || redinverted
646
        (factors[1] == 0 && factors[2] == 0)) { // // b == red || redinverted
647
      if (factors[3]) {
648
        return PngColorType::GrayAlpha;
649
      }
650
      return type;
651
    } else {
652
      if (factors[3]) {
653
        return PngColorType::RGBA;
654
      }
655
      return PngColorType::RGB;
656
    }
657
  }
658

659
  case PngColorType::GrayAlpha: {
660
    int8 factors[4]{0, 0, 0, 0};
661

662
    for (uint32 f = 0; f < 4; f++) {
663
      switch (swizzle.types[f]) {
664
      case TexelSwizzleType::Red:
665
      case TexelSwizzleType::Alpha:
666
        factors[f] = 1;
667
        break;
668
      case TexelSwizzleType::RedInverted:
669
      case TexelSwizzleType::AlphaInverted:
670
        factors[f] = -1;
671
        break;
672

673
      default:
674
        break;
675
      }
676
    }
677

678
    if ((factors[0] == factors[1] &&
679
         factors[1] == factors[2]) ||           // rgb == red || redinverted
680
        (factors[0] == 0 && factors[1] == 0) || // r == red || redinverted
681
        (factors[0] == 0 && factors[2] == 0) || // g == red || redinverted
682
        (factors[1] == 0 && factors[2] == 0)) { // // b == red || redinverted
683
      if (factors[3]) {
684
        return PngColorType::GrayAlpha;
685
      }
686
      return type;
687
    } else {
688
      if (factors[3]) {
689
        return PngColorType::RGBA;
690
      }
691
      return PngColorType::RGB;
692
    }
693
  }
694
  }
695
}*/
696

NEW
697
uint8 DesiredDDSChannels(TexelInputFormatType fmt) {
×
UNCOV
698
  switch (fmt) {
×
699
    using F = TexelInputFormatType;
700
  case F::BC4:
701
  case F::R8:
702
    return 1;
703

704
  case F::R5G6B5:
×
705
    return 3;
×
706

707
  case F::BC5:
×
708
  case F::RG8:
709
    return 2;
×
710

711
  case F::BC1:
712
  case F::BC2:
713
  case F::BC3:
714
  case F::RGBA4:
715
  case F::RGBA8:
716
  case F::BC7:
717
  case F::RGB5A1:
718
  case F::RGB10A2:
719
  case F::P8:
720
  case F::P4:
721
  case F::PVRTC2:
722
  case F::PVRTC4:
723
  case F::ETC1:
724
  case F::RGB8:
725
  case F::RGBA16:
726
  case F::BC6:
727
  case F::RGB9E5:
728
    return 4;
729

730
  case F::INVALID:
×
731
    return 0;
×
732
  }
733

734
  return 4;
735
}
736

NEW
737
uint8 DesiredDDSLegacyChannels(TexelInputFormatType fmt) {
×
738
  switch (fmt) {
9✔
739
    using F = TexelInputFormatType;
740
  case F::BC4:
741
  case F::R8:
742
    return 1;
743

744
  case F::R5G6B5:
×
745
  case F::BC5:
746
  case F::RG8:
747
  case F::RGB8:
748
    return 3;
×
749

750
  case F::BC1:
751
  case F::BC2:
752
  case F::BC3:
753
  case F::RGBA4:
754
  case F::RGBA8:
755
  case F::BC7:
756
  case F::RGB5A1:
757
  case F::RGB10A2:
758
  case F::P8:
759
  case F::P4:
760
  case F::PVRTC2:
761
  case F::PVRTC4:
762
  case F::ETC1:
763
  case F::RGBA16:
764
  case F::BC6:
765
  case F::RGB9E5:
766
    return 4;
767

768
  case F::INVALID:
×
769
    return 0;
×
770
  }
771

772
  return 4;
773
}
774

775
uint8 FormatChannels(TexelInputFormatType fmt) {
27✔
776
  switch (fmt) {
27✔
777
    using F = TexelInputFormatType;
778
  case F::BC4:
779
  case F::R8:
780
    return 1;
781

782
  case F::BC5:
2✔
783
  case F::RG8:
784
    return 2;
2✔
785

786
  case F::R5G6B5:
2✔
787
  case F::RGB8:
788
    return 3;
2✔
789

790
  case F::BC1:
20✔
791
  case F::BC2:
792
  case F::BC3:
793
  case F::RGBA4:
794
  case F::RGBA8:
795
  case F::BC7:
796
  case F::RGB5A1:
797
  case F::RGB10A2:
798
  case F::P8:
799
  case F::P4:
800
  case F::PVRTC2:
801
  case F::PVRTC4:
802
  case F::ETC1:
803
  case F::RGBA16:
804
  case F::BC6:
805
  case F::RGB9E5:
806
    return 4;
20✔
807

808
  case F::INVALID:
809
    return 0;
810
  }
811

812
  return 0;
813
}
814

815
struct LinearTile : TileBase {
×
816
  void reset(uint32, uint32, uint32) {}
×
817
  uint32 get(uint32 inTexel) const override { return inTexel; }
628,224✔
818
};
819

820
struct MortonTile : TileBase {
×
821
  MortonSettings settings;
822

823
  MortonTile(uint32 width, uint32 height) : settings(width, height) {}
×
824

825
  void reset(uint32, uint32, uint32) {}
×
826

827
  uint32 get(uint32 inTexel) const override {
×
828
    return MortonAddr(inTexel % settings.width, inTexel / settings.height,
×
829
                      settings);
×
830
  }
831
};
832

833
uint32 RoundToPow2(uint32 number) {
×
834
  number--;
×
835
  number |= number >> 1;
×
836
  number |= number >> 2;
×
837
  number |= number >> 4;
×
838
  number |= number >> 8;
×
839
  number |= number >> 16;
×
840
  number++;
×
841
  return number;
×
842
}
843

844
struct MortonPow2Tile : TileBase {
×
845
  size_t width;
846
  size_t height;
847
  size_t widthp2;
848

849
  MortonPow2Tile(size_t width_, size_t height_)
850
      : width(width_), height(height_),
851
        widthp2(RoundToPow2(std::max(width_, size_t(8)))) {}
×
852

853
  void reset(uint32, uint32, uint32) {}
×
854

855
  static size_t MortonAddr(size_t x, size_t y, size_t width) {
856
    const size_t x0 = x & 1;
×
857
    const size_t x1 = (x & 2) << 1;
×
858
    const size_t x2 = (x & 4) << 2;
×
859

860
    const size_t y0 = (y & 1) << 1;
×
861
    const size_t y1 = (y & 2) << 2;
×
862
    const size_t y2 = (y & 4) << 3;
×
863

864
    size_t retval = x0 | x1 | x2 | y0 | y1 | y2;
×
865

866
    const size_t macroX = x / 8;
×
867
    const size_t macroY = y / 8;
×
868
    const size_t macroWidth = width / 8;
×
869

870
    const size_t macroAddr = (macroWidth * macroY) + macroX;
×
871

872
    return retval | (macroAddr << 6);
×
873
  }
874

875
  uint32 get(uint32 inTexel) const override {
×
876
    return MortonAddr(inTexel % width, inTexel / height, widthp2);
×
877
  }
878
};
879

880
struct NXTile : TileBase {
12✔
881
  uint32 width;
882
  uint32 height;
883
  uint32 numUsedYBlockBits;
884
  uint32 yBlockMask;
885
  uint32 yTailMask;
886
  uint32 macroTileWidth;
887
  uint32 BPT;
888
  uint32 yTailOffset;
889
  uint32 upperBound;
890
  uint32 numUsedMicroYBits;
891
  uint32 midYShift;
892

893
  NXTile(size_t width_, size_t height_, TexelInputFormatType fmt)
12✔
894
      : width(width_), height(height_), BPT(GetBPT(fmt)),
12✔
895
        upperBound(width * height) {
12✔
896
    numUsedMicroYBits = std::min(1 << uint32(std::round(log2(height))), 3);
12✔
897
    midYShift = numUsedMicroYBits + 3;
12✔
898

899
    const uint32 macroTileHeight =
900
        std::min(uint32(std::round(log2((height + 7) / 8))), 4U);
12✔
901

902
    // addr bits 9 - 13
903
    // y bits 3 - 7
904
    yBlockMask = ((1 << macroTileHeight) - 1) << numUsedMicroYBits;
12✔
905
    numUsedYBlockBits = numUsedMicroYBits + macroTileHeight;
12✔
906

907
    // y bits 8 - end
908
    yTailMask = ~((1 << numUsedYBlockBits) - 1);
12✔
909

910
    macroTileWidth = ((width * BPT) + 63) / 64;
12✔
911

912
    yTailOffset = macroTileHeight + 2;
12✔
913
  }
12✔
914

915
  void reset(uint32, uint32, uint32) {}
×
916

917
  uint32 get(uint32 inTexel) const override {
260,672✔
918
    // 12 11 10 9  8  7  6  5  4  3  2  1  0
919
    // y  y  y  y  x  y  y  x  y  x  x  x  x
920
    // y*x*y{0,4}xyyxyx[x|0]{4}
921

922
    uint32 x = (inTexel % width) * BPT;
260,672✔
923
    uint32 y = inTexel / width;
260,672✔
924

925
    // Volumetrics not implemented
926
    // Some small rasters use alignment
927

928
    // x bits 0 - 5
929
    // y bits 0 - 2
930
    uint32 microTile = (x & 0xf) | ((y & 1) << 4) | ((x & 0x10) << 1) |
260,672✔
931
                       ((y & 0x6) << 5) | ((x & 0x20) << numUsedMicroYBits);
260,672✔
932

933
    // addr tail after yBlockMask bits
934
    // x bits 6 - end
935
    constexpr uint32 xTailMask = ~0x3f;
936

937
    uint32 macroTile = (((y & yBlockMask) << midYShift) | //
260,672✔
938
                        ((x & xTailMask) << numUsedYBlockBits)) +
260,672✔
939
                       (((y & yTailMask) << yTailOffset) * macroTileWidth);
260,672✔
940

941
    uint32 wholeTile = (microTile | macroTile) / BPT;
260,672✔
942

943
    /*if (wholeTile >= upperBound) [[unlikely]] {
944
      throw std::runtime_error("NX tile error, accessing block out of range");
945
    }*/
946

947
    return wholeTile;
260,672✔
948
  }
949
};
950

951
using TileVariant =
952
    std::variant<LinearTile, MortonTile, MortonPow2Tile, NXTile>;
953

954
TileVariant TileVariantFromCtx(NewTexelContextCreate ctx) {
42✔
955
  uint32 width = ctx.width;
42✔
956
  uint32 height = ctx.height;
42✔
957

958
  switch (ctx.baseFormat.tile) {
42✔
959
  case TexelTile::Linear:
30✔
960
    return LinearTile{};
961

962
  case TexelTile::Morton:
963
    return MortonTile(width, height);
×
964

965
  case TexelTile::MortonForcePow2:
×
966
    return MortonPow2Tile(width, height);
×
967

968
  case TexelTile::NX:
12✔
969
    return NXTile(width, height, ctx.baseFormat.type);
12✔
970

971
  case TexelTile::Custom:
972
    break;
973
  }
974

975
  return LinearTile{};
976
}
977

978
void DecodeToRGB(const char *data, NewTexelContextCreate ctx,
4✔
979
                 std::span<UCVector> outData) {
980
  if (BlockCompression(ctx.baseFormat.type)) {
4✔
981
    ctx.width = (ctx.width + 3) / 4;
1✔
982
    ctx.height = (ctx.height + 3) / 4;
1✔
983
  }
984
  TileVariant tvar(TileVariantFromCtx(ctx));
4✔
985
  const TileBase *tiler =
986
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
987
          ? [&] {
4✔
988
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
UNCOV
989
              return ctx.customTile;
×
990
            }()
991
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
8✔
992

993
  const size_t numBlocks = ctx.width * ctx.height;
4✔
994

995
  switch (ctx.baseFormat.type) {
4✔
996
    using F = TexelInputFormatType;
997
  case F::R5G6B5: {
2✔
998
    auto &codec = uni::FormatCodec::Get({
2✔
999
        .outType = uni::FormatType::UNORM,
1000
        .compType = uni::DataType::R5G6B5,
1001
    });
1002

1003
    size_t curTexel = 0;
1004
    if (ctx.baseFormat.swapPacked) {
2✔
1005
      const uint16 *iData = reinterpret_cast<const uint16 *>(data);
1006
      for (auto &t : outData) {
×
1007
        Vector4A16 value;
NEW
1008
        uint16 col = *(iData + tiler->get(curTexel++));
×
1009
        FByteswapper(col);
1010

1011
        codec.GetValue(value, reinterpret_cast<const char *>(&col));
×
1012
        t = Vector(Vector4A16(value * 0xff)).Convert<uint8>();
×
1013
      }
1014
    } else {
1015
      for (auto &t : outData) {
86,274✔
1016
        Vector4A16 value;
1017
        codec.GetValue(value, data + tiler->get(curTexel++) * 2);
86,272✔
1018
        t = Vector(Vector4A16(value * 0xff)).Convert<uint8>();
86,272✔
1019
      }
1020
    }
1021

1022
    break;
1023
  }
1024

1025
  case F::RG8: {
1✔
1026
    size_t curTexel = 0;
1027

1028
    if (ctx.baseFormat.snorm) {
1✔
1029
      const CVector2 *iData = reinterpret_cast<const CVector2 *>(data);
1030
      for (auto &t : outData) {
×
1031
        auto fData = (iData + tiler->get(curTexel++))->Convert<int>() + 0x80;
×
1032
        t = UCVector(0, fData.y, fData.x);
×
1033
      }
1034
    } else {
1035
      const UCVector2 *iData = reinterpret_cast<const UCVector2 *>(data);
1036
      for (auto &t : outData) {
65,537✔
1037
        auto tData = iData + tiler->get(curTexel++);
65,536✔
1038
        t = UCVector(0, tData->y, tData->x);
65,536✔
1039
      }
1040
    }
1041
    break;
1042
  }
1043

1044
  case F::BC5:
1045
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
1046
      DecodeBC5Block(data + tiler->get(p) * 16,
4,096✔
1047
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
1048
                     p / ctx.width, ctx.width, 3);
4,096✔
1049
    }
1050
    break;
1051

1052
  case F::BC4:
UNCOV
1053
    for (size_t p = 0; p < numBlocks; p++) {
×
UNCOV
1054
      _DecodeBC4Block(data + tiler->get(p) * 8,
×
UNCOV
1055
                      reinterpret_cast<char *>(outData.data()), p % ctx.width,
×
UNCOV
1056
                      p / ctx.width, ctx.width, 3);
×
1057
    }
1058

UNCOV
1059
    for (auto &p : outData) {
×
UNCOV
1060
      p.y = p.z = p.x;
×
1061
    }
1062

1063
    break;
1064

UNCOV
1065
  case F::R8: {
×
1066
    size_t curTexel = 0;
UNCOV
1067
    for (auto &t : outData) {
×
UNCOV
1068
      t = UCVector(*(data + tiler->get(curTexel++)));
×
1069
    }
1070

1071
    break;
1072
  }
1073

1074
  default:
×
1075
    throw std::logic_error("Implement rgb decode");
×
1076
  }
1077
}
4✔
1078

1079
void DecodeToRGBA(const char *data, NewTexelContextCreate ctx,
24✔
1080
                  std::span<UCVector4> outData) {
1081
  if (BlockCompression(ctx.baseFormat.type)) {
24✔
1082
    ctx.width = (ctx.width + 3) / 4;
13✔
1083
    ctx.height = (ctx.height + 3) / 4;
13✔
1084
  }
1085
  TileVariant tvar(TileVariantFromCtx(ctx));
24✔
1086
  const TileBase *tiler =
1087
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
1088
          ? [&] {
24✔
1089
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
UNCOV
1090
              return ctx.customTile;
×
1091
            }()
1092
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
48✔
1093

1094
  const size_t numBlocks = ctx.width * ctx.height;
24✔
1095

1096
  switch (ctx.baseFormat.type) {
24✔
1097
    using F = TexelInputFormatType;
1098
  case F::RGB10A2: {
2✔
1099
    auto &codec = uni::FormatCodec::Get({
2✔
1100
        .outType = uni::FormatType::UNORM,
1101
        .compType = uni::DataType::R10G10B10A2,
1102
    });
1103

1104
    size_t curTexel = 0;
1105
    if (ctx.baseFormat.swapPacked) {
2✔
1106
      const uint32 *iData = reinterpret_cast<const uint32 *>(data);
1107
      for (auto &t : outData) {
×
1108
        Vector4A16 value;
1109
        uint32 col = *(iData + tiler->get(curTexel++));
×
1110
        FByteswapper(col);
×
1111

1112
        codec.GetValue(value, reinterpret_cast<const char *>(&col));
×
1113
        t = (value * 0xff).Convert<uint8>();
1114
      }
1115
    } else {
1116
      for (auto &t : outData) {
131,074✔
1117
        Vector4A16 value;
1118
        codec.GetValue(value, data + tiler->get(curTexel++) * 4);
131,072✔
1119
        t = (value * 0xff).Convert<uint8>();
1120
      }
1121
    }
1122
    break;
1123
  }
1124

1125
  case F::RGBA4: {
1✔
1126
    const uint16 *iData = reinterpret_cast<const uint16 *>(data);
1127
    size_t curTexel = 0;
1128
    if (ctx.baseFormat.swapPacked) {
1✔
1129
      for (auto &t : outData) {
×
1130
        uint16 col = *(iData + tiler->get(curTexel++));
×
1131
        FByteswapper(col);
1132
        t = UCVector4(col << 4, col & 0xf0, (col >> 4) & 0xf0,
×
1133
                      (col >> 8) & 0xf0);
×
1134
      }
1135
    } else {
1136
      for (auto &t : outData) {
65,537✔
1137
        uint16 col = *(iData + tiler->get(curTexel++));
65,536✔
1138
        t = UCVector4(col << 4, col & 0xf0, (col >> 4) & 0xf0,
65,536✔
1139
                      (col >> 8) & 0xf0);
65,536✔
1140
      }
1141
    }
1142

1143
    break;
1144
  }
1145

1146
  case F::RGB5A1: {
2✔
1147
    const uint16 *iData = reinterpret_cast<const uint16 *>(data);
1148
    size_t curTexel = 0;
1149

1150
    if (ctx.baseFormat.swapPacked) {
2✔
1151
      for (auto &t : outData) {
×
1152
        uint16 col = *(iData + tiler->get(curTexel++));
×
1153
        FByteswapper(col);
1154
        t = UCVector4(col << 3, (col >> 2) & 0xf8, (col >> 7) & 0xf8,
×
1155
                      int16(col) >> 15);
×
1156
      }
1157
    } else {
1158
      for (auto &t : outData) {
131,074✔
1159
        uint16 col = *(iData + tiler->get(curTexel++));
131,072✔
1160
        t = UCVector4(col << 3, (col >> 2) & 0xf8, (col >> 7) & 0xf8,
131,072✔
1161
                      int16(col) >> 15);
131,072✔
1162
      }
1163
    }
1164

1165
    break;
1166
  }
1167

1168
  case F::PVRTC2:
1169
    pvr::PVRTDecompressPVRTC(data, 1, ctx.width, ctx.height,
2✔
1170
                             reinterpret_cast<uint8_t *>(outData.data()));
1171
    break;
1172

1173
  case F::PVRTC4:
1174
    pvr::PVRTDecompressPVRTC(data, 0, ctx.width, ctx.height,
2✔
1175
                             reinterpret_cast<uint8_t *>(outData.data()));
1176
    break;
1177

1178
  case F::ETC1:
1179
    pvr::PVRTDecompressETC(data, ctx.width, ctx.height,
2✔
1180
                           reinterpret_cast<uint8_t *>(outData.data()), 0);
1181
    break;
1182

1183
  case F::BC7: {
1184
    uint32 localBlock[16];
1185
    for (size_t p = 0; p < numBlocks; p++) {
8,194✔
1186
      if (!detexDecompressBlockBPTC(
8,192✔
1187
              reinterpret_cast<const uint8_t *>(data) + tiler->get(p) * 16, -1,
8,192✔
1188
              0, reinterpret_cast<uint8_t *>(localBlock))) [[unlikely]] {
×
1189
        throw std::runtime_error("Failed to decompress BC7 block");
×
1190
      }
1191
      uint32 x = p % ctx.width;
8,192✔
1192
      uint32 y = p / ctx.width;
8,192✔
1193
      uint32 blockOffset = ctx.width * y * 16 + x * 4;
8,192✔
1194

1195
      for (size_t r = 0; r < 4; r++) {
40,960✔
1196
        UCVector4 *addr = outData.data() + blockOffset + r * ctx.width * 4;
32,768✔
1197
        memcpy(static_cast<void *>(addr), localBlock + r * 4, 16);
32,768✔
1198
      }
1199
    }
1200
    break;
1201
  }
1202

1203
  case F::BC1:
1204
    for (size_t p = 0; p < numBlocks; p++) {
5,394✔
1205
      DecodeBC1BlockA(data + tiler->get(p) * 8,
5,392✔
1206
                      reinterpret_cast<char *>(outData.data()), p % ctx.width,
5,392✔
1207
                      p / ctx.width, ctx.width);
5,392✔
1208
    }
1209

1210
    break;
1211

1212
  case F::BC2:
1213
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
1214
      DecodeBC2Block(data + tiler->get(p) * 16,
4,096✔
1215
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
1216
                     p / ctx.width, ctx.width);
4,096✔
1217
    }
1218

1219
    break;
1220

1221
  case F::BC3:
1222
    for (size_t p = 0; p < numBlocks; p++) {
6,936✔
1223
      DecodeBC3Block(data + tiler->get(p) * 16,
6,928✔
1224
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
6,928✔
1225
                     p / ctx.width, ctx.width);
6,928✔
1226
    }
1227

1228
    break;
1229

1230
  default:
×
1231
    throw std::logic_error("Implement rgba decode");
×
1232
  }
1233
}
24✔
1234

1235
void DecodeToRG(const char *data, NewTexelContextCreate ctx,
1✔
1236
                std::span<UCVector2> outData) {
1237
  if (BlockCompression(ctx.baseFormat.type)) {
1✔
1238
    ctx.width = (ctx.width + 3) / 4;
1✔
1239
    ctx.height = (ctx.height + 3) / 4;
1✔
1240
  }
1241
  TileVariant tvar(TileVariantFromCtx(ctx));
1✔
1242
  const TileBase *tiler =
NEW
1243
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
1244
          ? [&] {
1✔
NEW
1245
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
NEW
1246
              return ctx.customTile;
×
1247
            }()
1248
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
2✔
1249
  const size_t numBlocks = ctx.width * ctx.height;
1✔
1250

1251
  switch (ctx.baseFormat.type) {
1✔
1252
    using F = TexelInputFormatType;
1253
  case F::BC5:
1254
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
1255
      DecodeBC5Block(data + tiler->get(p) * 16,
4,096✔
1256
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
1257
                     p / ctx.width, ctx.width, 2);
4,096✔
1258
    }
1259
    break;
1260

1261
  default:
1262
    break;
1263
  }
1264
}
1✔
1265

1266
void DecodeToGray(const char *data, NewTexelContextCreate ctx,
2✔
1267
                  std::span<char> outData) {
1268
  if (BlockCompression(ctx.baseFormat.type)) {
2✔
1269
    ctx.width = (ctx.width + 3) / 4;
2✔
1270
    ctx.height = (ctx.height + 3) / 4;
2✔
1271
  }
1272
  TileVariant tvar(TileVariantFromCtx(ctx));
2✔
1273
  const TileBase *tiler =
1274
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
1275
          ? [&] {
2✔
1276
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
UNCOV
1277
              return ctx.customTile;
×
1278
            }()
1279
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
4✔
1280

1281
  switch (ctx.baseFormat.type) {
2✔
1282
    using F = TexelInputFormatType;
1283
  case F::BC4: {
2✔
1284
    const size_t numBlocks = ctx.width * ctx.height;
2✔
1285
    for (size_t p = 0; p < numBlocks; p++) {
8,194✔
1286
      DecodeBC4Block(data + tiler->get(p) * 8,
8,192✔
1287
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
8,192✔
1288
                     p / ctx.width, ctx.width);
8,192✔
1289
    }
1290
    break;
1291
  }
1292

1293
  default:
1294
    break;
1295
  }
1296
}
2✔
1297

1298
void RetileData(const char *data, NewTexelContextCreate ctx, char *outData) {
11✔
1299
  const size_t BPT = GetBPT(ctx.baseFormat.type);
11✔
1300

1301
  if (BlockCompression(ctx.baseFormat.type)) {
1302
    ctx.width = (ctx.width + 3) / 4;
2✔
1303
    ctx.height = (ctx.height + 3) / 4;
2✔
1304
  }
1305
  TileVariant tvar(TileVariantFromCtx(ctx));
11✔
1306
  const TileBase *tiler =
1307
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
1308
          ? [&] {
11✔
1309
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
UNCOV
1310
              return ctx.customTile;
×
1311
            }()
1312
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
22✔
1313

1314
  const size_t numBlocks = ctx.width * ctx.height;
11✔
1315

1316
  if (ctx.baseFormat.swapPacked) {
11✔
NEW
1317
    if (ctx.baseFormat.type == TexelInputFormatType::R5G6B5 ||
×
1318
        ctx.baseFormat.type == TexelInputFormatType::RGBA4) {
1319
      uint16 *oData = reinterpret_cast<uint16 *>(outData);
NEW
1320
      for (size_t p = 0; p < numBlocks; p++, oData++) {
×
NEW
1321
        memcpy(oData, data + tiler->get(p) * BPT, BPT);
×
1322
        FByteswapper(*oData);
1323
      }
1324

NEW
1325
      return;
×
NEW
1326
    } else if (ctx.baseFormat.type == TexelInputFormatType::RGB10A2) {
×
1327
      uint32 *oData = reinterpret_cast<uint32 *>(outData);
NEW
1328
      for (size_t p = 0; p < numBlocks; p++, oData++) {
×
NEW
1329
        memcpy(oData, data + tiler->get(p) * BPT, BPT);
×
NEW
1330
        FByteswapper(*oData);
×
1331
      }
1332

1333
      return;
1334
    }
1335
  }
1336

1337
  for (size_t p = 0; p < numBlocks; p++) {
368,427✔
1338
    memcpy(outData + p * BPT, data + tiler->get(p) * BPT, BPT);
368,416✔
1339
  }
1340
}
1341

1342
void Reencode(NewTexelContextCreate ctx, uint32 numDesiredChannels,
27✔
1343
              const char *data, std::span<char> outData_) {
1344
  const uint32 numInputChannels = FormatChannels(ctx.baseFormat.type);
27✔
1345
  const uint32 numTexels = ctx.width * ctx.height;
27✔
1346
  std::string tempBuffer;
1347
  std::span<char> outData = outData_;
27✔
1348
  uint32 outDataOffset = numTexels * (numDesiredChannels - numInputChannels);
27✔
1349

1350
  if (numDesiredChannels < numInputChannels) {
27✔
NEW
1351
    tempBuffer.resize(numTexels * numInputChannels);
×
1352
    outData = tempBuffer;
1353
    outDataOffset = 0;
1354
  }
1355

1356
  char *outDataBegin = outData.data() + outDataOffset;
27✔
1357

1358
  char white = 0xff;
27✔
1359
  char black = 0;
27✔
1360

1361
  const char *swizzleData[4];
1362
  uint8 factors[4]{1, 1, 1, 1};
27✔
1363
  uint8 invert[4]{};
27✔
1364

1365
  for (uint8 index = 0; TexelSwizzleType t : ctx.baseFormat.swizzle.types) {
135✔
1366
    switch (t) {
108✔
NEW
1367
    case TexelSwizzleType::RedInverted:
×
NEW
1368
      invert[index] = 0xff;
×
1369
      [[fallthrough]];
1370
    case TexelSwizzleType::Red:
27✔
1371
      swizzleData[index] = outDataBegin + 2;
27✔
1372
      break;
27✔
NEW
1373
    case TexelSwizzleType::GreenInverted:
×
NEW
1374
      invert[index] = 0xff;
×
1375
      [[fallthrough]];
1376
    case TexelSwizzleType::Green:
27✔
1377
      swizzleData[index] = outDataBegin + 1;
27✔
1378
      break;
27✔
NEW
1379
    case TexelSwizzleType::BlueInverted:
×
1380
    case TexelSwizzleType::DeriveZOrBlueInverted:
NEW
1381
      invert[index] = 0xff;
×
1382
      [[fallthrough]];
1383
    case TexelSwizzleType::Blue:
27✔
1384
    case TexelSwizzleType::DeriveZOrBlue:
1385
      swizzleData[index] = outDataBegin;
27✔
1386
      break;
27✔
NEW
1387
    case TexelSwizzleType::AlphaInverted:
×
NEW
1388
      invert[index] = 0xff;
×
1389
      [[fallthrough]];
1390
    case TexelSwizzleType::Alpha:
27✔
1391
      swizzleData[index] = outDataBegin + 3;
27✔
1392
      break;
27✔
NEW
1393
    case TexelSwizzleType::Black:
×
1394
    case TexelSwizzleType::DeriveZ:
NEW
1395
      swizzleData[index] = &black;
×
NEW
1396
      factors[index] = 0;
×
NEW
1397
      break;
×
NEW
1398
    case TexelSwizzleType::White:
×
NEW
1399
      swizzleData[index] = &white;
×
NEW
1400
      factors[index] = 0;
×
NEW
1401
      break;
×
1402

1403
    default:
1404
      break;
1405
    }
1406

1407
    index++;
108✔
1408
  }
1409

1410
  if (numInputChannels == 1) {
27✔
1411
    for (uint32 i = 1; i < numDesiredChannels; i++) {
9✔
1412
      if (swizzleData[i] != &white && swizzleData[i] != &black) {
6✔
1413
        invert[i] = invert[0];
6✔
1414
        swizzleData[i] = swizzleData[0];
6✔
1415
        factors[i] = factors[0];
6✔
1416
      }
1417
    }
1418

1419
    if (ctx.baseFormat.type == TexelInputFormatType::R8) {
3✔
1420
      RetileData(data, ctx, outDataBegin);
4✔
1421
    } else {
1422
      DecodeToGray(
2✔
1423
          data, ctx,
1424
          outData.subspan(numTexels * (numDesiredChannels - 1), numTexels));
1✔
1425
    }
1426
  } else if (numInputChannels == 2) {
24✔
1427
    if (ctx.baseFormat.type == TexelInputFormatType::RG8) {
2✔
1428
      RetileData(data, ctx, outDataBegin);
2✔
1429
    } else {
1430
      DecodeToRG(data, ctx,
2✔
1431
                 {reinterpret_cast<UCVector2 *>(outDataBegin), numTexels});
1432
    }
1433
  } else if (numInputChannels == 3) {
22✔
1434
    if (ctx.baseFormat.type == TexelInputFormatType::RGB8) {
2✔
NEW
1435
      RetileData(data, ctx, outDataBegin);
×
1436
    } else {
1437
      DecodeToRGB(data, ctx,
4✔
1438
                  {reinterpret_cast<UCVector *>(outDataBegin), numTexels});
1439
    }
1440
  } else if (numInputChannels == 4) {
20✔
1441
    if (ctx.baseFormat.type == TexelInputFormatType::RGBA8) {
20✔
1442
      RetileData(data, ctx, outDataBegin);
4✔
1443
    } else {
1444
      DecodeToRGBA(data, ctx,
36✔
1445
                   {reinterpret_cast<UCVector4 *>(outDataBegin), numTexels});
1446
    }
1447
  }
1448

1449
  for (uint32 t = 0; t < numTexels; t++) {
1,176,859✔
1450
    char texel[4];
1451

1452
    for (uint32 s = 0; s < numDesiredChannels; s++) {
5,515,008✔
1453
      texel[s] = (invert[s] - uint8(*swizzleData[s])) * int8(~invert[s] | 1);
4,338,176✔
1454

1455
      swizzleData[s] += factors[s] * numInputChannels;
4,338,176✔
1456
    }
1457

1458
    memcpy(outData_.data() + t * numDesiredChannels, texel, numDesiredChannels);
1,176,832✔
1459
  }
1460

1461
  [&] {
81✔
1462
    if (numDesiredChannels < 3) {
27✔
1463
      return;
1464
    }
1465

1466
    if (ctx.baseFormat.swizzle.b == TexelSwizzleType::DeriveZ) {
27✔
NEW
1467
      ComputeBC5Blue(outData_.data(), numTexels * numDesiredChannels,
×
1468
                     numDesiredChannels);
NEW
1469
      return;
×
1470
    }
1471

1472
    if (numInputChannels == 2) {
27✔
1473
      if (ctx.baseFormat.swizzle.b != TexelSwizzleType::DeriveZOrBlue &&
2✔
1474
          ctx.baseFormat.swizzle.b != TexelSwizzleType::DeriveZOrBlueInverted) {
1475
        return;
1476
      }
1477

1478
      ComputeBC5Blue(outData_.data(), numTexels * numDesiredChannels,
2✔
1479
                     numDesiredChannels);
1480
    }
1481
  }();
27✔
1482

1483
  if ((numDesiredChannels > 2 &&
27✔
1484
       ctx.baseFormat.swizzle.b == TexelSwizzleType::DeriveZ) ||
27✔
NEW
1485
      numDesiredChannels == 2 &&
×
NEW
1486
          (ctx.baseFormat.swizzle.b == TexelSwizzleType::DeriveZOrBlue ||
×
1487
           ctx.baseFormat.swizzle.b ==
1488
               TexelSwizzleType::DeriveZOrBlueInverted)) {
NEW
1489
    ComputeBC5Blue(outData_.data(), numTexels * numDesiredChannels,
×
1490
                   numDesiredChannels);
1491
  }
1492

1493
  if (ctx.postProcess) {
27✔
NEW
1494
    ctx.postProcess(outData_.data(), numDesiredChannels, numTexels);
×
1495
  }
1496
}
27✔
1497

UNCOV
1498
struct NewTexelContextQOI : NewTexelContextImpl {
×
1499
  qoi_desc qoiDesc{};
1500
  std::string yasBuffer;
1501

1502
  NewTexelContextQOI(NewTexelContextCreate ctx_) : NewTexelContextImpl(ctx_) {
56✔
1503
    qoiDesc.width = ctx.width;
28✔
1504
    qoiDesc.height = ctx.height * std::max(uint16(1), ctx.depth);
28✔
1505
    qoiDesc.colorspace = !ctx.baseFormat.srgb;
28✔
1506
    qoiDesc.channels = DesiredQOIChannels(ctx.baseFormat.type);
28✔
1507
  }
28✔
1508

1509
  void InitBuffer() {
27✔
1510
    if (BlockCompression(ctx.baseFormat.type)) {
27✔
1511
      uint32 widthPadding = qoiDesc.width % 4;
14✔
1512
      widthPadding = widthPadding ? 4 - widthPadding : 0;
14✔
1513
      uint32 heightPadding = qoiDesc.height % 4;
14✔
1514
      heightPadding = heightPadding ? 4 - heightPadding : 0;
14✔
1515

1516
      const uint32 rasterDataSize = (qoiDesc.width + widthPadding) *
14✔
1517
                                    (qoiDesc.height + heightPadding) *
14✔
1518
                                    qoiDesc.channels;
14✔
1519
      yasBuffer.resize(rasterDataSize);
14✔
1520
      return;
14✔
1521
    }
1522

1523
    const uint32 rasterDataSize =
13✔
1524
        qoiDesc.width * qoiDesc.height * qoiDesc.channels;
13✔
1525
    yasBuffer.resize(rasterDataSize);
13✔
1526
  }
1527

1528
  void SendRasterData(const void *data, TexelInputLayout layout,
100✔
1529
                      TexelInputFormat *) override {
1530
    if (layout.mipMap > 0) {
100✔
1531
      return;
×
1532
    }
1533

1534
    auto mctx = ctx;
100✔
1535
    mctx.height *= std::max(mctx.depth, uint16(1));
100✔
1536

1537
    auto Write = [&](const void *buffer) {
100✔
1538
      int encodedSize = 0;
100✔
1539
      void *buffa = qoi_encode(buffer, &qoiDesc, &encodedSize);
100✔
1540
      std::string suffix;
1541

1542
      if (ctx.arraySize > 1) {
100✔
1543
        suffix.push_back('_');
64✔
1544
        suffix.append(std::to_string(layout.layer));
128✔
1545
      }
1546

1547
      if (layout.face != CubemapFace::NONE) {
100✔
1548
        suffix.push_back('_');
12✔
1549
        static const ReflectedEnum *refl = GetReflectedEnum<CubemapFace>();
12✔
1550
        suffix.append(refl->names[uint32(layout.face)]);
12✔
1551
      }
1552

1553
      suffix.append(".qoi");
100✔
1554

1555
      outCtx->NewFile(std::string(pathOverride.ChangeExtension(suffix)));
100✔
1556
      outCtx->SendData({static_cast<char *>(buffa), size_t(encodedSize)});
100✔
1557

1558
      free(buffa);
100✔
1559
    };
100✔
1560

1561
    bool mustDecode =
NEW
1562
        IsFormatSupported(ctx.formatOverride, ctx.baseFormat.type) ||
×
1563
        MustSwap(ctx.baseFormat.type, ctx.baseFormat.swapPacked) ||
75✔
1564
        ctx.baseFormat.tile != TexelTile::Linear ||
248✔
1565
        MustSwizzle(ctx.baseFormat.swizzle, qoiDesc.channels);
73✔
1566

1567
    if (mustDecode) {
1568
      InitBuffer();
27✔
1569
      Reencode(mctx, qoiDesc.channels, static_cast<const char *>(data),
54✔
1570
               yasBuffer);
1571
      Write(yasBuffer.data());
27✔
1572
    } else {
1573
      Write(data);
73✔
1574
    }
1575
  }
1576

1577
  bool ShouldDoMipmaps() override { return false; }
×
1578

1579
  void Finish() override {}
28✔
1580
};
1581

1582
struct NewTexelContextQOIBMP : NewTexelContextQOI {
1583
  BMPHeader bmpHdr;
1584
  BMPInfoHeader bmpInfo;
1585

1586
  NewTexelContextQOIBMP(NewTexelContextCreate ctx_) : NewTexelContextQOI(ctx_) {
×
1587
    if (BMPFallback(ctx.baseFormat.type)) {
×
1588
      FillBmpFormat(ctx, ctx.baseFormat, bmpInfo);
×
1589
    }
1590
  }
1591

1592
  bool ShouldDoMipmaps() override { return false; }
×
1593

1594
  void Finish() override {}
×
1595
};
1596

1597
struct NewTexelContextDDS : NewTexelContextImpl {
1598
  DDS dds;
1599
  DDS::Mips ddsMips{};
1600
  std::vector<std::vector<bool>> mipmaps;
1601
  std::string yasBuffer;
1602
  bool mustDecode;
1603

1604
  NewTexelContextDDS(NewTexelContextCreate ctx_, bool isBase = false)
28✔
1605
      : NewTexelContextImpl(ctx_), dds(MakeDDS(ctx)),
28✔
1606
        mustDecode(IsFormatSupported(ctx.formatOverride, ctx.baseFormat.type)) {
84✔
1607
    mipmaps.resize(std::max(1U, dds.mipMapCount));
31✔
1608
    const int8 numFaces = std::max(ctx.numFaces, int8(1));
28✔
1609
    const uint32 arraySize = dds.arraySize * numFaces;
28✔
1610

1611
    if (arraySize > 1) {
28✔
1612
      for (auto &a : mipmaps) {
19✔
1613
        a.resize(arraySize);
15✔
1614
      }
1615
    } else {
1616
      size_t curSlice = 0;
1617
      for (auto &a : mipmaps) {
53✔
1618
        a.resize(std::max(uint16(1), ddsMips.numSlices[curSlice++]));
29✔
1619
      }
1620
    }
1621

1622
    if (!isBase) {
28✔
NEW
1623
      TexelInputFormat baseFmt = ctx.baseFormat;
×
1624

NEW
1625
      if (mustDecode) {
×
NEW
1626
        uint8 numChannels = DesiredDDSChannels(ctx.baseFormat.type);
×
1627

1628
        switch (numChannels) {
1629
        case 1:
NEW
1630
          baseFmt.type = TexelInputFormatType::R8;
×
NEW
1631
          break;
×
1632
        case 2:
NEW
1633
          baseFmt.type = TexelInputFormatType::RG8;
×
NEW
1634
          break;
×
1635
        case 3:
NEW
1636
          baseFmt.type = TexelInputFormatType::RGB8;
×
NEW
1637
          break;
×
1638
        case 4:
NEW
1639
          baseFmt.type = TexelInputFormatType::RGBA8;
×
NEW
1640
          break;
×
1641
        default:
1642
          break;
1643
        }
1644
      }
1645

NEW
1646
      SetDDSFormat(dds, baseFmt);
×
1647
      dds.ComputeBPP();
×
1648
      yasBuffer.resize(dds.ComputeBufferSize(ddsMips));
×
1649
      for (int32 i = 0; i < ctx.numFaces; i++) {
×
1650
        dds.caps01 -= static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
×
1651
      }
1652
    }
1653
  }
28✔
1654

1655
  bool ShouldWrite() const {
×
1656
    for (auto &l : mipmaps) {
×
1657
      for (bool m : l) {
×
1658
        if (!m) {
×
1659
          return false;
1660
        }
1661
      }
1662
    }
1663

1664
    return true;
1665
  }
1666

1667
  void SendRasterData(const void *data, TexelInputLayout layout,
×
1668
                      TexelInputFormat *) override {
1669
    if (!ShouldDoMipmaps() && layout.mipMap > 0) {
×
1670
      return;
×
1671
    }
1672

1673
    auto &mipLayers = mipmaps.at(layout.mipMap);
×
1674

1675
    const uint32 layer =
1676
        layout.layer * std::max(int8(1), ctx.numFaces) + uint8(layout.face);
×
1677

1678
    if (mipLayers.empty() || mipLayers.at(layer)) {
×
1679
      // mipmap already filled
1680
      return;
×
1681
    }
1682

1683
    mipLayers.at(layer).flip();
×
1684

1685
    auto rData = yasBuffer.data() + ddsMips.offsets[layout.mipMap];
×
1686

1687
    const size_t rDataSize =
1688
        ddsMips.sizes[layout.mipMap] *
×
1689
        std::max(ddsMips.numSlices[layout.mipMap], uint16(1));
×
1690

1691
    if (layout.face != CubemapFace::NONE) {
×
1692
      dds.caps01 +=
1693
          static_cast<DDS_HeaderEnd::Caps01Flags>(int(layout.face) + 9);
1694
      rData += ddsMips.frameStride * (int(layout.face) - 1);
×
1695
    }
1696

1697
    ptrdiff_t boundDiff = (&yasBuffer.back() + 1) - (rData + rDataSize);
×
1698
    if (boundDiff < 0) {
×
1699
      throw std::runtime_error("Writing image data beyond buffer's bounds");
×
1700
    }
1701

1702
    auto mctx = ctx;
×
1703

1704
    for (uint32 m = 0; m < layout.mipMap; m++) {
×
1705
      mctx.width = std::max(1, mctx.width / 2);
×
1706
      mctx.depth = std::max(1, mctx.depth / 2);
×
1707
      mctx.height = std::max(1, mctx.height / 2);
×
1708
    }
1709

1710
    mctx.height *= mctx.depth;
×
1711

1712
    auto DecodeStream = [&] {
×
NEW
1713
      uint8 numChannels = DesiredDDSChannels(ctx.baseFormat.type);
×
1714
      if (numChannels == 4) {
1715
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(yasBuffer.data());
1716
        UCVector4 *edn =
1717
            reinterpret_cast<UCVector4 *>(yasBuffer.data() + rDataSize);
×
1718

1719
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
×
1720
      } else if (numChannels == 3) {
×
1721
        UCVector *bgn = reinterpret_cast<UCVector *>(yasBuffer.data());
1722
        UCVector *edn =
1723
            reinterpret_cast<UCVector *>(yasBuffer.data() + rDataSize);
×
1724

1725
        DecodeToRGB(static_cast<const char *>(data), mctx, {bgn, edn});
×
1726

1727
      } else if (numChannels == 1) {
×
1728
        DecodeToGray(static_cast<const char *>(data), mctx,
×
1729
                     {yasBuffer.data(), rDataSize});
1730
      } else {
1731
        throw std::logic_error("Implement channel");
×
1732
      }
1733
    };
1734

1735
    const bool mustDecode =
NEW
1736
        IsFormatSupported(ctx.formatOverride, ctx.baseFormat.type);
×
1737

1738
    if (mustDecode) {
×
1739
      DecodeStream();
×
NEW
1740
    } else if (ctx.baseFormat.tile == TexelTile::Linear &&
×
NEW
1741
               !ctx.baseFormat.swapPacked) {
×
UNCOV
1742
      memcpy(rData, data, rDataSize);
×
1743
    } else {
1744
      RetileData(static_cast<const char *>(data), mctx, rData);
×
1745
    }
1746

1747
    if (ShouldWrite()) {
×
1748
      outCtx->NewFile(std::string(pathOverride.ChangeExtension2("dds")));
×
1749
      outCtx->SendData(
×
1750
          {reinterpret_cast<const char *>(&dds), size_t(dds.DDS_SIZE)});
×
1751
      outCtx->SendData(yasBuffer);
×
1752

1753
      es::Dispose(yasBuffer);
×
1754
    }
1755
  }
1756

1757
  bool ShouldDoMipmaps() override {
358✔
1758
    return mainSettings.texelSettings.processMipMaps;
358✔
1759
  }
1760

1761
  void Finish() override {
×
1762
    if (!ShouldWrite()) {
×
1763
      throw std::runtime_error("Incomplete dds file");
×
1764
    }
1765
  }
1766
};
1767

1768
struct NewTexelContextDDSLegacy : NewTexelContextDDS {
1769
  std::vector<std::string> arrayMipmapBuffers;
1770

1771
  NewTexelContextDDSLegacy(NewTexelContextCreate ctx_)
28✔
1772
      : NewTexelContextDDS(ctx_, true) {
56✔
1773
    arrayMipmapBuffers.resize(dds.arraySize);
28✔
1774
    dds.arraySize = 1;
28✔
1775
    TexelInputFormat baseFmt = ctx.baseFormat;
28✔
1776

1777
    if (mustDecode) {
28✔
1778
      uint8 numChannels = DesiredDDSLegacyChannels(ctx.baseFormat.type);
1779
      switch (numChannels) {
1780
      case 1:
1781
        baseFmt.type = TexelInputFormatType::R8;
1782
        break;
1783
      case 2:
1784
        baseFmt.type = TexelInputFormatType::RG8;
1785
        break;
1786
      case 3:
1787
        baseFmt.type = TexelInputFormatType::RGB8;
1788
        break;
2✔
1789
      case 4:
1790
        baseFmt.type = TexelInputFormatType::RGBA8;
1791
        break;
6✔
1792
      default:
1793
        break;
1794
      }
1795
    }
1796

1797
    SetDDSLegacyFormat(dds, baseFmt.type);
28✔
1798
    dds.ComputeBPP();
28✔
1799
    dds.ComputeBufferSize(ddsMips);
28✔
1800

1801
    for (int32 i = 0; i < ctx.numFaces; i++) {
40✔
1802
      dds.caps01 -= static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
12✔
1803
    }
1804
  }
28✔
1805

1806
  bool ShouldWrite(int32 layer) const {
329✔
1807
    if (ctx.arraySize > 1 && layer > -1) {
329✔
1808
      const int8 numFaces = std::max(int8(1), ctx.numFaces);
224✔
1809
      for (auto &m : mipmaps) {
928✔
1810
        for (int8 f = 0; f < numFaces; f++) {
1,568✔
1811
          if (!m.at(layer * numFaces + f)) {
864✔
1812
            return false;
1813
          }
1814
        }
1815
      }
1816
    } else {
1817
      for (auto &l : mipmaps) {
222✔
1818
        for (bool m : l) {
168✔
1819
          if (!m) {
720✔
1820
            return false;
1821
          }
1822
        }
1823
      }
1824
    }
1825

1826
    return true;
1827
  }
1828

1829
  void SendRasterData(const void *data, TexelInputLayout layout,
358✔
1830
                      TexelInputFormat *) override {
1831
    if (!ShouldDoMipmaps() && layout.mipMap > 0) {
358✔
1832
      return;
57✔
1833
    }
1834

1835
    auto &buffar = arrayMipmapBuffers.at(layout.layer);
358✔
1836
    auto &mipLayers = mipmaps.at(layout.mipMap);
358✔
1837

1838
    const uint32 layer = layout.layer * std::max(int8(1), ctx.numFaces) +
358✔
1839
                         std::max(int(layout.face) - 1, 0);
668✔
1840

1841
    if (mipLayers.empty() || mipLayers.at(layer)) {
358✔
1842
      // mipmap already filled
1843
      return;
57✔
1844
    }
1845

1846
    mipLayers.at(layer).flip();
301✔
1847

1848
    if (buffar.empty()) {
301✔
1849
      buffar.resize(ddsMips.frameStride * std::max(int8(1), ctx.numFaces));
92✔
1850
    }
1851

1852
    const size_t rDataSize =
1853
        ddsMips.sizes[layout.mipMap] *
602✔
1854
        std::max(ddsMips.numSlices[layout.mipMap], uint16(1));
301✔
1855

1856
    auto rData = buffar.data() + ddsMips.offsets[layout.mipMap];
301✔
1857

1858
    if (layout.face != CubemapFace::NONE) {
301✔
1859
      dds.caps01 +=
1860
          static_cast<DDS_HeaderEnd::Caps01Flags>(int(layout.face) + 9);
48✔
1861
      rData += ddsMips.frameStride * (int(layout.face) - 1);
48✔
1862
    }
1863

1864
    ptrdiff_t boundDiff = (&buffar.back() + 1) - (rData + rDataSize);
301✔
1865
    if (boundDiff < 0) {
301✔
1866
      throw std::runtime_error("Writing image data beyond buffer's bounds");
×
1867
    }
1868

1869
    auto mctx = ctx;
301✔
1870

1871
    for (uint32 m = 0; m < layout.mipMap; m++) {
922✔
1872
      mctx.width = std::max(1, mctx.width / 2);
621✔
1873
      mctx.depth = std::max(1, mctx.depth / 2);
621✔
1874
      mctx.height = std::max(1, mctx.height / 2);
1,203✔
1875
    }
1876

1877
    mctx.height *= mctx.depth;
301✔
1878

1879
    auto DecodeStream = [&] {
9✔
1880
      uint8 numChannels = DesiredDDSLegacyChannels(ctx.baseFormat.type);
9✔
1881
      if (numChannels == 4) {
1882
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(buffar.data());
9✔
1883
        UCVector4 *edn =
1884
            reinterpret_cast<UCVector4 *>(buffar.data() + rDataSize);
6✔
1885

1886
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
13✔
1887
      } else if (numChannels == 3) {
3✔
1888
        UCVector *bgn = reinterpret_cast<UCVector *>(buffar.data());
1889
        UCVector *edn = reinterpret_cast<UCVector *>(buffar.data() + rDataSize);
2✔
1890

1891
        DecodeToRGB(static_cast<const char *>(data), mctx, {bgn, edn});
4✔
1892

1893
      } else if (numChannels == 1) {
1✔
1894
        DecodeToGray(static_cast<const char *>(data), mctx,
2✔
1895
                     {buffar.data(), rDataSize});
1896
      } else {
1897
        throw std::logic_error("Implement channel");
×
1898
      }
1899
    };
9✔
1900

1901
    if (mustDecode) {
301✔
1902
      DecodeStream();
9✔
1903
    } else if (ctx.baseFormat.tile == TexelTile::Linear &&
292✔
1904
               !ctx.baseFormat.swapPacked) {
286✔
1905
      memcpy(rData, data, rDataSize);
286✔
1906
    } else {
1907
      RetileData(static_cast<const char *>(data), mctx, rData);
12✔
1908
    }
1909

1910
    if (ShouldWrite(layout.layer)) {
301✔
1911
      std::string suffix;
1912

1913
      if (ctx.arraySize > 1) {
90✔
1914
        suffix.push_back('_');
64✔
1915
        suffix.append(std::to_string(layout.layer));
128✔
1916
      }
1917
      suffix.append(".dds");
90✔
1918

1919
      outCtx->NewFile(std::string(pathOverride.ChangeExtension(suffix)));
90✔
1920
      outCtx->SendData(
×
1921
          {reinterpret_cast<const char *>(&dds), size_t(dds.LEGACY_SIZE)});
90✔
1922
      outCtx->SendData(buffar);
90✔
1923

1924
      es::Dispose(buffar);
1925
    }
1926
  }
1927

1928
  void Finish() override {
28✔
1929
    if (!ShouldWrite(-1)) {
28✔
1930
      throw std::runtime_error("Incomplete dds file");
×
1931
    }
1932
  }
28✔
1933
};
1934

1935
struct PngIHDR {
×
1936
  uint32 id = CompileFourCC("IHDR");
1937
  uint32 width;
1938
  uint32 height;
1939
  uint8 bitDepth = 8;
1940
  PngColorType colorType = PngColorType::RGBA;
1941
  uint8 compressionMethod = 0;
1942
  uint8 filterMethod = 0;
1943
  uint8 interlaceMethod = 0;
1944
};
1945

1946
void FByteswapper(PngIHDR &item) {
×
1947
  FByteswapper(item.width);
×
1948
  FByteswapper(item.height);
×
1949
}
1950

1951
struct DeflateBlock {
1952
  int16 blockSize;
1953
  int16 blockSizeComplement;
1954

1955
  void BlockSize(int16 size) {
1956
    blockSize = size;
×
1957
    blockSizeComplement = ~size;
×
1958
  }
1959

1960
  void SwapEndian() {}
1961
};
1962

1963
struct Png {
×
1964
  uint64 id = 0x0A1A0A0D474E5089;
1965
  uint32 ihdrSize = 13;
1966
  PngIHDR ihdr;
1967
};
1968

1969
struct PngData {
1970
  uint32 ihdrCRC;
1971
  uint32 idatSize;
1972
  uint32 idat = CompileFourCC("IDAT");
1973
};
1974

1975
void FByteswapper(Png &item) {
×
1976
  FByteswapper(item.ihdrSize);
×
1977
  FByteswapper(item.ihdr);
1978
}
1979

1980
void FByteswapper(PngData &item) {
×
1981
  FByteswapper(item.ihdrCRC);
×
1982
  FByteswapper(item.idatSize);
×
1983
}
1984

1985
struct PngEnd {
1986
  uint32 idatCrc;
1987
  uint32 iendSize = 0;
1988
  uint32 iend = CompileFourCC("IEND");
1989
  uint32 iendCrc = crc32b(0, "IEND", 4);
1990
};
1991

1992
void FByteswapper(PngEnd &item) {
×
1993
  FByteswapper(item.idatCrc);
×
1994
  FByteswapper(item.iendSize);
×
1995
  FByteswapper(item.iendCrc);
×
1996
}
1997

1998
std::string MakeZlibStream(const char *buffer_, PngIHDR &hdr) {
×
1999
  std::stringstream str;
×
2000
  BinWritterRef_e wr(str);
2001
  wr.Write(uint16(0x178));
×
2002
  wr.SwapEndian(true);
2003
  const uint32 numChannels = GetPngChannels(hdr.colorType);
×
2004
  const uint32 pitch = hdr.width * numChannels;
×
2005
  const uint32 scanLine = pitch + 1;
×
2006
  uint32 adlerA = 1;
×
2007
  uint32 adlerB = 0;
×
2008

2009
  auto Adler = [&](uint8 c) {
2010
    adlerA = (c + adlerA) % 65521;
×
2011
    adlerB = (adlerA + adlerB) % 65521;
×
2012
  };
2013

2014
  auto AdlerL = [&](const char *data, size_t size) {
2015
    for (size_t i = 0; i < size; i++) {
×
2016
      Adler(data[i]);
×
2017
    }
2018
  };
2019

2020
  DeflateBlock block;
2021
  block.BlockSize(scanLine);
×
2022

2023
  if (numChannels < 3) {
×
2024
    for (uint32 h = 0; h < hdr.height; h++) {
×
2025
      str.put(h == hdr.height - 1);
×
2026
      wr.Write(block);
×
2027
      str.put(0); // filter type
×
2028
      Adler(0);
2029
      AdlerL(buffer_ + pitch * h, pitch);
×
2030
      str.write(buffer_ + pitch * h, pitch);
×
2031
    }
2032
  } else if (numChannels == 3) {
×
2033
    for (uint32 h = 0; h < hdr.height; h++) {
×
2034
      str.put(h == hdr.height - 1);
×
2035
      wr.Write(block);
×
2036
      str.put(0); // filter type
×
2037
      Adler(0);
2038
      const char *startLine = buffer_ + pitch * h;
×
2039

2040
      for (uint32 w = 0; w < hdr.width; w++) {
×
2041
        char rgb[3];
2042
        memcpy(rgb, startLine + w * 3, 3);
×
2043
        std::swap(rgb[0], rgb[2]);
2044
        AdlerL(rgb, 3);
2045
        str.write(rgb, 3);
×
2046
      }
2047
    }
2048
  } else {
2049
    for (uint32 h = 0; h < hdr.height; h++) {
×
2050
      str.put(h == hdr.height - 1);
×
2051
      wr.Write(block);
×
2052
      str.put(0); // filter type
×
2053
      Adler(0);
2054
      const char *startLine = buffer_ + pitch * h;
×
2055

2056
      for (uint32 w = 0; w < hdr.width; w++) {
×
2057
        char rgb[4];
2058
        memcpy(rgb, startLine + w * 4, 4);
×
2059
        std::swap(rgb[0], rgb[2]);
2060
        AdlerL(rgb, 4);
2061
        str.write(rgb, 4);
×
2062
      }
2063
    }
2064
  }
2065

2066
  adlerA |= adlerB << 16;
×
2067
  wr.Write(adlerA);
×
2068
  return std::move(str).str();
×
2069
}
2070

2071
struct NewTexelContextPNG : NewTexelContextImpl {
2072
  std::string yasBuffer;
2073
  Png hdr;
2074
  uint32 numChannels;
2075

2076
  NewTexelContextPNG(NewTexelContextCreate ctx_) : NewTexelContextImpl(ctx_) {
×
2077
    hdr.ihdr.width = ctx.width;
×
2078
    hdr.ihdr.height = ctx.height * std::max(uint16(1), ctx.depth);
×
NEW
2079
    hdr.ihdr.colorType = DesiredPngColorType(
×
NEW
2080
        DesiredPngColorType(ctx.baseFormat.type), ctx.baseFormat.swizzle);
×
UNCOV
2081
    numChannels = GetPngChannels(hdr.ihdr.colorType);
×
2082
  }
2083

2084
  void InitBuffer() {
×
2085
    if (BlockCompression(ctx.baseFormat.type)) {
×
2086
      uint32 widthPadding = hdr.ihdr.width % 4;
×
2087
      widthPadding = widthPadding ? 4 - widthPadding : 0;
×
2088
      uint32 heightPadding = hdr.ihdr.height % 4;
×
2089
      heightPadding = heightPadding ? 4 - heightPadding : 0;
×
2090

2091
      const uint32 rasterDataSize = (hdr.ihdr.width + widthPadding) *
×
2092
                                    (hdr.ihdr.height + heightPadding) *
×
2093
                                    numChannels;
×
2094
      yasBuffer.resize(rasterDataSize);
×
2095
      return;
×
2096
    }
2097

2098
    const uint32 rasterDataSize =
×
2099
        hdr.ihdr.width * hdr.ihdr.height * numChannels;
×
2100
    yasBuffer.resize(rasterDataSize);
×
2101
  }
2102

2103
  void SendRasterData(const void *data, TexelInputLayout layout,
×
2104
                      TexelInputFormat *) override {
2105
    if (layout.mipMap > 0) {
×
2106
      return;
×
2107
    }
2108

2109
    auto mctx = ctx;
×
2110
    mctx.height *= std::max(mctx.depth, uint16(1));
×
2111

2112
    auto Write = [&](const void *buffer) {
×
2113
      std::string suffix;
2114

2115
      if (ctx.arraySize > 1) {
×
2116
        suffix.push_back('_');
×
2117
        suffix.append(std::to_string(layout.layer));
×
2118
      }
2119

2120
      if (layout.face != CubemapFace::NONE) {
×
2121
        suffix.push_back('_');
×
2122
        static const ReflectedEnum *refl = GetReflectedEnum<CubemapFace>();
2123
        suffix.append(refl->names[uint32(layout.face)]);
×
2124
      }
2125

2126
      suffix.append(".png");
×
2127

2128
      std::string encodedData =
2129
          MakeZlibStream(static_cast<const char *>(buffer), hdr.ihdr);
×
2130

2131
      Png hdrCopy = hdr;
×
2132
      FByteswapper(hdrCopy);
×
2133
      PngData hdrData{
×
2134
          .ihdrCRC = crc32b(0, reinterpret_cast<const char *>(&hdrCopy.ihdr),
×
2135
                            sizeof(hdrCopy.ihdr) - 3),
2136
          .idatSize = uint32(encodedData.size()),
2137

2138
      };
2139
      FByteswapper(hdrData);
2140

2141
      PngEnd tail{
×
2142
          .idatCrc = crc32b(crc32b(0, "IDAT", 4), encodedData.data(),
×
2143
                            encodedData.size()),
2144
      };
2145

2146
      FByteswapper(tail);
×
2147

2148
      outCtx->NewFile(std::string(pathOverride.ChangeExtension(suffix)));
×
2149
      outCtx->SendData(
×
2150
          {reinterpret_cast<const char *>(&hdrCopy), sizeof(hdrCopy) - 3});
2151
      outCtx->SendData(
×
2152
          {reinterpret_cast<const char *>(&hdrData), sizeof(hdrData)});
2153
      outCtx->SendData(encodedData);
×
2154
      outCtx->SendData({reinterpret_cast<const char *>(&tail), sizeof(tail)});
×
2155
    };
2156

2157
    bool mustDecode =
NEW
2158
        IsFormatSupported(ctx.formatOverride, ctx.baseFormat.type) ||
×
NEW
2159
        MustSwap(ctx.baseFormat.type, ctx.baseFormat.swapPacked) ||
×
NEW
2160
        ctx.baseFormat.tile != TexelTile::Linear ||
×
NEW
2161
        MustSwizzle(ctx.baseFormat.swizzle, numChannels);
×
2162

2163
    if (mustDecode) {
2164
      InitBuffer();
×
NEW
2165
      Reencode(mctx, numChannels, static_cast<const char *>(data), yasBuffer);
×
2166
      Write(yasBuffer.data());
×
2167
    } else {
NEW
2168
      Write(data);
×
2169
    }
2170
  }
2171

2172
  bool ShouldDoMipmaps() override { return false; }
×
2173

2174
  void Finish() override {}
×
2175
};
2176

2177
std::unique_ptr<NewTexelContextImpl>
2178
CreateTexelContext(NewTexelContextCreate ctx) {
56✔
2179
  if (ctx.formatOverride == TexelContextFormat::Config) {
56✔
2180
    ctx.formatOverride = OutputFormat();
56✔
2181
  }
2182

2183
  switch (ctx.formatOverride) {
56✔
2184
  case TexelContextFormat::DDS:
×
2185
    return std::make_unique<NewTexelContextDDS>(ctx);
×
2186
  case TexelContextFormat::DDS_Legacy:
28✔
2187
    return std::make_unique<NewTexelContextDDSLegacy>(ctx);
28✔
2188
  case TexelContextFormat::QOI_BMP:
×
2189
    return std::make_unique<NewTexelContextQOIBMP>(ctx);
×
2190
  case TexelContextFormat::QOI:
28✔
2191
    return std::make_unique<NewTexelContextQOI>(ctx);
28✔
2192
  case TexelContextFormat::UPNG:
×
2193
    return std::make_unique<NewTexelContextPNG>(ctx);
×
2194
  default:
×
2195
    throw std::logic_error("Image format not supported");
×
2196
  }
2197
}
2198

2199
void NewTexelContextImpl::ProcessContextData() {
×
2200
  auto layout = ComputeTraditionalDataLayout();
×
2201

2202
  for (uint32 a = 0; a < ctx.arraySize; a++) {
×
2203
    const char *entryBegin =
×
2204
        static_cast<const char *>(ctx.data) + a * layout.groupSize;
2205

2206
    if (ctx.numFaces > 0) {
×
2207
      for (int8 f = 0; f < ctx.numFaces; f++) {
×
2208
        const char *faceBegin = entryBegin + f * layout.mipGroupSize;
×
2209

2210
        for (uint32 m = 0; m < ctx.numMipmaps; m++) {
×
2211
          SendRasterData(faceBegin + layout.mipOffsets[m],
×
2212
                         {
2213
                             .mipMap = uint8(m),
2214
                             .face = static_cast<CubemapFace>(f + 1),
×
2215
                             .layer = uint16(a),
2216
                         });
2217
        }
2218
      }
2219
    } else {
2220
      for (uint32 m = 0; m < ctx.numMipmaps; m++) {
×
2221
        SendRasterData(entryBegin + layout.mipOffsets[m],
×
2222
                       {
2223
                           .mipMap = uint8(m),
2224
                           .layer = uint16(a),
2225
                       });
2226
      }
2227
    }
2228
  }
2229
}
2230

2231
std::unique_ptr<NewTexelContextImpl>
2232
CreateTexelContext(NewTexelContextCreate ctx, AppContext *actx) {
56✔
2233
  auto retVal = CreateTexelContext(ctx);
56✔
2234
  if (ctx.texelOutput) {
56✔
2235
    retVal->outCtx = ctx.texelOutput;
×
2236
  } else {
2237
    TexelOutputContext otx;
2238
    otx.ctx = actx;
2239
    retVal->outVariant = otx;
2240
    retVal->outCtx = &std::get<TexelOutputContext>(retVal->outVariant);
56✔
2241
  }
2242

2243
  retVal->pathOverride = actx->workingFile;
2244
  if (ctx.data) {
56✔
2245
    retVal->ProcessContextData();
×
2246
    return {};
2247
  }
2248

2249
  return retVal;
2250
}
2251

2252
std::unique_ptr<NewTexelContextImpl>
2253
CreateTexelContext(NewTexelContextCreate ctx, AppExtractContext *ectx,
×
2254
                   const std::string &path) {
2255
  auto retVal = CreateTexelContext(ctx);
×
2256
  if (ctx.texelOutput) {
×
2257
    retVal->outCtx = ctx.texelOutput;
×
2258
  } else {
2259
    TexelOutputExtractContext otx;
2260
    otx.ctx = ectx;
2261
    retVal->outVariant = otx;
2262
    retVal->outCtx = &std::get<TexelOutputExtractContext>(retVal->outVariant);
×
2263
  }
2264

2265
  retVal->pathOverride.Load(path);
×
2266

2267
  if (ctx.data) {
×
2268
    retVal->ProcessContextData();
×
2269
    return {};
2270
  }
2271
  return retVal;
2272
}
2273

2274
void TexelOutputContext::SendData(std::string_view data) {
280✔
2275
  str->write(data.data(), data.size());
280✔
2276
}
280✔
2277
void TexelOutputContext::NewFile(std::string filePath) {
190✔
2278
  str = &ctx->NewFile(filePath).str;
190✔
2279
}
190✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc