From 395e26ef9c0ab4b0bd976f401af3e2b3038010bc Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 23 Oct 2010 13:36:55 -0400 Subject: [PATCH] changed most PIL paste() calls into composite.alpha_over() calls The ones I have not changed are those where paste() is really preferred, and I've noted why in comments. Calls to "dest.paste(src, rect, mask)" were converted to calls to "composite.alpha_over(dest, src, rect, mask)". --- chunk.py | 23 ++++++++++------ quadtree.py | 8 ++++-- textures.py | 79 +++++++++++++++++++++++++++-------------------------- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/chunk.py b/chunk.py index aefcab7..5b8fea5 100644 --- a/chunk.py +++ b/chunk.py @@ -22,6 +22,7 @@ import logging import nbt import textures import world +import composite """ This module has routines related to rendering one particular chunk into an @@ -36,7 +37,11 @@ image # of the dest image will have its alpha channel modified. To prevent this: # first use im.split() and take the third item which is the alpha channel and # use that as the mask. Then take the image and use im.convert("RGB") to strip -# the image from its alpha channel, and use that as the source to paste() +# the image from its alpha channel, and use that as the source to alpha_over() + +# (note that this workaround is NOT technically needed when using the +# alpha_over extension, BUT this extension may fall back to PIL's +# paste(), which DOES need the workaround.) def get_lvldata(filename): """Takes a filename and returns the Level struct, which contains all the @@ -556,36 +561,36 @@ class ChunkRenderer(object): # tint the block with a color proportional to its depth if cave: # no lighting for cave -- depth is probably more useful - img.paste(Image.blend(t[0],depth_colors[z],0.3), (imgx, imgy), t[1]) + composite.alpha_over(img, Image.blend(t[0],depth_colors[z],0.3), (imgx, imgy), t[1]) else: if not self.world.lighting: # no lighting at all - img.paste(t[0], (imgx, imgy), t[1]) + composite.alpha_over(img, t[0], (imgx, imgy), t[1]) elif blockid in transparent_blocks: # transparent means draw the whole # block shaded with the current # block's light black_coeff, _ = self.get_lighting_coefficient(x, y, z) - img.paste(Image.blend(t[0], black_color, black_coeff), (imgx, imgy), t[1]) + composite.alpha_over(img, Image.blend(t[0], black_color, black_coeff), (imgx, imgy), t[1]) else: # draw each face lit appropriately, # but first just draw the block - img.paste(t[0], (imgx, imgy), t[1]) + composite.alpha_over(img, t[0], (imgx, imgy), t[1]) # top face black_coeff, face_occlude = self.get_lighting_coefficient(x, y, z + 1) if not face_occlude: - img.paste((0,0,0), (imgx, imgy), ImageEnhance.Brightness(facemasks[0]).enhance(black_coeff)) + composite.alpha_over(img, black_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[0]).enhance(black_coeff)) # left face black_coeff, face_occlude = self.get_lighting_coefficient(x - 1, y, z) if not face_occlude: - img.paste((0,0,0), (imgx, imgy), ImageEnhance.Brightness(facemasks[1]).enhance(black_coeff)) + composite.alpha_over(img, black_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[1]).enhance(black_coeff)) # right face black_coeff, face_occlude = self.get_lighting_coefficient(x, y + 1, z) if not face_occlude: - img.paste((0,0,0), (imgx, imgy), ImageEnhance.Brightness(facemasks[2]).enhance(black_coeff)) + composite.alpha_over(img, black_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[2]).enhance(black_coeff)) # Draw edge lines if blockid in (44,): # step block @@ -616,6 +621,8 @@ def generate_facemasks(): toppart = textures.transform_image(white) leftpart = textures.transform_image_side(white) + # using the real PIL paste here (not alpha_over) because there is + # no alpha channel (and it's mode "L") top.paste(toppart, (0,0)) left.paste(leftpart, (0,6)) right = left.transpose(Image.FLIP_LEFT_RIGHT) diff --git a/quadtree.py b/quadtree.py index af4d15e..223deb2 100644 --- a/quadtree.py +++ b/quadtree.py @@ -29,6 +29,7 @@ import util from PIL import Image from optimizeimages import optimize_image +import composite """ @@ -449,7 +450,10 @@ def render_innertile(dest, name, imgformat, optimizeimg): # Create the actual image now img = Image.new("RGBA", (384, 384), (38,92,255,0)) - + + # we'll use paste (NOT alpha_over) for quadtree generation because + # this is just straight image stitching, not alpha blending + if q0path: try: quad0 = Image.open(q0path).resize((192,192), Image.ANTIALIAS) @@ -613,7 +617,7 @@ def render_worldtile(chunks, colstart, colend, rowstart, rowend, path, imgformat xpos = -192 + (col-colstart)*192 ypos = -96 + (row-rowstart)*96 - tileimg.paste(chunkimg.convert("RGB"), (xpos, ypos), chunkimg) + composite.alpha_over(tileimg, chunkimg.convert("RGB"), (xpos, ypos), chunkimg) # Save them tileimg.save(imgpath) diff --git a/textures.py b/textures.py index b942168..3f9a3bc 100644 --- a/textures.py +++ b/textures.py @@ -24,6 +24,7 @@ import numpy from PIL import Image, ImageEnhance import util +import composite def _find_file(filename, mode="rb"): """Searches for the given file and returns an open handle to it. @@ -157,13 +158,13 @@ def transform_image_side(img, blockID=None): # img to be unchanged mask = img.crop((0,8,16,16)) n = Image.new(img.mode, img.size, (38,92,255,0)) - n.paste(mask,(0,0,16,8), mask) + composite.alpha_over(n, mask,(0,0,16,8), mask) img = n if blockID in (78,): # snow # make the top three quarters transparent mask = img.crop((0,12,16,16)) n = Image.new(img.mode, img.size, (38,92,255,0)) - n.paste(mask,(0,12,16,16), mask) + composite.alpha_over(n, mask,(0,12,16,16), mask) img = n # Size of the cube side before shear @@ -189,7 +190,7 @@ def _build_block(top, side, blockID=None): top = transform_image(top, blockID) if not side: - img.paste(top, (0,0), top) + composite.alpha_over(img, top, (0,0), top) return img side = transform_image_side(side, blockID) @@ -212,29 +213,29 @@ def _build_block(top, side, blockID=None): if blockID in (37,38,6,39,40,50,83): ## flowers, sapling, mushrooms, regular torch, reeds # instead of pasting these blocks at the cube edges, place them in the middle: # and omit the top - img.paste(side, (6,3), side) - img.paste(otherside, (6,3), otherside) + composite.alpha_over(img, side, (6,3), side) + composite.alpha_over(img, otherside, (6,3), otherside) return img if blockID in (81,): # cacti! - img.paste(side, (2,6), side) - img.paste(otherside, (10,6), otherside) - img.paste(top, (0,2), top) + composite.alpha_over(img, side, (2,6), side) + composite.alpha_over(img, otherside, (10,6), otherside) + composite.alpha_over(img, top, (0,2), top) elif blockID in (44,): # half step # shift each texture down 6 pixels - img.paste(side, (0,12), side) - img.paste(otherside, (12,12), otherside) - img.paste(top, (0,6), top) + composite.alpha_over(img, side, (0,12), side) + composite.alpha_over(img, otherside, (12,12), otherside) + composite.alpha_over(img, top, (0,6), top) elif blockID in (78,): # snow # shift each texture down 9 pixels - img.paste(side, (0,6), side) - img.paste(otherside, (12,6), otherside) - img.paste(top, (0,9), top) + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,9), top) else: - img.paste(side, (0,6), side) - img.paste(otherside, (12,6), otherside) - img.paste(top, (0,0), top) + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,0), top) # Manually touch up 6 pixels that leave a gap because of how the # shearing works out. This makes the blocks perfectly tessellate-able @@ -366,7 +367,7 @@ def generate_special_texture(blockID, data): track = transform_image(raw_straight, blockID) img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(track, (0,12), track) + composite.alpha_over(img, track, (0,12), track) return (img.convert("RGB"), img.split()[3]) if blockID == 59: # crops @@ -376,9 +377,9 @@ def generate_special_texture(blockID, data): crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(crop1, (0,12), crop1) - img.paste(crop2, (6,3), crop2) - img.paste(crop3, (6,3), crop3) + composite.alpha_over(img, crop1, (0,12), crop1) + composite.alpha_over(img, crop2, (6,3), crop2) + composite.alpha_over(img, crop3, (6,3), crop3) return (img.convert("RGB"), img.split()[3]) if blockID == 61: #furnace @@ -388,9 +389,9 @@ def generate_special_texture(blockID, data): img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(side1, (0,6), side1) - img.paste(side2, (12,6), side2) - img.paste(top, (0,0), top) + composite.alpha_over(img, side1, (0,6), side1) + composite.alpha_over(img, side2, (12,6), side2) + composite.alpha_over(img, top, (0,0), top) return (img.convert("RGB"), img.split()[3]) if blockID == 62: # lit furnace @@ -400,9 +401,9 @@ def generate_special_texture(blockID, data): img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(side1, (0,6), side1) - img.paste(side2, (12,6), side2) - img.paste(top, (0,0), top) + composite.alpha_over(img, side1, (0,6), side1) + composite.alpha_over(img, side2, (12,6), side2) + composite.alpha_over(img, top, (0,0), top) return (img.convert("RGB"), img.split()[3]) if blockID == 65: # ladder @@ -414,22 +415,22 @@ def generate_special_texture(blockID, data): # have to render this thing anyway. same for data == 2 tex = transform_image_side(raw_texture) img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(tex, (0,6), tex) + composite.alpha_over(img, tex, (0,6), tex) return (img.convert("RGB"), img.split()[3]) if data == 2: tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(tex, (12,6), tex) + composite.alpha_over(img, tex, (12,6), tex) return (img.convert("RGB"), img.split()[3]) if data == 3: tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(tex, (0,0), tex) + composite.alpha_over(img, tex, (0,0), tex) return (img.convert("RGB"), img.split()[3]) if data == 4: tex = transform_image_side(raw_texture) img = Image.new("RGBA", (24,24), (38,92,255,0)) - img.paste(tex, (12,0), tex) + composite.alpha_over(img, tex, (12,0), tex) return (img.convert("RGB"), img.split()[3]) if blockID in (64,71): #wooden door, or iron door @@ -450,36 +451,36 @@ def generate_special_texture(blockID, data): if (data & 0x03) == 0: if not swung: tex = transform_image_side(raw_door) - img.paste(tex, (0,6), tex) + composite.alpha_over(img, tex, (0,6), tex) else: # flip first to set the doornob on the correct side tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) tex = tex.transpose(Image.FLIP_LEFT_RIGHT) - img.paste(tex, (0,0), tex) + composite.alpha_over(img, tex, (0,0), tex) if (data & 0x03) == 1: if not swung: tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - img.paste(tex, (0,0), tex) + composite.alpha_over(img, tex, (0,0), tex) else: tex = transform_image_side(raw_door) - img.paste(tex, (12,0), tex) + composite.alpha_over(img, tex, (12,0), tex) if (data & 0x03) == 2: if not swung: tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - img.paste(tex, (12,0), tex) + composite.alpha_over(img, tex, (12,0), tex) else: tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - img.paste(tex, (12,6), tex) + composite.alpha_over(img, tex, (12,6), tex) if (data & 0x03) == 3: if not swung: tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) - img.paste(tex, (12,6), tex) + composite.alpha_over(img, tex, (12,6), tex) else: tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - img.paste(tex, (0,6), tex) + composite.alpha_over(img, tex, (0,6), tex) return (img.convert("RGB"), img.split()[3])