485 lines
9.2 KiB
C
485 lines
9.2 KiB
C
/* (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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <png.h>
|
|
|
|
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<ys;y1++) ptrs[y1]=temporary_array+xs*y1;
|
|
for (;number_of_passes;number_of_passes--){
|
|
png_read_rows(png_ptr, ptrs, NULL, ys);
|
|
}
|
|
png_read_end(png_ptr, NULL);
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
|
{
|
|
int *imptr=image;
|
|
unsigned char *tptr=temporary_array;
|
|
int a;
|
|
|
|
for (y1=ys*xs;y1;y1--){
|
|
a=*tptr++;
|
|
a=a|(a<<8)|(a<<16);
|
|
*imptr++=a;
|
|
}
|
|
}
|
|
free(ptrs);
|
|
free(temporary_array);
|
|
fclose(f);
|
|
}
|
|
|
|
#ifndef Z_BEST_COMPRESSION
|
|
#define Z_BEST_COMPRESSION 9
|
|
#endif
|
|
|
|
void write_png(unsigned char *filename)
|
|
{
|
|
unsigned char *temporary;
|
|
unsigned char *tptr;
|
|
int *iptr,a;
|
|
FILE *f;
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
|
|
f=fopen(filename,"w");
|
|
if (!f){
|
|
fprintf(stderr,"Unable to open file %s.\n",filename);
|
|
exit(1);
|
|
}
|
|
temporary=malloc(xs*ys);
|
|
if (!temporary){
|
|
fprintf(stderr,"Out of memory.\n");
|
|
exit(1);
|
|
}
|
|
iptr=image;
|
|
tptr=temporary;
|
|
for (a=xs*ys;a;a--){
|
|
*tptr++=(*iptr++)>>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;
|
|
}
|