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

PredatorCZ / PreCore / 505

24 Mar 2024 11:59AM UTC coverage: 54.243% (-0.8%) from 55.056%
505

push

github

PredatorCZ
fix more build fails

4142 of 7636 relevant lines covered (54.24%)

8779.17 hits per line

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

50.61
/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/format/DDS.hpp"
10
#include "spike/gpu/BlockDecoder.inl"
11
#include "spike/gpu/addr_ps3.hpp"
12
#include "spike/io/binwritter_stream.hpp"
13
#include "spike/reflect/reflector.hpp"
14
#include "spike/uni/format.hpp"
15
#include <variant>
16

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

32
TexelContextFormat OutputFormat() {
×
33
  return mainSettings.texelSettings.outputFormat;
138✔
34
}
35

36
bool MustDecode(TexelInputFormatType fmt) {
401✔
37
  using F = TexelInputFormatType;
38

39
  switch (fmt) {
401✔
40
    // Not supported
41
  case F::PVRTC2:
42
  case F::PVRTC4:
43
  case F::ETC1:
44
    return true;
45

46
    // DDS, DDS_Legacy only
47
  case F::BC1:
48
  case F::BC2:
49
  case F::BC3:
50
    return OutputFormat() == TexelContextFormat::QOI ||
58✔
51
           OutputFormat() == TexelContextFormat::QOI_BMP;
52

53
    // DDS only
54
  case F::BC4:
55
  case F::BC5:
56
  case F::BC7:
57
  case F::RG8:
58
  case F::RGBA16:
59
  case F::BC6:
60
  case F::RGB9E5:
61
    return OutputFormat() != TexelContextFormat::DDS;
8✔
62

63
    // BMP, DDS only
64
  case F::RGB10A2:
65
  case F::RGB5A1:
66
    return OutputFormat() == TexelContextFormat::QOI ||
4✔
67
           OutputFormat() == TexelContextFormat::DDS_Legacy;
68

69
    // BMP only
70
  case F::P8:
71
  case F::P4:
72
    return OutputFormat() != TexelContextFormat::QOI_BMP;
×
73

74
  // DDS, DDS_Legacy, BMP only
75
  case F::RGBA4:
76
  case F::R5G6B5:
77
  case F::R8:
78
    return OutputFormat() == TexelContextFormat::QOI;
10✔
79

80
    // Supported for all
81
  case F::RGBA8:
82
  case F::INVALID:
83
    return false;
84

85
  // QOI, DDS_Legacy only
86
  case F::RGB8:
87
    return OutputFormat() == TexelContextFormat::DDS;
2✔
88
  }
89

90
  return false;
91
}
92

93
uint32 GetBPT(TexelInputFormatType fmt) {
20✔
94
  using F = TexelInputFormatType;
95

96
  switch (fmt) {
20✔
97
  case F::PVRTC2:
98
  case F::PVRTC4:
99
  case F::BC2:
100
  case F::BC3:
101
  case F::BC5:
102
  case F::BC7:
103
  case F::BC6:
104
    return 16;
105

106
  case F::ETC1:
3✔
107
  case F::BC1:
108
  case F::BC4:
109
  case F::RGBA16:
110
    return 8;
3✔
111

112
  case F::RG8:
3✔
113
  case F::RGB5A1:
114
  case F::RGBA4:
115
  case F::R5G6B5:
116
    return 2;
3✔
117

118
  case F::RGB10A2:
8✔
119
  case F::RGBA8:
120
  case F::RGB9E5:
121
    return 4;
8✔
122

123
  case F::P8:
3✔
124
  case F::P4:
125
  case F::R8:
126
    return 1;
3✔
127
  case F::RGB8:
×
128
    return 3;
×
129
  case F::INVALID:
130
    return 0;
131
  }
132

133
  return 0;
134
}
135

136
TexelDataLayout NewTexelContextImpl::ComputeTraditionalDataLayout(
×
137
    TexelInputFormatType *typeOverrides) {
138
  uint32 mipCount = std::max(ctx.numMipmaps, uint8(1));
×
139
  uint32 width = ctx.width;
×
140
  uint32 height = ctx.height;
×
141
  uint32 depth = std::max(ctx.depth, uint16(1));
×
142
  uint32 numFaces = std::max(ctx.numFaces, int8(1));
×
143

144
  TexelDataLayout retVal{};
×
145

146
  for (uint32 m = 0; m < mipCount; m++) {
×
147
    uint32 _width = width;
148
    uint32 _height = height;
149
    TexelInputFormatType type =
150
        typeOverrides ? typeOverrides[m] : ctx.baseFormat.type;
×
151
    uint32 bpt = GetBPT(type);
×
152

153
    if (BlockCompression(type)) {
154
      _width = (_width + 3) / 4;
×
155
      _height = (_height + 3) / 4;
×
156
    }
157

158
    retVal.mipSizes[m] = depth * _width * _height * bpt;
×
159
    retVal.mipOffsets[m] = retVal.mipGroupSize;
×
160
    retVal.mipGroupSize += retVal.mipSizes[m];
×
161
    width = std::max(1U, width / 2);
×
162
    height = std::max(1U, height / 2);
×
163
    depth = std::max(1U, depth / 2);
×
164
  }
165

166
  retVal.groupSize = retVal.mipGroupSize * numFaces;
×
167
  return retVal;
×
168
}
169

170
bool BMPFallback(TexelInputFormatType fmt) {
×
171
  using F = TexelInputFormatType;
172

173
  switch (fmt) {
×
174
  case F::RGB10A2:
175
  case F::RGB5A1:
176
  case F::P8:
177
  case F::P4:
178
  case F::RGBA4:
179
  case F::R5G6B5:
180
  case F::R8:
181
    return OutputFormat() == TexelContextFormat::QOI_BMP;
×
182

183
  default:
184
    return false;
185
  }
186

187
  return false;
188
}
189

190
#pragma pack(2)
191
struct BMPHeader {
×
192
  uint16 bfType = 0x4D42; // BM
193
  uint32 bfSize;
194
  uint16 bfReserved1 = 0;
195
  uint16 bfReserved2 = 0;
196
  uint32 bfOffBits = 54;
197
};
198
#pragma pack()
199

200
struct BMPInfoHeader {
×
201
  uint32 biSize = 40;
202
  uint32 biWidth;
203
  uint32 biHeight;
204
  uint16 biPlanes = 1;
205
  uint16 biBitCount = 32;
206
  uint32 biCompression = 0;
207
  uint32 biSizeImage;
208
  uint32 biXPelsPerMeter = 0;
209
  uint32 biYPelsPerMeter = 0;
210
  uint32 biClrUsed = 0;
211
  uint32 biClrImportant = 0;
212
};
213

214
union BMPMask {
215
  struct {
216
    uint32 red;
217
    uint32 green;
218
    uint32 blue;
219
    uint32 alpha;
220
  };
221
  uint32 data[4];
222
};
223

224
BMPMask SwizzleMask(BMPMask bits, TexelSwizzle swizzle[4]) {
×
225
  uint32 currentOffset = 0;
226
  BMPMask retVal{};
×
227

228
  for (uint32 c = 0; c < 4; c++) {
×
229
    uint32 swizzleIndex =
230
        swizzle[c] > TexelSwizzle::Alpha ? c : uint32(swizzle[c]);
×
231

232
    uint32 numBits = bits.data[swizzleIndex];
×
233
    uint32 mask = (1 << numBits) - 1;
×
234

235
    retVal.data[c] = mask << currentOffset;
×
236
    currentOffset += numBits;
×
237
  }
238

239
  return retVal;
×
240
}
241

242
BMPMask FillBmpFormat(NewTexelContextCreate ctx, TexelInputFormat fmt,
×
243
                      BMPInfoHeader &info) {
244
  using F = TexelInputFormatType;
245
  info.biWidth = ctx.width;
×
246
  info.biHeight = ctx.height;
×
247

248
  switch (fmt.type) {
×
249
  case F::RGB10A2:
×
250
    info.biBitCount = 32;
×
251
    info.biCompression = 6;
×
252
    return SwizzleMask({{.red = 10, .green = 10, .blue = 10, .alpha = 2}},
×
253
                       fmt.swizzle);
×
254
  case F::RGB5A1:
×
255
    info.biBitCount = 16;
×
256
    info.biCompression = 6;
×
257
    return SwizzleMask({{.red = 4, .green = 4, .blue = 4, .alpha = 1}},
×
258
                       fmt.swizzle);
×
259
  case F::RGBA4:
×
260
    info.biBitCount = 16;
×
261
    info.biCompression = 6;
×
262
    return SwizzleMask({{.red = 4, .green = 4, .blue = 4, .alpha = 4}},
×
263
                       fmt.swizzle);
×
264
  case F::R5G6B5:
×
265
    info.biBitCount = 16;
×
266
    info.biCompression = 3;
×
267
    return SwizzleMask({{.red = 5, .green = 6, .blue = 5, .alpha = 0}},
×
268
                       fmt.swizzle);
×
269
  default:
×
270
    return {};
×
271
  }
272

273
  return {};
274
}
275

276
DDS MakeDDS(NewTexelContextCreate ctx) {
28✔
277
  DDS dds;
278
  dds.width = ctx.width;
28✔
279
  dds.height = ctx.height;
28✔
280
  if (ctx.baseFormat.premultAlpha) {
28✔
281
    dds.alphaMode = dds.AlphaMode_Premultiplied;
×
282
  }
283

284
  dds.arraySize = ctx.arraySize;
28✔
285

286
  if (ctx.depth > 1) {
28✔
287
    dds.caps01 += dds.Caps01Flags_Volume;
288
    dds.flags += dds.Flags_Depth;
289
    dds.dimension = dds.Dimension_3D;
1✔
290
    dds.depth = ctx.depth;
1✔
291
  } else if (ctx.numFaces > 0) {
27✔
292
    dds.caps01 += dds.Caps01Flags_CubeMap;
293
    dds.miscFlag += dds.MiscFlag_CubeTexture;
294

295
    for (int32 i = 0; i < ctx.numFaces; i++) {
14✔
296
      dds.caps01 += static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
12✔
297
    }
298
  }
299

300
  if (mainSettings.texelSettings.processMipMaps) {
28✔
301
    dds.NumMipmaps(ctx.numMipmaps);
5✔
302
  }
303

304
  return dds;
28✔
305
}
306

307
void SetDDSFormat(DDS &dds, TexelInputFormat fmt) {
×
308
  dds = DDSFormat_DX10;
309
  switch (fmt.type) {
×
310
    using F = TexelInputFormatType;
311
  case F::BC1:
×
312
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC1_UNORM + fmt.srgb);
×
313
    break;
×
314

315
  case F::BC2:
×
316
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC2_UNORM + fmt.srgb);
×
317
    break;
×
318

319
  case F::BC3:
×
320
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC3_UNORM + fmt.srgb);
×
321
    break;
×
322

323
  case F::BC4:
×
324
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC4_UNORM + fmt.snorm);
×
325
    break;
×
326

327
  case F::BC5:
×
328
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC5_UNORM + fmt.snorm);
×
329
    break;
×
330

331
  case F::BC7:
×
332
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC7_UNORM + fmt.srgb);
×
333
    break;
×
334

335
  case F::RGBA4:
×
336
    dds.dxgiFormat = DXGI_FORMAT_B4G4R4A4_UNORM;
×
337
    break;
×
338

339
  case F::R5G6B5:
×
340
    dds.dxgiFormat = DXGI_FORMAT_B5G6R5_UNORM;
×
341
    break;
×
342

343
  case F::RGB5A1:
×
344
    dds.dxgiFormat = DXGI_FORMAT_B5G5R5A1_UNORM;
×
345
    break;
×
346

347
  case F::RGB10A2:
×
348
    dds.dxgiFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
×
349
    break;
×
350

351
  case F::RG8:
×
352
    dds.dxgiFormat =
×
353
        fmt.snorm ? DXGI_FORMAT_R8G8_SNORM : DXGI_FORMAT_R8G8_UNORM;
×
354
    break;
×
355

356
  case F::R8:
×
357
    dds.dxgiFormat = fmt.snorm ? DXGI_FORMAT_R8_SNORM : DXGI_FORMAT_R8_UNORM;
×
358
    break;
×
359

360
  case F::RGBA8:
×
361
  case F::P8:
362
  case F::P4:
363
  case F::PVRTC2:
364
  case F::PVRTC4:
365
  case F::ETC1:
366
  case F::RGB8:
367
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM + fmt.srgb);
×
368
    break;
×
369

370
  case F::RGBA16:
×
371
    dds.dxgiFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
×
372
    break;
×
373

374
  case F::BC6:
×
375
    dds.dxgiFormat = DXGI_FORMAT(DXGI_FORMAT_BC6H_UF16 + fmt.snorm);
×
376
    break;
×
377

378
  case F::RGB9E5:
×
379
    dds.dxgiFormat = DXGI_FORMAT_R9G9B9E5_SHAREDEXP;
×
380
    break;
×
381

382
  case F::INVALID:
×
383
    dds.dxgiFormat = DXGI_FORMAT_UNKNOWN;
×
384
    break;
×
385
  }
386
}
387

388
void SetDDSLegacyFormat(DDS &dds, TexelInputFormat fmt) {
28✔
389
  switch (fmt.type) {
28✔
390
    using F = TexelInputFormatType;
391
  case F::BC1:
392
    dds = DDSFormat_DXT1;
393
    break;
394

395
  case F::BC2:
396
    dds = DDSFormat_DXT3;
397
    break;
398

399
  case F::BC3:
400
    dds = DDSFormat_DXT5;
401
    break;
402

403
  case F::BC4:
404
  case F::R8:
405
    dds = DDSFormat_L8;
406
    break;
407

408
  case F::RGBA4:
409
    dds = DDSFormat_A4R4G4B4;
410
    break;
411

412
  case F::R5G6B5:
413
    dds = DDSFormat_R5G6B5;
414
    break;
415

416
  case F::RGB8:
417
    dds = DDSFormat_R8G8B8;
418
    break;
419

420
  case F::RGBA8:
421
  case F::BC7:
422
  case F::RGB5A1:
423
  case F::RGB10A2:
424
  case F::P8:
425
  case F::P4:
426
  case F::PVRTC2:
427
  case F::PVRTC4:
428
  case F::ETC1:
429
  case F::RGBA16:
430
  case F::BC6:
431
  case F::RGB9E5:
432
    dds = DDSFormat_A8R8G8B8;
433
    break;
434

435
  case F::BC5:
436
  case F::RG8:
437
    dds = DDSFormat_R8G8B8;
438
    break;
439

440
  case F::INVALID:
441
    break;
442
  }
443
}
28✔
444

445
uint8 GetQOIChannels(TexelInputFormatType fmt) {
×
446
  switch (fmt) {
×
447
    using F = TexelInputFormatType;
448
  case F::BC4:
449
  case F::R8:
450
  case F::R5G6B5:
451
  case F::BC5:
452
  case F::RG8:
453
  case F::RGB8:
454
    return 3;
455

456
  case F::BC1:
457
  case F::BC2:
458
  case F::BC3:
459
  case F::RGBA4:
460
  case F::RGBA8:
461
  case F::BC7:
462
  case F::RGB5A1:
463
  case F::RGB10A2:
464
  case F::P8:
465
  case F::P4:
466
  case F::PVRTC2:
467
  case F::PVRTC4:
468
  case F::ETC1:
469
  case F::RGBA16:
470
  case F::BC6:
471
  case F::RGB9E5:
472
    return 4;
473

474
  case F::INVALID:
×
475
    return 0;
×
476
  }
477

478
  return 4;
479
}
480

481
uint8 GetDDSChannels(TexelInputFormatType fmt) {
×
482
  switch (fmt) {
×
483
    using F = TexelInputFormatType;
484
  case F::BC4:
485
  case F::R8:
486
    return 1;
487

488
  case F::R5G6B5:
×
489
    return 3;
×
490

491
  case F::BC5:
×
492
  case F::RG8:
493
    return 2;
×
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::RGB8:
509
  case F::RGBA16:
510
  case F::BC6:
511
  case F::RGB9E5:
512
    return 4;
513

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

518
  return 4;
519
}
520

521
uint8 GetDDSLegacyChannels(TexelInputFormatType fmt) {
×
522
  switch (fmt) {
×
523
    using F = TexelInputFormatType;
524
  case F::BC4:
525
  case F::R8:
526
    return 1;
527

528
  case F::R5G6B5:
×
529
  case F::BC5:
530
  case F::RG8:
531
  case F::RGB8:
532
    return 3;
×
533

534
  case F::BC1:
535
  case F::BC2:
536
  case F::BC3:
537
  case F::RGBA4:
538
  case F::RGBA8:
539
  case F::BC7:
540
  case F::RGB5A1:
541
  case F::RGB10A2:
542
  case F::P8:
543
  case F::P4:
544
  case F::PVRTC2:
545
  case F::PVRTC4:
546
  case F::ETC1:
547
  case F::RGBA16:
548
  case F::BC6:
549
  case F::RGB9E5:
550
    return 4;
551

552
  case F::INVALID:
×
553
    return 0;
×
554
  }
555

556
  return 4;
557
}
558

559
struct LinearTile : TileBase {
×
560
  void reset(uint32, uint32, uint32) {}
×
561
  uint32 get(uint32 inTexel) const override { return inTexel; }
628,224✔
562
};
563

564
struct MortonTile : TileBase {
×
565
  MortonSettings settings;
566

567
  MortonTile(uint32 width, uint32 height) : settings(width, height) {}
×
568

569
  void reset(uint32, uint32, uint32) {}
×
570

571
  uint32 get(uint32 inTexel) const override {
×
572
    return MortonAddr(inTexel % settings.width, inTexel / settings.height,
×
573
                      settings);
×
574
  }
575
};
576

577
uint32 RoundToPow2(uint32 number) {
×
578
  number--;
×
579
  number |= number >> 1;
×
580
  number |= number >> 2;
×
581
  number |= number >> 4;
×
582
  number |= number >> 8;
×
583
  number |= number >> 16;
×
584
  number++;
×
585
  return number;
×
586
}
587

588
struct MortonPow2Tile : TileBase {
×
589
  size_t width;
590
  size_t height;
591
  size_t widthp2;
592

593
  MortonPow2Tile(size_t width_, size_t height_)
594
      : width(width_), height(height_),
595
        widthp2(RoundToPow2(std::max(width_, size_t(8)))) {}
×
596

597
  void reset(uint32, uint32, uint32) {}
×
598

599
  static size_t MortonAddr(size_t x, size_t y, size_t width) {
600
    const size_t x0 = x & 1;
×
601
    const size_t x1 = (x & 2) << 1;
×
602
    const size_t x2 = (x & 4) << 2;
×
603

604
    const size_t y0 = (y & 1) << 1;
×
605
    const size_t y1 = (y & 2) << 2;
×
606
    const size_t y2 = (y & 4) << 3;
×
607

608
    size_t retval = x0 | x1 | x2 | y0 | y1 | y2;
×
609

610
    const size_t macroX = x / 8;
×
611
    const size_t macroY = y / 8;
×
612
    const size_t macroWidth = width / 8;
×
613

614
    const size_t macroAddr = (macroWidth * macroY) + macroX;
×
615

616
    return retval | (macroAddr << 6);
×
617
  }
618

619
  uint32 get(uint32 inTexel) const override {
×
620
    return MortonAddr(inTexel % width, inTexel / height, widthp2);
×
621
  }
622
};
623

624
struct NXTile : TileBase {
12✔
625
  uint32 width;
626
  uint32 height;
627
  uint32 numUsedYBlockBits;
628
  uint32 yBlockMask;
629
  uint32 yTailMask;
630
  uint32 macroTileWidth;
631
  uint32 BPT;
632
  uint32 yTailOffset;
633
  uint32 upperBound;
634
  uint32 numUsedMicroYBits;
635
  uint32 midYShift;
636

637
  NXTile(size_t width_, size_t height_, TexelInputFormatType fmt)
12✔
638
      : width(width_), height(height_), BPT(GetBPT(fmt)),
12✔
639
        upperBound(width * height) {
12✔
640
    numUsedMicroYBits = std::min(1 << uint32(std::round(log2(height))), 3);
12✔
641
    midYShift = numUsedMicroYBits + 3;
12✔
642

643
    const uint32 macroTileHeight =
644
        std::min(uint32(std::round(log2((height + 7) / 8))), 4U);
12✔
645

646
    // addr bits 9 - 13
647
    // y bits 3 - 7
648
    yBlockMask = ((1 << macroTileHeight) - 1) << numUsedMicroYBits;
12✔
649
    numUsedYBlockBits = numUsedMicroYBits + macroTileHeight;
12✔
650

651
    // y bits 8 - end
652
    yTailMask = ~((1 << numUsedYBlockBits) - 1);
12✔
653

654
    macroTileWidth = ((width * BPT) + 63) / 64;
12✔
655

656
    yTailOffset = macroTileHeight + 2;
12✔
657
  }
12✔
658

659
  void reset(uint32, uint32, uint32) {}
×
660

661
  uint32 get(uint32 inTexel) const override {
260,672✔
662
    // 12 11 10 9  8  7  6  5  4  3  2  1  0
663
    // y  y  y  y  x  y  y  x  y  x  x  x  x
664
    // y*x*y{0,4}xyyxyx[x|0]{4}
665

666
    uint32 x = (inTexel % width) * BPT;
260,672✔
667
    uint32 y = inTexel / width;
260,672✔
668

669
    // Volumetrics not implemented
670
    // Some small rasters use alignment
671

672
    // x bits 0 - 5
673
    // y bits 0 - 2
674
    uint32 microTile = (x & 0xf) | ((y & 1) << 4) | ((x & 0x10) << 1) |
260,672✔
675
                       ((y & 0x6) << 5) | ((x & 0x20) << numUsedMicroYBits);
260,672✔
676

677
    // addr tail after yBlockMask bits
678
    // x bits 6 - end
679
    constexpr uint32 xTailMask = ~0x3f;
680

681
    uint32 macroTile = (((y & yBlockMask) << midYShift) | //
260,672✔
682
                        ((x & xTailMask) << numUsedYBlockBits)) +
260,672✔
683
                       (((y & yTailMask) << yTailOffset) * macroTileWidth);
260,672✔
684

685
    uint32 wholeTile = (microTile | macroTile) / BPT;
260,672✔
686

687
    /*if (wholeTile >= upperBound) [[unlikely]] {
688
      throw std::runtime_error("NX tile error, accessing block out of range");
689
    }*/
690

691
    return wholeTile;
260,672✔
692
  }
693
};
694

695
using TileVariant =
696
    std::variant<LinearTile, MortonTile, MortonPow2Tile, NXTile>;
697

698
TileVariant TileVariantFromCtx(NewTexelContextCreate ctx) {
42✔
699
  uint32 width = ctx.width;
42✔
700
  uint32 height = ctx.height;
42✔
701

702
  switch (ctx.baseFormat.tile) {
42✔
703
  case TexelTile::Linear:
30✔
704
    return LinearTile{};
705

706
  case TexelTile::Morton:
707
    return MortonTile(width, height);
×
708

709
  case TexelTile::MortonForcePow2:
×
710
    return MortonPow2Tile(width, height);
×
711

712
  case TexelTile::NX:
12✔
713
    return NXTile(width, height, ctx.baseFormat.type);
12✔
714

715
  case TexelTile::Custom:
716
    break;
717
  }
718

719
  return LinearTile{};
720
}
721

722
void DecodeToRGB(const char *data, NewTexelContextCreate ctx,
9✔
723
                 std::span<UCVector> outData) {
724
  if (BlockCompression(ctx.baseFormat.type)) {
9✔
725
    ctx.width = (ctx.width + 3) / 4;
3✔
726
    ctx.height = (ctx.height + 3) / 4;
3✔
727
  }
728
  TileVariant tvar(TileVariantFromCtx(ctx));
9✔
729
  const TileBase *tiler =
730
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
731
          ? [&] {
9✔
732
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
733
              return ctx.customTile;
734
            }()
735
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
18✔
736

737
  const size_t numBlocks = ctx.width * ctx.height;
9✔
738

739
  switch (ctx.baseFormat.type) {
9✔
740
    using F = TexelInputFormatType;
741
  case F::R5G6B5: {
2✔
742
    auto &codec = uni::FormatCodec::Get({
2✔
743
        .outType = uni::FormatType::UNORM,
744
        .compType = uni::DataType::R5G6B5,
745
    });
746

747
    size_t curTexel = 0;
748
    if (ctx.baseFormat.swapPacked) {
2✔
749
      const uint16 *iData = reinterpret_cast<const uint16 *>(data);
750
      for (auto &t : outData) {
×
751
        Vector4A16 value;
752
        uint32 col = *(iData + tiler->get(curTexel++));
×
753
        FByteswapper(col);
×
754

755
        codec.GetValue(value, reinterpret_cast<const char *>(&col));
×
756
        t = Vector(Vector4A16(value * 0xff)).Convert<uint8>();
×
757
      }
758
    } else {
759
      for (auto &t : outData) {
86,274✔
760
        Vector4A16 value;
761
        codec.GetValue(value, data + tiler->get(curTexel++) * 2);
86,272✔
762
        t = Vector(Vector4A16(value * 0xff)).Convert<uint8>();
86,272✔
763
      }
764
    }
765

766
    break;
767
  }
768

769
  case F::RG8: {
2✔
770
    size_t curTexel = 0;
771

772
    if (ctx.baseFormat.snorm) {
2✔
773
      const CVector2 *iData = reinterpret_cast<const CVector2 *>(data);
774
      for (auto &t : outData) {
×
775
        auto fData = (iData + tiler->get(curTexel++))->Convert<int>() + 0x80;
×
776
        t = UCVector(0, fData.y, fData.x);
×
777
      }
778
    } else {
779
      const UCVector2 *iData = reinterpret_cast<const UCVector2 *>(data);
780
      for (auto &t : outData) {
131,074✔
781
        auto tData = iData + tiler->get(curTexel++);
131,072✔
782
        t = UCVector(0, tData->y, tData->x);
131,072✔
783
      }
784
    }
785

786
    if (ctx.baseFormat.deriveZNormal) {
2✔
787
      ComputeBC5Blue(reinterpret_cast<char *>(outData.data()),
2✔
788
                     outData.size() * 3);
789
    }
790
    break;
791
  }
792

793
  case F::BC5:
794
    for (size_t p = 0; p < numBlocks; p++) {
8,194✔
795
      DecodeBC5Block(data + tiler->get(p) * 16,
8,192✔
796
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
8,192✔
797
                     p / ctx.width, ctx.width);
8,192✔
798
    }
799

800
    if (ctx.baseFormat.deriveZNormal) {
2✔
801
      ComputeBC5Blue(reinterpret_cast<char *>(outData.data()),
2✔
802
                     outData.size() * 3);
803
    }
804
    break;
805

806
  case F::BC4:
807
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
808
      _DecodeBC4Block(data + tiler->get(p) * 8,
4,096✔
809
                      reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
810
                      p / ctx.width, ctx.width, 3);
4,096✔
811
    }
812

813
    for (auto &p : outData) {
65,537✔
814
      p.y = p.z = p.x;
65,536✔
815
    }
816

817
    break;
818

819
  case F::R8: {
2✔
820
    size_t curTexel = 0;
821
    for (auto &t : outData) {
86,274✔
822
      t = UCVector(*(data + tiler->get(curTexel++)));
86,272✔
823
    }
824

825
    break;
826
  }
827

828
  default:
×
829
    throw std::logic_error("Implement rgb decode");
×
830
  }
831
}
9✔
832

833
void DecodeToRGBA(const char *data, NewTexelContextCreate ctx,
24✔
834
                  std::span<UCVector4> outData) {
835
  if (BlockCompression(ctx.baseFormat.type)) {
24✔
836
    ctx.width = (ctx.width + 3) / 4;
13✔
837
    ctx.height = (ctx.height + 3) / 4;
13✔
838
  }
839
  TileVariant tvar(TileVariantFromCtx(ctx));
24✔
840
  const TileBase *tiler =
841
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
842
          ? [&] {
24✔
843
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
844
              return ctx.customTile;
845
            }()
846
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
48✔
847

848
  const size_t numBlocks = ctx.width * ctx.height;
24✔
849

850
  switch (ctx.baseFormat.type) {
24✔
851
    using F = TexelInputFormatType;
852
  case F::RGB10A2: {
2✔
853
    auto &codec = uni::FormatCodec::Get({
2✔
854
        .outType = uni::FormatType::UNORM,
855
        .compType = uni::DataType::R10G10B10A2,
856
    });
857

858
    size_t curTexel = 0;
859
    if (ctx.baseFormat.swapPacked) {
2✔
860
      const uint32 *iData = reinterpret_cast<const uint32 *>(data);
861
      for (auto &t : outData) {
×
862
        Vector4A16 value;
863
        uint32 col = *(iData + tiler->get(curTexel++));
×
864
        FByteswapper(col);
×
865

866
        codec.GetValue(value, reinterpret_cast<const char *>(&col));
×
867
        t = (value * 0xff).Convert<uint8>();
868
      }
869
    } else {
870
      for (auto &t : outData) {
131,074✔
871
        Vector4A16 value;
872
        codec.GetValue(value, data + tiler->get(curTexel++) * 4);
131,072✔
873
        t = (value * 0xff).Convert<uint8>();
874
      }
875
    }
876
    break;
877
  }
878

879
  case F::RGBA4: {
1✔
880
    const uint16 *iData = reinterpret_cast<const uint16 *>(data);
881
    size_t curTexel = 0;
882
    if (ctx.baseFormat.swapPacked) {
1✔
883
      for (auto &t : outData) {
×
884
        uint16 col = *(iData + tiler->get(curTexel++));
×
885
        FByteswapper(col);
886
        t = UCVector4(col << 4, col & 0xf0, (col >> 4) & 0xf0,
×
887
                      (col >> 8) & 0xf0);
×
888
      }
889
    } else {
890
      for (auto &t : outData) {
65,537✔
891
        uint16 col = *(iData + tiler->get(curTexel++));
65,536✔
892
        t = UCVector4(col << 4, col & 0xf0, (col >> 4) & 0xf0,
65,536✔
893
                      (col >> 8) & 0xf0);
65,536✔
894
      }
895
    }
896

897
    break;
898
  }
899

900
  case F::RGB5A1: {
2✔
901
    const uint16 *iData = reinterpret_cast<const uint16 *>(data);
902
    size_t curTexel = 0;
903

904
    if (ctx.baseFormat.swapPacked) {
2✔
905
      for (auto &t : outData) {
×
906
        uint16 col = *(iData + tiler->get(curTexel++));
×
907
        FByteswapper(col);
908
        t = UCVector4(col << 3, (col >> 2) & 0xf8, (col >> 7) & 0xf8,
×
909
                      int16(col) >> 15);
×
910
      }
911
    } else {
912
      for (auto &t : outData) {
131,074✔
913
        uint16 col = *(iData + tiler->get(curTexel++));
131,072✔
914
        t = UCVector4(col << 3, (col >> 2) & 0xf8, (col >> 7) & 0xf8,
131,072✔
915
                      int16(col) >> 15);
131,072✔
916
      }
917
    }
918

919
    break;
920
  }
921

922
  case F::PVRTC2:
923
    pvr::PVRTDecompressPVRTC(data, 1, ctx.width, ctx.height,
2✔
924
                             reinterpret_cast<uint8_t *>(outData.data()));
925
    break;
926

927
  case F::PVRTC4:
928
    pvr::PVRTDecompressPVRTC(data, 0, ctx.width, ctx.height,
2✔
929
                             reinterpret_cast<uint8_t *>(outData.data()));
930
    break;
931

932
  case F::ETC1:
933
    pvr::PVRTDecompressETC(data, ctx.width, ctx.height,
2✔
934
                           reinterpret_cast<uint8_t *>(outData.data()), 0);
935
    break;
936

937
  case F::BC7: {
938
    uint32 localBlock[16];
939
    for (size_t p = 0; p < numBlocks; p++) {
8,194✔
940
      if (!detexDecompressBlockBPTC(
8,192✔
941
              reinterpret_cast<const uint8_t *>(data) + tiler->get(p) * 16, -1,
8,192✔
942
              0, reinterpret_cast<uint8_t *>(localBlock))) [[unlikely]] {
×
943
        throw std::runtime_error("Failed to decompress BC7 block");
×
944
      }
945
      uint32 x = p % ctx.width;
8,192✔
946
      uint32 y = p / ctx.width;
8,192✔
947
      uint32 blockOffset = ctx.width * y * 16 + x * 4;
8,192✔
948

949
      for (size_t r = 0; r < 4; r++) {
40,960✔
950
        UCVector4 *addr = outData.data() + blockOffset + r * ctx.width * 4;
32,768✔
951
        memcpy(static_cast<void *>(addr), localBlock + r * 4, 16);
32,768✔
952
      }
953
    }
954
    break;
955
  }
956

957
  case F::BC1:
958
    for (size_t p = 0; p < numBlocks; p++) {
5,394✔
959
      DecodeBC1BlockA(data + tiler->get(p) * 8,
5,392✔
960
                      reinterpret_cast<char *>(outData.data()), p % ctx.width,
5,392✔
961
                      p / ctx.width, ctx.width);
5,392✔
962
    }
963

964
    break;
965

966
  case F::BC2:
967
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
968
      DecodeBC2Block(data + tiler->get(p) * 16,
4,096✔
969
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
970
                     p / ctx.width, ctx.width);
4,096✔
971
    }
972

973
    break;
974

975
  case F::BC3:
976
    for (size_t p = 0; p < numBlocks; p++) {
6,936✔
977
      DecodeBC3Block(data + tiler->get(p) * 16,
6,928✔
978
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
6,928✔
979
                     p / ctx.width, ctx.width);
6,928✔
980
    }
981

982
    break;
983

984
  default:
×
985
    throw std::logic_error("Implement rgba decode");
×
986
  }
987
}
24✔
988

989
void DecodeToGray(const char *data, NewTexelContextCreate ctx,
1✔
990
                  std::span<char> outData) {
991
  if (BlockCompression(ctx.baseFormat.type)) {
1✔
992
    ctx.width = (ctx.width + 3) / 4;
1✔
993
    ctx.height = (ctx.height + 3) / 4;
1✔
994
  }
995
  TileVariant tvar(TileVariantFromCtx(ctx));
1✔
996
  const TileBase *tiler =
997
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
998
          ? [&] {
1✔
999
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
1000
              return ctx.customTile;
1001
            }()
1002
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
2✔
1003

1004
  switch (ctx.baseFormat.type) {
1✔
1005
    using F = TexelInputFormatType;
1006
  case F::BC4: {
1✔
1007
    const size_t numBlocks = ctx.width * ctx.height;
1✔
1008
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
1009
      DecodeBC4Block(data + tiler->get(p) * 8,
4,096✔
1010
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
1011
                     p / ctx.width, ctx.width);
4,096✔
1012
    }
1013
    break;
1014
  }
1015

1016
  default:
1017
    break;
1018
  }
1019
}
1✔
1020

1021
void RetileData(const char *data, NewTexelContextCreate ctx, char *outData) {
8✔
1022
  const size_t BPT = GetBPT(ctx.baseFormat.type);
8✔
1023

1024
  if (BlockCompression(ctx.baseFormat.type)) {
1025
    ctx.width = (ctx.width + 3) / 4;
2✔
1026
    ctx.height = (ctx.height + 3) / 4;
2✔
1027
  }
1028
  TileVariant tvar(TileVariantFromCtx(ctx));
8✔
1029
  const TileBase *tiler =
1030
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
1031
          ? [&] {
8✔
1032
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
1033
              return ctx.customTile;
1034
            }()
1035
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
16✔
1036

1037
  const size_t numBlocks = ctx.width * ctx.height;
8✔
1038
  for (size_t p = 0; p < numBlocks; p++) {
216,616✔
1039
    memcpy(outData + p * BPT, data + tiler->get(p) * BPT, BPT);
216,608✔
1040
  }
1041
}
8✔
1042

1043
struct NewTexelContextQOI : NewTexelContextImpl {
1044
  qoi_desc qoiDesc{};
1045
  std::string yasBuffer;
1046

1047
  NewTexelContextQOI(NewTexelContextCreate ctx_) : NewTexelContextImpl(ctx_) {
28✔
1048
    qoiDesc.width = ctx.width;
28✔
1049
    qoiDesc.height = ctx.height * std::max(uint16(1), ctx.depth);
28✔
1050
    qoiDesc.colorspace = !ctx.baseFormat.srgb;
28✔
1051
    qoiDesc.channels = GetQOIChannels(ctx.baseFormat.type);
28✔
1052
  }
28✔
1053

1054
  void InitBuffer() {
27✔
1055
    uint32 widthPadding = qoiDesc.width % 4;
27✔
1056
    widthPadding = widthPadding ? 4 - widthPadding : 0;
27✔
1057
    uint32 heightPadding = qoiDesc.height % 4;
27✔
1058
    heightPadding = heightPadding ? 4 - heightPadding : 0;
27✔
1059

1060
    const uint32 rasterDataSize = (qoiDesc.width + widthPadding) *
27✔
1061
                                  (qoiDesc.height + heightPadding) *
27✔
1062
                                  qoiDesc.channels;
27✔
1063
    yasBuffer.resize(rasterDataSize);
27✔
1064
  }
27✔
1065

1066
  void SendRasterData(const void *data, TexelInputLayout layout,
100✔
1067
                      TexelInputFormat *) override {
1068
    if (layout.mipMap > 0) {
100✔
1069
      return;
×
1070
    }
1071

1072
    auto mctx = ctx;
100✔
1073
    mctx.height *= std::max(mctx.depth, uint16(1));
100✔
1074

1075
    auto DecodeStream = [&] {
25✔
1076
      InitBuffer();
25✔
1077

1078
      if (qoiDesc.channels == 4) {
25✔
1079
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(yasBuffer.data());
1080
        UCVector4 *edn =
1081
            reinterpret_cast<UCVector4 *>(yasBuffer.data() + yasBuffer.size());
1082

1083
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
18✔
1084
      } else if (qoiDesc.channels == 3) {
7✔
1085
        UCVector *bgn = reinterpret_cast<UCVector *>(yasBuffer.data());
1086
        UCVector *edn =
1087
            reinterpret_cast<UCVector *>(yasBuffer.data() + yasBuffer.size());
1088

1089
        DecodeToRGB(static_cast<const char *>(data), mctx, {bgn, edn});
7✔
1090

1091
      } else if (qoiDesc.channels == 1) {
×
1092
        DecodeToGray(static_cast<const char *>(data), mctx,
×
1093
                     {yasBuffer.data(), yasBuffer.size()});
1094

1095
      } else {
1096
        throw std::logic_error("Implement channel");
×
1097
      }
1098
    };
25✔
1099

1100
    auto Write = [&](const void *buffer) {
100✔
1101
      int encodedSize = 0;
100✔
1102
      void *buffa = qoi_encode(buffer, &qoiDesc, &encodedSize);
100✔
1103
      std::string suffix;
1104

1105
      if (ctx.arraySize > 1) {
100✔
1106
        suffix.push_back('_');
64✔
1107
        suffix.append(std::to_string(layout.layer));
128✔
1108
      }
1109

1110
      if (layout.face != CubemapFace::NONE) {
100✔
1111
        suffix.push_back('_');
12✔
1112
        static const ReflectedEnum *refl = GetReflectedEnum<CubemapFace>();
12✔
1113
        suffix.append(refl->names[uint32(layout.face)]);
12✔
1114
      }
1115

1116
      suffix.append(".qoi");
100✔
1117

1118
      if (ectx) {
100✔
1119
        if (pathOverride.GetFullPath().empty()) {
×
1120
          throw std::logic_error("Expected path");
×
1121
        }
1122

1123
        ectx->NewFile(std::string(pathOverride.ChangeExtension(suffix)));
×
1124
        ectx->SendData({static_cast<char *>(buffa), size_t(encodedSize)});
×
1125
      } else {
1126
        AFileInfo &workingPath = pathOverride.GetFullPath().empty()
1127
                                     ? actx->workingFile
100✔
1128
                                     : pathOverride;
100✔
1129

1130
        BinWritterRef wr(
1131
            actx->NewFile(workingPath.ChangeExtension(suffix)).str);
100✔
1132
        wr.WriteBuffer(static_cast<char *>(buffa), encodedSize);
100✔
1133
      }
1134

1135
      free(buffa);
100✔
1136
    };
100✔
1137

1138
    if (MustDecode(ctx.baseFormat.type)) {
100✔
1139
      DecodeStream();
25✔
1140
      Write(yasBuffer.data());
25✔
1141
    } else if (ctx.baseFormat.tile == TexelTile::Linear) {
75✔
1142
      Write(data);
73✔
1143
    } else {
1144
      InitBuffer();
2✔
1145
      RetileData(static_cast<const char *>(data), mctx, yasBuffer.data());
2✔
1146
      Write(yasBuffer.data());
2✔
1147
    }
1148
  }
1149

1150
  bool ShouldDoMipmaps() override { return false; }
×
1151

1152
  void Finish() override {}
28✔
1153
};
1154

1155
struct NewTexelContextQOIBMP : NewTexelContextQOI {
1156
  BMPHeader bmpHdr;
1157
  BMPInfoHeader bmpInfo;
1158

1159
  NewTexelContextQOIBMP(NewTexelContextCreate ctx_) : NewTexelContextQOI(ctx_) {
×
1160
    if (BMPFallback(ctx.baseFormat.type)) {
×
1161
      FillBmpFormat(ctx, ctx.baseFormat, bmpInfo);
×
1162
    }
1163
  }
1164

1165
  bool ShouldDoMipmaps() override { return false; }
×
1166

1167
  void Finish() override {}
×
1168
};
1169

1170
struct NewTexelContextDDS : NewTexelContextImpl {
1171
  DDS dds;
1172
  DDS::Mips ddsMips{};
1173
  std::vector<std::vector<bool>> mipmaps;
1174
  std::string yasBuffer;
1175

1176
  NewTexelContextDDS(NewTexelContextCreate ctx_, bool isBase = false)
28✔
1177
      : NewTexelContextImpl(ctx_), dds(MakeDDS(ctx)) {
28✔
1178
    mipmaps.resize(std::max(1U, dds.mipMapCount));
31✔
1179
    const int8 numFaces = std::max(ctx.numFaces, int8(1));
28✔
1180
    const uint32 arraySize = dds.arraySize * numFaces;
28✔
1181

1182
    if (arraySize > 1) {
28✔
1183
      for (auto &a : mipmaps) {
19✔
1184
        a.resize(arraySize);
15✔
1185
      }
1186
    } else {
1187
      size_t curSlice = 0;
1188
      for (auto &a : mipmaps) {
53✔
1189
        a.resize(std::max(uint16(1), ddsMips.numSlices[curSlice++]));
29✔
1190
      }
1191
    }
1192

1193
    if (!isBase) {
28✔
1194
      SetDDSFormat(dds, ctx.baseFormat);
×
1195
      dds.ComputeBPP();
×
1196
      yasBuffer.resize(dds.ComputeBufferSize(ddsMips));
×
1197
      for (int32 i = 0; i < ctx.numFaces; i++) {
×
1198
        dds.caps01 -= static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
×
1199
      }
1200
    }
1201
  }
28✔
1202

1203
  bool ShouldWrite() const {
×
1204
    for (auto &l : mipmaps) {
×
1205
      for (bool m : l) {
×
1206
        if (!m) {
×
1207
          return false;
1208
        }
1209
      }
1210
    }
1211

1212
    return true;
1213
  }
1214

1215
  void SendRasterData(const void *data, TexelInputLayout layout,
×
1216
                      TexelInputFormat *) override {
1217
    if (!ShouldDoMipmaps() && layout.mipMap > 0) {
×
1218
      return;
×
1219
    }
1220

1221
    auto &mipLayers = mipmaps.at(layout.mipMap);
×
1222

1223
    const uint32 layer =
1224
        layout.layer * std::max(int8(1), ctx.numFaces) + uint8(layout.face);
×
1225

1226
    if (mipLayers.empty() || mipLayers.at(layer)) {
×
1227
      // mipmap already filled
1228
      return;
×
1229
    }
1230

1231
    mipLayers.at(layer).flip();
×
1232

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

1235
    const size_t rDataSize =
1236
        ddsMips.sizes[layout.mipMap] *
×
1237
        std::max(ddsMips.numSlices[layout.mipMap], uint16(1));
×
1238

1239
    if (layout.face != CubemapFace::NONE) {
×
1240
      dds.caps01 +=
1241
          static_cast<DDS_HeaderEnd::Caps01Flags>(int(layout.face) + 9);
1242
      rData += ddsMips.frameStride * (int(layout.face) - 1);
×
1243
    }
1244

1245
    ptrdiff_t boundDiff = (&yasBuffer.back() + 1) - (rData + rDataSize);
×
1246
    if (boundDiff < 0) {
×
1247
      throw std::runtime_error("Writing image data beyond buffer's bounds");
×
1248
    }
1249

1250
    auto mctx = ctx;
×
1251

1252
    for (uint32 m = 0; m < layout.mipMap; m++) {
×
1253
      mctx.width = std::max(1, mctx.width / 2);
×
1254
      mctx.depth = std::max(1, mctx.depth / 2);
×
1255
      mctx.height = std::max(1, mctx.height / 2);
×
1256
    }
1257

1258
    mctx.height *= mctx.depth;
×
1259

1260
    auto DecodeStream = [&] {
×
1261
      uint8 numChannels = GetDDSChannels(ctx.baseFormat.type);
×
1262
      if (numChannels == 4) {
1263
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(yasBuffer.data());
1264
        UCVector4 *edn =
1265
            reinterpret_cast<UCVector4 *>(yasBuffer.data() + rDataSize);
×
1266

1267
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
×
1268
      } else if (numChannels == 3) {
×
1269
        UCVector *bgn = reinterpret_cast<UCVector *>(yasBuffer.data());
1270
        UCVector *edn =
1271
            reinterpret_cast<UCVector *>(yasBuffer.data() + rDataSize);
×
1272

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

1275
      } else if (numChannels == 1) {
×
1276
        DecodeToGray(static_cast<const char *>(data), mctx,
×
1277
                     {yasBuffer.data(), rDataSize});
1278
      } else {
1279
        throw std::logic_error("Implement channel");
×
1280
      }
1281
    };
1282

1283
    const bool mustDecode = MustDecode(ctx.baseFormat.type);
×
1284

1285
    if (mustDecode) {
×
1286
      DecodeStream();
×
1287
    } else if (ctx.baseFormat.tile == TexelTile::Linear) {
×
1288
      memcpy(rData, data, rDataSize);
×
1289
    } else {
1290
      RetileData(static_cast<const char *>(data), mctx, rData);
×
1291
    }
1292

1293
    if (ShouldWrite()) {
×
1294
      if (ectx) {
×
1295
        if (pathOverride.GetFullPath().empty()) {
×
1296
          throw std::logic_error("Expected path");
×
1297
        }
1298

1299
        ectx->NewFile(std::string(pathOverride.ChangeExtension2("dds")));
×
1300
        ectx->SendData(
×
1301
            {reinterpret_cast<const char *>(&dds), size_t(dds.DDS_SIZE)});
×
1302
        ectx->SendData(yasBuffer);
×
1303
      } else {
1304
        AFileInfo &workingPath = pathOverride.GetFullPath().empty()
1305
                                     ? actx->workingFile
×
1306
                                     : pathOverride;
×
1307
        BinWritterRef wr(
1308
            actx->NewFile(workingPath.ChangeExtension2("dds")).str);
×
1309
        wr.WriteBuffer(reinterpret_cast<const char *>(&dds), dds.DDS_SIZE);
×
1310
        wr.WriteContainer(yasBuffer);
1311
      }
1312
      es::Dispose(yasBuffer);
×
1313
    }
1314
  }
1315

1316
  bool ShouldDoMipmaps() override {
358✔
1317
    return mainSettings.texelSettings.processMipMaps;
358✔
1318
  }
1319

1320
  void Finish() override {
×
1321
    if (!ShouldWrite()) {
×
1322
      throw std::runtime_error("Incomplete dds file");
×
1323
    }
1324
  }
1325
};
1326

1327
struct NewTexelContextDDSLegacy : NewTexelContextDDS {
1328
  std::vector<std::string> arrayMipmapBuffers;
1329

1330
  NewTexelContextDDSLegacy(NewTexelContextCreate ctx_)
28✔
1331
      : NewTexelContextDDS(ctx_, true) {
28✔
1332
    arrayMipmapBuffers.resize(dds.arraySize);
28✔
1333
    dds.arraySize = 1;
28✔
1334

1335
    SetDDSLegacyFormat(dds, ctx.baseFormat);
28✔
1336
    dds.ComputeBPP();
28✔
1337
    dds.ComputeBufferSize(ddsMips);
28✔
1338

1339
    for (int32 i = 0; i < ctx.numFaces; i++) {
40✔
1340
      dds.caps01 -= static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
12✔
1341
    }
1342
  }
28✔
1343

1344
  bool ShouldWrite(int32 layer) const {
329✔
1345
    if (ctx.arraySize > 1 && layer > -1) {
329✔
1346
      const int8 numFaces = std::max(int8(1), ctx.numFaces);
224✔
1347
      for (auto &m : mipmaps) {
928✔
1348
        for (int8 f = 0; f < numFaces; f++) {
1,568✔
1349
          if (!m.at(layer * numFaces + f)) {
864✔
1350
            return false;
1351
          }
1352
        }
1353
      }
1354
    } else {
1355
      for (auto &l : mipmaps) {
222✔
1356
        for (bool m : l) {
168✔
1357
          if (!m) {
720✔
1358
            return false;
1359
          }
1360
        }
1361
      }
1362
    }
1363

1364
    return true;
1365
  }
1366

1367
  void SendRasterData(const void *data, TexelInputLayout layout,
358✔
1368
                      TexelInputFormat *) override {
1369
    if (!ShouldDoMipmaps() && layout.mipMap > 0) {
358✔
1370
      return;
57✔
1371
    }
1372

1373
    auto &buffar = arrayMipmapBuffers.at(layout.layer);
358✔
1374
    auto &mipLayers = mipmaps.at(layout.mipMap);
358✔
1375

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

1379
    if (mipLayers.empty() || mipLayers.at(layer)) {
358✔
1380
      // mipmap already filled
1381
      return;
57✔
1382
    }
1383

1384
    mipLayers.at(layer).flip();
301✔
1385

1386
    if (buffar.empty()) {
301✔
1387
      buffar.resize(ddsMips.frameStride * std::max(int8(1), ctx.numFaces));
92✔
1388
    }
1389

1390
    const size_t rDataSize =
1391
        ddsMips.sizes[layout.mipMap] *
602✔
1392
        std::max(ddsMips.numSlices[layout.mipMap], uint16(1));
301✔
1393

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

1396
    if (layout.face != CubemapFace::NONE) {
301✔
1397
      dds.caps01 +=
1398
          static_cast<DDS_HeaderEnd::Caps01Flags>(int(layout.face) + 9);
48✔
1399
      rData += ddsMips.frameStride * (int(layout.face) - 1);
48✔
1400
    }
1401

1402
    ptrdiff_t boundDiff = (&buffar.back() + 1) - (rData + rDataSize);
301✔
1403
    if (boundDiff < 0) {
301✔
1404
      throw std::runtime_error("Writing image data beyond buffer's bounds");
×
1405
    }
1406

1407
    auto mctx = ctx;
301✔
1408

1409
    for (uint32 m = 0; m < layout.mipMap; m++) {
922✔
1410
      mctx.width = std::max(1, mctx.width / 2);
621✔
1411
      mctx.depth = std::max(1, mctx.depth / 2);
621✔
1412
      mctx.height = std::max(1, mctx.height / 2);
1,203✔
1413
    }
1414

1415
    mctx.height *= mctx.depth;
301✔
1416

1417
    auto DecodeStream = [&] {
9✔
1418
      uint8 numChannels = GetDDSLegacyChannels(ctx.baseFormat.type);
9✔
1419
      if (numChannels == 4) {
1420
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(buffar.data());
9✔
1421
        UCVector4 *edn =
1422
            reinterpret_cast<UCVector4 *>(buffar.data() + rDataSize);
6✔
1423

1424
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
7✔
1425
      } else if (numChannels == 3) {
3✔
1426
        UCVector *bgn = reinterpret_cast<UCVector *>(buffar.data());
1427
        UCVector *edn = reinterpret_cast<UCVector *>(buffar.data() + rDataSize);
2✔
1428

1429
        DecodeToRGB(static_cast<const char *>(data), mctx, {bgn, edn});
2✔
1430

1431
      } else if (numChannels == 1) {
1✔
1432
        DecodeToGray(static_cast<const char *>(data), mctx,
1✔
1433
                     {buffar.data(), rDataSize});
1434
      } else {
1435
        throw std::logic_error("Implement channel");
×
1436
      }
1437
    };
9✔
1438

1439
    const bool mustDecode = MustDecode(ctx.baseFormat.type);
301✔
1440

1441
    if (mustDecode) {
301✔
1442
      DecodeStream();
9✔
1443
    } else if (ctx.baseFormat.tile == TexelTile::Linear) {
292✔
1444
      memcpy(rData, data, rDataSize);
286✔
1445
    } else {
1446
      RetileData(static_cast<const char *>(data), mctx, rData);
6✔
1447
    }
1448

1449
    if (ShouldWrite(layout.layer)) {
301✔
1450
      std::string suffix;
1451

1452
      if (ctx.arraySize > 1) {
90✔
1453
        suffix.push_back('_');
64✔
1454
        suffix.append(std::to_string(layout.layer));
128✔
1455
      }
1456
      suffix.append(".dds");
90✔
1457

1458
      if (ectx) {
90✔
1459
        if (pathOverride.GetFullPath().empty()) {
×
1460
          throw std::logic_error("Expected path");
×
1461
        }
1462

1463
        ectx->NewFile(std::string(pathOverride.ChangeExtension(suffix)));
×
1464
        ectx->SendData(
×
1465
            {reinterpret_cast<const char *>(&dds), size_t(dds.LEGACY_SIZE)});
×
1466
        ectx->SendData(buffar);
×
1467
      } else {
1468
        AFileInfo &workingPath = pathOverride.GetFullPath().empty()
1469
                                     ? actx->workingFile
×
1470
                                     : pathOverride;
90✔
1471
        BinWritterRef wr(
1472
            actx->NewFile(workingPath.ChangeExtension(suffix)).str);
90✔
1473
        wr.WriteBuffer(reinterpret_cast<const char *>(&dds), dds.LEGACY_SIZE);
90✔
1474
        wr.WriteContainer(buffar);
1475
      }
1476
      es::Dispose(buffar);
1477
    }
1478
  }
1479

1480
  void Finish() override {
28✔
1481
    if (!ShouldWrite(-1)) {
28✔
1482
      throw std::runtime_error("Incomplete dds file");
×
1483
    }
1484
  }
28✔
1485
};
1486

1487
std::unique_ptr<NewTexelContextImpl>
1488
CreateTexelContext(NewTexelContextCreate ctx) {
56✔
1489
  switch (OutputFormat()) {
56✔
1490
  case TexelContextFormat::DDS:
×
1491
    return std::make_unique<NewTexelContextDDS>(ctx);
×
1492
  case TexelContextFormat::DDS_Legacy:
28✔
1493
    return std::make_unique<NewTexelContextDDSLegacy>(ctx);
28✔
1494
  case TexelContextFormat::QOI_BMP:
×
1495
    return std::make_unique<NewTexelContextQOIBMP>(ctx);
×
1496
  case TexelContextFormat::QOI:
28✔
1497
    return std::make_unique<NewTexelContextQOI>(ctx);
28✔
1498
  default:
×
1499
    throw std::logic_error("Image format not supported");
×
1500
  }
1501
}
1502

1503
void NewTexelContextImpl::ProcessContextData() {
×
1504
  auto layout = ComputeTraditionalDataLayout();
×
1505

1506
  for (uint32 a = 0; a < ctx.arraySize; a++) {
×
1507
    const char *entryBegin =
×
1508
        static_cast<const char *>(ctx.data) + a * layout.groupSize;
1509

1510
    if (ctx.numFaces > 0) {
×
1511
      for (int8 f = 0; f < ctx.numFaces; f++) {
×
1512
        const char *faceBegin = entryBegin + f * layout.mipGroupSize;
×
1513

1514
        for (uint32 m = 0; m < ctx.numMipmaps; m++) {
×
1515
          SendRasterData(faceBegin + layout.mipOffsets[m],
×
1516
                         {
1517
                             .mipMap = uint8(m),
1518
                             .face = static_cast<CubemapFace>(f + 1),
×
1519
                             .layer = uint16(a),
1520
                         });
1521
        }
1522
      }
1523
    } else {
1524
      for (uint32 m = 0; m < ctx.numMipmaps; m++) {
×
1525
        SendRasterData(entryBegin + layout.mipOffsets[m],
×
1526
                       {
1527
                           .mipMap = uint8(m),
1528
                           .layer = uint16(a),
1529
                       });
1530
      }
1531
    }
1532
  }
1533
}
1534

1535
std::unique_ptr<NewTexelContextImpl>
1536
CreateTexelContext(NewTexelContextCreate ctx, AppContext *actx) {
56✔
1537
  auto retVal = CreateTexelContext(ctx);
56✔
1538
  retVal->actx = actx;
56✔
1539
  if (ctx.data) {
56✔
1540
    retVal->ProcessContextData();
×
1541
    return {};
1542
  }
1543

1544
  return retVal;
1545
}
1546

1547
std::unique_ptr<NewTexelContextImpl>
1548
CreateTexelContext(NewTexelContextCreate ctx, AppExtractContext *ectx,
×
1549
                   const std::string &path) {
1550
  auto retVal = CreateTexelContext(ctx);
×
1551
  retVal->ectx = ectx;
×
1552
  retVal->pathOverride.Load(path);
×
1553
  if (ctx.data) {
×
1554
    retVal->ProcessContextData();
×
1555
    return {};
1556
  }
1557
  return retVal;
1558
}
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

© 2026 Coveralls, Inc