Here is a slight modification of SetPixel to make a nicely functioning multi-pixel version: SetPixels. It is working for me and is very, very fast compared with repeating calls to SetPixel (or using Fx).
Like SetPixel, though, it is only designed for the usual channel families (RGB, etc.).
The call is identical to SetPixel, except for the following.
(a) The array of expected floats (normalized quanta) should have length equal to (region width) * (region height) * (number of channels).
(b) The call accepts the region's x, y, width, and height or an equivalent geom string.
Incidentally, I don't understand why SetPixel has a 'geom' key when the width and height components of that make no sense. Also, SetPixel accepts an undocumented 'normalize' key (normalized values are expected by default), which I left in the SetPixels version.
You can call it like so --- just fill an array with appropriately sequenced quanta.
Code: Select all
# example: two channels, a 3x2 region of such pre-prepared pixels, to start at the (10,12) coordinate
@pixels = ($r1, $g1, $r2, $g2, $r3, $g3, $r4, $g4, $r5, $g5, $r6, $g6);
$image->SetPixels(x => 10, y => 12, width => 3, height => 2, channel => "RG", color => \@pixels);
If you pass too few quanta it will just process those you gave it with no error. Too many quanta will also give no error; only those that fill the region will be used. That behavior could be improved, I guess.
Anyway, here it is.
The comments (marked by faux <RDM></RDM> tags) show where something was added or (in just one line) deleted.
I don't know how to contribute this through any Git-bidness.
Code: Select all
#
###############################################################################
# #
# #
# #
# S e t P i x e l s #
# #
# #
# #
###############################################################################
#
#
void
SetPixels(ref,...)
Image::Magick::Q16HDRI ref = NO_INIT
ALIAS:
setpixel = 1
setPixel = 2
PPCODE:
{
AV
*av;
char
*attribute;
ChannelType
channel,
channel_mask;
ExceptionInfo
*exception;
Image
*image;
MagickBooleanType
normalize;
RectangleInfo
region;
register ssize_t
i;
register Quantum
*q;
ssize_t
option;
struct PackageInfo
*info;
SV
*perl_exception,
*reference; /* reference is the SV* of ref=SvIV(reference) */
PERL_UNUSED_VAR(ref);
PERL_UNUSED_VAR(ix);
exception=AcquireExceptionInfo();
perl_exception=newSVpv("",0);
reference=SvRV(ST(0));
av=(AV *) reference;
info=GetPackageInfo(aTHX_ (void *) av,(struct PackageInfo *) NULL,
exception);
image=SetupList(aTHX_ reference,&info,(SV ***) NULL,exception);
if (image == (Image *) NULL)
{
ThrowPerlException(exception,OptionError,"NoImagesDefined",
PackageName);
goto PerlException;
}
av=(AV *) NULL;
normalize=MagickTrue;
region.x=0;
region.y=0;
region.width=image->columns;
region.height=1;
if (items == 1)
(void) ParseAbsoluteGeometry(SvPV(ST(1),na),®ion);
channel=DefaultChannels;
for (i=2; i < items; i+=2)
{
attribute=(char *) SvPV(ST(i-1),na);
switch (*attribute)
{
case 'C':
case 'c':
{
if (LocaleCompare(attribute,"channel") == 0)
{
ssize_t
option;
option=ParseChannelOption(SvPV(ST(i),na));
if (option < 0)
{
ThrowPerlException(exception,OptionError,"UnrecognizedType",
SvPV(ST(i),na));
return;
}
channel=(ChannelType) option;
break;
}
if (LocaleCompare(attribute,"color") == 0)
{
if (SvTYPE(ST(i)) != SVt_RV)
{
char
message[MagickPathExtent];
(void) FormatLocaleString(message,MagickPathExtent,
"invalid %.60s value",attribute);
ThrowPerlException(exception,OptionError,message,
SvPV(ST(i),na));
}
av=(AV *) SvRV(ST(i));
break;
}
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
case 'g':
case 'G':
{
if (LocaleCompare(attribute,"geometry") == 0)
{
(void) ParseAbsoluteGeometry(SvPV(ST(i),na),®ion);
break;
}
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
# <RDM: added the following>
case 'h':
case 'H':
{
if (LocaleCompare(attribute,"height") == 0)
{
region.height=SvIV(ST(i));
break;
}
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
# </RDM>
case 'N':
case 'n':
{
if (LocaleCompare(attribute,"normalize") == 0)
{
option=ParseCommandOption(MagickBooleanOptions,MagickFalse,
SvPV(ST(i),na));
if (option < 0)
{
ThrowPerlException(exception,OptionError,"UnrecognizedType",
SvPV(ST(i),na));
break;
}
normalize=option != 0 ? MagickTrue : MagickFalse;
break;
}
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
# <RDM: added>
case 'w':
case 'W':
{
if (LocaleCompare(attribute,"width") == 0)
{
region.width=SvIV(ST(i));
break;
}
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
# </RDM>
case 'x':
case 'X':
{
if (LocaleCompare(attribute,"x") == 0)
{
region.x=SvIV(ST(i));
break;
}
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
case 'y':
case 'Y':
{
if (LocaleCompare(attribute,"y") == 0)
{
region.y=SvIV(ST(i));
break;
}
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
default:
{
ThrowPerlException(exception,OptionError,"UnrecognizedAttribute",
attribute);
break;
}
}
}
(void) SetImageStorageClass(image,DirectClass,exception);
channel_mask=SetImageChannelMask(image,channel);
#<RDM: replace the following line ...>
# q=GetAuthenticPixels(image,region.x,region.y,1,1,exception);
# </RDM>
# <RDM: ... with the following line.>
q=GetAuthenticPixels(image, region.x, region.y, region.width, region.height, exception);
# </RDM>
if ((q == (Quantum *) NULL) || (av == (AV *) NULL) ||
(SvTYPE(av) != SVt_PVAV))
PUSHs(&sv_undef);
else
{
double
scale;
register ssize_t
i;
# <RDM: added j, jmax for some accounting>
register ssize_t
j, jmax;
# </RDM
i=0;
# <RDM>
j=0;
jmax = region.width * region.height;
# </RDM>
scale=1.0;
if (normalize != MagickFalse)
scale=QuantumRange;
# <RDM>
# Loop until array of color bytes or number of pixels (times channels) in region is used up.
# (Should an error be thrown if those are not the same?)
while ((j < jmax) && (i < av_len(av)))
{
# </RDM>
if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
(i <= av_len(av)))
{
SetPixelRed(image,ClampToQuantum(scale*SvNV(*(
av_fetch(av,i,0)))),q);
i++;
}
if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
(i <= av_len(av)))
{
SetPixelGreen(image,ClampToQuantum(scale*SvNV(*(
av_fetch(av,i,0)))),q);
i++;
}
if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
(i <= av_len(av)))
{
SetPixelBlue(image,ClampToQuantum(scale*SvNV(*(
av_fetch(av,i,0)))),q);
i++;
}
if ((((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
(image->colorspace == CMYKColorspace)) && (i <= av_len(av)))
{
SetPixelBlack(image,ClampToQuantum(scale*
SvNV(*(av_fetch(av,i,0)))),q);
i++;
}
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
(i <= av_len(av)))
{
SetPixelAlpha(image,ClampToQuantum(scale*
SvNV(*(av_fetch(av,i,0)))),q);
i++;
}
# <RDM>
j++;
q += image->number_channels;
}
# end while
# </RDM>
(void) SyncAuthenticPixels(image,exception);
}
(void) SetImageChannelMask(image,channel_mask);
PerlException:
InheritPerlException(exception,perl_exception);
exception=DestroyExceptionInfo(exception);
SvREFCNT_dec(perl_exception);
}
I tried to make as few mods as possible to SetPixel for this.
Rick