mirror of https://github.com/GNOME/gimp.git
519 lines
14 KiB
C
519 lines
14 KiB
C
|
/* The GIMP -- an image manipulation program
|
||
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include "gdk/gdkkeysyms.h"
|
||
|
#include "appenv.h"
|
||
|
#include "actionarea.h"
|
||
|
#include "buildmenu.h"
|
||
|
#include "colormaps.h"
|
||
|
#include "color_select.h"
|
||
|
#include "errors.h"
|
||
|
#include "gdisplay.h"
|
||
|
#include "gimage.h"
|
||
|
#include "gimprc.h"
|
||
|
#include "general.h"
|
||
|
#include "image_render.h"
|
||
|
#include "interface.h"
|
||
|
#include "indexed_palette.h"
|
||
|
#include "undo.h"
|
||
|
|
||
|
#define EVENT_MASK GDK_BUTTON_PRESS_MASK | GDK_ENTER_NOTIFY_MASK
|
||
|
|
||
|
#define CELL_WIDTH 20
|
||
|
#define CELL_HEIGHT 20
|
||
|
#define P_AREA_WIDTH (CELL_WIDTH * 16)
|
||
|
#define P_AREA_HEIGHT (CELL_HEIGHT * 16)
|
||
|
|
||
|
/* Add these features:
|
||
|
*
|
||
|
* load/save colormaps
|
||
|
* requantize
|
||
|
* add color--by clicking in the checked region
|
||
|
* all changes need to flush colormap lookup cache
|
||
|
*/
|
||
|
|
||
|
typedef struct _IndexedPalette IndexedPalette;
|
||
|
|
||
|
struct _IndexedPalette {
|
||
|
GtkWidget *shell;
|
||
|
GtkWidget *vbox;
|
||
|
GtkWidget *palette;
|
||
|
GtkWidget *image_menu;
|
||
|
GtkWidget *image_option_menu;
|
||
|
|
||
|
/* state information */
|
||
|
int gimage_id;
|
||
|
int col_index;
|
||
|
};
|
||
|
|
||
|
/* indexed palette routines */
|
||
|
static void indexed_palette_draw (void);
|
||
|
static void indexed_palette_clear (void);
|
||
|
static void indexed_palette_update (int);
|
||
|
|
||
|
/* indexed palette menu callbacks */
|
||
|
static void indexed_palette_close_callback (GtkWidget *, gpointer);
|
||
|
static void indexed_palette_select_callback (int, int, int, ColorSelectState, void *);
|
||
|
static gint indexed_palette_delete_callback (GtkWidget *, GdkEvent *, gpointer);
|
||
|
|
||
|
/* event callback */
|
||
|
static gint indexed_palette_area_events (GtkWidget *, GdkEvent *);
|
||
|
|
||
|
/* create image menu */
|
||
|
static void image_menu_callback (GtkWidget *, gpointer);
|
||
|
static GtkWidget * create_image_menu (int *, int *, MenuItemCallback);
|
||
|
|
||
|
/* Only one indexed palette */
|
||
|
static IndexedPalette *indexedP = NULL;
|
||
|
|
||
|
/* Color select dialog */
|
||
|
static ColorSelectP color_select = NULL;
|
||
|
static int color_select_active = 0;
|
||
|
|
||
|
/* the action area structure */
|
||
|
static ActionAreaItem action_items[] =
|
||
|
{
|
||
|
{ "Close", indexed_palette_close_callback, NULL, NULL },
|
||
|
};
|
||
|
|
||
|
static MenuItem indexed_color_ops[] =
|
||
|
{
|
||
|
{ "Close", 'W', GDK_CONTROL_MASK,
|
||
|
indexed_palette_close_callback, NULL, NULL, NULL },
|
||
|
{ NULL, 0, 0, NULL, NULL, NULL, NULL },
|
||
|
};
|
||
|
|
||
|
|
||
|
/**************************************/
|
||
|
/* Public indexed palette functions */
|
||
|
/**************************************/
|
||
|
|
||
|
void
|
||
|
indexed_palette_create (int gimage_id)
|
||
|
{
|
||
|
GtkWidget *vbox;
|
||
|
GtkWidget *frame;
|
||
|
GtkWidget *util_box;
|
||
|
GtkWidget *label;
|
||
|
GtkWidget *arrow_hbox;
|
||
|
GtkWidget *arrow;
|
||
|
GtkWidget *ops_menu;
|
||
|
GtkWidget *menu_bar;
|
||
|
GtkWidget *menu_bar_item;
|
||
|
GtkAcceleratorTable *table;
|
||
|
int default_index;
|
||
|
|
||
|
if (!indexedP)
|
||
|
{
|
||
|
indexedP = g_malloc (sizeof (IndexedPalette));
|
||
|
indexedP->gimage_id = -1;
|
||
|
|
||
|
table = gtk_accelerator_table_new ();
|
||
|
|
||
|
/* The shell and main vbox */
|
||
|
indexedP->shell = gtk_dialog_new ();
|
||
|
gtk_window_set_title (GTK_WINDOW (indexedP->shell), "Indexed Color Palette");
|
||
|
gtk_window_add_accelerator_table (GTK_WINDOW (indexedP->shell), table);
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (indexedP->shell), "delete_event",
|
||
|
GTK_SIGNAL_FUNC (indexed_palette_delete_callback),
|
||
|
indexedP);
|
||
|
|
||
|
indexedP->vbox = vbox = gtk_vbox_new (FALSE, 1);
|
||
|
gtk_container_border_width (GTK_CONTAINER (vbox), 1);
|
||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (indexedP->shell)->vbox), vbox, TRUE, TRUE, 0);
|
||
|
|
||
|
/* The hbox to hold the command menu and image option menu box */
|
||
|
util_box = gtk_hbox_new (FALSE, 1);
|
||
|
gtk_box_pack_start (GTK_BOX (vbox), util_box, FALSE, FALSE, 0);
|
||
|
|
||
|
/* The GIMP image option menu */
|
||
|
label = gtk_label_new ("Image:");
|
||
|
gtk_box_pack_start (GTK_BOX (util_box), label, FALSE, FALSE, 2);
|
||
|
indexedP->image_option_menu = gtk_option_menu_new ();
|
||
|
indexedP->image_menu = create_image_menu (&gimage_id, &default_index, image_menu_callback);
|
||
|
gtk_box_pack_start (GTK_BOX (util_box), indexedP->image_option_menu, TRUE, TRUE, 2);
|
||
|
|
||
|
gtk_widget_show (indexedP->image_option_menu);
|
||
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (indexedP->image_option_menu), indexedP->image_menu);
|
||
|
if (default_index != -1)
|
||
|
gtk_option_menu_set_history (GTK_OPTION_MENU (indexedP->image_option_menu), default_index);
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
/* The indexed palette commands pulldown menu */
|
||
|
ops_menu = build_menu (indexed_color_ops, table);
|
||
|
|
||
|
menu_bar = gtk_menu_bar_new ();
|
||
|
gtk_box_pack_start (GTK_BOX (util_box), menu_bar, FALSE, FALSE, 2);
|
||
|
menu_bar_item = gtk_menu_item_new ();
|
||
|
gtk_container_add (GTK_CONTAINER (menu_bar), menu_bar_item);
|
||
|
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_bar_item), ops_menu);
|
||
|
arrow_hbox = gtk_hbox_new (FALSE, 1);
|
||
|
gtk_container_add (GTK_CONTAINER (menu_bar_item), arrow_hbox);
|
||
|
label = gtk_label_new ("Ops");
|
||
|
arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
|
||
|
gtk_box_pack_start (GTK_BOX (arrow_hbox), arrow, FALSE, FALSE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (arrow_hbox), label, FALSE, FALSE, 4);
|
||
|
gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
|
||
|
gtk_misc_set_alignment (GTK_MISC (arrow), 0.5, 0.5);
|
||
|
|
||
|
gtk_widget_show (arrow);
|
||
|
gtk_widget_show (label);
|
||
|
gtk_widget_show (arrow_hbox);
|
||
|
gtk_widget_show (menu_bar_item);
|
||
|
gtk_widget_show (menu_bar);
|
||
|
gtk_widget_show (util_box);
|
||
|
|
||
|
/* The palette frame */
|
||
|
frame = gtk_frame_new (NULL);
|
||
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
||
|
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
|
||
|
indexedP->palette = gtk_preview_new (GTK_PREVIEW_COLOR);
|
||
|
gtk_preview_size (GTK_PREVIEW (indexedP->palette), P_AREA_WIDTH, P_AREA_HEIGHT);
|
||
|
gtk_widget_set_events (indexedP->palette, EVENT_MASK);
|
||
|
gtk_signal_connect (GTK_OBJECT (indexedP->palette), "event",
|
||
|
(GtkSignalFunc) indexed_palette_area_events,
|
||
|
NULL);
|
||
|
gtk_container_add (GTK_CONTAINER (frame), indexedP->palette);
|
||
|
|
||
|
gtk_widget_show (indexedP->palette);
|
||
|
gtk_widget_show (frame);
|
||
|
|
||
|
/* The action area */
|
||
|
action_items[0].user_data = indexedP;
|
||
|
build_action_area (GTK_DIALOG (indexedP->shell), action_items, 1, 0);
|
||
|
|
||
|
gtk_widget_show (vbox);
|
||
|
gtk_widget_show (indexedP->shell);
|
||
|
|
||
|
indexed_palette_update (gimage_id);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!GTK_WIDGET_VISIBLE (indexedP->shell))
|
||
|
gtk_widget_show (indexedP->shell);
|
||
|
|
||
|
indexed_palette_update (gimage_id);
|
||
|
indexed_palette_update_image_list ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
indexed_palette_update_image_list ()
|
||
|
{
|
||
|
int default_index;
|
||
|
int default_id;
|
||
|
|
||
|
if (! indexedP)
|
||
|
return;
|
||
|
|
||
|
gtk_option_menu_remove_menu (GTK_OPTION_MENU (indexedP->image_option_menu));
|
||
|
gtk_widget_destroy (indexedP->image_menu);
|
||
|
|
||
|
default_id = indexedP->gimage_id;
|
||
|
indexedP->image_menu = create_image_menu (&default_id, &default_index, image_menu_callback);
|
||
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (indexedP->image_option_menu), indexedP->image_menu);
|
||
|
|
||
|
if (default_index != -1)
|
||
|
{
|
||
|
if (! GTK_WIDGET_IS_SENSITIVE (indexedP->vbox))
|
||
|
gtk_widget_set_sensitive (indexedP->vbox, TRUE);
|
||
|
gtk_option_menu_set_history (GTK_OPTION_MENU (indexedP->image_option_menu), default_index);
|
||
|
|
||
|
indexed_palette_update (default_id);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (GTK_WIDGET_IS_SENSITIVE (indexedP->vbox))
|
||
|
{
|
||
|
gtk_widget_set_sensitive (indexedP->vbox, FALSE);
|
||
|
indexed_palette_clear ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
indexed_palette_draw ()
|
||
|
{
|
||
|
GImage *gimage;
|
||
|
int i, j, k, l, b;
|
||
|
int col;
|
||
|
guchar row[P_AREA_WIDTH * 3];
|
||
|
|
||
|
if (!indexedP)
|
||
|
return;
|
||
|
if ((gimage = gimage_get_ID (indexedP->gimage_id)) == NULL)
|
||
|
return;
|
||
|
|
||
|
col = 0;
|
||
|
for (i = 0; i < 16; i++)
|
||
|
{
|
||
|
for (j = 0; j < 16 && col < gimage->num_cols; j++, col++)
|
||
|
{
|
||
|
for (k = 0; k < CELL_WIDTH; k++)
|
||
|
for (b = 0; b < 3; b++)
|
||
|
row[(j * CELL_WIDTH + k) * 3 + b] = gimage->cmap[col * 3 + b];
|
||
|
}
|
||
|
|
||
|
for (k = 0; k < CELL_HEIGHT; k++)
|
||
|
{
|
||
|
for (l = j * CELL_WIDTH; l < 16 * CELL_WIDTH; l++)
|
||
|
for (b = 0; b < 3; b++)
|
||
|
row[l * 3 + b] = ((((i * CELL_HEIGHT + k) & 0x4) ? (l) : (l + 0x4)) & 0x4) ?
|
||
|
blend_light_check[0] : blend_dark_check[0];
|
||
|
|
||
|
gtk_preview_draw_row (GTK_PREVIEW (indexedP->palette), row, 0,
|
||
|
i * CELL_HEIGHT + k, P_AREA_WIDTH);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gtk_widget_draw (indexedP->palette, NULL);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
indexed_palette_clear ()
|
||
|
{
|
||
|
int i, j;
|
||
|
int offset;
|
||
|
guchar row[P_AREA_WIDTH * 3];
|
||
|
|
||
|
if (!indexedP)
|
||
|
return;
|
||
|
|
||
|
for (i = 0; i < P_AREA_HEIGHT; i += 4)
|
||
|
{
|
||
|
offset = (i & 0x4) ? 0x4 : 0x0;
|
||
|
|
||
|
for (j = 0; j < P_AREA_WIDTH; j++)
|
||
|
{
|
||
|
row[j * 3 + 0] = row[j * 3 + 1] = row[j * 3 + 2] =
|
||
|
((j + offset) & 0x4) ? blend_light_check[0] : blend_dark_check[0];
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < 4; j++)
|
||
|
gtk_preview_draw_row (GTK_PREVIEW (indexedP->palette), row, 0, i + j, P_AREA_WIDTH);
|
||
|
}
|
||
|
|
||
|
gtk_widget_draw (indexedP->palette, NULL);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
indexed_palette_update (int gimage_id)
|
||
|
{
|
||
|
GImage *gimage;
|
||
|
|
||
|
if (!indexedP)
|
||
|
return;
|
||
|
if ((gimage = gimage_get_ID (gimage_id)) == NULL)
|
||
|
return;
|
||
|
|
||
|
if (gimage_base_type (gimage) == INDEXED)
|
||
|
{
|
||
|
indexedP->gimage_id = gimage_id;
|
||
|
indexed_palette_draw ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static gint
|
||
|
indexed_palette_delete_callback (GtkWidget *w,
|
||
|
GdkEvent *e,
|
||
|
gpointer client_data)
|
||
|
{
|
||
|
indexed_palette_close_callback (w, client_data);
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
indexed_palette_close_callback (GtkWidget *w,
|
||
|
gpointer client_data)
|
||
|
{
|
||
|
if (!indexedP)
|
||
|
return;
|
||
|
|
||
|
if (GTK_WIDGET_VISIBLE (indexedP->shell))
|
||
|
gtk_widget_hide (indexedP->shell);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
indexed_palette_select_callback (int r,
|
||
|
int g,
|
||
|
int b,
|
||
|
ColorSelectState state,
|
||
|
void *client_data)
|
||
|
{
|
||
|
GImage *gimage;
|
||
|
|
||
|
if (!indexedP)
|
||
|
return;
|
||
|
|
||
|
if ((gimage = gimage_get_ID (indexedP->gimage_id)) == NULL)
|
||
|
return;
|
||
|
|
||
|
if (color_select )
|
||
|
{
|
||
|
switch (state) {
|
||
|
case COLOR_SELECT_UPDATE:
|
||
|
break;
|
||
|
case COLOR_SELECT_OK:
|
||
|
gimage->cmap[indexedP->col_index * 3 + 0] = r;
|
||
|
gimage->cmap[indexedP->col_index * 3 + 1] = g;
|
||
|
gimage->cmap[indexedP->col_index * 3 + 2] = b;
|
||
|
|
||
|
gdisplays_update_full (gimage->ID);
|
||
|
indexed_palette_draw ();
|
||
|
/* Fallthrough */
|
||
|
case COLOR_SELECT_CANCEL:
|
||
|
color_select_hide (color_select);
|
||
|
color_select_active = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static gint
|
||
|
indexed_palette_area_events (GtkWidget *widget,
|
||
|
GdkEvent * event)
|
||
|
{
|
||
|
GImage *gimage;
|
||
|
GdkEventButton *bevent;
|
||
|
int r, g, b;
|
||
|
|
||
|
if (!indexedP)
|
||
|
return FALSE;
|
||
|
|
||
|
if ((gimage = gimage_get_ID (indexedP->gimage_id)) == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
switch (event->type)
|
||
|
{
|
||
|
case GDK_BUTTON_PRESS:
|
||
|
bevent = (GdkEventButton *) event;
|
||
|
|
||
|
if (bevent->button == 1)
|
||
|
{
|
||
|
indexedP->col_index = 16 * (bevent->y / CELL_HEIGHT) + (bevent->x / CELL_WIDTH);
|
||
|
r = gimage->cmap[indexedP->col_index * 3 + 0];
|
||
|
g = gimage->cmap[indexedP->col_index * 3 + 1];
|
||
|
b = gimage->cmap[indexedP->col_index * 3 + 2];
|
||
|
|
||
|
if (! color_select)
|
||
|
{
|
||
|
color_select = color_select_new (r, g, b, indexed_palette_select_callback, NULL);
|
||
|
color_select_active = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (! color_select_active)
|
||
|
color_select_show (color_select);
|
||
|
color_select_set_color (color_select, r, g, b, 1);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
image_menu_callback (GtkWidget *w,
|
||
|
gpointer client_data)
|
||
|
{
|
||
|
if (!indexedP)
|
||
|
return;
|
||
|
if (gimage_get_ID ((long) client_data) != NULL)
|
||
|
{
|
||
|
indexed_palette_update ((long) client_data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static GtkWidget *
|
||
|
create_image_menu (int *default_id,
|
||
|
int *default_index,
|
||
|
MenuItemCallback callback)
|
||
|
{
|
||
|
extern link_ptr image_list;
|
||
|
|
||
|
GImage *gimage;
|
||
|
GtkWidget *menu_item;
|
||
|
GtkWidget *menu;
|
||
|
char *menu_item_label;
|
||
|
char *image_name;
|
||
|
link_ptr tmp;
|
||
|
int num_items = 0;
|
||
|
int id;
|
||
|
|
||
|
id = -1;
|
||
|
|
||
|
*default_index = -1;
|
||
|
menu = gtk_menu_new ();
|
||
|
|
||
|
tmp = image_list;
|
||
|
while (tmp)
|
||
|
{
|
||
|
gimage = tmp->data;
|
||
|
tmp = next_item (tmp);
|
||
|
|
||
|
if (gimage_base_type (gimage) == INDEXED)
|
||
|
{
|
||
|
id = -1;
|
||
|
|
||
|
/* make sure the default index gets set to _something_, if possible */
|
||
|
if (*default_index == -1)
|
||
|
{
|
||
|
id = gimage->ID;
|
||
|
*default_index = num_items;
|
||
|
}
|
||
|
|
||
|
if (gimage->ID == *default_id)
|
||
|
{
|
||
|
id = *default_id;
|
||
|
*default_index = num_items;
|
||
|
}
|
||
|
|
||
|
image_name = prune_filename (gimage_filename (gimage));
|
||
|
menu_item_label = (char *) g_malloc (strlen (image_name) + 15);
|
||
|
sprintf (menu_item_label, "%s-%d", image_name, gimage->ID);
|
||
|
menu_item = gtk_menu_item_new_with_label (menu_item_label);
|
||
|
gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
|
||
|
(GtkSignalFunc) callback,
|
||
|
(gpointer) ((long) gimage->ID));
|
||
|
gtk_container_add (GTK_CONTAINER (menu), menu_item);
|
||
|
gtk_widget_show (menu_item);
|
||
|
|
||
|
g_free (menu_item_label);
|
||
|
num_items ++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!num_items)
|
||
|
{
|
||
|
menu_item = gtk_menu_item_new_with_label ("none");
|
||
|
gtk_container_add (GTK_CONTAINER (menu), menu_item);
|
||
|
gtk_widget_show (menu_item);
|
||
|
}
|
||
|
|
||
|
*default_id = id;
|
||
|
|
||
|
return menu;
|
||
|
}
|