/* (c) 2001 Karel 'Clock' Kulhavy * Serves the purpose to manipulate grayscale PNG images according to a * linear command list. * commandline format: improcess input_filename command_filename output_filename * Internal format: a 2D field of unsigned's * 0x000000 black * 0xffffff white * Commandfile format: * command argument1 argument2... * command argument1 argument2... * . * . * . */ #include #include #include int *image; int xs,ys; int force_gamma_1; void read_png(unsigned char *filename) { unsigned char *temporary_array; png_structp png_ptr; png_infop info_ptr; double gamma; int y1,number_of_passes; unsigned char **ptrs; FILE *f; f=fopen(filename,"r"); png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); info_ptr=png_create_info_struct(png_ptr); png_init_io(png_ptr,f); png_read_info(png_ptr, info_ptr); xs=png_get_image_width(png_ptr,info_ptr); ys=png_get_image_height(png_ptr,info_ptr); if (png_get_gAMA(png_ptr,info_ptr, &gamma)) { if (force_gamma_1) png_set_gamma(png_ptr, 1.0, 1.0); /* Forcing gamma is here for repairing files after processing * with GIMP, which deliberately writes gamma=00 00 b1 8f * even when input gamma was 00 01 86 a0 and no gamma setting * change is performed by user (simply saind, GIMP is * braindead) */ else png_set_gamma(png_ptr, 1.0, gamma); } else { png_set_gamma(png_ptr, 1.0, 0.454545); } { int bit_depth; int color_type; color_type=png_get_color_type(png_ptr, info_ptr); bit_depth=png_get_bit_depth(png_ptr, info_ptr); if (color_type==PNG_COLOR_TYPE_GRAY){ if (bit_depth<8){ png_set_expand(png_ptr); } if (bit_depth==16){ png_set_strip_16(png_ptr); } } if (color_type==PNG_COLOR_TYPE_PALETTE){ png_set_expand(png_ptr); png_set_rgb_to_gray(png_ptr,1,54.0/256,183.0/256); } if (color_type & PNG_COLOR_MASK_ALPHA){ png_set_strip_alpha(png_ptr); } if (color_type==PNG_COLOR_TYPE_RGB || color_type==PNG_COLOR_TYPE_RGB_ALPHA){ png_set_rgb_to_gray(png_ptr, 1, 54.0/256, 183.0/256); } } /* If the depth is different from 8 bits/gray, make the libpng expand * it to 8 bit gray. */ number_of_passes=png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr,info_ptr); temporary_array=malloc(xs*ys); image=malloc(xs*ys*sizeof(image)); ptrs=malloc(ys*sizeof(*ptrs)); for (y1=0;y1>16; } png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); info_ptr=png_create_info_struct(png_ptr); png_init_io(png_ptr,f); png_set_filter(png_ptr,0,PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_UP |PNG_FILTER_AVG|PNG_FILTER_PAETH); png_set_compression_level(png_ptr,Z_BEST_COMPRESSION); png_set_IHDR(png_ptr,info_ptr,xs,ys,8,PNG_COLOR_TYPE_GRAY,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); png_set_gAMA(png_ptr,info_ptr,1.0); png_write_info(png_ptr,info_ptr); tptr=temporary; for (a=ys;a;a--){ png_write_row(png_ptr,tptr); tptr+=xs; } png_write_end(png_ptr,info_ptr); png_destroy_write_struct(&png_ptr,&info_ptr); free(temporary); fclose(f); } void clip(void) { int a; int *iptr; int val; iptr=image; for (a=xs*ys;a;a--){ val=*iptr; if (val<0) val=0; if (val>0xffffff) val=0xffffff; *iptr++=val; } } void threshold(int param) { int a; int *iptr; int val; iptr=image; for (a=xs*ys;a;a--){ val=*iptr; *iptr++=(val>=param)?0xffffff:0; } } void mul(int param) { int a; int *iptr; int val; iptr=image; for (a=xs*ys;a;a--){ val=*iptr; val*=param; *iptr++=val; } } void add(int param) { int a; int *iptr; int val; iptr=image; for (a=xs*ys;a;a--){ val=*iptr; val+=param; *iptr++=val; } } void divide(int param) { int a; int *iptr; int val; iptr=image; for (a=xs*ys;a;a--){ val=*iptr; val/=param; *iptr++=val; } } void right_shift(int param) { int a; int *iptr; int val; iptr=image; for (a=xs*ys;a;a--){ val=*iptr; val>>=param; *iptr++=val; } } void left_shift(int param) { int a; int *iptr; int val; iptr=image; for (a=xs*ys;a;a--){ val=*iptr; val<<=param; *iptr++=val; } } void flip() { int *new; int x,y; int *iptr,*nptr; new=malloc(xs*ys*sizeof(*new)); if (!new){ fprintf(stderr,"Out of memory when flipping.\n"); exit(1); } iptr=image; nptr=new; for (y=ys;y;y--){ for(x=xs;x;x--){ *nptr=*iptr; iptr++; nptr+=ys; } nptr-=xs*ys; nptr++; } free(image); image=new; x=xs; xs=ys; ys=x; } void mirror(void){ int y; int *fptr, *rptr, *lptr; int xchg; lptr=image; for (y=ys;y;y--){ fptr=lptr; rptr=lptr+xs-1; while(rptr>fptr){ xchg=*rptr; *rptr=*fptr; *fptr=xchg; fptr++; rptr--; } lptr+=xs; } } void append(int lines, int value){ int *ptr; if (lines<=0) return; image=realloc(image,xs*(ys+lines)*sizeof(*image)); if (!image){ fprintf(stderr,"Out of memory when appending lines to the image.\n"); exit(1); } ptr=image+xs*ys; ys+=lines; for (lines*=xs;lines;lines--){ *ptr++=value; } } void detract(int lines){ if (lines>=ys) return; /* Invalid */ ys-=lines; image=realloc(image,xs*ys*sizeof(*image)); if (!image){ fprintf(stderr,"Out of memory at detract.\n"); exit(1); } } /* Blurs so that each pixel is replaced by sum of its value, value of "pixels" pixels * left and "pixels" pixels right, divided by 2*pixels+1. */ void blurbox(int pixels) { int *templine, *iptr; int *tptr; int *addptr,*subptr; int divisor=pixels*2+1; int y,x; int val; templine=malloc((2*pixels+xs)*sizeof(*templine)); if (!templine){ fprintf(stderr,"Out of memory when box blurring.\n"); exit(1); } iptr=image; for (y=ys;y;y--){ tptr=templine; val=*iptr/divisor; for (x=pixels;x;x--){ *tptr++=val; } for (x=xs;x;x--){ *tptr++=(*iptr++)/divisor; } val=iptr[-1]/divisor; iptr-=xs; for (x=pixels;x;x--){ *tptr++=val; } val=0; tptr=templine; for (x=divisor-1;x;x--){ val+=*tptr++; } addptr=tptr; subptr=templine; for (x=xs;x;x--){ val+=*addptr++; *iptr++=val; val-=*subptr++; } } free(templine); } void perform_command_line(unsigned char *command) { char *ptr; int param1, param2; /* Find the first space, newline, tab or null */ ptr=command; while(!(*ptr==0||*ptr==10||*ptr==32||*ptr==9)) ptr++; if (!*ptr) return; /* Invalid */ *ptr=0; ptr++; if (!strcmp(command,"clip")){ clip(); }else if (!strcmp(command,"threshold")){ param1=strtol(ptr,NULL,0); threshold(param1); }else if (!strcmp(command,"flip")){ flip(); }else if (!strcmp(command,"append")){ param1=strtol(ptr,&ptr,0); param2=strtol(ptr,&ptr,0); append(param1,param2); }else if (!strcmp (command,"detract")){ param1=strtol(ptr,&ptr,0); detract(param1); }else if (!strcmp(command,"mirror")){ mirror(); }else if (!strcmp(command,"blurbox")){ param1=strtol(ptr,&ptr,0); blurbox(param1); }else if (!strcmp(command,"gaussian")){ param1=strtol(ptr,&ptr,0); param2=strtol(ptr,&ptr,0); if (param1>0){ for (;param1;param1--){ blurbox(param2); } } }else if (!strcmp(command,"*")){ param1=strtol(ptr,&ptr,0); mul(param1); }else if (!strcmp(command,"+")){ param1=strtol(ptr,&ptr,0); add(param1); }else if (!strcmp(command,"/")){ param1=strtol(ptr,&ptr,0); if (param1) divide(param1); }else if (!strcmp(command,">>")){ param1=strtol(ptr,&ptr,0); right_shift(param1); }else if (!strcmp(command,"<<")){ param1=strtol(ptr,&ptr,0); left_shift(param1); }else{ fprintf(stderr,"Invalid command %s encountered.\n",command); } } void process_commands(unsigned char *filename) { FILE *f; unsigned char string[1024]; if (!strlen(filename)) return; /* "" as command filename */ f=fopen(filename,"r"); if (!f){ fprintf(stderr,"Can't open command file %s.\n",filename); exit(1); } while(fgets(string, sizeof(string),f)){ perform_command_line(string); } fclose(f); } int main(int argc, char** argv) { if (argc<4){ usage: fprintf(stderr, "Usage: improcess [-f] input_filename " "command_filename output_filename\nIf command_filename\ is \"\", then no operations are performed.\n"); return 0; } if (argv[1][0]=='-'&&argv[1][1]=='f'&&!argv[1][2]) { /* -f flag */ if (argc<5) goto usage; force_gamma_1=1; read_png(argv[2]); process_commands(argv[3]); write_png(argv[4]); }else{ read_png(argv[1]); process_commands(argv[2]); write_png(argv[3]); } return 0; }