From: Holger Schemel Date: Sat, 21 Sep 2024 20:35:06 +0000 (+0200) Subject: added color picker for selecting BD engine colors X-Git-Tag: 4.4.0.0-test-4~209 X-Git-Url: https://git.artsoft.org/?a=commitdiff_plain;h=5064c0a946aebe8e0c58398941a0b81ac07c161a;p=rocksndiamonds.git added color picker for selecting BD engine colors --- diff --git a/src/conf_gfx.c b/src/conf_gfx.c index 43d3b8b1..39ee3b58 100644 --- a/src/conf_gfx.c +++ b/src/conf_gfx.c @@ -11149,6 +11149,8 @@ struct ConfigInfo image_config[] = { "editor.settings.tabs.2nd_yoffset", "64" }, { "editor.settings.tabs.draw_xoffset", "0" }, { "editor.settings.tabs.draw_yoffset", "8" }, + { "editor.settings.colorpicker.x", "-1" }, + { "editor.settings.colorpicker.y", "-1" }, { "editor.settings.tooltip.x", "-1" }, { "editor.settings.tooltip.y", "-1" }, diff --git a/src/editor.c b/src/editor.c index 5baac1e2..58fcaee6 100644 --- a/src/editor.c +++ b/src/editor.c @@ -127,10 +127,11 @@ #define ED_ENGINE_TABS_YSTART (ED_SETTINGS_ENGINE_TABS_Y) #define ED_ENGINE_SETTINGS_XSTART (ED_SETTINGS_ENGINE_TABS_X + \ ED_SETTINGS_TABS_XOFFSET) -#define ED_ENGINE_SETTINGS_YSTART (ED_SETTINGS_ENGINE_TABS_Y + \ +#define ED_ENGINE_SETTINGS_YSTART_0 (ED_SETTINGS_ENGINE_TABS_Y + \ ED_TABBUTTON_YSIZE + \ ED_GADGET_TINY_DISTANCE + \ - ED_TAB_BAR_HEIGHT + \ + ED_TAB_BAR_HEIGHT) +#define ED_ENGINE_SETTINGS_YSTART (ED_ENGINE_SETTINGS_YSTART_0 + \ ED_SETTINGS_TABS_YOFFSET + \ getFontHeight(FONT_TEXT_1) + \ ED_GADGET_TEXT_DISTANCE) @@ -402,6 +403,15 @@ ED_ELEMENTLIST_YSIZE - \ 2 * ED_SCROLLBUTTON2_YSIZE) +#define COLORPICKER_X (editor.settings.colorpicker.x) +#define COLORPICKER_Y (editor.settings.colorpicker.y) +#define COLORPICKER_X_REDEFINED (COLORPICKER_X != -1) +#define COLORPICKER_Y_REDEFINED (COLORPICKER_Y != -1) +#define COLORPICKER_XPOS SX + (COLORPICKER_X_REDEFINED ? COLORPICKER_X : \ + ED_ENGINE_SETTINGS_XSTART) +#define COLORPICKER_YPOS SY + (COLORPICKER_Y_REDEFINED ? COLORPICKER_Y : \ + ED_ENGINE_SETTINGS_YSTART_0 + ED_GADGET_TEXT_DISTANCE) + // values for ClearEditorGadgetInfoText() and HandleEditorGadgetInfoText() #define INFOTEXT_FONT FONT_TEXT_2 #define INFOTEXT_XSIZE SXSIZE @@ -409,9 +419,10 @@ #define INFOTEXT_YSIZE_FULL (INFOTEXT_YSIZE + ED_GADGET_SMALL_DISTANCE) #define INFOTEXT_X (editor.settings.tooltip.x) #define INFOTEXT_Y (editor.settings.tooltip.y) -#define INFOTEXT_XY_REDEFINED (INFOTEXT_X != -1 || INFOTEXT_Y != -1) -#define INFOTEXT_XPOS SX + (INFOTEXT_XY_REDEFINED ? INFOTEXT_X : 0) -#define INFOTEXT_YPOS SY + (INFOTEXT_XY_REDEFINED ? INFOTEXT_Y : \ +#define INFOTEXT_X_REDEFINED (INFOTEXT_X != -1) +#define INFOTEXT_Y_REDEFINED (INFOTEXT_Y != -1) +#define INFOTEXT_XPOS SX + (INFOTEXT_X_REDEFINED ? INFOTEXT_X : 0) +#define INFOTEXT_YPOS SY + (INFOTEXT_Y_REDEFINED ? INFOTEXT_Y : \ SYSIZE - INFOTEXT_YSIZE) @@ -5429,14 +5440,15 @@ static struct int x, y; int gadget_id; int *value; + int color_type; char *infotext; } colorpicker_info[ED_NUM_COLORPICKERS] = { { ED_COLORPICKER_ID_PICK_COLOR, - 8, 40, + -1, -1, GADGET_ID_COLOR_PICKER, - NULL, + NULL, -1, "Select color" }, }; @@ -9477,6 +9489,10 @@ static void CreateColorPickerGadgets(void) unsigned int event_mask = GD_EVENT_COLOR_PICKER_LEAVING; int id = colorpicker_info[i].gadget_id; int type_id = colorpicker_info[i].gadget_type_id; + int x = (colorpicker_info[i].x != -1 ? SX + ED_SETTINGS_X(colorpicker_info[i].x) : + COLORPICKER_XPOS); + int y = (colorpicker_info[i].y != -1 ? SY + ED_SETTINGS_Y(colorpicker_info[i].y) : + COLORPICKER_YPOS); if (type_id != i) Fail("'colorpicker_info' structure corrupted at index %d -- please fix", i); @@ -9484,9 +9500,10 @@ static void CreateColorPickerGadgets(void) gi = CreateGadget(GDI_CUSTOM_ID, id, GDI_CUSTOM_TYPE_ID, type_id, GDI_INFO_TEXT, colorpicker_info[i].infotext, - GDI_X, SX + ED_SETTINGS_X(colorpicker_info[i].x), - GDI_Y, SY + ED_SETTINGS_Y(colorpicker_info[i].y), + GDI_X, x, + GDI_Y, y, GDI_TYPE, GD_TYPE_COLOR_PICKER, + GDI_TEXT_FONT, FONT_INPUT_2, GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1, GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2, GDI_BORDER_SIZE, gd->border_size, gd->border_size, @@ -9888,9 +9905,14 @@ static void MapCheckbuttonGadget(int id) MapGadget(gi); } -static void MapColorPickerGadget(int id) +static void MapColorPickerGadget(int id, int color_nr) { struct GadgetInfo *gi = level_editor_gadget[colorpicker_info[id].gadget_id]; + int color = gd_color_get_rgb(*bd_color[color_nr]); + + ModifyGadget(gi, GDI_COLOR_NR, color_nr, GDI_END); + ModifyGadget(gi, GDI_COLOR_TYPE, level.bd_color_type, GDI_END); + ModifyGadget(gi, GDI_COLOR_VALUE, color, GDI_END); MapGadget(gi); } @@ -16983,7 +17005,7 @@ static void HandleGraphicbuttonGadgets(struct GadgetInfo *gi) else if (type_id >= ED_GRAPHICBUTTON_ID_PICK_FIRST || type_id <= ED_GRAPHICBUTTON_ID_PICK_LAST) { - MapColorPickerGadget(ED_COLORPICKER_ID_PICK_COLOR); + MapColorPickerGadget(ED_COLORPICKER_ID_PICK_COLOR, type_id - ED_GRAPHICBUTTON_ID_PICK_FIRST); } } @@ -17125,7 +17147,12 @@ static void HandleColorPickerGadgets(struct GadgetInfo *gi) int type_id = gi->custom_type_id; if (type_id == ED_COLORPICKER_ID_PICK_COLOR) + { + if (gi->colorpicker.value != -1) + *bd_color[gi->colorpicker.nr] = gi->colorpicker.value; + DrawLevelConfigWindow(); + } level.changed = TRUE; } diff --git a/src/libgame/gadgets.c b/src/libgame/gadgets.c index 00e4b97c..9024ea82 100644 --- a/src/libgame/gadgets.c +++ b/src/libgame/gadgets.c @@ -45,6 +45,303 @@ static void (*PlayGadgetSoundActivating)(void) = NULL; static void (*PlayGadgetSoundSelecting)(void) = NULL; +// RGB/HSV conversions by David H from https://stackoverflow.com/questions/3018313/ + +// A bunch of magic numbers for UI positioning (used by DrawColorPicker() and its functions) +#define CP_GADGET_WIDTH (256) +#define CP_GADGET_HEIGHT (300) +#define CP_MAIN_GRADIENT_WIDTH (CP_GADGET_WIDTH) +#define CP_MAIN_GRADIENT_HEIGHT (CP_GADGET_WIDTH) +#define CP_SAMPLE_BOX_WIDTH (CP_GADGET_HEIGHT - CP_GADGET_WIDTH) +#define CP_SAMPLE_BOX_HEIGHT (CP_SAMPLE_BOX_WIDTH) +#define CP_HUE_GRADIENT_WIDTH (CP_GADGET_WIDTH - CP_SAMPLE_BOX_WIDTH) +#define CP_HUE_GRADIENT_HEIGHT (CP_SAMPLE_BOX_HEIGHT / 2) +#define CP_INDICATOR_SIZE (3) + +// Start of David H's conversion code +typedef struct +{ + double r; // a fraction between 0 and 1 + double g; // a fraction between 0 and 1 + double b; // a fraction between 0 and 1 +} RGBColor; + +typedef struct +{ + double h; // angle in degrees + double s; // a fraction between 0 and 1 + double v; // a fraction between 0 and 1 +} HSVColor; + +static HSVColor rgb_to_hsv(RGBColor in) +{ + HSVColor out; + double min, max, delta; + + min = in.r < in.g ? in.r : in.g; + min = min < in.b ? min : in.b; + + max = in.r > in.g ? in.r : in.g; + max = max > in.b ? max : in.b; + + out.v = max; // v + delta = max - min; + + if (delta < 0.00001) + { + out.s = 0; + out.h = 0; // undefined, maybe nan? + + return out; + } + + if (max > 0.0) + { + // NOTE: if Max is == 0, this divide would cause a crash + out.s = (delta / max); // s + } + else + { + // if max is 0, then r = g = b = 0 + // s = 0, h is undefined + out.s = 0.0; + out.h = NAN; // its now undefined + + return out; + } + + if (in.r >= max) // > is bogus, just keeps compilor happy + out.h = (in.g - in.b) / delta; // between yellow & magenta + else if (in.g >= max) + out.h = 2.0 + (in.b - in.r) / delta; // between cyan & yellow + else + out.h = 4.0 + (in.r - in.g) / delta; // between magenta & cyan + + out.h *= 60.0; // degrees + + if (out.h < 0.0) + out.h += 360.0; + + return out; +} + +static RGBColor hsv_to_rgb(HSVColor in) +{ + double hh, p, q, t, ff; + RGBColor out; + int i; + + if (in.s <= 0.0) + { + // < is bogus, just shuts up warnings + out.r = in.v; + out.g = in.v; + out.b = in.v; + + return out; + } + + hh = in.h; + if (hh >= 360.0) + hh = 0.0; + + hh /= 60.0; + i = (int)hh; + ff = hh - i; + p = in.v * (1.0 - in.s); + q = in.v * (1.0 - (in.s * ff)); + t = in.v * (1.0 - (in.s * (1.0 - ff))); + + switch (i) + { + case 0: + out.r = in.v; + out.g = t; + out.b = p; + break; + + case 1: + out.r = q; + out.g = in.v; + out.b = p; + break; + + case 2: + out.r = p; + out.g = in.v; + out.b = t; + break; + + case 3: + out.r = p; + out.g = q; + out.b = in.v; + break; + + case 4: + out.r = t; + out.g = p; + out.b = in.v; + break; + + case 5: + default: + out.r = in.v; + out.g = p; + out.b = q; + break; + } + + return out; +} +// End of David H's conversion code + +static HSVColor cp_color_hsv = { 180.0, 1.0, 1.0 }; // use some sane default values + +enum +{ + CP_STATE_NONE, + CP_STATE_GRADIENT_CHANGE, + CP_STATE_SLIDER_CHANGE, + CP_STATE_SAMPLE_BOX, +}; + +static SDL_Color from_RGBColor(RGBColor rgb_color) +{ + SDL_Color color = + { + rgb_color.r * 255, + rgb_color.g * 255, + rgb_color.b * 255, + 0xff + }; + + return color; +} + +static void set_pixel(SDL_Surface *surface, SDL_Color color, int x, int y) +{ + ((char *)surface->pixels)[(x + y * surface->w) * 4 ] = color.r; + ((char *)surface->pixels)[(x + y * surface->w) * 4 + 1] = color.g; + ((char *)surface->pixels)[(x + y * surface->w) * 4 + 2] = color.b; + ((char *)surface->pixels)[(x + y * surface->w) * 4 + 3] = color.a; +} + +static void DrawColorPicker_Gradient(SDL_Surface *surface, double hue) +{ + int xsize = CP_MAIN_GRADIENT_WIDTH; + int ysize = CP_MAIN_GRADIENT_HEIGHT; + int xpos = 0; + int ypos = 0; + int x, y; + + for (y = 0; y < ysize; y++) + { + for (x = 0; x < xsize; x++) + { + HSVColor hsv_color = + { + hue, + (double) x / xsize, + 1.0 - ((double) y / ysize), + }; + + set_pixel(surface, from_RGBColor(hsv_to_rgb(hsv_color)), xpos + x, ypos + y); + } + } +} + +static void DrawColorPicker_HueGradient(SDL_Surface *surface) +{ + int xsize = CP_HUE_GRADIENT_WIDTH; + int ysize = CP_HUE_GRADIENT_HEIGHT; + int xpos = 0; + int ypos = CP_MAIN_GRADIENT_HEIGHT; + int x, y; + + for (y = 0; y < ysize; y++) + { + for (x = 0; x < xsize; x++) + { + HSVColor hsv_color = + { + (double) x / xsize * 360, + 1.0, + 1.0, + }; + + set_pixel(surface, from_RGBColor(hsv_to_rgb(hsv_color)), xpos + x, ypos + y); + } + } +} + +static void DrawColorPicker_SampleBox(SDL_Surface *surface, SDL_Color color) +{ + SDL_Rect draw_rect = + { + CP_HUE_GRADIENT_WIDTH, + CP_MAIN_GRADIENT_HEIGHT, + CP_SAMPLE_BOX_WIDTH, + CP_SAMPLE_BOX_HEIGHT, + }; + Pixel pixel = SDL_MapRGB(surface->format, color.r, color.g, color.b); + + SDL_FillRect(surface, &draw_rect, pixel); +} + +static void DrawColorPicker_ColorText(int x, int y, int font_nr) +{ + RGBColor rgb_color = hsv_to_rgb(cp_color_hsv); + char text[128]; + + sprintf(text, "#%02x%02x%02x", + (int) (rgb_color.r * 255), + (int) (rgb_color.g * 255), + (int) (rgb_color.b * 255)); + + DrawText(x, y, text, font_nr); +} + +static void DrawColorPicker(struct GadgetInfo *gi) +{ + int x = gi->x + gi->border.xsize; + int y = gi->y + gi->border.ysize;; + int font_nr = gi->font; + int font_height = getFontHeight(font_nr); + int font_padding = (CP_HUE_GRADIENT_HEIGHT - font_height) / 2; + int xsize = CP_GADGET_WIDTH; + int ysize = CP_GADGET_HEIGHT; + int xsize_main = CP_MAIN_GRADIENT_WIDTH; + int ysize_main = CP_MAIN_GRADIENT_HEIGHT; + int xsize_hue = CP_HUE_GRADIENT_WIDTH; + int ysize_hue = CP_HUE_GRADIENT_HEIGHT; + int x_main = x + (xsize_main - 1) * cp_color_hsv.s; + int y_main = y + (ysize_main - 1) * (1.0 - cp_color_hsv.v); + int x_hue = x + ((cp_color_hsv.h / 360.0) * xsize_hue); + int y_hue = y + ysize_main; + int x_text = x + font_padding; + int y_text = y + ysize_main + ysize_hue + font_padding; + SDL_Surface *surface = SDL_CreateRGBSurface(0, xsize, ysize, 32, + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + + DrawColorPicker_Gradient(surface, cp_color_hsv.h); + DrawColorPicker_HueGradient(surface); + DrawColorPicker_SampleBox(surface, from_RGBColor(hsv_to_rgb(cp_color_hsv))); + + SDLBlitSurface(surface, drawto->surface, 0, 0, xsize, ysize, x, y); + SDL_FreeSurface(surface); + + // draw color position indicator + FillRectangle(drawto, x_main - 1, y, CP_INDICATOR_SIZE, ysize_main, BLACK_PIXEL); + FillRectangle(drawto, x, y_main - 1, xsize_main, CP_INDICATOR_SIZE, BLACK_PIXEL); + + // draw color slider + FillRectangle(drawto, x_hue - 1, y_hue, CP_INDICATOR_SIZE, ysize_hue, BLACK_PIXEL); + + // draw color RGB value + DrawColorPicker_ColorText(x_text, y_text, font_nr); +} + void InitGadgetsSoundCallback(void (*activating_function)(void), void (*selecting_function)(void)) { @@ -908,6 +1205,8 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct) y + border_y, width - 2 * border_x, height - 2 * border_y); + + DrawColorPicker(gi); } break; @@ -1388,6 +1687,18 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) gi->callback_action = va_arg(ap, gadget_function); break; + case GDI_COLOR_NR: + gi->colorpicker.nr = va_arg(ap, int); + break; + + case GDI_COLOR_TYPE: + gi->colorpicker.type = va_arg(ap, int); + break; + + case GDI_COLOR_VALUE: + gi->colorpicker.value = va_arg(ap, int); + break; + default: Fail("HandleGadgetTags(): unknown tag %d", tag); } @@ -1594,8 +1905,17 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) if (gi->type & GD_TYPE_COLOR_PICKER) { - gi->width = COLOR_PICKER_WIDTH; - gi->height = COLOR_PICKER_HEIGHT; + gi->width = CP_GADGET_WIDTH + 2 * gi->border.xsize; + gi->height = CP_GADGET_HEIGHT + 2 * gi->border.ysize; + + RGBColor cp_color_rgb = + { + ((gi->colorpicker.value >> 16) & 255) / 255.0, + ((gi->colorpicker.value >> 8) & 255) / 255.0, + ((gi->colorpicker.value ) & 255) / 255.0, + }; + + cp_color_hsv = rgb_to_hsv(cp_color_rgb); } } @@ -1973,7 +2293,7 @@ boolean HandleGadgets(int mx, int my, int button) // color picker does not select color when closed by clicking outside if (gi->type & GD_TYPE_COLOR_PICKER) - gadget_changed = FALSE; + gi->colorpicker.value = -1; DrawGadget(gi, DG_UNPRESSED, gi->direct_draw); @@ -2138,6 +2458,48 @@ boolean HandleGadgets(int mx, int my, int button) if (gi->selectbox.current_index != old_index) DrawGadget(gi, DG_PRESSED, gi->direct_draw); } + else if (gi->type & GD_TYPE_COLOR_PICKER && button) + { + static int state = CP_STATE_NONE; + int xsize_main = CP_MAIN_GRADIENT_WIDTH; + int ysize_main = CP_MAIN_GRADIENT_HEIGHT; + int xsize_hue = CP_HUE_GRADIENT_WIDTH; + int ysize_hue = CP_HUE_GRADIENT_HEIGHT; + int xpos = mx - gi->x - gi->border.xsize; + int ypos = my - gi->y - gi->border.ysize; + + if (!motion_status) + state = (ypos < ysize_main ? CP_STATE_GRADIENT_CHANGE : + xpos < xsize_hue && ypos < ysize_main + ysize_hue ? CP_STATE_SLIDER_CHANGE : + xpos >= xsize_hue && ypos >= ysize_main ? CP_STATE_SAMPLE_BOX : + CP_STATE_NONE); + + if (state == CP_STATE_GRADIENT_CHANGE) + { + cp_color_hsv.s = (double)MIN(MAX(0, xpos), xsize_main) / xsize_main; + cp_color_hsv.v = 1.0 - (double)MIN(MAX(0, ypos), ysize_main) / ysize_main; + + DrawColorPicker(gi); + } + else if (state == CP_STATE_SLIDER_CHANGE) + { + cp_color_hsv.h = (double)MIN(MAX(0, xpos), xsize_hue) / xsize_hue * 360.0; + + DrawColorPicker(gi); + } + else if (state == CP_STATE_SAMPLE_BOX) + { + RGBColor rgb_color = hsv_to_rgb(cp_color_hsv); + int color = ((int)(rgb_color.r * 255) << 16 | + (int)(rgb_color.g * 255) << 8 | + (int)(rgb_color.b * 255)); + + gi->event.type = GD_EVENT_COLOR_PICKER_LEAVING; + gi->colorpicker.value = color; + + DoGadgetCallbackAction(gi, TRUE); + } + } } // handle gadget popup info text diff --git a/src/libgame/gadgets.h b/src/libgame/gadgets.h index cc9620d5..c7e8d17e 100644 --- a/src/libgame/gadgets.h +++ b/src/libgame/gadgets.h @@ -63,8 +63,6 @@ // gadget structure constants #define MAX_GADGET_TEXTSIZE 1024 #define MAX_INFO_TEXTSIZE 1024 -#define COLOR_PICKER_WIDTH 256 -#define COLOR_PICKER_HEIGHT 300 // gadget creation tags #define GDI_END 0 @@ -121,6 +119,9 @@ #define GDI_DIRECT_DRAW 51 #define GDI_OVERLAY_TOUCH_BUTTON 52 #define GDI_CALLBACK_ACTION_ALWAYS 53 +#define GDI_COLOR_NR 54 +#define GDI_COLOR_TYPE 55 +#define GDI_COLOR_VALUE 56 // gadget deactivation hack #define GDI_ACTIVE_POS(a) ((a) < 0 ? POS_OFFSCREEN : (a)) @@ -245,6 +246,13 @@ struct GadgetWheelArea int width, height; // active area for wheel (size) }; +struct GadgetColorPicker +{ + int nr; // color slot (if using several colors) + int type; // color type (RGB, C64, C64DTV, Atari) + int value; // color value +}; + struct GadgetInfo { boolean deactivated; // flag to deactivate gadget @@ -285,6 +293,7 @@ struct GadgetInfo struct GadgetSelectbox selectbox; // fields for selectbox gadget struct GadgetScrollbar scrollbar; // fields for scrollbar gadget struct GadgetWheelArea wheelarea; // fields for scroll wheel area + struct GadgetColorPicker colorpicker; // fields for color picker gadget struct GadgetInfo *next; // next list entry }; diff --git a/src/main.h b/src/main.h index 8e1d35ea..522e4652 100644 --- a/src/main.h +++ b/src/main.h @@ -3507,6 +3507,7 @@ struct EditorSettingsInfo struct EditorTabsInfo tabs; + struct XY colorpicker; struct XY tooltip; };