Skip to content
Browse files

add the combine method

  • Loading branch information...
1 parent 220ccda commit b47464c19c81e26fb5c8086d66f05c012a6c36cc Tony Cook committed
Showing with 319 additions and 1 deletion.
  1. +4 −0 Changes
  2. +45 −0 Imager.pm
  3. +41 −0 Imager.xs
  4. +3 −0 MANIFEST
  5. +1 −1 Makefile.PL
  6. +81 −0 combine.im
  7. +3 −0 imager.h
  8. +40 −0 lib/Imager/Transformations.pod
  9. +101 −0 t/t63combine.t
View
4 Changes
@@ -13,6 +13,10 @@ Imager 0.79 - unreleased
- add wiggle.pl sample, as suggested by Dan Oppenheim.
+ - add the combine() method to combine channels from multiple source
+ images into a new image
+ https://rt.cpan.org/Ticket/Display.html?id=11872
+
Bug fixes:
- treat the co-efficients for convert() as doubles instead of floats.
View
45 Imager.pm
@@ -3466,6 +3466,46 @@ sub convert {
return $new;
}
+# combine channels from multiple input images, a class method
+sub combine {
+ my ($class, %opts) = @_;
+
+ my $src = delete $opts{src};
+ unless ($src) {
+ $class->_set_error("src parameter missing");
+ return;
+ }
+ my @imgs;
+ my $index = 0;
+ for my $img (@$src) {
+ unless (eval { $img->isa("Imager") }) {
+ $class->_set_error("src must contain image objects");
+ return;
+ }
+ unless ($img->{IMG}) {
+ $class->_set_error("empty input image");
+ return;
+ }
+ push @imgs, $img->{IMG};
+ }
+ my $result;
+ if (my $channels = delete $opts{channels}) {
+ $result = i_combine(\@imgs, $channels);
+ }
+ else {
+ $result = i_combine(\@imgs);
+ }
+ unless ($result) {
+ $class->_set_error($class->_error_as_msg);
+ return;
+ }
+
+ my $img = $class->new;
+ $img->{IMG} = $result;
+
+ return $img;
+}
+
# general function to map an image through lookup tables
@@ -4195,6 +4235,9 @@ circle() - L<Imager::Draw/circle> - draw a filled circle
colorcount() - L<Imager::Draw/colorcount> - the number of colors in an
image's palette (paletted images only)
+combine() - L<Imager::Transformations/combine> - combine channels from one or
+more images.
+
combines() - L<Imager::Draw/combines> - return a list of the different
combine type keywords
@@ -4399,6 +4442,8 @@ boxes, drawing - L<Imager::Draw/box>
changes between image - L<Imager::Filters/"Image Difference">
+channels, combine into one image - L<Imager::Transformations/combine>
+
color - L<Imager::Color>
color names - L<Imager::Color>, L<Imager::Color::Table>
View
41 Imager.xs
@@ -1757,6 +1757,47 @@ i_compose_mask(out, src, mask, out_left, out_top, src_left, src_top, mask_left,
int combine
double opacity
+Imager::ImgRaw
+i_combine(src_av, channels_av = NULL)
+ AV *src_av
+ AV *channels_av
+ PREINIT:
+ i_img **imgs = NULL;
+ STRLEN in_count;
+ int *channels = NULL;
+ int i;
+ SV **psv;
+ IV tmp;
+ CODE:
+ in_count = av_len(src_av) + 1;
+ if (in_count > 0) {
+ imgs = mymalloc(sizeof(i_img*) * in_count);
+ channels = mymalloc(sizeof(int) * in_count);
+ for (i = 0; i < in_count; ++i) {
+ psv = av_fetch(src_av, i, 0);
+ if (!psv || !*psv || !sv_derived_from(*psv, "Imager::ImgRaw")) {
+ myfree(imgs);
+ myfree(channels);
+ croak("imgs must contain only images");
+ }
+ tmp = SvIV((SV*)SvRV(*psv));
+ imgs[i] = INT2PTR(i_img*, tmp);
+ if (channels_av &&
+ (psv = av_fetch(channels_av, i, 0)) != NULL &&
+ *psv) {
+ channels[i] = SvIV(*psv);
+ }
+ else {
+ channels[i] = 0;
+ }
+ }
+ }
+ RETVAL = i_combine(imgs, channels, in_count);
+ myfree(imgs);
+ myfree(channels);
+ OUTPUT:
+ RETVAL
+
undef_int
i_flipxy(im, direction)
Imager::ImgRaw im
View
3 MANIFEST
@@ -170,6 +170,7 @@ apidocs.perl Build lib/Imager/APIRef.pm
bigtest.perl Library selection tester
bmp.c Reading and writing Windows BMP files
color.c Color translation and handling
+combine.im Channel combine
compose.im
conv.im
convert.im
@@ -311,6 +312,7 @@ samples/samp-tags.cgi Demonstrate image upload via a HTML form
samples/samp-tags.html Form for samp-tags.cgi
samples/slant_text.pl Using $font->transform() to slant text
samples/tk-photo.pl
+samples/wiggle.pl "Wiggle" stereoscopy
scale.im Newer scaling code
spot.perl For making an ordered dither matrix from a spot function
stackmach.c
@@ -349,6 +351,7 @@ t/t57infix.t
t/t58trans2.t
t/t59assem.t
t/t61filters.t
+t/t63combine.t Test combine() method
t/t64copyflip.t Test copy, flip, rotate, matrix_transform
t/t65crop.t
t/t66paste.t
View
2 Makefile.PL
@@ -157,7 +157,7 @@ if ($^O eq 'hpux') { $OSLIBS .= ' -ldld'; }
if (defined $Config{'d_dlsymun'}) { $OSDEF .= ' -DDLSYMUN'; }
my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o
- log.o gaussian.o conv.o pnm.o raw.o feat.o font.o
+ log.o gaussian.o conv.o pnm.o raw.o feat.o font.o combine.o
filters.o dynaload.o stackmach.o datatypes.o
regmach.o trans2.o quant.o error.o convert.o
map.o tags.o palimg.o maskimg.o img16.o rotate.o
View
81 combine.im
@@ -0,0 +1,81 @@
+/*
+=head1 NAME
+
+combine.im - combining channels into an image
+
+=head1 SYNOPSIS
+
+ out = i_combine(imgs, channels, count);
+
+=head1 DESCRIPTION
+
+Combines channels from the input images into an output image.
+
+=over
+
+=cut
+*/
+
+#include "imager.h"
+
+i_img *
+i_combine(i_img **imgs, const int *channels, int in_count) {
+ i_img *out = NULL;
+ int maxbits = 0;
+ i_img *maximg = NULL;
+ int i;
+ i_img_dim width, height;
+ i_img_dim x, y;
+
+ i_clear_error();
+ if (in_count <= 0) {
+ i_push_error(0, "At least one image must be supplied");
+ return NULL;
+ }
+ if (in_count > MAXCHANNELS) {
+ i_push_errorf(0, "Maximum of %d channels, you supplied %d",
+ MAXCHANNELS, in_count);
+ return NULL;
+ }
+
+ width = imgs[0]->xsize;
+ height = imgs[0]->ysize;
+ for (i = 0; i < in_count; ++i) {
+ if (imgs[i]->bits > maxbits) {
+ maximg = imgs[i];
+ maxbits = maximg->bits;
+ }
+ if (imgs[i]->xsize < width)
+ width = imgs[i]->xsize;
+ if (imgs[i]->ysize < height)
+ height = imgs[i]->ysize;
+ if (channels[i] < 0) {
+ i_push_error(0, "Channel numbers must be zero or positive");
+ return NULL;
+ }
+ if (channels[i] >= imgs[i]->channels) {
+ i_push_errorf(0, "Channel %d for image %d is too high (%d channels)",
+ channels[i], i, imgs[i]->channels);
+ return NULL;
+ }
+ }
+
+ out = i_sametype_chans(maximg, width, height, in_count);
+ if (!out)
+ return NULL;
+#code maxbits <= i_8_bits
+ IM_SAMPLE_T *in_row = mymalloc(sizeof(IM_SAMPLE_T) * width);
+ IM_COLOR *out_row = mymalloc(sizeof(IM_COLOR) * width);
+
+ for (y = 0; y < height; ++y) {
+ for (i = 0; i < in_count; ++i) {
+ IM_GSAMP(imgs[i], 0, width, y, in_row, channels + i, 1);
+ for (x = 0; x < width; ++x)
+ out_row[x].channel[i] = in_row[x];
+ }
+ IM_PLIN(out, 0, width, y, out_row);
+ }
+#/code
+
+ return out;
+}
View
3 imager.h
@@ -190,6 +190,9 @@ i_compose(i_img *out, i_img *src,
int out_left, int out_top, int src_left, int src_top,
int width, int height, int combine, double opacity);
+extern i_img *
+i_combine(i_img **src, const int *channels, int in_count);
+
undef_int i_flipxy (i_img *im, int direction);
extern i_img *i_rotate90(i_img *im, int degrees);
extern i_img *i_rotate_exact(i_img *im, double amount);
View
40 lib/Imager/Transformations.pod
@@ -48,6 +48,11 @@ Imager::Transformations - Simple transformations of one image into another.
[ 1, 0, 0 ],
[ 0, 0, 1 ] ]);
+ # build an image using channels from multiple input images
+ $new = $img->combine(src => [ $im1, $im2, $im3 ]);
+ $new = $img->combine(src => [ $im1, $im2, $im3 ],
+ channels => [ 2, 1, 0 ]);
+
# limit the range of red channel from 0..255 to 0..127
@map = map { int( $_/2 } 0..255;
$img->map( red=>\@map );
@@ -867,6 +872,41 @@ alpha channel:
[ 0, 0, 0, 0.5 ],
]);
+=item combine
+X<combine>
+
+Combine channels from one or more input images into a new image.
+
+Parameters:
+
+=over
+
+=item *
+
+C<src> - a reference to an array of input images. There must be at least
+one input image. A given image may appear more than once in C<src>.
+
+=item *
+
+C<channels> - a reference to an array of channels corresponding to the
+source images. If C<channels> is not supplied then the first channel
+from each input image is used. If the array referenced by C<channels>
+is shorter than that referenced by C<src> then the first channel is
+used from the extra images.
+
+=back
+
+ # make an rgb image from red, green, and blue images
+ my $rgb = Imager->combine(src => [ $red, $green, $blue ]);
+
+ # convert a BGR image into RGB
+ my $rgb = Imager->combine(src => [ $bgr, $bgr, $bgr ],
+ channels => [ 2, 1, 0 ]);
+
+ # add an alpha channel from another image
+ my $rgba = Imager->combine(src => [ $rgb, $rgb, $rgb, $alpha ],
+ channels => [ 0, 1, 2, 0 ]);
+
=back
=head2 Color Mappings
View
101 t/t63combine.t
@@ -0,0 +1,101 @@
+#!perl -w
+use strict;
+use Imager;
+use Test::More tests => 31;
+use Imager::Test qw/test_image test_image_double is_image/;
+
+my $test_im = test_image;
+my $test_im_dbl = test_image_double;
+
+{
+ # split out channels and put it back together
+ my $red = Imager->combine(src => [ $test_im ]);
+ ok($red, "extracted the red channel");
+ is($red->getchannels, 1, "red should be a single channel");
+ my $green = Imager->combine(src => [ $test_im ], channels => [ 1 ]);
+ ok($green, "extracted the green channel");
+ is($green->getchannels, 1, "green should be a single channel");
+ my $blue = $test_im->convert(preset => "blue");
+ ok($blue, "extracted blue (via convert)");
+
+ # put them back together
+ my $combined = Imager->combine(src => [ $red, $green, $blue ]);
+ is($combined->getchannels, 3, "check we got a three channel image");
+ is_image($combined, $test_im, "presto! check it's the same");
+}
+
+{
+ # no src
+ ok(!Imager->combine(), "no src");
+ is(Imager->errstr, "src parameter missing", "check message");
+}
+
+{
+ # bad image error
+ my $im = Imager->new;
+ ok(!Imager->combine(src => [ $im ]), "empty image");
+ is(Imager->errstr, "empty input image", "check message");
+}
+
+{
+ # not an image
+ my $im = {};
+ ok(!Imager->combine(src => [ $im ]), "not an image");
+ is(Imager->errstr, "src must contain image objects", "check message");
+}
+
+{
+ # no images
+ ok(!Imager->combine(src => []), "no images");
+ is(Imager->errstr, "At least one image must be supplied",
+ "check message");
+}
+
+{
+ # too many images
+ ok(!Imager->combine(src => [ ($test_im) x 5 ]), "too many source images");
+ is(Imager->errstr, "Maximum of 4 channels, you supplied 5",
+ "check message");
+}
+
+{
+ # negative channel
+ ok(!Imager->combine(src => [ $test_im ], channels => [ -1 ]),
+ "negative channel");
+ is(Imager->errstr, "Channel numbers must be zero or positive",
+ "check message");
+}
+
+{
+ # channel too high
+ ok(!Imager->combine(src => [ $test_im ], channels => [ 3 ]),
+ "too high channel");
+ is(Imager->errstr, "Channel 3 for image 0 is too high (3 channels)",
+ "check message");
+}
+
+{
+ # make sure we get the higher of the bits
+ my $out = Imager->combine(src => [ $test_im, $test_im_dbl ]);
+ ok($out, "make from 8 and double/sample images");
+ is($out->bits, "double", "check output bits");
+}
+
+{
+ # check high-bit processing
+ # split out channels and put it back together
+ my $red = Imager->combine(src => [ $test_im_dbl ]);
+ ok($red, "extracted the red channel");
+ is($red->getchannels, 1, "red should be a single channel");
+ my $green = Imager->combine(src => [ $test_im_dbl ], channels => [ 1 ]);
+ ok($green, "extracted the green channel");
+ is($green->getchannels, 1, "green should be a single channel");
+ my $blue = $test_im_dbl->convert(preset => "blue");
+ ok($blue, "extracted blue (via convert)");
+
+ # put them back together
+ my $combined = Imager->combine(src => [ $red, $green, $blue ]);
+ is($combined->getchannels, 3, "check we got a three channel image");
+ is_image($combined, $test_im_dbl, "presto! check it's the same");
+ is($combined->bits, "double", "and we got a double image output");
+}

0 comments on commit b47464c

Please sign in to comment.
Something went wrong with that request. Please try again.