main.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. * MODULE: g.pnmcomp
  3. * AUTHOR(S): Glynn Clements
  4. * PURPOSE: g.pnmcomp isn't meant for end users. It's an internal tool for use by
  5. * a wxGUI.
  6. * In essence, g.pnmcomp generates a PPM image by overlaying a series of
  7. * PPM/PGM pairs (PPM = RGB image, PGM = alpha channel).
  8. * COPYRIGHT: (C) 2006, 2011 by the GRASS Development Team
  9. *
  10. * This program is free software under the GNU General
  11. * Public License (>=v2). Read the file COPYING that
  12. * comes with GRASS for details.
  13. *
  14. */
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <stdarg.h>
  18. #include <string.h>
  19. #include <grass/gis.h>
  20. #include <grass/colors.h>
  21. #include <grass/glocale.h>
  22. static unsigned int width, height;
  23. static unsigned char *in_buf;
  24. static unsigned char *mask_buf;
  25. static unsigned char *out_buf;
  26. static unsigned char *out_mask_buf;
  27. static void erase(unsigned char *buf, const char *color)
  28. {
  29. unsigned char *p = buf;
  30. int r, g, b;
  31. unsigned char bg[3];
  32. unsigned int row, col, i;
  33. if (G_str_to_color(color, &r, &g, &b) != 1)
  34. G_fatal_error(_("Invalid color: %s"), color);
  35. bg[0] = (unsigned char)r;
  36. bg[1] = (unsigned char)g;
  37. bg[2] = (unsigned char)b;
  38. for (row = 0; row < height; row++)
  39. for (col = 0; col < width; col++)
  40. for (i = 0; i < 3; i++)
  41. *p++ = bg[i];
  42. }
  43. static int read_line(char *buf, int size, FILE * fp)
  44. {
  45. for (;;) {
  46. if (!fgets(buf, size, fp))
  47. G_fatal_error(_("Error reading PPM file"));
  48. if (buf[0] != '#')
  49. return 0;
  50. }
  51. }
  52. static void read_header(FILE * fp, unsigned char *magic, int *maxval)
  53. {
  54. unsigned int ncols, nrows;
  55. char buf[80];
  56. read_line(buf, sizeof(buf), fp);
  57. if (sscanf(buf, "P%c", magic) != 1)
  58. G_fatal_error(_("Invalid PPM file"));
  59. read_line(buf, sizeof(buf), fp);
  60. if (sscanf(buf, "%d %d", &ncols, &nrows) != 2)
  61. G_fatal_error(_("Invalid PPM file"));
  62. if (ncols != width || nrows != height)
  63. G_fatal_error("Expecting %dx%d image but got %dx%d image.",
  64. width, height, ncols, nrows);
  65. read_line(buf, sizeof(buf), fp);
  66. if (sscanf(buf, "%d", maxval) != 1)
  67. G_fatal_error(_("Invalid PPM file"));
  68. }
  69. static void read_pnm(const char *filename, unsigned char *buf, unsigned int components)
  70. {
  71. unsigned char magic;
  72. int maxval;
  73. unsigned char *p;
  74. unsigned int row, col, i;
  75. FILE *fp;
  76. fp = fopen(filename, "rb");
  77. if (!fp)
  78. G_fatal_error(_("File <%s> not found"), filename);
  79. read_header(fp, &magic, &maxval);
  80. switch (magic) {
  81. case '2':
  82. case '5':
  83. if (components == 3)
  84. G_fatal_error(_("Expecting PPM but got PGM"));
  85. break;
  86. case '3':
  87. case '6':
  88. if (components == 1)
  89. G_fatal_error(_("Expecting PGM but got PPM"));
  90. break;
  91. default:
  92. G_fatal_error(_("Invalid magic number: 'P%c'"), magic);
  93. break;
  94. }
  95. p = buf;
  96. for (row = 0; row < height; row++) {
  97. switch (magic) {
  98. case '2':
  99. for (col = 0; col < width; col++) {
  100. int y;
  101. if (fscanf(fp, "%d", &y) != 1)
  102. G_fatal_error(_("Invalid PGM file"));
  103. *p++ = (unsigned char)y;
  104. }
  105. break;
  106. case '3':
  107. for (col = 0; col < width; col++) {
  108. int r, g, b;
  109. if (fscanf(fp, "%d %d %d", &r, &g, &b) != 3)
  110. G_fatal_error(_("Invalid PPM file"));
  111. *p++ = (unsigned char)r;
  112. *p++ = (unsigned char)g;
  113. *p++ = (unsigned char)b;
  114. }
  115. break;
  116. case '5':
  117. if (fread(p, 1, width, fp) != width)
  118. G_fatal_error(_("Invalid PGM file"));
  119. p += width;
  120. break;
  121. case '6':
  122. if (fread(p, 3, width, fp) != width)
  123. G_fatal_error(_("Invalid PPM file"));
  124. p += 3 * width;
  125. break;
  126. }
  127. }
  128. p = buf;
  129. if (maxval != 255)
  130. for (row = 0; row < height; row++)
  131. for (col = 0; col < width; col++)
  132. for (i = 0; i < components; i++) {
  133. *p = *p * 255 / maxval;
  134. p++;
  135. }
  136. fclose(fp);
  137. }
  138. static void overlay(void)
  139. {
  140. const unsigned char *p = in_buf;
  141. const unsigned char *q = mask_buf;
  142. unsigned char *r = out_buf;
  143. unsigned char *s = out_mask_buf;
  144. unsigned int row, col, i;
  145. for (row = 0; row < height; row++)
  146. for (col = 0; col < width; col++) {
  147. int c1 = *q++;
  148. int c0 = 255 - c1;
  149. switch (c1) {
  150. case 0:
  151. p += 3;
  152. r += 3;
  153. s++;
  154. break;
  155. case 255:
  156. *r++ = *p++;
  157. *r++ = *p++;
  158. *r++ = *p++;
  159. *s++ = 255;
  160. break;
  161. default:
  162. for (i = 0; i < 3; i++) {
  163. *r = (*r * c0 + *p * c1) / 255;
  164. p++;
  165. r++;
  166. }
  167. *s = (*s * c0 + 255 * c1) / 255;
  168. s++;
  169. break;
  170. }
  171. }
  172. }
  173. static void overlay_alpha(float alpha)
  174. {
  175. const unsigned char *p = in_buf;
  176. const unsigned char *q = mask_buf;
  177. unsigned char *r = out_buf;
  178. unsigned char *s = out_mask_buf;
  179. unsigned int row, col, i;
  180. for (row = 0; row < height; row++)
  181. for (col = 0; col < width; col++) {
  182. int c = *q++;
  183. int c1 = (int)(c * alpha);
  184. int c0 = 255 - c1;
  185. if (!c) {
  186. p += 3;
  187. r += 3;
  188. s++;
  189. continue;
  190. }
  191. for (i = 0; i < 3; i++) {
  192. *r = (*r * c0 + *p * c1) / 256;
  193. p++;
  194. r++;
  195. }
  196. *s = (*s * c0 + 255 * c1) / 255;
  197. s++;
  198. }
  199. }
  200. static void write_ppm(const char *filename, const unsigned char *buf)
  201. {
  202. const unsigned char *p = buf;
  203. FILE *fp;
  204. fp = fopen(filename, "wb");
  205. if (!fp)
  206. G_fatal_error(_("Unable to open file <%s>"), filename);
  207. fprintf(fp, "P6\n%d %d\n255\n", width, height);
  208. if (fwrite(p, 3 * width, height, fp) != height)
  209. G_fatal_error(_("Error writing PPM file"));
  210. fclose(fp);
  211. }
  212. static void write_pgm(const char *filename, const unsigned char *buf)
  213. {
  214. const unsigned char *p = buf;
  215. FILE *fp;
  216. fp = fopen(filename, "wb");
  217. if (!fp)
  218. G_fatal_error(_("Unable to open file <%s>"), filename);
  219. fprintf(fp, "P5\n%d %d\n255\n", width, height);
  220. if (fwrite(p, width, height, fp) != height)
  221. G_fatal_error(_("Error writing PGM file"));
  222. fclose(fp);
  223. }
  224. int main(int argc, char *argv[])
  225. {
  226. struct GModule *module;
  227. struct
  228. {
  229. struct Option *in, *mask, *alpha, *out, *width, *height, *bg,
  230. *outmask;
  231. } opt;
  232. int i;
  233. G_gisinit(argv[0]);
  234. module = G_define_module();
  235. G_add_keyword(_("general"));
  236. G_add_keyword(_("display"));
  237. module->description = _("Overlays multiple PPM image files.");
  238. opt.in = G_define_standard_option(G_OPT_F_INPUT);
  239. opt.in->required = YES;
  240. opt.in->multiple = YES;
  241. opt.in->description = _("Name of input file(s)");
  242. opt.mask = G_define_standard_option(G_OPT_F_INPUT);
  243. opt.mask->key = "mask";
  244. opt.mask->required = NO;
  245. opt.mask->multiple = YES;
  246. opt.mask->description = _("Name of input mask file(s)");
  247. opt.alpha = G_define_option();
  248. opt.alpha->key = "opacity";
  249. opt.alpha->type = TYPE_DOUBLE;
  250. opt.alpha->multiple = YES;
  251. opt.alpha->description = _("Layer opacities");
  252. opt.out = G_define_standard_option(G_OPT_F_OUTPUT);
  253. opt.outmask = G_define_standard_option(G_OPT_F_OUTPUT);
  254. opt.outmask->key = "output_mask";
  255. opt.outmask->required = NO;
  256. opt.outmask->description = _("Name for output mask file");
  257. opt.width = G_define_option();
  258. opt.width->key = "width";
  259. opt.width->type = TYPE_INTEGER;
  260. opt.width->required = YES;
  261. opt.width->description = _("Image width");
  262. opt.height = G_define_option();
  263. opt.height->key = "height";
  264. opt.height->type = TYPE_INTEGER;
  265. opt.height->required = YES;
  266. opt.height->description = _("Image height");
  267. opt.bg = G_define_standard_option(G_OPT_C);
  268. opt.bg->key = "bgcolor";
  269. opt.bg->label = _("Background color");
  270. opt.bg->answer = NULL;
  271. if (G_parser(argc, argv))
  272. exit(EXIT_FAILURE);
  273. width = atoi(opt.width->answer);
  274. height = atoi(opt.height->answer);
  275. in_buf = G_malloc(width * height * 3);
  276. mask_buf = G_malloc(width * height);
  277. out_buf = G_malloc(width * height * 3);
  278. out_mask_buf = G_malloc(width * height);
  279. if (opt.bg->answer)
  280. erase(out_buf, opt.bg->answer);
  281. memset(out_mask_buf, 0, width * height);
  282. for (i = 0; opt.in->answers[i]; i++) {
  283. char *infile = opt.in->answers[i];
  284. char *maskfile = opt.mask->answer ? opt.mask->answers[i]
  285. : NULL;
  286. if (!maskfile)
  287. opt.mask->answer = NULL;
  288. if (maskfile && *maskfile) {
  289. read_pnm(infile, in_buf, 3);
  290. read_pnm(maskfile, mask_buf, 1);
  291. if (opt.alpha->answer) {
  292. float alpha = atof(opt.alpha->answers[i]);
  293. if (alpha == 1.0)
  294. overlay();
  295. else
  296. overlay_alpha(alpha);
  297. }
  298. else
  299. overlay();
  300. }
  301. else {
  302. read_pnm(infile, out_buf, 3);
  303. memset(out_mask_buf, 255, width * height);
  304. }
  305. }
  306. write_ppm(opt.out->answer, out_buf);
  307. if (opt.outmask->answer)
  308. write_pgm(opt.outmask->answer, out_mask_buf);
  309. exit(EXIT_SUCCESS);
  310. }