graphics.c revision 894e0ac8
1/* $XTermId: graphics.c,v 1.43 2014/05/28 22:27:07 tom Exp $ */ 2 3/* 4 * Copyright 2013,2014 by Ross Combs 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 */ 32 33#include <xterm.h> 34 35#include <stdio.h> 36#include <ctype.h> 37#include <stdlib.h> 38 39#include <data.h> 40#include <ptyx.h> 41 42#include <assert.h> 43#include <graphics.h> 44 45#undef DUMP_BITMAP 46#undef DUMP_COLORS 47#undef DEBUG_PALETTE 48#undef DEBUG_PIXEL 49#undef DEBUG_REFRESH 50 51/* TODO: 52 * ReGIS: 53 * - shading with text 54 * - polygon filling 55 * - plane write control 56 * - fix interpolated curves to more closely match implementation (identical despite direction and starting point) 57 * - text 58 * - input and output cursors 59 * - mouse input 60 * - stacks 61 * - investigate second graphic page for ReGIS -- does it also apply to text and sixel graphics? are the contents preserved? 62 * - font upload, italics, and other text attributes 63 * - enter/leave during a command 64 * - command display mode 65 * - scrolling 66 * - custom coordinate systems 67 * - scaling/re-rasterization to fit screen 68 * - macros 69 * sixel: 70 * - fix problem where new_row < 0 during sixel parsing (see FIXME) 71 * VT55/VT105 waveform graphics 72 * - everything 73 * common: 74 * - handle light/dark screen modes (CSI?5[hl]) 75 * - update text fg/bg color which overlaps images 76 * - erase graphic when erasing screen 77 * - handle graphic updates in scroll regions 78 * - handle rectangular area copies (verify they work with graphics) 79 * - maintain ordered list/array instead of qsort() 80 * - erase text under graphic if bg not transparent to avoid flickering (or not: bad if the font changes or window resizes) 81 * - erase graphics under graphic if same origin and bg not transparent to avoid flickering 82 * - erase scrolled portions of all graphics on alt buffer 83 * - delete graphic if scrolled past end of scrollback 84 * - delete graphic if all pixels are transparent/erased 85 * - dynamic memory allocation of graphics buffers, add configurable limits 86 * - auto convert color graphics in VT330 mode 87 * - posturize requested colors to match hardware palettes (e.g. four possible shades on VT240) 88 * - color register report/restore 89 * escape sequences: 90 * - way to query font size without "window ops" (or make "window ops" permissions more fine grained) 91 * - way to query and/or set the maximum number of color registers 92 * - way to query and set the number of graphics pages 93 * ReGIS extensions: 94 * - gradients 95 * - line width (RLogin has this and it is mentioned in docs for the DEC ReGIS to Postscript converter) 96 * - F option for screen command (mentioned in docs for the DEC ReGIS to Postscript converter) 97 * - transparency 98 * - background color as stackable write control 99 * - RGB triplets 100 * - true color (virtual color registers created upon lookup) 101 * - anti-aliasing 102 */ 103 104/* font sizes: 105 * VT510: 106 * 80 Columns 132 Columns Maximum Number of Lines 107 * 10 x 16 6 x 16 26 lines + keyboard indicator line 108 * 10 x 13 6 x 13 26 lines + keyboard indicator line 109 * 10 x 10 6 x 10 42 lines + keyboard indicator line 110 * 10 x 8 6 x 8 53 lines + keyboard indicator line 111*/ 112 113#define FOR_EACH_SLOT(ii) for (ii = 0U; ii < MAX_GRAPHICS; ii++) 114 115static ColorRegister *shared_color_registers; 116static Graphic *displayed_graphics[MAX_GRAPHICS]; 117static unsigned next_graphic_id = 0U; 118 119static ColorRegister * 120allocRegisters(void) 121{ 122 return TypeCallocN(ColorRegister, MAX_COLOR_REGISTERS); 123} 124 125static Graphic * 126freeGraphic(Graphic *obj) 127{ 128 if (obj) { 129 if (obj->pixels) 130 free(obj->pixels); 131 if (obj->private_color_registers) 132 free(obj->private_color_registers); 133 free(obj); 134 } 135 return NULL; 136} 137 138static Graphic * 139allocGraphic(void) 140{ 141 Graphic *result = TypeCalloc(Graphic); 142 if (result) { 143 if (!(result->pixels = TypeCallocN(RegisterNum, MAX_PIXELS))) { 144 result = freeGraphic(result); 145 } else if (!(result->private_color_registers = allocRegisters())) { 146 result = freeGraphic(result); 147 } 148 } 149 return result; 150} 151 152static Graphic * 153getActiveSlot(unsigned n) 154{ 155 if (n < MAX_GRAPHICS && 156 displayed_graphics[n] && 157 displayed_graphics[n]->valid) { 158 return displayed_graphics[n]; 159 } 160 return NULL; 161} 162 163static Graphic * 164getInactiveSlot(unsigned n) 165{ 166 if (n < MAX_GRAPHICS && 167 (!displayed_graphics[n] || 168 !displayed_graphics[n]->valid)) { 169 if (!displayed_graphics[n]) { 170 displayed_graphics[n] = allocGraphic(); 171 } 172 return displayed_graphics[n]; 173 } 174 return NULL; 175} 176 177static ColorRegister * 178getSharedRegisters(void) 179{ 180 if (!shared_color_registers) 181 shared_color_registers = allocRegisters(); 182 return shared_color_registers; 183} 184 185static void 186deactivateSlot(unsigned n) 187{ 188 if (n < MAX_GRAPHICS) { 189 displayed_graphics[n] = freeGraphic(displayed_graphics[n]); 190 } 191} 192 193extern RegisterNum 194read_pixel(Graphic *graphic, int x, int y) 195{ 196 if (x < 0 && x >= graphic->actual_width && 197 y < 0 && y >= graphic->actual_height) { 198 return COLOR_HOLE; 199 } 200 201 return graphic->pixels[y * graphic->max_width + x]; 202} 203 204void 205draw_solid_pixel(Graphic *graphic, int x, int y, unsigned color) 206{ 207 assert(color <= MAX_COLOR_REGISTERS); 208 209#ifdef DEBUG_PIXEL 210 TRACE(("drawing pixel at %d,%d color=%hu (hole=%hu, [%d,%d,%d])\n", 211 x, 212 y, 213 color, 214 COLOR_HOLE, 215 ((color != COLOR_HOLE) 216 ? (unsigned) graphic->color_registers[color].r : 0U), 217 ((color != COLOR_HOLE) 218 ? (unsigned) graphic->color_registers[color].g : 0U), 219 ((color != COLOR_HOLE) 220 ? (unsigned) graphic->color_registers[color].b : 0U))); 221#endif 222 if (x >= 0 && x < graphic->actual_width && 223 y >= 0 && y < graphic->actual_height) { 224 graphic->pixels[y * graphic->max_width + x] = (RegisterNum) color; 225 if (color < MAX_COLOR_REGISTERS) 226 graphic->color_registers_used[color] = 1; 227 } else { 228 TRACE(("pixel %d,%d out of bounds\n", x, y)); 229 } 230} 231 232void 233draw_solid_rectangle(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 234{ 235 int x, y; 236 int tmp; 237 238 assert(color <= MAX_COLOR_REGISTERS); 239 240 if (x1 > x2) { 241 EXCHANGE(x1, x2, tmp); 242 } 243 if (y1 > y2) { 244 EXCHANGE(y1, y2, tmp); 245 } 246 247 for (y = y1; y <= y2; y++) 248 for (x = x1; x < x2; x++) 249 draw_solid_pixel(graphic, x, y, color); 250} 251 252void 253draw_solid_line(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 254{ 255 int x, y; 256 int dx, dy; 257 int dir, diff; 258 259 assert(color <= MAX_COLOR_REGISTERS); 260 261 dx = abs(x1 - x2); 262 dy = abs(y1 - y2); 263 264 if (dx > dy) { 265 if (x1 > x2) { 266 int tmp; 267 EXCHANGE(x1, x2, tmp); 268 EXCHANGE(y1, y2, tmp); 269 } 270 if (y1 < y2) 271 dir = 1; 272 else if (y1 > y2) 273 dir = -1; 274 else 275 dir = 0; 276 277 diff = 0; 278 y = y1; 279 for (x = x1; x <= x2; x++) { 280 if (diff >= dx) { 281 diff -= dx; 282 y += dir; 283 } 284 diff += dy; 285 draw_solid_pixel(graphic, x, y, color); 286 } 287 } else { 288 if (y1 > y2) { 289 int tmp; 290 EXCHANGE(x1, x2, tmp); 291 EXCHANGE(y1, y2, tmp); 292 } 293 if (x1 < x2) 294 dir = 1; 295 else if (x1 > x2) 296 dir = -1; 297 else 298 dir = 0; 299 300 diff = 0; 301 x = x1; 302 for (y = y1; y <= y2; y++) { 303 if (diff >= dy) { 304 diff -= dy; 305 x += dir; 306 } 307 diff += dx; 308 draw_solid_pixel(graphic, x, y, color); 309 } 310 } 311} 312 313static void 314set_color_register(ColorRegister *color_registers, 315 unsigned color, 316 int r, 317 int g, 318 int b) 319{ 320 ColorRegister *reg = &color_registers[color]; 321 reg->r = (short) r; 322 reg->g = (short) g; 323 reg->b = (short) b; 324 reg->allocated = 0; 325} 326 327/* Graphics which don't use private colors will act as if they are using a 328 * device-wide color palette. 329 */ 330static void 331set_shared_color_register(unsigned color, int r, int g, int b) 332{ 333 Graphic *graphic; 334 unsigned ii; 335 336 assert(color < MAX_COLOR_REGISTERS); 337 338 set_color_register(getSharedRegisters(), color, r, g, b); 339 340 FOR_EACH_SLOT(ii) { 341 if (!(graphic = getActiveSlot(ii))) 342 continue; 343 if (graphic->private_colors) 344 continue; 345 346 if (graphic->color_registers_used[ii]) { 347 graphic->dirty = 1; 348 } 349 } 350} 351 352void 353update_color_register(Graphic *graphic, 354 unsigned color, 355 int r, 356 int g, 357 int b) 358{ 359 assert(color < MAX_COLOR_REGISTERS); 360 361 if (graphic->private_colors) { 362 set_color_register(graphic->private_color_registers, 363 color, r, g, b); 364 if (graphic->color_registers_used[color]) { 365 graphic->dirty = 1; 366 } 367 graphic->color_registers_used[color] = 1; 368 } else { 369 set_shared_color_register(color, r, g, b); 370 } 371} 372 373#define SQUARE(X) ( (X) * (X) ) 374 375RegisterNum 376find_color_register(ColorRegister const *color_registers, int r, int g, int b) 377{ 378 unsigned i; 379 unsigned d; 380 unsigned closest_index; 381 unsigned closest_distance; 382 383 /* I have no idea what algorithm DEC used for this. 384 * The documentation warns that it is unpredictable, especially with values 385 * far away from any allocated color so it is probably a very simple 386 * hueristic rather than something fancy like finding the minimum distance 387 * in a linear perceptive color space. 388 */ 389 closest_index = MAX_COLOR_REGISTERS; 390 closest_distance = 0U; 391 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 392 d = (unsigned) (SQUARE(2 * (color_registers[i].r - r)) + 393 SQUARE(3 * (color_registers[i].g - g)) + 394 SQUARE(1 * (color_registers[i].b - b))); 395 if (closest_index == MAX_COLOR_REGISTERS || d < closest_distance) { 396 closest_index = i; 397 closest_distance = d; 398 } 399 } 400 401 TRACE(("found closest color register to %d,%d,%d: %u (distance %u value %d,%d,%d)\n", 402 r, g, b, 403 closest_index, 404 closest_distance, 405 color_registers[closest_index].r, 406 color_registers[closest_index].g, 407 color_registers[closest_index].b)); 408 return (RegisterNum) closest_index; 409} 410 411static void 412init_color_registers(ColorRegister *color_registers, int terminal_id) 413{ 414 TRACE(("setting initial colors for terminal %d\n", terminal_id)); 415 { 416 unsigned i; 417 418 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 419 set_color_register(color_registers, (RegisterNum) i, 0, 0, 0); 420 } 421 } 422 423 /* 424 * default color registers: 425 * (mono) (color) 426 * VK100/GIGI (fixed) 427 * VT125: 428 * 0: 0% 0% 429 * 1: 33% blue 430 * 2: 66% red 431 * 3: 100% green 432 * VT240: 433 * 0: 0% 0% 434 * 1: 33% blue 435 * 2: 66% red 436 * 3: 100% green 437 * VT241: 438 * 0: 0% 0% 439 * 1: 33% blue 440 * 2: 66% red 441 * 3: 100% green 442 * VT330: 443 * 0: 0% 0% (bg for light on dark mode) 444 * 1: 33% blue (red?) 445 * 2: 66% red (green?) 446 * 3: 100% green (yellow?) (fg for light on dark mode) 447 * VT340: 448 * 0: 0% 0% (bg for light on dark mode) 449 * 1: 14% blue 450 * 2: 29% red 451 * 3: 43% green 452 * 4: 57% magenta 453 * 5: 71% cyan 454 * 6: 86% yellow 455 * 7: 100% 50% (fg for light on dark mode) 456 * 8: 0% 25% 457 * 9: 14% gray-blue 458 * 10: 29% gray-red 459 * 11: 43% gray-green 460 * 12: 57% gray-magenta 461 * 13: 71% gray-cyan 462 * 14: 86% gray-yellow 463 * 15: 100% 75% ("white") 464 * VT382: 465 * ? (FIXME: B&W only?) 466 * dxterm: 467 * ? 468 */ 469 switch (terminal_id) { 470 case 125: 471 case 241: 472 set_color_register(color_registers, 0, 0, 0, 0); 473 set_color_register(color_registers, 1, 0, 0, 100); 474 set_color_register(color_registers, 2, 0, 100, 0); 475 set_color_register(color_registers, 3, 100, 0, 0); 476 break; 477 case 240: 478 case 330: 479 set_color_register(color_registers, 0, 0, 0, 0); 480 set_color_register(color_registers, 1, 33, 33, 33); 481 set_color_register(color_registers, 2, 66, 66, 66); 482 set_color_register(color_registers, 3, 100, 100, 100); 483 break; 484 case 340: 485 default: 486 set_color_register(color_registers, 0, 0, 0, 0); 487 set_color_register(color_registers, 1, 20, 20, 80); 488 set_color_register(color_registers, 2, 80, 13, 13); 489 set_color_register(color_registers, 3, 20, 80, 20); 490 set_color_register(color_registers, 4, 80, 20, 80); 491 set_color_register(color_registers, 5, 20, 80, 80); 492 set_color_register(color_registers, 6, 80, 80, 20); 493 set_color_register(color_registers, 7, 53, 53, 53); 494 set_color_register(color_registers, 8, 26, 26, 26); 495 set_color_register(color_registers, 9, 33, 33, 60); 496 set_color_register(color_registers, 10, 60, 26, 26); 497 set_color_register(color_registers, 11, 33, 60, 33); 498 set_color_register(color_registers, 12, 60, 33, 60); 499 set_color_register(color_registers, 13, 33, 60, 60); 500 set_color_register(color_registers, 14, 60, 60, 33); 501 set_color_register(color_registers, 15, 80, 80, 80); 502 break; 503 case 382: /* FIXME: verify */ 504 set_color_register(color_registers, 0, 0, 0, 0); 505 set_color_register(color_registers, 1, 100, 100, 100); 506 break; 507 } 508 509#ifdef DEBUG_PALETTE 510 { 511 unsigned i; 512 513 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 514 printf("initial value for register %03u: %d,%d,%d\n", 515 i, 516 color_registers[i].r, 517 color_registers[i].g, 518 color_registers[i].b); 519 } 520 } 521#endif 522} 523 524unsigned 525get_color_register_count(TScreen const *screen) 526{ 527 unsigned num_color_registers; 528 529 if (screen->numcolorregisters >= 0) { 530 num_color_registers = (unsigned) screen->numcolorregisters; 531 } else { 532 num_color_registers = 0U; 533 } 534 535 if (num_color_registers > 1U) { 536 if (num_color_registers > MAX_COLOR_REGISTERS) 537 return MAX_COLOR_REGISTERS; 538 return num_color_registers; 539 } 540 541 /* 542 * color capabilities: 543 * VK100/GIGI 1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta) 544 * VT125 2 planes (4 registers) colorspace is (64?) (color), ? (grayscale) 545 * VT240 2 planes (4 registers) colorspace is 4 shades (grayscale) 546 * VT241 2 planes (4 registers) colorspace is ? (color), ? shades (grayscale) 547 * VT330 2 planes (4 registers) colorspace is 4 shades (grayscale) 548 * VT340 4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale) 549 * VT382 1 plane (two fixed colors: black and white) FIXME: verify 550 * dxterm ? 551 */ 552 switch (screen->terminal_id) { 553 case 125: 554 return 4U; 555 case 240: 556 return 4U; 557 case 241: 558 return 4U; 559 case 330: 560 return 4U; 561 case 340: 562 return 16U; 563 case 382: 564 return 2U; 565 default: 566 /* unknown graphics model -- might as well be generous */ 567 return MAX_COLOR_REGISTERS; 568 } 569} 570 571static void 572init_graphic(Graphic *graphic, 573 unsigned type, 574 int terminal_id, 575 int charrow, 576 int charcol, 577 unsigned num_color_registers, 578 int private_colors) 579{ 580 unsigned i; 581 582 TRACE(("initializing graphic object\n")); 583 584 graphic->dirty = 1; 585 for (i = 0U; i < MAX_PIXELS; i++) 586 graphic->pixels[i] = COLOR_HOLE; 587 memset(graphic->color_registers_used, 0, sizeof(graphic->color_registers_used)); 588 589 /* 590 * text and graphics interactions: 591 * VK100/GIGI text writes on top of graphics buffer, color attribute shared with text 592 * VT240,VT241,VT330,VT340 text writes on top of graphics buffer 593 * VT382 text writes on top of graphics buffer FIXME: verify 594 * VT125 graphics buffer overlaid on top of text in B&W display, text not present in color display 595 */ 596 597 /* 598 * dimensions (ReGIS logical, physical): 599 * VK100/GIGI 768x4?? 768x240(status?) 600 * VT125 768x460 768x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 601 * VT240 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 602 * VT241 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 603 * VT330 800x480 800x480(+?status) 604 * VT340 800x480 800x480(+?status) 605 * VT382 960x750 sixel only 606 * dxterm ?x? ?x? variable? 607 */ 608 graphic->max_width = BUFFER_WIDTH; 609 graphic->max_height = BUFFER_HEIGHT; 610 611 graphic->actual_width = 0; 612 graphic->actual_height = 0; 613 614 graphic->pixw = 1; 615 graphic->pixh = 1; 616 617 graphic->valid_registers = num_color_registers; 618 TRACE(("%d color registers\n", graphic->valid_registers)); 619 620 graphic->private_colors = private_colors; 621 if (graphic->private_colors) { 622 TRACE(("using private color registers\n")); 623 init_color_registers(graphic->private_color_registers, terminal_id); 624 graphic->color_registers = graphic->private_color_registers; 625 } else { 626 TRACE(("using shared color registers\n")); 627 graphic->color_registers = getSharedRegisters(); 628 } 629 630 graphic->charrow = charrow; 631 graphic->charcol = charcol; 632 graphic->type = type; 633 graphic->valid = 0; 634} 635 636Graphic * 637get_new_graphic(XtermWidget xw, int charrow, int charcol, unsigned type) 638{ 639 TScreen const *screen = TScreenOf(xw); 640 int bufferid = screen->whichBuf; 641 int terminal_id = screen->terminal_id; 642 Graphic *graphic; 643 unsigned ii; 644 645 FOR_EACH_SLOT(ii) { 646 if ((graphic = getInactiveSlot(ii))) { 647 TRACE(("using fresh graphic index=%u id=%u\n", ii, next_graphic_id)); 648 break; 649 } 650 } 651 652 /* if none are free, recycle the graphic scrolled back the farthest */ 653 if (!graphic) { 654 int min_charrow = 0; 655 Graphic *min_graphic = NULL; 656 657 FOR_EACH_SLOT(ii) { 658 if (!(graphic = getActiveSlot(ii))) 659 continue; 660 if (!min_graphic || graphic->charrow < min_charrow) { 661 min_charrow = graphic->charrow; 662 min_graphic = graphic; 663 } 664 } 665 TRACE(("recycling old graphic index=%u id=%u\n", ii, next_graphic_id)); 666 graphic = min_graphic; 667 } 668 669 if (graphic) { 670 unsigned num_color_registers; 671 num_color_registers = get_color_register_count(screen); 672 graphic->xw = xw; 673 graphic->bufferid = bufferid; 674 graphic->id = next_graphic_id++; 675 init_graphic(graphic, 676 type, 677 terminal_id, 678 charrow, 679 charcol, 680 num_color_registers, 681 screen->privatecolorregisters); 682 } 683 return graphic; 684} 685 686Graphic * 687get_new_or_matching_graphic(XtermWidget xw, 688 int charrow, 689 int charcol, 690 int actual_width, 691 int actual_height, 692 unsigned type) 693{ 694 TScreen const *screen = TScreenOf(xw); 695 int bufferid = screen->whichBuf; 696 Graphic *graphic; 697 unsigned ii; 698 699 FOR_EACH_SLOT(ii) { 700 if ((graphic = getActiveSlot(ii)) && 701 graphic->type == type && 702 graphic->bufferid == bufferid && 703 graphic->charrow == charrow && 704 graphic->charcol == charcol && 705 graphic->actual_width == actual_width && 706 graphic->actual_height == actual_height) { 707 TRACE(("found existing graphic index=%u id=%u\n", ii, graphic->id)); 708 return graphic; 709 } 710 } 711 712 /* if no match get a new graphic */ 713 if ((graphic = get_new_graphic(xw, charrow, charcol, type))) { 714 graphic->actual_width = actual_width; 715 graphic->actual_height = actual_height; 716 } 717 return graphic; 718} 719 720#define ScaleForXColor(s) (unsigned short) ((long)(s) * 65535 / 100) 721 722static Pixel 723color_register_to_xpixel(ColorRegister *reg, XtermWidget xw) 724{ 725 if (!reg->allocated) { 726 XColor def; 727 728 def.red = ScaleForXColor(reg->r); 729 def.green = ScaleForXColor(reg->g); 730 def.blue = ScaleForXColor(reg->b); 731 def.flags = DoRed | DoGreen | DoBlue; 732 if (!allocateBestRGB(xw, &def)) { 733 TRACE(("unable to allocate xcolor for color register\n")); 734 return 0UL; 735 } 736 reg->pix = def.pixel; 737 reg->allocated = 1; 738 } 739 740 /* FIXME: with so many possible colors we need to determine 741 * when to free them to be nice to PseudoColor displays 742 */ 743 return reg->pix; 744} 745 746static void 747refresh_graphic(TScreen const *screen, 748 Graphic const *graphic, 749 int xbase, 750 int ybase, 751 int x, 752 int y, 753 int w, 754 int h) 755{ 756 Display *display = screen->display; 757 Window vwindow = WhichVWin(screen)->window; 758 GC graphics_gc; 759 int r, c; 760 int pw, ph; 761 int rbase, cbase; 762 RegisterNum color; 763 RegisterNum old_fg; 764 XGCValues xgcv; 765 XtGCMask mask; 766 int holes, total; 767 768 TRACE(("refreshing graphic from %d,%d %dx%d (valid=%d, size=%dx%d, scale=%dx%d max=%dx%d) at base=%d,%d\n", 769 x, y, w, h, 770 graphic->valid, 771 graphic->actual_width, 772 graphic->actual_height, 773 graphic->pixw, 774 graphic->pixh, 775 graphic->max_width, 776 graphic->max_height, 777 xbase, ybase)); 778 779 memset(&xgcv, 0, sizeof(xgcv)); 780 xgcv.foreground = 0UL; 781 xgcv.graphics_exposures = False; 782 mask = GCForeground | GCGraphicsExposures; 783 graphics_gc = XCreateGC(display, vwindow, mask, &xgcv); 784 785 pw = graphic->pixw; 786 ph = graphic->pixh; 787 788 TRACE(("refreshed graphic covers 0,0 to %d,%d\n", 789 (graphic->actual_width - 1) * pw + pw - 1, 790 (graphic->actual_height - 1) * ph + ph - 1)); 791 TRACE(("refreshed area covers %d,%d to %d,%d\n", 792 x, y, 793 x + w - 1, 794 y + h - 1)); 795 796 old_fg = COLOR_HOLE; 797 holes = total = 0; 798 rbase = 0; 799 for (r = 0; r < graphic->actual_height; r++) { 800 int rtest = rbase; 801 802 rbase += ph; 803 if (rtest + ph - 1 < y) 804 continue; 805 if (rtest > y + h - 1) 806 continue; 807 808 cbase = 0; 809 for (c = 0; c < graphic->actual_width; c++) { 810 int ctest = cbase; 811 812 cbase += pw; 813 if (ctest + pw - 1 < x) 814 continue; 815 if (ctest > x + w - 1) 816 continue; 817 818 total++; 819 color = graphic->pixels[r * graphic->max_width + c]; 820 if (color == COLOR_HOLE) { 821 holes++; 822 continue; 823 } 824 825 if (color != old_fg) { 826 xgcv.foreground = 827 color_register_to_xpixel(&graphic->color_registers[color], 828 graphic->xw); 829 XChangeGC(display, graphics_gc, mask, &xgcv); 830 old_fg = color; 831 } 832 833 XFillRectangle(display, vwindow, graphics_gc, 834 xbase + ctest, 835 ybase + rtest, 836 (unsigned) pw, 837 (unsigned) ph); 838 } 839 } 840 841#ifdef DEBUG_REFRESH 842 { 843 XColor def; 844 845 def.red = (short) (1.0 * 65535.0); 846 def.green = (short) (0.1 * 65535.0); 847 def.blue = (short) (1.0 * 65535.0); 848 def.flags = DoRed | DoGreen | DoBlue; 849 if (allocateBestRGB(graphic->xw, &def)) { 850 xgcv.foreground = def.pixel; 851 XChangeGC(display, graphics_gc, mask, &xgcv); 852 } 853 XFillRectangle(display, vwindow, graphics_gc, 854 xbase + 0, 855 ybase + 0, 856 (unsigned) pw, (unsigned) ph); 857 XFillRectangle(display, vwindow, graphics_gc, 858 xbase + (graphic->actual_width - 1) * pw, 859 ybase + (graphic->actual_height - 1) * ph, 860 (unsigned) pw, (unsigned) ph); 861 862 def.red = (unsigned short) ((1.0 - 0.1 * (rand() / (double) 863 RAND_MAX) * 65535.0)); 864 def.green = (unsigned short) ((0.7 + 0.2 * (rand() / (double) 865 RAND_MAX)) * 65535.0); 866 def.blue = (unsigned short) ((0.1 + 0.1 * (rand() / (double) 867 RAND_MAX)) * 65535.0); 868 def.flags = DoRed | DoGreen | DoBlue; 869 if (allocateBestRGB(graphic->xw, &def)) { 870 xgcv.foreground = def.pixel; 871 XChangeGC(display, graphics_gc, mask, &xgcv); 872 } 873 XDrawLine(display, vwindow, graphics_gc, 874 xbase + x + 0, ybase + y + 0, 875 xbase + x + w - 1, ybase + y + 0); 876 XDrawLine(display, vwindow, graphics_gc, 877 xbase + x + w - 1, ybase + y + 0, 878 xbase + x + 0, ybase + y + h - 1); 879 XDrawLine(display, vwindow, graphics_gc, 880 xbase + x + 0, ybase + y + h - 1, 881 xbase + x + w - 1, ybase + y + h - 1); 882 XDrawLine(display, vwindow, graphics_gc, 883 xbase + x + w - 1, ybase + y + h - 1, 884 xbase + x + 0, ybase + y + 0); 885 } 886#endif 887 XFlush(display); 888 TRACE(("done refreshing graphic: %d of %d refreshed pixels were holes\n", 889 holes, total)); 890 891 XFreeGC(display, graphics_gc); 892} 893 894/* 895 * Primary color hues: 896 * blue: 0 degrees 897 * red: 120 degrees 898 * green: 240 degrees 899 */ 900void 901hls2rgb(int h, int l, int s, short *r, short *g, short *b) 902{ 903 double hs = (h + 240) % 360; 904 double hv = hs / 360.0; 905 double lv = l / 100.0; 906 double sv = s / 100.0; 907 double c, x, m, c2; 908 double r1, g1, b1; 909 int hpi; 910 911 if (s == 0) { 912 *r = *g = *b = (short) l; 913 return; 914 } 915 916 if ((c2 = ((2.0 * lv) - 1.0)) < 0.0) 917 c2 = -c2; 918 c = (1.0 - c2) * sv; 919 hpi = (int) (hv * 6.0); 920 x = (hpi & 1) ? c : 0.0; 921 m = lv - 0.5 * c; 922 923 switch (hpi) { 924 case 0: 925 r1 = c; 926 g1 = x; 927 b1 = 0.0; 928 break; 929 case 1: 930 r1 = x; 931 g1 = c; 932 b1 = 0.0; 933 break; 934 case 2: 935 r1 = 0.0; 936 g1 = c; 937 b1 = x; 938 break; 939 case 3: 940 r1 = 0.0; 941 g1 = x; 942 b1 = c; 943 break; 944 case 4: 945 r1 = x; 946 g1 = 0.0; 947 b1 = c; 948 break; 949 case 5: 950 r1 = c; 951 g1 = 0.0; 952 b1 = x; 953 break; 954 default: 955 TRACE(("Bad HLS input: [%d,%d,%d], returning white\n", h, l, s)); 956 *r = (short) 100; 957 *g = (short) 100; 958 *b = (short) 100; 959 return; 960 } 961 962 *r = (short) ((r1 + m) * 100.0 + 0.5); 963 *g = (short) ((g1 + m) * 100.0 + 0.5); 964 *b = (short) ((b1 + m) * 100.0 + 0.5); 965 966 if (*r < 0) 967 *r = 0; 968 else if (*r > 100) 969 *r = 100; 970 if (*g < 0) 971 *g = 0; 972 else if (*g > 100) 973 *g = 100; 974 if (*b < 0) 975 *b = 0; 976 else if (*b > 100) 977 *b = 100; 978} 979 980void 981dump_graphic(Graphic const *graphic) 982{ 983#if defined(DUMP_COLORS) || defined(DUMP_BITMAP) 984 RegisterNum color; 985#endif 986#ifdef DUMP_BITMAP 987 int r, c; 988 ColorRegister const *reg; 989#endif 990 991 (void) graphic; 992 993 TRACE(("graphic stats: id=%u charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n", 994 graphic->id, 995 graphic->charrow, 996 graphic->charcol, 997 graphic->actual_width, 998 graphic->actual_height, 999 graphic->pixw, 1000 graphic->pixh)); 1001 1002#ifdef DUMP_COLORS 1003 TRACE(("graphic colors:\n")); 1004 for (color = 0; color < graphic->valid_registers; color++) { 1005 TRACE(("%03u: %d,%d,%d\n", 1006 color, 1007 graphic->color_registers[color].r, 1008 graphic->color_registers[color].g, 1009 graphic->color_registers[color].b)); 1010 } 1011#endif 1012 1013#ifdef DUMP_BITMAP 1014 TRACE(("graphic pixels:\n")); 1015 for (r = 0; r < graphic->actual_height; r++) { 1016 for (c = 0; c < graphic->actual_width; c++) { 1017 color = graphic->pixels[r * graphic->max_width + c]; 1018 if (color == COLOR_HOLE) { 1019 TRACE(("?")); 1020 } else { 1021 reg = &graphic->color_registers[color]; 1022 if (reg->r + reg->g + reg->b > 200) { 1023 TRACE(("#")); 1024 } else if (reg->r + reg->g + reg->b > 150) { 1025 TRACE(("%%")); 1026 } else if (reg->r + reg->g + reg->b > 100) { 1027 TRACE((":")); 1028 } else if (reg->r + reg->g + reg->b > 80) { 1029 TRACE((".")); 1030 } else { 1031 TRACE((" ")); 1032 } 1033 } 1034 } 1035 TRACE(("\n")); 1036 } 1037 1038 TRACE(("\n")); 1039#endif 1040} 1041 1042/* Erase the portion of any displayed graphic overlapping with a rectangle 1043 * of the given size and location in pixels. 1044 * This is used to allow text to "erase" graphics underneath it. 1045 */ 1046static void 1047erase_graphic(Graphic *graphic, int x, int y, int w, int h) 1048{ 1049 RegisterNum hole = COLOR_HOLE; 1050 int pw, ph; 1051 int r, c; 1052 int rbase, cbase; 1053 1054 pw = graphic->pixw; 1055 ph = graphic->pixh; 1056 1057 TRACE(("erasing graphic %d,%d %dx%d\n", x, y, w, h)); 1058 1059 rbase = 0; 1060 for (r = 0; r < graphic->actual_height; r++) { 1061 if (rbase + ph - 1 >= y 1062 && rbase <= y + h - 1) { 1063 cbase = 0; 1064 for (c = 0; c < graphic->actual_width; c++) { 1065 if (cbase + pw - 1 >= x 1066 && cbase <= x + w - 1) { 1067 graphic->pixels[r * graphic->max_width + c] = hole; 1068 } 1069 cbase += pw; 1070 } 1071 } 1072 rbase += ph; 1073 } 1074} 1075 1076static int 1077compare_graphic_ids(const void *left, const void *right) 1078{ 1079 const Graphic *l = *(const Graphic *const *) left; 1080 const Graphic *r = *(const Graphic *const *) right; 1081 1082 if (!l->valid || !r->valid) 1083 return 0; 1084 if (l->id < r->id) 1085 return -1; 1086 else 1087 return 1; 1088} 1089 1090void 1091refresh_displayed_graphics(TScreen const *screen, 1092 int leftcol, 1093 int toprow, 1094 int ncols, 1095 int nrows) 1096{ 1097 Graphic *ordered_graphics[MAX_GRAPHICS]; 1098 Graphic *graphic; 1099 unsigned ii; 1100 unsigned jj = 0; 1101 int x, y, w, h; 1102 int xbase, ybase; 1103 1104 FOR_EACH_SLOT(ii) { 1105 if ((graphic = getActiveSlot(ii))) { 1106 ordered_graphics[jj++] = graphic; 1107 } 1108 } 1109 if (jj > 1) { 1110 qsort(ordered_graphics, 1111 (size_t) jj, 1112 sizeof(ordered_graphics[0]), 1113 compare_graphic_ids); 1114 } 1115 1116 for (ii = 0; ii < jj; ++ii) { 1117 graphic = ordered_graphics[ii]; 1118 if (graphic->bufferid != screen->whichBuf) 1119 continue; 1120 1121 x = (leftcol - graphic->charcol) * FontWidth(screen); 1122 y = (toprow - graphic->charrow) * FontHeight(screen); 1123 w = ncols * FontWidth(screen); 1124 h = nrows * FontHeight(screen); 1125 1126 xbase = (OriginX(screen) 1127 + graphic->charcol * FontWidth(screen)); 1128 ybase = (OriginY(screen) 1129 + (graphic->charrow - screen->topline) * FontHeight(screen)); 1130 1131 if (xbase + x + w + OriginX(screen) > FullWidth(screen)) 1132 w = FullWidth(screen) - (xbase + x + OriginX(screen)); 1133 if (ybase + y + h + OriginY(screen) > FullHeight(screen)) 1134 h = FullHeight(screen) - (ybase + y + OriginY(screen)); 1135 else if (ybase + y < OriginY(screen)) { 1136 int diff = OriginY(screen) - (ybase + y); 1137 y += diff; 1138 h -= diff; 1139 } 1140 1141 TRACE(("graphics refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d xbase=%d ybase=%d\n", 1142 screen->topline, 1143 leftcol, toprow, 1144 nrows, ncols, 1145 x, y, w, h, 1146 xbase, ybase)); 1147 refresh_graphic(screen, graphic, xbase, ybase, x, y, w, h); 1148 } 1149} 1150 1151void 1152refresh_modified_displayed_graphics(TScreen const *screen) 1153{ 1154 Graphic *graphic; 1155 unsigned ii; 1156 int leftcol, toprow; 1157 int nrows, ncols; 1158 int x, y, w, h; 1159 int xbase, ybase; 1160 1161 FOR_EACH_SLOT(ii) { 1162 if (!(graphic = getActiveSlot(ii))) 1163 continue; 1164 if (graphic->bufferid != screen->whichBuf) 1165 continue; 1166 if (!graphic->dirty) 1167 continue; 1168 1169 leftcol = graphic->charcol; 1170 toprow = graphic->charrow; 1171 nrows = (((graphic->actual_height * graphic->pixh) 1172 + FontHeight(screen) - 1) 1173 / FontHeight(screen)); 1174 ncols = (((graphic->actual_width * graphic->pixw) 1175 + FontWidth(screen) - 1) 1176 / FontWidth(screen)); 1177 1178 x = (leftcol - graphic->charcol) * FontWidth(screen); 1179 y = (toprow - graphic->charrow) * FontHeight(screen); 1180 w = ncols * FontWidth(screen); 1181 h = nrows * FontHeight(screen); 1182 1183 xbase = (OriginX(screen) 1184 + graphic->charcol * FontWidth(screen)); 1185 ybase = (OriginY(screen) 1186 + (graphic->charrow - screen->topline) * FontHeight(screen)); 1187 1188 if (xbase + x + w + OriginX(screen) > FullWidth(screen)) 1189 w = FullWidth(screen) - (xbase + x + OriginX(screen)); 1190 if (ybase + y + h + OriginY(screen) > FullHeight(screen)) 1191 h = FullHeight(screen) - (ybase + y + OriginY(screen)); 1192 else if (ybase + y < OriginY(screen)) { 1193 int diff = OriginY(screen) - (ybase + y); 1194 y += diff; 1195 h -= diff; 1196 } 1197 1198 TRACE(("full graphics refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d xbase=%d ybase=%d\n", 1199 screen->topline, 1200 leftcol, toprow, 1201 nrows, ncols, 1202 x, y, w, h, 1203 xbase, ybase)); 1204 refresh_graphic(screen, graphic, xbase, ybase, x, y, w, h); 1205 graphic->dirty = 0; 1206 } 1207} 1208 1209void 1210scroll_displayed_graphics(int rows) 1211{ 1212 Graphic *graphic; 1213 unsigned ii; 1214 1215 TRACE(("graphics scroll: moving all up %d rows\n", rows)); 1216 /* FIXME: VT125 ReGIS graphics are fixed at the upper left of the display; need to verify */ 1217 1218 FOR_EACH_SLOT(ii) { 1219 if (!(graphic = getActiveSlot(ii))) 1220 continue; 1221 1222 graphic->charrow -= rows; 1223 } 1224} 1225 1226void 1227pixelarea_clear_displayed_graphics(TScreen const *screen, 1228 int winx, 1229 int winy, 1230 int w, 1231 int h) 1232{ 1233 Graphic *graphic; 1234 unsigned ii; 1235 int x, y; 1236 1237 FOR_EACH_SLOT(ii) { 1238 if (!(graphic = getActiveSlot(ii))) 1239 continue; 1240 1241 x = winx - graphic->charcol * FontWidth(screen); 1242 y = winy - graphic->charrow * FontHeight(screen); 1243 1244 TRACE(("pixelarea graphics erase: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n", 1245 screen->topline, 1246 winx, winy, 1247 w, h, 1248 x, y)); 1249 erase_graphic(graphic, x, y, w, h); 1250 } 1251} 1252 1253void 1254chararea_clear_displayed_graphics(TScreen const *screen, 1255 int leftcol, 1256 int toprow, 1257 int ncols, 1258 int nrows) 1259{ 1260 int x, y, w, h; 1261 1262 x = leftcol * FontWidth(screen); 1263 y = toprow * FontHeight(screen); 1264 w = ncols * FontWidth(screen); 1265 h = nrows * FontHeight(screen); 1266 1267 TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n", 1268 screen->topline, 1269 leftcol, toprow, 1270 nrows, ncols, 1271 x, y, w, h)); 1272 pixelarea_clear_displayed_graphics(screen, x, y, w, h); 1273} 1274 1275void 1276reset_displayed_graphics(TScreen const *screen) 1277{ 1278 unsigned ii; 1279 1280 init_color_registers(getSharedRegisters(), screen->terminal_id); 1281 1282 TRACE(("resetting all graphics\n")); 1283 FOR_EACH_SLOT(ii) { 1284 deactivateSlot(ii); 1285 } 1286} 1287 1288#ifdef NO_LEAKS 1289void 1290noleaks_graphics(void) 1291{ 1292 unsigned ii; 1293 1294 FOR_EACH_SLOT(ii) { 1295 deactivateSlot(ii); 1296 } 1297} 1298#endif 1299