From: Tony Cook Date: Thu, 26 Mar 2015 04:36:49 +0000 (+1100) Subject: add colorchannels(), alphachannel() and colormodel() methods X-Git-Tag: v1.002~4 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/35db02fc244c096ba8e33b8220a02dadf7cacf81 add colorchannels(), alphachannel() and colormodel() methods also add model parameter to Imager->new() --- diff --git a/Imager.pm b/Imager.pm index 4db21396..4fc3243b 100644 --- a/Imager.pm +++ b/Imager.pm @@ -696,6 +696,10 @@ sub new { return; } } + elsif (%hsh) { + Imager->_set_error("new: supply xsize and ysize or a file access parameter or no parameters"); + return; + } return $self; } @@ -911,16 +915,29 @@ sub _sametype { # Sets an image to a certain size and channel number # if there was previously data in the image it is discarded +my %model_channels = + ( + gray => 1, + graya => 2, + rgb => 3, + rgba => 4, + ); + sub img_set { my $self=shift; my %hsh=(xsize=>100, ysize=>100, channels=>3, bits=>8, type=>'direct', @_); - if (defined($self->{IMG})) { - # let IIM_DESTROY destroy it, it's possible this image is - # referenced from a virtual image (like masked) - #i_img_destroy($self->{IMG}); - undef($self->{IMG}); + undef($self->{IMG}); + + if ($hsh{model}) { + if (my $channels = $model_channels{$hsh{model}}) { + $hsh{channels} = $channels; + } + else { + $self->_set_error("new: unknown value for model '$hsh{model}'"); + return; + } } if ($hsh{type} eq 'paletted' || $hsh{type} eq 'pseudo') { @@ -939,7 +956,7 @@ sub img_set { } unless ($self->{IMG}) { - $self->{ERRSTR} = Imager->_error_as_msg(); + $self->_set_error(Imager->_error_as_msg()); return; } @@ -3901,6 +3918,37 @@ sub getchannels { return i_img_getchannels($self->{IMG}); } +my @model_names = qw(unknown gray graya rgb rgba); + +sub colormodel { + my ($self, %opts) = @_; + + $self->_valid_image("colormodel") + or return; + + my $model = i_img_color_model($self->{IMG}); + + return $opts{numeric} ? $model : $model_names[$model]; +} + +sub colorchannels { + my ($self) = @_; + + $self->_valid_image("colorchannels") + or return; + + return i_img_color_channels($self->{IMG}); +} + +sub alphachannel { + my ($self) = @_; + + $self->_valid_image("alphachannel") + or return; + + return scalar(i_img_alpha_channel($self->{IMG})); +} + # Get channel mask sub getmask { @@ -4668,6 +4716,9 @@ addtag() - L - add image tags align_string() - L - draw text aligned on a point +alphachannel() - L - return the +channel index of the alpha channel (if any). + arc() - L - draw a filled arc bits() - L - number of bits per sample for the @@ -4682,9 +4733,15 @@ circle() - L - draw a filled circle close_log() - L - close the Imager debugging log. +colorchannels() - L - the number +of channels used for color. + colorcount() - L - the number of colors in an image's palette (paletted images only) +colormodel() - L - how color is +represented. + combine() - L - combine channels from one or more images. diff --git a/Imager.xs b/Imager.xs index 99e41738..6a205ba5 100644 --- a/Imager.xs +++ b/Imager.xs @@ -1720,6 +1720,22 @@ IV i_img_get_height(im) Imager::ImgRaw im +int +i_img_color_model(im) + Imager::ImgRaw im + +int +i_img_color_channels(im) + Imager::ImgRaw im + +int +i_img_alpha_channel(im) + Imager::ImgRaw im + CODE: + if (!i_img_alpha_channel(im, &RETVAL)) + XSRETURN(0); + OUTPUT: + RETVAL void i_img_is_monochrome(im) diff --git a/apidocs.perl b/apidocs.perl index 55804e04..927939a3 100644 --- a/apidocs.perl +++ b/apidocs.perl @@ -193,7 +193,12 @@ close OUT; sub make_func_list { - my @funcs = qw(i_img i_color i_fcolor i_fill_t mm_log mm_log i_img_color_channels i_img_has_alpha i_img_dim i_DF i_DFc i_DFp i_DFcp i_psamp_bits i_gsamp_bits i_psamp i_psampf); + my @funcs = + qw(i_img i_color i_fcolor i_fill_t mm_log mm_log i_color_model_t + im_context_t i_img_dim i_img_dim_u im_slot_t + i_polygon_t i_poly_fill_mode_t i_mutex_t + i_img_has_alpha i_DF i_DFc i_DFp i_DFcp i_psamp_bits i_gsamp_bits + i_psamp i_psampf); open FUNCS, "< imexttypes.h" or die "Cannot open imexttypes.h: $!\n"; my $in_struct; diff --git a/image.c b/image.c index 03dc6e2c..f280a28c 100644 --- a/image.c +++ b/image.c @@ -404,6 +404,85 @@ i_img_get_height(i_img *im) { return im->ysize; } +/* +=item i_img_color_model(im) +=category Image Information +=synopsis i_color_model_t cm = i_img_color_model(im); + +Returns the color model for the image. + +A future version of Imager will allow for images with extra channels +beyond gray/rgb and alpha. + +=cut +*/ +i_color_model_t +i_img_color_model(i_img *im) { + return (i_color_model_t)im->channels; +} + +/* +=item i_img_alpha_channel(im, &channel) +=category Image Information +=synopsis int alpha_channel; +=synopsis int has_alpha = i_img_alpha_channel(im, &alpha_channel); + +Work out the alpha channel for an image. + +If the image has an alpha channel, sets C<*channel> to the alpha +channel index and returns non-zero. + +If the image has no alpha channel, returns zero and C<*channel> is not +modified. + +C may be C. + +=cut +*/ + +int +i_img_alpha_channel(i_img *im, int *channel) { + i_color_model_t model = i_img_color_model(im); + switch (model) { + case icm_gray_alpha: + case icm_rgb_alpha: + if (channel) *channel = (int)model - 1; + return 1; + + default: + return 0; + } +} + +/* +=item i_img_color_channels(im) +=category Image Information +=synopsis int color_channels = i_img_color_channels(im); + +Returns the number of color channels in the image. For now this is +always 1 (for grayscale) or 3 (for RGB) but may be 0 in some special +cases in a future release of Imager. + +=cut +*/ + +int +i_img_color_channels(i_img *im) { + i_color_model_t model = i_img_color_model(im); + switch (model) { + case icm_gray_alpha: + case icm_rgb_alpha: + return (int)model - 1; + + case icm_gray: + case icm_rgb: + return (int)model; + + default: + return 0; + } +} + /* =item i_copyto_trans(C, C, C, C, C, C, C, C, C) diff --git a/imager.h b/imager.h index 27802866..f5bb2da5 100644 --- a/imager.h +++ b/imager.h @@ -64,6 +64,9 @@ int i_img_getmask (i_img *im); int i_img_getchannels(i_img *im); i_img_dim i_img_get_width(i_img *im); i_img_dim i_img_get_height(i_img *im); +i_color_model_t i_img_color_model(i_img *im); +int i_img_alpha_channel(i_img *im, int *channel); +int i_img_color_channels(i_img *im); /* Base functions */ diff --git a/imdatatypes.h b/imdatatypes.h index 30590ce9..dcb8e9a3 100644 --- a/imdatatypes.h +++ b/imdatatypes.h @@ -732,6 +732,54 @@ typedef struct { typedef struct i_render_tag i_render; +/* +=item i_color_model_t +=category Data Types +=order 95 + +Returned by L to indicate the color model of +the image. + +An enumerated type with the following possible values: + +=over + +=item * + +C - the image has no usable color data. In future +versions of Imager this will be returned in a few limited cases, +eg. when the source image is CMYK and the user has requested no color +translation is done. + +=item * + +C - gray scale with no alpha channel. + +=item * + +C - gray scale with an alpha channel. + +=item * + +C - RGB + +=item * + +C - RGB with an alpha channel. + +=back + +=cut +*/ + +typedef enum { + icm_unknown, + icm_gray, + icm_gray_alpha, + icm_rgb, + icm_rgb_alpha +} i_color_model_t; + #ifdef IMAGER_FORMAT_ATTR #define I_FORMAT_ATTR(format_index, va_index) \ __attribute ((format (printf, format_index, va_index))) diff --git a/imext.c b/imext.c index 98f2c457..36ba2b62 100644 --- a/imext.c +++ b/imext.c @@ -210,7 +210,12 @@ im_ext_funcs imager_function_table = i_poly_poly_aa, i_poly_poly_aa_cfill, i_poly_aa_m, - i_poly_aa_cfill_m + i_poly_aa_cfill_m, + + /* level 10 */ + i_img_alpha_channel, + i_img_color_model, + i_img_color_channels }; /* in general these functions aren't called by Imager internally, but diff --git a/imext.h b/imext.h index 6561e9a8..c288a423 100644 --- a/imext.h +++ b/imext.h @@ -243,6 +243,10 @@ extern im_ext_funcs *imager_function_ext_table; #define im_push_errorf (im_extt->f_im_push_errorf) +#define i_img_alpha_channel(im, channel) ((im_extt->f_i_img_alpha_channel)((im), (channel))) +#define i_img_color_model(im) ((im_extt->f_i_img_color_model)((im))) +#define i_img_color_channels(im) ((im_extt->f_i_img_color_channels)((im))) + #ifdef IMAGER_LOG #ifndef IMAGER_NO_CONTEXT #define mm_log(x) { i_lhead(__FILE__,__LINE__); i_loog x; } diff --git a/imexttypes.h b/imexttypes.h index 354b2113..320bea1f 100644 --- a/imexttypes.h +++ b/imexttypes.h @@ -34,7 +34,7 @@ will result in an increment of IMAGER_API_LEVEL. */ -#define IMAGER_API_LEVEL 9 +#define IMAGER_API_LEVEL 10 typedef struct { int version; @@ -266,9 +266,12 @@ typedef struct { const double *y, i_poly_fill_mode_t mode, i_fill_t *fill); - /* IMAGER_API_LEVEL 10 functions will be added here */ - + /* IMAGER_API_LEVEL 10 */ + int (*f_i_img_alpha_channel)(i_img *im, int *channel); + i_color_model_t (*f_i_img_color_model)(i_img *im); + int (*f_i_img_color_channels)(i_img *im); + /* IMAGER_API_LEVEL 11 functions will be added here */ } im_ext_funcs; #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table" diff --git a/immacros.h b/immacros.h index 0dbe5e5a..8b45f587 100644 --- a/immacros.h +++ b/immacros.h @@ -16,19 +16,7 @@ Return true if the image has an alpha channel. =cut */ -#define i_img_has_alpha(im) ((im)->channels == 2 || (im)->channels == 4) - -/* -=item i_img_color_channels(C) - -=category Image Information - -The number of channels holding color information. - -=cut -*/ - -#define i_img_color_channels(im) (i_img_has_alpha(im) ? (im)->channels - 1 : (im)->channels) +#define i_img_has_alpha(im) (i_img_alpha_channel((im), NULL)) /* =item i_psamp(im, left, right, y, samples, channels, channel_count) diff --git a/lib/Imager/APIRef.pod b/lib/Imager/APIRef.pod index 89554be1..69e1b140 100644 --- a/lib/Imager/APIRef.pod +++ b/lib/Imager/APIRef.pod @@ -114,11 +114,18 @@ Imager::APIRef - Imager's C API - reference. int channels = i_img_getchannels(img); i_img_dim width = i_img_get_width(im); i_img_dim height = i_img_get_height(im); + i_color_model_t cm = i_img_color_model(im); + int alpha_channel; + int has_alpha = i_img_alpha_channel(im, &alpha_channel); + int color_channels = i_img_color_channels(im); # Image quantization # Logging + # mutex + i_mutex_t mutex; + # Mutex functions i_mutex_t m = i_mutex_new(); i_mutex_destroy(m); @@ -387,6 +394,44 @@ A signed integer type that represents an image dimension or ordinate. May be larger than int on some platforms. +=for comment +From: File imdatatypes.h + +=item i_color_model_t + +Returned by L to indicate the color model of +the image. + +An enumerated type with the following possible values: + +=over + +=item * + +C - the image has no usable color data. In future +versions of Imager this will be returned in a few limited cases, +eg. when the source image is CMYK and the user has requested no color +translation is done. + +=item * + +C - gray scale with no alpha channel. + +=item * + +C - gray scale with an alpha channel. + +=item * + +C - RGB + +=item * + +C - RGB with an alpha channel. + +=back + + =for comment From: File imdatatypes.h @@ -1769,14 +1814,49 @@ From: File image.c =over -=item i_img_color_channels(C) +=item i_img_alpha_channel(im, &channel) + + int alpha_channel; + int has_alpha = i_img_alpha_channel(im, &alpha_channel); + +Work out the alpha channel for an image. +If the image has an alpha channel, sets C<*channel> to the alpha +channel index and returns non-zero. -The number of channels holding color information. +If the image has no alpha channel, returns zero and C<*channel> is not +modified. + +C may be C. =for comment -From: File immacros.h +From: File image.c + +=item i_img_color_channels(im) + + int color_channels = i_img_color_channels(im); + +Returns the number of color channels in the image. For now this is +always 1 (for grayscale) or 3 (for RGB) but may be 0 in some special +cases in a future release of Imager. + + +=for comment +From: File image.c + +=item i_img_color_model(im) + + i_color_model_t cm = i_img_color_model(im); + +Returns the color model for the image. + +A future version of Imager will allow for images with extra channels +beyond gray/rgb and alpha. + + +=for comment +From: File image.c =item i_img_get_height(C) @@ -1944,6 +2024,24 @@ This is an internal function called by the mm_log() macro. From: File log.c +=back + +=head2 mutex + +=over + +=item i_mutex_t +X + + i_mutex_t mutex; + +Opaque type for Imager's mutex API. + + +=for comment +From: File imdatatypes.h + + =back =head2 Mutex functions @@ -2430,6 +2528,22 @@ will change: =item * +B + +=item * + +B + +=item * + +B + +=item * + +B + +=item * + B =item * @@ -2438,6 +2552,10 @@ B =item * +B + +=item * + B diff --git a/lib/Imager/ImageTypes.pod b/lib/Imager/ImageTypes.pod index 80ac92b4..de5e2339 100644 --- a/lib/Imager/ImageTypes.pod +++ b/lib/Imager/ImageTypes.pod @@ -173,6 +173,11 @@ values are from 1 to 4. =item * +C - this overrides the value, if any, supplied for C. +This can be one of C, C, C or C. + +=item * + C - The storage type for samples in the image. Default: 8. Valid values are: @@ -286,6 +291,11 @@ parameter: my $img = Imager->new(file => $filename) or die Imager->errstr; +If none of C, C, C, C, C, C, +C or C is supplied, and other parameters I supplied +C<< Imager->new >> will return failure rather than returning an empty +image object. + =item img_set() img_set destroys the image data in the object and creates a new one @@ -321,11 +331,60 @@ value of C is undef. Same details apply as for L. =item getchannels() +XX print "Image has ",$img->getchannels(), " channels\n"; -To get the number of channels in an image C is used. +Returns the number of channels in an image. + +Note: previously the number of channels in an image mapped directly to +the color model of the image, ie a 4 channel image was always RGBA. +This may change in a future release of Imager. + +Returns an empty list if the image object is not initialized. + +=item colorchannels() +XX + +Returns the number of color channels. + +Currently this is always 1 or 3, but may be 0 for some rare images in +a future version of Imager. + +Returns an empty list if the image object is not initialized. + +=item colormodel() +XX + +Returns the color model of the image, including whether there is an +alpha channel. +By default this is returned as a string, one of C, C, +C, C or C. + +If you call C with a true numeric parameter: + + my $model = $img->colormodel(numeric => 1); + +then the color model is returned as a number, mapped as follows: + + Numeric String + ------- ------ + 0 unknown + 1 gray + 2 graya + 3 rgb + 4 rgba + +=item alphachannel() +XX + +Returns the channel index of the alpha channel of the image. + +This is 1 for grayscale images with alpha, 3 for RGB images with alpha +and will return C for all other images. + +Returns an empty list if the image object is not initialized. =item bits() @@ -340,6 +399,8 @@ channel in a pixel, 8 for a normal image, 16 for 16-bit image and # slower but more precise } +Returns an empty list if the image object is not initialized. + =item type() The type() method returns either 'direct' for direct color images or @@ -352,6 +413,8 @@ The type() method returns either 'direct' for direct color images or } } +Returns an empty list if the image object is not initialized. + =item virtual() The virtual() method returns non-zero if the image contains no actual @@ -398,6 +461,8 @@ those 2 colors are black and white, in either order. If a real bi-level organization image is ever added to Imager, this function will return true for that too. +Returns an empty list if the image object is not initialized. + =back =head2 Direct Type Images diff --git a/t/100-base/010-introvert.t b/t/100-base/010-introvert.t index 8e46f58f..a52aadd9 100644 --- a/t/100-base/010-introvert.t +++ b/t/100-base/010-introvert.t @@ -3,7 +3,7 @@ # to make sure we get expected values use strict; -use Test::More tests => 466; +use Test::More tests => 492; BEGIN { use_ok(Imager => qw(:handy :all)) } @@ -151,6 +151,10 @@ is($impal2->bits, 8, "check bits"); is($impal2->type, 'paletted', "check type"); is($impal2->getwidth, 200, "check width"); is($impal2->getheight, 201, "check height"); +is($impal2->colormodel, "rgb", "check color model (string)"); +is($impal2->colormodel(numeric => 1), 3, "check color model (numeric)"); +is($impal2->alphachannel, undef, "check alpha channel (has none)"); +is($impal2->colorchannels, 3, "check colorchannels"); { my $red_idx = $impal2->addcolors(colors=>[$red]); @@ -227,6 +231,12 @@ is($impal2->getheight, 201, "check height"); is($im->errstr, "getmask: empty input image", "check message"); is($im->setmask, undef, "can't set mask of empty image"); is($im->errstr, "setmask: empty input image", "check message"); + is($im->colorchannels, undef, "can't get colorchannels of empty image"); + is($im->errstr, "colorchannels: empty input image", "check message"); + is($im->alphachannel, undef, "can't get alphachannel of empty image"); + is($im->errstr, "alphachannel: empty input image", "check message"); + is($im->colormodel, undef, "can't get colormodel of empty image"); + is($im->errstr, "colormodel: empty input image", "check message"); } { # basic checks, 8-bit direct images @@ -1121,6 +1131,25 @@ my $psamp_outside_error = "Image position outside of image"; "check error message"); } +{ + my @tests = + ( + [ "gray", 1, undef ], + [ "graya", 1, 1 ], + [ "rgb", 3, undef ], + [ "rgba", 3, 3 ], + ); + for my $test (@tests) { + my ($model, $color_channels, $alpha) = @$test; + my $im = Imager->new(model => $model, xsize => 10, ysize => 10) + or die "Cannot create $model image:", Imager->errstr; + ok($im, "make a $model image via model"); + is($im->colormodel, $model, "check colormodel is $model"); + is($im->alphachannel, $alpha, "check alphachannel"); + is($im->colorchannels, $color_channels, "check colorchannels"); + } +} + Imager->close_log(); unless ($ENV{IMAGER_KEEP_FILES}) { diff --git a/t/450-api/100-inline.t b/t/450-api/100-inline.t index c9a1f939..9d2cd4fb 100644 --- a/t/450-api/100-inline.t +++ b/t/450-api/100-inline.t @@ -21,7 +21,7 @@ plan skip_all => "perl 5.005_04, 5.005_05 too buggy" print STDERR "Inline version $Inline::VERSION\n"; -plan tests => 117; +plan tests => 120; require Inline; Inline->import(with => 'Imager'); Inline->import("FORCE"); # force rebuild @@ -467,6 +467,25 @@ test_slots() { return 1; } +int +color_channels(Imager im) { + return i_img_color_channels(im); +} + +int +color_model(Imager im) { + return (int)i_img_color_model(im); +} + +int +alpha_channel(Imager im) { + int channel; + if (!i_img_alpha_channel(im, &channel)) + channel = -1; + + return channel; +} + EOS my $im = Imager->new(xsize=>50, ysize=>50); @@ -683,6 +702,13 @@ for my $bits (8, 16) { is($im->type, "paletted", "make sure we kept the image type"); } +{ + my $rgb = Imager->new(xsize => 10, ysize => 10); + is(color_model($rgb), 3, "check i_img_color_model() api"); + is(color_channels($rgb), 3, "check i_img_color_channels() api"); + is(alpha_channel($rgb), -1, "check i_img_alpha_channel() api"); +} + ok(test_mutex(), "call mutex APIs"); ok(test_slots(), "call slot APIs"); diff --git a/xt/x20spell.t b/xt/x20spell.t index 9661e0eb..5a861725 100644 --- a/xt/x20spell.t +++ b/xt/x20spell.t @@ -48,6 +48,7 @@ dpi eg equalities gaussian +grayscale ie infix invocant