def test_pnmimage_quantize(): img = PNMImage(32, 32, 3) for x in range(32): for y in range(32): img.set_xel_val(x, y, randint(0, 100), randint(50, 100), randint(0, 1)) hist = PNMImage.Histogram() img.make_histogram(hist) num_colors = hist.get_num_pixels() assert num_colors > 100 img2 = PNMImage(img) img2.quantize(100) hist = PNMImage.Histogram() img2.make_histogram(hist) assert hist.get_num_pixels() <= 100 # Make sure that this is reasonably close max_dist = 0 for x in range(32): for y in range(32): diff = img.get_xel(x, y) - img2.get_xel(x, y) max_dist = max(max_dist, diff.length_squared()) # Also make sure that they are not out of range of the original col = img2.get_xel_val(x, y) assert col.r <= 100 assert col.g >= 50 and col.g <= 100 assert col.b in (0, 1) assert max_dist < 0.1 ** 2
def _write_bitmap(self, fp, image, size, bpp): """ Writes the bitmap header and data of an .ico file. """ fp.write( struct.pack('<IiiHHIIiiII', 40, size, size * 2, 1, bpp, 0, 0, 0, 0, 0, 0)) # XOR mask if bpp == 24: # Align rows to 4-byte boundary rowalign = b'\0' * (-(size * 3) & 3) for y in xrange(size): for x in xrange(size): r, g, b = image.getXel(x, size - y - 1) fp.write( struct.pack('<BBB', int(b * 255), int(g * 255), int(r * 255))) fp.write(rowalign) elif bpp == 32: for y in xrange(size): for x in xrange(size): r, g, b, a = image.getXelA(x, size - y - 1) fp.write( struct.pack('<BBBB', int(b * 255), int(g * 255), int(r * 255), int(a * 255))) elif bpp == 8: # We'll have to generate a palette of 256 colors. hist = PNMImage.Histogram() image2 = PNMImage(image) if image2.hasAlpha(): image2.premultiplyAlpha() image2.removeAlpha() image2.quantize(256) image2.make_histogram(hist) colors = list(hist.get_pixels()) assert len(colors) <= 256 # Write the palette. i = 0 while i < 256 and i < len(colors): r, g, b, a = colors[i] fp.write(struct.pack('<BBBB', b, g, r, 0)) i += 1 if i < 256: # Fill the rest with zeroes. fp.write(b'\x00' * (4 * (256 - i))) # Write indices. Align rows to 4-byte boundary. rowalign = b'\0' * (-size & 3) for y in xrange(size): for x in xrange(size): pixel = image2.get_pixel(x, size - y - 1) index = colors.index(pixel) if index >= 256: # Find closest pixel instead. index = closest_indices[index - 256] fp.write(struct.pack('<B', index)) fp.write(rowalign) else: raise ValueError("Invalid bpp %d" % (bpp)) # Create an AND mask, aligned to 4-byte boundary if image.hasAlpha() and bpp <= 8: rowalign = b'\0' * (-((size + 7) >> 3) & 3) for y in xrange(size): mask = 0 num_bits = 7 for x in xrange(size): a = image.get_alpha_val(x, size - y - 1) if a <= 1: mask |= (1 << num_bits) num_bits -= 1 if num_bits < 0: fp.write(struct.pack('<B', mask)) mask = 0 num_bits = 7 if num_bits < 7: fp.write(struct.pack('<B', mask)) fp.write(rowalign) else: andsize = (size + 7) >> 3 if andsize % 4 != 0: andsize += 4 - (andsize % 4) fp.write(b'\x00' * (andsize * size))