[IMAGE-SIG] Dib GDI leak/fix + contributed plugin
Roger Burnham
rburnham@cri-inc.com
Sat, 2 May 1998 15:49:27 -5000
Hi,
version: Imaging-0.3a3
I've found that deleting an ImageWin.Dib instance does not free the
GDI resources created with:
dib->bitmap = CreateDIBSection(...)
In the app I'm developing, this caused the GDI resources to be
consumed after acquiring/deleting ~200 images.
The fix is
------------------dib.h------------------:
...
struct ImagingDIBInstance {
...
HGDIOBJ origobj; /* remember the original selected object */
};
...
------------------dib.h------------------:
------------------dib.c------------------:
in ImagingNewDIB(...)
change:
SelectObject(dib->dc, dib->bitmap);
to:
dib->origobj = SelectObject(dib->dc, dib->bitmap);
change ImagingDeleteDIB(...) to be:
void
ImagingDeleteDIB(ImagingDIB dib)
{
/* Clean up */
if (dib->palette)
DeleteObject(dib->palette);
if (dib->dc) {
SelectObject(dib->dc, dib->origobj);
DeleteDC(dib->dc);
}
if (dib->bitmap)
DeleteObject(dib->bitmap);
free(dib->info);
}
E.g. the problem was that the bitmap was freed while still selected
in the device context.
Also, here is a module I wrote to present a "stack" of bmp images as
a single image object. In the app alluded to, we collect a set of four
images, and calculate a fifth and sixth plane. These are stored as a unit,
and when displayed, the user can flip thru the planes with the up/down keys.
------------------BMPStackImagePlugin.py------------------:
'''
Imaging module plugin to handle a stack of BMP images. Weve also added
a persistent dictionary attached to each image.
'''
import os
import Image, BmpImagePlugin
import cPickle, cStringIO
_bmpStackPrefix = 'BMPSTK'
def _accept(prefix):
'''Image.open will call to see if we can handle prefix.
'''
return prefix[:6] == _bmpStackPrefix
class BMPStackFile(BmpImagePlugin.BmpImageFile):
'''A stack of BmpImageFile images.
'''
format = _bmpStackPrefix
format_description = "Stack of Windows Bitmaps"
def _open(self):
'''See if our magic is at the file head. If so, reposition at the
file start.
'''
s = self.fp.read(6)
if s[:6] != _bmpStackPrefix:
raise SyntaxError, "Not a BMP stack"
self.frame = -1
self.fp2 = self.fp
self.offset = self.fp.tell()
self.seek(0)
def seek(self, frame):
'''Read the next image in the file.
'''
if frame != self.frame + 1:
raise ValueError, "cannot seek to frame %d" % frame
self.frame = frame
self.fp = self.fp2
self.fp.seek(self.offset)
try:
self.info = cPickle.Unpickler(self.fp).load()
except:
raise SyntaxError, "file does not contain an info dict"
s = self.fp.read(14)
if s[:2] != "BM":
raise SyntaxError, "stack does not contain a BM image"
self._bitmap()
offset = self.tile[0][2]
stride = self.tile[0][3][1]
self.offset = offset + stride*self.size[1]
def tell(self):
'''Return which image number we are positioned at.
'''
return self.frame
def _save(im, fp, filename):
'''Image.save will call us to save the stack to a file.
'''
fp.write(_bmpStackPrefix)
params = im.encoderinfo
for img in im:
infoFd = cStringIO.StringIO()
cPickle.Pickler(infoFd, 1).dump(img.info)
fp.write(infoFd.getvalue())
img.params = params
img.encoderconfig = ()
BmpImagePlugin._save(img, fp, filename)
Image.register_open(BMPStackFile.format, BMPStackFile, _accept)
Image.register_save(BMPStackFile.format, _save)
Image.register_extension(BMPStackFile.format, '.bmpstk')
class BMPStack(Image.Image):
'''Class that presents a stack of images with the same interface as
a single image. The active image is set via the member variable
"cursor".
'''
__vdict = None
__vdict_name = '_BMPStack__vdict'
def __init__(self, imageObj):
'''Given a file path, or an image object, wrap it as a stack.
'''
self.__dict__[self.__vdict_name] = {}
Image.Image.__init__(self)
if type(imageObj) == type(''):
path = imageObj
img = Image.open(path)
size = img.size
mode = img.mode
i = 1
imgs = []
while 1:
try:
tile = img.tile
if img.size != size:
raise ValueError, 'Images in stack must have same size'
next = img.convert(mode)
next.tile = tile
imgs.append(next)
img.seek(i)
i = i + 1
except:
break
else:
mode = 'L'
size = imageObj.size
img = Image.new(mode, size)
imgs = [imageObj]
path = None
self.__vdict['mode'] = mode
self.__vdict['size'] = size
self.__vdict['format'] = _bmpStackPrefix
self.__vdict['filename'] = path
self.__vdict['cursor'] = 0
self.__vdict['len'] = len(imgs)
self.__vdict['imgs'] = imgs
def __del__(self):
del self.__vdict
def __getattr__(self, name):
'''Handle image.attr references. If attr==cursor, return the
"active" image plane number (0...). If the active image has
the attribute, return it. Otherwise attempt to get it from
the wrapping object.
'''
if name == 'cursor':
return self.__vdict['cursor']
if self.__vdict.has_key('imgs'):
if hasattr(self.__vdict['imgs'][self.__vdict['cursor']], name):
return getattr(self.__vdict['imgs'][self.__vdict['cursor']],
name)
else:
return self.__vdict[name]
else:
return self.__vdict[name]
def __getitem__(self, i):
'''Handle stack indexing, e.g. image[i] references, but do not change
the cursor location.
'''
return self.__vdict['imgs'][i]
def __len__(self):
'''Return the number of images in the stack.
'''
return len(self.__vdict['imgs'])
def __setattr__(self, name, value):
'''Handle image.attr = value references. Look for cursor setting and
wrap the value into the allowable range. If the stack does not exist
yet, set the attr value in the wrapper context, otherwise, set the
attribute for the current image (e.g. image[cursor].name = value.
'''
if name == 'cursor':
if value >= self.__vdict['len']:
self.__vdict['cursor'] = 0
elif value < 0:
self.__vdict['cursor'] = self.__vdict['len'] - 1
else:
self.__vdict['cursor'] = value
return
if self.__vdict.has_key('imgs'):
setattr(self.__vdict['imgs'][self.__vdict['cursor']], name, value)
else:
self.__vdict[name] = value
def __setitem__(self, index, img):
'''Handle image[i] = img references. If set beyond the current length,
fill missing values with empyt strings (NEED to rethink this...).
'''
imgs = len(self.__vdict['imgs'])
if index >= imgs:
diff = index - imgs + 1
while diff > 0:
self.__vdict['imgs'].append('')
diff = diff - 1
self.__vdict['imgs'][index] = img
self.__vdict['len'] = len(self.__vdict['imgs'])
def append(self, img):
'''Handle image.append(img) references in the obvious way...
'''
self.__vdict['imgs'].append(img)
self.__vdict['len'] = len(self.__vdict['imgs'])
------------------BMPStackImagePlugin.py------------------:
Cheers,
Roger Burnham
Cambridge Research & Instrumentation
80 Ashford Street
Boston, MA 02134
rburnham@cri-inc.com
www.cri-inc.com