Advertisement
AboodXD

CompressBC3

Oct 30th, 2017
51
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.31 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3.  
  4. def CompressAlphaBlock(AlphaBlock, Palette, Width, Height, x, y):
  5.     Indices = [0] * 16
  6.  
  7.     for y2 in range(4):
  8.         for x2 in range(4):
  9.             if (y + y2) < Height and (x + x2) < Width:
  10.                 Index = 0
  11.                 Delta = 2147483647
  12.  
  13.                 A = AlphaBlock[y2 * 4 + x2]
  14.  
  15.                 for i in range(len(Palette)):
  16.                     if A == Palette[i]:
  17.                         Index = i
  18.                         break
  19.  
  20.                     AlphaDelta = A - Palette[i]
  21.                     AlphaDelta = max(AlphaDelta, -AlphaDelta)
  22.  
  23.                     if AlphaDelta < Delta:
  24.                         Delta = AlphaDelta
  25.                         Index = i
  26.  
  27.                 Indices[y2 * 4 + x2] = Index
  28.  
  29.     return Indices
  30.  
  31. def GetAlphaPalette(A0, A1):
  32.     Palette = bytearray(8)
  33.     Palette[0] = A0
  34.     Palette[1] = A1
  35.  
  36.     if A0 > A1:
  37.         Palette[2] = (6 * A0 + 1 * A1) // 7
  38.         Palette[3] = (5 * A0 + 2 * A1) // 7
  39.         Palette[4] = (4 * A0 + 3 * A1) // 7
  40.         Palette[5] = (3 * A0 + 4 * A1) // 7
  41.         Palette[6] = (2 * A0 + 5 * A1) // 7
  42.         Palette[7] = (1 * A0 + 6 * A1) // 7
  43.  
  44.     else:
  45.         Palette[2] = (4 * A0 + 1 * A1) // 5
  46.         Palette[3] = (3 * A0 + 2 * A1) // 5
  47.         Palette[4] = (2 * A0 + 3 * A1) // 5
  48.         Palette[5] = (1 * A0 + 4 * A1) // 5
  49.         Palette[6] = 0
  50.         Palette[7] = 0xFF
  51.  
  52.     return Palette
  53.  
  54.  
  55. def CompressBlock(ColorBlock, Palette, Width, Height, x, y, ScaleR, ScaleG, ScaleB):
  56.     Indices = [0] * 16
  57.  
  58.     for y2 in range(4):
  59.         for x2 in range(4):
  60.             if y + y2 < Height and x + x2 < Width:
  61.                 Index = 0
  62.                 Delta = 2147483647
  63.  
  64.                 Color = ColorBlock[y2 * 4 + x2]
  65.                 R = (Color >> 16) & 0xFF
  66.                 G = (Color >> 8) & 0xFF
  67.                 B = Color & 0xFF
  68.                 A = (Color >> 24) & 0xFF
  69.  
  70.                 for i in range(len(Palette)):
  71.                     if Color == Palette[i]:
  72.                         Index = i
  73.                         break
  74.  
  75.                     RedDelta = R - ((Palette[i] >> 16) & 0xFF)
  76.                     GreenDelta = G - ((Palette[i] >> 8) & 0xFF)
  77.                     BlueDelta = B - (Palette[i] & 0xFF)
  78.  
  79.                     RedDelta = max(RedDelta, -RedDelta)
  80.                     GreenDelta = max(GreenDelta, -GreenDelta)
  81.                     BlueDelta = max(BlueDelta, -BlueDelta)
  82.  
  83.                     NewDelta = RedDelta * ScaleR + GreenDelta * ScaleG + BlueDelta * ScaleB
  84.  
  85.                     if NewDelta < Delta:
  86.                         Delta = NewDelta
  87.                         Index = i
  88.  
  89.                 Indices[y2 * 4 + x2] = Index
  90.  
  91.     return Indices
  92.  
  93. def FindMinMax(Colors):
  94.     MaxR = 0
  95.     MaxG = 0
  96.     MaxB = 0
  97.  
  98.     MinR = 255
  99.     MinG = 255
  100.     MinB = 255
  101.  
  102.     TransparentBlock = True
  103.  
  104.     for Color in Colors:
  105.         R = (Color >> 16) & 0xFF
  106.         G = (Color >> 8) & 0xFF
  107.         B = Color & 0xFF
  108.         A = (Color >> 24) & 0xFF
  109.  
  110.         if not A:
  111.             continue
  112.  
  113.         TransparentBlock = False
  114.  
  115.         # Max color
  116.         if R > MaxR: MaxR = R
  117.         if G > MaxG: MaxG = G
  118.         if B > MaxB: MaxB = B
  119.  
  120.         # Min color
  121.         if R < MinR: MinR = R
  122.         if G < MinG: MinG = G
  123.         if B < MinB: MinB = B
  124.  
  125.     if TransparentBlock:
  126.         MinR = 0
  127.         MinG = 0
  128.         MinB = 0
  129.  
  130.         MaxR = 0
  131.         MaxG = 0
  132.         MaxB = 0
  133.  
  134.     return MinR, MinG, MinB, MaxR, MaxG, MaxB, TransparentBlock
  135.  
  136. def ToARGB8(Red, Green, Blue, Alpha):
  137.     R = int(Red * 255)
  138.     G = int(Green * 255)
  139.     B = int(Blue * 255)
  140.     A = int(Alpha * 255)
  141.  
  142.     return (A << 24) | (R << 16) | (G << 8) | B
  143.  
  144. def GetPalette(Color0, Color1):
  145.     Palette = [0] * 4
  146.     Palette[0] = ToARGB8(((Color0 >> 11) & 0b11111) / 31, ((Color0 >> 5) & 0b111111) / 63, (Color0 & 0b11111) / 31, 0)
  147.     Palette[1] = ToARGB8(((Color1 >> 11) & 0b11111) / 31, ((Color1 >> 5) & 0b111111) / 63, (Color1 & 0b11111) / 31, 0)
  148.  
  149.     R0 = (Palette[0] >> 16) & 0xFF
  150.     G0 = (Palette[0] >> 8) & 0xFF
  151.     B0 = Palette[0] & 0xFF
  152.     R1 = (Palette[1] >> 16) & 0xFF
  153.     G1 = (Palette[1] >> 8) & 0xFF
  154.     B1 = Palette[1] & 0xFF
  155.  
  156.     Palette[2] = ((2 * R0 // 3 + 1 * R1 // 3) << 16) | ((2 * G0 // 3 + 1 * G1 // 3) << 8) | (2 * B0 // 3 + 1 * B1 // 3)
  157.     Palette[3] = ((1 * R0 // 3 + 2 * R1 // 3) << 16) | ((1 * G0 // 3 + 2 * G1 // 3) << 8) | (1 * B0 // 3 + 2 * B1 // 3)
  158.  
  159.     return Palette
  160.  
  161. def ToA8(Alpha):
  162.     return int(Alpha / 15 * 255)
  163.  
  164. def ToA4(Alpha):
  165.     return int(Alpha / 255 * 15)
  166.  
  167.  
  168. def GetTruePalette(MinR, MinG, MinB, MaxR, MaxG, MaxB, AlphaBlock):
  169.     Palette = [0] * 4
  170.  
  171.     if AlphaBlock:
  172.         Palette[0] = 0xFF000000 | int(MinR << 16) | int(MinG << 8) | MinB
  173.         Palette[1] = 0xFF000000 | int(MaxR << 16) | int(MaxG << 8) | MaxB
  174.         Palette[2] = 0xFF000000 | int((MaxR // 2 + MinR // 2) << 16) | int((MaxG // 2 + MinG // 2) << 8) | int(MaxB // 2 + MinB // 2)
  175.         Palette[3] = 0
  176.  
  177.     else:
  178.         Palette[0] = 0xFF000000 | int(MaxR << 16) | int(MaxG << 8) | MaxB
  179.         Palette[1] = 0xFF000000 | int(MinR << 16) | int(MinG << 8) | MinB
  180.         Palette[2] = 0xFF000000 | int((2 * MinR // 3 + 1 * MaxR // 3) << 16) | int((2 * MinG // 3 + 1 * MaxG // 3) << 8) | int(2 * MinB / 3 + 1 * MaxB / 3)
  181.         Palette[3] = 0xFF000000 | int((1 * MinR // 3 + 2 * MaxR // 3) << 16) | int((1 * MinG // 3 + 2 * MaxG // 3) << 8) | int(1 * MinB / 3 + 2 * MaxB / 3)
  182.  
  183.     return Palette
  184.  
  185. def ToRGB565(R, G, B):
  186.     return ((R >> 3) << 11) | ((G >> 2) << 5) | (B >> 3)
  187.  
  188.  
  189. def FindColors(Colors):
  190.     ThresholdMin = 0x02
  191.     ThresholdMax = 0xFD
  192.  
  193.     PartMin = 255
  194.     PartMax = 0
  195.     Min = 255
  196.     Max = 0
  197.     UseLessThanAlgorithm = False  # Used when colors are close to both 0 and 0xFF
  198.  
  199.     for Color in Colors:
  200.         if Color <= ThresholdMin:
  201.             UseLessThanAlgorithm = True
  202.  
  203.         elif Color >= ThresholdMax:
  204.             UseLessThanAlgorithm = True
  205.  
  206.         if not UseLessThanAlgorithm and Color < PartMin:
  207.             PartMin = Color
  208.  
  209.         if not UseLessThanAlgorithm and Color > PartMax:
  210.             PartMax = Color
  211.  
  212.         if Color < Min:
  213.             Min = Color
  214.  
  215.         if Color > Max:
  216.             Max = Color
  217.  
  218.     if Max <= 0x15 or (Min <= 0x05 and Max <= 0x30) or Min >= 0xEA or (Max >= 0xFA and Min >= 0xCF):  # What is good here?
  219.         UseLessThanAlgorithm = False
  220.  
  221.     else:
  222.         Max = PartMax
  223.         Min = PartMin
  224.  
  225.     if not UseLessThanAlgorithm and Min == Max:
  226.         Max -= 1
  227.  
  228.     if Max < 0:
  229.         Max = 256 + Max
  230.  
  231.     Color0 = Min if UseLessThanAlgorithm else Max
  232.     Color1 = Max if UseLessThanAlgorithm else Min
  233.  
  234.     return Color0, Color1
  235.  
  236.  
  237. def CompressBC3(SrcPtr, Stride, Width, Height):
  238.     Stride //= 4
  239.  
  240.     print("Stride: %d" % Stride)
  241.     print("Width: %d" % Width)
  242.     print("Height: %d" % Height)
  243.     print("Uncompressed data length: %d" % len(SrcPtr))
  244.  
  245.     DstPtr = bytearray()
  246.  
  247.     for y in range(0, Height, 4):
  248.         for x in range(0, Width, 4):
  249.             Colors = []
  250.             Alphas = []
  251.             ActualColors = [0] * 16
  252.             ActualAlphas =  bytearray(16)
  253.  
  254.             for y2 in range(4):
  255.                 for x2 in range(4):
  256.                     if y + y2 < Height and x + x2 < Width:
  257.                         # Read RGBA data and convert it to ARGB
  258.                         pos = (y + y2) * Stride + (x + x2)
  259.                         pos *= 4
  260.  
  261.                         A = SrcPtr[pos + 3]
  262.                         R = SrcPtr[pos]
  263.                         G = SrcPtr[pos + 1]
  264.                         B = SrcPtr[pos + 2]
  265.  
  266.                         Color = (A << 24) | (R << 16) | (G << 8) | B
  267.  
  268.                         Colors.append(Color)
  269.                         Alphas.append(A)
  270.                         ActualColors[y2 * 4 + x2] = Color
  271.                         ActualAlphas[y2 * 4 + x2] = A
  272.  
  273.             MinR, MinG, MinB, MaxR, MaxG, MaxB, TransparentBlock = FindMinMax(Colors)
  274.             Alpha0, Alpha1 = FindColors(Alphas)
  275.  
  276.             Color0 = ToRGB565(MaxR, MaxG, MaxB)
  277.             Color1 = ToRGB565(MinR, MinG, MinB)
  278.  
  279.             if Color0 == Color1:
  280.                 Color0 += 1
  281.  
  282.             #Palette = GetTruePalette(MinR, MinG, MinB, MaxR, MaxG, MaxB, False)
  283.             Palette = GetPalette(Color0, Color1)
  284.             AlphaPalette = GetAlphaPalette(Alpha0, Alpha1)
  285.  
  286.             Indices = 0
  287.             AlphaIndices = 0
  288.  
  289.             if not TransparentBlock:
  290.                 IndexBlock = CompressBlock(ActualColors, Palette, Width, Height, x, y, 256 - (MaxR - MinR), 256 - (MaxG - MinG), 256 - (MaxB - MinB))
  291.                 AlphaIndexBlock = CompressAlphaBlock(ActualAlphas, AlphaPalette, Width, Height, x, y)
  292.  
  293.                 for y2 in range(4):
  294.                     for x2 in range(4):
  295.                         i = y2 * 4 + x2
  296.                         Indices |= IndexBlock[i] << (i * 2)
  297.                         AlphaIndices |= AlphaIndexBlock[i] << (i * 3)
  298.  
  299.             else:
  300.                 Indices = 0xFFFFFFFF
  301.  
  302.                 TransparentIndex = 0
  303.                 for i in range(len(AlphaPalette)):
  304.                     if not AlphaPalette[i]:
  305.                         TransparentIndex = i
  306.                         break
  307.  
  308.                 if AlphaPalette[TransparentIndex]:
  309.                     raise RuntimeError
  310.  
  311.                 for y2 in range(4):
  312.                     for x2 in range(4):
  313.                         AlphaIndices |= TransparentIndex << ((y2 * 4 + x2) * 3)
  314.  
  315.             DstPtr += bytes([Alpha0])
  316.             DstPtr += bytes([Alpha1])
  317.  
  318.             DstPtr += bytes([AlphaIndices & 0xFF])
  319.             DstPtr += bytes([(AlphaIndices >> 8) & 0xFF])
  320.             DstPtr += bytes([(AlphaIndices >> 16) & 0xFF])
  321.             DstPtr += bytes([(AlphaIndices >> 24) & 0xFF])
  322.             DstPtr += bytes([(AlphaIndices >> 32) & 0xFF])
  323.             DstPtr += bytes([(AlphaIndices >> 40) & 0xFF])
  324.  
  325.             DstPtr += bytes([Color0 & 0xFF])
  326.             DstPtr += bytes([(Color0 >> 8) & 0xFF])
  327.             DstPtr += bytes([Color1 & 0xFF])
  328.             DstPtr += bytes([(Color1 >> 8) & 0xFF])
  329.  
  330.             DstPtr += bytes([Indices & 0xFF])
  331.             DstPtr += bytes([(Indices >> 8) & 0xFF])
  332.             DstPtr += bytes([(Indices >> 16) & 0xFF])
  333.             DstPtr += bytes([(Indices >> 24) & 0xFF])
  334.  
  335.     return DstPtr
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement