Skip to content

Commit

Permalink
Merge 2f39d70 into 64dc0cc
Browse files Browse the repository at this point in the history
  • Loading branch information
encukou committed Oct 28, 2013
2 parents 64dc0cc + 2f39d70 commit de05149
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 27 deletions.
39 changes: 35 additions & 4 deletions tmxlib/canvas.py
Expand Up @@ -43,7 +43,7 @@ class Canvas(PilImage):

def __init__(self, size=(0, 0), commands=()):
self.size = size
self.pil_image = Image.new('RGBA', size)
self.pil_image = Image.new('RGBA', size, color=(0, 0, 0, 0))

for command in commands:
command.draw(self)
Expand All @@ -55,12 +55,23 @@ def to_image(self):
"""
return PilImage(data=self._repr_png_())

@property
def trans(self):
return None

@trans.setter
def trans(self, new_trans):
if new_trans is not None:
raise ValueError('Canvas does not support trans')

def _parent_info(self):
return 0, 0, self.to_image()

def draw_image(self, image, pos=(0, 0)):
def draw_image(self, image, pos=(0, 0), opacity=1):
"""Paste the given image at the given position
"""
if not opacity:
return
x, y = pos

try:
Expand All @@ -73,10 +84,30 @@ def draw_image(self, image, pos=(0, 0)):
pil_image = parent.pil_image
except AttributeError:
input = BytesIO(parent._repr_png_())
pil_image = Image.open(input)
pil_image = Image.open(input).convert('RGBA')
if crop:
pil_image = pil_image.crop((image.x, image.y,
image.x + image.width,
image.y + image.height))

self.pil_image.paste(pil_image, (x, y))
if opacity == 1:
alpha_channel = pil_image
self.pil_image.paste(pil_image, (x, y), mask=alpha_channel)
else:
# Create temporary image the same size as the canvas
bigger_image = Image.new('RGBA',
(self.width, self.height),
color=(0, 0, 0, 0))
# Blit into it
bigger_image.paste(pil_image, (x, y))
# Reduce its alpha
bands = bigger_image.split()
alpha_channel = bands[3]
alpha_channel = alpha_channel.point(
lambda x: int(x * opacity))
bigger_image = Image.merge('RGBA', bands[:3] + (alpha_channel, ))
# Finally, blit it to the canvas
self.pil_image = Image.alpha_composite(
self.pil_image,
bigger_image)
# Thanks, PIL, for making this so easy!
6 changes: 4 additions & 2 deletions tmxlib/draw.py
Expand Up @@ -35,9 +35,11 @@ class DrawImageCommand(DrawCommand):
"""
x, y = helpers.unpacked_properties('pos')

def __init__(self, image, pos=(0, 0)):
def __init__(self, image, pos=(0, 0), opacity=1):
self.image = image
self.pos = pos
self.opacity = opacity

def draw(self, canvas):
canvas.draw_image(self.image, self.pos)
canvas.draw_image(self.image, self.pos,
opacity=self.opacity)
10 changes: 8 additions & 2 deletions tmxlib/image.py
Expand Up @@ -30,8 +30,14 @@ def open(filename, trans=None, size=None):
:param filename: Name of the file to load the image from
:param trans:
Optional color that should be rendered as transparent
(this is not implemented yet)
Optional color that should be loaded as transparent
.. note::
Currently, loading images that use color-key transparency
is very inefficient.
If possible, use the alpha channel instead.
:param size:
Optional (width, height) tuple.
If specified, the file will not be read from disk when the image size
Expand Down
10 changes: 8 additions & 2 deletions tmxlib/image_base.py
Expand Up @@ -106,7 +106,13 @@ class Image(ImageBase, fileio.ReadWriteBase):
.. attribute:: trans
A color key used for transparency (currently not implemented)
A color key used for transparency
.. note::
Currently, loading images that use color-key transparency
is very inefficient.
If possible, use the alpha channel instead.
Images support indexing (``img[x, y]``); see
:meth:`tmxlib.image_base.ImageBase.__getitem__`
Expand All @@ -119,10 +125,10 @@ class Image(ImageBase, fileio.ReadWriteBase):
top_left = 0, 0

def __init__(self, data=None, trans=None, size=None, source=None):
self.trans = trans
self._data = data
self.source = source
self._size = size
self.trans = trans

@property
def size(self):
Expand Down
35 changes: 31 additions & 4 deletions tmxlib/image_pil.py
Expand Up @@ -19,16 +19,17 @@ def load_image(self):
"""Load the image from self.data, and set self.size
"""
try:
self._pil_image
self._pil_image_original
return self.size
except AttributeError:
self._pil_image = Image.open(BytesIO(self.data))
self._pil_image = self._pil_image.convert('RGBA')
w, h = self._pil_image.size
pil_image = Image.open(BytesIO(self.data))
pil_image = pil_image.convert('RGBA')
w, h = pil_image.size
if self._size:
assert (w, h) == self._size
else:
self._size = w, h
self._pil_image_original = pil_image
return w, h

@property
Expand All @@ -37,8 +38,34 @@ def pil_image(self):
return self._pil_image
except AttributeError:
self.load_image()
pil_image = self._pil_image_original
if self.trans:
pil_image = pil_image.copy()
datas = pil_image.getdata()
new_data = []
xtrans = tuple(int(n * 255) for n in self.trans)
for item in datas:
itpl = tuple(item)
if itpl[:3] == xtrans:
new_data.append(itpl[:3] + (0,))
else:
new_data.append(item)
pil_image.putdata(new_data)
self._pil_image = pil_image
return self._pil_image

@property
def trans(self):
return self._trans

@trans.setter
def trans(self, new_trans):
self._trans = new_trans
try:
del self._pil_image
except AttributeError:
pass

def get_pixel(self, x, y):
x, y = self._wrap_coords(x, y)
return tuple(v / 255 for v in self.pil_image.getpixel((x, y)))
Expand Down
49 changes: 45 additions & 4 deletions tmxlib/image_png.py
@@ -1,25 +1,39 @@

from __future__ import division

from six import BytesIO
import itertools

import six
from six import BytesIO
import png
from array import array

import tmxlib
import tmxlib.image_base


def _grouper(iterable, n):
"Collect data into fixed-length chunks, ignoring extras at the end"
# grouper('ABCDEFG', 3) --> ABC DEF
args = [iter(iterable)] * n
if six.PY3:
izip = zip
else:
izip = itertools.izip
return izip(*args)


class PngImage(tmxlib.image_base.Image):
def load_image(self):
"""Load the image from self.data, and set self.size
"""
try:
self._image_data
self._image_data_original
return self.size
except AttributeError:
reader = png.Reader(bytes=self.data).asRGBA8()
w, h, data, meta = reader
self._image_data = tuple(data)
self._image_data_original = tuple(data)
if self._size:
assert (w, h) == self._size
else:
Expand All @@ -32,8 +46,33 @@ def image_data(self):
return self._image_data
except AttributeError:
self.load_image()
data = self._image_data_original
if self.trans:
xtrans = tuple(int(n * 255) for n in self.trans[:3])
new_data = []
for line in data:
new_data.append(array(
'B',
itertools.chain.from_iterable(
v[:3] + (0,) if tuple(v[:3]) == xtrans else v
for v in _grouper(line, 4))))
self._image_data = new_data
else:
self._image_data = data
return self._image_data

@property
def trans(self):
return self._trans

@trans.setter
def trans(self, new_trans):
self._trans = new_trans
try:
del self._image_data
except AttributeError:
pass

def get_pixel(self, x, y):
x, y = self._wrap_coords(x, y)
return tuple(v / 255 for v in self.image_data[y][x * 4:(x + 1) * 4])
Expand All @@ -43,7 +82,9 @@ def _repr_png_(self, _crop_box=None):
See: http://ipython.org/ipython-doc/stable/config/integrating.html
"""
if _crop_box:
if _crop_box or self.trans:
if not _crop_box:
_crop_box = 0, 0, self.width, self.height
left, up, right, low = _crop_box
data = [l[left * 4:right * 4] for l in self.image_data[up:low]]
out = BytesIO()
Expand Down
1 change: 1 addition & 0 deletions tmxlib/layer.py
Expand Up @@ -248,6 +248,7 @@ def generate_draw_commands(self):
yield draw.DrawImageCommand(
image=tile.image,
pos=(tile.pixel_x, tile.pixel_y - tile.pixel_height),
opacity=self.opacity,
)

def _repr_png_(self):
Expand Down
Binary file added tmxlib_test/data/colorcorners-mid-alpha.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tmxlib_test/data/colorcorners-mid-nored.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tmxlib_test/data/colorcorners-mid-noyellow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 7 additions & 6 deletions tmxlib_test/image_to_term.py
@@ -1,4 +1,4 @@
from __future__ import division
from __future__ import division, unicode_literals


def term256color(r, g, b, a):
Expand All @@ -10,9 +10,10 @@ def f(v):
return 5
else:
return round(v)
r = f(r / a * 6)
g = f(g / a * 6)
b = f(b / a * 6)
term = a / 256 / 256 * 6
r = f(r * term)
g = f(g * term)
b = f(b * term)
return int(16 + r * 36 + g * 6 + b)


Expand All @@ -33,10 +34,10 @@ def image_to_term256(pil_image):
for y in range(height // 2):
try:
for x in range(width):
result.append(u'\033[48;5;%dm\033[38;5;%dm' % (
result.append('\033[48;5;%dm\033[38;5;%dm' % (
term256color(*im.getpixel((x, y * 2))),
term256color(*im.getpixel((x, y * 2 + 1)))))
result.append(u'\N{LOWER HALF BLOCK}')
result.append('\N{LOWER HALF BLOCK}')
finally:
result.append('\033[0m\n')
return ''.join(result)

0 comments on commit de05149

Please sign in to comment.