/* * 'Solarize' plugin for the GIMP. * * Copyright (C) 1995 Spencer Kimball and Peter Mattis * Copyright (C) 1996 Torsten Martinsen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: solarize.c,v 1.8 1996/08/22 10:28:11 torsten Exp $ * * @(GIMP) = * @(GIMP_DEP) = * @(GIMP_OBJ) = * @(GIMP_LIB) = * @(GIMP_AUTHOR) = * @(GIMP_EMAIL) = * @(GIMP_DESC) = * @(GIMP_VERSION) = <$Revision: 1.8 $> * @(GIMP_URL) = */ /* * This filter produces the 'solarization' effect seen when exposing a * photographic film to light during the development process. * This is done by inverting all pixels above the set threshold level. * * If the option 'Maximize Contrast' is enabled, Solarize tries to avoid * reducing the contrast of the image. This usually gives better results. * * If you #define AUTOAPPLY as 1, you also get the 'Auto Apply' option. * As you may know, this has some drawbacks due to limitations in the * current (0.60) API. * * This filter also (sort of) works with indexed images. */ #include #include "gimp.h" #define AUTOAPPLY 0 static void solarize(Image, Image); static void scale_callback(int, void *, void *); static void ok_callback(int, void *, void *); static void cancel_callback(int, void *, void *); static void toggle_callback(int, void *, void *); static int threshold = 50; static long stretch; static int stretch_ID; static int dialog_ID; static Image input, output; #if AUTOAPPLY static long aapply; static int aapply_ID; static unsigned char *saved; static void saveimage(void); static void freshen(void); static void toggle_callback_aapply(int item_ID, void *client_data, void *call_data); #endif int main(int argc, char **argv) { void *data; int group_ID, scaler_ID, temp_ID; if (gimp_init(argc, argv)) { input = 0; output = 0; input = gimp_get_input_image(0); if (input) switch (gimp_image_type(input)) { case RGB_IMAGE: case GRAY_IMAGE: case RGBA_IMAGE: case GRAYA_IMAGE: case INDEXED_IMAGE: output = gimp_get_output_image(0); if (output) { #if AUTOAPPLY saveimage(); #endif data = gimp_get_params(); if (data) threshold = ((long *) data)[0]; dialog_ID = gimp_new_dialog("Solarize"); gimp_new_label (dialog_ID, DEFAULT, "Options"); group_ID = gimp_new_row_group(dialog_ID, DEFAULT, NORMAL, ""); #if AUTOAPPLY aapply_ID = gimp_new_check_button(dialog_ID, group_ID, "Auto Apply"); aapply = 1; gimp_change_item(dialog_ID, aapply_ID, sizeof(aapply), &aapply); gimp_add_callback(dialog_ID, aapply_ID, toggle_callback_aapply, &aapply); #endif stretch_ID = gimp_new_check_button(dialog_ID, group_ID, "Maximize Contrast"); stretch = 1; gimp_change_item(dialog_ID, stretch_ID, sizeof(stretch), &stretch); gimp_add_callback(dialog_ID, stretch_ID, toggle_callback, &stretch); temp_ID = gimp_new_row_group (dialog_ID, group_ID, NORMAL, ""); gimp_new_label (dialog_ID, temp_ID, "Threshold:"); scaler_ID = gimp_new_scale(dialog_ID, temp_ID, 1, 255, threshold, 0); gimp_add_callback(dialog_ID, scaler_ID, scale_callback, &threshold); gimp_add_callback(dialog_ID, gimp_ok_item_id(dialog_ID), ok_callback, 0); gimp_add_callback(dialog_ID, gimp_cancel_item_id(dialog_ID), cancel_callback, 0); #if AUTOAPPLY solarize(input, output); gimp_update_image(output); #endif if (gimp_show_dialog(dialog_ID)) { gimp_set_params(sizeof(int), &threshold); #if AUTOAPPLY if (!aapply) { solarize(input, output); gimp_update_image(output); } } else if (aapply) { freshen(); gimp_update_image(output); } free(saved); #else solarize(input, output); gimp_update_image(output); } #endif } break; default: gimp_message("solarize: cannot operate on unknown image types"); break; } if (input) gimp_free_image(input); if (output) gimp_free_image(output); gimp_quit(); } return 0; } #if AUTOAPPLY static void saveimage(void) { saved = malloc(gimp_image_width(input) * gimp_image_height(input) * gimp_image_channels(input)); memcpy(saved, gimp_image_data(input), gimp_image_width(input) * gimp_image_height(input)*gimp_image_channels(input)); } static void freshen(void) { memcpy(gimp_image_data(output), saved, gimp_image_width(input) * gimp_image_height(input) * gimp_image_channels(input)); } #endif static void solarize(Image input, Image output) { long width, height; long channels, rowstride; unsigned char *src_row, *dest_row; unsigned char *src, *dest; int x1, y1, x2, y2, row, col, v, xx1, xx2; gimp_image_area(input, &x1, &y1, &x2, &y2); width = gimp_image_width(input); height = gimp_image_height(input); channels = gimp_image_channels(input); rowstride = width * channels; src_row = gimp_image_data(input); dest_row = gimp_image_data(output); if (gimp_image_type(input) != INDEXED_IMAGE) { xx1 = x1*channels; xx2 = x2*channels; src_row += rowstride * y1 + x1; dest_row += rowstride * y1 + x1; for (row = y1; row < y2; row++) { src = src_row; dest = dest_row; for (col = xx1; col < xx2; col++) { if (stretch) { v = *src++; if (v > threshold) v = (255-v)*255/(255-threshold); else v = v*255/threshold; *dest++ = v; } else *dest++ = (*src > threshold) ? 255 - *src++ : *src++; } src_row += rowstride; dest_row += rowstride; } } else { unsigned char * src_cmap, * dest_cmap, * out; int cmapsize, i; memcpy(dest_row, src_row, width*height); cmapsize = 3*gimp_image_colors(input); src_cmap = gimp_image_cmap(input); dest_cmap = malloc(cmapsize); for (i = 0, out = dest_cmap; i < cmapsize; ++i) { if (stretch) { v = *src_cmap++; if (v > threshold) v = (255-v)*255/(255-threshold); else v = v*255/threshold; *out++ = v; } else *out++ = (*src_cmap > threshold) ? 255 - *src_cmap++ : *src_cmap++; gimp_set_image_colors(output, dest_cmap, cmapsize/3); free(dest_cmap); } } /* Copy alpha channel */ if ((channels == 1) || (channels == 3)) return; src_row = gimp_image_data(input) + rowstride * y1 + (x1+1) * channels - 1; dest_row = gimp_image_data(output) + rowstride * y1 + (x1+1) * channels - 1; for (row = y1; row < y2; ++row) { src = src_row; dest = dest_row; for (col = x1; col < x2; ++col) { *dest = *src; dest += channels; src += channels; } dest_row += rowstride; src_row += rowstride; } } static void scale_callback(int item_ID, void *client_data, void *call_data) { #if AUTOAPPLY if (aapply && (*((int *) client_data) != *((long *) call_data))) { *((int *) client_data) = *((long *) call_data); solarize(input, output); gimp_update_image(output); } #endif *((int *) client_data) = *((long *) call_data); } static void ok_callback(int item_ID, void *client_data, void *call_data) { gimp_close_dialog(dialog_ID, 1); } static void cancel_callback(int item_ID, void *client_data, void *call_data) { gimp_close_dialog(dialog_ID, 0); } #if AUTOAPPLY static void toggle_callback_aapply(int item_ID, void *client_data, void *call_data) { *((long *) client_data) = *((long *) call_data); if (aapply) { solarize(input, output); gimp_update_image(output); } else { freshen(); gimp_update_image(output); } } #endif static void toggle_callback(int item_ID, void *client_data, void *call_data) { *((long *) client_data) = *((long *) call_data); }