/ src / Ryujinx.Graphics.Texture / ETC2Decoder.cs
ETC2Decoder.cs
  1  using Ryujinx.Common;
  2  using Ryujinx.Common.Memory;
  3  using System;
  4  using System.Buffers.Binary;
  5  using System.Runtime.InteropServices;
  6  
  7  namespace Ryujinx.Graphics.Texture
  8  {
  9      public static class ETC2Decoder
 10      {
 11          private const uint AlphaMask = 0xff000000u;
 12  
 13          private const int BlockWidth = 4;
 14          private const int BlockHeight = 4;
 15  
 16          private static readonly int[][] _etc1Lut =
 17          {
 18              new int[] { 2, 8, -2, -8 },
 19              new int[] { 5, 17, -5, -17 },
 20              new int[] { 9, 29, -9, -29 },
 21              new int[] { 13, 42, -13, -42 },
 22              new int[] { 18, 60, -18, -60 },
 23              new int[] { 24, 80, -24, -80 },
 24              new int[] { 33, 106, -33, -106 },
 25              new int[] { 47, 183, -47, -183 },
 26          };
 27  
 28          private static readonly int[] _etc2Lut =
 29          {
 30              3, 6, 11, 16, 23, 32, 41, 64,
 31          };
 32  
 33          private static readonly int[][] _etc2AlphaLut =
 34          {
 35              new int[] { -3, -6, -9, -15, 2, 5, 8, 14 },
 36              new int[] { -3, -7, -10, -13, 2, 6, 9, 12 },
 37              new int[] { -2, -5, -8, -13, 1, 4, 7, 12 },
 38              new int[] { -2, -4, -6, -13, 1, 3, 5, 12 },
 39              new int[] { -3, -6, -8, -12, 2, 5, 7, 11 },
 40              new int[] { -3, -7, -9, -11, 2, 6, 8, 10 },
 41              new int[] { -4, -7, -8, -11, 3, 6, 7, 10 },
 42              new int[] { -3, -5, -8, -11, 2, 4, 7, 10 },
 43              new int[] { -2, -6, -8, -10, 1, 5, 7, 9 },
 44              new int[] { -2, -5, -8, -10, 1, 4, 7, 9 },
 45              new int[] { -2, -4, -8, -10, 1, 3, 7, 9 },
 46              new int[] { -2, -5, -7, -10, 1, 4, 6, 9 },
 47              new int[] { -3, -4, -7, -10, 2, 3, 6, 9 },
 48              new int[] { -1, -2, -3, -10, 0, 1, 2, 9 },
 49              new int[] { -4, -6, -8, -9, 3, 5, 7, 8 },
 50              new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
 51          };
 52  
 53          public static MemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
 54          {
 55              ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
 56  
 57              int inputOffset = 0;
 58  
 59              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
 60  
 61              Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
 62              Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
 63  
 64              int imageBaseOOffs = 0;
 65  
 66              for (int l = 0; l < levels; l++)
 67              {
 68                  int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
 69                  int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
 70  
 71                  for (int l2 = 0; l2 < layers; l2++)
 72                  {
 73                      for (int z = 0; z < depth; z++)
 74                      {
 75                          for (int y = 0; y < hInBlocks; y++)
 76                          {
 77                              int ty = y * BlockHeight;
 78                              int bh = Math.Min(BlockHeight, height - ty);
 79  
 80                              for (int x = 0; x < wInBlocks; x++)
 81                              {
 82                                  int tx = x * BlockWidth;
 83                                  int bw = Math.Min(BlockWidth, width - tx);
 84  
 85                                  ulong colorBlock = dataUlong[inputOffset++];
 86  
 87                                  DecodeBlock(tile, colorBlock);
 88  
 89                                  for (int py = 0; py < bh; py++)
 90                                  {
 91                                      int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
 92  
 93                                      for (int px = 0; px < bw; px++)
 94                                      {
 95                                          int oOffs = oOffsBase + px;
 96  
 97                                          outputUint[oOffs] = tile[py * BlockWidth + px] | AlphaMask;
 98                                      }
 99                                  }
100                              }
101                          }
102  
103                          imageBaseOOffs += width * height;
104                      }
105                  }
106  
107                  width = Math.Max(1, width >> 1);
108                  height = Math.Max(1, height >> 1);
109                  depth = Math.Max(1, depth >> 1);
110              }
111  
112              return output;
113          }
114  
115          public static MemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
116          {
117              ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
118  
119              int inputOffset = 0;
120  
121              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
122  
123              Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
124              Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
125  
126              int imageBaseOOffs = 0;
127  
128              for (int l = 0; l < levels; l++)
129              {
130                  int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
131                  int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
132  
133                  for (int l2 = 0; l2 < layers; l2++)
134                  {
135                      for (int z = 0; z < depth; z++)
136                      {
137                          for (int y = 0; y < hInBlocks; y++)
138                          {
139                              int ty = y * BlockHeight;
140                              int bh = Math.Min(BlockHeight, height - ty);
141  
142                              for (int x = 0; x < wInBlocks; x++)
143                              {
144                                  int tx = x * BlockWidth;
145                                  int bw = Math.Min(BlockWidth, width - tx);
146  
147                                  ulong colorBlock = dataUlong[inputOffset++];
148  
149                                  DecodeBlockPta(tile, colorBlock);
150  
151                                  for (int py = 0; py < bh; py++)
152                                  {
153                                      int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
154  
155                                      tile.Slice(py * BlockWidth, bw).CopyTo(outputUint.Slice(oOffsBase, bw));
156                                  }
157                              }
158                          }
159  
160                          imageBaseOOffs += width * height;
161                      }
162                  }
163  
164                  width = Math.Max(1, width >> 1);
165                  height = Math.Max(1, height >> 1);
166                  depth = Math.Max(1, depth >> 1);
167              }
168  
169              return output;
170          }
171  
172          public static MemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
173          {
174              ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
175  
176              int inputOffset = 0;
177  
178              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
179  
180              Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
181              Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
182  
183              int imageBaseOOffs = 0;
184  
185              for (int l = 0; l < levels; l++)
186              {
187                  int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
188                  int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
189  
190                  for (int l2 = 0; l2 < layers; l2++)
191                  {
192                      for (int z = 0; z < depth; z++)
193                      {
194                          for (int y = 0; y < hInBlocks; y++)
195                          {
196                              int ty = y * BlockHeight;
197                              int bh = Math.Min(BlockHeight, height - ty);
198  
199                              for (int x = 0; x < wInBlocks; x++)
200                              {
201                                  int tx = x * BlockWidth;
202                                  int bw = Math.Min(BlockWidth, width - tx);
203  
204                                  ulong alphaBlock = dataUlong[inputOffset];
205                                  ulong colorBlock = dataUlong[inputOffset + 1];
206  
207                                  inputOffset += 2;
208  
209                                  DecodeBlock(tile, colorBlock);
210  
211                                  byte alphaBase = (byte)alphaBlock;
212                                  int[] alphaTable = _etc2AlphaLut[(alphaBlock >> 8) & 0xf];
213                                  int alphaMultiplier = (int)(alphaBlock >> 12) & 0xf;
214                                  ulong alphaIndices = BinaryPrimitives.ReverseEndianness(alphaBlock);
215  
216                                  if (alphaMultiplier != 0)
217                                  {
218                                      for (int py = 0; py < bh; py++)
219                                      {
220                                          int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
221  
222                                          for (int px = 0; px < bw; px++)
223                                          {
224                                              int oOffs = oOffsBase + px;
225                                              int alphaIndex = (int)((alphaIndices >> (((px * BlockHeight + py) ^ 0xf) * 3)) & 7);
226  
227                                              byte a = Saturate(alphaBase + alphaTable[alphaIndex] * alphaMultiplier);
228  
229                                              outputUint[oOffs] = tile[py * BlockWidth + px] | ((uint)a << 24);
230                                          }
231                                      }
232                                  }
233                                  else
234                                  {
235                                      uint a = (uint)alphaBase << 24;
236  
237                                      for (int py = 0; py < bh; py++)
238                                      {
239                                          int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
240  
241                                          for (int px = 0; px < bw; px++)
242                                          {
243                                              int oOffs = oOffsBase + px;
244  
245                                              outputUint[oOffs] = tile[py * BlockWidth + px] | a;
246                                          }
247                                      }
248                                  }
249                              }
250                          }
251  
252                          imageBaseOOffs += width * height;
253                      }
254                  }
255  
256                  width = Math.Max(1, width >> 1);
257                  height = Math.Max(1, height >> 1);
258                  depth = Math.Max(1, depth >> 1);
259              }
260  
261              return output;
262          }
263  
264          private static void DecodeBlock(Span<uint> tile, ulong block)
265          {
266              uint blockLow = (uint)(block >> 0);
267              uint blockHigh = (uint)(block >> 32);
268  
269              uint r1, g1, b1;
270              uint r2, g2, b2;
271  
272              bool differentialMode = (blockLow & 0x2000000) != 0;
273  
274              if (differentialMode)
275              {
276                  (r1, g1, b1, r2, g2, b2) = UnpackRgb555DiffEndPoints(blockLow);
277  
278                  if (r2 > 31)
279                  {
280                      DecodeBlock59T(tile, blockLow, blockHigh);
281                  }
282                  else if (g2 > 31)
283                  {
284                      DecodeBlock58H(tile, blockLow, blockHigh);
285                  }
286                  else if (b2 > 31)
287                  {
288                      DecodeBlock57P(tile, block);
289                  }
290                  else
291                  {
292                      r1 |= r1 >> 5;
293                      g1 |= g1 >> 5;
294                      b1 |= b1 >> 5;
295  
296                      r2 = (r2 << 3) | (r2 >> 2);
297                      g2 = (g2 << 3) | (g2 >> 2);
298                      b2 = (b2 << 3) | (b2 >> 2);
299  
300                      DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
301                  }
302              }
303              else
304              {
305                  r1 = (blockLow & 0x0000f0) >> 0;
306                  g1 = (blockLow & 0x00f000) >> 8;
307                  b1 = (blockLow & 0xf00000) >> 16;
308  
309                  r2 = (blockLow & 0x00000f) << 4;
310                  g2 = (blockLow & 0x000f00) >> 4;
311                  b2 = (blockLow & 0x0f0000) >> 12;
312  
313                  r1 |= r1 >> 4;
314                  g1 |= g1 >> 4;
315                  b1 |= b1 >> 4;
316  
317                  r2 |= r2 >> 4;
318                  g2 |= g2 >> 4;
319                  b2 |= b2 >> 4;
320  
321                  DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
322              }
323          }
324  
325          private static void DecodeBlockPta(Span<uint> tile, ulong block)
326          {
327              uint blockLow = (uint)(block >> 0);
328              uint blockHigh = (uint)(block >> 32);
329  
330              (uint r1, uint g1, uint b1, uint r2, uint g2, uint b2) = UnpackRgb555DiffEndPoints(blockLow);
331  
332              bool fullyOpaque = (blockLow & 0x2000000) != 0;
333  
334              if (fullyOpaque)
335              {
336                  if (r2 > 31)
337                  {
338                      DecodeBlock59T(tile, blockLow, blockHigh);
339                  }
340                  else if (g2 > 31)
341                  {
342                      DecodeBlock58H(tile, blockLow, blockHigh);
343                  }
344                  else if (b2 > 31)
345                  {
346                      DecodeBlock57P(tile, block);
347                  }
348                  else
349                  {
350                      r1 |= r1 >> 5;
351                      g1 |= g1 >> 5;
352                      b1 |= b1 >> 5;
353  
354                      r2 = (r2 << 3) | (r2 >> 2);
355                      g2 = (g2 << 3) | (g2 >> 2);
356                      b2 = (b2 << 3) | (b2 >> 2);
357  
358                      DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
359                  }
360  
361                  for (int i = 0; i < tile.Length; i++)
362                  {
363                      tile[i] |= AlphaMask;
364                  }
365              }
366              else
367              {
368                  if (r2 > 31)
369                  {
370                      DecodeBlock59T(tile, blockLow, blockHigh, AlphaMask);
371                  }
372                  else if (g2 > 31)
373                  {
374                      DecodeBlock58H(tile, blockLow, blockHigh, AlphaMask);
375                  }
376                  else if (b2 > 31)
377                  {
378                      DecodeBlock57P(tile, block);
379  
380                      for (int i = 0; i < tile.Length; i++)
381                      {
382                          tile[i] |= AlphaMask;
383                      }
384                  }
385                  else
386                  {
387                      r1 |= r1 >> 5;
388                      g1 |= g1 >> 5;
389                      b1 |= b1 >> 5;
390  
391                      r2 = (r2 << 3) | (r2 >> 2);
392                      g2 = (g2 << 3) | (g2 >> 2);
393                      b2 = (b2 << 3) | (b2 >> 2);
394  
395                      DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2, AlphaMask);
396                  }
397              }
398          }
399  
400          private static (uint, uint, uint, uint, uint, uint) UnpackRgb555DiffEndPoints(uint blockLow)
401          {
402              uint r1 = (blockLow & 0x0000f8) >> 0;
403              uint g1 = (blockLow & 0x00f800) >> 8;
404              uint b1 = (blockLow & 0xf80000) >> 16;
405  
406              uint r2 = (uint)((sbyte)(r1 >> 3) + ((sbyte)((blockLow & 0x000007) << 5) >> 5));
407              uint g2 = (uint)((sbyte)(g1 >> 3) + ((sbyte)((blockLow & 0x000700) >> 3) >> 5));
408              uint b2 = (uint)((sbyte)(b1 >> 3) + ((sbyte)((blockLow & 0x070000) >> 11) >> 5));
409  
410              return (r1, g1, b1, r2, g2, b2);
411          }
412  
413          private static void DecodeBlock59T(Span<uint> tile, uint blockLow, uint blockHigh, uint alphaMask = 0)
414          {
415              uint r1 = (blockLow & 3) | ((blockLow >> 1) & 0xc);
416              uint g1 = (blockLow >> 12) & 0xf;
417              uint b1 = (blockLow >> 8) & 0xf;
418  
419              uint r2 = (blockLow >> 20) & 0xf;
420              uint g2 = (blockLow >> 16) & 0xf;
421              uint b2 = (blockLow >> 28) & 0xf;
422  
423              r1 |= r1 << 4;
424              g1 |= g1 << 4;
425              b1 |= b1 << 4;
426  
427              r2 |= r2 << 4;
428              g2 |= g2 << 4;
429              b2 |= b2 << 4;
430  
431              int dist = _etc2Lut[((blockLow >> 24) & 1) | ((blockLow >> 25) & 6)];
432  
433              Span<uint> palette = stackalloc uint[4];
434  
435              palette[0] = Pack(r1, g1, b1);
436              palette[1] = Pack(r2, g2, b2, dist);
437              palette[2] = Pack(r2, g2, b2);
438              palette[3] = Pack(r2, g2, b2, -dist);
439  
440              blockHigh = BinaryPrimitives.ReverseEndianness(blockHigh);
441  
442              for (int y = 0; y < BlockHeight; y++)
443              {
444                  for (int x = 0; x < BlockWidth; x++)
445                  {
446                      int offset = (y * 4) + x;
447                      int index = (x * 4) + y;
448  
449                      int paletteIndex = (int)((blockHigh >> index) & 1) | (int)((blockHigh >> (index + 15)) & 2);
450  
451                      tile[offset] = palette[paletteIndex];
452  
453                      if (alphaMask != 0)
454                      {
455                          if (paletteIndex == 2)
456                          {
457                              tile[offset] = 0;
458                          }
459                          else
460                          {
461                              tile[offset] |= alphaMask;
462                          }
463                      }
464                  }
465              }
466          }
467  
468          private static void DecodeBlock58H(Span<uint> tile, uint blockLow, uint blockHigh, uint alphaMask = 0)
469          {
470              uint r1 = (blockLow >> 3) & 0xf;
471              uint g1 = ((blockLow << 1) & 0xe) | ((blockLow >> 12) & 1);
472              uint b1 = ((blockLow >> 23) & 1) | ((blockLow >> 7) & 6) | ((blockLow >> 8) & 8);
473  
474              uint r2 = (blockLow >> 19) & 0xf;
475              uint g2 = ((blockLow >> 31) & 1) | ((blockLow >> 15) & 0xe);
476              uint b2 = (blockLow >> 27) & 0xf;
477  
478              uint rgb1 = Pack4Be(r1, g1, b1);
479              uint rgb2 = Pack4Be(r2, g2, b2);
480  
481              r1 |= r1 << 4;
482              g1 |= g1 << 4;
483              b1 |= b1 << 4;
484  
485              r2 |= r2 << 4;
486              g2 |= g2 << 4;
487              b2 |= b2 << 4;
488  
489              int dist = _etc2Lut[(rgb1 >= rgb2 ? 1u : 0u) | ((blockLow >> 23) & 2) | ((blockLow >> 24) & 4)];
490  
491              Span<uint> palette = stackalloc uint[4];
492  
493              palette[0] = Pack(r1, g1, b1, dist);
494              palette[1] = Pack(r1, g1, b1, -dist);
495              palette[2] = Pack(r2, g2, b2, dist);
496              palette[3] = Pack(r2, g2, b2, -dist);
497  
498              blockHigh = BinaryPrimitives.ReverseEndianness(blockHigh);
499  
500              for (int y = 0; y < BlockHeight; y++)
501              {
502                  for (int x = 0; x < BlockWidth; x++)
503                  {
504                      int offset = (y * 4) + x;
505                      int index = (x * 4) + y;
506  
507                      int paletteIndex = (int)((blockHigh >> index) & 1) | (int)((blockHigh >> (index + 15)) & 2);
508  
509                      tile[offset] = palette[paletteIndex];
510  
511                      if (alphaMask != 0)
512                      {
513                          if (paletteIndex == 2)
514                          {
515                              tile[offset] = 0;
516                          }
517                          else
518                          {
519                              tile[offset] |= alphaMask;
520                          }
521                      }
522                  }
523              }
524          }
525  
526          private static void DecodeBlock57P(Span<uint> tile, ulong block)
527          {
528              int r0 = (int)((block >> 1) & 0x3f);
529              int g0 = (int)(((block >> 9) & 0x3f) | ((block & 1) << 6));
530              int b0 = (int)(((block >> 31) & 1) | ((block >> 15) & 6) | ((block >> 16) & 0x18) | ((block >> 3) & 0x20));
531  
532              int rh = (int)(((block >> 24) & 1) | ((block >> 25) & 0x3e));
533              int gh = (int)((block >> 33) & 0x7f);
534              int bh = (int)(((block >> 43) & 0x1f) | ((block >> 27) & 0x20));
535  
536              int rv = (int)(((block >> 53) & 7) | ((block >> 37) & 0x38));
537              int gv = (int)(((block >> 62) & 3) | ((block >> 46) & 0x7c));
538              int bv = (int)((block >> 56) & 0x3f);
539  
540              r0 = (r0 << 2) | (r0 >> 4);
541              g0 = (g0 << 1) | (g0 >> 6);
542              b0 = (b0 << 2) | (b0 >> 4);
543  
544              rh = (rh << 2) | (rh >> 4);
545              gh = (gh << 1) | (gh >> 6);
546              bh = (bh << 2) | (bh >> 4);
547  
548              rv = (rv << 2) | (rv >> 4);
549              gv = (gv << 1) | (gv >> 6);
550              bv = (bv << 2) | (bv >> 4);
551  
552              for (int y = 0; y < BlockHeight; y++)
553              {
554                  for (int x = 0; x < BlockWidth; x++)
555                  {
556                      int offset = y * BlockWidth + x;
557  
558                      byte r = Saturate(((x * (rh - r0)) + (y * (rv - r0)) + (r0 * 4) + 2) >> 2);
559                      byte g = Saturate(((x * (gh - g0)) + (y * (gv - g0)) + (g0 * 4) + 2) >> 2);
560                      byte b = Saturate(((x * (bh - b0)) + (y * (bv - b0)) + (b0 * 4) + 2) >> 2);
561  
562                      tile[offset] = Pack(r, g, b);
563                  }
564              }
565          }
566  
567          private static void DecodeBlockETC1(
568              Span<uint> tile,
569              uint blockLow,
570              uint blockHigh,
571              uint r1,
572              uint g1,
573              uint b1,
574              uint r2,
575              uint g2,
576              uint b2,
577              uint alphaMask = 0)
578          {
579              int[] table1 = _etc1Lut[(blockLow >> 29) & 7];
580              int[] table2 = _etc1Lut[(blockLow >> 26) & 7];
581  
582              bool flip = (blockLow & 0x1000000) != 0;
583  
584              if (!flip)
585              {
586                  for (int y = 0; y < BlockHeight; y++)
587                  {
588                      for (int x = 0; x < BlockWidth / 2; x++)
589                      {
590                          uint color1 = CalculatePixel(r1, g1, b1, x + 0, y, blockHigh, table1, alphaMask);
591                          uint color2 = CalculatePixel(r2, g2, b2, x + 2, y, blockHigh, table2, alphaMask);
592  
593                          int offset1 = y * BlockWidth + x;
594                          int offset2 = y * BlockWidth + x + 2;
595  
596                          tile[offset1] = color1;
597                          tile[offset2] = color2;
598                      }
599                  }
600              }
601              else
602              {
603                  for (int y = 0; y < BlockHeight / 2; y++)
604                  {
605                      for (int x = 0; x < BlockWidth; x++)
606                      {
607                          uint color1 = CalculatePixel(r1, g1, b1, x, y + 0, blockHigh, table1, alphaMask);
608                          uint color2 = CalculatePixel(r2, g2, b2, x, y + 2, blockHigh, table2, alphaMask);
609  
610                          int offset1 = (y * BlockWidth) + x;
611                          int offset2 = ((y + 2) * BlockWidth) + x;
612  
613                          tile[offset1] = color1;
614                          tile[offset2] = color2;
615                      }
616                  }
617              }
618          }
619  
620          private static uint CalculatePixel(uint r, uint g, uint b, int x, int y, uint block, int[] table, uint alphaMask)
621          {
622              int index = x * BlockHeight + y;
623              uint msb = block << 1;
624              uint tableIndex = index < 8
625                  ? ((block >> (index + 24)) & 1) + ((msb >> (index + 8)) & 2)
626                  : ((block >> (index + 8)) & 1) + ((msb >> (index - 8)) & 2);
627  
628              if (alphaMask != 0)
629              {
630                  if (tableIndex == 0)
631                  {
632                      return Pack(r, g, b) | alphaMask;
633                  }
634                  else if (tableIndex == 2)
635                  {
636                      return 0;
637                  }
638                  else
639                  {
640                      return Pack(r, g, b, table[tableIndex]) | alphaMask;
641                  }
642              }
643  
644              return Pack(r, g, b, table[tableIndex]);
645          }
646  
647          private static uint Pack(uint r, uint g, uint b, int offset)
648          {
649              r = Saturate((int)(r + offset));
650              g = Saturate((int)(g + offset));
651              b = Saturate((int)(b + offset));
652  
653              return Pack(r, g, b);
654          }
655  
656          private static uint Pack(uint r, uint g, uint b)
657          {
658              return r | (g << 8) | (b << 16);
659          }
660  
661          private static uint Pack4Be(uint r, uint g, uint b)
662          {
663              return (r << 8) | (g << 4) | b;
664          }
665  
666          private static byte Saturate(int value)
667          {
668              return value > byte.MaxValue ? byte.MaxValue : value < byte.MinValue ? byte.MinValue : (byte)value;
669          }
670  
671          private static int CalculateOutputSize(int width, int height, int depth, int levels, int layers)
672          {
673              int size = 0;
674  
675              for (int l = 0; l < levels; l++)
676              {
677                  size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
678              }
679  
680              return size;
681          }
682      }
683  }