/* **************************************************************************** * * MODULE: r.composite * AUTHOR(S): Glynn Clements - glynn.clements@virgin.net * PURPOSE: Combine red, green and blue layers into a single * layer using a quantisation of the RGB color space. * Using Floyd-Steinberg dithering * * COPYRIGHT: (C) 2001 by the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS * for details. * *****************************************************************************/ #include #include #include #include #include struct band { struct Option *opt_name; struct Option *opt_levels; char *name; int levels; int maxlev; int offset; int file; int type; int size; unsigned char *array[3]; short *floyd[2]; struct Colors colors; }; static char * const color_names[3] = {"red", "green", "blue"}; static struct band B[3]; static int closest; static void make_color_cube(struct Colors *colors); static int quantize(int c, int x); int main(int argc, char **argv) { struct GModule *module; struct Option *opt_out; struct Option *opt_lev; struct Flag *flg_d; struct Flag *flg_c; int dither; char *out_name; int out_file; CELL *out_array; struct Colors out_colors; int levels; char *mapset; int atrow, atcol; struct Cell_head window; unsigned char *dummy, *nulls; int i, j; struct History history; G_gisinit(argv[0]); module = G_define_module(); module->keywords = _("raster"); module->description = _("Combines red, green and blue map layers into " "a single composite map layer."); for (i = 0; i < 3; i++) { struct Option *opt; char buff[80]; B[i].opt_name = opt = G_define_option(); sprintf(buff, "%s", color_names[i]); opt->key = G_store(buff); opt->type = TYPE_STRING; opt->answer = NULL; opt->required = YES; opt->gisprompt = "old,cell,raster"; sprintf(buff, _("Name of raster map layer to be used for <%s>"), color_names[i]); opt->description= G_store(buff); } opt_lev = G_define_option(); opt_lev->key = "levels"; opt_lev->type = TYPE_INTEGER; opt_lev->required = NO; opt_lev->options = "1-256"; opt_lev->answer = "32"; opt_lev->description= _("Number of levels to be used for each component"); for (i = 0; i < 3; i++) { struct Option *opt; char buff[80]; B[i].opt_levels = opt = G_define_option(); sprintf(buff, "lev_%s", color_names[i]); opt->key = G_store(buff); opt->type = TYPE_INTEGER; opt->required = NO; opt->options = "1-256"; sprintf(buff, _("Number of levels to be used for <%s>"), color_names[i]); opt->description= G_store(buff); } opt_out = G_define_option(); opt_out->key = "output"; opt_out->type = TYPE_STRING; opt_out->required = YES; opt_out->gisprompt = "new,cell,raster"; opt_out->description= _("Name of raster map to contain results"); flg_d = G_define_flag(); flg_d->key = 'd'; flg_d->description = _("Dither"); flg_c = G_define_flag(); flg_c->key = 'c'; flg_c->description = _("Use closest color"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); levels = atoi(opt_lev->answer); dither = flg_d->answer; closest = flg_c->answer; /* read in current window */ G_get_window(&window); dummy = G_malloc(window.cols); nulls = G_malloc(window.cols); for (i = 0; i < 3; i++) { struct band *b = &B[i]; /* Get name of layer to be used */ b->name = b->opt_name->answer; mapset = G_find_cell2(b->name, ""); if (mapset == NULL) G_fatal_error("Cell layer [%s] does not exist", b->name); /* Make sure map is available */ if ((b->file = G_open_cell_old(b->name, mapset)) == -1) G_fatal_error("Unable to open cellfile for [%s]", b->name); b->type = G_raster_map_type(b->name, mapset); b->size = G_raster_size(b->type); /* Reading color lookup table */ if (G_read_colors(b->name, mapset, &b->colors) == -1) G_fatal_error("Color file for [%s] not available", b->name); for (j = 0; j < 3; j++) b->array[j] = (i == j) ? G_malloc(window.cols) : dummy; b->levels = b->opt_levels->answer ? atoi(b->opt_levels->answer) : levels; b->maxlev = b->levels - 1; b->offset = 128 / b->maxlev; if (dither) for (j = 0; j < 2; j++) b->floyd[j] = G_calloc(window.cols + 2, sizeof(short)); } /* open output files */ out_name = opt_out->answer; mapset = G_find_cell2(out_name, ""); if (mapset != NULL) G_remove("cell", out_name); if ((out_file = G_open_cell_new(out_name)) < 0) G_fatal_error(_("Unable to create output <%s>"), out_name); out_array = G_allocate_cell_buf() ; /* Make color table */ make_color_cube(&out_colors); for (atrow = 0; atrow < window.rows; atrow++) { G_percent(atrow, window.rows, 2); for (i = 0; i < 3; i++) { struct band *b = &B[i]; if (G_get_raster_row_colors( b->file, atrow, &b->colors, b->array[0], b->array[1], b->array[2], nulls) < 0) G_fatal_error(_("Error reading '%s' map"), color_names[i]); if (dither) { short *tmp = b->floyd[0]; b->floyd[0] = b->floyd[1]; for (atcol = 0; atcol < window.cols + 2; atcol++) tmp[atcol] = 0; b->floyd[1] = tmp; } } for (atcol = 0; atcol < window.cols; atcol++) { int val[3]; if (nulls[atcol]) { G_set_c_null_value(&out_array[atcol], 1); continue; } for (i = 0; i < 3; i++) { struct band *b = &B[i]; int v = b->array[i][atcol]; if (dither) { int r, w, d; v += b->floyd[0][atcol+1] / 16; v = (v < 0) ? 0 : (v > 255) ? 255 : v; r = quantize(i, v); w = r * 255 / b->maxlev; d = v - w; b->floyd[0][atcol+2] += 7 * d; b->floyd[1][atcol+0] += 3 * d; b->floyd[1][atcol+1] += 5 * d; b->floyd[1][atcol+2] += 1 * d; val[i] = r; } else val[i] = quantize(i, v); } out_array[atcol] = (CELL) (val[2] * B[1].levels + val[1]) * B[0].levels + val[0]; } if(G_put_raster_row(out_file, out_array, CELL_TYPE) < 0) G_fatal_error(_("G_put_raster_row failed (file system full?)")); } G_percent(window.rows, window.rows, 5); /* Close the input files */ for (i = 0; i < 3; i++) G_close_cell(B[i].file); /* Close the output file */ G_close_cell(out_file); G_write_colors(out_name, G_mapset(), &out_colors); G_short_history(out_name, "raster", &history); G_command_history(&history); G_write_history(out_name, &history); exit(EXIT_SUCCESS); } static int quantize(int c, int x) { return closest ? (x + B[c].offset) * B[c].maxlev / 256 : x * B[c].levels / 256; } static void make_color_cube(struct Colors *colors) { int nr = B[0].levels; int ng = B[1].levels; int nb = B[2].levels; int mr = B[0].maxlev; int mg = B[1].maxlev; int mb = B[2].maxlev; int g, b; int i = 0; G_init_colors(colors); for (b = 0; b < nb; b++) { G_percent(b, nb, 5); for (g = 0; g < ng; g++) { int blu = b * 255 / mb; int grn = g * 255 / mg; CELL i0 = i; CELL i1 = i + mr; G_add_c_raster_color_rule(&i0, 0, grn, blu, &i1, 255, grn, blu, colors); i += nr; } } G_percent(nb, nb, 5); }