1 /*****************************************************************************
2 * This file is part of gfxprim library. *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
19 * Copyright (C) 2009-2011 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
32 #include "histogram.h"
34 static GP_ProgressCallback
*progress_callback
= NULL
;
36 static const char *progress_prefix
= NULL
;
38 static int show_progress(GP_ProgressCallback
*self
)
40 fprintf(stderr
, "\r%s %3.2f%%",
41 progress_prefix
, self
->percentage
);
46 static int param_err(const struct param
*self
, const char *val
, void *priv
)
48 /* invalid parameter name */
50 fprintf(stderr
, "ERROR: %s: invalid parameter '%s'\n",
55 /* just regular error */
56 fprintf(stderr
, "ERROR: %s: invalid %s parameter %s = '%s'",
57 (char *)priv
, param_type_name(self
->type
), self
->name
, val
);
59 if (self
->type
== PARAM_ENUM
) {
62 fprintf(stderr
, " is not in [");
64 for (i
= 0; self
->enum_table
[i
] != NULL
; i
++)
65 if (self
->enum_table
[i
+1] == NULL
)
66 fprintf(stderr
, "'%s']", self
->enum_table
[i
]);
68 fprintf(stderr
, "'%s' | ", self
->enum_table
[i
]);
71 fprintf(stderr
, "\n");
76 static void print_error(const char *error
)
78 fprintf(stderr
, "ERROR: %s\n", error
);
83 static const char *resize_algs
[] = {
92 static int resize_check_ratio(const struct param
*self
__attribute__((unused
)),
93 void *val
, int count
__attribute__((unused
)))
95 float f
= *((float*)val
);
103 static struct param resize_params
[] = {
104 {"alg", PARAM_ENUM
, "algorithm to be used", resize_algs
, NULL
},
105 {"ratio", PARAM_FLOAT
, "scale ratio", NULL
, resize_check_ratio
},
106 {NULL
, 0, NULL
, NULL
, NULL
}
109 static GP_RetCode
resize(GP_Context
**c
, const char *params
)
114 if (param_parse(params
, resize_params
, "resize", param_err
,
119 print_error("resize: ratio parameter is missing");
123 GP_Size w
= ratio
* (*c
)->w
;
124 GP_Size h
= ratio
* (*c
)->h
;
125 GP_Context
*res
= NULL
;
127 res
= GP_FilterResize(*c
, NULL
, alg
, w
, h
, progress_callback
);
140 static const char *scale_algs
[] = {
149 static int scale_check_size(const struct param
*self
__attribute__((unused
)),
150 void *val
, int count
__attribute__((unused
)))
152 int i
= *((int*)val
);
160 static struct param scale_params
[] = {
161 {"alg", PARAM_ENUM
, "algorithm to be used", scale_algs
, NULL
},
162 {"w", PARAM_INT
, "new width (only width may be passed)", NULL
, scale_check_size
},
163 {"h", PARAM_INT
, "new height (only height may be passed)", NULL
, scale_check_size
},
164 {NULL
, 0, NULL
, NULL
, NULL
}
167 static GP_RetCode
scale(GP_Context
**c
, const char *params
)
173 if (param_parse(params
, scale_params
, "scale", param_err
,
177 if (w
== -1 && h
== -1) {
178 print_error("scale: w and/or h missing");
183 w
= (*c
)->w
* (1.00 * h
/(*c
)->h
) + 0.5;
186 h
= (*c
)->h
* (1.00 * w
/(*c
)->w
) + 0.5;
188 GP_Context
*res
= NULL
;
190 res
= GP_FilterResize(*c
, NULL
, alg
, w
, h
, progress_callback
);
203 static const char *rotate_rots
[] = {
209 static struct param rotate_params
[] = {
210 {"rot", PARAM_ENUM
, "image rotation", rotate_rots
, NULL
},
211 {NULL
, 0, NULL
, NULL
, NULL
}
214 static GP_RetCode
rotate(GP_Context
**c
, const char *params
)
218 if (param_parse(params
, rotate_params
, "rotate", param_err
, &rot
))
222 print_error("rotate: rot parameter is missing");
226 GP_Context
*res
= NULL
;
230 res
= GP_FilterRotate90_Alloc(*c
, progress_callback
);
233 res
= GP_FilterRotate180_Alloc(*c
, progress_callback
);
236 res
= GP_FilterRotate270_Alloc(*c
, progress_callback
);
251 static struct param mirror_params
[] = {
252 {"vert", PARAM_BOOL
, "mirror vertically", NULL
, NULL
},
253 {"horiz", PARAM_BOOL
, "mirror horizontally", NULL
, NULL
},
254 {NULL
, 0, NULL
, NULL
, NULL
}
257 static GP_RetCode
mirror(GP_Context
**c
, const char *params
)
259 int vert
= 0, horiz
= 0;
261 if (param_parse(params
, mirror_params
, "mirror", param_err
, &vert
, &horiz
))
265 GP_FilterMirrorV(*c
, *c
, progress_callback
);
268 GP_FilterMirrorH(*c
, *c
, progress_callback
);
273 /* brightness filter */
275 static struct param bright_params
[] = {
276 {"inc", PARAM_INT
, "brightness increment", NULL
, NULL
},
277 {"chann", PARAM_STR
, "Channel name {R, G, B, A, V, ...}", NULL
, NULL
},
278 {NULL
, 0, NULL
, NULL
, NULL
}
281 static GP_RetCode
bright(GP_Context
**c
, const char *params
)
286 if (param_parse(params
, bright_params
, "bright", param_err
, &bright
, &chann
))
290 print_error("bright: bright parameter is zero or missing");
294 GP_FILTER_PARAMS((*c
)->pixel_type
, filter_params
);
297 GP_FilterParamSetIntAll(filter_params
, bright
);
299 GP_FilterParam
*param
= GP_FilterParamChannel(filter_params
, chann
);
302 print_error("bright: Invalid channel name");
306 GP_FilterParamSetIntAll(filter_params
, 0);
307 param
->val
.i
= bright
;
310 GP_FilterBrightness(*c
, *c
, filter_params
, progress_callback
);
317 static struct param contrast_params
[] = {
318 {"mul", PARAM_FLOAT
, "contrast (1.5 = +50%, 0.5 = -50%)", NULL
, NULL
},
319 {"chann", PARAM_STR
, "Channel name {R, G, B, A, V, ...}", NULL
, NULL
},
320 {NULL
, 0, NULL
, NULL
, NULL
}
323 static GP_RetCode
contrast(GP_Context
**c
, const char *params
)
328 if (param_parse(params
, contrast_params
, "contrast", param_err
, &mul
, &chann
))
332 print_error("contrast: mul parameter must be >= 0");
336 GP_FILTER_PARAMS((*c
)->pixel_type
, filter_params
);
339 GP_FilterParamSetFloatAll(filter_params
, mul
);
341 GP_FilterParam
*param
= GP_FilterParamChannel(filter_params
, chann
);
344 print_error("contrast: Invalid channel name");
348 GP_FilterParamSetFloatAll(filter_params
, 1);
352 GP_FilterContrast(*c
, *c
, filter_params
, progress_callback
);
359 static struct param invert_params
[] = {
360 {NULL
, 0, NULL
, NULL
, NULL
}
363 static GP_RetCode
invert(GP_Context
**c
, const char *params
)
365 if (param_parse(params
, invert_params
, "invert", param_err
))
368 GP_FilterInvert(*c
, *c
, progress_callback
);
375 static struct param blur_params
[] = {
376 {"sigma", PARAM_FLOAT
, "sigma parameter, radii of blur (sets both)", NULL
, NULL
},
377 {"sigma_x", PARAM_FLOAT
, "sigma parameter for horizontal direction", NULL
, NULL
},
378 {"sigma_y", PARAM_FLOAT
, "sigma parameter for vertical direction", NULL
, NULL
},
379 {NULL
, 0, NULL
, NULL
, NULL
}
382 static GP_RetCode
blur(GP_Context
**c
, const char *params
)
388 if (param_parse(params
, blur_params
, "blur", param_err
, &sigma
, &sigma_x
, &sigma_y
))
396 if (sigma_x
<= 0 && sigma_y
<= 0) {
397 print_error("blur: at least one of sigma_x and sigma_y must be >= 0");
401 GP_FilterGaussianBlur_Raw(*c
, *c
, sigma_x
, sigma_y
, progress_callback
);
408 //TODO: this should be generated
409 static const char *dither_formats
[] = {
420 static const GP_PixelType dither_pixel_types
[] = {
430 static struct param dither_params
[] = {
431 {"format", PARAM_ENUM
, "pixel type to be used", dither_formats
, NULL
},
432 {NULL
, 0, NULL
, NULL
, NULL
}
435 static GP_RetCode
dither(GP_Context
**c
, const char *params
)
439 if (param_parse(params
, dither_params
, "dither", param_err
, &fmt
))
443 print_error("dither: invalid format or format param missing");
448 bw
= GP_FilterFloydSteinberg_RGB888_Alloc(*c
, dither_pixel_types
[fmt
],
451 //TODO: so far we convert the context back to RGB888
452 //(so we can do further work with it)
453 GP_Blit(bw
, 0, 0, GP_ContextW(bw
), GP_ContextH(bw
), *c
, 0, 0);
460 /* jpg save filter */
462 static struct param save_jpg_params
[] = {
463 {"file", PARAM_STR
, "Filename to save the result", NULL
, NULL
},
464 {NULL
, 0, NULL
, NULL
, NULL
}
467 static GP_RetCode
save_jpg(GP_Context
**c
, const char *params
)
471 if (param_parse(params
, save_jpg_params
, "jpg", param_err
, &file
))
475 print_error("jpg: filename missing");
479 GP_SaveJPG(*c
, file
, progress_callback
);
484 /* png save filter */
486 static struct param save_png_params
[] = {
487 {"file", PARAM_STR
, "Filename to save the result", NULL
, NULL
},
488 {NULL
, 0, NULL
, NULL
, NULL
}
491 static GP_RetCode
save_png(GP_Context
**c
, const char *params
)
495 if (param_parse(params
, save_png_params
, "png", param_err
, &file
))
499 print_error("png: filename missing");
503 GP_SavePNG(*c
, file
, progress_callback
);
510 static struct param add_noise_params
[] = {
511 {"percents", PARAM_FLOAT
, "Percents of noise to add", NULL
, NULL
},
512 {"chann", PARAM_STR
, "Channel name {R, G, B, A, V, ...}", NULL
, NULL
},
513 {NULL
, 0, NULL
, NULL
, NULL
}
516 static uint32_t add_noise_op(uint32_t val
, uint8_t bits
, GP_FilterParam
*param
)
519 int max
= (1<<bits
) - 1;
524 ret
= val
* (1 - perc
) + (random() % max
) * perc
;
535 static uint32_t no_op(uint32_t val
)
540 static GP_RetCode
add_noise(GP_Context
**c
, const char *params
)
545 if (param_parse(params
, add_noise_params
, "add_noise", param_err
, &percents
, &chann
))
548 GP_FILTER_PARAMS((*c
)->pixel_type
, priv
);
549 GP_FilterParamSetFloatAll(priv
, percents
/100);
550 GP_FILTER_PARAMS((*c
)->pixel_type
, op_callbacks
);
553 GP_FilterParamSetPtrAll(op_callbacks
, add_noise_op
);
555 GP_FilterParam
*param
= GP_FilterParamChannel(op_callbacks
, chann
);
558 print_error("add_noise: Invalid channel name");
563 GP_FilterParamSetPtrAll(op_callbacks
, no_op
);
564 param
->val
.ptr
= add_noise_op
;
567 GP_FilterPoint(*c
, *c
, op_callbacks
, priv
, progress_callback
);
574 static const char *arithmetic_ops
[] = {
583 static struct param arithmetic_params
[] = {
584 {"file", PARAM_STR
, "Filename of image to use.", NULL
, NULL
},
585 {"op", PARAM_ENUM
, "Arithmetic peration", arithmetic_ops
, NULL
},
586 {NULL
, 0, NULL
, NULL
, NULL
}
589 static GP_RetCode
arithmetic(GP_Context
**c
, const char *params
)
594 if (param_parse(params
, arithmetic_params
, "arithmetic", param_err
, &file
, &op
))
598 print_error("arithmetic: Filename missing");
602 GP_Context
*img
, *res
= NULL
;
604 if ((img
= GP_LoadImage(file
, progress_callback
)) == NULL
) {
605 print_error("arithmetic: Invalid image.");
611 res
= GP_FilterDifference(*c
, img
, NULL
, progress_callback
);
614 res
= GP_FilterAddition(*c
, img
, NULL
, progress_callback
);
617 res
= GP_FilterMultiply(*c
, img
, NULL
, progress_callback
);
620 res
= GP_FilterMin(*c
, img
, NULL
, progress_callback
);
623 res
= GP_FilterMax(*c
, img
, NULL
, progress_callback
);
639 static struct param histogram_params
[] = {
640 {"file", PARAM_STR
, "Filename of image to use.", NULL
, NULL
},
641 {NULL
, 0, NULL
, NULL
, NULL
}
644 static GP_RetCode
histogram(GP_Context
**c
, const char *params
)
646 char *file
= "histogram.png";
648 if (param_parse(params
, histogram_params
, "histogram", param_err
, &file
))
652 print_error("histogram: Filename missing");
656 histogram_to_png(*c
, file
);
665 struct param
*param_desc
;
666 GP_RetCode (*apply
)(GP_Context
**c
, const char *params
);
669 static struct filter filter_table
[] = {
670 {"rotate", "rotate image", rotate_params
, rotate
},
671 {"mirror", "mirror vertically/horizontally", mirror_params
, mirror
},
672 {"scale", "scale image to given width and height", scale_params
, scale
},
673 {"resize", "resize image by given ratio", resize_params
, resize
},
674 {"bright", "alter image brightness", bright_params
, bright
},
675 {"contrast", "alter image contrast", contrast_params
, contrast
},
676 {"invert", "inverts image", invert_params
, invert
},
677 {"add_noise", "adds noise", add_noise_params
, add_noise
},
678 {"blur", "gaussian blur", blur_params
, blur
},
679 {"dither", "dithers bitmap", dither_params
, dither
},
680 {"arithmetic", "arithmetic operation", arithmetic_params
, arithmetic
},
681 {"histogram", "save histogram into image file", histogram_params
, histogram
},
682 {"jpg", "save jpg image", save_jpg_params
, save_jpg
},
683 {"png", "save png image", save_png_params
, save_png
},
684 {NULL
, NULL
, NULL
, NULL
}
687 static struct filter
*get_filter(const char *name
)
691 for (i
= 0; filter_table
[i
].name
!= NULL
; i
++) {
692 if (!strcasecmp(filter_table
[i
].name
, name
))
693 return &filter_table
[i
];
699 static void print_filter_help(void)
703 for (i
= 0; filter_table
[i
].name
!= NULL
; i
++) {
704 printf("%s\n", filter_table
[i
].name
);
706 j
= strlen(filter_table
[i
].name
);
712 printf("* %s\n", filter_table
[i
].desc
);
715 param_describe(filter_table
[i
].param_desc
, " ");
722 #define FILTERS_MAX 255
724 static const char *filter_params
[FILTERS_MAX
];
725 static const struct filter
*filters
[FILTERS_MAX
];
726 static unsigned int filter_cnt
= 0;
728 static void add_filter(char *params
)
730 if (filter_cnt
>= FILTERS_MAX
) {
731 fprintf(stderr
, "Maximal number of filters exceeded (%u), "
732 "increase and recompile.", FILTERS_MAX
);
736 const char *name
= strsep(¶ms
, ":");
738 filters
[filter_cnt
] = get_filter(name
);
740 if (filters
[filter_cnt
] == NULL
) {
741 fprintf(stderr
, "Invalid filter name '%s'\n", name
);
745 filter_params
[filter_cnt
++] = params
;
748 static void apply_filters(GP_Context
**src
)
753 for (i
= 0; i
< filter_cnt
; i
++) {
756 snprintf(buf
, sizeof(buf
), "Filter %s", filters
[i
]->name
);
758 progress_prefix
= buf
;
760 if ((ret
= filters
[i
]->apply(src
, filter_params
[i
]))) {
761 fprintf(stderr
, "Error: %s\n", GP_RetCodeName(ret
));
765 if (progress_callback
!= NULL
)
766 fprintf(stderr
, " done\n");
770 static const char *app_help
= {
772 " <<<<<<<<<< Bitmap Grinder >>>>>>>>>>> \n"
776 " +-{ D| |010101011. \n"
777 " | \\| | +-.0100101. \n"
778 " O=+ +~+-----+ .10110101. \n"
782 " Program options \n"
783 " =============== \n"
785 "-h - prints this help \n"
786 "-p - show filter progress \n"
787 "-v int - sets gfxprim verbosity level \n"
788 "-o fmt - output format, ppm, jpg, png \n"
789 "-f params - apply filter, multiple filters may be used\n"
794 " grider -f resize:ratio=1.5 -f contrast:mul=1.2 in.png\n"
796 " * will resize image 1.5 times and increases contrast \n"
797 " by 20%. The result is, just for now, saved to \n"
798 " out_X.ppm where X is number which is increased for \n"
799 " each image given as parameter. \n"
801 " List of filters \n"
802 " =============== \n"
805 static void print_help(void)
811 static const char *out_fmts
[] = {
818 static void check_fmt(const char *fmt
)
822 for (i
= 0; out_fmts
[i
] != NULL
; i
++)
823 if (!strcmp(out_fmts
[i
], fmt
))
826 fprintf(stderr
, "Invalid output format '%s'\n", fmt
);
829 static void save_by_fmt(struct GP_Context
*bitmap
, const char *name
, const char *fmt
)
833 progress_prefix
= "Saving Image";
835 if (!strcmp(fmt
, "ppm"))
836 ret
= GP_SavePPM(name
, bitmap
, "b");
837 else if (!strcmp(fmt
, "jpg"))
838 ret
= GP_SaveJPG(bitmap
, name
, progress_callback
);
839 else if (!strcmp(fmt
, "png"))
840 ret
= GP_SavePNG(bitmap
, name
, progress_callback
);
843 fprintf(stderr
, "Failed to save bitmap: %s\n", GP_RetCodeName(ret
));
847 if (progress_callback
!= NULL
)
848 fprintf(stderr
, " done\n");
851 int main(int argc
, char *argv
[])
855 const char *out_fmt
= "ppm";
857 GP_ProgressCallback callback
= {
858 .callback
= show_progress
,
861 while ((opt
= getopt(argc
, argv
, "f:ho:pv:")) != -1) {
871 fprintf(stderr
, "ERROR: invalid debug level "
872 "'%s', expected number > 0\n",
887 progress_callback
= &callback
;
895 if (optind
>= argc
) {
896 fprintf(stderr
, "ERROR: Expected bitmap filenames\n");
900 for (i
= optind
; i
< argc
; i
++) {
903 snprintf(buf
, sizeof(buf
), "out_%03i.%s", i
- optind
+ 1, out_fmt
);
904 fprintf(stderr
, "Processing '%s' -> '%s'\n", argv
[i
], buf
);
906 progress_prefix
= "Loading image";
908 if ((bitmap
= GP_LoadImage(argv
[i
], progress_callback
)) == NULL
) {
909 fprintf(stderr
, "Failed to load bitmap: %s\n", strerror(errno
));
913 if (progress_callback
!= NULL
)
914 fprintf(stderr
, " done\n");
916 apply_filters(&bitmap
);
918 save_by_fmt(bitmap
, buf
, out_fmt
);