def test_paletted_merge(self): if not hasattr(Image, 'FASTOCTREE'): raise SkipTest() # generate RGBA images with a transparent rectangle in the lower right img1 = ImageSource(Image.new('RGBA', (50, 50), (0, 255, 0, 255))).as_image() draw = ImageDraw.Draw(img1) draw.rectangle((25, 25, 49, 49), fill=(0, 0, 0, 0)) paletted_img = quantize(img1, alpha=True) assert img_has_transparency(paletted_img) assert paletted_img.mode == 'P' rgba_img = Image.new('RGBA', (50, 50), (255, 0, 0, 255)) draw = ImageDraw.Draw(rgba_img) draw.rectangle((25, 25, 49, 49), fill=(0, 0, 0, 0)) img1 = ImageSource(paletted_img) img2 = ImageSource(rgba_img) # generate base image and merge the others above img3 = ImageSource(Image.new('RGBA', (50, 50), (0, 0, 255, 255))) result = merge_images([img3, img1, img2], ImageOptions(transparent=True)) img = result.as_image() assert img.mode == 'RGBA' eq_(img.getpixel((49, 49)), (0, 0, 255, 255)) eq_(img.getpixel((0, 0)), (255, 0, 0, 255))
def test_composite_merge(self): # http://stackoverflow.com/questions/3374878 if not hasattr(Image, 'alpha_composite'): raise SkipTest() img1 = Image.new('RGBA', size=(100, 100), color=(255, 0, 0, 255)) draw = ImageDraw.Draw(img1) draw.rectangle((33, 0, 66, 100), fill=(255, 0, 0, 128)) draw.rectangle((67, 0, 100, 100), fill=(255, 0, 0, 0)) img1 = ImageSource(img1) img2 = Image.new('RGBA', size=(100, 100), color=(0, 255, 0, 255)) draw = ImageDraw.Draw(img2) draw.rectangle((0, 33, 100, 66), fill=(0, 255, 0, 128)) draw.rectangle((0, 67, 100, 100), fill=(0, 255, 0, 0)) img2 = ImageSource(img2) result = merge_images([img2, img1], ImageOptions(transparent=True)) img = result.as_image() eq_(img.mode, 'RGBA') assert_colors_equal(img, [(1089, (0, 255, 0, 255)), (1089, (255, 255, 255, 0)), (1122, (0, 255, 0, 128)), (1122, (128, 126, 0, 255)), (1122, (255, 0, 0, 128)), (1156, (170, 84, 0, 191)), (3300, (255, 0, 0, 255))])
def test_solid_merge(self): img1 = ImageSource(Image.new('RGB', (10, 10), (255, 0, 255))) img2 = ImageSource(Image.new('RGB', (10, 10), (0, 255, 255))) result = merge_images([img1, img2], ImageOptions(transparent=False)) img = result.as_image() eq_(img.getpixel((0, 0)), (0, 255, 255))
def test_composite_merge(self): # http://stackoverflow.com/questions/3374878 if not hasattr(Image, 'alpha_composite'): raise SkipTest() img1 = Image.new('RGBA', size=(100, 100), color=(255, 0, 0, 255)) draw = ImageDraw.Draw(img1) draw.rectangle((33, 0, 66, 100), fill=(255, 0, 0, 128)) draw.rectangle((67, 0, 100, 100), fill=(255, 0, 0, 0)) img1 = ImageSource(img1) img2 = Image.new('RGBA', size =(100, 100), color=(0, 255, 0, 255)) draw = ImageDraw.Draw(img2) draw.rectangle((0, 33, 100, 66), fill=(0, 255, 0, 128)) draw.rectangle((0, 67, 100, 100), fill=(0, 255, 0, 0)) img2 = ImageSource(img2) result = merge_images([img2, img1], ImageOptions(merge='composite', transparent=True)) img = result.as_image() eq_(img.mode, 'RGBA') eq_(sorted(img.getcolors()), sorted([ (1089, (0, 255, 0, 255)), (1089, (255, 255, 255, 0)), (1122, (0, 255, 0, 128)), (1122, (128, 126, 0, 255)), (1122, (255, 0, 0, 128)), (1156, (170, 84, 0, 191)), (3300, (255, 0, 0, 255))]))
def test_output_formats_png24(self): img = Image.new('RGBA', (100, 100)) image_opts = PNG_FORMAT.copy() image_opts.colors = 0 # TODO image_opts ir = ImageSource(img, image_opts=image_opts) img = Image.open(ir.as_buffer()) eq_(img.mode, 'RGBA') assert img.getpixel((0, 0)) == (0, 0, 0, 0)
def test_opacity_merge_mixed_modes(self): img1 = ImageSource(Image.new('RGBA', (10, 10), (255, 0, 255, 255))) img2 = ImageSource(Image.new('RGB', (10, 10), (0, 255, 255)).convert('P'), image_opts=ImageOptions(opacity=0.5)) result = merge_images([img1, img2], ImageOptions(transparent=True)) img = result.as_image() eq_(img.getpixel((0, 0)), (127, 127, 255, 255))
def create_debug_img(size, transparent=True): if transparent: img = Image.new("RGBA", size) else: img = Image.new("RGB", size, ImageColor.getrgb("#EEE")) draw = ImageDraw.Draw(img) draw_pattern(draw, size) return img
def test_output_formats_png8(self): img = Image.new('RGBA', (100, 100)) ir = ImageSource(img, image_opts=PNG_FORMAT) img = Image.open( ir.as_buffer( ImageOptions(colors=256, transparent=True, format='image/png'))) assert img.mode == 'P' assert img.getpixel((0, 0)) == 255
def test_opacity_merge_mixed_modes(self): img1 = ImageSource(Image.new('RGBA', (10, 10), (255, 0, 255, 255))) img2 = ImageSource(Image.new('RGB', (10, 10), (0, 255, 255)).convert('P'), image_opts=ImageOptions(opacity=0.5)) result = merge_images([img1, img2], ImageOptions(transparent=True)) img = result.as_image() assert_colors_equal(img, [ (10*10, (127, 127, 255, 255)), ])
def test_opacity_merge_mixed_modes(self): img1 = ImageSource(Image.new('RGBA', (10, 10), (255, 0, 255, 255))) img2 = ImageSource(Image.new('RGB', (10, 10), (0, 255, 255)).convert('P'), image_opts=ImageOptions(opacity=0.5)) result = merge_images([img1, img2], ImageOptions(transparent=True)) img = result.as_image() assert_colors_equal(img, [ (10 * 10, (127, 127, 255, 255)), ])
def create_image(size, color=None, mode=None): if color is not None: if isinstance(color, basestring): if mode is None: mode = 'RGB' img = Image.new(mode, size, color=color) else: if mode is None: mode = 'RGBA' if len(color) == 4 else 'RGB' img = Image.new(mode, size, color=tuple(color)) else: img = create_debug_img(size) return img
def concat_legends(legends, format='png', size=None, bgcolor='#ffffff', transparent=True): """ Merge multiple legends into one :param images: list of `ImageSource`, bottom image first :param format: the format of the output `ImageSource` :param size: size of the merged image, if ``None`` the size will be calculated :rtype: `ImageSource` """ if not legends: return BlankImageSource(size=(1, 1), image_opts=ImageOptions( bgcolor=bgcolor, transparent=transparent)) if len(legends) == 1: return legends[0] legends = legends[:] legends.reverse() if size is None: legend_width = 0 legend_height = 0 legend_position_y = [] #iterate through all legends, last to first, calc img size and remember the y-position for legend in legends: legend_position_y.append(legend_height) tmp_img = legend.as_image() legend_width = max(legend_width, tmp_img.size[0]) legend_height += tmp_img.size[ 1] #images shall not overlap themselfs size = [legend_width, legend_height] bgcolor = ImageColor.getrgb(bgcolor) if transparent: img = Image.new('RGBA', size, bgcolor + (0, )) else: img = Image.new('RGB', size, bgcolor) for i in range(len(legends)): legend_img = legends[i].as_image() if legend_img.mode == 'RGBA': # paste w transparency mask from layer img.paste(legend_img, (0, legend_position_y[i]), legend_img) else: img.paste(legend_img, (0, legend_position_y[i])) return ImageSource(img, image_opts=ImageOptions(format=format))
def test_concatenation(self): legends = [] img_1 = Image.new(mode='RGBA', size=(30, 10), color="red") img_2 = Image.new(mode='RGBA', size=(10, 10), color="black") img_3 = Image.new(mode='RGBA', size=(50, 80), color="blue") legends.append(ImageSource(img_1)) legends.append(ImageSource(img_2)) legends.append(ImageSource(img_3)) source = concat_legends(legends) src_img = source.as_image() assert src_img.getpixel((0, 90)) == (255, 0, 0, 255) assert src_img.getpixel((0, 80)) == (0, 0, 0, 255) assert src_img.getpixel((0, 0)) == (0, 0, 255, 255) assert src_img.getpixel((49, 99)) == (255, 255, 255, 0) assert is_png(source.as_buffer())
def test_concatenation(self): legends = [] img_1 = Image.new(mode='RGBA', size=(30,10), color="red") img_2 = Image.new(mode='RGBA', size=(10,10), color="black") img_3 = Image.new(mode='RGBA', size=(50,80), color="blue") legends.append(ImageSource(img_1)) legends.append(ImageSource(img_2)) legends.append(ImageSource(img_3)) source = concat_legends(legends) src_img = source.as_image() assert src_img.getpixel((0,90)) == (255,0,0,255) assert src_img.getpixel((0,80)) == (0,0,0,255) assert src_img.getpixel((0,0)) == (0,0,255,255) assert src_img.getpixel((49,99)) == (255,255,255,0) assert is_png(source.as_buffer())
def create_image(size, image_opts=None): """ Create a new image that is compatible with the given `image_opts`. Takes into account mode, transparent, bgcolor. """ from mapproxy.platform.image import Image, ImageColor if image_opts is None: mode = 'RGB' bgcolor = (255, 255, 255) else: mode = image_opts.mode if mode in (None, 'P'): if image_opts.transparent: mode = 'RGBA' else: mode = 'RGB' bgcolor = image_opts.bgcolor or (255, 255, 255) if isinstance(bgcolor, basestring): bgcolor = ImageColor.getrgb(bgcolor) if image_opts.transparent and len(bgcolor) == 3: bgcolor = bgcolor + (0, ) if image_opts.mode == 'I': bgcolor = bgcolor[0] return Image.new(mode, size, bgcolor)
def test_mask_partial_image_bgcolor(self): img = ImageSource( Image.new("RGB", (100, 100), color=(100, 0, 200)), image_opts=ImageOptions(bgcolor=(200, 30, 120)) ) result = mask_image_source_from_coverage(img, [0, 0, 10, 10], SRS(4326), coverage([5, 5, 30, 30])) eq_(result.as_image().getcolors(), [(7500, (200, 30, 120)), (2500, (100, 0, 200))])
def test_one_point(self): img = Image.new('RGB', (100, 100), color='#ff0000') draw = ImageDraw.Draw(img) draw.point((99, 99)) del draw assert not is_single_color_image(img)
def test_rgb(self): if hasattr(Image, 'FASTOCTREE'): img = Image.new('RGB', (10, 10)) assert not img_has_transparency(img) img = quantize(img, alpha=False) assert not img_has_transparency(img)
def test_solid_paletted_image(self): img = Image.new('P', (100, 100), color=20) palette = [] for i in range(256): palette.extend((i, i // 2, i % 3)) img.putpalette(palette) eq_(is_single_color_image(img), (20, 10, 2))
def test_composite_merge_opacity(self): if not hasattr(Image, 'alpha_composite'): raise SkipTest() bg = Image.new('RGBA', size=(100, 100), color=(255, 0, 255, 255)) bg = ImageSource(bg) fg = Image.new('RGBA', size=(100, 100), color=(0, 0, 0, 0)) draw = ImageDraw.Draw(fg) draw.rectangle((10, 10, 89, 89), fill=(0, 255, 255, 255)) fg = ImageSource(fg, image_opts=ImageOptions(opacity=0.5)) result = merge_images([bg, fg], ImageOptions(transparent=True)) img = result.as_image() eq_(img.mode, 'RGBA') assert_colors_equal(img, [(3600, (255, 0, 255, 255)), (6400, (128, 127, 255, 255))])
def test_layers_with_opacity(self): # overlay with opacity -> request should not be combined common_params = (r'?SERVICE=WMS&FORMAT=image%2Fpng' '&REQUEST=GetMap&HEIGHT=200&SRS=EPSG%3A4326&styles=' '&VERSION=1.1.1&BBOX=9.0,50.0,10.0,51.0' '&WIDTH=200') img_bg = create_tmp_image((200, 200), color=(0, 0, 0)) img_fg = create_tmp_image((200, 200), color=(255, 0, 128)) expected_req = [ ({'path': '/service_a' + common_params + '&layers=a_one'}, {'body': img_bg, 'headers': {'content-type': 'image/png'}}), ({'path': '/service_a' + common_params + '&layers=a_two'}, {'body': img_fg, 'headers': {'content-type': 'image/png'}}), ] with mock_httpd(('localhost', 42423), expected_req): self.common_map_req.params.layers = 'opacity_base,opacity_overlay' resp = self.app.get(self.common_map_req) eq_(resp.content_type, 'image/png') data = StringIO(resp.body) assert is_png(data) img = Image.open(data) eq_(img.getcolors()[0], ((200*200),(127, 0, 64)))
def assert_image_mode(img, mode): pos = img.tell() try: img = Image.open(img) eq_(img.mode, mode) finally: img.seek(pos)
def test_mask_partial_image_transparent(self): img = ImageSource(Image.new('RGB', (100, 100), color=(100, 0, 200)), image_opts=ImageOptions(transparent=True)) result = mask_image_source_from_coverage(img, [0, 0, 10, 10], SRS(4326), coverage([5, 5, 30, 30])) eq_(result.as_image().getcolors(), [(7500, (255, 255, 255, 0)), (2500, (100, 0, 200, 255))])
def test_combined_transp_color(self): # merged to one request because both layers share the same transparent_color common_params = (r'?SERVICE=WMS&FORMAT=image%2Fpng' '&REQUEST=GetMap&HEIGHT=200&SRS=EPSG%3A4326&styles=' '&VERSION=1.1.1&BBOX=9.0,50.0,10.0,51.0' '&WIDTH=200&transparent=True') with tmp_image((200, 200), color=(255, 0, 0), format='png') as img: img = img.read() expected_req = [ ({ 'path': '/service_a' + common_params + '&layers=a_iopts_one,a_iopts_two' }, { 'body': img, 'headers': { 'content-type': 'image/png' } }), ] with mock_httpd(('localhost', 42423), expected_req): self.common_map_req.params.layers = 'layer_image_opts1,layer_image_opts2' self.common_map_req.params.transparent = True resp = self.app.get(self.common_map_req) resp.content_type = 'image/png' data = StringIO(resp.body) assert is_png(data) img = Image.open(data) eq_(img.getcolors()[0], ((200 * 200), (255, 0, 0, 0)))
def test_get_legendgraphic_111(self): self.common_lg_req_111.params['scale'] = '5.0' with tmp_image((256, 256), format='png') as img: img_data = img.read() expected_req1 = ({ 'path': r'/service?LAYER=foo&SERVICE=WMS&FORMAT=image%2Fpng' '&REQUEST=GetLegendGraphic&SCALE=5.0&' '&VERSION=1.1.1&SLD_VERSION=1.1.0' }, { 'body': img_data, 'headers': { 'content-type': 'image/png' } }) expected_req2 = ({ 'path': r'/service?LAYER=bar&SERVICE=WMS&FORMAT=image%2Fpng' '&REQUEST=GetLegendGraphic&SCALE=5.0&' '&VERSION=1.1.1&SLD_VERSION=1.1.0' }, { 'body': img_data, 'headers': { 'content-type': 'image/png' } }) with mock_httpd(('localhost', 42423), [expected_req1, expected_req2]): resp = self.app.get(self.common_lg_req_111) eq_(resp.content_type, 'image/png') data = StringIO(resp.body) assert is_png(data) assert Image.open(data).size == (256, 512)
def test_transparent_watermark_tile(self): with tmp_image((256, 256), format='png', color=(0, 0, 0, 0), mode='RGBA') as img: expected_req = ({ 'path': r'/service?LAYERs=blank&SERVICE=WMS&FORMAT=image%2Fpng' '&REQUEST=GetMap&HEIGHT=256&SRS=EPSG%3A4326&styles=' '&VERSION=1.1.1&BBOX=-180.0,-90.0,0.0,90.0' '&WIDTH=256' }, { 'body': img.read(), 'headers': { 'content-type': 'image/jpeg' } }) with mock_httpd(('localhost', 42423), [expected_req]): resp = self.app.get( '/tms/1.0.0/watermark_transp/EPSG4326/0/0/0.png') eq_(resp.content_type, 'image/png') img = Image.open(StringIO(resp.body)) colors = img.getcolors() assert len(colors) >= 2 eq_(sorted(colors)[-1][1], (0, 0, 0, 0))
def _make_transparent(img, color, tolerance=10): img.load() if img.mode == 'P': img = img.convert('RGBA') channels = img.split() mask_channels = [] for ch, c in zip(channels, color): # create bit mask for each matched color low_c, high_c = c - tolerance, c + tolerance mask_channels.append( Image.eval(ch, lambda x: 255 if low_c <= x <= high_c else 0)) # multiply channel bit masks to get a single mask alpha = reduce(ImageChops.multiply, mask_channels) # invert to get alpha channel alpha = ImageChops.invert(alpha) if len(channels) == 4: # multiply with existing alpha alpha = ImageChops.multiply(alpha, channels[-1]) img.putalpha(alpha) return img
def test_mask_outside_of_image_transparent(self): img = ImageSource(Image.new('RGB', (100, 100), color=(100, 0, 200)), image_opts=ImageOptions(transparent=True)) result = mask_image_source_from_coverage(img, [0, 0, 10, 10], SRS(4326), coverage([20, 20, 30, 30])) eq_(result.as_image().getcolors(), [((100 * 100), (255, 255, 255, 0))])
def test_solid_paletted_image(self): img = Image.new('P', (100, 100), color=20) palette = [] for i in range(256): palette.extend((i, i//2, i%3)) img.putpalette(palette) eq_(is_single_color_image(img), (20, 10, 2))
def test_get_map(self): resp = self.app.get(self.common_map_req) eq_(resp.content_type, 'image/png') data = StringIO(resp.body) assert is_png(data) img = Image.open(data) img = img.convert('RGB') eq_(img.getcolors(), [(200*200, (255, 0, 0))])
def test_rbga(self): if hasattr(Image, 'FASTOCTREE'): img = Image.new('RGBA', (10, 10), (100, 200, 50, 255)) img.paste((255, 50, 50, 0), (3, 3, 7, 7)) assert img_has_transparency(img) img = quantize(img, alpha=True) assert img_has_transparency(img)
def test_composite_merge_opacity(self): if not hasattr(Image, 'alpha_composite'): raise SkipTest() bg = Image.new('RGBA', size=(100, 100), color=(255, 0, 255, 255)) bg = ImageSource(bg) fg = Image.new('RGBA', size =(100, 100), color=(0, 0, 0, 0)) draw = ImageDraw.Draw(fg) draw.rectangle((10, 10, 89, 89), fill=(0, 255, 255, 255)) fg = ImageSource(fg, image_opts=ImageOptions(opacity=0.5)) result = merge_images([bg, fg], ImageOptions(transparent=True)) img = result.as_image() eq_(img.mode, 'RGBA') assert_colors_equal(img, [ (3600, (255, 0, 255, 255)), (6400, (128, 127, 255, 255))])
def test_unicode(self): font = ImageFont.load_default() td = TextDraw(u'Héllö\nWørld', font, placement='cc') img = Image.new('RGB', (100, 100)) draw = ImageDraw.Draw(img) total_box, boxes = td.text_boxes(draw, (100, 100)) eq_(total_box, (35, 38, 65, 63)) eq_(boxes, [(35, 38, 65, 49), (35, 52, 65, 63)])
def test_multiline_ul(self): font = ImageFont.load_default() td = TextDraw('Hello\nWorld', font) img = Image.new('RGB', (100, 100)) draw = ImageDraw.Draw(img) total_box, boxes = td.text_boxes(draw, (100, 100)) eq_(total_box, (5, 5, 35, 30)) eq_(boxes, [(5, 5, 35, 16), (5, 19, 35, 30)])
def test_multiline_center(self): font = ImageFont.load_default() td = TextDraw('Hello\nWorld', font, placement='cc') img = Image.new('RGB', (100, 100)) draw = ImageDraw.Draw(img) total_box, boxes = td.text_boxes(draw, (100, 100)) eq_(total_box, (35, 38, 65, 63)) eq_(boxes, [(35, 38, 65, 49), (35, 52, 65, 63)])
def test_multiline_lr(self): font = ImageFont.load_default() td = TextDraw('Hello\nWorld', font, placement='lr') img = Image.new('RGB', (100, 100)) draw = ImageDraw.Draw(img) total_box, boxes = td.text_boxes(draw, (100, 100)) eq_(total_box, (65, 70, 95, 95)) eq_(boxes, [(65, 70, 95, 81), (65, 84, 95, 95)])
def check_placement(self, x, y): font = ImageFont.load_default() td = TextDraw('Hello\nWorld\n%s %s' % (x, y), font, placement=x+y, padding=5, linespacing=2) img = Image.new('RGB', (100, 100)) draw = ImageDraw.Draw(img) td.draw(draw, img.size) img.show()
def test_returns_imagesource(self): img_src1 = ImageSource(Image.new('RGBA', (100, 100))) env = make_wsgi_env('', extra_environ={}) img_src2 = self.server.decorate_img( img_src1, 'wms.map', ['layer1'], env, self.query_extent ) assert isinstance(img_src2, ImageSource)
def test_ul(self): font = ImageFont.load_default() td = TextDraw('Hello', font) img = Image.new('RGB', (100, 100)) draw = ImageDraw.Draw(img) total_box, boxes = td.text_boxes(draw, (100, 100)) eq_(total_box, boxes[0]) eq_(len(boxes), 1)
def test_get_tile_uncached(self): resp = self.app.get('/tms/1.0.0/wms_cache/0/0/0.jpeg') eq_(resp.content_type, 'image/png') data = StringIO(resp.body) assert is_png(data) img = Image.open(data) eq_(img.mode, 'RGBA') eq_(img.getcolors(), [(256*256, (255, 255, 255, 0))])
def test_shapely_mask_with_transform_partial_image_transparent(self): img = ImageSource(Image.new("RGB", (100, 100), color=(100, 0, 200)), image_opts=ImageOptions(transparent=True)) p = Polygon([(0, 0), (222000, 0), (222000, 222000), (0, 222000)]) # ~ 2x2 degres result = mask_image_source_from_coverage(img, [0, 0, 10, 10], SRS(4326), coverage(p, "EPSG:3857")) # 20*20 = 400 eq_(result.as_image().getcolors(), [(10000 - 400, (255, 255, 255, 0)), (400, (100, 0, 200, 255))])
def test_original_imagesource_returned_when_no_callback(self): img_src1 = ImageSource(Image.new('RGBA', (100, 100))) env = make_wsgi_env('', extra_environ={}) img_src2 = self.server.decorate_img( img_src1, 'wms.map', ['layer1'], env, self.query_extent ) eq_(img_src1, img_src2)