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

PredatorCZ / PreCore / 493

10 Dec 2023 01:42PM UTC coverage: 55.199% (-0.07%) from 55.265%
493

push

github

PredatorCZ
texel fix extension overrides for extract contexts

0 of 2 new or added lines in 1 file covered. (0.0%)

5 existing lines in 1 file now uncovered.

4141 of 7502 relevant lines covered (55.2%)

8927.01 hits per line

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

51.74
/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
    for (auto &t : outData) {
86,274✔
749
      Vector4A16 value;
750
      codec.GetValue(value, data + tiler->get(curTexel++) * 2);
86,272✔
751
      t = Vector(Vector4A16(value * 0xff)).Convert<uint8>();
86,272✔
752
    }
753

754
    break;
755
  }
756

757
  case F::RG8: {
2✔
758
    size_t curTexel = 0;
759

760
    if (ctx.baseFormat.snorm) {
2✔
761
      const CVector2 *iData = reinterpret_cast<const CVector2 *>(data);
762
      for (auto &t : outData) {
×
763
        auto fData = (iData + tiler->get(curTexel++))->Convert<int>() + 0x80;
×
764
        t = UCVector(0, fData.y, fData.x);
×
765
      }
766
    } else {
767
      const UCVector2 *iData = reinterpret_cast<const UCVector2 *>(data);
768
      for (auto &t : outData) {
131,074✔
769
        auto tData = iData + tiler->get(curTexel++);
131,072✔
770
        t = UCVector(0, tData->y, tData->x);
131,072✔
771
      }
772
    }
773

774
    if (ctx.baseFormat.deriveZNormal) {
2✔
775
      ComputeBC5Blue(reinterpret_cast<char *>(outData.data()),
2✔
776
                     outData.size() * 3);
777
    }
778
    break;
779
  }
780

781
  case F::BC5:
782
    for (size_t p = 0; p < numBlocks; p++) {
8,194✔
783
      DecodeBC5Block(data + tiler->get(p) * 16,
8,192✔
784
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
8,192✔
785
                     p / ctx.width, ctx.width);
8,192✔
786
    }
787

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

794
  case F::BC4:
795
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
796
      _DecodeBC4Block(data + tiler->get(p) * 8,
4,096✔
797
                      reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
798
                      p / ctx.width, ctx.width, 3);
4,096✔
799
    }
800

801
    for (auto &p : outData) {
65,537✔
802
      p.y = p.z = p.x;
65,536✔
803
    }
804

805
    break;
806

807
  case F::R8: {
2✔
808
    size_t curTexel = 0;
809
    for (auto &t : outData) {
86,274✔
810
      t = UCVector(*(data + tiler->get(curTexel++)));
86,272✔
811
    }
812

813
    break;
814
  }
815

816
  default:
×
817
    throw std::logic_error("Implement rgb decode");
×
818
  }
819
}
9✔
820

821
void DecodeToRGBA(const char *data, NewTexelContextCreate ctx,
24✔
822
                  std::span<UCVector4> outData) {
823
  if (BlockCompression(ctx.baseFormat.type)) {
24✔
824
    ctx.width = (ctx.width + 3) / 4;
13✔
825
    ctx.height = (ctx.height + 3) / 4;
13✔
826
  }
827
  TileVariant tvar(TileVariantFromCtx(ctx));
24✔
828
  const TileBase *tiler =
829
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
830
          ? [&] {
24✔
831
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
832
              return ctx.customTile;
833
            }()
834
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
48✔
835

836
  const size_t numBlocks = ctx.width * ctx.height;
24✔
837

838
  switch (ctx.baseFormat.type) {
24✔
839
    using F = TexelInputFormatType;
840
  case F::RGB10A2: {
2✔
841
    auto &codec = uni::FormatCodec::Get({
2✔
842
        .outType = uni::FormatType::UNORM,
843
        .compType = uni::DataType::R10G10B10A2,
844
    });
845

846
    size_t curTexel = 0;
847
    for (auto &t : outData) {
131,074✔
848
      Vector4A16 value;
849
      codec.GetValue(value, data + tiler->get(curTexel++) * 4);
131,072✔
850
      t = (value * 0xff).Convert<uint8>();
851
    }
852
    break;
853
  }
854

855
  case F::RGBA4: {
1✔
856
    const uint16 *iData = reinterpret_cast<const uint16 *>(data);
857
    size_t curTexel = 0;
858
    for (auto &t : outData) {
65,537✔
859
      uint16 col = *(iData + tiler->get(curTexel++));
65,536✔
860
      t = UCVector4(col << 4, col & 0xf0, (col >> 4) & 0xf0, (col >> 8) & 0xf0);
65,536✔
861
    }
862

863
    break;
864
  }
865

866
  case F::RGB5A1: {
2✔
867
    const uint16 *iData = reinterpret_cast<const uint16 *>(data);
868
    size_t curTexel = 0;
869
    for (auto &t : outData) {
131,074✔
870
      uint16 col = *(iData + tiler->get(curTexel++));
131,072✔
871
      t = UCVector4(col << 3, (col >> 2) & 0xf8, (col >> 7) & 0xf8,
131,072✔
872
                    int16(col) >> 15);
131,072✔
873
    }
874

875
    break;
876
  }
877

878
  case F::PVRTC2:
879
    pvr::PVRTDecompressPVRTC(data, 1, ctx.width, ctx.height,
2✔
880
                             reinterpret_cast<uint8_t *>(outData.data()));
881
    break;
882

883
  case F::PVRTC4:
884
    pvr::PVRTDecompressPVRTC(data, 0, ctx.width, ctx.height,
2✔
885
                             reinterpret_cast<uint8_t *>(outData.data()));
886
    break;
887

888
  case F::ETC1:
889
    pvr::PVRTDecompressETC(data, ctx.width, ctx.height,
2✔
890
                           reinterpret_cast<uint8_t *>(outData.data()), 0);
891
    break;
892

893
  case F::BC7: {
894
    uint32 localBlock[16];
895
    for (size_t p = 0; p < numBlocks; p++) {
8,194✔
896
      if (!detexDecompressBlockBPTC(
8,192✔
897
              reinterpret_cast<const uint8_t *>(data) + tiler->get(p) * 16, -1,
8,192✔
898
              0, reinterpret_cast<uint8_t *>(localBlock))) [[unlikely]] {
×
899
        throw std::runtime_error("Failed to decompress BC7 block");
×
900
      }
901
      uint32 x = p % ctx.width;
8,192✔
902
      uint32 y = p / ctx.width;
8,192✔
903
      uint32 blockOffset = ctx.width * y * 16 + x * 4;
8,192✔
904

905
      for (size_t r = 0; r < 4; r++) {
40,960✔
906
        UCVector4 *addr = outData.data() + blockOffset + r * ctx.width * 4;
32,768✔
907
        memcpy(static_cast<void *>(addr), localBlock + r * 4, 16);
32,768✔
908
      }
909
    }
910
    break;
911
  }
912

913
  case F::BC1:
914
    for (size_t p = 0; p < numBlocks; p++) {
5,394✔
915
      DecodeBC1BlockA(data + tiler->get(p) * 8,
5,392✔
916
                      reinterpret_cast<char *>(outData.data()), p % ctx.width,
5,392✔
917
                      p / ctx.width, ctx.width);
5,392✔
918
    }
919

920
    break;
921

922
  case F::BC2:
923
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
924
      DecodeBC2Block(data + tiler->get(p) * 16,
4,096✔
925
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
926
                     p / ctx.width, ctx.width);
4,096✔
927
    }
928

929
    break;
930

931
  case F::BC3:
932
    for (size_t p = 0; p < numBlocks; p++) {
6,936✔
933
      DecodeBC3Block(data + tiler->get(p) * 16,
6,928✔
934
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
6,928✔
935
                     p / ctx.width, ctx.width);
6,928✔
936
    }
937

938
    break;
939

940
  default:
×
941
    throw std::logic_error("Implement rgba decode");
×
942
  }
943
}
24✔
944

945
void DecodeToGray(const char *data, NewTexelContextCreate ctx,
1✔
946
                  std::span<char> outData) {
947
  if (BlockCompression(ctx.baseFormat.type)) {
1✔
948
    ctx.width = (ctx.width + 3) / 4;
1✔
949
    ctx.height = (ctx.height + 3) / 4;
1✔
950
  }
951
  TileVariant tvar(TileVariantFromCtx(ctx));
1✔
952
  const TileBase *tiler =
953
      ctx.customTile && ctx.baseFormat.tile == TexelTile::Custom
×
954
          ? [&] {
1✔
955
              ctx.customTile->reset(ctx.width, ctx.height, ctx.depth);
×
956
              return ctx.customTile;
957
            }()
958
          : std::visit([](auto &item) -> const TileBase * { return &item; }, tvar);
2✔
959

960
  switch (ctx.baseFormat.type) {
1✔
961
    using F = TexelInputFormatType;
962
  case F::BC4: {
1✔
963
    const size_t numBlocks = ctx.width * ctx.height;
1✔
964
    for (size_t p = 0; p < numBlocks; p++) {
4,097✔
965
      DecodeBC4Block(data + tiler->get(p) * 8,
4,096✔
966
                     reinterpret_cast<char *>(outData.data()), p % ctx.width,
4,096✔
967
                     p / ctx.width, ctx.width);
4,096✔
968
    }
969
    break;
970
  }
971

972
  default:
973
    break;
974
  }
975
}
1✔
976

977
void RetileData(const char *data, NewTexelContextCreate ctx, char *outData) {
8✔
978
  const size_t BPT = GetBPT(ctx.baseFormat.type);
8✔
979

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

993
  const size_t numBlocks = ctx.width * ctx.height;
8✔
994
  for (size_t p = 0; p < numBlocks; p++) {
216,616✔
995
    memcpy(outData + p * BPT, data + tiler->get(p) * BPT, BPT);
216,608✔
996
  }
997
}
8✔
998

999
struct NewTexelContextQOI : NewTexelContextImpl {
1000
  qoi_desc qoiDesc{};
1001
  std::string yasBuffer;
1002

1003
  NewTexelContextQOI(NewTexelContextCreate ctx_) : NewTexelContextImpl(ctx_) {
28✔
1004
    qoiDesc.width = ctx.width;
28✔
1005
    qoiDesc.height = ctx.height * std::max(uint16(1), ctx.depth);
28✔
1006
    qoiDesc.colorspace = !ctx.baseFormat.srgb;
28✔
1007
    qoiDesc.channels = GetQOIChannels(ctx.baseFormat.type);
28✔
1008
  }
28✔
1009

1010
  void InitBuffer() {
27✔
1011
    uint32 widthPadding = qoiDesc.width % 4;
27✔
1012
    widthPadding = widthPadding ? 4 - widthPadding : 0;
27✔
1013
    uint32 heightPadding = qoiDesc.height % 4;
27✔
1014
    heightPadding = heightPadding ? 4 - heightPadding : 0;
27✔
1015

1016
    const uint32 rasterDataSize = (qoiDesc.width + widthPadding) *
27✔
1017
                                  (qoiDesc.height + heightPadding) *
27✔
1018
                                  qoiDesc.channels;
27✔
1019
    yasBuffer.resize(rasterDataSize);
27✔
1020
  }
27✔
1021

1022
  void SendRasterData(const void *data, TexelInputLayout layout,
100✔
1023
                      TexelInputFormat *) override {
1024
    if (layout.mipMap > 0) {
100✔
1025
      return;
×
1026
    }
1027

1028
    auto mctx = ctx;
100✔
1029
    mctx.height *= std::max(mctx.depth, uint16(1));
100✔
1030

1031
    auto DecodeStream = [&] {
25✔
1032
      InitBuffer();
25✔
1033

1034
      if (qoiDesc.channels == 4) {
25✔
1035
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(yasBuffer.data());
1036
        UCVector4 *edn =
1037
            reinterpret_cast<UCVector4 *>(yasBuffer.data() + yasBuffer.size());
1038

1039
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
18✔
1040
      } else if (qoiDesc.channels == 3) {
7✔
1041
        UCVector *bgn = reinterpret_cast<UCVector *>(yasBuffer.data());
1042
        UCVector *edn =
1043
            reinterpret_cast<UCVector *>(yasBuffer.data() + yasBuffer.size());
1044

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

1047
      } else if (qoiDesc.channels == 1) {
×
1048
        DecodeToGray(static_cast<const char *>(data), mctx,
×
1049
                     {yasBuffer.data(), yasBuffer.size()});
1050

1051
      } else {
1052
        throw std::logic_error("Implement channel");
×
1053
      }
1054
    };
25✔
1055

1056
    auto Write = [&](const void *buffer) {
100✔
1057
      int encodedSize = 0;
100✔
1058
      void *buffa = qoi_encode(buffer, &qoiDesc, &encodedSize);
100✔
1059
      std::string suffix;
1060

1061
      if (ctx.arraySize > 1) {
100✔
1062
        suffix.push_back('_');
64✔
1063
        suffix.append(std::to_string(layout.layer));
128✔
1064
      }
1065

1066
      if (layout.face != CubemapFace::NONE) {
100✔
1067
        suffix.push_back('_');
12✔
1068
        static const ReflectedEnum *refl = GetReflectedEnum<CubemapFace>();
12✔
1069
        suffix.append(refl->names[uint32(layout.face)]);
12✔
1070
      }
1071

1072
      suffix.append(".qoi");
100✔
1073

1074
      if (ectx) {
100✔
1075
        if (pathOverride.GetFullPath().empty()) {
×
1076
          throw std::logic_error("Expected path");
×
1077
        }
1078

1079
        ectx->NewFile(std::string(pathOverride.ChangeExtension(suffix)));
×
1080
        ectx->SendData({static_cast<char *>(buffa), size_t(encodedSize)});
×
1081
      } else {
1082
        AFileInfo &workingPath = pathOverride.GetFullPath().empty()
1083
                                     ? actx->workingFile
100✔
1084
                                     : pathOverride;
100✔
1085

1086
        BinWritterRef wr(
1087
            actx->NewFile(workingPath.ChangeExtension(suffix)).str);
100✔
1088
        wr.WriteBuffer(static_cast<char *>(buffa), encodedSize);
100✔
1089
      }
1090

1091
      free(buffa);
100✔
1092
    };
100✔
1093

1094
    if (MustDecode(ctx.baseFormat.type)) {
100✔
1095
      DecodeStream();
25✔
1096
      Write(yasBuffer.data());
25✔
1097
    } else if (ctx.baseFormat.tile == TexelTile::Linear) {
75✔
1098
      Write(data);
73✔
1099
    } else {
1100
      InitBuffer();
2✔
1101
      RetileData(static_cast<const char *>(data), mctx, yasBuffer.data());
2✔
1102
      Write(yasBuffer.data());
2✔
1103
    }
1104
  }
1105

1106
  bool ShouldDoMipmaps() override { return false; }
×
1107

1108
  void Finish() override {}
28✔
1109
};
1110

1111
struct NewTexelContextQOIBMP : NewTexelContextQOI {
1112
  BMPHeader bmpHdr;
1113
  BMPInfoHeader bmpInfo;
1114

1115
  NewTexelContextQOIBMP(NewTexelContextCreate ctx_) : NewTexelContextQOI(ctx_) {
×
1116
    if (BMPFallback(ctx.baseFormat.type)) {
×
1117
      FillBmpFormat(ctx, ctx.baseFormat, bmpInfo);
×
1118
    }
1119
  }
1120

1121
  bool ShouldDoMipmaps() override { return false; }
×
1122

1123
  void Finish() override {}
×
1124
};
1125

1126
struct NewTexelContextDDS : NewTexelContextImpl {
1127
  DDS dds;
1128
  DDS::Mips ddsMips{};
1129
  std::vector<std::vector<bool>> mipmaps;
1130
  std::string yasBuffer;
1131

1132
  NewTexelContextDDS(NewTexelContextCreate ctx_, bool isBase = false)
28✔
1133
      : NewTexelContextImpl(ctx_), dds(MakeDDS(ctx)) {
28✔
1134
    mipmaps.resize(std::max(1U, dds.mipMapCount));
31✔
1135
    const int8 numFaces = std::max(ctx.numFaces, int8(1));
28✔
1136
    const uint32 arraySize = dds.arraySize * numFaces;
28✔
1137

1138
    if (arraySize > 1) {
28✔
1139
      for (auto &a : mipmaps) {
19✔
1140
        a.resize(arraySize);
15✔
1141
      }
1142
    } else {
1143
      size_t curSlice = 0;
1144
      for (auto &a : mipmaps) {
53✔
1145
        a.resize(std::max(uint16(1), ddsMips.numSlices[curSlice++]));
29✔
1146
      }
1147
    }
1148

1149
    if (!isBase) {
28✔
1150
      SetDDSFormat(dds, ctx.baseFormat);
×
1151
      dds.ComputeBPP();
×
1152
      yasBuffer.resize(dds.ComputeBufferSize(ddsMips));
×
1153
      for (int32 i = 0; i < ctx.numFaces; i++) {
×
1154
        dds.caps01 -= static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
×
1155
      }
1156
    }
1157
  }
28✔
1158

1159
  bool ShouldWrite() const {
×
1160
    for (auto &l : mipmaps) {
×
1161
      for (bool m : l) {
×
1162
        if (!m) {
×
1163
          return false;
1164
        }
1165
      }
1166
    }
1167

1168
    return true;
1169
  }
1170

1171
  void SendRasterData(const void *data, TexelInputLayout layout,
×
1172
                      TexelInputFormat *) override {
1173
    if (!ShouldDoMipmaps() && layout.mipMap > 0) {
×
1174
      return;
×
1175
    }
1176

1177
    auto &mipLayers = mipmaps.at(layout.mipMap);
×
1178

1179
    const uint32 layer =
1180
        layout.layer * std::max(int8(1), ctx.numFaces) + uint8(layout.face);
×
1181

1182
    if (mipLayers.empty() || mipLayers.at(layer)) {
×
1183
      // mipmap already filled
1184
      return;
×
1185
    }
1186

1187
    mipLayers.at(layer).flip();
×
1188

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

1191
    const size_t rDataSize =
1192
        ddsMips.sizes[layout.mipMap] *
×
1193
        std::max(ddsMips.numSlices[layout.mipMap], uint16(1));
×
1194

1195
    if (layout.face != CubemapFace::NONE) {
×
1196
      dds.caps01 +=
1197
          static_cast<DDS_HeaderEnd::Caps01Flags>(int(layout.face) + 9);
1198
      rData += ddsMips.frameStride * (int(layout.face) - 1);
×
1199
    }
1200

1201
    ptrdiff_t boundDiff = (&yasBuffer.back() + 1) - (rData + rDataSize);
×
1202
    if (boundDiff < 0) {
×
1203
      throw std::runtime_error("Writing image data beyond buffer's bounds");
×
1204
    }
1205

1206
    auto mctx = ctx;
×
1207

1208
    for (uint32 m = 0; m < layout.mipMap; m++) {
×
1209
      mctx.width = std::max(1, mctx.width / 2);
×
1210
      mctx.depth = std::max(1, mctx.depth / 2);
×
1211
      mctx.height = std::max(1, mctx.height / 2);
×
1212
    }
1213

1214
    mctx.height *= mctx.depth;
×
1215

1216
    auto DecodeStream = [&] {
×
1217
      uint8 numChannels = GetDDSChannels(ctx.baseFormat.type);
×
1218
      if (numChannels == 4) {
1219
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(yasBuffer.data());
1220
        UCVector4 *edn =
1221
            reinterpret_cast<UCVector4 *>(yasBuffer.data() + rDataSize);
×
1222

1223
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
×
1224
      } else if (numChannels == 3) {
×
1225
        UCVector *bgn = reinterpret_cast<UCVector *>(yasBuffer.data());
1226
        UCVector *edn =
1227
            reinterpret_cast<UCVector *>(yasBuffer.data() + rDataSize);
×
1228

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

1231
      } else if (numChannels == 1) {
×
1232
        DecodeToGray(static_cast<const char *>(data), mctx,
×
1233
                     {yasBuffer.data(), rDataSize});
1234
      } else {
1235
        throw std::logic_error("Implement channel");
×
1236
      }
1237
    };
1238

1239
    const bool mustDecode = MustDecode(ctx.baseFormat.type);
×
1240

1241
    if (mustDecode) {
×
1242
      DecodeStream();
×
1243
    } else if (ctx.baseFormat.tile == TexelTile::Linear) {
×
1244
      memcpy(rData, data, rDataSize);
×
1245
    } else {
1246
      RetileData(static_cast<const char *>(data), mctx, rData);
×
1247
    }
1248

1249
    if (ShouldWrite()) {
×
1250
      if (ectx) {
×
1251
        if (pathOverride.GetFullPath().empty()) {
×
1252
          throw std::logic_error("Expected path");
×
1253
        }
1254

NEW
1255
        ectx->NewFile(std::string(pathOverride.ChangeExtension2("dds")));
×
1256
        ectx->SendData({reinterpret_cast<const char *>(&dds), size_t(dds.DDS_SIZE)});
×
1257
        ectx->SendData(yasBuffer);
×
1258
      } else {
1259
        AFileInfo &workingPath = pathOverride.GetFullPath().empty()
1260
                                     ? actx->workingFile
×
1261
                                     : pathOverride;
×
1262
        BinWritterRef wr(
1263
            actx->NewFile(workingPath.ChangeExtension2("dds")).str);
×
1264
        wr.WriteBuffer(reinterpret_cast<const char *>(&dds), dds.DDS_SIZE);
×
1265
        wr.WriteContainer(yasBuffer);
1266
      }
1267
      es::Dispose(yasBuffer);
×
1268
    }
1269
  }
1270

1271
  bool ShouldDoMipmaps() override {
358✔
1272
    return mainSettings.texelSettings.processMipMaps;
358✔
1273
  }
1274

1275
  void Finish() override {
×
1276
    if (!ShouldWrite()) {
×
1277
      throw std::runtime_error("Incomplete dds file");
×
1278
    }
1279
  }
1280
};
1281

1282
struct NewTexelContextDDSLegacy : NewTexelContextDDS {
1283
  std::vector<std::string> arrayMipmapBuffers;
1284

1285
  NewTexelContextDDSLegacy(NewTexelContextCreate ctx_)
28✔
1286
      : NewTexelContextDDS(ctx_, true) {
28✔
1287
    arrayMipmapBuffers.resize(dds.arraySize);
28✔
1288
    dds.arraySize = 1;
28✔
1289

1290
    SetDDSLegacyFormat(dds, ctx.baseFormat);
28✔
1291
    dds.ComputeBPP();
28✔
1292
    dds.ComputeBufferSize(ddsMips);
28✔
1293

1294
    for (int32 i = 0; i < ctx.numFaces; i++) {
40✔
1295
      dds.caps01 -= static_cast<DDS_HeaderEnd::Caps01Flags>(i + 10);
12✔
1296
    }
1297
  }
28✔
1298

1299
  bool ShouldWrite(int32 layer) const {
329✔
1300
    if (ctx.arraySize > 1 && layer > -1) {
329✔
1301
      const int8 numFaces = std::max(int8(1), ctx.numFaces);
224✔
1302
      for (auto &m : mipmaps) {
928✔
1303
        for (int8 f = 0; f < numFaces; f++) {
1,568✔
1304
          if (!m.at(layer * numFaces + f)) {
864✔
1305
            return false;
1306
          }
1307
        }
1308
      }
1309
    } else {
1310
      for (auto &l : mipmaps) {
222✔
1311
        for (bool m : l) {
168✔
1312
          if (!m) {
720✔
1313
            return false;
1314
          }
1315
        }
1316
      }
1317
    }
1318

1319
    return true;
1320
  }
1321

1322
  void SendRasterData(const void *data, TexelInputLayout layout,
358✔
1323
                      TexelInputFormat *) override {
1324
    if (!ShouldDoMipmaps() && layout.mipMap > 0) {
358✔
1325
      return;
57✔
1326
    }
1327

1328
    auto &buffar = arrayMipmapBuffers.at(layout.layer);
358✔
1329
    auto &mipLayers = mipmaps.at(layout.mipMap);
358✔
1330

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

1334
    if (mipLayers.empty() || mipLayers.at(layer)) {
358✔
1335
      // mipmap already filled
1336
      return;
57✔
1337
    }
1338

1339
    mipLayers.at(layer).flip();
301✔
1340

1341
    if (buffar.empty()) {
301✔
1342
      buffar.resize(ddsMips.frameStride * std::max(int8(1), ctx.numFaces));
92✔
1343
    }
1344

1345
    const size_t rDataSize =
1346
        ddsMips.sizes[layout.mipMap] *
602✔
1347
        std::max(ddsMips.numSlices[layout.mipMap], uint16(1));
301✔
1348

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

1351
    if (layout.face != CubemapFace::NONE) {
301✔
1352
      dds.caps01 +=
1353
          static_cast<DDS_HeaderEnd::Caps01Flags>(int(layout.face) + 9);
48✔
1354
      rData += ddsMips.frameStride * (int(layout.face) - 1);
48✔
1355
    }
1356

1357
    ptrdiff_t boundDiff = (&buffar.back() + 1) - (rData + rDataSize);
301✔
1358
    if (boundDiff < 0) {
301✔
1359
      throw std::runtime_error("Writing image data beyond buffer's bounds");
×
1360
    }
1361

1362
    auto mctx = ctx;
301✔
1363

1364
    for (uint32 m = 0; m < layout.mipMap; m++) {
922✔
1365
      mctx.width = std::max(1, mctx.width / 2);
621✔
1366
      mctx.depth = std::max(1, mctx.depth / 2);
621✔
1367
      mctx.height = std::max(1, mctx.height / 2);
1,203✔
1368
    }
1369

1370
    mctx.height *= mctx.depth;
301✔
1371

1372
    auto DecodeStream = [&] {
9✔
1373
      uint8 numChannels = GetDDSLegacyChannels(ctx.baseFormat.type);
9✔
1374
      if (numChannels == 4) {
1375
        UCVector4 *bgn = reinterpret_cast<UCVector4 *>(buffar.data());
9✔
1376
        UCVector4 *edn =
1377
            reinterpret_cast<UCVector4 *>(buffar.data() + rDataSize);
6✔
1378

1379
        DecodeToRGBA(static_cast<const char *>(data), mctx, {bgn, edn});
7✔
1380
      } else if (numChannels == 3) {
3✔
1381
        UCVector *bgn = reinterpret_cast<UCVector *>(buffar.data());
1382
        UCVector *edn = reinterpret_cast<UCVector *>(buffar.data() + rDataSize);
2✔
1383

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

1386
      } else if (numChannels == 1) {
1✔
1387
        DecodeToGray(static_cast<const char *>(data), mctx,
1✔
1388
                     {buffar.data(), rDataSize});
1389
      } else {
1390
        throw std::logic_error("Implement channel");
×
1391
      }
1392
    };
9✔
1393

1394
    const bool mustDecode = MustDecode(ctx.baseFormat.type);
301✔
1395

1396
    if (mustDecode) {
301✔
1397
      DecodeStream();
9✔
1398
    } else if (ctx.baseFormat.tile == TexelTile::Linear) {
292✔
1399
      memcpy(rData, data, rDataSize);
286✔
1400
    } else {
1401
      RetileData(static_cast<const char *>(data), mctx, rData);
6✔
1402
    }
1403

1404
    if (ShouldWrite(layout.layer)) {
301✔
1405
      std::string suffix;
1406

1407
      if (ctx.arraySize > 1) {
90✔
1408
        suffix.push_back('_');
64✔
1409
        suffix.append(std::to_string(layout.layer));
128✔
1410
      }
1411
      suffix.append(".dds");
90✔
1412

1413
      if (ectx) {
90✔
1414
        if (pathOverride.GetFullPath().empty()) {
×
1415
          throw std::logic_error("Expected path");
×
1416
        }
1417

NEW
1418
        ectx->NewFile(std::string(pathOverride.ChangeExtension(suffix)));
×
1419
        ectx->SendData({reinterpret_cast<const char *>(&dds), size_t(dds.LEGACY_SIZE)});
×
1420
        ectx->SendData(buffar);
×
1421
      } else {
1422
        AFileInfo &workingPath = pathOverride.GetFullPath().empty()
1423
                                     ? actx->workingFile
×
1424
                                     : pathOverride;
90✔
1425
        BinWritterRef wr(
1426
            actx->NewFile(workingPath.ChangeExtension(suffix)).str);
90✔
1427
        wr.WriteBuffer(reinterpret_cast<const char *>(&dds), dds.LEGACY_SIZE);
90✔
1428
        wr.WriteContainer(buffar);
1429
      }
1430
      es::Dispose(buffar);
1431
    }
1432
  }
1433

1434
  void Finish() override {
28✔
1435
    if (!ShouldWrite(-1)) {
28✔
1436
      throw std::runtime_error("Incomplete dds file");
×
1437
    }
1438
  }
28✔
1439
};
1440

1441
std::unique_ptr<NewTexelContextImpl>
1442
CreateTexelContext(NewTexelContextCreate ctx) {
56✔
1443
  switch (OutputFormat()) {
56✔
1444
  case TexelContextFormat::DDS:
×
1445
    return std::make_unique<NewTexelContextDDS>(ctx);
×
1446
  case TexelContextFormat::DDS_Legacy:
28✔
1447
    return std::make_unique<NewTexelContextDDSLegacy>(ctx);
28✔
1448
  case TexelContextFormat::QOI_BMP:
×
1449
    return std::make_unique<NewTexelContextQOIBMP>(ctx);
×
1450
  case TexelContextFormat::QOI:
28✔
1451
    return std::make_unique<NewTexelContextQOI>(ctx);
28✔
1452
  default:
×
1453
    throw std::logic_error("Image format not supported");
×
1454
  }
1455
}
1456

1457
void NewTexelContextImpl::ProcessContextData() {
×
1458
  auto layout = ComputeTraditionalDataLayout();
×
1459

1460
  for (uint32 a = 0; a < ctx.arraySize; a++) {
×
1461
    const char *entryBegin =
×
1462
        static_cast<const char *>(ctx.data) + a * layout.groupSize;
1463

1464
    if (ctx.numFaces > 0) {
×
1465
      for (int8 f = 0; f < ctx.numFaces; f++) {
×
1466
        const char *faceBegin = entryBegin + f * layout.mipGroupSize;
×
1467

1468
        for (uint32 m = 0; m < ctx.numMipmaps; m++) {
×
1469
          SendRasterData(faceBegin + layout.mipOffsets[m],
×
1470
                         {
1471
                             .mipMap = uint8(m),
1472
                             .face = static_cast<CubemapFace>(f + 1),
×
1473
                             .layer = uint16(a),
1474
                         });
1475
        }
1476
      }
1477
    } else {
1478
      for (uint32 m = 0; m < ctx.numMipmaps; m++) {
×
1479
        SendRasterData(entryBegin + layout.mipOffsets[m],
×
1480
                       {
1481
                           .mipMap = uint8(m),
1482
                           .layer = uint16(a),
1483
                       });
1484
      }
1485
    }
1486
  }
1487
}
1488

1489
std::unique_ptr<NewTexelContextImpl>
1490
CreateTexelContext(NewTexelContextCreate ctx, AppContext *actx) {
56✔
1491
  auto retVal = CreateTexelContext(ctx);
56✔
1492
  retVal->actx = actx;
56✔
1493
  if (ctx.data) {
56✔
1494
    retVal->ProcessContextData();
×
1495
    return {};
1496
  }
1497

1498
  return retVal;
1499
}
1500

1501
std::unique_ptr<NewTexelContextImpl>
1502
CreateTexelContext(NewTexelContextCreate ctx, AppExtractContext *ectx,
×
1503
                   const std::string &path) {
1504
  auto retVal = CreateTexelContext(ctx);
×
1505
  retVal->ectx = ectx;
×
1506
  retVal->pathOverride.Load(path);
×
1507
  if (ctx.data) {
×
1508
    retVal->ProcessContextData();
×
1509
    return {};
1510
  }
1511
  return retVal;
1512
}
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