Advertisement
Zoinkity

CIMG class for AKI fighters (python)

Feb 16th, 2015
388
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.76 KB | None | 0 0
  1. class Cimg():
  2.     def __init__(self, width=0, height=0, depth=0, img=b'', pal=(b'',), palpref=0):
  3.         self.width = width
  4.         self.height = height
  5.         self.depth = depth
  6.         self.img = img
  7.         self.pal = pal
  8.         self.palpref = palpref
  9.  
  10.     def __str__(self):
  11.         if not self.depth:
  12.             return "Cimg: None"
  13.         return "Cimg: {:d}x{:d}, {:d} bit ({:d} colors in {:d} palettes)".format(self.width, self.height, self.depth, len(self.pal[0])>>1, len(self.pal))
  14.  
  15.     @classmethod
  16.     def fromhex(cls, img, pal):
  17.         if isinstance(img, str):
  18.             img = bytes.fromhex(img)
  19.         if isinstance(pal, str):
  20.             pal = bytes.fromhex(pal)
  21.         return cls.read(img, pal)
  22.  
  23.     @classmethod
  24.     def read(cls, img, pal):
  25.         import struct
  26.         w, h, np, pp, pn, wb, hb = struct.unpack_from(">BBHBBBB", img, 0)
  27.         l = len(pal)//(pn+1)
  28.         # Depth = image size * 8 / (width * height)
  29.         d = (len(img)-8)<<3
  30.         d//=w
  31.         d//=h
  32.         p = tuple(pal[i:i+l] for i in range(0, len(pal), l))
  33.         return cls(w+1, h+1, d, img[8:], p, pp)
  34.  
  35.     @classmethod
  36.     def frompng(cls, data, palettes=1, prefered=0):
  37.         """This is sloppy and only supports paletted images.
  38.        See TEXfile.frompng() for a full rant.
  39.  
  40.        <palettes> subdivides the png's palette into multiple Cimg palettes.
  41.            Set above 1 if you're importing a multipalette file.
  42.            Do not set to <= 0!  The number of colors is divided by this value!
  43.        If palettes is set, <prefered> sets which palette is primary.
  44.        """
  45.         import png
  46.         from array import array
  47.         # They test for bytes via isarray(), which throws an exception in 3.x.
  48.         if isinstance(data, (bytes, bytearray)):
  49.             p = png.Reader(bytes=data)
  50.         else:
  51.             p = png.Reader(data)
  52.  
  53.         p.preamble()
  54.         pal = p.palette(alpha='force')
  55.         npal = len(pal)
  56.         l = npal//palettes
  57.         pal = tuple(pal32to16(pal[i:i+l], True) for i in range(0,npal,l))
  58.  
  59.         # TODO: create a translation table for image
  60.         # mapping later instances of identical colors to previous ones.  Byteorder doesn't matter.
  61.  
  62.         w, h, i, m = p.read()
  63.         d = m.get('bitdepth',8)
  64.         if npal:
  65.             t = 8 if npal>16 else 4
  66.         else:
  67.             t = d
  68.         # Pack this up into a bytearray with 4bit or 8bit entries.
  69.         if d>8 or not npal:
  70.             raise TypeError("Only paletted images are currently supported.")
  71.         img = bytearray()
  72.         if t==8:
  73.             for j in i:
  74.                 img.extend(j[:w])
  75.         elif t==4:
  76.             from itertools import zip_longest
  77.             for j in i:
  78.                 v = [k<<4 | l for k,l in zip_longest(j[::2], j[1::2], fillvalue=0)]
  79.                 img.extend(v[:w>>1])
  80.  
  81.         return cls(w, h, t, img, pal, min(len(pal)-1, prefered))
  82.  
  83.     def write(self, imgfile=None, palfile=None):
  84.         """Writes a Cimg image and palette binary file.
  85.        Returns image and palette as a tuple of bytes object.
  86.  
  87.        If given, imgfile and palfile may be filenames or an object
  88.            with a write method that supports the buffer interface.
  89.        You do not have to define both imgfile and palfile.
  90.        """
  91.         import struct
  92.  
  93.         w = self.width - 1
  94.         h = self.height- 1
  95.         p = b''
  96.         for i in pal:
  97.             p+=i
  98.         d = struct.pack(">BBHHBB", w, h, len(p)>>1, len(self.pal)-1, w.bit_length(), h.bit_length()) + self.img
  99.  
  100.         # If a real file, close it.  Otherwise, return the contents.
  101.         if isinstance(imgfile, str):
  102.             with open(imgfile, 'wb') as f:
  103.                 f.write(d)
  104.         if hasattr(imgfile, 'write'):
  105.             imgfile.write(d)
  106.         if isinstance(palfile, str):
  107.             with open(palfile, 'wb') as f:
  108.                 f.write(p)
  109.         if hasattr(palfile, 'write'):
  110.             palfile.write(p)
  111.         return (d, p)
  112.  
  113.     def topng(self, compress=9, file=None, palette=None):
  114.         """Outputs Cimg as a png to either a provided file or as a bytes object.
  115.        The png format doesn't support multiple palettes (that I'm aware of)
  116.            so for multipalette images you can specify <palette> to extract a particular one.
  117.            Setting to -1 will output all palettes up to 256 colors.
  118.            Setting to None outputs the prefered palette color.
  119.        """
  120.         import png
  121.  
  122.         if not self.pal or not self.img:
  123.             raise ValueError("Cimgs require both an image and a palette to output as png.")
  124.  
  125.         # Create a file to stuff this into.
  126.         do = None
  127.         if isinstance(file, str):
  128.             f = open(file, 'wb')
  129.             do = 'close'
  130.         elif hasattr(file, 'write'):
  131.             f = file
  132.         else:
  133.             import io
  134.             f = io.BytesIO()
  135.             do='get'
  136.  
  137.         # Pack the palette.  Needs alpha on the other end, the schmucks.
  138.         if palette is None:
  139.             p = self.pal[self.palpref]
  140.         elif palette>=0:
  141.             p = self.pal[palette]
  142.         else:
  143.             p = bytearray()
  144.             for i in self.pal:
  145.                 p.extend(i)
  146.             pass
  147.         p = pal16to32(p[:512], True)
  148.         p = zip(*[iter(p)]*4)
  149.         # Write to a file.  PyPng expects rows, not an array, when providing packed data.
  150.         i = png.Writer(width=self.width, height=self.height, bitdepth=self.depth, palette=p, compression=compress)
  151.         l = len(self.img)//self.height
  152.         i.write_packed(f, [self.img[i:i+l] for i in range(0,len(self.img),l)])
  153.  
  154.         # If a real file, close it.  Otherwise, return the contents.
  155.         if do=='close':
  156.             f.close()
  157.         elif do=='get':
  158.             return f.getvalue()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement