Skip to content

Commit

Permalink
Partial support for writing PAM format
Browse files Browse the repository at this point in the history
  • Loading branch information
jsummers committed Nov 16, 2013
1 parent 1462ff5 commit 028d80c
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 31 deletions.
10 changes: 10 additions & 0 deletions src/imagew-allfmts.c
Expand Up @@ -74,6 +74,11 @@ IW_IMPL(int) iw_read_file_by_fmt(struct iw_context *ctx,
retval = iw_read_pnm_file(ctx,readdescr);
break;

case IW_FORMAT_PAM:
supported=1;
retval = iw_read_pam_file(ctx,readdescr);
break;

default:
iw_set_errorf(ctx,"Attempt to read unknown file format (%d)",fmt);
goto done;
Expand Down Expand Up @@ -148,6 +153,11 @@ IW_IMPL(int) iw_write_file_by_fmt(struct iw_context *ctx,
retval = iw_write_pnm_file(ctx,writedescr);
break;

case IW_FORMAT_PAM:
supported=1;
retval = iw_write_pam_file(ctx,writedescr);
break;

case IW_FORMAT_GIF:
break;

Expand Down
1 change: 1 addition & 0 deletions src/imagew-cmd.c
Expand Up @@ -402,6 +402,7 @@ static int get_fmt_from_name(const char *s)
if(!strcmp(s,"ppm")) return IW_FORMAT_PPM;
if(!strcmp(s,"pgm")) return IW_FORMAT_PGM;
if(!strcmp(s,"pbm")) return IW_FORMAT_PBM;
if(!strcmp(s,"pam")) return IW_FORMAT_PAM;
return IW_FORMAT_UNKNOWN;
}

Expand Down
128 changes: 100 additions & 28 deletions src/imagew-pnm.c
Expand Up @@ -268,11 +268,15 @@ IW_IMPL(int) iw_read_pnm_file(struct iw_context *ctx, struct iw_iodescr *iodescr
return retval;
}

IW_IMPL(int) iw_read_pam_file(struct iw_context *ctx, struct iw_iodescr *iodescr)
{
return iw_read_pnm_file(ctx, iodescr);
}

struct iwpnmwcontext {
struct iw_iodescr *iodescr;
struct iw_context *ctx;
struct iw_image *img;
const struct iw_palette *iwpal;
iw_byte *rowbuf;
int requested_output_format;
int actual_output_format;
Expand All @@ -284,7 +288,31 @@ static void iwpnm_write(struct iwpnmwcontext *wctx, const void *buf, size_t n)
(*wctx->iodescr->write_fn)(wctx->ctx,wctx->iodescr,buf,n);
}

static int iwpnm_write_ppm_main(struct iwpnmwcontext *wctx)
static int write_pam_header(struct iwpnmwcontext *wctx, int numchannels,
int maxcolorcode, const char *tupltype)
{
char tmpstring[80];

iw_snprintf(tmpstring, sizeof(tmpstring), "P7\n");
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
iw_snprintf(tmpstring, sizeof(tmpstring), "WIDTH %d\n", wctx->img->width);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
iw_snprintf(tmpstring, sizeof(tmpstring), "HEIGHT %d\n", wctx->img->height);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
iw_snprintf(tmpstring, sizeof(tmpstring), "DEPTH %d\n", numchannels);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
iw_snprintf(tmpstring, sizeof(tmpstring), "MAXVAL %d\n", maxcolorcode);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
iw_snprintf(tmpstring, sizeof(tmpstring), "TUPLTYPE %s\n", tupltype);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
iw_snprintf(tmpstring, sizeof(tmpstring), "ENDHDR\n");
iwpnm_write(wctx, tmpstring, strlen(tmpstring));

return 1;
}

// Write a PPM or PAM-RGB file.
static int iwpnm_write_rgb_main(struct iwpnmwcontext *wctx)
{
struct iw_image *img;
int retval = 0;
Expand Down Expand Up @@ -316,24 +344,29 @@ static int iwpnm_write_ppm_main(struct iwpnmwcontext *wctx)
if(wctx->img->maxcolorcode[IW_CHANNELTYPE_GREEN] != wctx->maxcolorcode ||
wctx->img->maxcolorcode[IW_CHANNELTYPE_BLUE] != wctx->maxcolorcode)
{
iw_set_error(wctx->ctx,"PNM/PPM format requires equal bit depths");
iw_set_error(wctx->ctx,"PNM/PPM/PAM format requires equal bit depths");
goto done;
}
}
}

if(wctx->maxcolorcode<1 || wctx->maxcolorcode>65535) {
iw_set_error(wctx->ctx,"Unsupported PPM bit depth");
iw_set_error(wctx->ctx,"Unsupported PPM/PAM bit depth");
goto done;
}

outrowsize = bytes_per_ppm_pixel*img->width;
wctx->rowbuf = iw_mallocz(wctx->ctx, outrowsize);
if(!wctx->rowbuf) goto done;

iw_snprintf(tmpstring, sizeof(tmpstring), "P6\n%d %d\n%d\n", img->width,
img->height, wctx->maxcolorcode);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
if(wctx->requested_output_format==IW_FORMAT_PAM) {
write_pam_header(wctx, 3, wctx->maxcolorcode, "RGB");
}
else {
iw_snprintf(tmpstring, sizeof(tmpstring), "P6\n%d %d\n%d\n", img->width,
img->height, wctx->maxcolorcode);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
}

for(j=0;j<img->height;j++) {
if(img->imgtype==IW_IMGTYPE_RGB && img->bit_depth==8) {
Expand Down Expand Up @@ -369,23 +402,47 @@ static int iwpnm_write_ppm_main(struct iwpnmwcontext *wctx)
return retval;
}

static int iwpnm_write_pgm_main(struct iwpnmwcontext *wctx)
// Returns 1 if the image is paletted, and has a grayscale palette with
// exactly 2 entries.
static int has_bw_palette(struct iwpnmwcontext *wctx, struct iw_image *img)
{
const struct iw_palette *iwpal;

if(img->imgtype!=IW_IMGTYPE_PALETTE) return 0;
if(!iw_get_value(wctx->ctx,IW_VAL_OUTPUT_PALETTE_GRAYSCALE)) return 0;

iwpal = iw_get_output_palette(wctx->ctx);
if(iwpal->num_entries != 2) return 0;

return 1;
}

// Write a PGM or PAM-GRAYSCALE or PAM-BLACKANDWHITE file.
static int iwpnm_write_gray_main(struct iwpnmwcontext *wctx)
{
struct iw_image *img;
int retval = 0;
int j;
size_t outrowsize;
char tmpstring[80];
int bytes_per_ppm_pixel;
int is_bilevel = 0;

img = wctx->img;

if(img->imgtype!=IW_IMGTYPE_GRAY) {
if(has_bw_palette(wctx, img)) {
is_bilevel = 1;
}
else if(img->imgtype!=IW_IMGTYPE_GRAY) {
iw_set_error(wctx->ctx,"Cannot write non-grayscale image to PGM file");
goto done;
}

if(img->bit_depth==8) {
if(is_bilevel) {
bytes_per_ppm_pixel=1;
wctx->maxcolorcode = 1;
}
else if(img->bit_depth==8) {
bytes_per_ppm_pixel=1;
wctx->maxcolorcode = 255;
}
Expand All @@ -410,9 +467,19 @@ static int iwpnm_write_pgm_main(struct iwpnmwcontext *wctx)
wctx->rowbuf = iw_mallocz(wctx->ctx, outrowsize);
if(!wctx->rowbuf) goto done;

iw_snprintf(tmpstring, sizeof(tmpstring), "P5\n%d %d\n%d\n", img->width,
img->height, wctx->maxcolorcode);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
if(wctx->requested_output_format==IW_FORMAT_PAM) {
if(is_bilevel) {
write_pam_header(wctx, 1, 1, "BLACKANDWHITE");
}
else {
write_pam_header(wctx, 1, wctx->maxcolorcode, "GRAYSCALE");
}
}
else {
iw_snprintf(tmpstring, sizeof(tmpstring), "P5\n%d %d\n%d\n", img->width,
img->height, wctx->maxcolorcode);
iwpnm_write(wctx, tmpstring, strlen(tmpstring));
}

for(j=0;j<img->height;j++) {
iwpnm_write(wctx, &img->pixels[j*img->bpr], outrowsize);
Expand All @@ -429,24 +496,12 @@ static int iwpnm_write_pbm_main(struct iwpnmwcontext *wctx)
struct iw_image *img;
int retval = 0;
int i,j;
int ok;
size_t outrowsize;
char tmpstring[80];

img = wctx->img;

// Make sure the image is paletted, and has a grayscale palette with
// exactly 2 entries.
ok=0;
if(img->imgtype==IW_IMGTYPE_PALETTE) {
if(iw_get_value(wctx->ctx,IW_VAL_OUTPUT_PALETTE_GRAYSCALE)) {
wctx->iwpal = iw_get_output_palette(wctx->ctx);
if(wctx->iwpal->num_entries == 2) {
ok=1;
}
}
}
if(!ok) {
if(!has_bw_palette(wctx, img)) {
iw_set_error(wctx->ctx,"Cannot write this image type to a PBM file");
goto done;
}
Expand Down Expand Up @@ -519,14 +574,26 @@ IW_IMPL(int) iw_write_pnm_file(struct iw_context *ctx, struct iw_iodescr *iodesc
ret=0;
switch(wctx->actual_output_format) {
case IW_FORMAT_PPM:
ret = iwpnm_write_ppm_main(wctx);
ret = iwpnm_write_rgb_main(wctx);
break;
case IW_FORMAT_PGM:
ret = iwpnm_write_pgm_main(wctx);
ret = iwpnm_write_gray_main(wctx);
break;
case IW_FORMAT_PBM:
ret = iwpnm_write_pbm_main(wctx);
break;
case IW_FORMAT_PAM:
if(wctx->img->imgtype==IW_IMGTYPE_RGB) {
ret = iwpnm_write_rgb_main(wctx);
}
else if(wctx->img->imgtype==IW_IMGTYPE_GRAY || wctx->img->imgtype==IW_IMGTYPE_PALETTE) {
ret = iwpnm_write_gray_main(wctx);
}
else {
iw_set_error(wctx->ctx,"Unsupported image type for PAM");
goto done;
}
break;
default:
iw_set_error(wctx->ctx,"Internal: Bad image type for PNM");
goto done;
Expand All @@ -544,3 +611,8 @@ IW_IMPL(int) iw_write_pnm_file(struct iw_context *ctx, struct iw_iodescr *iodesc
}
return retval;
}

IW_IMPL(int) iw_write_pam_file(struct iw_context *ctx, struct iw_iodescr *iodescr)
{
return iw_write_pnm_file(ctx, iodescr);
}
14 changes: 13 additions & 1 deletion src/imagew-util.c
Expand Up @@ -453,6 +453,7 @@ IW_IMPL(int) iw_detect_fmt_from_filename(const char *fn)
{"pbm", IW_FORMAT_PBM},
{"pgm", IW_FORMAT_PGM},
{"ppm", IW_FORMAT_PPM},
{"pam", IW_FORMAT_PAM},
{NULL, 0}
};

Expand Down Expand Up @@ -483,6 +484,7 @@ IW_IMPL(const char*) iw_get_fmt_name(int fmt)
case IW_FORMAT_PBM: n="PBM"; break;
case IW_FORMAT_PGM: n="PGM"; break;
case IW_FORMAT_PPM: n="PPM"; break;
case IW_FORMAT_PAM: n="PAM"; break;
}
return n;
}
Expand Down Expand Up @@ -516,9 +518,12 @@ IW_IMPL(int) iw_detect_fmt_of_file(const iw_byte *buf, size_t n)
{
fmt=IW_FORMAT_WEBP;
}
else if(buf[0]=='P' && (buf[1]>='1' && buf[1]<='7')) {
else if(buf[0]=='P' && (buf[1]>='1' && buf[1]<='6')) {
return IW_FORMAT_PNM;
}
else if(buf[0]=='P' && (buf[1]=='7' && buf[2]==0x0a)) {
return IW_FORMAT_PAM;
}

return fmt;
}
Expand Down Expand Up @@ -562,6 +567,11 @@ IW_IMPL(unsigned int) iw_get_profile_by_fmt(int fmt)
#endif
break;

case IW_FORMAT_PAM:
p = IW_PROFILE_16BPS | IW_PROFILE_REDUCEDBITDEPTHS | IW_PROFILE_GRAYSCALE |
IW_PROFILE_GRAY1 | IW_PROFILE_TRANSPARENCY;
break;

case IW_FORMAT_PNM:
p = IW_PROFILE_16BPS | IW_PROFILE_REDUCEDBITDEPTHS | IW_PROFILE_GRAYSCALE |
IW_PROFILE_GRAY1;
Expand Down Expand Up @@ -608,6 +618,7 @@ IW_IMPL(int) iw_is_input_fmt_supported(int fmt)
case IW_FORMAT_GIF:
case IW_FORMAT_BMP:
case IW_FORMAT_PNM:
case IW_FORMAT_PAM:
return 1;
}
return 0;
Expand All @@ -632,6 +643,7 @@ IW_IMPL(int) iw_is_output_fmt_supported(int fmt)
case IW_FORMAT_PPM:
case IW_FORMAT_PGM:
case IW_FORMAT_PBM:
case IW_FORMAT_PAM:
return 1;
}
return 0;
Expand Down
7 changes: 5 additions & 2 deletions src/imagew.h
Expand Up @@ -158,9 +158,10 @@ extern "C" {
// When reading a file, we'll consider everything to be PNM.
// When writing a file, we may need to distinguish between the subtypes.
#define IW_FORMAT_PNM 8
#define IW_FORMAT_PBM 9 // Reserved
#define IW_FORMAT_PGM 10 // Reserved
#define IW_FORMAT_PBM 9
#define IW_FORMAT_PGM 10
#define IW_FORMAT_PPM 11
#define IW_FORMAT_PAM 12

// These codes are used tell IW about the capabilities of the output format,
// so that it can make good decisions about what to do.
Expand Down Expand Up @@ -711,6 +712,8 @@ IW_EXPORT(int) iw_read_gif_file(struct iw_context *ctx, struct iw_iodescr *iodes
IW_EXPORT(int) iw_read_pnm_file(struct iw_context *ctx, struct iw_iodescr *iodescr);
// The output format can be refined by setting IW_VAL_OUTPUT_FORMAT.
IW_EXPORT(int) iw_write_pnm_file(struct iw_context *ctx, struct iw_iodescr *iodescr);
IW_EXPORT(int) iw_read_pam_file(struct iw_context *ctx, struct iw_iodescr *iodescr);
IW_EXPORT(int) iw_write_pam_file(struct iw_context *ctx, struct iw_iodescr *iodescr);
IW_EXPORT(char*) iw_get_libwebp_dec_version_string(char *s, int s_len);
IW_EXPORT(char*) iw_get_libwebp_enc_version_string(char *s, int s_len);

Expand Down

0 comments on commit 028d80c

Please sign in to comment.