Beispiel #1
0
 def _galx_frames(self, info):
     frames = []
     offsets = []
     self.fp.seek(info["offset"])
     for frame in info["frames"]:
         if info["bpp"] not in _GAL_MODE:
             raise GalImageError("Unsupported GAL pixel format")
         mode, rawmode = _GAL_MODE[frame["bpp"]]
         layermode = mode
         if len(frame["layers"]) > 1:
             print("Warning multi-layer GAL/X image")
         for layer in frame["layers"]:
             offsets.append(self.fp.tell())
             layer_size = si32le(self.fp.read(4))
             self.fp.seek(layer_size, 1)
             if layer["alpha_on"]:
                 alpha_size = si32le(self.fp.read(4))
                 self.fp.seek(alpha_size, 1)
                 if mode == "RGB":
                     mode = "RGBA"
                 elif mode == "P":
                     mode = "PA"
                 else:
                     raise GalImageError("unsupported GAL alpha mode")
                 break
             break
         frames.append((frame["name"], len(frame["layers"]), mode,
                        layermode, rawmode, frame["box"], frame["palette"]))
     return frames, offsets
Beispiel #2
0
 def _decode_alpha(self, frame, info):
     alpha_size = si32le(self.fd.read(4))
     packed_data = zlib.decompress(self.fd.read(alpha_size))
     unpacked = self._unpack_layer(
         BytesIO(packed_data),
         frame,
         info["block_width"],
         info["block_height"],
         info["randomized"],
         info["frames"],
         True,
     )
     size = self.state.xsize, self.state.ysize
     mask = Image.frombytes("L", size, unpacked, "raw", "L",
                            frame["alpha_stride"])
     if Image.getmodebase(self.mode) == "RGB":
         band = 3
     else:
         band = 1
     self.im.putband(mask.im, band)
Beispiel #3
0
 def decode(self, buffer):
     info, layermode, rawmode, frame_index = self.args
     compression = _GAL_COMPRESSION.get(info["compression"])
     frame = info["frames"][frame_index]
     for layer in frame["layers"]:
         layer_size = si32le(self.fd.read(4))
         if compression == "zip":
             packed_data = zlib.decompress(self.fd.read(layer_size))
             layer["data"] = self._unpack_layer(
                 BytesIO(packed_data),
                 frame,
                 info["block_width"],
                 info["block_height"],
                 info["randomized"],
                 info["frames"],
             )
             self._set_as_raw(layer["data"], layermode, rawmode,
                              frame["stride"])
             if layer["alpha_on"]:
                 self._decode_alpha(frame, info)
         elif compression == "jpeg":
             jpeg_data = self.fd.read(layer_size)
             self._set_as_jpeg(jpeg_data, layermode)
             if layer["alpha_on"]:
                 self._decode_alpha(frame, info)
         else:
             packed_data = self.fd.read(layer_size)
             layer["data"] = self._unpack_layer(
                 BytesIO(packed_data),
                 frame,
                 info["block_width"],
                 info["block_height"],
                 info["randomized"],
                 info["frames"],
             )
             self._set_as_raw(layer["data"], layermode, rawmode,
                              frame["stride"])
         # TODO: handle multi-layer frames
         break
     return 0, 0
Beispiel #4
0
 def _gal_info(self, header):
     """LiveMaker GAL image."""
     read, seek = self.fp.read, self.fp.seek
     header += read(2)
     info = {}
     info["version"] = header[4:]
     try:
         version = int(info["version"])
     except ValueError:
         raise GalImageError("Unsupported GAL version {}".format(header))
     if version > 102:
         header_size = si32le(read(4))
         header = read(header_size)
         info["width"] = i32le(header, 4)
         info["height"] = i32le(header, 8)
         info["bpp"] = si32le(header, 0xC)
         info["frame_count"] = si32le(header, 0x10)
         if info["frame_count"] > 1:
             print(
                 "Warning: multi-frame GAL images are not fully supported")
         info["randomized"] = header[0x15]
         info["compression"] = header[0x16]
         info["bg_color"] = i32le(header, 0x18)
         info["block_width"] = si32le(header, 0x1C)
         info["block_height"] = si32le(header, 0x20)
         info["offset"] = header_size + 11
     elif version >= 100:
         # fixed header size
         header = read(0x10)
         name_length = i32le(header)
         seek(name_length + 17, 1)
         info["width"] = i32le(header, 4)
         info["height"] = i32le(header, 8)
         info["bpp"] = si32le(header, 0xC)
         info["offset"] = name_length + 45
         info["block_width"] = 0
         info["block_height"] = 0
         info["randomized"] = 0
         info["compression"] = None
         info["frame_count"] = 1
     else:
         raise GalImageError("Unsupported GAL version {}".format(header))
     return info
Beispiel #5
0
    def _unpack_layer(self,
                      packed,
                      frame_info,
                      block_width,
                      block_height,
                      randomized,
                      frames,
                      is_alpha=False):
        # Based on GARbro ImageGAL.cs:ReadBlocks() implementation
        if block_width <= 0 or block_height <= 0:
            return packed.read()
        width = frame_info["width"]
        height = frame_info["height"]
        if is_alpha:
            bpp = 8
            stride = frame_info["alpha_stride"]
        else:
            bpp = frame_info["bpp"]
            stride = frame_info["stride"]
        blocks_w = (width + block_width - 1) // block_width
        blocks_h = (height + block_height - 1) // block_height
        blocks_count = blocks_w * blocks_h
        block_refs = []
        for i in range(blocks_count):
            frame_ref = si32le(packed.read(4))
            layer_ref = si32le(packed.read(4))
            block_refs.append((frame_ref, layer_ref))
        if randomized:
            raise GalImageError(
                "LivemakerPro encrypted images are unsupported")
        i = 0
        data = bytearray(stride * height)
        for y in range(0, height, block_height):
            # account for block size alignment padding
            run_height = min(block_height, height - y)
            for x in range(0, width, block_width):
                frame_ref, layer_ref = block_refs[i]

                run_width = min(block_width, width - x)
                dst = y * stride + (x * bpp + 7) // 8
                chunk_size = (run_width * bpp + 7) // 8
                if frame_ref == -1:
                    # read block as raw data
                    for j in range(run_height):
                        chunk = packed.read(chunk_size)
                        for k in range(chunk_size):
                            data[dst + k] = chunk[k]
                        dst += stride
                elif frame_ref == -2:
                    # copy block from this layer
                    src_x = block_width * (layer_ref % blocks_w)
                    src_y = block_height * (layer_ref // blocks_w)
                    src = src_y * stride + (src_x * bpp + 7) // 8
                    for j in range(run_height):
                        for k in range(chunk_size):
                            data[dst + k] = data[src + k]
                        src += stride
                        dst += stride
                else:
                    # copy block from another frame/layer
                    if frame_ref >= len(frames) or layer_ref >= len(
                            frames[frame_ref]["layers"]):
                        raise GalImageError("Invalid GaleFrame reference")
                    if is_alpha:
                        ref_data = frames[frame_ref]["layers"][layer_ref][
                            "alpha_data"]
                    else:
                        ref_data = frames[frame_ref]["layers"][layer_ref][
                            "data"]
                    for j in range(run_height):
                        for k in range(chunk_size):
                            data[dst + k] = ref_data[dst + k]
                i += 1
        return bytes(data)
Beispiel #6
0
 def _gal_frames(self, info):
     read = self.fp.read
     seek = self.fp.seek
     frames = []
     offsets = []
     seek(info["offset"])
     info["frames"] = []
     for i in range(info["frame_count"]):
         frame_info = {}
         name_len = i32le(read(4))
         frame_info["name"] = read(name_len).decode("cp932")
         mask = i32le(read(4))
         seek(9, 1)
         layer_count = i32le(read(4))
         if layer_count < 1:
             raise GalImageError("Invalid GAL frame")
         frame_info["width"] = si32le(read(4))
         frame_info["height"] = si32le(read(4))
         bpp = i32le(read(4))
         if bpp not in _GAL_MODE or bpp > 32:
             print(layer_count)
             print(frame_info, mask)
             print(bpp)
             raise GalImageError("Unsupported GAL pixel format")
         frame_info["bpp"] = bpp
         if bpp <= 8:
             palette_size = 1 << bpp
             frame_info["palette"] = ImagePalette.raw(
                 "BGRX", read(palette_size * 4))
         else:
             frame_info["palette"] = None
         mode, rawmode = _GAL_MODE[bpp]
         layermode = mode
         stride = (frame_info["width"] * bpp + 7) // 8
         if bpp >= 8:
             # align to 4 byte boundary
             stride = (stride + 3) & ~3
         frame_info["stride"] = stride
         frame_info["alpha_stride"] = (frame_info["width"] + 3) & ~3
         frame_info["layers"] = []
         for j in range(layer_count):
             layer_info = {}
             left = si32le(read(4))
             top = si32le(read(4))
             layer_info["origin"] = (left, top)
             layer_info["visible"] = read(1)[0]
             layer_info["trans_color"] = si32le(read(4))
             layer_info["alpha"] = si32le(read(4))
             layer_info["alpha_on"] = read(1)[0]
             name_len = i32le(read(4))
             seek(name_len, 1)
             if int(info["version"]) >= 107:
                 layer_info["lock"] = read(1)[0]
             if j == 0:
                 offsets.append(self.fp.tell())
             else:
                 print(
                     "Warning: multilayer Gale images not fully supported")
             layer_size = si32le(read(4))
             seek(layer_size, 1)
             alpha_size = si32le(read(4))
             if layer_info["alpha_on"] and alpha_size > 0:
                 if mode == "RGB":
                     mode = "RGBA"
                 elif mode == "P":
                     mode = "PA"
                 else:
                     raise GalImageError("unsupported GAL alpha mode")
             seek(alpha_size, 1)
             frame_info["layers"].append(layer_info)
         info["frames"].append(frame_info)
         box = (0, 0, frame_info["width"], frame_info["height"])
         frames.append((frame_info["name"], layer_count, mode, layermode,
                        rawmode, box, frame_info["palette"]))
         # TODO: handle multi-frame images
         break
     return frames, offsets