[Image-SIG] Re: Image.putpalette() bug

Derek Simkowiak dereks@realloc.net
Wed, 30 Apr 2003 16:09:19 -0700


Derek Simkowiak wrote:
> [dereks@localhost src]$ ./wal_test.py
> Traceback (most recent call last):
>   File "./wal_test.py", line 7, in ?
>     wal_image.show()
>   File "/usr/lib/python2.2/site-packages/PIL/Image.py", line 770, in show
>     _showxv(self, title, command)
>   File "/usr/lib/python2.2/site-packages/PIL/Image.py", line 1079, in 
> _showxv
>     file = self.convert(base)._dump(format=format)
>   File "/usr/lib/python2.2/site-packages/PIL/Image.py", line 345, in _dump
>     self.load()
>   File "/usr/lib/python2.2/site-packages/PIL/Image.py", line 414, in load
>     self.im.putpalette(self.palette.rawmode, self.palette.data)
> ValueError: unrecognized image mode
> [dereks@localhost src]$

	On the trail of this bug, I may have found some more clues (but I don't 
know how to fix it).

	I think the problem of trying to do a "putpalette()" on an image that 
has self->image->mode set to "RGB" occurs here:

 >   File "/usr/lib/python2.2/site-packages/PIL/Image.py", line 1079, in
 > _showxv
 >     file = self.convert(base)._dump(format=format)

	In _imaging.c the function "convert()" does this near line 873:

-----------------------
     if (!mode) {
         /* Map palette image to full depth */
         if (!imIn->palette)
             return (Imaging) ImagingError_ModeError();
         mode = imIn->palette->mode;
     } [...]
-----------------------

	I think that "mode" is in fact NULL at this point, because of the fact 
that line 1079 of Image.py above calls "convert() without a "mode" 
argument, and it has a default value of "None".  So, back in the C code, 
I think "mode" gets set to "imIn->palette->mode" in the code above, 
which is "RGB" because the mode of the palette is RGB.

	Then, with "mode" set to "RGB", we get to:

     if (strcmp(imIn->mode, "P") == 0)
         return frompalette(imOut, imIn, mode);

	Since our imIn is our Image with mode "P", frompalette() gets called, 
and does this:

     imOut = ImagingNew2(mode, imOut, imIn);
     [...]
     return imOut;

	That is, our call to convert() results in a call to frompalette() which 
returns a new Image that has "mode" set to "RGB".  Then, back at line 
1079 of Image.py:

 >   File "/usr/lib/python2.2/site-packages/PIL/Image.py", line 1079, in
 > _showxv
 >     file = self.convert(base)._dump(format=format)

	...we call _dump() on the result of that convert().  _dump() eventually 
tries to self.load() the returned Image (which is the new Image with 
mode "RGB"), where it tries the putpalette() that dies because we're 
calling putpalette() on an image with mode "RGB" (instead of "P" or "L").

	I have not verified this hypothesis, but it might be worth checking 
out.  Another note is that at the very beginning of ImagingNew2() there 
is this test:

     if (imOut) {
         /* make sure images match */
         if (strcmp(imOut->mode, mode) != 0
             || imOut->xsize != imIn->xsize
             || imOut->ysize != imIn->ysize) {
             ImagingError_Mismatch();
             return NULL;
         }
     } [...]

	This looks to me like the test should be:

         if (strcmp(imOut->mode, inIn->mode) != 0
             || imOut->xsize != imIn->xsize
             || imOut->ysize != imIn->ysize) {
             ImagingError_Mismatch();
             return NULL;
         }

	That is, shouldn't it be a comparison between the Image modes, and not 
the output Image the passed-in mode?  Or am I totally on the wrong track?

	Anyway, as a hack if nothing else, the following change made it so my 
test script "wal_test.py" works:

[dereks@localhost PIL]$ diff ./Image.py Image-dist.py
414,415c414
<           if self.mode != "RGB":
<                   self.im.putpalette(self.palette.data, 
self.palette.rawmode)
---
 >             self.im.putpalette(self.palette.rawmode, self.palette.data)

	I do not know if this is the "correct" fix for the problem or not.


Thank You,
Derek Simkowiak