For pedagogical purposes, I'm trying to set up a simple drawing canvas
on which to draw and display a single image with control of the
individual pixels. Efficiency is no concern. To this end, I have a
MutableImage class with getPixel and setPixel methods. Everything
seems to work fine, except when blitting the generated image, pixels
from columns on the left side for some reason also get displayed in
columns about 2/3 to the right, e.g. in a 500 pixel wide image, the
content of columns 0-100 gets erroneously drawn also in columns
~300-400 (overwriting whatever should be in those columns). My hack
around this is to save my generated image to file, then load it again
with pyglet.image.load. When I then blit my generated image newly
loaded from the file, it displays properly. So if my data is getting
saved properly, does this mean there's a bug in blitting images after
set_data?
p.s. I also get strange behavior in the saved .png. The generated
image displays correctly in my window, but the saved file doesn't
match, almost like a whole color channel is missing. Seems like
/something/ is screwy with the channels.
Here's my code:
import pyglet
class MutableImage(object):
FORMAT = 'RGB'
def __init__(self, image):
self.image = image
self.imageData = image.get_data(MutableImage.FORMAT, image.width *
len(MutableImage.FORMAT))
self.dataList = list(self.imageData)
def getPixel(self, x, y):
'''Returns tuple of (r, g, b)'''
pixelIdx = x + (y * self.image.width)
byteIdx = pixelIdx * len(MutableImage.FORMAT)
return (
ord(self.dataList[byteIdx]),
ord(self.dataList[byteIdx + 1]),
ord(self.dataList[byteIdx + 2])
)
def setPixel(self, x, y, color):
''' color is tuple of (R, G, B) as integers 0-255 '''
# ignore requests to draw out of bounds
if self.inBounds(x, y):
pixelIdx = x + (y * self.image.width)
byteIdx = pixelIdx * len(MutableImage.FORMAT)
r, g, b = color
self.dataList[byteIdx] = chr(r)
self.dataList[byteIdx + 1] = chr(g)
self.dataList[byteIdx + 2] = chr(b)
def getImage(self):
''' return the current state of data '''
data = ''.join(self.dataList)
self.image.set_data(MutableImage.FORMAT, self.image.width *
len(MutableImage.FORMAT), data)
# without hack of saving as file then loading as file, a
fraction of left side
# gets redrawn on right side for no apparent reason when we
blit the image
self.image.save('hack-around.png')
return pyglet.image.load('hack-around.png')
def inBounds(self, x, y):
if x < self.image.width and y < self.image.height:
return True
return False
def clamp(self, x, y):
if x >= self.image.width:
x = self.image.width - 1
if y >= self.image.height:
y = self.image.height - 1
return (x, y)
def paintRectangle(self, lowerLeftCorner, upperRightCorner, color):
''' coords in (x, y) tuple '''
x1, y1 = lowerLeftCorner
x2, y2 = upperRightCorner
x1, y1 = self.clamp(x1, y1)
x2, y2 = self.clamp(x2, y2)
for x in range(x1, x2 + 1):
for y in range(y1, y2 + 1):
self.setPixel(x, y, color)
def copyRegion(self, src, dest, width, height):
''' src and dest (x, y) tuples of lower left corner of regions '''
pass
def paintImage(self, dest, srcImage):
''' dest specifies lower left corner where to draw the
srcImage '''
mutableSrcImage = MutableImage(srcImage)
destX, destY = dest
for x in range(srcImage.width):
for y in range(srcImage.height):
color = mutableSrcImage.getPixel(x, y)
self.setPixel(x + destX, y + destY, color)
def displayWindow(image):
window = pyglet.window.Window(image.width, image.height,
caption='image render')
@window.event
def on_draw():
image.blit(0, 0)
pyglet.app.run()
mi = MutableImage(pyglet.image.create(500, 900))
mi.paintRectangle((100, 0), (500, 900), (0, 255, 0))
for x in range(50, 500):
mi.setPixel(x, 850, (255, 0, 0))
for x in range(50, 500):
mi.setPixel(x, 450, (255, 0, 0))
for x in range(50, 500):
mi.setPixel(x, 250, (255, 0, 0))
mi.paintRectangle((0, 0), (100, 100), (0, 255, 255))
mi.paintRectangle((450, 850), (500, 900), (0, 255, 255))
displayWindow(mi.getImage())