links/graphics/improcess.c

486 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;
}