def resize(self, source, width, height, resize_canvas=True): if not resize_canvas: # this optimised implementation doesn't deal with this. # so delegate to affine() return super(Nearest, self).resize(source, width, height, resize_canvas=resize_canvas) pixels = array.array('B') pixelsize = source.pixelsize x_ratio = fdiv(source.width, width) # get the x-axis ratio y_ratio = fdiv(source.height, height) # get the y-axis ratio y_range = range( height) # an iterator over the indices of all lines (y-axis) x_range = range( width) # an iterator over the indices of all rows (x-axis) for y in y_range: y += 0.5 # use the center of each pixel source_y = int(y * y_ratio) # get the source line for x in x_range: x += 0.5 # use the center of each pixel source_x = int(x * x_ratio) # get the source row pixels.extend(source.pixels.get(source_x, source_y)) return get_pixel_array(pixels, width, height, pixelsize)
def _channels_data_to_image(channels_data, mode, size, depth): if size == (0, 0): return w, h = size num_channels = mode.length assert depth == 8 assert len(channels_data) == num_channels total_size = w * h * num_channels image_bytes = array.array(str("B"), [0] * total_size) for index, channel in enumerate(channels_data): # zip and zip-with-prediction data is already decoded data = channel.data if channel.compression == Compression.PACK_BITS: data = packbits.decode(data) image_bytes[index::num_channels] = array.array(str("B"), data) pixels = get_pixel_array(image_bytes, w, h, mode.length) return LoadedImage(mode, w, h, pixels)
def pixel_array_factory(colors, alpha=True): height = len(colors) width = len(colors[0]) if height else 0 pixel_size = 4 if alpha else 3 pixel_array = get_pixel_array(array.array('i', [0] * width * height * pixel_size), width, height, pixel_size) for y in range(height): for x in range(width): pixel_array.set(x, y, colors[y][x].to_pixel(pixel_size)) return pixel_array
def pixel_array_factory(colors, alpha=True): height = len(colors) width = len(colors[0]) if height else 0 pixel_size = 4 if alpha else 3 pixel_array = get_pixel_array(array.array('B', [0] * width * height * pixel_size), width, height, pixel_size) for y in range(height): for x in range(width): pixel_array.set(x, y, colors[y][x].to_pixel(pixel_size)) return pixel_array
def get_image(self): # go to the start of the pixel array self.fileobj.seek(self.offset) # since bmps are stored upside down, initialize a pixel list initial = array.array("B", [0] * self.width * self.height * self.pixelsize) pixel_array = get_pixel_array(initial, self.width, self.height, self.pixelsize) # iterate BACKWARDS over the line indices so we don't have to reverse # later. this is why we intialize pixels above. for row_num in range(self.height - 1, -1, -1): self.read_row(pixel_array, row_num) # TODO: Not necessarily RGB return Image(pixel_array, RGB, palette=self.palette)
def new(cls, mode, width, height, background_color, palette=None, meta=None): color = background_color.to_pixel(mode.length) pixel_array = get_pixel_array( array.array('B', color) * width * height, width, height, mode.length) return LoadedImage(mode, width, height, pixel_array, palette=palette, meta=meta)
def decode(fileobj): decoder = TonyJpegDecoder() jpegsrc = fileobj.read() try: bmpout = decoder.decode(jpegsrc) except: fileobj.seek(0) return None pixels = array.array('B') row_width = decoder.Width * PIXELSIZE # rows are bottom to top for reversed_row_num in range(decoder.Height - 1, -1, -1): start = reversed_row_num * (row_width + 2) end = start + row_width pixels.extend(bmpout[start:end]) #pixels.extend(bmpout[:3 * decoder.Width]) #del bmpout[:3 * decoder.Width] #del bmpout[:2] # kill padding pixel_array = get_pixel_array(pixels, decoder.Width, decoder.Height, PIXELSIZE) return Image(pixel_array, RGB)
def read_pixels(fileobj, headers): fileobj.seek(headers.palette_start) palette = [] for _ in range(headers.ncolors): blue, green, red, _ = struct.unpack('<BBBB', fileobj.read(4)) palette.append((red, green, blue)) # set palette to None instead of empty list when there's no palette palette = palette or None read_row = ROW_READERS[headers.bits_per_pixel] fileobj.seek(headers.offset) # since bmps are stored upside down, initialize a pixel list initial = array.array('B', [0] * headers.width * headers.height * headers.pixelsize) pixel_array = get_pixel_array(initial, headers.width, headers.height, headers.pixelsize) # iterate BACKWARDS over the line indices so we don't have to reverse # later. this is why we intialize pixels above. for row_num in range(headers.height - 1, -1, -1): read_row(fileobj, headers, pixel_array, row_num) return pixel_array, palette
def resize(self, source, width, height, resize_canvas=True): if not resize_canvas: # this optimised implementation doesn't deal with this. # so delegate to affine() return super(Nearest, self).resize( source, width, height, resize_canvas=resize_canvas ) pixels = array.array('B') pixelsize = source.pixelsize x_ratio = fdiv(source.width, width) # get the x-axis ratio y_ratio = fdiv(source.height, height) # get the y-axis ratio y_range = range(height) # an iterator over the indices of all lines (y-axis) x_range = range(width) # an iterator over the indices of all rows (x-axis) for y in y_range: y += 0.5 # use the center of each pixel source_y = int(y * y_ratio) # get the source line for x in x_range: x += 0.5 # use the center of each pixel source_x = int(x * x_ratio) # get the source row pixels.extend(source.pixels.get(source_x, source_y)) return get_pixel_array(pixels, width, height, pixelsize)
def _channels_data_to_image(channels_data, mode, size, depth): if size == (0, 0): return num_channels = mode.length assert depth == 8 assert len(channels_data) == num_channels total_size = size[0]*size[1]*num_channels image_bytes = array.array(str("B"), [0]*total_size) for index, channel in enumerate(channels_data): data = channel.data # zip and zip-with-prediction data is already decoded if channel.compression == Compression.PACK_BITS: data = packbits.decode(data) image_bytes[index::num_channels] = array.array(str("B"), data) pixels = get_pixel_array(image_bytes, size[0], size[1], mode.length) return Image(pixels, mode)
def resize(self, source, width, height, resize_canvas=True): if not resize_canvas: # this optimised implementation doesn't deal with this. # so delegate to affine() return super(Bilinear, self).resize( source, width, height, resize_canvas=resize_canvas ) x_ratio = fdiv(source.width, width) # get the x-axis ratio y_ratio = fdiv(source.height, height) # get the y-axis ratio pixelsize = source.pixelsize pixels = array.array('B') if source.palette: raise NotImplementedError("Resampling of paletted images is not yet supported") if x_ratio < 1 and y_ratio < 1: if not (width % source.width) and not (height % source.height): # optimisation: if doing a perfect upscale, # can just use nearest neighbor (it's much faster) return nearest.resize(source, width, height) has_alpha = source.mode.alpha color_channels_range = range(pixelsize - 1 if has_alpha else pixelsize) y_range = range(height) # an iterator over the indices of all lines (y-axis) x_range = range(width) # an iterator over the indices of all rows (x-axis) for y in y_range: src_y = (y + 0.5) * y_ratio - 0.5 # use the center of each pixel src_y_i = int(src_y) weight_y0 = 1 - abs(src_y - src_y_i) for x in x_range: src_x = (x + 0.5) * x_ratio - 0.5 src_x_i = int(src_x) weight_x0 = 1 - abs(src_x - src_x_i) channel_sums = [0.0] * pixelsize # populate <=4 nearest src_pixels, taking care not to go off # the edge of the image. src_pixels = [source.get_color(src_y_i, src_x_i), None, None, None] if src_x_i + 1 < source.width: src_pixels[1] = source.get_color(src_y_i, src_x_i + 1) else: weight_x0 = 1 if src_y_i + 1 < source.height: src_pixels[2] = source.get_color(src_y_i + 1, src_x_i) if src_x_i + 1 < source.height: src_pixels[3] = source.get_color(src_y_i + 1, src_x_i + 1) else: weight_y0 = 1 for i, src_pixel in enumerate(src_pixels): if src_pixel is None: continue src_pixel = src_pixel.to_pixel(pixelsize) weight_x = (1 - weight_x0) if (i % 2) else weight_x0 weight_y = (1 - weight_y0) if (i // 2) else weight_y0 alpha_weight = weight_x * weight_y color_weight = alpha_weight alpha = 255 if has_alpha: alpha = src_pixel[-1] if not alpha: continue color_weight *= (alpha / 255.0) for channel_index, channel_value in zip(color_channels_range, src_pixel): channel_sums[channel_index] += color_weight * channel_value if has_alpha: channel_sums[-1] += alpha_weight * alpha if has_alpha: total_alpha_multiplier = channel_sums[-1] / 255.0 if total_alpha_multiplier: # (avoid div/0) for channel_index in color_channels_range: channel_sums[channel_index] /= total_alpha_multiplier pixels.extend([int(round(s)) for s in channel_sums]) return get_pixel_array(pixels, width, height, pixelsize)
def new(cls, mode, width, height, background_color, palette=None, meta=None): color = background_color.to_pixel(mode.length) pixel_array = get_pixel_array(array.array("B", color) * width * height, width, height, mode.length) return LoadedImage(mode, width, height, pixel_array, palette=palette, meta=meta)
def handle_chunk_IHDR(self, chunk, length): # http://www.w3.org/TR/PNG/#11IHDR if length != 13: raise ChunkError( 'IHDR chunk has incorrect length %s, should be 13.' % length) (self.width, self.height, self.bit_depth, self.color_type, self.compression_method, self.filter_method, self.interlace_method) = struct.unpack("!2I5B", chunk) # Check that the header specifies only valid combinations. if self.bit_depth not in ALLOWED_BIT_DEPTHS: raise PNGReaderError("invalid bit depth %d" % self.bit_depth) if self.color_type not in ALLOWED_COLOR_TYPES: raise PNGReaderError("invalid colour type %d" % self.color_type) # Check indexed (palettized) images have 8 or fewer bits # per pixel; check only indexed or greyscale images have # fewer than 8 bits per pixel. if ((self.color_type & 1 and self.bit_depth > 8) or (self.bit_depth < 8 and self.color_type not in (0, 3))): raise PNGReaderError( "Illegal combination of bit depth (%d)" " and colour type (%d)." " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." % (self.bit_depth, self.color_type)) if self.compression_method != 0: raise PNGReaderError("unknown compression method %d" % self.compression_method) if self.filter_method != 0: raise PNGReaderError( "Unknown filter method %d," " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." % self.filter_method) if self.interlace_method not in (0, 1): raise PNGReaderError( "Unknown interlace method %d," " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." % self.interlace_method) self.pixelsize = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4, }[self.color_type] # Derived values # http://www.w3.org/TR/PNG/#6Colour-values colormap = bool(self.color_type & 1) greyscale = not (self.color_type & 2) alpha = bool(self.color_type & 4) if greyscale or colormap: color_planes = 1 else: color_planes = 3 planes = color_planes + alpha self.colormap = colormap self.greyscale = greyscale self.alpha = alpha self.mode = RGBA if self.alpha else RGB self.color_planes = color_planes self.planes = planes self.psize = fdiv(self.bit_depth, 8) * planes if int(self.psize) == self.psize: self.psize = int(self.psize) self.filter_unit = max(1, self.psize) self.row_bytes = int(math.ceil(self.width * self.psize)) # scanline stuff self.scanline = array.array('B') if self.bit_depth == 16: array_code = 'H' else: array_code = 'B' data = array.array(array_code, [0] * self.width * self.height * self.pixelsize) self.pixels = get_pixel_array(data, self.width, self.height, self.pixelsize) if self.interlace_method: self.adam7 = Adam7(self) self.scanline_length = self.adam7.get_scanline_length() self._process_scanline = self._process_interlaced_scanline else: self.previous_scanline = None self.scanline_length = self.row_bytes + 1 self.current_y = 0 self._process_scanline = self._process_straightlaced_scanline
def handle_chunk_IHDR(self, chunk, length): # http://www.w3.org/TR/PNG/#11IHDR if length != 13: raise ChunkError('IHDR chunk has incorrect length %s, should be 13.' % length) (self.width, self.height, self.bit_depth, self.color_type, self.compression_method, self.filter_method, self.interlace_method) = struct.unpack("!2I5B", chunk) # Check that the header specifies only valid combinations. if self.bit_depth not in ALLOWED_BIT_DEPTHS: raise PNGReaderError("invalid bit depth %d" % self.bit_depth) if self.color_type not in ALLOWED_COLOR_TYPES: raise PNGReaderError("invalid colour type %d" % self.color_type) # Check indexed (palettized) images have 8 or fewer bits # per pixel; check only indexed or greyscale images have # fewer than 8 bits per pixel. if ((self.color_type & 1 and self.bit_depth > 8) or (self.bit_depth < 8 and self.color_type not in (0,3))): raise PNGReaderError("Illegal combination of bit depth (%d)" " and colour type (%d)." " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." % (self.bit_depth, self.color_type)) if self.compression_method != 0: raise PNGReaderError("unknown compression method %d" % self.compression_method) if self.filter_method != 0: raise PNGReaderError("Unknown filter method %d," " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." % self.filter_method) if self.interlace_method not in (0, 1): raise PNGReaderError("Unknown interlace method %d," " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." % self.interlace_method) self.pixelsize = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4, }[self.color_type] # Derived values # http://www.w3.org/TR/PNG/#6Colour-values colormap = bool(self.color_type & 1) greyscale = not (self.color_type & 2) alpha = bool(self.color_type & 4) if greyscale or colormap: color_planes = 1 else: color_planes = 3 planes = color_planes + alpha self.colormap = colormap self.greyscale = greyscale self.alpha = alpha self.color_planes = color_planes self.planes = planes self.psize = fdiv(self.bit_depth, 8) * planes if int(self.psize) == self.psize: self.psize = int(self.psize) self.filter_unit = max(1, self.psize) self.row_bytes = int(math.ceil(self.width * self.psize)) # scanline stuff self.scanline = array.array('B') if self.bit_depth == 16: array_code = 'H' else: array_code = 'B' data = array.array(array_code, [0] * self.width * self.height * self.pixelsize) self.pixels = get_pixel_array(data, self.width, self.height, self.pixelsize) if self.interlace_method: self.adam7 = Adam7(self) self.scanline_length = self.adam7.get_scanline_length() self._process_scanline = self._process_interlaced_scanline else: self.previous_scanline = None self.scanline_length = self.row_bytes + 1 self.current_y = 0 self._process_scanline = self._process_straightlaced_scanline
def new(cls, width, height, background_color, mode, palette=None): color = background_color.to_pixel(mode.length) pixel_array = get_pixel_array(array.array('B', color * width * height), width, height, mode.length) return Image(pixel_array, mode, palette=palette)