Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class Cimg():
- def __init__(self, width=0, height=0, depth=0, img=b'', pal=(b'',), palpref=0):
- self.width = width
- self.height = height
- self.depth = depth
- self.img = img
- self.pal = pal
- self.palpref = palpref
- def __str__(self):
- if not self.depth:
- return "Cimg: None"
- 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))
- @classmethod
- def fromhex(cls, img, pal):
- if isinstance(img, str):
- img = bytes.fromhex(img)
- if isinstance(pal, str):
- pal = bytes.fromhex(pal)
- return cls.read(img, pal)
- @classmethod
- def read(cls, img, pal):
- import struct
- w, h, np, pp, pn, wb, hb = struct.unpack_from(">BBHBBBB", img, 0)
- l = len(pal)//(pn+1)
- # Depth = image size * 8 / (width * height)
- d = (len(img)-8)<<3
- d//=w
- d//=h
- p = tuple(pal[i:i+l] for i in range(0, len(pal), l))
- return cls(w+1, h+1, d, img[8:], p, pp)
- @classmethod
- def frompng(cls, data, palettes=1, prefered=0):
- """This is sloppy and only supports paletted images.
- See TEXfile.frompng() for a full rant.
- <palettes> subdivides the png's palette into multiple Cimg palettes.
- Set above 1 if you're importing a multipalette file.
- Do not set to <= 0! The number of colors is divided by this value!
- If palettes is set, <prefered> sets which palette is primary.
- """
- import png
- from array import array
- # They test for bytes via isarray(), which throws an exception in 3.x.
- if isinstance(data, (bytes, bytearray)):
- p = png.Reader(bytes=data)
- else:
- p = png.Reader(data)
- p.preamble()
- pal = p.palette(alpha='force')
- npal = len(pal)
- l = npal//palettes
- pal = tuple(pal32to16(pal[i:i+l], True) for i in range(0,npal,l))
- # TODO: create a translation table for image
- # mapping later instances of identical colors to previous ones. Byteorder doesn't matter.
- w, h, i, m = p.read()
- d = m.get('bitdepth',8)
- if npal:
- t = 8 if npal>16 else 4
- else:
- t = d
- # Pack this up into a bytearray with 4bit or 8bit entries.
- if d>8 or not npal:
- raise TypeError("Only paletted images are currently supported.")
- img = bytearray()
- if t==8:
- for j in i:
- img.extend(j[:w])
- elif t==4:
- from itertools import zip_longest
- for j in i:
- v = [k<<4 | l for k,l in zip_longest(j[::2], j[1::2], fillvalue=0)]
- img.extend(v[:w>>1])
- return cls(w, h, t, img, pal, min(len(pal)-1, prefered))
- def write(self, imgfile=None, palfile=None):
- """Writes a Cimg image and palette binary file.
- Returns image and palette as a tuple of bytes object.
- If given, imgfile and palfile may be filenames or an object
- with a write method that supports the buffer interface.
- You do not have to define both imgfile and palfile.
- """
- import struct
- w = self.width - 1
- h = self.height- 1
- p = b''
- for i in pal:
- p+=i
- d = struct.pack(">BBHHBB", w, h, len(p)>>1, len(self.pal)-1, w.bit_length(), h.bit_length()) + self.img
- # If a real file, close it. Otherwise, return the contents.
- if isinstance(imgfile, str):
- with open(imgfile, 'wb') as f:
- f.write(d)
- if hasattr(imgfile, 'write'):
- imgfile.write(d)
- if isinstance(palfile, str):
- with open(palfile, 'wb') as f:
- f.write(p)
- if hasattr(palfile, 'write'):
- palfile.write(p)
- return (d, p)
- def topng(self, compress=9, file=None, palette=None):
- """Outputs Cimg as a png to either a provided file or as a bytes object.
- The png format doesn't support multiple palettes (that I'm aware of)
- so for multipalette images you can specify <palette> to extract a particular one.
- Setting to -1 will output all palettes up to 256 colors.
- Setting to None outputs the prefered palette color.
- """
- import png
- if not self.pal or not self.img:
- raise ValueError("Cimgs require both an image and a palette to output as png.")
- # Create a file to stuff this into.
- do = None
- if isinstance(file, str):
- f = open(file, 'wb')
- do = 'close'
- elif hasattr(file, 'write'):
- f = file
- else:
- import io
- f = io.BytesIO()
- do='get'
- # Pack the palette. Needs alpha on the other end, the schmucks.
- if palette is None:
- p = self.pal[self.palpref]
- elif palette>=0:
- p = self.pal[palette]
- else:
- p = bytearray()
- for i in self.pal:
- p.extend(i)
- pass
- p = pal16to32(p[:512], True)
- p = zip(*[iter(p)]*4)
- # Write to a file. PyPng expects rows, not an array, when providing packed data.
- i = png.Writer(width=self.width, height=self.height, bitdepth=self.depth, palette=p, compression=compress)
- l = len(self.img)//self.height
- i.write_packed(f, [self.img[i:i+l] for i in range(0,len(self.img),l)])
- # If a real file, close it. Otherwise, return the contents.
- if do=='close':
- f.close()
- elif do=='get':
- return f.getvalue()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement