UNIXworkcode

1 /* 2 * Copyright 2021 Olaf Wintermann 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 */ 22 23 #include "colorchooser.h" 24 25 #include <Xm/XmAll.h> 26 #include <Xm/XmP.h> 27 #include <X11/Xft/Xft.h> 28 29 #include <inttypes.h> 30 #include <limits.h> 31 32 #define XNE_COLOR_DIALOG_TITLE "Select Color" 33 34 #define WIDGET_SPACING 5 35 #define WINDOW_SPACING 8 36 37 typedef struct { 38 Widget dialog; 39 40 Widget preview; 41 Widget textfield; 42 Widget selector; 43 GC gc; 44 GC selGC; 45 XftDraw *d; // preview xft drawable 46 47 uint8_t base_red; 48 uint8_t base_green; 49 uint8_t base_blue; 50 51 int base_sel_y; 52 float hue; 53 float saturation; 54 float value; 55 56 uint8_t selected_red; 57 uint8_t selected_green; 58 uint8_t selected_blue; 59 XftColor selected_color; 60 61 XImage *image1; 62 Dimension img1_width; 63 Dimension img1_height; 64 65 XImage *image2; 66 Dimension img2_width; 67 Dimension img2_height; 68 69 int has_selection; 70 Dimension img2_select_x; 71 Dimension img2_select_y; 72 73 int parse_textfield_input; 74 75 int status; 76 int end; 77 } cgData; 78 79 static XftDraw* create_xft_draw(Widget w); 80 static void selector_expose(Widget w, XtPointer u, XtPointer c); 81 static void selector_input(Widget w, XtPointer u, XtPointer c); 82 static void preview_color(Widget w, XtPointer u, XtPointer c); 83 static void textfield_changed(Widget w, XtPointer u, XtPointer c); 84 static void set_base_color(cgData *data, int r, int g, int b); 85 static void select_color(cgData *data, int r, int g, int b); 86 87 static void draw_img2(Display *dp, Window win, cgData *data); 88 89 static void okCB(Widget w, XtPointer u, XtPointer c); 90 static void cancelCB(Widget w, XtPointer u, XtPointer c); 91 92 int ColorChooser(Widget parent, int *red, int *green, int *blue) { 93 Arg args[32]; 94 int n; 95 XmString str; 96 97 cgData data; 98 memset(&data, 0, sizeof(data)); 99 100 data.base_sel_y = -1; 101 102 n = 0; 103 XtSetArg(args[n], XmNautoUnmanage, False); n++; 104 Widget form = XmCreateFormDialog(parent, "ColorDialog", args, n); 105 Widget dialog = XtParent(form); 106 XtVaSetValues(dialog, XmNtitle, XNE_COLOR_DIALOG_TITLE, NULL); 107 XtManageChild(form); 108 109 // UI 110 111 // color preview field 112 n = 0; 113 XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); n++; 114 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 115 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 116 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 117 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 118 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 119 XtSetArg(args[n], XmNrightPosition, 30); n++; 120 //XtSetArg(args[n], XmNwidth, 50); n++; 121 data.preview = XmCreateDrawingArea(form, "cgPreview", args, n); 122 XtManageChild(data.preview); 123 XtAddCallback(data.preview, XmNexposeCallback, preview_color, &data); 124 125 // rgb textfield 126 n = 0; 127 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 128 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 129 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 130 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 131 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 132 XtSetArg(args[n], XmNleftPosition, 33); n++; 133 data.textfield = XmCreateTextField(form, "cgText", args, n); 134 XtManageChild(data.textfield); 135 XtAddCallback(data.textfield, XmNvalueChangedCallback, textfield_changed, &data); 136 137 XtVaSetValues(data.preview, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, data.textfield, NULL); 138 139 140 // ok/cancel buttons 141 n = 0; 142 str = XmStringCreateSimple("OK"); 143 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 144 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 145 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 146 XtSetArg(args[n], XmNleftPosition, 12); n++; 147 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 148 XtSetArg(args[n], XmNrightPosition, 37); n++; 149 XtSetArg(args[n], XmNlabelString, str); n++; 150 Widget ok = XmCreatePushButton(form, "cgOK", args, n); 151 XtManageChild(ok); 152 XmStringFree(str); 153 XtAddCallback(ok, XmNactivateCallback, okCB, &data); 154 155 n = 0; 156 str = XmStringCreateSimple("Cancel"); 157 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 158 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 159 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 160 XtSetArg(args[n], XmNleftPosition, 63); n++; 161 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 162 XtSetArg(args[n], XmNrightPosition, 88); n++; 163 XtSetArg(args[n], XmNlabelString, str); n++; 164 Widget cancel = XmCreatePushButton(form, "cgOK", args, n); 165 XtManageChild(cancel); 166 XmStringFree(str); 167 XtAddCallback(cancel, XmNactivateCallback, cancelCB, &data); 168 169 XtVaSetValues(form, XmNdefaultButton, ok, NULL); 170 XtVaSetValues(form, XmNcancelButton, cancel, NULL); 171 172 // color selector 173 static XtTranslations selectorTranslations = NULL; 174 if(!selectorTranslations) { 175 selectorTranslations = XtParseTranslationTable("<Btn1Down>: DrawingAreaInput() ManagerGadgetArm()\n<Btn1Motion>: DrawingAreaInput() ManagerGadgetButtonMotion()"); 176 } 177 178 n = 0; 179 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 180 XtSetArg(args[n], XmNtopWidget, data.textfield); n++; 181 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 182 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 183 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 184 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 185 XtSetArg(args[n], XmNbottomWidget, ok); n++; 186 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 187 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 188 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 189 XtSetArg(args[n], XmNwidth, 500); n++; 190 XtSetArg(args[n], XmNheight, 400); n++; 191 XtSetArg(args[n], XmNtranslations, selectorTranslations); n++; 192 data.selector = XmCreateDrawingArea(form, "cgSelector", args, n); 193 XtManageChild(data.selector); 194 XtAddCallback(data.selector, XmNexposeCallback, selector_expose, &data); 195 XtAddCallback(data.selector, XmNinputCallback, selector_input, &data); 196 197 XGCValues gcValues; 198 gcValues.background = data.selector->core.background_pixel; 199 gcValues.foreground = BlackPixelOfScreen(DefaultScreenOfDisplay(XtDisplay(data.selector))); 200 gcValues.graphics_exposures = 0; 201 data.gc = XtAllocateGC(data.selector, 0, GCForeground | GCBackground, &gcValues, 0, 0); 202 203 gcValues.background = data.selector->core.background_pixel; 204 gcValues.foreground = WhitePixelOfScreen(DefaultScreenOfDisplay(XtDisplay(data.selector))); 205 gcValues.graphics_exposures = 0; 206 data.selGC = XtAllocateGC(data.selector, 0, GCForeground | GCBackground, &gcValues, 0, 0); 207 208 // initial color 209 select_color(&data, *red / 257, *green / 257, *blue / 257); 210 211 // base color 212 set_base_color(&data, (*red)/257, (*green)/257, (*blue)/257); 213 214 // manage dialog 215 XtManageChild(dialog); 216 217 data.parse_textfield_input = 1; 218 219 XtAppContext app = XtWidgetToApplicationContext(dialog); 220 while(!data.end && !XtAppGetExitFlag(app)) { 221 XEvent event; 222 XtAppNextEvent(app, &event); 223 XtDispatchEvent(&event); 224 } 225 226 XtReleaseGC(data.selector, data.gc); 227 XtReleaseGC(data.selector, data.selGC); 228 if(data.image1) XDestroyImage(data.image1); 229 if(data.image2) XDestroyImage(data.image2); 230 if(data.d) XftDrawDestroy(data.d); 231 XtDestroyWidget(dialog); 232 233 if(data.status == 1) { 234 *red = data.selected_color.color.red; 235 *green = data.selected_color.color.green; 236 *blue = data.selected_color.color.blue; 237 } 238 239 return data.status; 240 } 241 242 static XftDraw* create_xft_draw(Widget w) { 243 XWindowAttributes attributes; 244 XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attributes); 245 246 Screen *screen = w->core.screen; 247 Visual *visual = screen->root_visual; 248 for(int i=0;i<screen->ndepths;i++) { 249 Depth d = screen->depths[i]; 250 if(d.depth == w->core.depth) { 251 visual = d.visuals; 252 break; 253 } 254 } 255 256 Display *dp = XtDisplay(w); 257 return XftDrawCreate( 258 dp, 259 XtWindow(w), 260 visual, 261 w->core.colormap); 262 263 } 264 265 /* 266 * get number of shifts for specific color mask 267 */ 268 static int get_shift(unsigned long mask) { 269 if(mask == 0) { 270 return 0; 271 } 272 273 int shift = 0; 274 while((mask & 1L) == 0) { 275 shift++; 276 mask >>= 1; 277 } 278 return shift; 279 } 280 281 /* 282 * get number of bits from color mask 283 */ 284 static int get_mask_len(unsigned long mask) { 285 if(mask == 0) { 286 return 0; 287 } 288 289 while((mask & 1L) == 0) { 290 mask >>= 1; 291 } 292 int len = 0; 293 while((mask & 1L) == 1) { 294 len++; 295 mask >>= 1; 296 } 297 return len; 298 } 299 300 static Visual* get_visual(Screen *screen, int depth) { 301 // get the correct visual for current screen/depth 302 Visual *visual = NULL; 303 for(int i=0;i<screen->ndepths;i++) { 304 Depth d = screen->depths[i]; 305 if(d.depth == depth) { 306 for(int v=0;v<d.nvisuals;v++) { 307 Visual *vs = &d.visuals[v]; 308 if(get_mask_len(vs->red_mask) == 8) { 309 visual = vs; 310 break; 311 } 312 } 313 } 314 } 315 return visual; 316 } 317 318 #define IMG1_WIDTH 20 319 #define IMG1_X_OFFSET 20 320 #define IMG1_Y_OFFSET 10 321 #define IMG2_X_OFFSET 10 322 #define IMG2_Y_OFFSET 10 323 324 static void init_pix1(cgData *data, Widget w) { 325 Display *dp = XtDisplay(w); 326 Dimension width = IMG1_WIDTH; 327 Dimension height = w->core.height - IMG1_Y_OFFSET*2; 328 329 if(data->image1) { 330 if(height == data->img2_height) return; 331 XDestroyImage(data->image1); 332 } 333 334 float height6 = ((float)height)/6.f; 335 float step = ((float)255.f)/height6; 336 337 Visual *visual = get_visual(w->core.screen, w->core.depth); 338 if(!visual) { 339 return; 340 } 341 342 //get number of shifts required for each color bit mask 343 int red_shift = get_shift(visual->red_mask); 344 int green_shift = get_shift(visual->green_mask); 345 int blue_shift = get_shift(visual->blue_mask); 346 347 uint32_t *imgdata = malloc(width * height * 4); // will be freed by XDestroyImage 348 uint32_t pixel_init = UINT32_MAX ^ (visual->red_mask ^ visual->green_mask ^ visual->blue_mask); 349 350 351 int m = 0; 352 float r = 0xFF; 353 float g = 0; 354 float b = 0; 355 for(int i=0;i<height;i++) { 356 if(r < 0) r = 0; 357 if(r > 255) r = 255; 358 if(g < 0) g = 0; 359 if(g > 255) g = 255; 360 if(b < 0) b = 0; 361 if(b > 255) b = 255; 362 363 uint8_t red = r; 364 uint8_t green = g; 365 uint8_t blue = b; 366 367 switch(m) { 368 case 0: { 369 g += step; 370 if(g >= 255) m++; 371 break; 372 } 373 case 1: { 374 r -= step; 375 if(r <= 0) m++; 376 break; 377 } 378 case 2: { 379 b += step; 380 if(b >= 255) m++; 381 break; 382 } 383 case 3: { 384 g -= step; 385 if(g <= 0) m++; 386 break; 387 } 388 case 4: { 389 r += step; 390 if(r >= 255) m++; 391 break; 392 } 393 case 5: { 394 b -= step; 395 if(b <= 0) m = 0; 396 break; 397 } 398 } 399 400 uint32_t out = pixel_init; 401 out ^= (red << red_shift); 402 out ^= (green << green_shift); 403 out ^= (blue << blue_shift); 404 405 for(int j=0;j<width;j++) { 406 imgdata[i*width + j] = out; 407 } 408 } 409 410 data->image1 = XCreateImage(dp, visual, w->core.depth, ZPixmap, 0, (char*)imgdata, width, height, 32, 0); 411 412 data->img1_width = width; 413 data->img1_height = height; 414 } 415 416 static void init_pix2(cgData *data, Widget w) { 417 Display *dp = XtDisplay(w); 418 Dimension width = w->core.width - IMG1_X_OFFSET - IMG1_WIDTH - 2*IMG2_X_OFFSET; 419 Dimension height = w->core.height - 2*IMG2_Y_OFFSET; 420 421 if(data->image2) { 422 if(width == data->img2_width && height == data->img2_height) return; 423 XDestroyImage(data->image2); 424 data->has_selection = 0; 425 } 426 427 if(!data->has_selection) { 428 data->img2_select_x = (float)width * data->value - 1; 429 data->img2_select_y = height - ((float)height * data->saturation); 430 data->has_selection = 1; 431 } 432 433 Visual *visual = get_visual(w->core.screen, w->core.depth); 434 if(!visual) { 435 return; 436 } 437 438 //get number of shifts required for each color bit mask 439 int red_shift = get_shift(visual->red_mask); 440 int green_shift = get_shift(visual->green_mask); 441 int blue_shift = get_shift(visual->blue_mask); 442 443 uint32_t *imgdata = malloc(width * height * 4); 444 uint32_t pixel_init = UINT32_MAX ^ (visual->red_mask ^ visual->green_mask ^ visual->blue_mask); 445 446 float y_base_r = data->base_red; 447 float y_base_g = data->base_green; 448 float y_base_b = data->base_blue; 449 450 float y_step_r = (255-data->base_red) / (float)(height-2*IMG2_Y_OFFSET); 451 float y_step_g = (255-data->base_green) / (float)(height-2*IMG2_Y_OFFSET); 452 float y_step_b = (255-data->base_blue) / (float)(height-2*IMG2_Y_OFFSET); 453 454 for(int y=0;y<height;y++) { 455 float x_step_r = y_base_r / (float)width; 456 float x_step_g = y_base_g / (float)width; 457 float x_step_b = y_base_b / (float)width; 458 459 float r = y_base_r; 460 float g = y_base_g; 461 float b = y_base_b; 462 463 y_base_r += y_step_r; 464 y_base_g += y_step_g; 465 y_base_b += y_step_b; 466 if(y_base_r >= 255) y_base_r = 255; 467 if(y_base_g >= 255) y_base_g = 255; 468 if(y_base_b >= 255) y_base_b = 255; 469 470 for(int x=width-1;x>=0;x--) { 471 if(r < 0) r = 0; 472 if(g < 0) g = 0; 473 if(b < 0) b = 0; 474 475 uint8_t red = r; 476 uint8_t green = g; 477 uint8_t blue = b; 478 479 r -= x_step_r; 480 g -= x_step_g; 481 b -= x_step_b; 482 483 uint32_t out = pixel_init; 484 out ^= (red << red_shift); 485 out ^= (green << green_shift); 486 out ^= (blue << blue_shift); 487 imgdata[y*width + x] = out; 488 } 489 } 490 491 data->image2 = XCreateImage(dp, visual, w->core.depth, ZPixmap, 0, (char*)imgdata, width, height, 32, 0); 492 493 data->img2_width = width; 494 data->img2_height = height; 495 } 496 497 static void draw_img2(Display *dp, Window win, cgData *data) { 498 int img2_x = IMG1_X_OFFSET + data->img1_width + IMG2_X_OFFSET; 499 int img2_y = IMG2_Y_OFFSET; 500 XPutImage(dp, win, data->gc, data->image2, 0, 0, img2_x, img2_y, data->img2_width, data->img2_height); 501 if(data->has_selection) { 502 XDrawLine(dp, win, data->selGC, 503 img2_x, img2_y + data->img2_select_y, 504 img2_x + data->img2_width - 1, img2_y + data->img2_select_y); 505 XDrawLine(dp, win, data->selGC, 506 img2_x + data->img2_select_x, img2_y, 507 img2_x + data->img2_select_x, img2_y + data->img2_height - 1); 508 } 509 } 510 511 #define HUE_INDICATOR_SIZE 9 512 513 static void selector_expose(Widget w, XtPointer u, XtPointer c) { 514 cgData *data = u; 515 Dimension width = w->core.width; 516 Dimension height = w->core.height; 517 Display *dp = XtDisplay(w); 518 Window win = XtWindow(w); 519 520 init_pix1(data, w); 521 init_pix2(data, w); 522 523 if(data->base_sel_y < 0) { 524 float img1h = data->img1_height; 525 float deg2pix = img1h / 360.f; 526 data->base_sel_y = data->hue * deg2pix; 527 } 528 529 // clear all 530 531 // top 532 XClearArea(XtDisplay(w), XtWindow(w), 1, 1, width-2, IMG1_Y_OFFSET-1, False); 533 // bottom 534 XClearArea(XtDisplay(w), XtWindow(w), 535 1, IMG1_Y_OFFSET + data->img1_height, 536 width-2, height - IMG1_Y_OFFSET - data->img1_height - 2, False); 537 // left 538 XClearArea(XtDisplay(w), XtWindow(w), 1, 1, IMG1_X_OFFSET-1, height-2, False); 539 540 XPoint indicator[4]; 541 indicator[0].x = IMG1_X_OFFSET-1; 542 indicator[0].y = IMG1_Y_OFFSET + data->base_sel_y; 543 544 indicator[1].x = IMG1_X_OFFSET-HUE_INDICATOR_SIZE - 1; 545 indicator[1].y = IMG1_Y_OFFSET + data->base_sel_y - HUE_INDICATOR_SIZE; 546 547 indicator[2].x = IMG1_X_OFFSET-HUE_INDICATOR_SIZE - 1; 548 indicator[2].y = IMG1_Y_OFFSET + data->base_sel_y + HUE_INDICATOR_SIZE; 549 550 indicator[3].x = IMG1_X_OFFSET-1; 551 indicator[3].y = IMG1_Y_OFFSET + data->base_sel_y; 552 553 554 XFillPolygon(dp, win, data->gc, indicator, 4, Convex, CoordModeOrigin); 555 556 // right 557 XClearArea(XtDisplay(w), XtWindow(w), 558 IMG1_X_OFFSET + data->img1_width + IMG2_X_OFFSET + data->img2_width, 1, 559 width - 1 - IMG1_X_OFFSET - data->img1_width - IMG2_X_OFFSET - data->img2_width, height-2, False); 560 // middle 561 XClearArea(XtDisplay(w), XtWindow(w), 562 IMG1_X_OFFSET + data->img1_width, 1, 563 IMG2_X_OFFSET, height-2, False); 564 565 566 if(data->image1) { 567 XPutImage(dp, win, data->gc, data->image1, 0, 0, IMG1_X_OFFSET, IMG1_Y_OFFSET, data->img1_width, data->img1_height); 568 } 569 if(data->image2) { 570 draw_img2(dp, win, data); 571 } 572 573 XDrawRectangle(dp, win, DefaultGC(dp, 0), 0, 0, width-1, height-1); 574 575 } 576 577 #define SELECT_MARGIN 5 578 static int translate_img1(cgData *data, int x, int y, int *trans_x, int *trans_y) { 579 if(x < IMG1_X_OFFSET - HUE_INDICATOR_SIZE) return 0; 580 if(y < IMG1_Y_OFFSET) return 0; 581 582 int tx = x - IMG1_X_OFFSET; 583 int ty = y - IMG1_Y_OFFSET; 584 585 if(tx < data->img1_width && ty < data->img1_height) { 586 *trans_x = tx; 587 *trans_y = ty; 588 return 1; 589 } 590 return 0; 591 } 592 593 static int translate_img2(cgData *data, int x, int y, int *trans_x, int *trans_y) { 594 if(x < IMG1_X_OFFSET + IMG2_X_OFFSET + data->img1_width - SELECT_MARGIN) return 0; 595 if(y < IMG1_Y_OFFSET) return 0; 596 597 int tx = x - (IMG1_X_OFFSET + IMG2_X_OFFSET + data->img1_width); 598 int ty = y - IMG2_Y_OFFSET; 599 600 if(tx < data->img2_width + SELECT_MARGIN && ty < data->img2_height + SELECT_MARGIN) { 601 if(tx < 0) tx = 0; 602 if(ty < 0) ty = 0; 603 if(tx >= data->img2_width) tx = data->img2_width-1; 604 if(ty >= data->img2_height) ty = data->img2_height-1; 605 *trans_x = tx; 606 *trans_y = ty; 607 return 1; 608 } 609 return 0; 610 } 611 612 static void selector_input(Widget w, XtPointer u, XtPointer c) { 613 XmDrawingAreaCallbackStruct *cb = (XmDrawingAreaCallbackStruct*)c; 614 cgData *data = u; 615 Display *dp = XtDisplay(w); 616 Window win = XtWindow(w); 617 618 Dimension x, y; 619 if(cb->event->type == MotionNotify) { 620 x = cb->event->xmotion.x; 621 y = cb->event->xmotion.y; 622 } else { 623 x = cb->event->xbutton.x; 624 y = cb->event->xbutton.y; 625 } 626 627 int tx, ty; 628 if(translate_img1(data, x, y, &tx, &ty)) { 629 uint32_t color = XGetPixel(data->image1, tx, ty); 630 int red_shift = get_shift(data->image1->red_mask); 631 int green_shift = get_shift(data->image1->green_mask); 632 int blue_shift = get_shift(data->image1->blue_mask); 633 634 data->base_red = (color & data->image1->red_mask) >> red_shift; 635 data->base_green = (color & data->image1->green_mask) >> green_shift; 636 data->base_blue = (color & data->image1->blue_mask) >> blue_shift; 637 638 data->base_sel_y = ty; 639 640 XDestroyImage(data->image2); 641 data->image2 = NULL; 642 init_pix2(data, w); 643 644 data->has_selection = 0; 645 selector_expose(w, u, NULL); 646 } 647 if(translate_img2(data, x, y, &tx, &ty)) { 648 uint32_t color = XGetPixel(data->image2, tx, ty); 649 int red_shift = get_shift(data->image2->red_mask); 650 int green_shift = get_shift(data->image2->green_mask); 651 int blue_shift = get_shift(data->image2->blue_mask); 652 653 data->img2_select_x = tx; 654 data->img2_select_y = ty; 655 data->has_selection = 1; 656 657 select_color(data, 658 (color & data->image2->red_mask) >> red_shift, 659 (color & data->image2->green_mask) >> green_shift, 660 (color & data->image2->blue_mask) >> blue_shift); 661 662 preview_color(data->preview, data, NULL); 663 664 draw_img2(dp, win, data); 665 } 666 } 667 668 static float max3(float a, float b, float c) { 669 if(a > b) { 670 return a > c ? a : c; 671 } else { 672 return b > c ? b : c; 673 } 674 } 675 676 static float min3(float a, float b, float c) { 677 if(a < b) { 678 return a < c ? a : c; 679 } else { 680 return b < c ? b : c; 681 } 682 } 683 684 static void rgbToHsv(int red, int green, int blue, float *hue, float *saturation, float *value) { 685 float r = red; 686 float g = green; 687 float b = blue; 688 r /= 255; 689 g /= 255; 690 b /= 255; 691 692 float rgbMax = max3(r, g, b); 693 float rgbMin = min3(r, g, b); 694 695 float mmDiff = rgbMax - rgbMin; 696 697 float h; 698 if(rgbMax == rgbMin) { 699 h = 0; 700 } else if(rgbMax == r) { 701 h = 60.f * (g-b)/mmDiff; 702 } else if(rgbMax == g) { 703 h = 60.f * (2.f + (b-r)/mmDiff); 704 } else { 705 h = 60.f * (4.f + (r-g)/mmDiff); 706 } 707 if(h < 0) h += 360; 708 709 float s = rgbMax == 0 ? 0 : mmDiff/rgbMax; 710 float v = rgbMax; 711 712 *hue = h; 713 *saturation = s; 714 *value = v; 715 } 716 717 static void hsvToRgb(float hue, float saturation, float value, int *red, int *green, int *blue) { 718 int hi = hue/60; 719 float f = (hue/60) - hi; 720 float p = value * (1-saturation); 721 float q = value * (1-saturation * f); 722 float t = value * (1-saturation * (1-f)); 723 724 float r, g, b; 725 switch(hi) { 726 case 1: { 727 r = q; 728 g = value; 729 b = p; 730 break; 731 } 732 case 2: { 733 r = p; 734 g = value; 735 b = t; 736 break; 737 } 738 case 3: { 739 r = p; 740 g = q; 741 b = value; 742 break; 743 } 744 case 4: { 745 r = t; 746 g = p; 747 b = value; 748 break; 749 } 750 case 5: { 751 r = value; 752 g = p; 753 b = q; 754 break; 755 } 756 default: { 757 r = value; 758 g = t; 759 b = p; 760 } 761 } 762 763 *red = r * 255; 764 *green = g * 255; 765 *blue = b * 255; 766 } 767 768 static void set_base_color(cgData *data, int r, int g, int b) { 769 float h, s, v; 770 rgbToHsv(r, g, b, &h, &s, &v); 771 772 int red, green, blue; 773 hsvToRgb(h, 1, 1, &red, &green, &blue); 774 775 data->base_red = red; 776 data->base_green = green; 777 data->base_blue = blue; 778 data->hue = h; 779 data->saturation = s; 780 data->value = v; 781 data->base_sel_y = -1; 782 } 783 784 static void select_color(cgData *data, int r, int g, int b) { 785 XftColor color; 786 color.pixel = 0; 787 color.color.alpha = 0xFFFF; 788 color.color.red = r * 257; 789 color.color.green = g * 257; 790 color.color.blue = b * 257; 791 data->selected_color = color; 792 793 char buf[8]; 794 snprintf(buf, 8, "#%02x%02x%02x", r, g, b); 795 data->parse_textfield_input = 0; 796 XmTextFieldSetString(data->textfield, buf); 797 data->parse_textfield_input = 1; 798 } 799 800 static void preview_color(Widget w, XtPointer u, XtPointer c) { 801 cgData *data = u; 802 if(!data->d) { 803 data->d = create_xft_draw(w); 804 } 805 806 XftDrawRect(data->d, &data->selected_color, 0, 0, w->core.width, w->core.height); 807 XDrawRectangle(XtDisplay(w), XtWindow(w), data->gc, 0, 0, w->core.width-1, w->core.height-1); 808 } 809 810 static void textfield_changed(Widget w, XtPointer u, XtPointer c) { 811 cgData *data = u; 812 if(!data->parse_textfield_input) return; 813 814 char *colorStr = XmTextFieldGetString(w); 815 if(strlen(colorStr) != 7) { 816 XtFree(colorStr); 817 return; 818 } 819 820 Display *dp = XtDisplay(w); 821 Colormap cmap = w->core.colormap; 822 XColor color; 823 if(XParseColor(dp, cmap, colorStr, &color)) { 824 XftColor c; 825 c.pixel = 0; 826 c.color.alpha = 0xFFFF; 827 c.color.red = color.red; 828 c.color.green = color.green; 829 c.color.blue = color.blue; 830 data->selected_color = c; 831 832 set_base_color(data, color.red/257, color.green/257, color.blue/257); 833 834 XDestroyImage(data->image2); 835 data->image2 = NULL; 836 init_pix2(data, w); 837 838 data->has_selection = 0; 839 840 selector_expose(data->selector, data, NULL); 841 preview_color(data->preview, data, NULL); 842 } 843 XtFree(colorStr); 844 } 845 846 static void okCB(Widget w, XtPointer u, XtPointer c) { 847 cgData *data = u; 848 data->status = 1; 849 data->end = 1; 850 } 851 852 static void cancelCB(Widget w, XtPointer u, XtPointer c) { 853 cgData *data = u; 854 data->status = 0; 855 data->end = 1; 856 } 857