Skip to content

Commit

Permalink
implement spatial (vs pixel) dimensions through tags for PNG files
Browse files Browse the repository at this point in the history
  • Loading branch information
Tony Cook committed Jul 28, 2001
1 parent 4eaa6f2 commit a0f08fc
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 11 deletions.
3 changes: 3 additions & 0 deletions Imager/Changes
Expand Up @@ -461,6 +461,9 @@ Revision history for Perl extension Imager.
- freetype 2 support
- exposed the matrix_transform() function to the OO interface
- added Imager::Matrix2d convenience class
- support for setting the resolution when writing to PNG
- retrieve physical resolution to tags when reading from PNG
- found an XS bug in the interface to i_tags_add()

=================================================================

Expand Down
20 changes: 20 additions & 0 deletions Imager/Imager.pm
Expand Up @@ -3376,6 +3376,26 @@ the first block of the first gif comment before each image.
Where applicable, the ("name") is the name of that field from the GIF89
standard.
Some standard tags will be implemented as time goes by:
=over
=item i_xres
=item 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.
=item 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 rations
then this is scaled so the smaller value is 72dpi.
=back
=head1 BUGS
box, arc, circle do not support antialiasing yet. arc, is only filled
Expand Down
2 changes: 1 addition & 1 deletion Imager/Imager.xs
Expand Up @@ -2613,7 +2613,7 @@ i_tags_add(im, name, code, data, idata)
else
name = NULL;
if (SvOK(ST(3)))
name = SvPV(ST(3), len);
data = SvPV(ST(3), len);
else {
data = NULL;
len = 0;
Expand Down
2 changes: 2 additions & 0 deletions Imager/TODO
Expand Up @@ -25,6 +25,8 @@ MultiImage & metadata support:
local errors?
- SEE design/represent.txt for proposed new structure and
interface design that takes these factors into account.
- define common i_* tags for specifying attribute common among images
like spatial resolution

New Features:
- Add mng support.
Expand Down
51 changes: 51 additions & 0 deletions Imager/png.c
Expand Up @@ -110,6 +110,8 @@ check_if_png(char *file_name, FILE **fp) {
return(!png_sig_cmp((png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
}

static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);

/* Read a PNG file. You may want to return an error code if the read
* fails (depending upon the failure). There are two "prototypes" given
* here - one where we are given the filename, and we need to open the
Expand Down Expand Up @@ -246,6 +248,9 @@ i_readpng(int fd) {
/* read rest of file, and get additional chunks in info_ptr - REQUIRED */

png_read_end(png_ptr, info_ptr);

get_png_tags(im, png_ptr, info_ptr);

/* clean up after the read, and free any memory allocated - REQUIRED */
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);

Expand All @@ -263,6 +268,10 @@ i_writepng(i_img *im,int fd) {
png_infop info_ptr;
int width,height,y;
volatile int cspace,channels;
double xres, yres;
int aspect_only, have_res;
double offx, offy;
char offunit[20] = "pixel";

mm_log((1,"i_writepng(0x%x,fd %d)\n",im,fd));

Expand Down Expand Up @@ -331,6 +340,28 @@ i_writepng(i_img *im,int fd) {
png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

have_res = 1;
if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
; /* nothing to do */
else
yres = xres;
}
else {
if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
xres = yres;
else
have_res = 0;
}
if (have_res) {
aspect_only = 0;
i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
xres /= 0.0254;
yres /= 0.0254;
png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
}

png_write_info(png_ptr, info_ptr);
if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
for (y = 0; y < height; y++)
Expand Down Expand Up @@ -432,6 +463,8 @@ i_readpng_scalar(char *data, int length) {
for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); }
mm_log((1,"made it to here 2\n"));
png_read_end(png_ptr, info_ptr);
get_png_tags(im, png_ptr, info_ptr);

mm_log((1,"made it to here 3\n"));
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
mm_log((1,"made it to here 4\n"));
Expand Down Expand Up @@ -516,3 +549,21 @@ i_readpng_scalar(char *data, int length) {

/* return im; */
/* } */

static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
png_uint_32 xres, yres;
int unit_type;
if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
if (unit_type == PNG_RESOLUTION_METER) {
i_tags_set_float(&im->tags, "i_xres", 0, xres * 0.0254);
i_tags_set_float(&im->tags, "i_yres", 0, xres * 0.0254);
}
else {
i_tags_addn(&im->tags, "i_xres", 0, xres);
i_tags_addn(&im->tags, "i_yres", 0, yres);
i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
}
}
}

31 changes: 21 additions & 10 deletions Imager/t/t102png.t
Expand Up @@ -7,7 +7,7 @@
# (It may become useful if the test is moved to ./t subdirectory.)
use lib qw(blib/lib blib/arch);

BEGIN { $| = 1; print "1..10\n"; }
BEGIN { $| = 1; print "1..12\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager qw(:all);

Expand Down Expand Up @@ -36,10 +36,14 @@ i_box_filled($timg, 0, 0, 20, 20, $green);
i_box_filled($timg, 2, 2, 18, 18, $trans);

if (!i_has_format("png")) {
for (2..10) {
for (2..12) {
print "ok $_ # skip no png support\n";
}
} else {
Imager::i_tags_add($img, "i_xres", 0, "300", 0);
Imager::i_tags_add($img, "i_yres", 0, undef, 200);
# the following confuses the GIMP
#Imager::i_tags_add($img, "i_aspect_only", 0, undef, 1);
open(FH,">testout/t102.png") || die "cannot open testout/t102.png for writing\n";
binmode(FH);
i_writepng($img,fileno(FH)) || print "not ";
Expand All @@ -57,14 +61,21 @@ if (!i_has_format("png")) {
print i_img_diff($img, $cmpimg)
? "not ok 4 # saved image different\n" : "ok 4\n";

my %tags = map { Imager::i_tags_get($img, $_) }
0..Imager::i_tags_count($img) - 1;
abs($tags{i_xres} - 300) < 1 or print "not ";
print "ok 5 # i_xres: $tags{i_xres}\n";
abs($tags{i_yres} - 200) < 1 or print "not ";
print "ok 6 # i_yres: $tags{i_yres}\n";

open FH, "> testout/t102_trans.png"
or die "Cannot open testout/t102_trans.png: $!";
binmode FH;
if (i_writepng($timg, fileno(FH))) {
print "ok 5\n";
print "ok 7\n";
}
else {
print "ok 5 # skip - png transparency not yet implemented\n";
print "ok 7 # skip - png transparency not yet implemented\n";
}
close FH;

Expand All @@ -74,10 +85,10 @@ if (!i_has_format("png")) {
$cmpimg=i_readpng(fileno(FH)) || print "not ";
close(FH);

print "ok 6\n";
print "ok 8\n";
print "# png average mean square pixel difference: ",sqrt(i_img_diff($timg,$cmpimg))/150*150,"\n";
print i_img_diff($timg, $cmpimg)
? "not ok 7 # saved image different\n" : "ok 7\n";
? "not ok 9 # saved image different\n" : "ok 9\n";

# REGRESSION TEST
# png.c 1.1 would produce an incorrect image when loading images with
Expand All @@ -88,24 +99,24 @@ if (!i_has_format("png")) {
# 1.1 may segfault here (it does with libefence)
my $pimg = i_readpng(fileno(FH))
or print "not ";
print "ok 8\n";
print "ok 10\n";
close FH;
open FH, "< testimg/palette_out.png"
or die "cannot open testimg/palette_out.png: $!\n";
binmode FH;
my $poimg = i_readpng(fileno(FH))
or print "not ";
print "ok 9\n";
print "ok 11\n";
close FH;
if (i_img_diff($pimg, $poimg)) {
print <<EOS;
not ok 10 # regression or you may need a more recent libpng
not ok 12 # regression or you may need a more recent libpng
# this tests a bug in Imager's png.c v1.1
# if also tickles a bug in libpng before 1.0.5, so you may need to
# upgrade libpng
EOS
}
else {
print "ok 10\n";
print "ok 12\n";
}
}
82 changes: 82 additions & 0 deletions Imager/tags.c
Expand Up @@ -216,3 +216,85 @@ int i_tags_delbycode(i_img_tags *tags, int code) {
return count;
}

int i_tags_get_float(i_img_tags *tags, char *name, int code, double *value) {
int index;
i_img_tag *entry;

if (name) {
if (!i_tags_find(tags, name, 0, &index))
return 0;
}
else {
if (!i_tags_findn(tags, code, 0, &index))
return 0;
}
entry = tags->tags+index;
if (entry->data)
*value = atof(entry->data);
else
*value = entry->idata;

return 1;
}

int i_tags_set_float(i_img_tags *tags, char *name, int code, double value) {
char temp[40];

sprintf(temp, "%.30g", value);
if (name)
i_tags_delbyname(tags, name);
else
i_tags_delbycode(tags, code);

return i_tags_add(tags, name, code, temp, strlen(temp), 0);
}

int i_tags_get_int(i_img_tags *tags, char *name, int code, int *value) {
int index;
i_img_tag *entry;

if (name) {
if (!i_tags_find(tags, name, 0, &index))
return 0;
}
else {
if (!i_tags_findn(tags, code, 0, &index))
return 0;
}
entry = tags->tags+index;
if (entry->data)
*value = atoi(entry->data);
else
*value = entry->idata;

return 1;
}

int i_tags_get_string(i_img_tags *tags, char *name, int code,
char *value, size_t value_size) {
int index;
i_img_tag *entry;

if (name) {
if (!i_tags_find(tags, name, 0, &index))
return 0;
}
else {
if (!i_tags_findn(tags, code, 0, &index))
return 0;
}
entry = tags->tags+index;
if (entry->data) {
size_t cpsize = value_size < entry->size ? value_size : entry->size;
memcpy(value, entry->data, cpsize);
if (cpsize == value_size)
--cpsize;
value[cpsize] = '\0';
}
else {
sprintf(value, "%d", entry->data);
}

return 1;
}

0 comments on commit a0f08fc

Please sign in to comment.