"Fossies" - the Fresh Open Source Software Archive 
Member "xterm-379/graphics.c" (16 May 2022, 48122 Bytes) of package /linux/misc/xterm-379.tgz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "graphics.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
372_vs_373.
1 /* $XTermId: graphics.c,v 1.118 2022/05/16 23:35:50 tom Exp $ */
2
3 /*
4 * Copyright 2013-2021,2022 by Ross Combs
5 * Copyright 2013-2021,2022 by Thomas E. Dickey
6 *
7 * All Rights Reserved
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the
11 * "Software"), to deal in the Software without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sublicense, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
24 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 *
28 * Except as contained in this notice, the name(s) of the above copyright
29 * holders shall not be used in advertising or otherwise to promote the
30 * sale, use or other dealings in this Software without prior written
31 * authorization.
32 */
33
34 #include <xterm.h>
35
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <stdlib.h>
39
40 #include <data.h>
41 #include <ptyx.h>
42
43 #include <assert.h>
44 #include <graphics.h>
45
46 #undef DUMP_BITMAP
47 #undef DUMP_COLORS
48 #undef DEBUG_PALETTE
49 #undef DEBUG_PIXEL
50 #undef DEBUG_REFRESH
51
52 /*
53 * graphics TODO list
54 *
55 * ReGIS:
56 * - ship a default alphabet zero font instead of scaling Xft fonts
57 * - input cursors
58 * - output cursors
59 * - mouse/tablet/arrow-key input
60 * - fix graphic pages for ReGIS -- they should also apply to text and sixel graphics
61 * - fix interpolated curves to more closely match implementation (identical despite direction and starting point)
62 * - non-ASCII alphabets
63 * - enter/leave anywhere in a command
64 * - locator key definitions (DECLKD)
65 * - command display mode
66 * - re-rasterization on window resize
67 * - macros
68 * - improved fills for narrow angles (track actual lines not just pixels)
69 * - hardcopy/screen-capture support (need dialog of some sort for safety)
70 * - error reporting
71 *
72 * sixel:
73 * - fix problem where new_row < 0 during sixel parsing (see FIXME)
74 * - screen-capture support (need dialog of some sort for safety)
75 *
76 * VT55/VT105 waveform graphics
77 * - everything
78 *
79 * Tektronix:
80 * - color (VT340 4014 emulation, 41xx, IRAF GTERM, and also MS-DOS Kermit color support)
81 * - polygon fill (41xx)
82 * - clear area extension
83 * - area fill extension
84 * - pixel operations (RU/RS/RP)
85 * - research other 41xx and 42xx extensions
86 *
87 * common graphics features:
88 * - handle light/dark screen modes (CSI?5[hl])
89 * - update text fg/bg color which overlaps images
90 * - handle graphic updates in scroll regions (verify effect on graphics)
91 * - handle rectangular area copies (verify they work with graphics)
92 * - invalidate graphics under graphic if same origin, at least as big, and bg not transparent
93 * - invalidate graphic if completely scrolled past end of scrollback
94 * - invalidate graphic if all pixels are transparent/erased
95 * - invalidate graphic if completely scrolled out of alt buffer
96 * - posturize requested colors to match hardware palettes (e.g. only four possible shades on VT240)
97 * - color register report/restore
98 * - ability to select/copy graphics for pasting in other programs
99 * - ability to show non-scroll-mode sixel graphics in a separate window
100 * - ability to show ReGIS graphics in a separate window
101 * - ability to show Tektronix graphics in VT100 window
102 * - truncate graphics at bottom edge of terminal?
103 * - locator events (DECEFR DECSLE DECELR DECLRP)
104 * - locator controller mode (CSI6i / CSI7i)
105 *
106 * new escape sequences:
107 * - way to query text font size without "window ops" (or make "window ops" permissions more fine grained)
108 * - way to query and set the number of graphics pages
109 *
110 * ReGIS extensions:
111 * - non-integer text scaling
112 * - free distortionless text rotation (vs. simulating the distortion and aligning to 45deg increments)
113 * - font characteristics: bold/underline/italic
114 * - remove/increase arbitrary limits (pattern size, pages, alphabets, stack size, font names, etc.)
115 * - shade/fill with borders
116 * - sprites (copy portion of page into/out of buffer with scaling and rotation)
117 * - ellipses
118 * - 2D patterns
119 * - option to set actual graphic size (not just coordinate range)
120 * - gradients (for lines and fills)
121 * - line width (RLogin has this and it is mentioned in docs for the DEC ReGIS to Postscript converter)
122 * - transparency
123 * - background color as stackable write control
124 * - true color (virtual color registers created upon lookup)
125 * - anti-aliasing
126 * - variable-width (proportional) text
127 */
128
129 /* font sizes:
130 * VT510:
131 * 80 Columns 132 Columns Maximum Number of Lines
132 * 10 x 16 6 x 16 26 lines + keyboard indicator line
133 * 10 x 13 6 x 13 26 lines + keyboard indicator line
134 * 10 x 10 6 x 10 42 lines + keyboard indicator line
135 * 10 x 8 6 x 8 53 lines + keyboard indicator line
136 */
137
138 typedef struct allocated_color_register {
139 struct allocated_color_register *next;
140 Pixel pix;
141 short r, g, b;
142 } AllocatedColorRegister;
143
144 #define LOOKUP_WIDTH 16
145 static AllocatedColorRegister *allocated_colors[LOOKUP_WIDTH][LOOKUP_WIDTH][LOOKUP_WIDTH];
146
147 #define FOR_EACH_SLOT(ii) for (ii = 0U; ii < MAX_GRAPHICS; ii++)
148
149 static ColorRegister *shared_color_registers;
150 static Graphic *displayed_graphics[MAX_GRAPHICS];
151 static unsigned next_graphic_id = 0U;
152 static unsigned used_graphics; /* 0 to MAX_GRAPHICS */
153
154 static int valid_graphics;
155 static GC graphics_gc;
156 static XGCValues xgcv;
157 static ColorRegister last_color;
158 static ColorRegister gc_color;
159
160 #define DiffColor(this,that) \
161 (this.r != that.r || \
162 this.g != that.g || \
163 this.b != that.b)
164
165 static ColorRegister null_color =
166 {-1, -1, -1};
167
168 static ColorRegister *
169 allocRegisters(void)
170 {
171 return TypeCallocN(ColorRegister, MAX_COLOR_REGISTERS);
172 }
173
174 static Graphic *
175 freeGraphic(Graphic *obj)
176 {
177 if (obj) {
178 free(obj->pixels);
179 free(obj->private_color_registers);
180 free(obj);
181 }
182 return NULL;
183 }
184
185 static Graphic *
186 allocGraphic(int max_w, int max_h)
187 {
188 Graphic *result = TypeCalloc(Graphic);
189 if (result) {
190 result->max_width = max_w;
191 result->max_height = max_h;
192 if (!(result->pixels = TypeCallocN(RegisterNum,
193 (size_t) max_w * (size_t) max_h))) {
194 result = freeGraphic(result);
195 } else if (!(result->private_color_registers = allocRegisters())) {
196 result = freeGraphic(result);
197 }
198 }
199 return result;
200 }
201
202 #define getActiveSlot(n) \
203 (((n) < MAX_GRAPHICS && \
204 displayed_graphics[n] && \
205 displayed_graphics[n]->valid) \
206 ? displayed_graphics[n] \
207 : NULL)
208
209 static Graphic *
210 getInactiveSlot(const TScreen *screen, unsigned n)
211 {
212 if (n < MAX_GRAPHICS &&
213 (!displayed_graphics[n] ||
214 !displayed_graphics[n]->valid)) {
215 if (!displayed_graphics[n]) {
216 displayed_graphics[n] = allocGraphic(screen->graphics_max_wide,
217 screen->graphics_max_high);
218 used_graphics += (displayed_graphics[n] != NULL);
219 }
220 return displayed_graphics[n];
221 }
222 return NULL;
223 }
224
225 static ColorRegister *
226 getSharedRegisters(void)
227 {
228 if (!shared_color_registers)
229 shared_color_registers = allocRegisters();
230 return shared_color_registers;
231 }
232
233 static void
234 deactivateSlot(unsigned n)
235 {
236 if ((n < MAX_GRAPHICS) && displayed_graphics[n]) {
237 displayed_graphics[n] = freeGraphic(displayed_graphics[n]);
238 used_graphics--;
239 }
240 }
241
242 extern RegisterNum
243 read_pixel(Graphic *graphic, int x, int y)
244 {
245 return (((x) >= 0 &&
246 (x) < (graphic)->actual_width &&
247 (y) >= 0 &&
248 (y) < (graphic)->actual_height)
249 ? (graphic)->pixels[(y) * (graphic)->max_width + (x)]
250 : (RegisterNum) COLOR_HOLE);
251 }
252
253 #define _draw_pixel(G, X, Y, C) \
254 do { \
255 unsigned _cell = (unsigned)((Y) * (G)->max_width + (X)); \
256 SetSpixel(G, _cell, (RegisterNum) (C)); \
257 } while (0)
258
259 void
260 draw_solid_pixel(Graphic *graphic, int x, int y, unsigned color)
261 {
262 assert(color <= MAX_COLOR_REGISTERS);
263
264 #ifdef DEBUG_PIXEL
265 TRACE(("drawing pixel at %d,%d color=%hu (hole=%hu, [%d,%d,%d])\n",
266 x,
267 y,
268 color,
269 COLOR_HOLE,
270 ((color != COLOR_HOLE)
271 ? (unsigned) graphic->color_registers[color].r : 0U),
272 ((color != COLOR_HOLE)
273 ? (unsigned) graphic->color_registers[color].g : 0U),
274 ((color != COLOR_HOLE)
275 ? (unsigned) graphic->color_registers[color].b : 0U)));
276 #endif
277 if (x >= 0 && x < graphic->actual_width &&
278 y >= 0 && y < graphic->actual_height) {
279 _draw_pixel(graphic, x, y, color);
280 if (color < MAX_COLOR_REGISTERS)
281 graphic->color_registers_used[color] = 1;
282 }
283 }
284
285 void
286 draw_solid_rectangle(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color)
287 {
288 int x, y;
289 int tmp;
290
291 assert(color <= MAX_COLOR_REGISTERS);
292
293 if (x1 > x2) {
294 EXCHANGE(x1, x2, tmp);
295 }
296 if (y1 > y2) {
297 EXCHANGE(y1, y2, tmp);
298 }
299
300 if (x2 < 0 || x1 >= graphic->actual_width ||
301 y2 < 0 || y1 >= graphic->actual_height)
302 return;
303
304 if (x1 < 0)
305 x1 = 0;
306 if (x2 >= graphic->actual_width)
307 x2 = graphic->actual_width - 1;
308 if (y1 < 0)
309 y1 = 0;
310 if (y2 >= graphic->actual_height)
311 y2 = graphic->actual_height - 1;
312
313 if (color < MAX_COLOR_REGISTERS)
314 graphic->color_registers_used[color] = 1;
315 for (y = y1; y <= y2; y++)
316 for (x = x1; x <= x2; x++)
317 _draw_pixel(graphic, x, y, color);
318 }
319
320 void
321 copy_overlapping_area(Graphic *graphic, int src_ul_x, int src_ul_y,
322 int dst_ul_x, int dst_ul_y, unsigned w, unsigned h,
323 unsigned default_color)
324 {
325 int sx, ex, dx;
326 int sy, ey, dy;
327 int xx, yy;
328 RegisterNum color;
329
330 if (dst_ul_x <= src_ul_x) {
331 sx = 0;
332 ex = (int) w - 1;
333 dx = +1;
334 } else {
335 sx = (int) w - 1;
336 ex = 0;
337 dx = -1;
338 }
339
340 if (dst_ul_y <= src_ul_y) {
341 sy = 0;
342 ey = (int) h - 1;
343 dy = +1;
344 } else {
345 sy = (int) h - 1;
346 ey = 0;
347 dy = -1;
348 }
349
350 for (yy = sy; yy != ey + dy; yy += dy) {
351 int dst_y = dst_ul_y + yy;
352 int src_y = src_ul_y + yy;
353 if (dst_y < 0 || dst_y >= (int) graphic->actual_height)
354 continue;
355
356 for (xx = sx; xx != ex + dx; xx += dx) {
357 int dst_x = dst_ul_x + xx;
358 int src_x = src_ul_x + xx;
359 int cell;
360 if (dst_x < 0 || dst_x >= (int) graphic->actual_width)
361 continue;
362
363 if (src_x < 0 || src_x >= (int) graphic->actual_width ||
364 src_y < 0 || src_y >= (int) graphic->actual_height)
365 color = (RegisterNum) default_color;
366 else
367 color = graphic->pixels[(unsigned) (src_y *
368 graphic->max_width) +
369 (unsigned) src_x];
370
371 cell = (int) ((unsigned) (dst_y * graphic->max_width) +
372 (unsigned) dst_x);
373 SetSpixel(graphic, cell, color);
374 }
375 }
376 }
377
378 #define set_color_register(color_registers, color, pr, pg, pb) \
379 do { \
380 ColorRegister *reg = &color_registers[color]; \
381 reg->r = (short) pr; \
382 reg->g = (short) pg; \
383 reg->b = (short) pb; \
384 } while (0)
385
386 /* Graphics which don't use private colors will act as if they are using a
387 * device-wide color palette.
388 */
389 static void
390 set_shared_color_register(unsigned color, int r, int g, int b)
391 {
392 unsigned ii;
393
394 assert(color < MAX_COLOR_REGISTERS);
395
396 set_color_register(getSharedRegisters(), color, r, g, b);
397
398 if (!used_graphics)
399 return;
400
401 FOR_EACH_SLOT(ii) {
402 Graphic *graphic;
403
404 if (!(graphic = getActiveSlot(ii)))
405 continue;
406 if (graphic->private_colors)
407 continue;
408
409 if (graphic->color_registers_used[ii]) {
410 graphic->dirty = True;
411 }
412 }
413 }
414
415 void
416 update_color_register(Graphic *graphic,
417 unsigned color,
418 int r,
419 int g,
420 int b)
421 {
422 assert(color < MAX_COLOR_REGISTERS);
423
424 if (graphic->private_colors) {
425 set_color_register(graphic->private_color_registers,
426 color, r, g, b);
427 if (graphic->color_registers_used[color]) {
428 graphic->dirty = True;
429 }
430 graphic->color_registers_used[color] = 1;
431 } else {
432 set_shared_color_register(color, r, g, b);
433 }
434 }
435
436 #define SQUARE(X) ( (X) * (X) )
437
438 RegisterNum
439 find_color_register(ColorRegister const *color_registers, int r, int g, int b)
440 {
441 unsigned i;
442 unsigned closest_index;
443 unsigned closest_distance;
444
445 /* I have no idea what algorithm DEC used for this.
446 * The documentation warns that it is unpredictable, especially with values
447 * far away from any allocated color so it is probably a very simple
448 * heuristic rather than something fancy like finding the minimum distance
449 * in a linear perceptive color space.
450 */
451 closest_index = MAX_COLOR_REGISTERS;
452 closest_distance = 0U;
453 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) {
454 unsigned d = (unsigned) (SQUARE(2 * (color_registers[i].r - r)) +
455 SQUARE(3 * (color_registers[i].g - g)) +
456 SQUARE(1 * (color_registers[i].b - b)));
457 if (closest_index == MAX_COLOR_REGISTERS || d < closest_distance) {
458 closest_index = i;
459 closest_distance = d;
460 }
461 }
462
463 TRACE(("found closest color register to %d,%d,%d: %u (distance %u value %d,%d,%d)\n",
464 r, g, b,
465 closest_index,
466 closest_distance,
467 color_registers[closest_index].r,
468 color_registers[closest_index].g,
469 color_registers[closest_index].b));
470 return (RegisterNum) closest_index;
471 }
472
473 static void
474 init_color_registers(ColorRegister *color_registers, int graphics_termid)
475 {
476 TRACE(("setting initial colors for terminal %d\n", graphics_termid));
477 {
478 unsigned i;
479
480 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) {
481 set_color_register(color_registers, (RegisterNum) i, 0, 0, 0);
482 }
483 }
484
485 /*
486 * default color registers:
487 * (mono) (color)
488 * VK100/GIGI (fixed)
489 * VT125:
490 * 0: 0% 0%
491 * 1: 33% blue
492 * 2: 66% red
493 * 3: 100% green
494 * VT240:
495 * 0: 0% 0%
496 * 1: 33% blue
497 * 2: 66% red
498 * 3: 100% green
499 * VT241:
500 * 0: 0% 0%
501 * 1: 33% blue
502 * 2: 66% red
503 * 3: 100% green
504 * VT330:
505 * 0: 0% 0% (bg for light on dark mode)
506 * 1: 33% blue (red?)
507 * 2: 66% red (green?)
508 * 3: 100% green (yellow?) (fg for light on dark mode)
509 * VT340:
510 * 0: 0% 0% (bg for light on dark mode)
511 * 1: 14% blue
512 * 2: 29% red
513 * 3: 43% green
514 * 4: 57% magenta
515 * 5: 71% cyan
516 * 6: 86% yellow
517 * 7: 100% 50% (fg for light on dark mode)
518 * 8: 0% 25%
519 * 9: 14% gray-blue
520 * 10: 29% gray-red
521 * 11: 43% gray-green
522 * 12: 57% gray-magenta
523 * 13: 71% gray-cyan
524 * 14: 86% gray-yellow
525 * 15: 100% 75% ("white")
526 * VT382:
527 * ? (FIXME: B&W only?)
528 * dxterm:
529 * ?
530 */
531 switch (graphics_termid) {
532 case 125:
533 case 241:
534 set_color_register(color_registers, 0, 0, 0, 0);
535 set_color_register(color_registers, 1, 0, 0, 100);
536 set_color_register(color_registers, 2, 0, 100, 0);
537 set_color_register(color_registers, 3, 100, 0, 0);
538 break;
539 case 240:
540 case 330:
541 set_color_register(color_registers, 0, 0, 0, 0);
542 set_color_register(color_registers, 1, 33, 33, 33);
543 set_color_register(color_registers, 2, 66, 66, 66);
544 set_color_register(color_registers, 3, 100, 100, 100);
545 break;
546 case 340:
547 default:
548 set_color_register(color_registers, 0, 0, 0, 0);
549 set_color_register(color_registers, 1, 20, 20, 80);
550 set_color_register(color_registers, 2, 80, 13, 13);
551 set_color_register(color_registers, 3, 20, 80, 20);
552 set_color_register(color_registers, 4, 80, 20, 80);
553 set_color_register(color_registers, 5, 20, 80, 80);
554 set_color_register(color_registers, 6, 80, 80, 20);
555 set_color_register(color_registers, 7, 53, 53, 53);
556 set_color_register(color_registers, 8, 26, 26, 26);
557 set_color_register(color_registers, 9, 33, 33, 60);
558 set_color_register(color_registers, 10, 60, 26, 26);
559 set_color_register(color_registers, 11, 33, 60, 33);
560 set_color_register(color_registers, 12, 60, 33, 60);
561 set_color_register(color_registers, 13, 33, 60, 60);
562 set_color_register(color_registers, 14, 60, 60, 33);
563 set_color_register(color_registers, 15, 80, 80, 80);
564 break;
565 case 382: /* FIXME: verify */
566 set_color_register(color_registers, 0, 0, 0, 0);
567 set_color_register(color_registers, 1, 100, 100, 100);
568 break;
569 }
570
571 #ifdef DEBUG_PALETTE
572 {
573 unsigned i;
574
575 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) {
576 TRACE(("initial value for register %03u: %d,%d,%d\n",
577 i,
578 color_registers[i].r,
579 color_registers[i].g,
580 color_registers[i].b));
581 }
582 }
583 #endif
584 }
585
586 unsigned
587 get_color_register_count(TScreen const *screen)
588 {
589 unsigned num_color_registers;
590
591 if (screen->numcolorregisters >= 0) {
592 num_color_registers = (unsigned) screen->numcolorregisters;
593 } else {
594 num_color_registers = 0U;
595 }
596
597 if (num_color_registers > 1U) {
598 if (num_color_registers > MAX_COLOR_REGISTERS)
599 return MAX_COLOR_REGISTERS;
600 return num_color_registers;
601 }
602
603 /*
604 * color capabilities:
605 * VK100/GIGI 1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta)
606 * VT125 2 planes (4 registers) colorspace is (64?) (color), ? (grayscale)
607 * VT240 2 planes (4 registers) colorspace is 4 shades (grayscale)
608 * VT241 2 planes (4 registers) colorspace is ? (color), ? shades (grayscale)
609 * VT330 2 planes (4 registers) colorspace is 4 shades (grayscale)
610 * VT340 4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale)
611 * VT382 1 plane (two fixed colors: black and white) FIXME: verify
612 * dxterm ?
613 */
614 switch (screen->graphics_termid) {
615 case 125:
616 return 4U;
617 case 240:
618 return 4U;
619 case 241:
620 return 4U;
621 case 330:
622 return 4U;
623 case 340:
624 return 16U;
625 case 382:
626 return 2U;
627 default:
628 /* unknown graphics model -- might as well be generous */
629 return MAX_COLOR_REGISTERS;
630 }
631 }
632
633 static void
634 init_graphic(Graphic *graphic,
635 unsigned type,
636 int graphics_termid,
637 int charrow,
638 int charcol,
639 unsigned num_color_registers,
640 int private_colors)
641 {
642 const unsigned max_pixels = (unsigned) (graphic->max_width *
643 graphic->max_height);
644
645 TRACE(("init_graphic %u pixels at %d,%d\n", max_pixels, charrow, charcol));
646
647 graphic->hidden = False;
648 graphic->dirty = True;
649 memset(graphic->pixels, COLOR_HOLE & 0xff, max_pixels * sizeof(RegisterNum));
650 memset(graphic->color_registers_used, 0, sizeof(graphic->color_registers_used));
651
652 /*
653 * text and graphics interactions:
654 * VK100/GIGI text writes on top of graphics buffer, color attribute shared with text
655 * VT240,VT241,VT330,VT340 text writes on top of graphics buffer
656 * VT382 text writes on top of graphics buffer FIXME: verify
657 * VT125 graphics buffer overlaid on top of text in B&W display, text not present in color display
658 */
659
660 /*
661 * dimensions (ReGIS logical, physical):
662 * VK100/GIGI 768x4?? 768x240(status?)
663 * VT125 768x460 768x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation")
664 * VT240 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation")
665 * VT241 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation")
666 * VT330 800x480 800x480(+?status)
667 * VT340 800x480 800x480(+?status)
668 * VT382 960x750 sixel only
669 * dxterm ?x? ?x? variable?
670 */
671
672 graphic->actual_width = 0;
673 graphic->actual_height = 0;
674
675 graphic->pixw = 1;
676 graphic->pixh = 1;
677
678 graphic->valid_registers = num_color_registers;
679 TRACE(("%d color registers\n", graphic->valid_registers));
680
681 graphic->private_colors = private_colors;
682 if (graphic->private_colors) {
683 TRACE(("using private color registers\n"));
684 init_color_registers(graphic->private_color_registers, graphics_termid);
685 graphic->color_registers = graphic->private_color_registers;
686 } else {
687 TRACE(("using shared color registers\n"));
688 graphic->color_registers = getSharedRegisters();
689 }
690
691 graphic->charrow = charrow;
692 graphic->charcol = charcol;
693 graphic->type = type;
694 graphic->valid = False;
695 }
696
697 Graphic *
698 get_new_graphic(XtermWidget xw, int charrow, int charcol, unsigned type)
699 {
700 TScreen const *screen = TScreenOf(xw);
701 const int bufferid = screen->whichBuf;
702 const int graphics_termid = GraphicsTermId(screen);
703 Graphic *graphic = NULL;
704 unsigned ii;
705
706 FOR_EACH_SLOT(ii) {
707 if ((graphic = getInactiveSlot(screen, ii))) {
708 TRACE(("using fresh graphic index=%u id=%u\n", ii, next_graphic_id));
709 break;
710 }
711 }
712
713 /* if none are free, recycle the graphic scrolled back the farthest */
714 if (!graphic) {
715 int min_charrow = 0;
716 Graphic *min_graphic = NULL;
717
718 FOR_EACH_SLOT(ii) {
719 if (!(graphic = getActiveSlot(ii)))
720 continue;
721 if (!min_graphic || graphic->charrow < min_charrow) {
722 min_charrow = graphic->charrow;
723 min_graphic = graphic;
724 }
725 }
726 TRACE(("recycling old graphic index=%u id=%u\n", ii, next_graphic_id));
727 graphic = min_graphic;
728 }
729
730 if (graphic) {
731 unsigned num_color_registers;
732 num_color_registers = get_color_register_count(screen);
733 graphic->xw = xw;
734 graphic->bufferid = bufferid;
735 graphic->id = next_graphic_id++;
736 init_graphic(graphic,
737 type,
738 graphics_termid,
739 charrow,
740 charcol,
741 num_color_registers,
742 screen->privatecolorregisters);
743 }
744 return graphic;
745 }
746
747 Graphic *
748 get_new_or_matching_graphic(XtermWidget xw,
749 int charrow,
750 int charcol,
751 int actual_width,
752 int actual_height,
753 unsigned type)
754 {
755 TScreen const *screen = TScreenOf(xw);
756 const int bufferid = screen->whichBuf;
757 Graphic *graphic;
758 unsigned ii;
759
760 FOR_EACH_SLOT(ii) {
761 TRACE(("checking slot=%u for graphic at %d,%d %dx%d bufferid=%d type=%u\n", ii,
762 charrow, charcol,
763 actual_width, actual_height,
764 bufferid, type));
765 if ((graphic = getActiveSlot(ii))) {
766 if (graphic->type == type &&
767 graphic->bufferid == bufferid &&
768 graphic->charrow == charrow &&
769 graphic->charcol == charcol &&
770 graphic->actual_width == actual_width &&
771 graphic->actual_height == actual_height) {
772 TRACE(("found existing graphic slot=%u id=%u\n", ii, graphic->id));
773 return graphic;
774 }
775 TRACE(("not a match: graphic at %d,%d %dx%d bufferid=%d type=%u\n",
776 graphic->charrow, graphic->charcol,
777 graphic->actual_width, graphic->actual_height,
778 graphic->bufferid, graphic->type));
779 }
780 }
781
782 /* if no match get a new graphic */
783 if ((graphic = get_new_graphic(xw, charrow, charcol, type))) {
784 graphic->actual_width = actual_width;
785 graphic->actual_height = actual_height;
786 TRACE(("no match; created graphic at %d,%d %dx%d bufferid=%d type=%u\n",
787 graphic->charrow, graphic->charcol,
788 graphic->actual_width, graphic->actual_height,
789 graphic->bufferid, graphic->type));
790 }
791 return graphic;
792 }
793
794 #define ScaleForXColor(s) (unsigned short) ((unsigned long)(s) * MAX_U_COLOR / CHANNEL_MAX)
795
796 static int
797 save_allocated_color(const ColorRegister *reg, XtermWidget xw, Pixel *pix)
798 {
799 unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX;
800 unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX;
801 unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX;
802 XColor xcolor;
803 AllocatedColorRegister *new_color;
804
805 /* *INDENT-EQLS* */
806 xcolor.pixel = 0UL;
807 xcolor.red = ScaleForXColor(reg->r);
808 xcolor.green = ScaleForXColor(reg->g);
809 xcolor.blue = ScaleForXColor(reg->b);
810 xcolor.flags = DoRed | DoGreen | DoBlue;
811
812 if (!allocateBestRGB(xw, &xcolor)) {
813 TRACE(("unable to allocate xcolor\n"));
814 *pix = 0UL;
815 return 0;
816 } else {
817 *pix = xcolor.pixel;
818
819 if (!(new_color = TypeMalloc(AllocatedColorRegister))) {
820 TRACE(("unable to save pixel %lu\n", (unsigned long) *pix));
821 return 0;
822 } else {
823 new_color->r = reg->r;
824 new_color->g = reg->g;
825 new_color->b = reg->b;
826 new_color->pix = *pix;
827 new_color->next = allocated_colors[rr][gg][bb];
828
829 allocated_colors[rr][gg][bb] = new_color;
830
831 return 1;
832 }
833 }
834 }
835
836 /* FIXME: with so many possible colors we need to determine
837 * when to free them to be nice to PseudoColor displays
838 */
839 static Pixel
840 color_register_to_xpixel(const ColorRegister *reg, XtermWidget xw)
841 {
842 Pixel result;
843 unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX;
844 unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX;
845 unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX;
846 const AllocatedColorRegister *search;
847
848 for (search = allocated_colors[rr][gg][bb]; search; search = search->next) {
849 if (search->r == reg->r &&
850 search->g == reg->g &&
851 search->b == reg->b) {
852 return search->pix;
853 }
854 }
855
856 save_allocated_color(reg, xw, &result);
857 return result;
858 }
859
860 static void
861 refresh_graphic(TScreen const *screen,
862 Graphic const *graphic,
863 ColorRegister *buffer,
864 int refresh_x,
865 int refresh_y,
866 int refresh_w,
867 int refresh_h,
868 int draw_x,
869 int draw_y,
870 int draw_w,
871 int draw_h)
872 {
873 int const pw = graphic->pixw;
874 int const ph = graphic->pixh;
875 int const graph_x = graphic->charcol * FontWidth(screen);
876 int const graph_y = graphic->charrow * FontHeight(screen);
877 int const graph_w = graphic->actual_width;
878 int const graph_h = graphic->actual_height;
879 int const mw = graphic->max_width;
880
881 int r, c;
882 int pmy;
883 RegisterNum regnum;
884
885 if_TRACE(int holes = 0);
886 if_TRACE(int total = 0);
887 if_TRACE(int out_of_range = 0);
888
889 TRACE(("refreshing graphic %u from %d,%d %dx%d (valid=%d, size=%dx%d, scale=%dx%d max=%dx%d)\n",
890 graphic->id,
891 graph_x, graph_y, draw_w, draw_h,
892 graphic->valid,
893 graphic->actual_width,
894 graphic->actual_height,
895 pw, ph,
896 graphic->max_width,
897 graphic->max_height));
898
899 TRACE(("refresh pixmap starts at %d,%d\n", refresh_x, refresh_y));
900
901 for (r = 0, pmy = graph_y; r < graph_h; r++, pmy += ph) {
902 int pmx, buffer_y, pixel_y;
903
904 if (pmy + ph - 1 < draw_y)
905 continue;
906 if (pmy > draw_y + draw_h - 1)
907 break;
908
909 if (pmy < draw_y ||
910 pmy < refresh_y ||
911 pmy > refresh_y + refresh_h - 1) {
912 if_TRACE(out_of_range++);
913 continue;
914 }
915 pixel_y = r * mw;
916 buffer_y = (pmy - refresh_y) * refresh_w;
917
918 for (c = 0, pmx = graph_x; c < graph_w; c++, pmx += pw) {
919
920 if (pmx + pw - 1 < draw_x)
921 continue;
922 if (pmx > draw_x + draw_w - 1)
923 break;
924
925 if (pmx < draw_x ||
926 pmx < refresh_x ||
927 pmx > refresh_x + refresh_w - 1) {
928 if_TRACE(out_of_range++);
929 continue;
930 }
931
932 if_TRACE(total++);
933 regnum = graphic->pixels[pixel_y + c];
934 if (regnum == COLOR_HOLE) {
935 if_TRACE(holes++);
936 } else {
937 buffer[buffer_y + (pmx - refresh_x)] =
938 graphic->color_registers[regnum];
939 }
940 }
941 }
942
943 TRACE(("done refreshing graphic: %d of %d refreshed pixels were holes; %d were out of pixmap range\n",
944 holes, total, out_of_range));
945 }
946
947 /*
948 * Primary color hues:
949 * blue: 0 degrees
950 * red: 120 degrees
951 * green: 240 degrees
952 */
953 void
954 hls2rgb(int h, int l, int s, short *r, short *g, short *b)
955 {
956 const int hs = ((h + 240) / 60) % 6;
957 const double lv = l / 100.0;
958 const double sv = s / 100.0;
959 double c, x, m, c2;
960 double r1, g1, b1;
961
962 if (s == 0) {
963 *r = *g = *b = (short) l;
964 return;
965 }
966
967 c2 = (2.0 * lv) - 1.0;
968 if (c2 < 0.0)
969 c2 = -c2;
970 c = (1.0 - c2) * sv;
971 x = (hs & 1) ? c : 0.0;
972 m = lv - 0.5 * c;
973
974 switch (hs) {
975 case 0:
976 r1 = c;
977 g1 = x;
978 b1 = 0.0;
979 break;
980 case 1:
981 r1 = x;
982 g1 = c;
983 b1 = 0.0;
984 break;
985 case 2:
986 r1 = 0.0;
987 g1 = c;
988 b1 = x;
989 break;
990 case 3:
991 r1 = 0.0;
992 g1 = x;
993 b1 = c;
994 break;
995 case 4:
996 r1 = x;
997 g1 = 0.0;
998 b1 = c;
999 break;
1000 case 5:
1001 r1 = c;
1002 g1 = 0.0;
1003 b1 = x;
1004 break;
1005 default:
1006 TRACE(("Bad HLS input: [%d,%d,%d], returning white\n", h, l, s));
1007 *r = (short) 100;
1008 *g = (short) 100;
1009 *b = (short) 100;
1010 return;
1011 }
1012
1013 *r = (short) ((r1 + m) * 100.0 + 0.5);
1014 *g = (short) ((g1 + m) * 100.0 + 0.5);
1015 *b = (short) ((b1 + m) * 100.0 + 0.5);
1016
1017 if (*r < 0)
1018 *r = 0;
1019 else if (*r > 100)
1020 *r = 100;
1021 if (*g < 0)
1022 *g = 0;
1023 else if (*g > 100)
1024 *g = 100;
1025 if (*b < 0)
1026 *b = 0;
1027 else if (*b > 100)
1028 *b = 100;
1029 }
1030
1031 void
1032 dump_graphic(Graphic const *graphic)
1033 {
1034 #if defined(DUMP_COLORS) || defined(DUMP_BITMAP)
1035 RegisterNum color;
1036 #endif
1037 #ifdef DUMP_BITMAP
1038 int r, c;
1039 ColorRegister const *reg;
1040 #endif
1041
1042 (void) graphic;
1043
1044 TRACE(("graphic stats: id=%u charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n",
1045 graphic->id,
1046 graphic->charrow,
1047 graphic->charcol,
1048 graphic->actual_width,
1049 graphic->actual_height,
1050 graphic->pixw,
1051 graphic->pixh));
1052
1053 #ifdef DUMP_COLORS
1054 TRACE(("graphic colors:\n"));
1055 for (color = 0; color < graphic->valid_registers; color++) {
1056 TRACE(("%03u: %d,%d,%d\n",
1057 color,
1058 graphic->color_registers[color].r,
1059 graphic->color_registers[color].g,
1060 graphic->color_registers[color].b));
1061 }
1062 #endif
1063
1064 #ifdef DUMP_BITMAP
1065 TRACE(("graphic pixels:\n"));
1066 for (r = 0; r < graphic->actual_height; r++) {
1067 for (c = 0; c < graphic->actual_width; c++) {
1068 color = graphic->pixels[r * graphic->max_width + c];
1069 if (color == COLOR_HOLE) {
1070 TRACE(("?"));
1071 } else {
1072 reg = &graphic->color_registers[color];
1073 if (reg->r + reg->g + reg->b > 200) {
1074 TRACE(("#"));
1075 } else if (reg->r + reg->g + reg->b > 150) {
1076 TRACE(("%%"));
1077 } else if (reg->r + reg->g + reg->b > 100) {
1078 TRACE((":"));
1079 } else if (reg->r + reg->g + reg->b > 80) {
1080 TRACE(("."));
1081 } else {
1082 TRACE((" "));
1083 }
1084 }
1085 }
1086 TRACE(("\n"));
1087 }
1088
1089 TRACE(("\n"));
1090 #endif
1091 }
1092
1093 /* Erase the portion of any displayed graphic overlapping with a rectangle
1094 * of the given size and location in pixels relative to the start of the
1095 * graphic. This is used to allow text to "erase" graphics underneath it.
1096 */
1097 static void
1098 erase_graphic(Graphic *graphic, int x, int y, int w, int h)
1099 {
1100 const int pw = graphic->pixw;
1101 const int ph = graphic->pixh;
1102 const int r_min = y - ph + 1;
1103 const int r_max = y + h - 1;
1104 const int c_min = x - pw + 1;
1105 const int c_max = x + w - 1;
1106
1107 int r;
1108 int rbase = 0;
1109
1110 TRACE(("erasing graphic %d,%d %dx%d\n", x, y, w, h));
1111
1112 for (r = 0; r < graphic->actual_height; r++) {
1113 if (rbase >= r_min
1114 && rbase <= r_max) {
1115 int c;
1116 int cbase = 0;
1117 for (c = 0; c < graphic->actual_width; c++) {
1118 if (cbase >= c_min
1119 && cbase <= c_max) {
1120 const int cell = r * graphic->max_width + c;
1121 ClrSpixel(graphic, cell);
1122 }
1123 cbase += pw;
1124 }
1125 }
1126 rbase += ph;
1127 }
1128 }
1129
1130 static int
1131 compare_graphic_ids(const void *left, const void *right)
1132 {
1133 const Graphic *l = *(const Graphic *const *) left;
1134 const Graphic *r = *(const Graphic *const *) right;
1135
1136 if (!l->valid || !r->valid)
1137 return 0;
1138
1139 if (l->bufferid < r->bufferid)
1140 return -1;
1141 else if (l->bufferid > r->bufferid)
1142 return 1;
1143
1144 if (l->id < r->id)
1145 return -1;
1146 else
1147 return 1;
1148 }
1149
1150 static void
1151 clip_area(int *orig_x, int *orig_y, int *orig_w, int *orig_h,
1152 int clip_x, int clip_y, int clip_w, int clip_h)
1153 {
1154 if (*orig_x < clip_x) {
1155 const int diff = clip_x - *orig_x;
1156 *orig_x += diff;
1157 *orig_w -= diff;
1158 }
1159 if (*orig_w > 0 && *orig_x + *orig_w > clip_x + clip_w) {
1160 *orig_w -= (*orig_x + *orig_w) - (clip_x + clip_w);
1161 }
1162
1163 if (*orig_y < clip_y) {
1164 const int diff = clip_y - *orig_y;
1165 *orig_y += diff;
1166 *orig_h -= diff;
1167 }
1168 if (*orig_h > 0 && *orig_y + *orig_h > clip_y + clip_h) {
1169 *orig_h -= (*orig_y + *orig_h) - (clip_y + clip_h);
1170 }
1171 }
1172
1173 static Bool
1174 GetGraphicsOrder(TScreen *screen,
1175 int skip_clean,
1176 Graphic *ordered_graphics[MAX_GRAPHICS],
1177 unsigned *resultp)
1178 {
1179 unsigned ii;
1180 unsigned active_count;
1181
1182 *resultp = active_count = 0;
1183 FOR_EACH_SLOT(ii) {
1184 Graphic *graphic;
1185 if (!(graphic = getActiveSlot(ii)))
1186 continue;
1187 TRACE(("refreshing graphic %d on buffer %d, current buffer %d\n",
1188 graphic->id, graphic->bufferid, screen->whichBuf));
1189 if (screen->whichBuf == 0) {
1190 if (graphic->bufferid != 0) {
1191 TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d\n",
1192 graphic->id, graphic->bufferid, screen->whichBuf));
1193 continue;
1194 }
1195 } else {
1196 if (graphic->bufferid == 0 && graphic->charrow >= 0) {
1197 TRACE(("skipping graphic %d from normal buffer (%d) when drawing screen=%d because it is not in scrollback area\n",
1198 graphic->id, graphic->bufferid, screen->whichBuf));
1199 continue;
1200 }
1201 if (graphic->bufferid == 1 &&
1202 graphic->charrow + (graphic->actual_height +
1203 FontHeight(screen) - 1) /
1204 FontHeight(screen) < 0) {
1205 TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d because it is completely in scrollback area\n",
1206 graphic->id, graphic->bufferid, screen->whichBuf));
1207 continue;
1208 }
1209 }
1210 if (graphic->hidden)
1211 continue;
1212 ordered_graphics[active_count++] = graphic;
1213 }
1214
1215 if (active_count == 0)
1216 return False;
1217 if (active_count > 1) {
1218 qsort(ordered_graphics,
1219 (size_t) active_count,
1220 sizeof(ordered_graphics[0]),
1221 compare_graphic_ids);
1222 }
1223
1224 if (skip_clean) {
1225 unsigned jj;
1226 unsigned skip_count;
1227
1228 for (jj = 0; jj < active_count; ++jj) {
1229 if (ordered_graphics[jj]->dirty)
1230 break;
1231 }
1232 skip_count = jj;
1233 if (skip_count == active_count)
1234 return False;
1235
1236 active_count -= skip_count;
1237 for (jj = 0; jj < active_count; ++jj) {
1238 ordered_graphics[jj] = ordered_graphics[jj + skip_count];
1239 }
1240 }
1241 *resultp = active_count;
1242 return True;
1243 }
1244
1245 static ColorRegister *
1246 AllocGraphicsBuffer(TScreen *screen,
1247 int ncols,
1248 int nrows)
1249 {
1250 int xx, yy;
1251 int const refresh_w = ncols * FontWidth(screen);
1252 int const refresh_h = nrows * FontHeight(screen);
1253 ColorRegister *buffer;
1254
1255 if (!(buffer = TypeMallocN(ColorRegister,
1256 (unsigned) refresh_w * (unsigned) refresh_h))) {
1257 TRACE(("unable to allocate %dx%d buffer for graphics refresh\n",
1258 refresh_w, refresh_h));
1259 } else {
1260 /* assuming two's complement, the memset will be much faster than loop */
1261 if ((unsigned short) null_color.r == 0xffff) {
1262 memset(buffer, 0xff,
1263 sizeof(ColorRegister) * (size_t) (refresh_h * refresh_w));
1264 } else {
1265 for (yy = 0; yy < refresh_h; yy++) {
1266 for (xx = 0; xx < refresh_w; xx++) {
1267 buffer[yy * refresh_w + xx] = null_color;
1268 }
1269 }
1270 }
1271 }
1272 return buffer;
1273 }
1274
1275 typedef struct {
1276 int x_min;
1277 int x_max;
1278 int y_min;
1279 int y_max;
1280 } ClipLimits;
1281
1282 static Boolean
1283 RefreshClipped(TScreen *screen,
1284 int leftcol,
1285 int toprow,
1286 int ncols,
1287 int nrows,
1288 Graphic *ordered_graphics[MAX_GRAPHICS],
1289 unsigned active_count,
1290 ColorRegister *buffer,
1291 ClipLimits * result)
1292 {
1293 int const scroll_y = screen->topline * FontHeight(screen);
1294 int const refresh_x = leftcol * FontWidth(screen);
1295 int const refresh_y = toprow * FontHeight(screen) + scroll_y;
1296 int const refresh_w = ncols * FontWidth(screen);
1297 int const refresh_h = nrows * FontHeight(screen);
1298 ClipLimits my_limits;
1299 unsigned jj;
1300
1301 int const altarea_x = 0;
1302 int const altarea_y = 0;
1303 int const altarea_w = Width(screen) * FontWidth(screen);
1304 int const altarea_h = Height(screen) * FontHeight(screen);
1305
1306 int const scrollarea_x = 0;
1307 int const scrollarea_y = scroll_y;
1308 int const scrollarea_w = Width(screen) * FontWidth(screen);
1309 int const scrollarea_h = -scroll_y;
1310
1311 int const mainarea_x = 0;
1312 int const mainarea_y = scroll_y;
1313 int const mainarea_w = Width(screen) * FontWidth(screen);
1314 int const mainarea_h = -scroll_y + Height(screen) * FontHeight(screen);
1315
1316 my_limits.x_min = refresh_x + refresh_w;
1317 my_limits.x_max = refresh_x - 1;
1318 my_limits.y_min = refresh_y + refresh_h;
1319 my_limits.y_max = refresh_y - 1;
1320 for (jj = 0; jj < active_count; ++jj) {
1321 Graphic *graphic = ordered_graphics[jj];
1322 int draw_x = graphic->charcol * FontWidth(screen);
1323 int draw_y = graphic->charrow * FontHeight(screen);
1324 int draw_w = graphic->actual_width;
1325 int draw_h = graphic->actual_height;
1326
1327 if (screen->whichBuf != 0) {
1328 if (graphic->bufferid != 0) {
1329 /* clip to alt buffer */
1330 clip_area(&draw_x, &draw_y, &draw_w, &draw_h,
1331 altarea_x, altarea_y, altarea_w, altarea_h);
1332 } else {
1333 /* clip to scrollback area */
1334 clip_area(&draw_x, &draw_y, &draw_w, &draw_h,
1335 scrollarea_x, scrollarea_y,
1336 scrollarea_w, scrollarea_h);
1337 }
1338 } else {
1339 /* clip to scrollback + normal area */
1340 clip_area(&draw_x, &draw_y, &draw_w, &draw_h,
1341 mainarea_x, mainarea_y,
1342 mainarea_w, mainarea_h);
1343 }
1344
1345 clip_area(&draw_x, &draw_y, &draw_w, &draw_h,
1346 refresh_x, refresh_y, refresh_w, refresh_h);
1347
1348 TRACE(("refresh: graph=%u\n", jj));
1349 TRACE((" refresh_x=%d refresh_y=%d refresh_w=%d refresh_h=%d\n",
1350 refresh_x, refresh_y, refresh_w, refresh_h));
1351 TRACE((" draw_x=%d draw_y=%d draw_w=%d draw_h=%d\n",
1352 draw_x, draw_y, draw_w, draw_h));
1353
1354 if (draw_w > 0 && draw_h > 0) {
1355 refresh_graphic(screen, graphic, buffer,
1356 refresh_x, refresh_y,
1357 refresh_w, refresh_h,
1358 draw_x, draw_y,
1359 draw_w, draw_h);
1360 if (draw_x < my_limits.x_min)
1361 my_limits.x_min = draw_x;
1362 if (draw_x + draw_w - 1 > my_limits.x_max)
1363 my_limits.x_max = draw_x + draw_w - 1;
1364 if (draw_y < my_limits.y_min)
1365 my_limits.y_min = draw_y;
1366 if (draw_y + draw_h - 1 > my_limits.y_max)
1367 my_limits.y_max = draw_y + draw_h - 1;
1368 }
1369 graphic->dirty = False;
1370 }
1371
1372 if (my_limits.x_max < refresh_x ||
1373 my_limits.x_min > refresh_x + refresh_w - 1 ||
1374 my_limits.y_max < refresh_y ||
1375 my_limits.y_min > refresh_y + refresh_h - 1) {
1376 return False;
1377 }
1378 *result = my_limits;
1379 return True;
1380 }
1381
1382 static Boolean
1383 FindGraphicHoles(int refresh_x,
1384 int refresh_y,
1385 int refresh_w,
1386 ColorRegister *buffer,
1387 ClipLimits * limits,
1388 unsigned *result)
1389 {
1390 const int y_min = limits->y_min - refresh_y;
1391 const int y_max = limits->y_max - refresh_y;
1392 const int x_min = limits->x_min - refresh_x;
1393 const int x_max = limits->x_max - refresh_x;
1394 const ColorRegister *base = buffer + (y_min * refresh_w);
1395 int xx, yy;
1396
1397 unsigned holes = 0U;
1398 unsigned non_holes = 0U;
1399
1400 for (yy = y_min; yy <= y_max; yy++) {
1401 const ColorRegister *scan = base + x_min;
1402 for (xx = x_min; xx <= x_max; xx++) {
1403 if (scan->r < 0 || scan->g < 0 || scan->b < 0) {
1404 holes++;
1405 } else {
1406 non_holes++;
1407 }
1408 if (non_holes && holes)
1409 goto finish;
1410 ++scan;
1411 }
1412 base += refresh_w;
1413 }
1414
1415 finish:
1416 *result = holes;
1417 return (non_holes != 0);
1418 }
1419
1420 /* the coordinates are relative to the screen */
1421 static void
1422 refresh_graphics(XtermWidget xw,
1423 int leftcol,
1424 int toprow,
1425 int ncols,
1426 int nrows,
1427 int skip_clean)
1428 {
1429 TScreen *const screen = TScreenOf(xw);
1430 Display *const display = screen->display;
1431 Window const drawable = VDrawable(screen);
1432 int const scroll_y = screen->topline * FontHeight(screen);
1433 int const refresh_x = leftcol * FontWidth(screen);
1434 int const refresh_y = toprow * FontHeight(screen) + scroll_y;
1435 int const refresh_w = ncols * FontWidth(screen);
1436
1437 Graphic *ordered_graphics[MAX_GRAPHICS];
1438 unsigned active_count;
1439 unsigned holes;
1440 int xx, yy;
1441
1442 ColorRegister *buffer;
1443 ClipLimits clip_limits;
1444
1445 if_TRACE(int const refresh_h = nrows * FontHeight(screen));
1446
1447 if (!GetGraphicsOrder(screen, skip_clean, ordered_graphics, &active_count))
1448 return;
1449
1450 if (!valid_graphics) {
1451 memset(&xgcv, 0, sizeof(xgcv));
1452 xgcv.graphics_exposures = False;
1453 graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv);
1454 last_color = null_color;
1455 gc_color = null_color;
1456 if (graphics_gc == None) {
1457 TRACE(("unable to allocate GC for graphics refresh\n"));
1458 valid_graphics = -1;
1459 } else {
1460 valid_graphics = 1;
1461 }
1462 }
1463 if (valid_graphics < 0)
1464 return;
1465
1466 if ((buffer = AllocGraphicsBuffer(screen, ncols, nrows)) == NULL)
1467 return;
1468
1469 TRACE(("refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d (%d,%d %dx%d)\n",
1470 screen->topline,
1471 leftcol, toprow,
1472 nrows, ncols,
1473 refresh_x, refresh_y,
1474 refresh_w, refresh_h));
1475
1476 if (!RefreshClipped(screen, leftcol, toprow, ncols, nrows,
1477 ordered_graphics,
1478 active_count,
1479 buffer,
1480 &clip_limits)) {
1481 free(buffer);
1482 return;
1483 }
1484
1485 if (!FindGraphicHoles(refresh_x,
1486 refresh_y,
1487 refresh_w,
1488 buffer,
1489 &clip_limits,
1490 &holes)) {
1491 TRACE(("refresh: visible graphics areas are erased; nothing to do\n"));
1492 free(buffer);
1493 return;
1494 }
1495
1496 /*
1497 * If we have any holes we can't just copy an image rectangle, and masking
1498 * with bitmaps is very expensive. This fallback is surprisingly faster
1499 * than the XPutImage version in some cases, but I don't know why.
1500 * (This is even though there's no X11 primitive for drawing a horizontal
1501 * line of height one and no attempt is made to handle multiple lines at
1502 * once.)
1503 */
1504 if (holes > 0U) {
1505 int run;
1506
1507 run = 0;
1508 for (yy = clip_limits.y_min - refresh_y;
1509 yy <= clip_limits.y_max - refresh_y;
1510 yy++) {
1511 for (xx = clip_limits.x_min - refresh_x;
1512 xx <= clip_limits.x_max - refresh_x;
1513 xx++) {
1514 const ColorRegister color = buffer[yy * refresh_w + xx];
1515
1516 if (color.r < 0 || color.g < 0 || color.b < 0) {
1517 last_color = color;
1518 if (run > 0) {
1519 XDrawLine(display, drawable, graphics_gc,
1520 OriginX(screen) + refresh_x + xx - run,
1521 (OriginY(screen) - scroll_y) + refresh_y + yy,
1522 OriginX(screen) + refresh_x + xx - 1,
1523 (OriginY(screen) - scroll_y) + refresh_y + yy);
1524 run = 0;
1525 }
1526 continue;
1527 }
1528
1529 if (DiffColor(color, last_color)) {
1530 last_color = color;
1531 if (run > 0) {
1532 XDrawLine(display, drawable, graphics_gc,
1533 OriginX(screen) + refresh_x + xx - run,
1534 (OriginY(screen) - scroll_y) + refresh_y + yy,
1535 OriginX(screen) + refresh_x + xx - 1,
1536 (OriginY(screen) - scroll_y) + refresh_y + yy);
1537 run = 0;
1538 }
1539
1540 if (DiffColor(color, gc_color)) {
1541 xgcv.foreground =
1542 color_register_to_xpixel(&color, xw);
1543 XChangeGC(display, graphics_gc, GCForeground, &xgcv);
1544 gc_color = color;
1545 }
1546 }
1547 run++;
1548 }
1549 if (run > 0) {
1550 last_color = null_color;
1551 XDrawLine(display, drawable, graphics_gc,
1552 OriginX(screen) + refresh_x + xx - run,
1553 (OriginY(screen) - scroll_y) + refresh_y + yy,
1554 OriginX(screen) + refresh_x + xx - 1,
1555 (OriginY(screen) - scroll_y) + refresh_y + yy);
1556 run = 0;
1557 }
1558 }
1559 } else {
1560 ColorRegister old_colors[2];
1561 Pixel fg, old_result[2];
1562 XImage *image;
1563 char *imgdata;
1564 const unsigned image_w = ((unsigned) clip_limits.x_max + 1U -
1565 (unsigned) clip_limits.x_min);
1566 const unsigned image_h = ((unsigned) clip_limits.y_max + 1U -
1567 (unsigned) clip_limits.y_min);
1568 int nn;
1569
1570 image = XCreateImage(display, xw->visInfo->visual,
1571 (unsigned) xw->visInfo->depth,
1572 ZPixmap, 0, NULL,
1573 image_w, image_h,
1574 (int) (sizeof(int) * 8U), 0);
1575 if (!image) {
1576 TRACE(("unable to allocate XImage for graphics refresh\n"));
1577 free(buffer);
1578 return;
1579 }
1580 imgdata = TypeMallocN(char, (size_t)(image_h * (unsigned)image->bytes_per_line));
1581 if (!imgdata) {
1582 TRACE(("unable to allocate XImage for graphics refresh\n"));
1583 XDestroyImage(image);
1584 free(buffer);
1585 return;
1586 }
1587 image->data = imgdata;
1588
1589 fg = 0U;
1590 nn = 0;
1591
1592 /* two-level cache cuts down on lookup-calls */
1593 old_result[0] = 0U;
1594 old_result[1] = 0U;
1595 old_colors[0] = null_color;
1596 old_colors[1] = null_color;
1597
1598 for (yy = clip_limits.y_min - refresh_y;
1599 yy <= clip_limits.y_max - refresh_y;
1600 yy++) {
1601 for (xx = clip_limits.x_min - refresh_x;
1602 xx <= clip_limits.x_max - refresh_x;
1603 xx++) {
1604 const ColorRegister color = buffer[yy * refresh_w + xx];
1605
1606 if (DiffColor(color, old_colors[nn])) {
1607 if (DiffColor(color, old_colors[!nn])) {
1608 nn = !nn;
1609 fg = color_register_to_xpixel(&color, xw);
1610 old_result[nn] = fg;
1611 old_colors[nn] = color;
1612 } else {
1613 nn = !nn;
1614 fg = old_result[nn];
1615 }
1616 }
1617
1618 XPutPixel(image,
1619 xx + refresh_x - clip_limits.x_min,
1620 yy + refresh_y - clip_limits.y_min, fg);
1621 }
1622 }
1623
1624 XPutImage(display, drawable, graphics_gc, image,
1625 0, 0,
1626 OriginX(screen) + clip_limits.x_min,
1627 (OriginY(screen) - scroll_y) + clip_limits.y_min,
1628 image_w, image_h);
1629 free(imgdata);
1630 image->data = NULL;
1631 XDestroyImage(image);
1632 }
1633
1634 free(buffer);
1635 XFlush(display);
1636 }
1637
1638 void
1639 refresh_displayed_graphics(XtermWidget xw,
1640 int leftcol,
1641 int toprow,
1642 int ncols,
1643 int nrows)
1644 {
1645 refresh_graphics(xw, leftcol, toprow, ncols, nrows, 0);
1646 }
1647
1648 void
1649 refresh_modified_displayed_graphics(XtermWidget xw)
1650 {
1651 TScreen const *screen = TScreenOf(xw);
1652 refresh_graphics(xw, 0, 0, MaxCols(screen), MaxRows(screen), 1);
1653 }
1654
1655 void
1656 scroll_displayed_graphics(XtermWidget xw, int rows)
1657 {
1658 if (used_graphics) {
1659 TScreen const *screen = TScreenOf(xw);
1660 unsigned ii;
1661
1662 TRACE(("graphics scroll: moving all up %d rows\n", rows));
1663 /* FIXME: VT125 ReGIS graphics are fixed at the upper left of the display; need to verify */
1664
1665 FOR_EACH_SLOT(ii) {
1666 Graphic *graphic;
1667
1668 if (!(graphic = getActiveSlot(ii)))
1669 continue;
1670 if (graphic->bufferid != screen->whichBuf)
1671 continue;
1672 if (graphic->hidden)
1673 continue;
1674
1675 graphic->charrow -= rows;
1676 }
1677 }
1678 }
1679
1680 void
1681 pixelarea_clear_displayed_graphics(TScreen const *screen,
1682 int winx,
1683 int winy,
1684 int w,
1685 int h)
1686 {
1687 unsigned ii;
1688
1689 if (!used_graphics)
1690 return;
1691
1692 FOR_EACH_SLOT(ii) {
1693 Graphic *graphic;
1694 /* FIXME: are these coordinates (scrolled) screen-relative? */
1695 int const scroll_y = (screen->whichBuf == 0
1696 ? screen->topline * FontHeight(screen)
1697 : 0);
1698 int graph_x;
1699 int graph_y;
1700 int x, y;
1701
1702 if (!(graphic = getActiveSlot(ii)))
1703 continue;
1704 if (graphic->bufferid != screen->whichBuf)
1705 continue;
1706 if (graphic->hidden)
1707 continue;
1708
1709 graph_x = graphic->charcol * FontWidth(screen);
1710 graph_y = graphic->charrow * FontHeight(screen);
1711 x = winx - graph_x;
1712 y = (winy - scroll_y) - graph_y;
1713
1714 TRACE(("pixelarea clear graphics: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n",
1715 screen->topline,
1716 winx, winy,
1717 w, h,
1718 x, y));
1719 erase_graphic(graphic, x, y, w, h);
1720 }
1721 }
1722
1723 void
1724 chararea_clear_displayed_graphics(TScreen const *screen,
1725 int leftcol,
1726 int toprow,
1727 int ncols,
1728 int nrows)
1729 {
1730 if (used_graphics) {
1731 int const x = leftcol * FontWidth(screen);
1732 int const y = toprow * FontHeight(screen);
1733 int const w = ncols * FontWidth(screen);
1734 int const h = nrows * FontHeight(screen);
1735
1736 TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n",
1737 screen->topline,
1738 leftcol, toprow,
1739 nrows, ncols,
1740 x, y, w, h));
1741 pixelarea_clear_displayed_graphics(screen, x, y, w, h);
1742 }
1743 }
1744
1745 void
1746 reset_displayed_graphics(TScreen const *screen)
1747 {
1748 init_color_registers(getSharedRegisters(), GraphicsTermId(screen));
1749
1750 if (used_graphics) {
1751 unsigned ii;
1752
1753 TRACE(("resetting all graphics\n"));
1754 FOR_EACH_SLOT(ii) {
1755 deactivateSlot(ii);
1756 }
1757 }
1758 }
1759
1760 #ifdef NO_LEAKS
1761 void
1762 noleaks_graphics(Display *dpy)
1763 {
1764 unsigned ii;
1765
1766 FOR_EACH_SLOT(ii) {
1767 deactivateSlot(ii);
1768 }
1769 if (valid_graphics > 0)
1770 XFreeGC(dpy, graphics_gc);
1771 }
1772 #endif