Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Various changes:

 - fix drawing text on 2/4 channel images for FT2
 - reading ASCII PBMs was broken, it assumed there was whitespace 
   between samples but there doesn't need to be
 - add makemap type of mono/monochrome for producing monochrome images
 - roughly tripled speed of reading any sort of PNM
 - reading a pnm can now return a partial image if you set allow_partial
 - reading a bmp can now return a partial image if you set allow_partial
 - we can now read 16-bit/sample binary PGM/PPM images
 - we can now write 16-bit/sample binary PGM/PPM files if explicitly 
   requested (since GIMP can't read them)
 - reading a tiff will now only return an incomplete image if you set 
   allow_partial
 - some documentation reformatting
  • Loading branch information...
commit 9c106321e22c5a74d0e5d946b452db24e1c9d6f7 1 parent 31d007e
Tony Cook authored
Showing with 1,577 additions and 280 deletions.
  1. +6 −4 Imager.pm
  2. +8 −5 Imager.xs
  3. +1 −0  MANIFEST
  4. +1 −1  Makefile.PL
  5. +4 −4 TODO
  6. +131 −53 bmp.c
  7. +19 −12 freetyp2.c
  8. +69 −0 image.c
  9. +6 −3 imager.h
  10. +3 −0  imdatatypes.h
  11. +14 −0 imrender.h
  12. +37 −1 lib/Imager/Files.pod
  13. +42 −26 lib/Imager/ImageTypes.pod
  14. +153 −2 lib/Imager/Test.pm
  15. +491 −125 pnm.c
  16. +18 −0 quant.c
  17. +204 −0 render.im
  18. +12 −0 rendert.h
  19. +275 −33 t/t104ppm.t
  20. +2 −1  t/t106tiff.t
  21. +0 −2  t/t15color.t
  22. +4 −0 testimg/bad_asc.pbm
  23. +5 −0 testimg/bad_asc.pgm
  24. +5 −0 testimg/bad_asc.ppm
  25. BIN  testimg/maxval_256.ppm
  26. BIN  testimg/pbm_base.pgm
  27. +5 −0 testimg/pgm.pgm
  28. +4 −0 testimg/short_asc.pbm
  29. +5 −0 testimg/short_asc.pgm
  30. +5 −0 testimg/short_asc.ppm
  31. +3 −0  testimg/short_bin.pbm
  32. +5 −0 testimg/short_bin.pgm
  33. +5 −0 testimg/short_bin.ppm
  34. +5 −0 testimg/short_bin16.pgm
  35. +5 −0 testimg/short_bin16.ppm
  36. +25 −8 tiff.c
View
10 Imager.pm
@@ -1269,11 +1269,13 @@ sub read {
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;
}
@@ -1282,7 +1284,7 @@ sub read {
}
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;
@@ -1301,7 +1303,7 @@ sub read {
}
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;
View
13 Imager.xs
@@ -535,6 +535,8 @@ static struct value_name make_color_names[] =
{ "webmap", mc_web_map, },
{ "addi", mc_addi, },
{ "mediancut", mc_median_cut, },
+ { "mono", mc_mono, },
+ { "monochrome", mc_mono, },
};
static struct value_name translate_names[] =
@@ -2250,9 +2252,9 @@ i_test_format_probe(ig, length)
#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
@@ -2881,9 +2883,9 @@ i_readgif_multi_wiol(ig)
Imager::ImgRaw
-i_readpnm_wiol(ig, length)
+i_readpnm_wiol(ig, allow_partial)
Imager::IO ig
- int length
+ int allow_partial
undef_int
@@ -2912,8 +2914,9 @@ i_writebmp_wiol(im,ig)
Imager::IO ig
Imager::ImgRaw
-i_readbmp_wiol(ig)
+i_readbmp_wiol(ig, allow_partial=0)
Imager::IO ig
+ int allow_partial
undef_int
View
1  MANIFEST
@@ -173,6 +173,7 @@ raw.c
regmach.c
regmach.h
regops.perl
+render.im
rgb.c Reading and writing SGI rgb files
rotate.c
rubthru.im
View
2  Makefile.PL
@@ -157,7 +157,7 @@ my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o
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 };
View
8 TODO
@@ -187,7 +187,7 @@ MultiImage & metadata support:
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
@@ -209,7 +209,7 @@ New Features:
(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:
@@ -304,7 +304,7 @@ Format specific issues:
- 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
@@ -322,7 +322,7 @@ Format specific issues:
- 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
View
184 bmp.c
@@ -45,14 +45,14 @@ static int write_8bit_data(io_glue *ig, i_img *im);
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)
@@ -102,7 +102,7 @@ BI_BITFIELDS images too, but I need a test image.
*/
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;
@@ -140,22 +140,25 @@ i_readbmp_wiol(io_glue *ig) {
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:
@@ -663,9 +666,9 @@ Returns the image or NULL.
*/
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;
@@ -690,17 +693,18 @@ read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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) {
@@ -744,9 +748,16 @@ read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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;
@@ -782,7 +793,7 @@ point.
*/
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;
@@ -791,23 +802,25 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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;
@@ -856,9 +869,16 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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;
@@ -884,9 +904,16 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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;
@@ -914,9 +941,16 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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];
@@ -929,9 +963,16 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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;
@@ -956,7 +997,7 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
}
/*
-=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.
@@ -966,9 +1007,9 @@ Returns the image or NULL.
*/
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;
@@ -980,17 +1021,18 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
}
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) {
@@ -1032,9 +1074,16 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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;
@@ -1052,9 +1101,16 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
/* 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]);
@@ -1074,9 +1130,16 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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];
@@ -1087,9 +1150,16 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
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;
@@ -1129,7 +1199,7 @@ static struct bm_masks std_masks[] =
};
/*
-=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.
@@ -1139,7 +1209,8 @@ Returns the image or NULL.
*/
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;
@@ -1244,10 +1315,17 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count,
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)
View
31 freetyp2.c
@@ -645,6 +645,7 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
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));
@@ -663,6 +664,9 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
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];
@@ -688,6 +692,8 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
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;
@@ -698,6 +704,8 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
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) {
@@ -728,20 +736,16 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
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;
}
}
@@ -751,6 +755,9 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
ty -= slot->advance.y / 64;
}
+ if (aa)
+ i_render_done(&render);
+
return 1;
}
View
69 image.c
@@ -2153,8 +2153,77 @@ i_test_format_probe(io_glue *data, int length) {
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
View
9 imager.h
@@ -352,6 +352,7 @@ extern i_img *i_img_16_new_low(i_img *im, int x, int y, int ch);
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);
@@ -363,7 +364,7 @@ undef_int i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor);
#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);
@@ -401,11 +402,11 @@ void i_qdist(i_img *im);
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]);
@@ -560,4 +561,6 @@ void malloc_state(void);
#endif /* IMAGER_MALLOC_DEBUG */
+#include "imrender.h"
+
#endif
View
3  imdatatypes.h
@@ -387,6 +387,7 @@ typedef enum i_make_colors_tag {
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;
@@ -518,5 +519,7 @@ enum {
#include "iolayert.h"
+#include "rendert.h"
+
#endif
View
14 imrender.h
@@ -0,0 +1,14 @@
+#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
View
38 lib/Imager/Files.pod
@@ -52,6 +52,10 @@ supply the filename:
$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:
@@ -362,6 +366,38 @@ Imager can read both the ASCII and binary versions of each of the PBM
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
@@ -383,7 +419,7 @@ to control output:
=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
View
68 lib/Imager/ImageTypes.pod
@@ -679,13 +679,12 @@ some standard information.
=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);
@@ -697,28 +696,39 @@ is converted. A floating point number stored as a string.
$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
@@ -836,23 +846,29 @@ change. Possible values are:
=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.
View
155 lib/Imager/Test.pm
@@ -4,7 +4,7 @@ use Test::Builder;
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) = @_;
@@ -25,6 +25,145 @@ sub diff_text_with_nul {
"$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__
@@ -51,7 +190,19 @@ No functions are exported by default.
=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
View
616 pnm.c
@@ -15,7 +15,7 @@ pnm.c - implements reading and writing ppm/pnm/pbm files, uses io layer.
=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);
@@ -75,9 +75,11 @@ Returns a pointer to the byte or NULL on failure (internal).
=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;
@@ -88,7 +90,6 @@ gnext(mbuf *mb) {
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;
}
@@ -108,9 +109,11 @@ the byte or NULL on failure (internal).
=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;
@@ -121,7 +124,6 @@ gpeek(mbuf *mb) {
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;
}
@@ -129,7 +131,27 @@ gpeek(mbuf *mb) {
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;
+}
/*
@@ -202,6 +224,10 @@ gnum(mbuf *mb, int *i) {
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);
@@ -209,35 +235,307 @@ gnum(mbuf *mb, int *i) {
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);
@@ -327,11 +625,6 @@ i_readpnm_wiol(io_glue *ig, int length) {
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;
@@ -350,71 +643,172 @@ i_readpnm_wiol(io_glue *ig, int length) {
}
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();
@@ -424,83 +818,55 @@ i_writeppm_wiol(i_img *im, io_glue *ig) {
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);
@@ -512,7 +878,7 @@ i_writeppm_wiol(i_img *im, io_glue *ig) {
=head1 AUTHOR
-Arnar M. Hrafnkelsson <addi@umich.edu>
+Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook<tony@imager.perl.org>
=head1 SEE ALSO
View
18 quant.c
@@ -6,6 +6,7 @@
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
@@ -71,6 +72,10 @@ i_quant_makemap(i_quantize *quant, i_img **imgs, int count) {
makemap_mediancut(quant, imgs, count);
break;
+ case mc_mono:
+ makemap_mono(quant);
+ break;
+
case mc_addi:
default:
makemap_addi(quant, imgs, count);
@@ -697,6 +702,19 @@ makemap_mediancut(i_quantize *quant, i_img **imgs, int 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
View
204 render.im
@@ -0,0 +1,204 @@
+/*
+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
View
12 rendert.h
@@ -0,0 +1,12 @@
+#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
View
308 t/t104ppm.t
@@ -1,7 +1,8 @@
#!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);
@@ -9,12 +10,7 @@ my $green = i_color_new(0,255,0,255);
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));
@@ -65,10 +61,12 @@ is(i_img_diff($gimg, $gcmpimg), 0,
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
@@ -87,9 +85,10 @@ check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 255);
# 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;
@@ -103,12 +102,14 @@ check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 255);
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
@@ -127,13 +128,15 @@ check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 255);
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;
@@ -142,11 +145,12 @@ check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 255);
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
@@ -194,6 +198,15 @@ check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 255);
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'),
@@ -206,6 +219,242 @@ check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 255);
"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',