rmabry wrote:
When giving the 6-element affine matrix in PerlMagick, the last element is not read
I should also mention that for arrays of length 1 through 6, the affine matrix is not properly formed because the last element is not read. (Giving it a bogus 7-element array will cause the first 6 elements to be read, which would be a work-around if such a thing is left unfixed.) The array is initialized to the identity array [1,0,0,1,0,0] and then the elements of the 6-member array are read in over that (with the caveat above). This can lead to some very
naughty behavior, indeed! (There's a pun of size zero in there.) For instance,
$image->AffineTransform(affine => [1,1,1,2]);
actually results in the matrix [1,1,1,1,0,0] being produced because only the [1,1,1] is copied over the [1,0,0,1,0,0]. That resulting matrix produces a transform that is "singular" (its determinant is zero for those of you who recall such things) and the program hangs. (Or at least it goes off for a long time and I kill it.)
My feeling:
1. There really should be a check for a singular matrix in any case.
2. The bug itself should be fixed!
3. But instead of reading *up to* six elements as the code now attempts to do, it should only be legal to read 4 or 6 matrix elements. If 4 are read the remaining two (tx and ty) can be assumed zero, i.e., no translation.
The following seems to work for me in Magick.xs, circa line 7378:
Code: Select all
case 75: /* AffineTransform */
{
DrawInfo
*draw_info;
draw_info=CloneDrawInfo(info ? info->image_info : (ImageInfo *) NULL,
(DrawInfo *) NULL);
if (attribute_flag[0] != 0)
{
AV
*av;
av=(AV *) argument_list[0].array_reference;
/* getrid of this...
if (av_len(av) >= 1)
draw_info->affine.sx=(double) SvNV(*(av_fetch(av,0,0)));
if (av_len(av) >= 2)
draw_info->affine.rx=(double) SvNV(*(av_fetch(av,1,0)));
if (av_len(av) >= 3)
draw_info->affine.ry=(double) SvNV(*(av_fetch(av,2,0)));
if (av_len(av) >= 4)
draw_info->affine.sy=(double) SvNV(*(av_fetch(av,3,0)));
if (av_len(av) >= 5)
draw_info->affine.tx=(double) SvNV(*(av_fetch(av,4,0)));
if (av_len(av) >= 6)
draw_info->affine.ty=(double) SvNV(*(av_fetch(av,5,0)));
*/
if(av_len(av) != 3 && av_len(av) != 5)
{
ThrowPerlException(&exception,OptionError,
"Affine Matrix must have 4 or 6 elements",PackageName);
goto PerlException;
}
draw_info->affine.sx=(double) SvNV(*(av_fetch(av,0,0)));
draw_info->affine.rx=(double) SvNV(*(av_fetch(av,1,0)));
draw_info->affine.ry=(double) SvNV(*(av_fetch(av,2,0)));
draw_info->affine.sy=(double) SvNV(*(av_fetch(av,3,0)));
if(fabs(draw_info->affine.sx * draw_info->affine.sy
- draw_info->affine.rx * draw_info->affine.ry) < MagickEpsilon)
{
ThrowPerlException(&exception,OptionError,
"Affine Matrix is singular",PackageName);
goto PerlException;
}
if(av_len(av) == 5 )
{
draw_info->affine.tx=(double) SvNV(*(av_fetch(av,4,0)));
draw_info->affine.ty=(double) SvNV(*(av_fetch(av,5,0)));
}
}
/* end changes */
if (attribute_flag[6] != 0)
/* etc. */
Rick