return $self;
}
+ my $allow_partial = $input{allow_partial};
+ defined $allow_partial or $allow_partial = 0;
+
if ( $input{'type'} eq 'tiff' ) {
my $page = $input{'page'};
defined $page or $page = 0;
- # Fixme, check if that length parameter is ever needed
- $self->{IMG}=i_readtiff_wiol( $IO, -1, $page );
+ $self->{IMG}=i_readtiff_wiol( $IO, $allow_partial, $page );
if ( !defined($self->{IMG}) ) {
$self->{ERRSTR}=$self->_error_as_msg(); return undef;
}
}
if ( $input{'type'} eq 'pnm' ) {
- $self->{IMG}=i_readpnm_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
+ $self->{IMG}=i_readpnm_wiol( $IO, $allow_partial );
if ( !defined($self->{IMG}) ) {
$self->{ERRSTR}='unable to read pnm image: '._error_as_msg();
return undef;
}
if ( $input{'type'} eq 'bmp' ) {
- $self->{IMG}=i_readbmp_wiol( $IO );
+ $self->{IMG}=i_readbmp_wiol( $IO, $allow_partial );
if ( !defined($self->{IMG}) ) {
$self->{ERRSTR}=$self->_error_as_msg();
return undef;
{ "webmap", mc_web_map, },
{ "addi", mc_addi, },
{ "mediancut", mc_median_cut, },
+ { "mono", mc_mono, },
+ { "monochrome", mc_mono, },
};
static struct value_name translate_names[] =
#ifdef HAVE_LIBTIFF
Imager::ImgRaw
-i_readtiff_wiol(ig, length, page=0)
+i_readtiff_wiol(ig, allow_partial, page=0)
Imager::IO ig
- int length
+ int allow_partial
int page
void
Imager::ImgRaw
-i_readpnm_wiol(ig, length)
+i_readpnm_wiol(ig, allow_partial)
Imager::IO ig
- int length
+ int allow_partial
undef_int
Imager::IO ig
Imager::ImgRaw
-i_readbmp_wiol(ig)
+i_readbmp_wiol(ig, allow_partial=0)
Imager::IO ig
+ int allow_partial
undef_int
regmach.c
regmach.h
regops.perl
+render.im
rgb.c Reading and writing SGI rgb files
rotate.c
rubthru.im
regmach.o trans2.o quant.o error.o convert.o
map.o tags.o palimg.o maskimg.o img16.o rotate.o
bmp.o tga.o rgb.o color.o fills.o imgdouble.o limits.o hlines.o
- imext.o scale.o rubthru.o);
+ imext.o scale.o rubthru.o render.o);
$Recommends{Imager} =
{ 'Parse::RecDescent' => 0 };
New Features:
- Add mng support, pcx and aalib support.
- - Windows icon files (.ico)
+ - Windows icon files (.ico) (done)
- ILBM (Amiga) images
- photoshop files (I think I've seen docs)
- XBM
(or even from an existing bold or slanted font)
- utf8 support for text output
- (available for FT1, freetype2, should be easy for Win32)
+ (available for FT1, freetype2, T1, Win32)
- easy interfaces for text output:
- align text around point, including:
- provide patches for libgif and libungif that fix their bugs
and give a useful extension interface. Probe for the
installation of the patches in Makefile.PL to let gif.c
- know what features it can use.
+ know what features it can use. (no need anymore)
- Add options for pnm writer to save in any of the p1..P6
formats. Even if the input has 1 channel, write 3 and such
- read more metadata from images, esp tiff tags, EXIF format information
from TIFF and JPEG.
-- handle 16-bit/sample pgm/ppm files
+- handle 16-bit/sample pgm/ppm files (done)
- "jpeg lossless rotation" - directly manipulates the JPEG
representation to rotate, scale or in some limited cases, crop an
static int write_24bit_data(io_glue *ig, i_img *im);
static int read_bmp_pal(io_glue *ig, i_img *im, int count);
static i_img *read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
- int compression, long offbits);
+ int compression, long offbits, int allow_partial);
static i_img *read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
- int compression, long offbits);
+ int compression, long offbits, int allow_partial);
static i_img *read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
- int compression, long offbits);
+ int compression, long offbits, int allow_partial);
static i_img *read_direct_bmp(io_glue *ig, int xsize, int ysize,
int bit_count, int clr_used, int compression,
- long offbits);
+ long offbits, int allow_partial);
/*
=item i_writebmp_wiol(im, io_glue)
*/
i_img *
-i_readbmp_wiol(io_glue *ig) {
+i_readbmp_wiol(io_glue *ig, int allow_partial) {
int b_magic, m_magic, filesize, res1, res2, infohead_size;
int xsize, ysize, planes, bit_count, compression, size_image, xres, yres;
int clr_used, clr_important, offbits;
switch (bit_count) {
case 1:
- im = read_1bit_bmp(ig, xsize, ysize, clr_used, compression, offbits);
+ im = read_1bit_bmp(ig, xsize, ysize, clr_used, compression, offbits,
+ allow_partial);
break;
case 4:
- im = read_4bit_bmp(ig, xsize, ysize, clr_used, compression, offbits);
+ im = read_4bit_bmp(ig, xsize, ysize, clr_used, compression, offbits,
+ allow_partial);
break;
case 8:
- im = read_8bit_bmp(ig, xsize, ysize, clr_used, compression, offbits);
+ im = read_8bit_bmp(ig, xsize, ysize, clr_used, compression, offbits,
+ allow_partial);
break;
case 32:
case 24:
case 16:
im = read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression,
- offbits);
+ offbits, allow_partial);
break;
default:
*/
static i_img *
read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
- int compression, long offbits) {
+ int compression, long offbits, int allow_partial) {
i_img *im;
- int x, y, lasty, yinc;
+ int x, y, lasty, yinc, start_y;
i_palidx *line, *p;
unsigned char *packed;
int line_size = (xsize + 7)/8;
line_size = (line_size+3) / 4 * 4;
if (ysize > 0) {
- y = ysize-1;
+ start_y = ysize-1;
lasty = -1;
yinc = -1;
}
else {
/* when ysize is -ve it's a top-down image */
ysize = -ysize;
- y = 0;
+ start_y = 0;
lasty = ysize;
yinc = 1;
}
+ y = start_y;
if (!clr_used)
clr_used = 2;
if (clr_used < 0 || clr_used > 2) {
if (ig->readcb(ig, packed, line_size) != line_size) {
myfree(packed);
myfree(line);
- i_push_error(0, "failed reading 1-bit bmp data");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(start_y - y));
+ return im;
+ }
+ else {
+ i_push_error(0, "failed reading 1-bit bmp data");
+ i_img_destroy(im);
+ return NULL;
+ }
}
in = packed;
bit = 0x80;
*/
static i_img *
read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
- int compression, long offbits) {
+ int compression, long offbits, int allow_partial) {
i_img *im;
int x, y, lasty, yinc;
i_palidx *line, *p;
unsigned char *in;
int size, i;
long base_offset;
+ int starty;
/* line_size is going to be smaller than xsize in most cases (and
when it's not, xsize is itself small), and hence not overflow */
line_size = (line_size+3) / 4 * 4;
if (ysize > 0) {
- y = ysize-1;
+ starty = ysize-1;
lasty = -1;
yinc = -1;
}
else {
/* when ysize is -ve it's a top-down image */
ysize = -ysize;
- y = 0;
+ starty = 0;
lasty = ysize;
yinc = 1;
}
+ y = starty;
if (!clr_used)
clr_used = 16;
if (ig->readcb(ig, packed, line_size) != line_size) {
myfree(packed);
myfree(line);
- i_push_error(0, "failed reading 4-bit bmp data");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(y - starty));
+ return im;
+ }
+ else {
+ i_push_error(0, "failed reading 4-bit bmp data");
+ i_img_destroy(im);
+ return NULL;
+ }
}
in = packed;
p = line;
if (ig->readcb(ig, packed, 2) != 2) {
myfree(packed);
myfree(line);
- i_push_error(0, "missing data during decompression");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(y - starty));
+ return im;
+ }
+ else {
+ i_push_error(0, "missing data during decompression");
+ i_img_destroy(im);
+ return NULL;
+ }
}
else if (packed[0]) {
line[0] = packed[1] >> 4;
if (ig->readcb(ig, packed, 2) != 2) {
myfree(packed);
myfree(line);
- i_push_error(0, "missing data during decompression");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(y - starty));
+ return im;
+ }
+ else {
+ i_push_error(0, "missing data during decompression");
+ i_img_destroy(im);
+ return NULL;
+ }
}
x += packed[0];
y += yinc * packed[1];
if (ig->readcb(ig, packed, read_size) != read_size) {
myfree(packed);
myfree(line);
- i_push_error(0, "missing data during decompression");
- /*i_img_destroy(im);*/
- return im;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(y - starty));
+ return im;
+ }
+ else {
+ i_push_error(0, "missing data during decompression");
+ i_img_destroy(im);
+ return NULL;
+ }
}
for (i = 0; i < size; ++i) {
line[0] = packed[i] >> 4;
}
/*
-=item read_8bit_bmp(ig, xsize, ysize, clr_used, compression)
+=item read_8bit_bmp(ig, xsize, ysize, clr_used, compression, allow_partial)
Reads in the palette and image data for a 8-bit/pixel image.
*/
static i_img *
read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
- int compression, long offbits) {
+ int compression, long offbits, int allow_partial) {
i_img *im;
- int x, y, lasty, yinc;
+ int x, y, lasty, yinc, start_y;
i_palidx *line;
int line_size = xsize;
long base_offset;
}
if (ysize > 0) {
- y = ysize-1;
+ start_y = ysize-1;
lasty = -1;
yinc = -1;
}
else {
/* when ysize is -ve it's a top-down image */
ysize = -ysize;
- y = 0;
+ start_y = 0;
lasty = ysize;
yinc = 1;
}
+ y = start_y;
if (!clr_used)
clr_used = 256;
if (clr_used > 256 || clr_used < 0) {
while (y != lasty) {
if (ig->readcb(ig, line, line_size) != line_size) {
myfree(line);
- i_push_error(0, "failed reading 8-bit bmp data");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(start_y - y));
+ return im;
+ }
+ else {
+ i_push_error(0, "failed reading 8-bit bmp data");
+ i_img_destroy(im);
+ return NULL;
+ }
}
i_ppal(im, 0, xsize, y, line);
y += yinc;
/* there's always at least 2 bytes in a sequence */
if (ig->readcb(ig, packed, 2) != 2) {
myfree(line);
- i_push_error(0, "missing data during decompression");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y));
+ return im;
+ }
+ else {
+ i_push_error(0, "missing data during decompression");
+ i_img_destroy(im);
+ return NULL;
+ }
}
if (packed[0]) {
memset(line, packed[1], packed[0]);
case BMPRLE_DELTA:
if (ig->readcb(ig, packed, 2) != 2) {
myfree(line);
- i_push_error(0, "missing data during decompression");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y));
+ return im;
+ }
+ else {
+ i_push_error(0, "missing data during decompression");
+ i_img_destroy(im);
+ return NULL;
+ }
}
x += packed[0];
y += yinc * packed[1];
read_size = (count+1) / 2 * 2;
if (ig->readcb(ig, line, read_size) != read_size) {
myfree(line);
- i_push_error(0, "missing data during decompression");
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y));
+ return im;
+ }
+ else {
+ i_push_error(0, "missing data during decompression");
+ i_img_destroy(im);
+ return NULL;
+ }
}
i_ppal(im, x, x+count, y, line);
x += count;
};
/*
-=item read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression)
+=item read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression, allow_partial)
Skips the palette and reads in the image data for a direct colour image.
*/
static i_img *
read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count,
- int clr_used, int compression, long offbits) {
+ int clr_used, int compression, long offbits,
+ int allow_partial) {
i_img *im;
int x, y, lasty, yinc;
i_color *line, *p;
for (x = 0; x < xsize; ++x) {
unsigned pixel;
if (!read_packed(ig, unpack_code, &pixel)) {
- i_push_error(0, "failed reading image data");
myfree(line);
- i_img_destroy(im);
- return NULL;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", lasty - y);
+ return im;
+ }
+ else {
+ i_push_error(0, "failed reading image data");
+ i_img_destroy(im);
+ return NULL;
+ }
}
for (i = 0; i < 3; ++i) {
if (masks.shifts[i] > 0)
int ch;
i_color pel;
int loadFlags = FT_LOAD_DEFAULT;
+ i_render render;
mm_log((1, "i_ft2_text(handle %p, im %p, tx %d, ty %d, cl %p, cheight %f, cwidth %f, text %p, len %d, align %d, aa %d)\n",
handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
return 0;
+ if (aa)
+ i_render_init(&render, im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
+
if (!align) {
/* this may need adjustment */
tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
ft2_push_message(error);
i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
c, index);
+ if (aa)
+ i_render_done(&render);
return 0;
}
slot = handle->face->glyph;
if (error) {
ft2_push_message(error);
i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
+ if (aa)
+ i_render_done(&render);
return 0;
}
if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
last_mode = slot->bitmap.pixel_mode;
last_grays = slot->bitmap.num_grays;
}
-
+
bmp = slot->bitmap.buffer;
for (y = 0; y < slot->bitmap.rows; ++y) {
- for (x = 0; x < slot->bitmap.width; ++x) {
- int value = map[bmp[x]];
- if (value) {
- i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
- for (ch = 0; ch < im->channels; ++ch) {
- pel.channel[ch] =
- ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
- }
- i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
- }
- }
+ if (last_mode == ft_pixel_mode_grays &&
+ last_grays != 255) {
+ for (x = 0; x < slot->bitmap.width; ++x)
+ bmp[x] = map[bmp[x]];
+ }
+ i_render_color(&render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
+ slot->bitmap.width, bmp, cl);
bmp += slot->bitmap.pitch;
}
}
ty -= slot->advance.y / 64;
}
+ if (aa)
+ i_render_done(&render);
+
return 1;
}
return NULL;
}
+/*
+=item i_img_is_monochrome(img, &zero_is_white)
+
+Tests an image to check it meets our monochrome tests.
+
+The idea is that a file writer can use this to test where it should
+write the image in whatever bi-level format it uses, eg. pbm for pnm.
+
+For performance of encoders we require monochrome images:
+
+=over
+
+=item *
+be paletted
+=item *
+
+have a palette of two colors, containing only (0,0,0) and
+(255,255,255) in either order.
+
+=back
+
+zero_is_white is set to non-zero iff the first palette entry is white.
+
+=cut
+*/
+
+int
+i_img_is_monochrome(i_img *im, int *zero_is_white) {
+ if (im->type == i_palette_type
+ && i_colorcount(im) == 2) {
+ i_color colors[2];
+ i_getcolors(im, 0, colors, 2);
+ if (im->channels == 3) {
+ if (colors[0].rgb.r == 255 &&
+ colors[0].rgb.g == 255 &&
+ colors[0].rgb.b == 255 &&
+ colors[1].rgb.r == 0 &&
+ colors[1].rgb.g == 0 &&
+ colors[1].rgb.b == 0) {
+ *zero_is_white = 0;
+ return 1;
+ }
+ else if (colors[0].rgb.r == 0 &&
+ colors[0].rgb.g == 0 &&
+ colors[0].rgb.b == 0 &&
+ colors[1].rgb.r == 255 &&
+ colors[1].rgb.g == 255 &&
+ colors[1].rgb.b == 255) {
+ *zero_is_white = 1;
+ return 1;
+ }
+ }
+ else if (im->channels == 1) {
+ if (colors[0].channel[0] == 255 &&
+ colors[1].channel[1] == 0) {
+ *zero_is_white = 0;
+ return 1;
+ }
+ else if (colors[0].channel[0] == 0 &&
+ colors[0].channel[0] == 255) {
+ *zero_is_white = 1;
+ return 1;
+ }
+ }
+ }
+
+ *zero_is_white = 0;
+ return 0;
+}
/*
=back
extern i_img *i_img_double_new(int x, int y, int ch);
extern i_img *i_img_double_new_low(i_img *im, int x, int y, int ch);
+extern int i_img_is_monochrome(i_img *im, int *zero_is_white);
const char * i_test_format_probe(io_glue *data, int length);
#endif /* HAVE_LIBJPEG */
#ifdef HAVE_LIBTIFF
-i_img * i_readtiff_wiol(io_glue *ig, int length, int page);
+i_img * i_readtiff_wiol(io_glue *ig, int allow_partial, int page);
i_img ** i_readtiff_multi_wiol(io_glue *ig, int length, int *count);
undef_int i_writetiff_wiol(i_img *im, io_glue *ig);
undef_int i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count);
i_img * i_readraw_wiol(io_glue *ig, int x, int y, int datachannels, int storechannels, int intrl);
undef_int i_writeraw_wiol(i_img* im, io_glue *ig);
-i_img * i_readpnm_wiol(io_glue *ig, int length);
+i_img * i_readpnm_wiol(io_glue *ig, int allow_partial);
undef_int i_writeppm_wiol(i_img *im, io_glue *ig);
extern int i_writebmp_wiol(i_img *im, io_glue *ig);
-extern i_img *i_readbmp_wiol(io_glue *ig);
+extern i_img *i_readbmp_wiol(io_glue *ig, int allow_partial);
int tga_header_verify(unsigned char headbuf[18]);
#endif /* IMAGER_MALLOC_DEBUG */
+#include "imrender.h"
+
#endif
mc_web_map, /* Use the 216 colour web colour map */
mc_addi, /* Addi's algorithm */
mc_median_cut, /* median cut - similar to giflib, hopefully */
+ mc_mono, /* fixed mono color map */
mc_mask = 0xFF /* (mask for generator) */
} i_make_colors;
#include "iolayert.h"
+#include "rendert.h"
+
#endif
--- /dev/null
+#ifndef IMAGER_IMRENDER_H
+#define IMAGER_IMRENDER_H
+
+#include "rendert.h"
+
+extern void
+i_render_init(i_render *r, i_img *im, int width);
+extern void
+i_render_done(i_render *r);
+extern void
+i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
+ i_color const *color);
+
+#endif
$img->read(file => $filename)
or die "Cannot read $filename: ", $img->errstr;
+The read() method accepts the C<allow_partial> parameter. If this is
+non-zero then read() can return true on an incomplete image and set
+the C<i_incomplete> tag.
+
=item write
and the C<write()> method to write an image:
PNM does not support the spatial resolution tags.
+The following tags are set when reading a PNM file:
+
+=over
+
+=item *
+
+X<pnm_maxval>pnm_maxval - the maxvals number from the PGM/PPM header.
+Always set to 2 for a PBM file.
+
+=item *
+
+X<pnm_type>pnm_type - the type number from the PNM header, 1 for ASCII
+PBM files, 2 for ASCII PGM files, 3 for ASCII PPM files, 4 for binary
+PBM files, 5 for binary PGM files, 6 for binary PPM files.
+
+=back
+
+The following tag is checked when writing an image with more than
+8-bits/sample:
+
+=over
+
+=item *
+
+X<pnm_write_wide_data>pnm_write_wide_data - if this is non-zero then
+write() can write PGM/PPM files with 16-bits/sample. Some
+applications, for example GIMP 2.2, and tools can only read
+8-bit/sample binary PNM files, so Imager will only write a 16-bit
+image when this tag is non-zero.
+
+=back
+
=head2 JPEG
You can supply a C<jpegquality> parameter (0-100) when writing a JPEG
=item jpeg_density_unit
The value of the density unit field in the JFIF header. This is
-ignored on writing if the i_aspect_only tag is non-zero.
+ignored on writing if the C<i_aspect_only> tag is non-zero.
The C<i_xres> and C<i_yres> tags are expressed in pixels per inch no
matter the value of this tag, they will be converted to/from the value
=over
-=item i_xres
-
-=item i_yres
+=item *
-The spatial resolution of the image in pixels per inch. If the image
-format uses a different scale, eg. pixels per meter, then this value
-is converted. A floating point number stored as a string.
+X<i_xres tag>X<i_yres tag>X<tags, i_xres>X<tags, i_yres>i_xres, i_yres
+- The spatial resolution of the image in pixels per inch. If the
+image format uses a different scale, eg. pixels per meter, then this
+value is converted. A floating point number stored as a string.
# our image was generated as a 300 dpi image
$img->settag(name => 'i_xres', value => 300);
$img->settag(name => 'i_xres', value => 100 * 2.54);
$img->settag(name => 'i_yres', value => 100 * 2.54);
-=item i_aspect_only
+=item *
+
+X<i_aspect_only tag>X<tags, i_aspect_only>i_aspect_only - If this is
+non-zero then the values in i_xres and i_yres are treated as a ratio
+only. If the image format does not support aspect ratios then this is
+scaled so the smaller value is 72dpi.
-If this is non-zero then the values in i_xres and i_yres are treated
-as a ratio only. If the image format does not support aspect ratios
-then this is scaled so the smaller value is 72dpi.
+=item *
+
+X<i_incomplete tag>X<tags, i_incomplete>i_incomplete - If this tag is
+present then the whole image could not be read. This isn't
+implemented for all images yet, and may not be.
-=item i_incomplete
+=item *
-If this tag is present then the whole image could not be read. This
-isn't implemented for all images yet, and may not be.
+X<i_lines_read tag>X<tags, i_lines_read>i_lines_read - If
+C<i_incomplete> is set then this tag may be set to the number of
+scanlines successfully read from the file. This can be used to decide
+whether an image is worth processing.
-=item i_format
+=item *
-The file format this file was read from.
+X<i_format tag>X<tags, i_format>i_format - The file format this file
+was read from.
=back
=head2 Quantization options
-These options can be specified when calling write_multi() for gif
-files, when writing a single image with the gifquant option set to
-'gen', or for direct calls to i_writegif_gen and i_writegif_callback.
+These options can be specified when calling
+L<Imager::ImageTypes/to_paletted>, write_multi() for gif files, when
+writing a single image with the gifquant option set to 'gen', or for
+direct calls to i_writegif_gen and i_writegif_callback.
=over
=over
-=item none
+=item *
-Only colors supplied in 'colors' are used.
+none - only colors supplied in 'colors' are used.
-=item webmap
+=item *
-The web color map is used (need url here.)
+webmap - the web color map is used (need url here.)
-=item addi
+=item *
-The original code for generating the color map (Addi's code) is used.
+addi - The original code for generating the color map (Addi's code) is
+used.
-=item mediancut
+=item *
-Uses a mediancut algorithm, faster than 'addi', but not as good a
+mediancut - Uses a mediancut algorithm, faster than 'addi', but not as good a
result.
+=item *
+
+mono, monochrome - a fixed black and white palette, suitable for
+producing bi-level images (eg. facsimile)
+
=back
Other methods may be added in the future.
require Exporter;
use vars qw(@ISA @EXPORT_OK);
@ISA = qw(Exporter);
-@EXPORT_OK = qw(diff_text_with_nul);
+@EXPORT_OK = qw(diff_text_with_nul test_image_raw test_image_16 is_color3 is_color1 is_image);
sub diff_text_with_nul {
my ($desc, $text1, $text2, @params) = @_;
"$desc - check result different");
}
+sub is_color3($$$$$) {
+ my ($color, $red, $green, $blue, $comment) = @_;
+
+ my $builder = Test::Builder->new;
+
+ unless (defined $color) {
+ $builder->ok(0, $comment);
+ $builder->diag("color is undef");
+ return;
+ }
+ unless ($color->can('rgba')) {
+ $builder->ok(0, $comment);
+ $builder->diag("color is not a color object");
+ return;
+ }
+
+ my ($cr, $cg, $cb) = $color->rgba;
+ unless ($builder->ok($cr == $red && $cg == $green && $cb == $blue, $comment)) {
+ $builder->diag(<<END_DIAG);
+Color mismatch:
+ Red: $red vs $cr
+Green: $green vs $cg
+ Blue: $blue vs $cb
+END_DIAG
+ return;
+ }
+
+ return 1;
+}
+
+sub is_color1($$$) {
+ my ($color, $grey, $comment) = @_;
+
+ my $builder = Test::Builder->new;
+
+ unless (defined $color) {
+ $builder->ok(0, $comment);
+ $builder->diag("color is undef");
+ return;
+ }
+ unless ($color->can('rgba')) {
+ $builder->ok(0, $comment);
+ $builder->diag("color is not a color object");
+ return;
+ }
+
+ my ($cgrey) = $color->rgba;
+ unless ($builder->ok($cgrey == $grey, $comment)) {
+ $builder->diag(<<END_DIAG);
+Color mismatch:
+ Grey: $grey vs $cgrey
+END_DIAG
+ return;
+ }
+
+ return 1;
+}
+
+sub test_image_raw {
+ my $green=Imager::i_color_new(0,255,0,255);
+ my $blue=Imager::i_color_new(0,0,255,255);
+ my $red=Imager::i_color_new(255,0,0,255);
+
+ my $img=Imager::ImgRaw::new(150,150,3);
+
+ Imager::i_box_filled($img,70,25,130,125,$green);
+ Imager::i_box_filled($img,20,25,80,125,$blue);
+ Imager::i_arc($img,75,75,30,0,361,$red);
+ Imager::i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]);
+
+ $img;
+}
+
+sub test_image_16 {
+ my $green = Imager::Color->new(0, 255, 0, 255);
+ my $blue = Imager::Color->new(0, 0, 255, 255);
+ my $red = Imager::Color->new(255, 0, 0, 255);
+ my $img = Imager->new(xsize => 150, ysize => 150, bits => 16);
+ $img->box(filled => 1, color => $green, box => [ 70, 25, 130, 125 ]);
+ $img->box(filled => 1, color => $blue, box => [ 20, 25, 80, 125 ]);
+ $img->arc(x => 75, y => 75, r => 30, color => $red);
+ $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]);
+
+ $img;
+}
+
+sub is_image($$$) {
+ my ($left, $right, $comment) = @_;
+
+ my $builder = Test::Builder->new;
+
+ unless (defined $left) {
+ $builder->ok(0, $comment);
+ $builder->diag("left is undef");
+ return;
+ }
+ unless (defined $right) {
+ $builder->ok(0, $comment);
+ $builder->diag("right is undef");
+ return;
+ }
+ unless ($left->{IMG}) {
+ $builder->ok(0, $comment);
+ $builder->diag("left image has no low level object");
+ return;
+ }
+ unless ($right->{IMG}) {
+ $builder->ok(0, $comment);
+ $builder->diag("right image has no low level object");
+ return;
+ }
+ unless ($left->getwidth == $right->getwidth) {
+ $builder->ok(0, $comment);
+ $builder->diag("left width " . $left->getwidth . " vs right width "
+ . $right->getwidth);
+ return;
+ }
+ unless ($left->getheight == $right->getheight) {
+ $builder->ok(0, $comment);
+ $builder->diag("left height " . $left->getheight . " vs right height "
+ . $right->getheight);
+ return;
+ }
+ unless ($left->getchannels == $right->getchannels) {
+ $builder->ok(0, $comment);
+ $builder->diag("left channels " . $left->getchannels . " vs right channels "
+ . $right->getchannels);
+ return;
+ }
+ my $diff = Imager::i_img_diff($left->{IMG}, $right->{IMG});
+ unless ($diff == 0) {
+ $builder->ok(0, $comment);
+ $builder->diag("image data different - $diff");
+ return;
+ }
+
+ return $builder->ok(1, $comment);
+}
+
1;
__END__
=over
-=item diff_text_with_nul($test_name, $text1, $text2, @optios)
+=item is_color3($color, $red, $blue, $green, $comment)
+
+Tests is $color matches the given ($red, $blue, $green)
+
+=item test_image_raw()
+
+Returns a 150x150x3 Imager::ImgRaw test image.
+
+=item test_image_16()
+
+Returns a 150x150x3 16-bit/sample OO test image.
+
+=item diff_text_with_nul($test_name, $text1, $text2, @options)
Creates 2 test images and writes $text1 to the first image and $text2
to the second image with the string() method. Each call adds 3 ok/not
=head1 SYNOPSIS
io_glue *ig = io_new_fd( fd );
- i_img *im = i_readpnm_wiol(ig, -1); // no limit on how much is read
+ i_img *im = i_readpnm_wiol(ig, 0); // no limit on how much is read
// or
io_glue *ig = io_new_fd( fd );
return_code = i_writepnm_wiol(im, ig);
=cut
*/
+#define gnext(mb) (((mb)->cp == (mb)->len) ? gnextf(mb) : (mb)->buf + (mb)->cp++)
+
static
char *
-gnext(mbuf *mb) {
+gnextf(mbuf *mb) {
io_glue *ig = mb->ig;
if (mb->cp == mb->len) {
mb->cp = 0;
return NULL;
}
if (mb->len == 0) {
- i_push_error(errno, "unexpected end of file");
mm_log((1, "i_readpnm: end of file\n"));
return NULL;
}
=cut
*/
+#define gpeek(mb) ((mb)->cp == (mb)->len ? gpeekf(mb) : (mb)->buf + (mb)->cp)
+
static
char *
-gpeek(mbuf *mb) {
+gpeekf(mbuf *mb) {
io_glue *ig = mb->ig;
if (mb->cp == mb->len) {
mb->cp = 0;
return NULL;
}
if (mb->len == 0) {
- i_push_error(0, "unexpected end of file");
mm_log((1, "i_readpnm: end of file\n"));
return NULL;
}
return &mb->buf[mb->cp];
}
-
+int
+gread(mbuf *mb, unsigned char *buf, size_t read_size) {
+ int total_read = 0;
+ if (mb->cp != mb->len) {
+ int avail_size = mb->len - mb->cp;
+ int use_size = read_size > avail_size ? avail_size : read_size;
+ memcpy(buf, mb->buf+mb->cp, use_size);
+ mb->cp += use_size;
+ total_read += use_size;
+ read_size -= use_size;
+ buf += use_size;
+ }
+ if (read_size) {
+ io_glue *ig = mb->ig;
+ int read_res = i_io_read(ig, buf, read_size);
+ if (read_res >= 0) {
+ total_read += read_res;
+ }
+ }
+ return total_read;
+}
/*
if (!skip_spaces(mb)) return 0;
+ if (!(cp = gpeek(mb)))
+ return 0;
+ if (!misnumber(*cp))
+ return 0;
while( (cp = gpeek(mb)) && misnumber(*cp) ) {
*i = *i*10+(*cp-'0');
cp = gnext(mb);
return 1;
}
+static
+i_img *
+read_pgm_ppm_bin8(mbuf *mb, i_img *im, int width, int height,
+ int channels, int maxval, int allow_partial) {
+ i_color *line, *linep;
+ int read_size;
+ unsigned char *read_buf, *readp;
+ int x, y, ch;
+ int rounder = maxval / 2;
+
+ line = mymalloc(width * sizeof(i_color));
+ read_size = channels * width;
+ read_buf = mymalloc(read_size);
+ for(y=0;y<height;y++) {
+ linep = line;
+ readp = read_buf;
+ if (gread(mb, read_buf, read_size) != read_size) {
+ myfree(line);
+ myfree(read_buf);
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", y);
+ return im;
+ }
+ else {
+ i_push_error(0, "short read - file truncated?");
+ i_img_destroy(im);
+ return NULL;
+ }
+ }
+ if (maxval == 255) {
+ for(x=0; x<width; x++) {
+ for(ch=0; ch<channels; ch++) {
+ linep->channel[ch] = *readp++;
+ }
+ ++linep;
+ }
+ }
+ else {
+ for(x=0; x<width; x++) {
+ for(ch=0; ch<channels; ch++) {
+ /* we just clamp samples to the correct range */
+ unsigned sample = *readp++;
+ if (sample > maxval)
+ sample = maxval;
+ linep->channel[ch] = (sample * 255 + rounder) / maxval;
+ }
+ ++linep;
+ }
+ }
+ i_plin(im, 0, width, y, line);
+ }
+ myfree(read_buf);
+ myfree(line);
+
+ return im;
+}
+
+static
+i_img *
+read_pgm_ppm_bin16(mbuf *mb, i_img *im, int width, int height,
+ int channels, int maxval, int allow_partial) {
+ i_fcolor *line, *linep;
+ int read_size;
+ unsigned char *read_buf, *readp;
+ int x, y, ch;
+ double maxvalf = maxval;
+
+ line = mymalloc(width * sizeof(i_fcolor));
+ read_size = channels * width * 2;
+ read_buf = mymalloc(read_size);
+ for(y=0;y<height;y++) {
+ linep = line;
+ readp = read_buf;
+ if (gread(mb, read_buf, read_size) != read_size) {
+ myfree(line);
+ myfree(read_buf);
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", y);
+ return im;
+ }
+ else {
+ i_push_error(0, "short read - file truncated?");
+ i_img_destroy(im);
+ return NULL;
+ }
+ }
+ for(x=0; x<width; x++) {
+ for(ch=0; ch<channels; ch++) {
+ unsigned sample = (readp[0] << 8) + readp[1];
+ if (sample > maxval)
+ sample = maxval;
+ readp += 2;
+ linep->channel[ch] = sample / maxvalf;
+ }
+ ++linep;
+ }
+ i_plinf(im, 0, width, y, line);
+ }
+ myfree(read_buf);
+ myfree(line);
+
+ return im;
+}
+
+static
+i_img *
+read_pbm_bin(mbuf *mb, i_img *im, int width, int height, int allow_partial) {
+ i_palidx *line, *linep;
+ int read_size;
+ unsigned char *read_buf, *readp;
+ int x, y;
+ unsigned mask;
+
+ line = mymalloc(width * sizeof(i_palidx));
+ read_size = (width + 7) / 8;
+ read_buf = mymalloc(read_size);
+ for(y = 0; y < height; y++) {
+ if (gread(mb, read_buf, read_size) != read_size) {
+ myfree(line);
+ myfree(read_buf);
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", y);
+ return im;
+ }
+ else {
+ i_push_error(0, "short read - file truncated?");
+ i_img_destroy(im);
+ return NULL;
+ }
+ }
+ linep = line;
+ readp = read_buf;
+ mask = 0x80;
+ for(x = 0; x < width; ++x) {
+ *linep++ = *readp & mask ? 1 : 0;
+ mask >>= 1;
+ if (mask == 0) {
+ ++readp;
+ mask = 0x80;
+ }
+ }
+ i_ppal(im, 0, width, y, line);
+ }
+ myfree(read_buf);
+ myfree(line);
+
+ return im;
+}
+
+/* unlike pgm/ppm pbm:
+ - doesn't require spaces between samples (bits)
+ - 1 (maxval) is black instead of white
+*/
+static
+i_img *
+read_pbm_ascii(mbuf *mb, i_img *im, int width, int height, int allow_partial) {
+ i_palidx *line, *linep;
+ int x, y;
+
+ line = mymalloc(width * sizeof(i_palidx));
+ for(y = 0; y < height; y++) {
+ linep = line;
+ for(x = 0; x < width; ++x) {
+ char *cp;
+ skip_spaces(mb);
+ if (!(cp = gnext(mb)) || (*cp != '0' && *cp != '1')) {
+ myfree(line);
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", y);
+ return im;
+ }
+ else {
+ if (cp)
+ i_push_error(0, "invalid data for ascii pnm");
+ else
+ i_push_error(0, "short read - file truncated?");
+ i_img_destroy(im);
+ return NULL;
+ }
+ }
+ *linep++ = *cp == '0' ? 0 : 1;
+ }
+ i_ppal(im, 0, width, y, line);
+ }
+ myfree(line);
+
+ return im;
+}
+
+static
+i_img *
+read_pgm_ppm_ascii(mbuf *mb, i_img *im, int width, int height, int channels,
+ int maxval, int allow_partial) {
+ i_color *line, *linep;
+ int x, y, ch;
+ int rounder = maxval / 2;
+
+ line = mymalloc(width * sizeof(i_color));
+ for(y=0;y<height;y++) {
+ linep = line;
+ for(x=0; x<width; x++) {
+ for(ch=0; ch<channels; ch++) {
+ int sample;
+
+ if (!gnum(mb, &sample)) {
+ myfree(line);
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_incomplete", 1);
+ i_tags_setn(&im->tags, "i_lines_read", 1);
+ return im;
+ }
+ else {
+ if (gpeek(mb))
+ i_push_error(0, "invalid data for ascii pnm");
+ else
+ i_push_error(0, "short read - file truncated?");
+ i_img_destroy(im);
+ return NULL;
+ }
+ }
+ if (sample > maxval)
+ sample = maxval;
+ linep->channel[ch] = (sample * 255 + rounder) / maxval;
+ }
+ ++linep;
+ }
+ i_plin(im, 0, width, y, line);
+ }
+ myfree(line);
+
+ return im;
+}
+
+static
+i_img *
+read_pgm_ppm_ascii_16(mbuf *mb, i_img *im, int width, int height,
+ int channels, int maxval, int allow_partial) {
+ i_fcolor *line, *linep;
+ int x, y, ch;
+ double maxvalf = maxval;
+
+ line = mymalloc(width * sizeof(i_fcolor));
+ for(y=0;y<height;y++) {
+ linep = line;
+ for(x=0; x<width; x++) {
+ for(ch=0; ch<channels; ch++) {
+ int sample;
+
+ if (!gnum(mb, &sample)) {
+ myfree(line);
+ if (allow_partial) {
+ }
+ else {
+ if (gpeek(mb))
+ i_push_error(0, "invalid data for ascii pnm");
+ else
+ i_push_error(0, "short read - file truncated?");
+ i_img_destroy(im);
+ return NULL;
+ }
+ }
+ if (sample > maxval)
+ sample = maxval;
+ linep->channel[ch] = sample / maxvalf;
+ }
+ ++linep;
+ }
+ i_plinf(im, 0, width, y, line);
+ }
+ myfree(line);
+
+ return im;
+}
/*
-=item i_readpnm_wiol(ig, length)
+=item i_readpnm_wiol(ig, allow_partial)
Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
ig - io_glue object
- length - maximum length to read from data source, before closing it -1
- signifies no limit.
+ allow_partial - allows a partial file to be read successfully
=cut
*/
i_img *
-i_readpnm_wiol(io_glue *ig, int length) {
+i_readpnm_wiol(io_glue *ig, int allow_partial) {
i_img* im;
int type;
- int x, y, ch;
int width, height, maxval, channels, pcount;
int rounder;
char *cp;
- unsigned char *uc;
mbuf buf;
- i_color val;
i_clear_error();
-
- mm_log((1,"i_readpnm(ig %p, length %d)\n", ig, length));
+ mm_log((1,"i_readpnm(ig %p, allow_partial %d)\n", ig, allow_partial));
io_glue_commit_types(ig);
init_buf(&buf, ig);
mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n"));
return NULL;
}
- else if (type >= 4 && maxval > 255) {
- i_push_errorf(0, "maxval of %d is over 255 - not currently supported by Imager for binary pnm", maxval);
- mm_log((1, "i_readpnm: maxval of %d is over 255 - not currently supported by Imager for binary pnm\n", maxval));
- return NULL;
- }
} else maxval=1;
rounder = maxval / 2;
}
mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
-
- im = i_img_empty_ch(NULL, width, height, channels);
- i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0);
+ if (type == 1 || type == 4) {
+ i_color pbm_pal[2];
+ pbm_pal[0].channel[0] = 255;
+ pbm_pal[1].channel[0] = 0;
+
+ im = i_img_pal_new(width, height, 1, 256);
+ i_addcolors(im, pbm_pal, 2);
+ }
+ else {
+ if (maxval > 255)
+ im = i_img_16_new(width, height, channels);
+ else
+ im = i_img_8_new(width, height, channels);
+ }
switch (type) {
case 1: /* Ascii types */
+ im = read_pbm_ascii(&buf, im, width, height, allow_partial);
+ break;
+
case 2:
case 3:
- for(y=0;y<height;y++) for(x=0; x<width; x++) {
- for(ch=0; ch<channels; ch++) {
- int t;
- if (gnum(&buf, &t)) val.channel[ch] = (t * 255 + rounder) / maxval;
- else {
- mm_log((1,"i_readpnm: gnum() returned false in data\n"));
- return im;
- }
- }
- i_ppix(im, x, y, &val);
- }
+ if (maxval > 255)
+ im = read_pgm_ppm_ascii_16(&buf, im, width, height, channels, maxval, allow_partial);
+ else
+ im = read_pgm_ppm_ascii(&buf, im, width, height, channels, maxval, allow_partial);
break;
case 4: /* binary pbm */
- for(y=0;y<height;y++) for(x=0; x<width; x+=8) {
- if ( (uc = (unsigned char*)gnext(&buf)) ) {
- int xt;
- int pc = width-x < 8 ? width-x : 8;
- /* mm_log((1,"i_readpnm: y=%d x=%d pc=%d\n", y, x, pc)); */
- for(xt = 0; xt<pc; xt++) {
- val.channel[0] = (*uc & (128>>xt)) ? 0 : 255;
- i_ppix(im, x+xt, y, &val);
- }
- } else {
- mm_log((1,"i_readpnm: gnext() returned false in data\n"));
- return im;
- }
- }
+ im = read_pbm_bin(&buf, im, width, height, allow_partial);
break;
case 5: /* binary pgm */
case 6: /* binary ppm */
- for(y=0;y<height;y++) for(x=0; x<width; x++) {
- for(ch=0; ch<channels; ch++) {
- if ( (uc = (unsigned char*)gnext(&buf)) )
- val.channel[ch] = (*uc * 255 + rounder) / maxval;
- else {
- mm_log((1,"i_readpnm: gnext() returned false in data\n"));
- return im;
- }
- }
- i_ppix(im, x, y, &val);
- }
+ if (maxval > 255)
+ im = read_pgm_ppm_bin16(&buf, im, width, height, channels, maxval, allow_partial);
+ else
+ im = read_pgm_ppm_bin8(&buf, im, width, height, channels, maxval, allow_partial);
break;
+
default:
mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
return NULL;
}
+
+ if (!im)
+ return NULL;
+
+ i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0);
+ i_tags_setn(&im->tags, "pnm_maxval", maxval);
+ i_tags_setn(&im->tags, "pnm_type", type);
+
return im;
}
+static
+int
+write_pbm(i_img *im, io_glue *ig, int zero_is_white) {
+ int x, y;
+ i_palidx *line;
+ int write_size;
+ unsigned char *write_buf;
+ unsigned char *writep;
+ char header[255];
+ unsigned mask;
+
+ sprintf(header, "P4\012# CREATOR: Imager\012%d %d\012",
+ im->xsize, im->ysize);
+ if (i_io_write(ig, header, strlen(header)) < 0) {
+ i_push_error(0, "could not write pbm header");
+ return 0;
+ }
+ write_size = (im->xsize + 7) / 8;
+ line = mymalloc(sizeof(i_palidx) * im->xsize);
+ write_buf = mymalloc(write_size);
+ for (y = 0; y < im->ysize; ++y) {
+ i_gpal(im, 0, im->xsize, y, line);
+ mask = 0x80;
+ writep = write_buf;
+ memset(write_buf, 0, write_size);
+ for (x = 0; x < im->xsize; ++x) {
+ if (zero_is_white ? line[x] : !line[x])
+ *writep |= mask;
+ mask >>= 1;
+ if (!mask) {
+ ++writep;
+ mask = 0x80;
+ }
+ }
+ if (i_io_write(ig, write_buf, write_size) != write_size) {
+ i_push_error(0, "write failure");
+ myfree(write_buf);
+ myfree(line);
+ return 0;
+ }
+ }
+ myfree(write_buf);
+ myfree(line);
+
+ return 1;
+}
+
+static
+int
+write_ppm_data_8(i_img *im, io_glue *ig) {
+ int write_size = im->xsize * im->channels;
+ unsigned char *data = mymalloc(write_size);
+ int y = 0;
+ int rc = 1;
+
+ while (y < im->ysize && rc >= 0) {
+ i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
+ if (i_io_write(ig, data, write_size) != write_size) {
+ i_push_error(errno, "could not write ppm data");
+ rc = 0;
+ break;
+ }
+ ++y;
+ }
+ myfree(data);
+
+ return rc;
+}
+
+static
+int
+write_ppm_data_16(i_img *im, io_glue *ig) {
+ int sample_count = im->channels * im->xsize;
+ int write_size = sample_count * 2;
+ int line_size = sample_count * sizeof(i_fsample_t);
+ i_fsample_t *line_buf = mymalloc(line_size);
+ i_fsample_t *samplep;
+ unsigned char *write_buf = mymalloc(write_size);
+ unsigned char *writep;
+ int sample_num;
+ int y = 0;
+ int rc = 1;
+
+ while (y < im->ysize) {
+ i_gsampf(im, 0, im->xsize, y, line_buf, NULL, im->channels);
+ samplep = line_buf;
+ writep = write_buf;
+ for (sample_num = 0; sample_num < sample_count; ++sample_num) {
+ unsigned sample16 = SampleFTo16(*samplep++);
+ *writep++ = sample16 >> 8;
+ *writep++ = sample16 & 0xFF;
+ }
+ if (i_io_write(ig, write_buf, write_size) != write_size) {
+ i_push_error(errno, "could not write ppm data");
+ rc = 0;
+ break;
+ }
+ ++y;
+ }
+ myfree(line_buf);
+ myfree(write_buf);
+
+ return rc;
+}
undef_int
i_writeppm_wiol(i_img *im, io_glue *ig) {
char header[255];
- int rc;
+ int zero_is_white;
+ int wide_data;
mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
i_clear_error();
io_glue_commit_types(ig);
- if (im->channels == 3) {
- sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
- if (ig->writecb(ig,header,strlen(header))<0) {
- i_push_error(errno, "could not write ppm header");
- mm_log((1,"i_writeppm: unable to write ppm header.\n"));
- return(0);
- }
+ if (i_img_is_monochrome(im, &zero_is_white)) {
+ return write_pbm(im, ig, zero_is_white);
+ }
+ else {
+ int type;
+ int maxval;
- if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
- rc = ig->writecb(ig,im->idata,im->bytes);
+ if (!i_tags_get_int(&im->tags, "pnm_write_wide_data", 0, &wide_data))
+ wide_data = 0;
+
+ if (im->channels == 3) {
+ type = 6;
}
- else {
- unsigned char *data = mymalloc(3 * im->xsize);
- if (data != NULL) {
- int y = 0;
- static int rgb_chan[3] = { 0, 1, 2 };
-
- rc = 0;
- while (y < im->ysize && rc >= 0) {
- i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
- rc = ig->writecb(ig, data, im->xsize * 3);
- ++y;
- }
- myfree(data);
- }
- else {
- i_push_error(0, "Out of memory");
- return 0;
- }
+ else if (im->channels == 1) {
+ type = 5;
}
- if (rc<0) {
- i_push_error(errno, "could not write ppm data");
- mm_log((1,"i_writeppm: unable to write ppm data.\n"));
+ else {
+ i_push_error(0, "can only save 1 or 3 channel images to pnm");
+ mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
return(0);
}
- }
- else if (im->channels == 1) {
- sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
- im->xsize, im->ysize);
- if (ig->writecb(ig,header, strlen(header)) < 0) {
- i_push_error(errno, "could not write pgm header");
- mm_log((1,"i_writeppm: unable to write pgm header.\n"));
+ if (im->bits <= 8 || !wide_data)
+ maxval = 255;
+ else
+ maxval = 65535;
+
+ sprintf(header,"P%d\n#CREATOR: Imager\n%d %d\n%d\n",
+ type, im->xsize, im->ysize, maxval);
+
+ if (ig->writecb(ig,header,strlen(header)) != strlen(header)) {
+ i_push_error(errno, "could not write ppm header");
+ mm_log((1,"i_writeppm: unable to write ppm header.\n"));
return(0);
}
if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
- rc=ig->writecb(ig,im->idata,im->bytes);
- }
- else {
- unsigned char *data = mymalloc(im->xsize);
- if (data != NULL) {
- int y = 0;
- int chan = 0;
-
- rc = 0;
- while (y < im->ysize && rc >= 0) {
- i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
- rc = ig->writecb(ig, data, im->xsize);
- ++y;
- }
- myfree(data);
- }
- else {
- i_push_error(0, "Out of memory");
+ if (ig->writecb(ig,im->idata,im->bytes) != im->bytes) {
+ i_push_error(errno, "could not write ppm data");
return 0;
}
}
- if (rc<0) {
- i_push_error(errno, "could not write pgm data");
- mm_log((1,"i_writeppm: unable to write pgm data.\n"));
- return(0);
+ else if (maxval == 255) {
+ if (!write_ppm_data_8(im, ig))
+ return 0;
+ }
+ else {
+ if (!write_ppm_data_16(im, ig))
+ return 0;
}
- }
- else {
- i_push_error(0, "can only save 1 or 3 channel images to pnm");
- mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
- return(0);
}
ig->closecb(ig);
=head1 AUTHOR
-Arnar M. Hrafnkelsson <addi@umich.edu>
+Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook<tony@imager.perl.org>
=head1 SEE ALSO
static void makemap_addi(i_quantize *, i_img **imgs, int count);
static void makemap_mediancut(i_quantize *, i_img **imgs, int count);
+static void makemap_mono(i_quantize *);
static
void
makemap_mediancut(quant, imgs, count);
break;
+ case mc_mono:
+ makemap_mono(quant);
+ break;
+
case mc_addi:
default:
makemap_addi(quant, imgs, count);
i_mempool_destroy(&mp);
}
+static void
+makemap_mono(i_quantize *quant) {
+ quant->mc_colors[0].rgba.r = 0;
+ quant->mc_colors[0].rgba.g = 0;
+ quant->mc_colors[0].rgba.b = 0;
+ quant->mc_colors[0].rgba.a = 255;
+ quant->mc_colors[1].rgba.r = 255;
+ quant->mc_colors[1].rgba.g = 255;
+ quant->mc_colors[1].rgba.b = 255;
+ quant->mc_colors[1].rgba.a = 255;
+ quant->mc_count = 2;
+}
+
#define pboxjump 32
/* Define one of the following 4 symbols to choose a colour search method
--- /dev/null
+/*
+Render utilities
+*/
+#include "imager.h"
+
+#define RENDER_MAGIC 0x765AE
+
+typedef void (*render_color_f)(i_render *, int, int, int, unsigned char const *src, i_color const *color);
+
+#code
+
+static void IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
+static void IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
+
+static render_color_f IM_SUFFIX(render_color_tab)[] =
+ {
+ NULL,
+ IM_SUFFIX(render_color_13),
+ IM_SUFFIX(render_color_alpha),
+ IM_SUFFIX(render_color_13),
+ IM_SUFFIX(render_color_alpha),
+ };
+
+#/code
+
+void
+i_render_init(i_render *r, i_img *im, int width) {
+ r->magic = RENDER_MAGIC;
+ r->im = im;
+ r->width = width;
+ r->line_8 = NULL;
+ r->line_double = NULL;
+#code im->bits <= 8
+ r->IM_SUFFIX(line) = mymalloc(sizeof(i_fcolor) * width);
+#/code
+}
+
+void
+i_render_done(i_render *r) {
+ if (r->line_8)
+ myfree(r->line_8);
+ else
+ myfree(r->line_double);
+ r->magic = 0;
+}
+
+void
+i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
+ i_color const *color) {
+ i_img *im = r->im;
+ if (y < 0 || y >= im->ysize)
+ return;
+ if (x < 0) {
+ width += x;
+ src -= x;
+ x = 0;
+ }
+ if (x + width > im->xsize) {
+ width = im->xsize - x;
+ }
+ if (x >= im->xsize || x + width <= 0 || width <= 0)
+ return;
+
+ /* avoid as much work as we can */
+ while (width > 0 && *src == 0) {
+ --width;
+ ++src;
+ ++x;
+ }
+ while (width > 0 && src[width-1] == 0) {
+ --width;
+ }
+ if (!width)
+ return;
+
+ /* make sure our line buffer is big enough */
+ if (width > r->width) {
+ int new_width = r->width * 2;
+ if (new_width < width)
+ new_width = width;
+
+ if (r->line_8)
+ r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
+ else
+ r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
+ }
+
+#code r->im->bits <= 8
+ (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
+#/code
+}
+
+static void
+dump_src(const char *note, unsigned char const *src, int width) {
+ int i;
+ printf("%s - %p/%d\n", note, src, width);
+ for (i = 0; i < width; ++i) {
+ printf("%02x ", src[i]);
+ }
+ putchar('\n');
+}
+
+#code
+
+static
+void
+IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width,
+ unsigned char const *src, i_color const *color) {
+ i_img *im = r->im;
+ IM_COLOR *linep = r->IM_SUFFIX(line);
+ int ch, channels = im->channels;
+ int fetch_offset;
+#undef STORE_COLOR
+#ifdef IM_EIGHT_BIT
+#define STORE_COLOR (*color)
+#else
+ i_fcolor fcolor;
+
+ for (ch = 0; ch < channels; ++ch) {
+ fcolor.channel[ch] = color->channel[ch] / 255.0;
+ }
+#define STORE_COLOR fcolor
+#endif
+
+ fetch_offset = 0;
+ while (fetch_offset < width && *src == 0xFF) {
+ *linep++ = STORE_COLOR;
+ ++src;
+ ++fetch_offset;
+ }
+ IM_GLIN(im, x+fetch_offset, x+width, y, linep);
+ while (fetch_offset < width) {
+#ifdef IM_EIGHT_BIT
+ IM_WORK_T alpha = *src++;
+#else
+ IM_WORK_T alpha = *src++ / 255.0;
+#endif
+ if (alpha == IM_SAMPLE_MAX)
+ *linep = STORE_COLOR;
+ else if (alpha) {
+ for (ch = 0; ch < channels; ++ch) {
+ linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha)
+ + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
+ }
+ }
+ ++linep;
+ ++fetch_offset;
+ }
+ IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
+}
+
+static
+void
+IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width,
+ unsigned char const *src, i_color const *color) {
+ IM_COLOR *linep = r->IM_SUFFIX(line);
+ int ch;
+ int alpha_channel = r->im->channels - 1;
+ int fetch_offset;
+#undef STORE_COLOR
+#ifdef IM_EIGHT_BIT
+#define STORE_COLOR (*color)
+#else
+ i_fcolor fcolor;
+
+ for (ch = 0; ch < r->im->channels; ++ch) {
+ fcolor.channel[ch] = color->channel[ch] / 255.0;
+ }
+#define STORE_COLOR fcolor
+#endif
+
+ fetch_offset = 0;
+ while (fetch_offset < width && *src == 0xFF) {
+ *linep++ = STORE_COLOR;
+ ++src;
+ ++fetch_offset;
+ }
+ IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
+ while (fetch_offset < width) {
+#ifdef IM_EIGHT_BIT
+ IM_WORK_T src_alpha = *src++;
+#else
+ IM_WORK_T src_alpha = *src++ / 255.0;
+#endif
+ if (src_alpha == IM_SAMPLE_MAX)
+ *linep = STORE_COLOR;
+ else if (src_alpha) {
+ IM_WORK_T remains = - src_alpha;
+ IM_WORK_T orig_alpha = linep->channel[alpha_channel];
+ IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
+ for (ch = 0; ch < alpha_channel; ++ch) {
+ linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
+ + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
+ ) / dest_alpha;
+ }
+ linep->channel[alpha_channel] = dest_alpha;
+ }
+ ++linep;
+ ++fetch_offset;
+ }
+ IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
+}
+
+#/code
--- /dev/null
+#ifndef IMAGER_RENDERT_H
+#define IMAGER_RENDERT_H
+
+typedef struct {
+ int magic;
+ i_img *im;
+ i_color *line_8;
+ i_fcolor *line_double;
+ int width;
+} i_render;
+
+#endif
#!perl -w
use Imager ':all';
-use Test::More tests => 64;
+use Test::More tests => 143;
use strict;
+use Imager::Test qw(test_image_raw test_image_16 is_color3 is_color1 is_image);
init_log("testout/t104ppm.log",1);
my $blue = i_color_new(0,0,255,255);
my $red = i_color_new(255,0,0,255);
-my $img = Imager::ImgRaw::new(150,150,3);
-
-i_box_filled($img,70,25,130,125,$green);
-i_box_filled($img,20,25,80,125,$blue);
-i_arc($img,75,75,30,0,361,$red);
-i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]);
+my $img = test_image_raw();
my $fh = openimage(">testout/t104.ppm");
my $IO = Imager::io_new_fd(fileno($fh));
my $ooim = Imager->new;
ok($ooim->read(file=>"testimg/simple.pbm"), "read simple pbm, via OO");
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 0), 255);
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 1), 0);
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 0), 0);
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 255);
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 0), 0);
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 1), 255);
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 0), 255);
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 0);
+is($ooim->type, 'paletted', "check pbm read as paletted");
+is($ooim->tags(name=>'pnm_type'), 1, "check pnm_type tag");
{
# https://rt.cpan.org/Ticket/Display.html?id=7465
# check the pixels
ok(my ($white, $grey, $green) = $maxval->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
- check_color($white, 255, 255, 255, "white pixel");
- check_color($grey, 130, 130, 130, "grey pixel");
- check_color($green, 125, 125, 0, "green pixel");
+ is_color3($white, 255, 255, 255, "white pixel");
+ is_color3($grey, 130, 130, 130, "grey pixel");
+ is_color3($green, 125, 125, 0, "green pixel");
+ is($maxval->tags(name=>'pnm_type'), 6, "check pnm_type tag on maxval");
# and do the same for ASCII images
my $maxval_asc = Imager->new;
is($maxval_asc->getchannels, 3, "channel count");
is($maxval_asc->getwidth, 3, "width");
is($maxval_asc->getheight, 1, "height");
+
+ is($maxval->tags(name=>'pnm_type'), 6, "check pnm_type tag on maxval");
# check the pixels
ok(my ($white_asc, $grey_asc, $green_asc) = $maxval_asc->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
- check_color($white_asc, 255, 255, 255, "white asc pixel");
- check_color($grey_asc, 130, 130, 130, "grey asc pixel");
- check_color($green_asc, 125, 125, 0, "green asc pixel");
+ is_color3($white_asc, 255, 255, 255, "white asc pixel");
+ is_color3($grey_asc, 130, 130, 130, "grey asc pixel");
+ is_color3($green_asc, 125, 125, 0, "green asc pixel");
}
{ # previously we didn't validate maxval at all, make sure it's
like($maxval65536->errstr, qr/maxval of 65536 is over 65535 - invalid pnm file/,
"error expected from reading maxval_65536.ppm");
- # maxval of 256 is valid, but Imager can't handle it yet in binary files
+ # maxval of 256 is valid, and handled as of 0.56
my $maxval256 = Imager->new;
- ok(!$maxval256->read(file=>'testimg/maxval_256.ppm'),
- "should fail reading maxval 256 image");
- print "# ",$maxval256->errstr,"\n";
- like($maxval256->errstr, qr/maxval of 256 is over 255 - not currently supported by Imager/,
- "error expected from reading maxval_256.ppm");
+ ok($maxval256->read(file=>'testimg/maxval_256.ppm'),
+ "should succeed reading maxval 256 image");
+ is_color3($maxval256->getpixel(x => 0, 'y' => 0),
+ 0, 0, 0, "check black in maxval_256");
+ is_color3($maxval256->getpixel(x => 0, 'y' => 1),
+ 255, 255, 255, "check white in maxval_256");
+ is($maxval256->bits, 16, "check bits/sample on maxval 256");
# make sure we handle maxval > 255 for ascii
my $maxval4095asc = Imager->new;
is($maxval4095asc->getchannels, 3, "channels");
is($maxval4095asc->getwidth, 3, "width");
is($maxval4095asc->getheight, 1, "height");
+ is($maxval4095asc->bits, 16, "check bits/sample on maxval 4095");
ok(my ($white, $grey, $green) = $maxval4095asc->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
- check_color($white, 255, 255, 255, "white 4095 pixel");
- check_color($grey, 128, 128, 128, "grey 4095 pixel");
- check_color($green, 127, 127, 0, "green 4095 pixel");
+ is_color3($white, 255, 255, 255, "white 4095 pixel");
+ is_color3($grey, 128, 128, 128, "grey 4095 pixel");
+ is_color3($green, 127, 127, 0, "green 4095 pixel");
}
{ # check i_format is set when reading a pnm file
Imager->set_file_limits(reset=>1);
}
+{
+ # check we correctly sync with the data stream
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/pgm.pgm', type => 'pnm'),
+ "read pgm.pgm");
+ print "# ", $im->getsamples('y' => 0), "\n";
+ is_color1($im->getpixel(x=>0, 'y' => 0), 254, "check top left");
+}
+
{ # check error messages set correctly
my $im = Imager->new(xsize=>100, ysize=>100, channels=>4);
ok(!$im->write(file=>"testout/t104_fail.ppm", type=>'pnm'),
"check error message");
}
+# various bad input files
+print "# check error handling\n";
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_bin.ppm', type=>'pnm'),
+ "fail to read short bin ppm");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_bin16.ppm', type=>'pnm'),
+ "fail to read short bin ppm (maxval 65535)");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_bin.pgm', type=>'pnm'),
+ "fail to read short bin pgm");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_bin16.pgm', type=>'pnm'),
+ "fail to read short bin pgm (maxval 65535)");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_bin.pbm', type => 'pnm'),
+ "fail to read a short bin pbm");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_asc.ppm', type => 'pnm'),
+ "fail to read a short asc ppm");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_asc.pgm', type => 'pnm'),
+ "fail to read a short asc pgm");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/short_asc.pbm', type => 'pnm'),
+ "fail to read a short asc pbm");
+ cmp_ok($im->errstr, '=~', 'short read - file truncated',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/bad_asc.ppm', type => 'pnm'),
+ "fail to read a bad asc ppm");
+ cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/bad_asc.pgm', type => 'pnm'),
+ "fail to read a bad asc pgm");
+ cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok(!$im->read(file => 'testimg/bad_asc.pbm', type => 'pnm'),
+ "fail to read a bad asc pbm");
+ cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm',
+ "check error message");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_bin.ppm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bin ppm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_bin16.ppm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bin16 ppm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+ is($im->bits, 16, "check correct bits");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_bin.pgm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bin pgm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_bin16.pgm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bin16 pgm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_bin.pbm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bin pbm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_asc.ppm', type => 'pnm',
+ allow_partial => 1),
+ "partial read asc ppm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_asc.pgm', type => 'pnm',
+ allow_partial => 1),
+ "partial read asc pgm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/short_asc.pbm', type => 'pnm',
+ allow_partial => 1),
+ "partial read asc pbm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/bad_asc.ppm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bad asc ppm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/bad_asc.pgm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bad asc pgm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ my $im = Imager->new;
+ ok($im->read(file => 'testimg/bad_asc.pbm', type => 'pnm',
+ allow_partial => 1),
+ "partial read bad asc pbm");
+ is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+ is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+ print "# monochrome output\n";
+ my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
+ ok($im->addcolors(colors => [ '#000000', '#FFFFFF' ]),
+ "add black and white");
+ $im->box(filled => 1, xmax => 4, color => '#000000');
+ $im->box(filled => 1, xmin => 5, color => '#FFFFFF');
+ is($im->type, 'paletted', 'mono still paletted');
+ ok($im->write(file => 'testout/t104_mono.pbm', type => 'pnm'),
+ "save as pbm");
+
+ # check it
+ my $imread = Imager->new;
+ ok($imread->read(file => 'testout/t104_mono.pbm', type=>'pnm'),
+ "read it back in")
+ or print "# ", $imread->errstr, "\n";
+ is($imread->type, 'paletted', "check result is paletted");
+ is($imread->tags(name => 'pnm_type'), 4, "check type");
+}
+
+{
+ print "# 16-bit output\n";
+ my $data;
+ my $im = test_image_16();
+
+ # without tag, it should do 8-bit output
+ ok($im->write(data => \$data, type => 'pnm'),
+ "write 16-bit image as 8-bit/sample ppm");
+ my $im8 = Imager->new;
+ ok($im8->read(data => $data), "read it back");
+ is($im8->tags(name => 'pnm_maxval'), 255, "check maxval");
+ is_image($im, $im8, "check image matches");
+
+ # try 16-bit output
+ $im->settag(name => 'pnm_write_wide_data', value => 1);
+ $data = '';
+ ok($im->write(data => \$data, type => 'pnm'),
+ "write 16-bit image as 16-bit/sample ppm");
+ $im->write(file=>'testout/t104_16.ppm');
+ my $im16 = Imager->new;
+ ok($im16->read(data => $data), "read it back");
+ is($im16->tags(name => 'pnm_maxval'), 65535, "check maxval");
+ $im16->write(file=>'testout/t104_16b.ppm');
+ is_image($im, $im16, "check image matches");
+}
+
sub openimage {
my $fname = shift;
local(*FH);
is($g, $gray, "compare gray");
}
-sub check_color {
- my ($c, $red, $green, $blue, $note) = @_;
-
- my ($r, $g, $b) = $c->rgba;
- is_deeply([ $r, $g, $b], [ $red, $green, $blue ],
- "$note ($r, $g, $b) compared to ($red, $green, $blue)");
-}
print "# diff $diff\n";
ok($diff == 0, "image mismatch");
my $bad = Imager->new;
- ok($bad->read(file=>'testimg/comp4bad.tif'), "bad image not returned");
+ ok($bad->read(file=>'testimg/comp4bad.tif',
+ allow_partial=>1), "bad image not returned");
ok(scalar $bad->tags(name=>'i_incomplete'), "incomplete tag not set");
ok($img8->write(file=>'testout/t106_pal8.tif'), "writing 8-bit paletted");
my $cmp8 = Imager->new;
BEGIN { use_ok('Imager'); };
-require "t/testtools.pl";
-
init_log("testout/t15color.log",1);
my $c1 = Imager::Color->new(100, 150, 200, 250);
--- /dev/null
+P1
+2 2
+10
+0x
\ No newline at end of file
--- /dev/null
+P2
+2 2
+255
+255 255
+255 x
\ No newline at end of file
--- /dev/null
+P3
+2 2
+255
+255 255 255 255 255 255
+255 255 x
\ No newline at end of file
--- /dev/null
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+2 2
+255
+þýüû
\ No newline at end of file
--- /dev/null
+P1
+2 2
+01
+1
\ No newline at end of file
--- /dev/null
+P2
+2 2
+255
+255 255
+255
\ No newline at end of file
--- /dev/null
+P3
+2 2
+255
+255 255 255 255 255 255
+255 255 255
\ No newline at end of file
--- /dev/null
+P4
+16 2
+ú\80þ
\ No newline at end of file
--- /dev/null
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+2 2
+255
+ÿÿÿ
\ No newline at end of file
--- /dev/null
+P6
+# CREATOR: The GIMP's PNM Filter Version 1.0
+2 2
+255
+ÿÿÿÿÿÿÿÿÿ
\ No newline at end of file
--- /dev/null
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+2 2
+65535
+ÿÿÿÿÿÿ
\ No newline at end of file
--- /dev/null
+P6
+# CREATOR: The GIMP's PNM Filter Version 1.0
+2 2
+65535
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
\ No newline at end of file
/* do nothing */
}
-static i_img *read_one_tiff(TIFF *tif) {
+static i_img *read_one_tiff(TIFF *tif, int allow_partial) {
i_img *im;
uint32 width, height;
uint16 channels;
++row;
}
if (row < height) {
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_lines_read", row);
+ }
+ else {
+ i_img_destroy(im);
+ _TIFFfree(buffer);
+ return NULL;
+ }
error = 1;
}
/* Ideally we'd optimize the palette, but that could be expensive
uint32 newrows, i_row;
if (!TIFFReadRGBAStrip(tif, row, raster)) {
- error++;
- break;
+ if (allow_partial) {
+ i_tags_setn(&im->tags, "i_lines_read", row);
+ error++;
+ break;
+ }
+ else {
+ i_push_error(0, "could not read TIFF image strip");
+ _TIFFfree(raster);
+ i_img_destroy(im);
+ return NULL;
+ }
}
newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
}
if (error) {
mm_log((1, "i_readtiff_wiol: error during reading\n"));
- i_tags_addn(&im->tags, "i_incomplete", 0, 1);
+ i_tags_setn(&im->tags, "i_incomplete", 1);
}
if (raster)
_TIFFfree( raster );
=cut
*/
i_img*
-i_readtiff_wiol(io_glue *ig, int length, int page) {
+i_readtiff_wiol(io_glue *ig, int allow_partial, int page) {
TIFF* tif;
TIFFErrorHandler old_handler;
TIFFErrorHandler old_warn_handler;
/* Also add code to check for mmapped code */
io_glue_commit_types(ig);
- mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
+ mm_log((1, "i_readtiff_wiol(ig %p, allow_partial %d, page %d)\n", ig, allow_partial, page));
tif = TIFFClientOpen("(Iolayer)",
"rm",
}
}
- im = read_one_tiff(tif);
+ im = read_one_tiff(tif, allow_partial);
if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
TIFFSetErrorHandler(old_handler);
*count = 0;
do {
- i_img *im = read_one_tiff(tif);
+ i_img *im = read_one_tiff(tif, 0);
if (!im)
break;
if (++*count > result_alloc) {