"Fossies" - the Fresh Open Source Software Archive 
Member "xterm-379/graphics_regis.c" (16 May 2022, 224306 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_regis.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_regis.c,v 1.130 2022/05/16 23:31:18 tom Exp $ */
2
3 /*
4 * Copyright 2014-2021,2022 by Ross Combs
5 * Copyright 2014-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 <math.h>
39 #include <stdlib.h>
40
41 #include <fontutils.h>
42 #include <ptyx.h>
43
44 #include <assert.h>
45 #include <graphics.h>
46 #include <graphics_regis.h>
47
48 /* get rid of shadowing warnings (we will not draw Bessel functions) */
49 #define y1 my_y1
50 #define y0 my_y0
51
52 #define SCALE_FIXED_POINT 16U
53
54 #undef DEBUG_PARSING
55 #undef DEBUG_ALPHABET_LOOKUP
56 #undef DEBUG_ALPHABETS
57 #undef DEBUG_BEZIER
58 #undef DEBUG_SPLINE_SEGMENTS
59 #undef DEBUG_SPLINE_POINTS
60 #undef DEBUG_SPLINE_WITH_ROTATION
61 #undef DEBUG_SPLINE_WITH_OVERDRAW
62 #undef DEBUG_ARC_POINTS
63 #undef DEBUG_ARC_CENTER
64 #undef DEBUG_ARC_START
65 #undef DEBUG_ARC_END
66 #undef DEBUG_SPECIFIC_CHAR_METRICS
67 #define IS_DEBUG_CHAR(CH) ((CH) == 'W') /* glyphs to dump to terminal */
68 #undef DEBUG_COMPUTED_FONT_METRICS
69 #undef DEBUG_FONT_NAME
70 #undef DEBUG_FONT_SIZE_SEARCH
71 #undef DEBUG_XFT_GLYPH
72 #undef DEBUG_GLYPH_RETRIEVAL
73 #undef DEBUG_XFT_GLYPH_LOADING
74 #undef DEBUG_LOAD
75
76 /* controls for extensions over VT3x0 limitations */
77 #define ENABLE_RGB_COLORSPECS
78 #undef ENABLE_FREE_ROTATION
79 #undef ENABLE_DISTORTIONLESS_ROTATION
80 #define ENABLE_UPLOAD_ALPHABET_FROM_FONT
81 #define ENABLE_UPLOAD_ALPHABET_ZERO
82 #define ENABLE_USER_FONT_SIZE
83 #define ENABLE_VARIABLE_ITALICS
84
85 #define MIN_ITERATIONS_BEFORE_REFRESH 10U
86 #define MIN_MS_BEFORE_REFRESH 33
87 /* *INDENT-OFF* */
88 typedef struct RegisPoint {
89 int x, y;
90 } RegisPoint;
91
92 typedef struct RegisWriteControls {
93 unsigned pv_multiplier;
94 unsigned pattern;
95 unsigned pattern_multiplier;
96 unsigned invert_pattern;
97 unsigned plane_mask;
98 unsigned write_style;
99 RegisterNum foreground;
100 unsigned shading_enabled;
101 char shading_character;
102 int shading_reference;
103 unsigned shading_reference_dim;
104 unsigned line_width;
105 } RegisWriteControls;
106
107 typedef struct RegisTextControls {
108 unsigned alphabet_num;
109 unsigned character_set_l; /* default: "(B" (ASCII) */
110 unsigned character_set_r; /* default: "-@" (Latin-1) */
111 unsigned character_display_w;
112 unsigned character_display_h;
113 unsigned character_unit_cell_w;
114 unsigned character_unit_cell_h;
115 int character_inc_x;
116 int character_inc_y;
117 int string_rotation;
118 int character_rotation;
119 int slant; /* for italic/oblique */
120 } RegisTextControls;
121
122 #define FixedCopy(dst, src, len) strncpy(dst, src, len - 1)[len - 1] = '\0'
123 #define CopyFontname(dst, src) FixedCopy(dst, src, (size_t) REGIS_FONTNAME_LEN)
124
125 #define MAX_REGIS_PAGES 8U
126
127 #define MAX_REGIS_ALPHABETS 8U
128 #define REGIS_ALPHABET_NAME_LEN 11U
129 #define REGIS_FONTNAME_LEN 256U
130 /* enough for a 16x24 font (about 100KB) */
131 #define MAX_REGIS_ALPHABET_BYTES (256U * 16U * 24U)
132 #define MAX_GLYPH_PIXELS 8192U
133 #define MAX_GLYPHS 256U
134 #define INVALID_ALPHABET_NUM ~0U
135
136 typedef struct RegisAlphabet {
137 unsigned alphabet_num;
138 unsigned pixw, pixh;
139 char name[REGIS_ALPHABET_NAME_LEN];
140 char fontname[REGIS_FONTNAME_LEN];
141 int use_font;
142 int loaded[MAX_GLYPHS];
143 Char *bytes;
144 } RegisAlphabet;
145
146 typedef struct RegisDataFragment {
147 char const *start;
148 unsigned pos;
149 unsigned len;
150 } RegisDataFragment;
151 /* *INDENT-ON* */
152
153 #define POSITION_STACK_SIZE 16U
154 #define DUMMY_STACK_X -32768
155 #define DUMMY_STACK_Y -32768
156
157 #define CURVE_POSITION_ARC_EDGE 0U
158 #define CURVE_POSITION_ARC_CENTER 1U
159 #define CURVE_POSITION_OPEN_CURVE 2U
160 #define CURVE_POSITION_CLOSED_CURVE 3U
161
162 #define MAX_INPUT_CURVE_POINTS 16U
163 #define MAX_CURVE_POINTS (MAX_INPUT_CURVE_POINTS + 4U)
164
165 #define MAX_FILL_POINTS 2048U
166
167 typedef struct RegisParseState {
168 RegisDataFragment input;
169 char *temp;
170 unsigned templen;
171 char command;
172 char option;
173 /* position stack */
174 int stack_x[POSITION_STACK_SIZE];
175 int stack_y[POSITION_STACK_SIZE];
176 unsigned stack_next; /* next empty position */
177 /* curve options */
178 int curve_mode;
179 int arclen;
180 int x_points[MAX_CURVE_POINTS];
181 int y_points[MAX_CURVE_POINTS];
182 unsigned num_points;
183 /* load options */
184 char load_name[REGIS_ALPHABET_NAME_LEN];
185 unsigned load_alphabet;
186 unsigned load_w, load_h;
187 unsigned load_index;
188 unsigned load_glyph;
189 unsigned load_row;
190 /* text options */
191 unsigned text_tilt_state;
192 } RegisParseState;
193
194 #define TEXT_TILT_STATE_READY 0U
195 #define TEXT_TILT_STATE_GOT_D 1U
196 #define TEXT_TILT_STATE_GOT_DS 2U
197 #define TEXT_TILT_STATE_GOT_DSD 3U
198
199 typedef struct RegisGraphicsContext {
200 XtermWidget current_widget;
201 Graphic *destination_graphic;
202 Graphic *display_graphic;
203 int graphics_termid;
204 int x_off, y_off;
205 int x_div, y_div;
206 int width, height;
207 unsigned all_planes;
208 RegisterNum background;
209 char const *builtin_font;
210 RegisAlphabet alphabets[MAX_REGIS_ALPHABETS];
211 RegisWriteControls persistent_write_controls;
212 RegisWriteControls temporary_write_controls;
213 RegisTextControls persistent_text_controls;
214 RegisTextControls temporary_text_controls;
215 RegisTextControls *current_text_controls;
216 int multi_input_mode;
217 int graphics_output_cursor_x;
218 int graphics_output_cursor_y;
219 unsigned pattern_count;
220 unsigned pattern_bit;
221 int fill_mode;
222 RegisPoint fill_points[MAX_FILL_POINTS];
223 unsigned fill_point_count;
224 unsigned destination_page;
225 unsigned display_page;
226 Boolean force_refresh;
227 } RegisGraphicsContext;
228
229 static RegisGraphicsContext persistent_context;
230 static RegisParseState persistent_state;
231
232 #define MAX_PATTERN_BITS 8U
233
234 #define WRITE_STYLE_OVERLAY 1U
235 #define WRITE_STYLE_REPLACE 2U
236 #define WRITE_STYLE_COMPLEMENT 3U
237 #define WRITE_STYLE_ERASE 4U
238
239 #define WRITE_SHADING_REF_Y 0U
240 #define WRITE_SHADING_REF_X 1U
241 #define WRITE_SHADING_REF_NONE 2U
242
243 /* keypress event example: http://iraf.net/forum/viewtopic.php?showtopic=61692 */
244
245 #define MIN2(X, Y) ( (X) < (Y) ? (X) : (Y) )
246 #define MIN3(X, Y, Z) ( MIN2(MIN2((X), (Y)), MIN2((Y), (Z))) )
247 #define MAX2(X, Y) ( (X) > (Y) ? (X) : (Y) )
248 #define MAX3(X, Y, Z) ( MAX2(MAX2((X), (Y)), MAX2((Y), (Z))) )
249
250 #define ROT_LEFT_N(V, N) ( (((V) << ((N) & 3U )) & 255U) | \
251 ((V) >> (8U - ((N) & 3U))) )
252 #define ROT_LEFT(V) ( (((V) << 1U) & 255U) | ((V) >> 7U) )
253
254 /* convert user coordinates to absolute pixel coordinates */
255 #define SCALE_XCOORD(C, X, S) ( ( (X) * ((C)->width - 1) ) / ( (C)->x_div * (S) ) )
256 #define SCALE_YCOORD(C, Y, S) ( ( (Y) * ((C)->height - 1) ) / ( (C)->y_div * (S) ) )
257 #define TRANSLATE_XCOORD(C, X, S) SCALE_XCOORD((C), (X) - (C)->x_off * (S), (S) )
258 #define TRANSLATE_YCOORD(C, Y, S) SCALE_YCOORD((C), (Y) - (C)->y_off * (S), (S) )
259
260 #if 0
261 /* convert absolute pixel coordinate to user coordinates */
262 #define SCALE_XPIX(C, X, S) ( ( (X) * ((C)->x_div * (S) ) ) / ((C)->width - 1) )
263 #define SCALE_YPIX(C, Y, S) ( ( (Y) * ((C)->y_div * (S) ) ) / ((C)->height - 1) )
264 #define TRANSLATE_XPIX(C, X, S) ( SCALE_XPIX((C), (X), (S) ) + (C)->x_off * (S) )
265 #define TRANSLATE_YPIX(C, Y, S) ( SCALE_YPIX((C), (Y), (S) ) + (C)->y_off * (S) )
266 #endif
267
268 #define READ_PIXEL(C, X, Y) read_pixel((C)->destination_graphic, (X), (Y))
269 #define DRAW_PIXEL(C, X, Y, COL) draw_solid_pixel((C)->destination_graphic, (X), (Y), (COL))
270 #define DRAW_ALL(C, COL) \
271 draw_solid_rectangle((C)->destination_graphic, 0, 0, (C)->width, (C)->height, (COL))
272
273 static unsigned get_shade_character_pixel(Char const *pixels,
274 unsigned w, unsigned h,
275 unsigned smaxf, unsigned scale,
276 int slant_dx, int px, int py);
277 static void get_bitmap_of_character(RegisGraphicsContext const *context,
278 int ch, unsigned maxw, unsigned maxh,
279 Char *pixels,
280 unsigned *w, unsigned *h,
281 unsigned max_pixels);
282
283 static void
284 init_regis_load_state(RegisParseState *state)
285 {
286 state->load_index = MAX_REGIS_ALPHABETS;
287 state->load_w = 8U;
288 state->load_h = 10U;
289 state->load_alphabet = 1U; /* FIXME: is this the correct default */
290 state->load_name[0] = '\0';
291 state->load_glyph = (unsigned) (Char) '\0';
292 state->load_row = 0U;
293 }
294
295 static void
296 init_regis_parse_state(RegisParseState *state)
297 {
298 state->command = '_';
299 state->option = '_';
300 state->stack_next = 0U;
301 state->load_index = MAX_REGIS_ALPHABETS;
302 init_regis_load_state(state);
303 }
304
305 static int
306 ifloor(double d)
307 {
308 double dl = floor(d);
309 return (int) dl;
310 }
311
312 static int
313 isqrt(double d)
314 {
315 double dl = sqrt(d);
316 return (int) dl;
317 }
318
319 static void
320 draw_regis_pixel(RegisGraphicsContext *context, int x, int y,
321 unsigned value)
322 {
323 unsigned color = 0;
324
325 switch (context->temporary_write_controls.write_style) {
326 case WRITE_STYLE_OVERLAY:
327 /*
328 * Update pixels with foreground when pattern is 1,
329 * don't change when pattern is 0.
330 */
331 if (!value) {
332 return;
333 }
334
335 if (context->temporary_write_controls.invert_pattern) {
336 color = context->background;
337 } else {
338 color = context->temporary_write_controls.foreground;
339 }
340 break;
341
342 case WRITE_STYLE_REPLACE:
343 /*
344 * Update pixels with foreground when pattern is 1,
345 * set to background when pattern is 0.
346 */
347 {
348 unsigned fg, bg;
349
350 if (context->temporary_write_controls.invert_pattern) {
351 fg = context->background;
352 bg = context->temporary_write_controls.foreground;
353 } else {
354 fg = context->temporary_write_controls.foreground;
355 bg = context->background;
356 }
357 color = value ? fg : bg;
358 }
359 break;
360
361 case WRITE_STYLE_COMPLEMENT:
362 /*
363 * Update pixels with background when pattern is 1,
364 * don't change when pattern is 0.
365 */
366 if (!value) {
367 return;
368 }
369
370 color = READ_PIXEL(context, x, y);
371 if (color == COLOR_HOLE)
372 color = context->background;
373 color = color ^ context->all_planes;
374 break;
375
376 case WRITE_STYLE_ERASE:
377 /* Update pixels to foreground. */
378 if (context->temporary_write_controls.invert_pattern) {
379 color = context->temporary_write_controls.foreground;
380 } else {
381 color = context->background;
382 }
383 break;
384 }
385
386 if (context->temporary_write_controls.plane_mask != context->all_planes) {
387 unsigned old_color = READ_PIXEL(context, x, y);
388 if (old_color == COLOR_HOLE)
389 old_color = context->background;
390 color = (color & context->temporary_write_controls.plane_mask) |
391 (old_color & ~context->temporary_write_controls.plane_mask);
392 }
393
394 DRAW_PIXEL(context, x, y, color);
395 }
396
397 static void
398 shade_pattern_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref,
399 int x, int y)
400 {
401 unsigned value;
402
403 if (dim == WRITE_SHADING_REF_X) {
404 int delta = x > ref ? 1 : -1;
405 int curr_x;
406
407 context->pattern_bit = 1U << (((unsigned) y) & 7U);
408 for (curr_x = ref; curr_x != x + delta; curr_x += delta) {
409 value = context->temporary_write_controls.pattern &
410 context->pattern_bit;
411 draw_regis_pixel(context, curr_x, y, value);
412 }
413 } else if (dim == WRITE_SHADING_REF_Y) {
414 int delta = y > ref ? 1 : -1;
415 int curr_y;
416
417 for (curr_y = ref; curr_y != y + delta; curr_y += delta) {
418 context->pattern_bit = 1U << (((unsigned) curr_y) & 7U);
419 value = context->temporary_write_controls.pattern &
420 context->pattern_bit;
421 draw_regis_pixel(context, x, curr_y, value);
422 }
423 } else {
424 TRACE(("ERROR: shading requested, but there is no reference axis\n"));
425 }
426 }
427
428 static void
429 shade_char_to_pixel(RegisGraphicsContext *context, Char const *pixels,
430 unsigned w, unsigned h, unsigned dim, int ref, int x, int y)
431 {
432 unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
433 unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
434 unsigned smaxf;
435 unsigned s;
436 unsigned scale;
437 unsigned value;
438
439 if (xmaxf > ymaxf) {
440 smaxf = ymaxf;
441 s = h;
442 } else {
443 smaxf = xmaxf;
444 s = w;
445 }
446 scale = (s << SCALE_FIXED_POINT) / smaxf;
447
448 if (dim == WRITE_SHADING_REF_X) {
449 int delta = x > ref ? 1 : -1;
450 int curr_x;
451
452 for (curr_x = ref; curr_x != x + delta; curr_x += delta) {
453 value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0,
454 curr_x, y);
455 draw_regis_pixel(context, curr_x, y, value);
456 }
457 } else if (dim == WRITE_SHADING_REF_Y) {
458 int delta = y > ref ? 1 : -1;
459 int curr_y;
460
461 for (curr_y = ref; curr_y != y + delta; curr_y += delta) {
462 value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0, x,
463 curr_y);
464 draw_regis_pixel(context, x, curr_y, value);
465 }
466 } else {
467 TRACE(("ERROR: shading requested, but there is no reference axis\n"));
468 }
469 }
470
471 static void
472 draw_patterned_pixel(RegisGraphicsContext *context, int x, int y)
473 {
474 if (context->pattern_count >=
475 context->temporary_write_controls.pattern_multiplier) {
476 context->pattern_count = 0U;
477 context->pattern_bit = ROT_LEFT(context->pattern_bit);
478 }
479 context->pattern_count++;
480
481 draw_regis_pixel(context, x, y,
482 context->temporary_write_controls.pattern &
483 context->pattern_bit);
484 }
485
486 static void
487 shade_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref,
488 int x, int y)
489 {
490 if (context->temporary_write_controls.shading_character != '\0') {
491 unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
492 unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
493 char ch = context->temporary_write_controls.shading_character;
494 Char pixels[MAX_GLYPH_PIXELS];
495 unsigned w, h;
496
497 get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
498 MAX_GLYPH_PIXELS);
499 if (w > 0 && h > 0) {
500 shade_char_to_pixel(context, pixels, w, h, dim, ref, x, y);
501 }
502 } else {
503 shade_pattern_to_pixel(context, dim, ref, x, y);
504 }
505 }
506
507 static void
508 draw_or_save_patterned_pixel(RegisGraphicsContext *context, int x, int y)
509 {
510 if (context->fill_mode == 1) {
511 if (context->fill_point_count >= MAX_FILL_POINTS) {
512 TRACE(("point %d,%d can not be added to filled polygon\n",
513 x, y));
514 return;
515 }
516 if (context->fill_point_count > 0U &&
517 context->fill_points[context->fill_point_count - 1U].x == x &&
518 context->fill_points[context->fill_point_count - 1U].y == y) {
519 return;
520 }
521 context->fill_points[context->fill_point_count].x = x;
522 context->fill_points[context->fill_point_count].y = y;
523 context->fill_point_count++;
524 return;
525 }
526
527 if (context->temporary_write_controls.shading_enabled) {
528 unsigned dim = context->temporary_write_controls.shading_reference_dim;
529 int ref = context->temporary_write_controls.shading_reference;
530
531 shade_to_pixel(context, dim, ref, x, y);
532 return;
533 }
534
535 draw_patterned_pixel(context, x, y);
536 }
537
538 static int
539 sort_points(void const *l, void const *r)
540 {
541 RegisPoint const *const lp = (RegisPoint const *)l;
542 RegisPoint const *const rp = (RegisPoint const *)r;
543
544 if (lp->y < rp->y)
545 return -1;
546 if (lp->y > rp->y)
547 return +1;
548 if (lp->x < rp->x)
549 return -1;
550 if (lp->x > rp->x)
551 return +1;
552 return 0;
553 }
554
555 static void
556 draw_shaded_polygon(RegisGraphicsContext *context)
557 {
558 unsigned p;
559 int old_x, old_y;
560 int inside;
561 Char pixels[MAX_GLYPH_PIXELS];
562 unsigned w = 1, h = 1;
563
564 char ch = context->temporary_write_controls.shading_character;
565 unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
566 unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
567
568 get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
569 MAX_GLYPH_PIXELS);
570 if (w < 1U || h < 1U) {
571 return;
572 }
573
574 qsort(context->fill_points, (size_t) context->fill_point_count,
575 sizeof(context->fill_points[0]), sort_points);
576
577 old_x = DUMMY_STACK_X;
578 old_y = DUMMY_STACK_Y;
579 inside = 0;
580 for (p = 0U; p < context->fill_point_count; p++) {
581 int new_x = context->fill_points[p].x;
582 int new_y = context->fill_points[p].y;
583 #if 0
584 printf("got %d,%d (%d,%d) inside=%d\n", new_x, new_y, old_x, old_y, inside);
585 #endif
586
587 /*
588 * FIXME: This is using pixels to represent lines which loses
589 * information about exact slope and how many lines are present which
590 * causes misbehavior with some inputs (especially complex polygons).
591 * It also takes more room than remembering vertices, but I'd rather
592 * not have to implement line segments for arcs. Maybe store a count
593 * at each vertex instead (doesn't fix the slope problem).
594 */
595 /*
596 * FIXME: Change this to only draw inside of polygons, and round
597 * points in a uniform direction to avoid overlapping drawing. As an
598 * option we could continue to support drawing the outline.
599 */
600 if (new_y != old_y) {
601 if (inside) {
602 /*
603 * Just draw the vertical line when there is not a matching
604 * edge on the right side.
605 */
606 shade_char_to_pixel(context, pixels, w, h,
607 WRITE_SHADING_REF_X,
608 old_x, old_x, old_y);
609 }
610 inside = 1;
611 } else {
612 if (inside) {
613 shade_char_to_pixel(context, pixels, w, h,
614 WRITE_SHADING_REF_X,
615 old_x, new_x, new_y);
616 }
617 if (new_x > old_x + 1) {
618 inside = !inside;
619 }
620 }
621
622 old_x = new_x;
623 old_y = new_y;
624 }
625
626 context->destination_graphic->dirty = True;
627 }
628
629 static void
630 draw_filled_polygon(RegisGraphicsContext *context)
631 {
632 unsigned p;
633 int old_x, old_y;
634 int inside;
635
636 qsort(context->fill_points, (size_t) context->fill_point_count,
637 sizeof(context->fill_points[0]), sort_points);
638
639 old_x = DUMMY_STACK_X;
640 old_y = DUMMY_STACK_Y;
641 inside = 0;
642 for (p = 0U; p < context->fill_point_count; p++) {
643 int new_x = context->fill_points[p].x;
644 int new_y = context->fill_points[p].y;
645 #if 0
646 printf("got %d,%d (%d,%d) inside=%d\n", new_x, new_y, old_x, old_y, inside);
647 #endif
648
649 /*
650 * FIXME: This is using pixels to represent lines which loses
651 * information about exact slope and how many lines are present which
652 * causes misbehavior with some inputs (especially complex polygons).
653 * It also takes more room than remembering vertices, but I'd rather
654 * not have to implement line segments for arcs. Maybe store a count
655 * at each vertex instead (doesn't fix the slope problem).
656 */
657 /*
658 * FIXME: Change this to only draw inside of polygons, and round
659 * points in a uniform direction to avoid overlapping drawing. As an
660 * option we could continue to support drawing the outline.
661 */
662 if (new_y != old_y) {
663 if (inside) {
664 /*
665 * Just draw the vertical line when there is not a matching
666 * edge on the right side.
667 */
668 shade_pattern_to_pixel(context, WRITE_SHADING_REF_X,
669 old_x, old_x, old_y);
670 }
671 inside = 1;
672 } else {
673 if (inside) {
674 shade_pattern_to_pixel(context, WRITE_SHADING_REF_X,
675 old_x, new_x, new_y);
676 }
677 if (new_x > old_x + 1) {
678 inside = !inside;
679 }
680 }
681
682 old_x = new_x;
683 old_y = new_y;
684 }
685
686 context->destination_graphic->dirty = True;
687 }
688
689 static void
690 draw_patterned_line(RegisGraphicsContext *context, int x1, int y1,
691 int x2, int y2)
692 {
693 int x, y;
694 int dx, dy;
695 int dir, diff;
696
697 dx = abs(x1 - x2);
698 dy = abs(y1 - y2);
699
700 if (dx > dy) {
701 if (x1 > x2) {
702 int tmp;
703 EXCHANGE(x1, x2, tmp);
704 EXCHANGE(y1, y2, tmp);
705 }
706 if (y1 < y2)
707 dir = 1;
708 else if (y1 > y2)
709 dir = -1;
710 else
711 dir = 0;
712
713 diff = 0;
714 y = y1;
715 for (x = x1; x <= x2; x++) {
716 if (diff >= dx) {
717 diff -= dx;
718 y += dir;
719 }
720 diff += dy;
721 draw_or_save_patterned_pixel(context, x, y);
722 }
723 } else {
724 if (y1 > y2) {
725 int tmp;
726 EXCHANGE(y1, y2, tmp);
727 EXCHANGE(x1, x2, tmp);
728 }
729 if (x1 < x2)
730 dir = 1;
731 else if (x1 > x2)
732 dir = -1;
733 else
734 dir = 0;
735
736 diff = 0;
737 x = x1;
738 for (y = y1; y <= y2; y++) {
739 if (diff >= dy) {
740 diff -= dy;
741 x += dir;
742 }
743 diff += dx;
744 draw_or_save_patterned_pixel(context, x, y);
745 }
746 }
747
748 context->destination_graphic->dirty = True;
749 }
750
751 typedef struct {
752 int dxx;
753 int dxy;
754 int dyx;
755 int dyy;
756 } quadmap_coords;
757
758 static void
759 draw_patterned_arc(RegisGraphicsContext *context,
760 int cx, int cy,
761 int ex, int ey,
762 int a_start, int a_length,
763 int *ex_final, int *ey_final)
764 {
765 const double third = hypot((double) (cx - ex), (double) (cy - ey));
766 const int radius = (int) third;
767 const int ra = radius;
768 const int rb = radius;
769 const quadmap_coords neg_quadmap[4] =
770 {
771 {-1, 0, 0, +1},
772 {0, -1, -1, 0},
773 {+1, 0, 0, -1},
774 {0, +1, +1, 0},
775 };
776 const quadmap_coords pos_quadmap[4] =
777 {
778 {-1, 0, 0, -1},
779 {0, -1, +1, 0},
780 {+1, 0, 0, +1},
781 {0, +1, -1, 0},
782 };
783 const quadmap_coords *quadmap;
784 int total_points;
785 int half_degree;
786 int points_start, points_stop;
787 int points;
788 unsigned iterations;
789 long rx, ry;
790 long dx, dy;
791 int x, y;
792 long e2;
793 long error;
794
795 TRACE(("orig a_length=%d a_start=%d\n", a_length, a_start));
796 if (a_length == 0)
797 return;
798 if (a_length > 0) {
799 quadmap = pos_quadmap;
800 } else {
801 quadmap = neg_quadmap;
802 if (a_start != 0)
803 a_start = 3600 - a_start;
804 a_length = abs(a_length);
805 }
806 TRACE(("positive a_length=%d a_start=%d\n", a_length, a_start));
807
808 rx = -ra;
809 ry = 0;
810 e2 = rb;
811 dx = (2 * rx + 1) * e2 * e2;
812 dy = rx * rx;
813 error = dx + dy;
814 total_points = 0;
815 do {
816 total_points += 4;
817 e2 = 2 * error;
818 if (e2 >= dx) {
819 rx++;
820 dx += 2 * rb * rb;
821 error += dx;
822 }
823 if (e2 <= dy) {
824 ry++;
825 dy += 2 * ra * ra;
826 error += dy;
827 }
828 }
829 while (rx <= 0);
830
831 /* FIXME: This is apparently not accurate enough because some arcs start or
832 * end a few pixels off. Maybe compare line slopes in the loop below
833 * instead?
834 */
835 half_degree = total_points * 5;
836 points_start = (total_points * a_start - half_degree) / 3600;
837 points_stop = (total_points * a_start +
838 total_points * a_length + half_degree) / 3600;
839 TRACE(("drawing arc with %d points clockwise from %g degrees for %g degrees (from point %d to %d out of %d)\n",
840 total_points, a_start / 10.0, a_length / 10.0, points_start, points_stop,
841 total_points));
842
843 /* FIXME: The four pixels at the cardinal directions are double-drawn. */
844 points = 0;
845 for (iterations = 0U; iterations < 8U; iterations++) {
846 int q2 = iterations & 0x3;
847
848 rx = -ra;
849 ry = 0;
850 e2 = rb;
851 dx = (2 * rx + 1) * e2 * e2;
852 dy = rx * rx;
853 error = dx + dy;
854 do {
855 #ifdef DEBUG_ARC_POINTS
856 double rad = atan2(
857 (double) (quadmap[q2].dyx * rx +
858 quadmap[q2].dyy * ry),
859 (double) (quadmap[q2].dxx * rx +
860 quadmap[q2].dxy * ry));
861 double deg = (360.0 * rad / (2.0 * M_PI));
862 if (deg < 0.0)
863 deg += 360.0;
864 #endif
865
866 if (points >= points_start && points <= points_stop) {
867 x = (int) (cx +
868 quadmap[q2].dxx * rx +
869 quadmap[q2].dxy * ry);
870 y = (int) (cy +
871 quadmap[q2].dyx * rx +
872 quadmap[q2].dyy * ry);
873 #ifdef DEBUG_ARC_POINTS
874 TRACE(("drawing point %u at %d,%d (%.5g deg)\n",
875 points, x, y, deg));
876 #endif
877 draw_or_save_patterned_pixel(context, x, y);
878 if (ex_final)
879 *ex_final = x;
880 if (ey_final)
881 *ey_final = y;
882 } else {
883 #ifdef DEBUG_ARC_POINTS
884 x = (int) (cx + quadmap[q2].dxx * rx + quadmap[q2].dxy * ry);
885 y = (int) (cy + quadmap[q2].dyx * rx + quadmap[q2].dyy * ry);
886 TRACE(("skipping point %u at %d,%d which is outside of range (%.5g deg)\n",
887 points, x, y, deg));
888 #endif
889 }
890 points++;
891
892 e2 = 2 * error;
893 if (e2 >= dx) {
894 rx++;
895 dx += 2 * rb * rb;
896 error += dx;
897 }
898 if (e2 <= dy) {
899 ry++;
900 dy += 2 * ra * ra;
901 error += dy;
902 }
903 }
904 while (rx <= 0);
905 }
906
907 context->destination_graphic->dirty = True;
908 }
909
910 /*
911 * The plot* functions are based on optimized rasterization primitives written
912 * by Zingl Alois.
913 * See http://members.chello.at/easyfilter/bresenham.html
914 */
915
916 /*
917 * FIXME:
918 * This is a terrible temporary hack. The plot functions below can be adapted
919 * to work like the other rasterization functions but there's no point in doing
920 * that until we know we don't have to write something completely different.
921 */
922 static RegisGraphicsContext *global_context;
923 static void
924 setPixel(int x, int y)
925 {
926 draw_or_save_patterned_pixel(global_context, x, y);
927 }
928
929 static void
930 plotLine(int x0, int y0, int x1, int y1)
931 {
932 int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
933 int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
934 int err = dx + dy; /* error value e_xy */
935
936 for (;;) { /* loop */
937 int e2;
938 setPixel(x0, y0);
939 e2 = 2 * err;
940 if (e2 >= dy) { /* e_xy+e_x > 0 */
941 if (x0 == x1)
942 break;
943 err += dy;
944 x0 += sx;
945 }
946 if (e2 <= dx) { /* e_xy+e_y < 0 */
947 if (y0 == y1)
948 break;
949 err += dx;
950 y0 += sy;
951 }
952 }
953 }
954
955 static void
956 plotQuadBezierSeg(int x0, int y0, int x1, int y1, int x2, int y2)
957 { /* plot a limited quadratic Bezier segment */
958 int sx = x2 - x1;
959 int sy = y2 - y1;
960 long xx = (x0 - x1); /* relative values for checks */
961 long yy = (y0 - y1);
962 double cur = (double) (xx * sy - yy * sx); /* curvature */
963
964 assert(xx * sx <= 0 && yy * sy <= 0); /* sign of gradient must not change */
965
966 if (sx * (long) sx + sy * (long) sy > xx * xx + yy * yy) { /* begin with longer part */
967 x2 = x0;
968 x0 = sx + x1;
969 y2 = y0;
970 y0 = sy + y1;
971 cur = -cur; /* swap P0 P2 */
972 }
973 if (cur != 0.0) { /* no straight line */
974 long xy;
975 double dx, dy, err;
976
977 xx += sx;
978 xx *= (sx = (x0 < x2) ? 1 : -1); /* x step direction */
979 yy += sy;
980 yy *= (sy = (y0 < y2) ? 1 : -1); /* y step direction */
981 xy = 2 * xx * yy;
982 xx *= xx;
983 yy *= yy; /* differences 2nd degree */
984 if (cur * sx * sy < 0) { /* negated curvature? */
985 xx = -xx;
986 yy = -yy;
987 xy = -xy;
988 cur = -cur;
989 }
990 /* differences 1st degree */
991 dx = ((4.0 * sy * cur * (x1 - x0)) + (double) xx) - (double) xy;
992 dy = ((4.0 * sx * cur * (y0 - y1)) + (double) yy) - (double) xy;
993 xx += xx;
994 yy += yy;
995 err = dx + dy + (double) xy; /* error 1st step */
996 do {
997 setPixel(x0, y0); /* plot curve */
998 if (x0 == x2 && y0 == y2)
999 return; /* last pixel -> curve finished */
1000 y1 = (2 * err) < dx; /* save value for test of y step */
1001 if ((2 * err) > dy) {
1002 x0 += sx;
1003 dx -= (double) xy;
1004 dy += (double) yy;
1005 err += dy;
1006 } /* x step */
1007 if (y1) {
1008 y0 += sy;
1009 dy -= (double) xy;
1010 dx += (double) xx;
1011 err += dx;
1012 } /* y step */
1013 } while (dy < 0 && dx > 0); /* gradient negates -> algorithm fails */
1014 }
1015 plotLine(x0, y0, x2, y2); /* plot remaining part to end */
1016 }
1017
1018 #if 0
1019 static void
1020 plotQuadBezier(int x0, int y0, int x1, int y1, int x2, int y2)
1021 { /* plot any quadratic Bezier curve */
1022 int x = x0 - x1;
1023 int y = y0 - y1;
1024 double t = x0 - 2 * x1 + x2;
1025 double r;
1026
1027 if ((long) x * (x2 - x1) > 0) { /* horizontal cut at P4? */
1028 if ((long) y * (y2 - y1) > 0) /* vertical cut at P6 too? */
1029 if (fabs((y0 - 2 * y1 + y2) / t * x) > abs(y)) { /* which first? */
1030 x0 = x2;
1031 x2 = x + x1;
1032 y0 = y2;
1033 y2 = y + y1; /* swap points */
1034 } /* now horizontal cut at P4 comes first */
1035 t = (x0 - x1) / t;
1036 r = (1 - t) * ((1 - t) * y0 + 2.0 * t * y1) + t * t * y2; /* By(t=P4) */
1037 t = (x0 * x2 - x1 * x1) * t / (x0 - x1); /* gradient dP4/dx=0 */
1038 x = ifloor(t + 0.5);
1039 y = ifloor(r + 0.5);
1040 r = (y1 - y0) * (t - x0) / (x1 - x0) + y0; /* intersect P3 | P0 P1 */
1041 plotQuadBezierSeg(x0, y0, x, ifloor(r + 0.5), x, y);
1042 r = (y1 - y2) * (t - x2) / (x1 - x2) + y2; /* intersect P4 | P1 P2 */
1043 x0 = x1 = x;
1044 y0 = y;
1045 y1 = ifloor(r + 0.5); /* P0 = P4, P1 = P8 */
1046 }
1047 if ((long) (y0 - y1) * (y2 - y1) > 0) { /* vertical cut at P6? */
1048 t = y0 - 2 * y1 + y2;
1049 t = (y0 - y1) / t;
1050 r = (1 - t) * ((1 - t) * x0 + 2.0 * t * x1) + t * t * x2; /* Bx(t=P6) */
1051 t = (y0 * y2 - y1 * y1) * t / (y0 - y1); /* gradient dP6/dy=0 */
1052 x = ifloor(r + 0.5);
1053 y = ifloor(t + 0.5);
1054 r = (x1 - x0) * (t - y0) / (y1 - y0) + x0; /* intersect P6 | P0 P1 */
1055 plotQuadBezierSeg(x0, y0, ifloor(r + 0.5), y, x, y);
1056 r = (x1 - x2) * (t - y2) / (y1 - y2) + x2; /* intersect P7 | P1 P2 */
1057 x0 = x;
1058 x1 = ifloor(r + 0.5);
1059 y0 = y1 = y; /* P0 = P6, P1 = P7 */
1060 }
1061 plotQuadBezierSeg(x0, y0, x1, y1, x2, y2); /* remaining part */
1062 }
1063 #endif
1064
1065 static void
1066 plotCubicBezierSeg(int x0, int y0,
1067 double x1, double y1,
1068 double x2, double y2,
1069 int x3, int y3)
1070 { /* plot limited cubic Bezier segment */
1071 int f, fx, fy, tt;
1072 int leg = 1;
1073 int sx = x0 < x3 ? 1 : -1;
1074 int sy = y0 < y3 ? 1 : -1; /* step direction */
1075 double xc = -fabs(x0 + x1 - x2 - x3);
1076 double xa = xc - 4 * sx * (x1 - x2);
1077 double xb = sx * (x0 - x1 - x2 + x3);
1078 double yc = -fabs(y0 + y1 - y2 - y3);
1079 double ya = yc - 4 * sy * (y1 - y2);
1080 double yb = sy * (y0 - y1 - y2 + y3);
1081 double ab, ac, bc, cb, xx, xy, yy, dx, dy, ex, *pxy;
1082 double EP = 0.01;
1083 /* check for curve restrains */
1084 /* slope P0-P1 == P2-P3 and (P0-P3 == P1-P2 or no slope change) */
1085 assert((x1 - x0) * (x2 - x3) < EP &&
1086 ((x3 - x0) * (x1 - x2) < EP || xb * xb < xa * xc + EP));
1087 assert((y1 - y0) * (y2 - y3) < EP &&
1088 ((y3 - y0) * (y1 - y2) < EP || yb * yb < ya * yc + EP));
1089
1090 if (xa == 0.0 && ya == 0.0) { /* quadratic Bezier */
1091 sx = ifloor((3 * x1 - x0 + 1) / 2);
1092 sy = ifloor((3 * y1 - y0 + 1) / 2); /* new midpoint */
1093 plotQuadBezierSeg(x0, y0, sx, sy, x3, y3);
1094 return;
1095 }
1096 x1 = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + 1; /* line lengths */
1097 x2 = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3) + 1;
1098 do { /* loop over both ends */
1099 ab = xa * yb - xb * ya;
1100 ac = xa * yc - xc * ya;
1101 bc = xb * yc - xc * yb;
1102 ex = ab * (ab + ac - 3 * bc) + ac * ac; /* P0 part of self-intersection loop? */
1103 f = ((ex > 0.0)
1104 ? 1
1105 : isqrt(1 + 1024 / x1)); /* calculate resolution */
1106 ab *= f;
1107 ac *= f;
1108 bc *= f;
1109 ex *= f * f; /* increase resolution */
1110 xy = 9 * (ab + ac + bc) / 8;
1111 cb = 8 * (xa - ya); /* init differences of 1st degree */
1112 dx = 27 * (8 * ab * (yb * yb - ya * yc) +
1113 ex * (ya + 2 * yb + yc)) / 64 - ya * ya * (xy - ya);
1114 dy = 27 * (8 * ab * (xb * xb - xa * xc) -
1115 ex * (xa + 2 * xb + xc)) / 64 - xa * xa * (xy + xa);
1116 /* init differences of 2nd degree */
1117 xx = 3 * (3 * ab * (3 * yb * yb - ya * ya - 2 * ya * yc) -
1118 ya * (3 * ac * (ya + yb) + ya * cb)) / 4;
1119 yy = 3 * (3 * ab * (3 * xb * xb - xa * xa - 2 * xa * xc) -
1120 xa * (3 * ac * (xa + xb) + xa * cb)) / 4;
1121 xy = xa * ya * (6 * ab + 6 * ac - 3 * bc + cb);
1122 ac = ya * ya;
1123 cb = xa * xa;
1124 xy = 3 * (xy + 9 * f * (cb * yb * yc - xb * xc * ac) -
1125 18 * xb * yb * ab) / 8;
1126
1127 if (ex < 0) { /* negate values if inside self-intersection loop */
1128 dx = -dx;
1129 dy = -dy;
1130 xx = -xx;
1131 yy = -yy;
1132 xy = -xy;
1133 ac = -ac;
1134 cb = -cb;
1135 } /* init differences of 3rd degree */
1136 ab = 6 * ya * ac;
1137 ac = -6 * xa * ac;
1138 bc = 6 * ya * cb;
1139 cb = -6 * xa * cb;
1140 dx += xy;
1141 ex = dx + dy;
1142 dy += xy; /* error of 1st step */
1143
1144 for (pxy = &xy, fx = fy = f; x0 != x3 && y0 != y3;) {
1145 setPixel(x0, y0); /* plot curve */
1146 do { /* move sub-steps of one pixel */
1147 if (dx > *pxy || dy < *pxy)
1148 goto exit; /* confusing values */
1149 y1 = 2 * ex - dy; /* save value for test of y step */
1150 if (2 * ex >= dx) { /* x sub-step */
1151 fx--;
1152 ex += dx += xx;
1153 dy += xy += ac;
1154 yy += bc;
1155 xx += ab;
1156 }
1157 if (y1 <= 0) { /* y sub-step */
1158 fy--;
1159 ex += dy += yy;
1160 dx += xy += bc;
1161 xx += ac;
1162 yy += cb;
1163 }
1164 } while (fx > 0 && fy > 0); /* pixel complete? */
1165 if (2 * fx <= f) {
1166 x0 += sx;
1167 fx += f;
1168 } /* x step */
1169 if (2 * fy <= f) {
1170 y0 += sy;
1171 fy += f;
1172 } /* y step */
1173 if (pxy == &xy && dx < 0 && dy > 0)
1174 pxy = &EP; /* pixel ahead valid */
1175 }
1176 exit:
1177 EXCHANGE(x0, x3, tt);
1178 sx = -sx;
1179 xb = -xb; /* swap legs */
1180 EXCHANGE(y0, y3, tt);
1181 sy = -sy;
1182 yb = -yb;
1183 x1 = x2;
1184 } while (leg--); /* try other end */
1185 plotLine(x0, y0, x3, y3); /* remaining part in case of cusp or crunode */
1186 }
1187
1188 static void
1189 plotCubicBezier(int x0, int y0, int x1, int y1,
1190 int x2, int y2, int x3, int y3)
1191 { /* plot any cubic Bezier curve */
1192 int n = 0, i = 0;
1193 long xc = x0 + x1 - x2 - x3;
1194 long xa = xc - 4 * (x1 - x2);
1195 long xb = x0 - x1 - x2 + x3;
1196 long xd = xb + 4 * (x1 + x2);
1197 long yc = y0 + y1 - y2 - y3;
1198 long ya = yc - 4 * (y1 - y2);
1199 long yb = y0 - y1 - y2 + y3;
1200 long yd = yb + 4 * (y1 + y2);
1201 double fx0 = x0;
1202 double fy0 = y0;
1203 double t1 = (double) (xb * xb - xa * xc), t2, t[5];
1204
1205 #ifdef DEBUG_BEZIER
1206 printf("plotCubicBezier(%d,%d, %d,%d, %d,%d, %d,%d\n",
1207 x0, y0, x1, y1, x2, y2, x3, y3);
1208 #endif
1209 /* sub-divide curve at gradient sign changes */
1210 if (xa == 0) { /* horizontal */
1211 if (labs(xc) < 2 * labs(xb))
1212 t[n++] = (double) xc / (2.0 * (double) xb); /* one change */
1213 } else if (t1 > 0.0) { /* two changes */
1214 t2 = sqrt(t1);
1215 t1 = ((double) xb - t2) / (double) xa;
1216 if (fabs(t1) < 1.0)
1217 t[n++] = t1;
1218 t1 = ((double) xb + t2) / (double) xa;
1219 if (fabs(t1) < 1.0)
1220 t[n++] = t1;
1221 }
1222 t1 = (double) (yb * yb - ya * yc);
1223 if (ya == 0) { /* vertical */
1224 if (labs(yc) < 2 * labs(yb))
1225 t[n++] = (double) yc / (2.0 * (double) yb); /* one change */
1226 } else if (t1 > 0.0) { /* two changes */
1227 t2 = sqrt(t1);
1228 t1 = ((double) yb - t2) / (double) ya;
1229 if (fabs(t1) < 1.0)
1230 t[n++] = t1;
1231 t1 = ((double) yb + t2) / (double) ya;
1232 if (fabs(t1) < 1.0)
1233 t[n++] = t1;
1234 }
1235 for (i = 1; i < n; i++) /* bubble sort of 4 points */
1236 if ((t1 = t[i - 1]) > t[i]) {
1237 t[i - 1] = t[i];
1238 t[i] = t1;
1239 i = 0;
1240 }
1241
1242 t1 = -1.0;
1243 t[n] = 1.0; /* begin / end point */
1244 for (i = 0; i <= n; i++) { /* plot each segment separately */
1245 double fx1, fx2, fx3;
1246 double fy1, fy2, fy3;
1247
1248 t2 = t[i]; /* sub-divide at t[i-1], t[i] */
1249 fx1 = (t1 * (t1 * (double) xb - (double) (2 * xc)) -
1250 t2 * (t1 * (t1 * (double) xa - (double) (2 * xb)) + (double)
1251 xc) + (double) xd) / 8 - fx0;
1252 fy1 = (t1 * (t1 * (double) yb - (double) (2 * yc)) -
1253 t2 * (t1 * (t1 * (double) ya - (double) (2 * yb)) + (double)
1254 yc) + (double) yd) / 8 - fy0;
1255 fx2 = (t2 * (t2 * (double) xb - (double) (2 * xc)) -
1256 t1 * (t2 * (t2 * (double) xa - (double) (2 * xb)) + (double)
1257 xc) + (double) xd) / 8 - fx0;
1258 fy2 = (t2 * (t2 * (double) yb - (double) (2 * yc)) -
1259 t1 * (t2 * (t2 * (double) ya - (double) (2 * yb)) + (double)
1260 yc) + (double) yd) / 8 - fy0;
1261 fx0 -= fx3 = (t2 * (t2 * ((double) (3 * xb) - t2 * (double) xa) -
1262 (double) (3 * xc)) + (double) xd) / 8;
1263 fy0 -= fy3 = (t2 * (t2 * ((double) (3 * yb) - t2 * (double) ya) -
1264 (double) (3 * yc)) + (double) yd) / 8;
1265 x3 = ifloor(fx3 + 0.5);
1266 y3 = ifloor(fy3 + 0.5); /* scale bounds to int */
1267 if (fx0 != 0.0) {
1268 fx1 *= fx0 = (x0 - x3) / fx0;
1269 fx2 *= fx0;
1270 }
1271 if (fy0 != 0.0) {
1272 fy1 *= fy0 = (y0 - y3) / fy0;
1273 fy2 *= fy0;
1274 }
1275 if (x0 != x3 || y0 != y3) /* segment t1 - t2 */
1276 plotCubicBezierSeg(x0, y0,
1277 x0 + fx1, y0 + fy1,
1278 x0 + fx2, y0 + fy2,
1279 x3, y3);
1280 x0 = x3;
1281 y0 = y3;
1282 fx0 = fx3;
1283 fy0 = fy3;
1284 t1 = t2;
1285 }
1286 }
1287
1288 #if 0
1289 static void
1290 plotQuadSpline(int n, int x[], int y[], int skip_segments)
1291 { /* plot quadratic spline, destroys input arrays x,y */
1292 #define M_MAX 12
1293 double mi = 1, m[M_MAX]; /* diagonal constants of matrix */
1294 int i, x0, y0, x1, y1, x2, y2;
1295 #ifdef DEBUG_SPLINE_SEGMENTS
1296 int color = 0;
1297 #endif
1298
1299 assert(n > 1); /* need at least 3 points P[0]..P[n] */
1300
1301 #ifdef DEBUG_SPLINE_POINTS
1302 {
1303 int save_pattern;
1304
1305 i = 0;
1306 global_context->temporary_write_controls.foreground = 11;
1307 save_pattern = global_context->temporary_write_controls.pattern;
1308 global_context->temporary_write_controls.pattern = 0xff;
1309 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
1310 3600, NULL, NULL);
1311 i++;
1312 global_context->temporary_write_controls.foreground = 15;
1313 for (; i < n; i++) {
1314 draw_patterned_arc(global_context,
1315 x[i], y[i],
1316 x[i] + 2, y[i],
1317 0, 3600, NULL, NULL);
1318 }
1319 global_context->temporary_write_controls.foreground = 10;
1320 draw_patterned_arc(global_context, x[i], y[n], x[i] + 2, y[i], 0,
1321 3600, NULL, NULL);
1322 global_context->temporary_write_controls.pattern = save_pattern;
1323 }
1324 #endif
1325
1326 x2 = x[n];
1327 y2 = y[n];
1328
1329 x[1] = x0 = 8 * x[1] - 2 * x[0]; /* first row of matrix */
1330 y[1] = y0 = 8 * y[1] - 2 * y[0];
1331
1332 for (i = 2; i < n; i++) { /* forward sweep */
1333 if (i - 2 < M_MAX)
1334 m[i - 2] = mi = 1.0 / (6.0 - mi);
1335 x[i] = x0 = ifloor(8 * x[i] - x0 * mi + 0.5); /* store yi */
1336 y[i] = y0 = ifloor(8 * y[i] - y0 * mi + 0.5);
1337 }
1338 x1 = ifloor((x0 - 2 * x2) / (5.0 - mi) + 0.5); /* correction last row */
1339 y1 = ifloor((y0 - 2 * y2) / (5.0 - mi) + 0.5);
1340
1341 for (i = n - 2; i > 0; i--) { /* back substitution */
1342 if (i <= M_MAX)
1343 mi = m[i - 1];
1344 x0 = ifloor((x[i] - x1) * mi + 0.5); /* next corner */
1345 y0 = ifloor((y[i] - y1) * mi + 0.5);
1346 #ifdef DEBUG_SPLINE_SEGMENTS
1347 color++;
1348 global_context->temporary_write_controls.foreground = color;
1349 #endif
1350 if ((n - 2) - i < skip_segments)
1351 plotQuadBezier((x0 + x1) / 2, (y0 + y1) / 2, x1, y1, x2, y2);
1352 x2 = (x0 + x1) / 2;
1353 x1 = x0;
1354 y2 = (y0 + y1) / 2;
1355 y1 = y0;
1356 }
1357 #ifdef DEBUG_SPLINE_SEGMENTS
1358 color++;
1359 global_context->temporary_write_controls.foreground = color;
1360 #endif
1361 if (skip_segments > 0)
1362 plotQuadBezier(x[0], y[0], x1, y1, x2, y2);
1363 }
1364 #endif
1365
1366 static void
1367 plotCubicSpline(int n, int x[], int y[], int skip_first_last)
1368 {
1369 #define M_MAX 12
1370 double mi = 0.25, m[M_MAX]; /* diagonal constants of matrix */
1371 int x3, y3, x4, y4;
1372 int i, x0, y0, x1, y1, x2, y2;
1373 #ifdef DEBUG_SPLINE_SEGMENTS
1374 RegisterNum color = 0;
1375 #endif
1376
1377 assert(n > 2); /* need at least 4 points P[0]..P[n] */
1378
1379 #ifdef DEBUG_SPLINE_POINTS
1380 {
1381 unsigned save_pattern;
1382
1383 i = 0;
1384 global_context->temporary_write_controls.foreground = 11;
1385 save_pattern = global_context->temporary_write_controls.pattern;
1386 global_context->temporary_write_controls.pattern = 0xff;
1387 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
1388 3600, NULL, NULL);
1389 i++;
1390 global_context->temporary_write_controls.foreground = 15;
1391 for (; i < n; i++) {
1392 draw_patterned_arc(global_context,
1393 x[i], y[i],
1394 x[i] + 2, y[i],
1395 0, 3600, NULL, NULL);
1396 }
1397 global_context->temporary_write_controls.foreground = 10;
1398 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
1399 3600, NULL, NULL);
1400 global_context->temporary_write_controls.pattern = save_pattern;
1401 }
1402 #endif
1403
1404 x3 = x[n - 1];
1405 y3 = y[n - 1];
1406 x4 = x[n];
1407 y4 = y[n];
1408
1409 x[1] = x0 = 12 * x[1] - 3 * x[0]; /* first row of matrix */
1410 y[1] = y0 = 12 * y[1] - 3 * y[0];
1411
1412 for (i = 2; i < n; i++) { /* forward sweep */
1413 if (i - 2 < M_MAX)
1414 m[i - 2] = mi = 0.25 / (2.0 - mi);
1415 x[i] = x0 = ifloor(12 * x[i] - 2 * x0 * mi + 0.5);
1416 y[i] = y0 = ifloor(12 * y[i] - 2 * y0 * mi + 0.5);
1417 }
1418 x2 = ifloor((x0 - 3 * x4) / (7 - 4 * mi) + 0.5); /* correct last row */
1419 /* printf("y0=%d, y4=%d mi=%g\n", y0, y4, mi); */
1420 y2 = ifloor((y0 - 3 * y4) / (7 - 4 * mi) + 0.5);
1421 /* printf("y2=%d, y3=%d, y4=%d\n", y2, y3, y4); */
1422 #ifdef DEBUG_SPLINE_SEGMENTS
1423 color++;
1424 global_context->temporary_write_controls.foreground = color;
1425 #endif
1426 if (!skip_first_last)
1427 plotCubicBezier(x3, y3, (x2 + x4) / 2, (y2 + y4) / 2, x4, y4, x4, y4);
1428
1429 if (n - 3 < M_MAX)
1430 mi = m[n - 3];
1431 x1 = ifloor((x[n - 2] - 2 * x2) * mi + 0.5);
1432 y1 = ifloor((y[n - 2] - 2 * y2) * mi + 0.5);
1433 for (i = n - 3; i > 0; i--) { /* back substitution */
1434 if (i <= M_MAX)
1435 mi = m[i - 1];
1436 x0 = ifloor((x[i] - 2 * x1) * mi + 0.5);
1437 y0 = ifloor((y[i] - 2 * y1) * mi + 0.5);
1438 x4 = ifloor((x0 + 4 * x1 + x2 + 3) / 6.0); /* reconstruct P[i] */
1439 y4 = ifloor((y0 + 4 * y1 + y2 + 3) / 6.0);
1440 #ifdef DEBUG_SPLINE_SEGMENTS
1441 color++;
1442 global_context->temporary_write_controls.foreground = color;
1443 #endif
1444 #define CB_PARM(num) ifloor((num) / 3.0 + 0.5)
1445 plotCubicBezier(x4, y4,
1446 CB_PARM(2 * x1 + x2),
1447 CB_PARM(2 * y1 + y2),
1448 CB_PARM(x1 + 2 * x2),
1449 CB_PARM(y1 + 2 * y2),
1450 x3, y3);
1451 x3 = x4;
1452 y3 = y4;
1453 x2 = x1;
1454 y2 = y1;
1455 x1 = x0;
1456 y1 = y0;
1457 }
1458 x0 = x[0];
1459 x4 = ifloor((3 * x0 + 7 * x1 + 2 * x2 + 6) / 12.0); /* reconstruct P[1] */
1460 y0 = y[0];
1461 y4 = ifloor((3 * y0 + 7 * y1 + 2 * y2 + 6) / 12.0);
1462 #ifdef DEBUG_SPLINE_SEGMENTS
1463 global_context->temporary_write_controls.foreground = 4;
1464 #endif
1465 plotCubicBezier(x4, y4,
1466 CB_PARM(2 * x1 + x2),
1467 CB_PARM(2 * y1 + y2),
1468 CB_PARM(x1 + 2 * x2),
1469 CB_PARM(y1 + 2 * y2),
1470 x3, y3);
1471 #ifdef DEBUG_SPLINE_SEGMENTS
1472 color++;
1473 global_context->temporary_write_controls.foreground = color;
1474 #endif
1475 if (!skip_first_last)
1476 plotCubicBezier(x0, y0, x0, y0, (x0 + x1) / 2, (y0 + y1) / 2, x4, y4);
1477 }
1478
1479 static unsigned
1480 find_free_alphabet_index(RegisGraphicsContext *context, unsigned alphabet,
1481 unsigned pixw, unsigned pixh)
1482 {
1483 unsigned ii, jj;
1484
1485 /* try an exact match */
1486 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
1487 if (context->alphabets[ii].alphabet_num == alphabet &&
1488 context->alphabets[ii].pixw == pixw &&
1489 context->alphabets[ii].pixh == pixh) {
1490 return ii;
1491 }
1492 }
1493
1494 /* otherwise use any empty slot */
1495 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
1496 if (context->alphabets[ii].alphabet_num == INVALID_ALPHABET_NUM) {
1497 context->alphabets[ii].alphabet_num = alphabet;
1498 context->alphabets[ii].pixw = pixw;
1499 context->alphabets[ii].pixh = pixh;
1500 return ii;
1501 }
1502 }
1503
1504 /* otherwise recycle a slot with a different font size */
1505 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
1506 if (context->alphabets[ii].alphabet_num == alphabet) {
1507 context->alphabets[ii].pixw = pixw;
1508 context->alphabets[ii].pixh = pixh;
1509 context->alphabets[ii].name[0] = '\0';
1510 context->alphabets[ii].fontname[0] = '\0';
1511 context->alphabets[ii].use_font = 0;
1512 if (context->alphabets[ii].bytes != NULL) {
1513 free(context->alphabets[ii].bytes);
1514 context->alphabets[ii].bytes = NULL;
1515 }
1516 for (jj = 0U; jj < MAX_GLYPHS; jj++) {
1517 context->alphabets[ii].loaded[jj] = 0;
1518 }
1519 return ii;
1520 }
1521 }
1522
1523 /* finally just recycle this arbitrary slot */
1524 context->alphabets[0U].alphabet_num = alphabet;
1525 context->alphabets[0U].pixw = pixw;
1526 context->alphabets[0U].pixh = pixh;
1527 context->alphabets[0U].name[0] = '\0';
1528 context->alphabets[0U].fontname[0] = '\0';
1529 context->alphabets[0U].use_font = 0;
1530 if (context->alphabets[0U].bytes != NULL) {
1531 free(context->alphabets[0U].bytes);
1532 context->alphabets[0U].bytes = NULL;
1533 }
1534 for (jj = 0U; jj < MAX_GLYPHS; jj++) {
1535 context->alphabets[0U].loaded[jj] = 0;
1536 }
1537
1538 return 0U;
1539 }
1540
1541 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
1542 static void
1543 dump_bitmap_pixels(Char const *pixels, unsigned w, unsigned h)
1544 {
1545 unsigned yy, xx;
1546
1547 for (yy = 0U; yy < h; yy++) {
1548 printf(" ");
1549 for (xx = 0U; xx < w; xx++) {
1550 if (pixels[yy * w + xx]) {
1551 printf("#");
1552 } else {
1553 printf("_");
1554 }
1555 }
1556 printf("\n");
1557 }
1558 }
1559 #endif
1560
1561 #if OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
1562 static int
1563 copy_bitmap_from_xft_font(XtermWidget xw, XftFont *font, FcChar32 ch,
1564 Char *pixels, unsigned w, unsigned h,
1565 unsigned xmin, unsigned ymin)
1566 {
1567 /*
1568 * FIXME: cache:
1569 * - the bitmap for the last M characters and target dimensions
1570 * - reuse the pixmap object where possible
1571 */
1572 Display *display = XtDisplay(xw);
1573 Screen *screen = XtScreen(xw);
1574 XftColor bg, fg;
1575 Pixmap bitmap;
1576 XftDraw *draw;
1577 XImage *image;
1578 GC glyph_gc;
1579 unsigned bmw, bmh;
1580 unsigned xx, yy;
1581
1582 bmw = w + xmin;
1583 bmh = h;
1584 if (bmw < 1 || bmh < 1) {
1585 TRACE(("refusing impossible bitmap size w=%d h=%d xmin=%d ymin=%d for ch='%c'\n",
1586 bmw, bmh, xmin, ymin, ch));
1587 return 0;
1588 }
1589 bitmap = XCreatePixmap(display,
1590 DefaultRootWindow(display),
1591 bmw, bmh, (unsigned) getVisualDepth(xw));
1592 if (bitmap == None) {
1593 TRACE(("unable to create Pixmap for Xft\n"));
1594 return 0;
1595 }
1596 draw = XftDrawCreate(display, bitmap, xw->visInfo->visual,
1597 XDefaultColormap(display,
1598 XScreenNumberOfScreen(screen)));
1599 if (!draw) {
1600 TRACE(("unable to create XftDraw\n"));
1601 XFreePixmap(display, bitmap);
1602 return 0;
1603 }
1604
1605 bg.pixel = 0UL;
1606 bg.color.red = 0;
1607 bg.color.green = 0;
1608 bg.color.blue = 0;
1609 bg.color.alpha = 0x0;
1610 XftDrawRect(draw, &bg, 0, 0, bmw, bmh);
1611
1612 fg.pixel = 1UL;
1613 fg.color.red = 0xffff;
1614 fg.color.green = 0xffff;
1615 fg.color.blue = 0xffff;
1616 fg.color.alpha = 0xffff;
1617 XftDrawString32(draw, &fg, font, -(int) xmin, font->ascent - (int) ymin,
1618 &ch, 1);
1619
1620 glyph_gc = XCreateGC(display, bitmap, 0UL, NULL);
1621 if (!glyph_gc) {
1622 TRACE(("unable to create GC\n"));
1623 XftDrawDestroy(draw);
1624 XFreePixmap(display, bitmap);
1625 return 0;
1626 }
1627 XSetForeground(display, glyph_gc, 1UL);
1628 XSetBackground(display, glyph_gc, 0UL);
1629 image = XGetImage(display, bitmap, 0, 0, w, h, 1UL, XYPixmap);
1630 if (!image) {
1631 TRACE(("unable to create XImage\n"));
1632 XFreeGC(display, glyph_gc);
1633 XftDrawDestroy(draw);
1634 XFreePixmap(display, bitmap);
1635 return 0;
1636 }
1637
1638 for (yy = 0U; yy < h; yy++) {
1639 #ifdef DEBUG_XFT_GLYPH_COPY
1640 TRACE(("'%c'[%02u]:", ch, yy));
1641 #endif
1642 for (xx = 0U; xx < w; xx++) {
1643 unsigned long pix;
1644 pix = XGetPixel(image, (int) xx, (int) yy);
1645 pixels[yy * w + xx] = (unsigned char) pix;
1646 #ifdef DEBUG_XFT_GLYPH_COPY
1647 TRACE((" %lu", pix));
1648 #endif
1649 }
1650 #ifdef DEBUG_XFT_GLYPH_COPY
1651 TRACE(("\n"));
1652 #endif
1653 }
1654
1655 XFreeGC(display, glyph_gc);
1656 XDestroyImage(image);
1657 XftDrawDestroy(draw);
1658 XFreePixmap(display, bitmap);
1659 return 1;
1660 }
1661
1662 static void
1663 get_xft_glyph_dimensions(XtermWidget xw, XftFont *font, unsigned *w,
1664 unsigned *h, unsigned *xmin, unsigned *ymin)
1665 {
1666 unsigned workw, workh;
1667 FcChar32 ch;
1668 Char *pixels;
1669 Char *pixelp;
1670 unsigned yy, xx;
1671 unsigned char_count, pixel_count;
1672 unsigned real_minx, real_maxx, real_miny, real_maxy;
1673 unsigned char_minx, char_maxx, char_miny, char_maxy;
1674
1675 /*
1676 * For each ASCII or ISO-8859-1 printable code, find out what its
1677 * dimensions are.
1678 *
1679 * We actually render the glyphs and determine the extents ourselves
1680 * because the font library can lie by several pixels, and since we are
1681 * doing manual character placement in fixed areas the glyph boundary needs
1682 * to be accurate.
1683 *
1684 * Ignore control characters and spaces - their extent information is
1685 * misleading.
1686 */
1687
1688 /* Our "work area" is just a buffer which should be big enough to hold the
1689 * largest glyph even if its size is under-reported by a couple of pixels
1690 * in each dimension.
1691 */
1692 workw = (unsigned) font->max_advance_width + 2U;
1693 if (font->ascent + font->descent > font->height) {
1694 workh = (unsigned) (font->ascent + font->descent) + 2U;
1695 } else {
1696 workh = (unsigned) font->height + 2U;
1697 }
1698
1699 if (!(pixels = TypeMallocN(Char, (size_t) (workw * workh)))) {
1700 *w = 0U;
1701 *h = 0U;
1702 #ifdef DEBUG_COMPUTED_FONT_METRICS
1703 TRACE(("reported metrics:\n"));
1704 TRACE((" %ux%u ascent=%u descent=%u\n", font->max_advance_width,
1705 font->height, font->ascent, font->descent));
1706 TRACE(("computed metrics:\n"));
1707 TRACE((" (unable to allocate pixel array)\n"));
1708 #endif
1709 return;
1710 }
1711
1712 /* FIXME: ch is in UCS32 -- try to support non-ASCII characters */
1713 char_count = 0U;
1714 real_minx = workw - 1U;
1715 real_maxx = 0U;
1716 real_miny = workh - 1U;
1717 real_maxy = 0U;
1718 for (ch = 33; ch < 256; ++ch) {
1719 if (ch >= 127 && ch <= 160) {
1720 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
1721 if (IS_DEBUG_CHAR(ch))
1722 printf("char: '%c' not in interesting range; ignoring\n",
1723 (char) ch);
1724 #endif
1725 continue;
1726 }
1727 if (!FcCharSetHasChar(font->charset, ch)) {
1728 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
1729 if (IS_DEBUG_CHAR(ch))
1730 printf("char: '%c' not in charset; ignoring\n", (char) ch);
1731 #endif
1732 continue;
1733 }
1734
1735 if (!copy_bitmap_from_xft_font(xw, font, ch, pixels,
1736 workw, workh, 0U, 0U)) {
1737 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
1738 if (IS_DEBUG_CHAR(ch))
1739 printf("char: '%c' bitmap could not be copied; ignoring\n",
1740 (char) ch);
1741 #endif
1742 continue;
1743 }
1744
1745 pixel_count = 0U;
1746 char_minx = workh - 1U;
1747 char_maxx = 0U;
1748 char_miny = workh - 1U;
1749 char_maxy = 0U;
1750 pixelp = pixels;
1751 for (yy = 0U; yy < workh; yy++) {
1752 for (xx = 0U; xx < workw; xx++) {
1753 if (*pixelp++) {
1754 if (xx < char_minx)
1755 char_minx = xx;
1756 else if (xx > char_maxx)
1757 char_maxx = xx;
1758 if (yy < char_miny)
1759 char_miny = yy;
1760 else if (yy > char_maxy)
1761 char_maxy = yy;
1762 pixel_count++;
1763 }
1764 }
1765 }
1766 if (pixel_count < 1U) {
1767 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
1768 if (IS_DEBUG_CHAR(ch))
1769 printf("char: '%c' has no pixels; ignoring\n", (char) ch);
1770 #endif
1771 continue;
1772 }
1773 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
1774 if (IS_DEBUG_CHAR(ch)) {
1775 printf("char: '%c' (%d)\n", (char) ch, ch);
1776 printf(" minx: %u\n", char_minx);
1777 printf(" maxx: %u\n", char_maxx);
1778 printf(" miny: %u\n", char_miny);
1779 printf(" maxy: %u\n", char_maxy);
1780 dump_bitmap_pixels(pixels, workw, workh);
1781 printf("\n");
1782 }
1783 #endif
1784
1785 if (char_minx < real_minx)
1786 real_minx = char_minx;
1787 if (char_maxx > real_maxx)
1788 real_maxx = char_maxx;
1789 if (char_miny < real_miny)
1790 real_miny = char_miny;
1791 if (char_maxy > real_maxy)
1792 real_maxy = char_maxy;
1793 char_count++;
1794 }
1795
1796 free(pixels);
1797
1798 if (char_count < 1U) {
1799 #ifdef DEBUG_COMPUTED_FONT_METRICS
1800 TRACE(("reported metrics:\n"));
1801 TRACE((" %ux%u ascent=%u descent=%u\n", font->max_advance_width,
1802 font->height, font->ascent, font->descent));
1803 TRACE(("computed metrics:\n"));
1804 TRACE((" (no characters found)\n"));
1805 #endif
1806 *w = 0U;
1807 *h = 0U;
1808 return;
1809 }
1810
1811 *w = (unsigned) (1 + real_maxx - real_minx);
1812 *h = (unsigned) (1 + real_maxy - real_miny);
1813 *xmin = real_minx;
1814 *ymin = real_miny;
1815
1816 #ifdef DEBUG_COMPUTED_FONT_METRICS
1817 printf("reported metrics:\n");
1818 printf(" %ux%u ascent=%u descent=%u\n", font->max_advance_width,
1819 font->height, font->ascent, font->descent);
1820 printf("computed metrics:\n");
1821 printf(" real_minx=%u real_maxx=%u real_miny=%u real_maxy=%u\n",
1822 real_minx, real_maxx, real_miny, real_maxy);
1823 printf(" final: %ux%u xmin=%u ymin=%u\n", *w, *h, *xmin, *ymin);
1824 #endif
1825 }
1826
1827 #define FONT_SIZE_CACHE_SIZE 32U
1828
1829 /* Find the font pixel size which returns the font which is closest to the given
1830 * maxw and maxh without overstepping either dimension.
1831 */
1832 static XftFont *
1833 find_best_xft_font_size(XtermWidget xw,
1834 char const *fontname,
1835 unsigned maxw, unsigned maxh, unsigned max_pixels,
1836 unsigned *w, unsigned *h,
1837 unsigned *xmin, unsigned *ymin)
1838 {
1839 Display *display = XtDisplay(xw);
1840 Screen *screen = XtScreen(xw);
1841 XftFont *font;
1842 unsigned targeth;
1843 unsigned ii, cacheindex;
1844 /* FIXME: change cache to just cache the final result and put it in a
1845 * wrapper function
1846 */
1847 static struct {
1848 char fontname[REGIS_FONTNAME_LEN];
1849 unsigned maxw, maxh, max_pixels;
1850 unsigned targeth;
1851 unsigned w, h;
1852 unsigned xmin;
1853 unsigned ymin;
1854 } cache[FONT_SIZE_CACHE_SIZE];
1855
1856 assert(display);
1857 assert(screen);
1858 assert(fontname);
1859 assert(w);
1860 assert(h);
1861 assert(xmin);
1862 assert(ymin);
1863
1864 #ifdef DEBUG_FONT_SIZE_SEARCH
1865 TRACE(("determining best size of font '%s' for %ux%u glyph with max_pixels=%u\n",
1866 fontname, maxw, maxh, max_pixels));
1867 #endif
1868 cacheindex = FONT_SIZE_CACHE_SIZE;
1869 for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) {
1870 if (cache[ii].maxw == maxw && cache[ii].maxh == maxh &&
1871 cache[ii].max_pixels == max_pixels &&
1872 strcmp(cache[ii].fontname, fontname) == 0) {
1873 cacheindex = ii;
1874 break;
1875 }
1876 }
1877
1878 if (cacheindex < FONT_SIZE_CACHE_SIZE) {
1879 targeth = cache[cacheindex].targeth;
1880 } else {
1881 targeth = maxh * 10U + 5U;
1882 }
1883 for (;;) {
1884 if (targeth <= 5U) {
1885 TRACE(("Giving up finding suitable Xft font size for \"%s\" at %ux%u.\n",
1886 fontname, maxw, maxh));
1887 return NULL;
1888 }
1889
1890 /*
1891 * Xft does a bad job at:
1892 * - two-color low-resolution anti-aliased fonts
1893 * - non-anti-aliased fonts at low resolution unless a font size is
1894 * given (pixel size does not help, and the value of the font size
1895 * doesn't appear to matter).
1896 *
1897 * In those two cases it literally drops pixels, sometimes whole
1898 * columns, making the glyphs unreadable and at least ugly even when
1899 * readable.
1900 */
1901 font = NULL;
1902 /*
1903 * FIXME:
1904 * Also, we need to scale the width and height separately. The
1905 * CHAR_WIDTH and CHAR_HEIGHT attributes would seem to be ideal, but
1906 * don't appear to have any effect if set. Instead we will manually
1907 * scale the bitmap later, which may be very ugly because we won't try
1908 * to identify different parts of glyphs or preserve density.
1909 */
1910 {
1911 XftPattern *pat;
1912 XftPattern *match;
1913 XftResult status;
1914
1915 if ((pat = XftNameParse(fontname))) {
1916 #ifdef DEBUG_FONT_SIZE_SEARCH
1917 TRACE(("trying targeth=%g\n", targeth / 10.0));
1918 #endif
1919 XftPatternBuild(pat,
1920 #if 0
1921 /* arbitrary value */
1922 XFT_SIZE, XftTypeDouble, 12.0,
1923 #endif
1924 XFT_PIXEL_SIZE, XftTypeDouble, (double)
1925 targeth / 10.0,
1926 #if 0
1927 XFT_CHAR_WIDTH, XftTypeInteger, (int) maxw,
1928 XFT_CHAR_HEIGHT, XftTypeInteger, (int)
1929 (targeth / 10U),
1930 #endif
1931 XFT_SPACING, XftTypeInteger, XFT_MONO,
1932 XFT_SLANT, XftTypeInteger, 0,
1933 XFT_ANTIALIAS, XftTypeBool, False,
1934 NULL);
1935 if ((match = XftFontMatch(display,
1936 XScreenNumberOfScreen(screen),
1937 pat, &status))) {
1938 font = XftFontOpenPattern(display, match);
1939 maybeXftCache(xw, font);
1940 }
1941 XftPatternDestroy(pat);
1942 }
1943 }
1944 if (!font) {
1945 #ifdef DEBUG_FONT_SIZE_SEARCH
1946 {
1947 char buffer[1024];
1948
1949 if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer)))
1950 printf("font name unparsed: \"%s\"\n", buffer);
1951 }
1952 #endif
1953 TRACE(("unable to open a monospaced Xft font matching '%s' with pixelsize %g\n",
1954 fontname, targeth / 10.0));
1955 return NULL;
1956 }
1957 #ifdef DEBUG_FONT_SIZE_SEARCH
1958 {
1959 char buffer[1024];
1960
1961 if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) {
1962 TRACE(("Testing font named \"%s\"\n", buffer));
1963 } else {
1964 TRACE(("Testing unknown font\n"));
1965 }
1966 }
1967 #endif
1968
1969 if (cacheindex < FONT_SIZE_CACHE_SIZE &&
1970 targeth == cache[cacheindex].targeth) {
1971 *w = cache[cacheindex].w;
1972 *h = cache[cacheindex].h;
1973 *xmin = cache[cacheindex].xmin;
1974 *ymin = cache[cacheindex].ymin;
1975 } else {
1976 get_xft_glyph_dimensions(xw, font, w, h, xmin, ymin);
1977
1978 if (*w < 1 || *h < 1) {
1979 #ifdef DEBUG_FONT_SIZE_SEARCH
1980 TRACE(("got %ux%u dimensions for target size targeth=%d; trying reduced target size\n",
1981 *w, *h, targeth));
1982 #endif
1983 targeth--;
1984 continue;
1985 }
1986 }
1987 #ifdef DEBUG_FONT_SIZE_SEARCH
1988 TRACE(("checking max=%ux%u targeth=%u.%u\n", maxw, maxh, targeth /
1989 10U, targeth % 10U));
1990 #endif
1991
1992 if (*h > maxh) {
1993 XftFontClose(display, font);
1994 #ifdef DEBUG_FONT_SIZE_SEARCH
1995 TRACE(("got %ux%u glyph; too tall; reducing target size\n", *w, *h));
1996 #endif
1997 if (*h > 2U * maxh) {
1998 targeth /= (*h / maxh);
1999 } else if (targeth > 10U && *h > maxh + 1U) {
2000 targeth -= 10U;
2001 } else {
2002 targeth--;
2003 }
2004 continue;
2005 }
2006 if (*w > maxw) {
2007 XftFontClose(display, font);
2008 #ifdef DEBUG_FONT_SIZE_SEARCH
2009 TRACE(("got %ux%u glyph; too wide; reducing target size\n", *w, *h));
2010 #endif
2011 if (*w > 2U * maxw) {
2012 targeth /= (*w / maxw);
2013 } else if (targeth > 10U && *w > maxw + 1U) {
2014 targeth -= 10U;
2015 } else {
2016 targeth--;
2017 }
2018 continue;
2019 }
2020 if (*w * *h > max_pixels) {
2021 XftFontClose(display, font);
2022 #ifdef DEBUG_FONT_SIZE_SEARCH
2023 TRACE(("got %ux%u glyph; too many pixels; reducing target size\n",
2024 *w, *h));
2025 #endif
2026 if (*w * *h > 2U * max_pixels) {
2027 unsigned min = *w < *h ? *w : *h;
2028 unsigned divisor = (*w * *h) / (max_pixels * min);
2029 if (divisor > 1U) {
2030 targeth /= divisor;
2031 } else if (targeth > 10U) {
2032 targeth -= 10U;
2033 } else {
2034 targeth--;
2035 }
2036 } else {
2037 targeth--;
2038 }
2039 continue;
2040 }
2041 #ifdef DEBUG_FONT_NAME
2042 {
2043 char buffer[1024];
2044
2045 if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) {
2046 TRACE(("Final font for \"%s\" max %dx%d is \"%s\"\n",
2047 fontname, maxw, maxh, buffer));
2048 } else {
2049 TRACE(("Final font for \"%s\" max %dx%d is unknown\n",
2050 fontname, maxw, maxh));
2051 }
2052 }
2053 #endif
2054
2055 if (cacheindex == FONT_SIZE_CACHE_SIZE) {
2056 for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) {
2057 if (cache[ii].maxw == 0U || cache[ii].maxh == 0U ||
2058 cache[ii].max_pixels == 0U) {
2059 CopyFontname(cache[ii].fontname, fontname);
2060 cache[ii].maxw = maxw;
2061 cache[ii].maxh = maxh;
2062 cache[ii].max_pixels = max_pixels;
2063 cache[ii].targeth = targeth;
2064 cache[ii].w = *w;
2065 cache[ii].h = *h;
2066 cache[ii].xmin = *xmin;
2067 cache[ii].ymin = *ymin;
2068 break;
2069 }
2070 }
2071 if (ii == FONT_SIZE_CACHE_SIZE) {
2072 ii = targeth % FONT_SIZE_CACHE_SIZE;
2073 CopyFontname(cache[ii].fontname, fontname);
2074 cache[ii].maxw = maxw;
2075 cache[ii].maxh = maxh;
2076 cache[ii].max_pixels = max_pixels;
2077 cache[ii].targeth = targeth;
2078 cache[ii].w = *w;
2079 cache[ii].h = *h;
2080 cache[ii].xmin = *xmin;
2081 cache[ii].ymin = *ymin;
2082 }
2083 }
2084 return font;
2085 }
2086 }
2087 #endif
2088
2089 static int
2090 get_xft_bitmap_of_character(RegisGraphicsContext const *context,
2091 char const *fontname, int ch,
2092 unsigned maxw, unsigned maxh, Char *pixels,
2093 unsigned max_pixels, unsigned *w, unsigned *h)
2094 {
2095 /*
2096 * See Xft / RENDERFONT stuff in fontutils.c and used in utils.c
2097 * Add a separate configuration for ReGIS.
2098 */
2099 /*
2100 * FIXME: cache:
2101 * - reuse the font where possible
2102 */
2103 #ifdef XRENDERFONT
2104 XtermWidget xw = context->destination_graphic->xw;
2105 Display *display = XtDisplay(xw);
2106 XftFont *font;
2107 unsigned xmin = 0U, ymin = 0U;
2108
2109 # ifdef DEBUG_XFT_GLYPH_LOADING
2110 TRACE(("trying to load glyph '%c' at max size %dx%d\n", ch, maxw, maxh));
2111 # endif
2112 if (!(font = find_best_xft_font_size(xw, fontname, maxw, maxh,
2113 max_pixels, w, h, &xmin, &ymin))) {
2114 TRACE(("Unable to find suitable Xft font\n"));
2115 return 0;
2116 }
2117
2118 if (*w == 0U || *h == 0U) {
2119 TRACE(("empty glyph found for '%c'\n", ch));
2120 XftFontClose(display, font);
2121 return 1;
2122 }
2123
2124 if (!copy_bitmap_from_xft_font(xw, font, CharOf(ch), pixels, *w, *h,
2125 xmin, ymin)) {
2126 TRACE(("Unable to create bitmap for '%c'\n", ch));
2127 XftFontClose(display, font);
2128 return 0;
2129 }
2130 XftFontClose(display, font);
2131 # ifdef DEBUG_XFT_GLYPH_LOADING
2132 TRACE(("loaded glyph '%c' at max size %dx%d\n", ch, maxw, maxh));
2133 # endif
2134
2135 return 1;
2136 #else
2137 (void) context;
2138 (void) fontname;
2139 (void) ch;
2140 (void) maxw;
2141 (void) maxh;
2142 (void) pixels;
2143 (void) max_pixels;
2144 (void) w;
2145 (void) h;
2146
2147 TRACE(("Not rendering Xft font for ReGIS (support not compiled in).\n"));
2148 return 0;
2149 #endif
2150 }
2151
2152 static unsigned
2153 find_best_alphabet_index(RegisGraphicsContext const *context,
2154 unsigned minw, unsigned minh,
2155 unsigned targetw, unsigned targeth,
2156 unsigned max_pixels)
2157 {
2158 unsigned ii;
2159 unsigned bestmatch;
2160 unsigned bestw, besth;
2161
2162 assert(context);
2163 assert(targetw);
2164 assert(targeth);
2165 assert(max_pixels);
2166
2167 bestmatch = MAX_REGIS_ALPHABETS;
2168 bestw = 0U;
2169 besth = 0U;
2170 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
2171 if (context->alphabets[ii].alphabet_num ==
2172 context->current_text_controls->alphabet_num &&
2173 context->alphabets[ii].pixw >= minw &&
2174 context->alphabets[ii].pixh >= minh &&
2175 context->alphabets[ii].pixw <= targetw &&
2176 context->alphabets[ii].pixh <= targeth &&
2177 ((context->alphabets[ii].pixw >= bestw &&
2178 context->alphabets[ii].pixh > besth) ||
2179 (context->alphabets[ii].pixw > bestw &&
2180 context->alphabets[ii].pixh >= besth)) &&
2181 context->alphabets[ii].pixw *
2182 context->alphabets[ii].pixh <= max_pixels) {
2183 bestmatch = ii;
2184 bestw = context->alphabets[ii].pixw;
2185 besth = context->alphabets[ii].pixh;
2186 }
2187 }
2188
2189 /* If we can't find one to scale up, look for one to scale down. */
2190 if (bestmatch == MAX_REGIS_ALPHABETS) {
2191 bestw = max_pixels;
2192 besth = max_pixels;
2193 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
2194 if (context->alphabets[ii].alphabet_num ==
2195 context->current_text_controls->alphabet_num &&
2196 context->alphabets[ii].pixw >= minw &&
2197 context->alphabets[ii].pixh >= minh &&
2198 ((context->alphabets[ii].pixw <= bestw &&
2199 context->alphabets[ii].pixh < besth) ||
2200 (context->alphabets[ii].pixw < bestw &&
2201 context->alphabets[ii].pixh <= besth)) &&
2202 context->alphabets[ii].pixw *
2203 context->alphabets[ii].pixh <= max_pixels) {
2204 bestmatch = ii;
2205 bestw = context->alphabets[ii].pixw;
2206 besth = context->alphabets[ii].pixh;
2207 }
2208 }
2209 }
2210 #ifdef DEBUG_ALPHABET_LOOKUP
2211 if (bestmatch < MAX_REGIS_ALPHABETS) {
2212 TRACE(("for target size %ux%u alphabet %u found index %u size %ux%u font=%s\n",
2213 targetw, targeth, context->current_text_controls->alphabet_num,
2214 bestmatch,
2215 bestw, besth,
2216 context->alphabets[bestmatch].use_font ?
2217 context->alphabets[bestmatch].fontname : "(none)"));
2218 } else {
2219 TRACE(("for target size %ux%u alphabet %u found no suitable alphabets\n",
2220 targetw, targeth, context->current_text_controls->alphabet_num));
2221 }
2222 #endif
2223
2224 return bestmatch;
2225 }
2226
2227 #define GLYPH_WIDTH_BYTES(PIXW) ( ((PIXW) + 7U) >> 3U )
2228
2229 static int
2230 get_user_bitmap_of_character(RegisGraphicsContext const *context,
2231 int ch,
2232 unsigned alphabet_index,
2233 Char *pixels,
2234 unsigned int max_pixels)
2235 {
2236 const Char *glyph;
2237 unsigned w, h;
2238 unsigned xx, yy;
2239 unsigned byte, bit;
2240
2241 assert(context);
2242 assert(pixels);
2243
2244 if (!context->alphabets[alphabet_index].loaded[(Char) ch]) {
2245 TRACE(("BUG: in alphabet %u with alphabet index %u user glyph for '%c' not loaded\n",
2246 context->current_text_controls->alphabet_num, alphabet_index,
2247 ch));
2248 return 0;
2249 }
2250
2251 assert(context->alphabets[alphabet_index].bytes);
2252
2253 w = context->alphabets[alphabet_index].pixw;
2254 h = context->alphabets[alphabet_index].pixh;
2255 glyph = &context->alphabets[alphabet_index]
2256 .bytes[(Char) ch * GLYPH_WIDTH_BYTES(w) * h];
2257
2258 if (w * h > max_pixels) {
2259 TRACE(("in alphabet %u with alphabet index %u user glyph for '%c' is too large: %ux%u (max_pixels=%u)\n",
2260 context->current_text_controls->alphabet_num, alphabet_index,
2261 ch, w, h, max_pixels));
2262 return 0;
2263 }
2264
2265 for (yy = 0U; yy < h; yy++) {
2266 for (xx = 0U; xx < w; xx++) {
2267 byte = yy * GLYPH_WIDTH_BYTES(w) + (xx >> 3U);
2268 bit = xx & 7U;
2269 pixels[yy * w + xx] = (Char) (((unsigned) glyph[byte]
2270 >> (7U - bit)) & 1U);
2271 }
2272 }
2273
2274 return 1;
2275 }
2276
2277 /*
2278 * alphabets
2279 * 0 built-in
2280 * 1-N custom (max is 3 on VT3X0 -- up to MAX_REGIS_ALPHABETS with xterm)
2281 *
2282 * built-in 7-bit charsets
2283 * (B ASCII
2284 * (0 DEC special graphics
2285 * (> DEC technical
2286 * (A NCR British
2287 * (4 NCR Dutch
2288 * (5 NCR Finnish
2289 * (R NCR French
2290 * (9 NCR French Canadian
2291 * (K NCR German
2292 * (Y NCR Italian
2293 * (' NCR Norwegian/Danish
2294 * (!6 NCR Portuguese
2295 * (Z NCR Spanish
2296 * (7 NCR Swedish
2297 * (- NCR Swiss
2298 *
2299 * -@ ???
2300 *
2301 * built-in 8-bit charsets
2302 * )%5 DEC supplemental graphics
2303 * -A ISO Latin-1 supplemental
2304 * )< user-preferred supplemental (94 chars)
2305 *
2306 * defaults
2307 * terminal char cell size charsets angle
2308 * VT3x0 S1 0:ASCII(94) 0 (positive)
2309 *
2310 */
2311 static void
2312 get_bitmap_of_character(RegisGraphicsContext const *context, int ch,
2313 unsigned maxw, unsigned maxh, Char *pixels,
2314 unsigned *w, unsigned *h, unsigned max_pixels)
2315 {
2316 unsigned bestmatch;
2317 char const *fontname = NULL;
2318
2319 assert(context);
2320 assert(w);
2321 assert(h);
2322
2323 #ifdef DEBUG_GLYPH_RETRIEVAL
2324 TRACE(("getting bitmap of glyph %d, current alphabet %d\n", ch,
2325 context->current_text_controls->alphabet_num));
2326 #endif
2327
2328 if (maxw < 1U || maxh < 1U || max_pixels < 1U) {
2329 *w = 0U;
2330 *h = 0U;
2331 return;
2332 }
2333
2334 if (context->current_text_controls->alphabet_num == 0)
2335 fontname = context->builtin_font;
2336
2337 *w = 0U;
2338 *h = 0U;
2339
2340 bestmatch = find_best_alphabet_index(context, 1U, 1U, maxw, maxh,
2341 max_pixels);
2342 if (bestmatch < MAX_REGIS_ALPHABETS) {
2343 RegisAlphabet const *alpha = &context->alphabets[bestmatch];
2344
2345 #ifdef DEBUG_GLYPH_RETRIEVAL
2346 TRACE(("checking user glyph for slot=%u alphabet=%d use_font=%d loaded=%d\n",
2347 bestmatch, alpha->alphabet_num, alpha->use_font,
2348 alpha->loaded[ch]));
2349 #endif
2350 if (!alpha->use_font &&
2351 get_user_bitmap_of_character(context, ch, bestmatch, pixels,
2352 max_pixels)) {
2353 #ifdef DEBUG_GLYPH_RETRIEVAL
2354 TRACE(("found user glyph for alphabet number %d (index %u)\n\n",
2355 alpha->alphabet_num, bestmatch));
2356 #endif
2357 *w = alpha->pixw;
2358 *h = alpha->pixh;
2359 return;
2360 }
2361
2362 if (alpha->use_font)
2363 fontname = alpha->fontname;
2364 }
2365
2366 if (fontname) {
2367 #ifdef DEBUG_GLYPH_RETRIEVAL
2368 TRACE(("using xft font %s\n", fontname));
2369 #endif
2370 if (get_xft_bitmap_of_character(context, fontname, ch,
2371 maxw, maxh, pixels,
2372 max_pixels, w, h)) {
2373 if (*w > maxw) {
2374 TRACE(("BUG: Xft glyph is too wide: %ux%u but max is %ux%u\n",
2375 *w, *h, maxw, maxh));
2376 } else if (*h > maxh) {
2377 TRACE(("BUG: Xft glyph is too tall: %ux%u but max is %ux%u\n",
2378 *w, *h, maxw, maxh));
2379 } else if (*w * *h > max_pixels) {
2380 TRACE(("BUG: Xft glyph has too many pixels: %u but max is %u\n",
2381 *w * *h, max_pixels));
2382 } else {
2383 TRACE(("got glyph from \"%s\" for alphabet number %d\n",
2384 fontname, context->current_text_controls->alphabet_num));
2385 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
2386 if (IS_DEBUG_CHAR(ch)) {
2387 printf("got %ux%u Xft bitmap for '%c' target size %ux%u:\n",
2388 *w, *h,
2389 ch, maxw, maxh);
2390 dump_bitmap_pixels(pixels, *w, *h);
2391 printf("\n");
2392 }
2393 #endif
2394 return;
2395 }
2396 }
2397 }
2398
2399 TRACE(("unable to load any bitmap for character '%c' in alphabet number %u at %ux%u\n",
2400 ch, context->current_text_controls->alphabet_num, maxw, maxh));
2401
2402 /*
2403 * The VT3x0 series (and probably earlier ReGIS implementations) use a solid
2404 * block glyph for unknown glyphs.
2405 */
2406 {
2407 unsigned xx, yy;
2408
2409 *w = MIN2(8U, maxh);
2410 *h = MIN2(10U, maxw);
2411 for (yy = 0U; yy < *h; yy++)
2412 for (xx = 0U; xx < *w; xx++)
2413 pixels[yy * *w + xx] = '\1';
2414 }
2415 }
2416
2417 #define ROT_SHEAR_SCALE 8192
2418 #define SIGNED_UNSIGNED_MOD(VAL, BASE) ( (((VAL) % (int) (BASE)) + (int) (BASE)) % (int) (BASE) )
2419
2420 static unsigned
2421 get_shade_character_pixel(Char const *pixels, unsigned w, unsigned h,
2422 unsigned smaxf, unsigned scale, int slant_dx,
2423 int px, int py)
2424 {
2425 unsigned wx, wy;
2426 unsigned fx, fy;
2427
2428 wx = (unsigned) SIGNED_UNSIGNED_MOD(px -
2429 (slant_dx * SIGNED_UNSIGNED_MOD(py, smaxf))
2430 / ROT_SHEAR_SCALE, smaxf);
2431 wy = (unsigned) SIGNED_UNSIGNED_MOD(py, smaxf);
2432
2433 fx = (wx * scale) >> SCALE_FIXED_POINT;
2434 fy = (wy * scale) >> SCALE_FIXED_POINT;
2435 if (fx < w && fy < h) {
2436 return (unsigned) pixels[fy * w + fx];
2437 }
2438 return 0U;
2439 }
2440
2441 static void
2442 draw_character(RegisGraphicsContext *context, int ch,
2443 int slant_dx, int rot_shear_x,
2444 int rot_shear_y, int x_sign_x, int x_sign_y,
2445 int y_sign_x, int y_sign_y)
2446 {
2447 const unsigned xmaxd = context->current_text_controls->character_display_w;
2448 const unsigned ymaxd = context->current_text_controls->character_display_h;
2449 const unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
2450 const unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
2451 unsigned w, h;
2452 unsigned xscale, yscale;
2453 unsigned fx, fy;
2454 unsigned px, py;
2455 int sx;
2456 int rx, ry;
2457 int ox, oy;
2458 unsigned pad_left, pad_right;
2459 unsigned pad_top, pad_bottom;
2460 Char pixels[MAX_GLYPH_PIXELS];
2461 unsigned value;
2462
2463 get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
2464 MAX_GLYPH_PIXELS);
2465 if (w < 1 || h < 1) {
2466 return;
2467 }
2468
2469 if (xmaxd > xmaxf) {
2470 pad_left = (xmaxd - xmaxf) / 2U;
2471 pad_right = (xmaxd - xmaxf) - pad_left;
2472 } else {
2473 pad_left = 0U;
2474 pad_right = 0U;
2475 }
2476 if (ymaxd > ymaxf) {
2477 pad_top = (ymaxd - ymaxf) / 2U;
2478 pad_bottom = (ymaxd - ymaxf) - pad_top;
2479 } else {
2480 pad_top = 0U;
2481 pad_bottom = 0U;
2482 }
2483
2484 xscale = (w << SCALE_FIXED_POINT) / xmaxf;
2485 yscale = (h << SCALE_FIXED_POINT) / ymaxf;
2486
2487 for (py = 0U; py < ymaxd; py++) {
2488 for (px = 0U; px < xmaxd; px++) {
2489 if (py < pad_top || px < pad_left ||
2490 py >= ymaxd - pad_bottom || px >= xmaxd - pad_right) {
2491 value = 0U;
2492 } else {
2493 fx = ((px - pad_left) * xscale) >> SCALE_FIXED_POINT;
2494 fy = ((py - pad_top) * yscale) >> SCALE_FIXED_POINT;
2495 if (fx < w && fy < h) {
2496 value = (unsigned) pixels[fy * w + fx];
2497 } else {
2498 value = 0U;
2499 }
2500 }
2501
2502 sx = (int) px + (slant_dx * (int) py) / ROT_SHEAR_SCALE;
2503 rx = x_sign_x * sx + x_sign_y * (int) py;
2504 ry = y_sign_x * sx + y_sign_y * (int) py;
2505 ox = rx + (rot_shear_x * ry) / ROT_SHEAR_SCALE;
2506 oy = ry + (rot_shear_y * ox) / ROT_SHEAR_SCALE;
2507 ox += (rot_shear_x * oy) / ROT_SHEAR_SCALE;
2508
2509 draw_regis_pixel(context,
2510 (int) context->graphics_output_cursor_x + ox,
2511 (int) context->graphics_output_cursor_y + oy,
2512 value);
2513 }
2514 }
2515 }
2516
2517 static void
2518 move_text(RegisGraphicsContext *context, int dx, int dy)
2519 {
2520 double total_rotation;
2521 int str_invert;
2522 int str_shear_x, str_shear_y;
2523 int ox, oy;
2524
2525 total_rotation = 2.0 * M_PI *
2526 context->current_text_controls->string_rotation / 360.0;
2527 while (total_rotation > 1.5 * M_PI) {
2528 total_rotation -= 2.0 * M_PI;
2529 }
2530 if (total_rotation > 0.5 * M_PI) {
2531 total_rotation -= M_PI;
2532 str_invert = -1;
2533 } else {
2534 str_invert = 1;
2535 }
2536 str_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
2537 str_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
2538
2539 total_rotation = 2.0 * M_PI *
2540 context->current_text_controls->character_rotation / 360.0;
2541 while (total_rotation > 1.5 * M_PI) {
2542 total_rotation -= 2.0 * M_PI;
2543 }
2544
2545 TRACE(("str_shear: %.5f, %.5f (sign=%d)\n",
2546 str_shear_x / (double) ROT_SHEAR_SCALE,
2547 str_shear_y / (double) ROT_SHEAR_SCALE,
2548 str_invert));
2549
2550 ox = str_invert * dx + (str_shear_x * dy) / ROT_SHEAR_SCALE;
2551 oy = str_invert * dy + (str_shear_y * ox) / ROT_SHEAR_SCALE;
2552 ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
2553
2554 TRACE(("after pv output updating position %+d,%+d\n", ox, oy));
2555 context->graphics_output_cursor_x += ox;
2556 context->graphics_output_cursor_y += oy;
2557
2558 return;
2559 }
2560
2561 #define UPSCALE_TEXT_DIMENSION(D) do { \
2562 *(D) = (unsigned)((double)(*(D)) * M_SQRT2); \
2563 } while (0)
2564
2565 static void
2566 draw_text(RegisGraphicsContext *context, char const *str)
2567 {
2568 #ifndef ENABLE_DISTORTIONLESS_ROTATION
2569 RegisTextControls *old_text_controls = NULL;
2570 static RegisTextControls scratch_text_controls;
2571 #endif
2572 double total_rotation;
2573 size_t ii;
2574 int str_invert;
2575 int str_shear_x, str_shear_y;
2576 int slant_dx;
2577 int chr_x_sign_x, chr_x_sign_y;
2578 int chr_y_sign_x, chr_y_sign_y;
2579 int chr_shear_x, chr_shear_y;
2580 int begin_x, begin_y;
2581 int rx, ry;
2582 int ox, oy;
2583
2584 #ifdef DEBUG_ALPHABETS
2585 {
2586 unsigned n;
2587
2588 for (n = 0U; n < MAX_REGIS_ALPHABETS; n++) {
2589 printf("alphabet index %u\n", n);
2590 if (context->alphabets[n].alphabet_num != INVALID_ALPHABET_NUM) {
2591 printf(" alphabet_num=%u\n", context->alphabets[n].alphabet_num);
2592 printf(" pixw=%d\n", context->alphabets[n].pixw);
2593 printf(" pixh=%d\n", context->alphabets[n].pixh);
2594 printf(" name=\"%s\"\n", context->alphabets[n].name);
2595 printf(" use_font=%d\n", context->alphabets[n].use_font);
2596 printf(" fontname=\"%s\"\n", context->alphabets[n].fontname);
2597 printf(" bytes=%p\n", context->alphabets[n].bytes);
2598 }
2599 }
2600 }
2601 #endif
2602
2603 if (context->current_text_controls->slant <= -75 ||
2604 context->current_text_controls->slant >= +75) {
2605 TRACE(("ERROR: unsupported character slant angle %d\n",
2606 context->current_text_controls->slant));
2607 return;
2608 }
2609
2610 /* FIXME: grab when first entering command */
2611 begin_x = context->graphics_output_cursor_x;
2612 begin_y = context->graphics_output_cursor_y;
2613
2614 #ifndef ENABLE_DISTORTIONLESS_ROTATION
2615 if (context->current_text_controls->character_rotation != 0 &&
2616 context->current_text_controls->character_rotation != 90 &&
2617 context->current_text_controls->character_rotation != 180 &&
2618 context->current_text_controls->character_rotation != 270) {
2619 old_text_controls = context->current_text_controls;
2620 scratch_text_controls = *context->current_text_controls;
2621 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_display_w);
2622 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_display_h);
2623 /* FIXME: Not sure if this is really scaled. The increment seems to
2624 * _not_ be scaled.
2625 */
2626 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_unit_cell_w);
2627 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_unit_cell_h);
2628 context->current_text_controls = &scratch_text_controls;
2629 TRACE(("scaled up text to %dx%d\n",
2630 scratch_text_controls.character_display_w,
2631 scratch_text_controls.character_display_h));
2632 }
2633 #endif
2634
2635 total_rotation = 2.0 * M_PI *
2636 context->current_text_controls->string_rotation / 360.0;
2637 while (total_rotation > 1.5 * M_PI) {
2638 total_rotation -= 2.0 * M_PI;
2639 }
2640 if (total_rotation > 0.5 * M_PI) {
2641 total_rotation -= M_PI;
2642 str_invert = -1;
2643 } else {
2644 str_invert = 1;
2645 }
2646 str_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
2647 str_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
2648
2649 total_rotation = 2.0 * M_PI *
2650 context->current_text_controls->character_rotation / 360.0;
2651 while (total_rotation > 1.5 * M_PI) {
2652 total_rotation -= 2.0 * M_PI;
2653 }
2654 if (total_rotation > 0.5 * M_PI) {
2655 total_rotation -= M_PI;
2656 chr_x_sign_x = -1;
2657 chr_x_sign_y = 0;
2658 chr_y_sign_x = 0;
2659 chr_y_sign_y = -1;
2660 } else {
2661 chr_x_sign_x = 1;
2662 chr_x_sign_y = 0;
2663 chr_y_sign_x = 0;
2664 chr_y_sign_y = 1;
2665 }
2666 chr_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
2667 chr_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
2668
2669 {
2670 const int slant = context->current_text_controls->slant;
2671
2672 TRACE(("float version: %.5f\n", tan(2.0 * M_PI * abs(slant) / 360.0)));
2673 /* The slant is negative for forward-leaning characters. */
2674 if (slant > 0) {
2675 slant_dx = (int) +(tan(2.0 * M_PI * abs(slant) / 360.0) * ROT_SHEAR_SCALE);
2676 } else if (slant < 0) {
2677 slant_dx = (int) -(tan(2.0 * M_PI * abs(slant) / 360.0) * ROT_SHEAR_SCALE);
2678 } else {
2679 slant_dx = 0;
2680 }
2681 TRACE(("string rotation: %d\n",
2682 context->current_text_controls->string_rotation));
2683 TRACE(("character rotation: %d\n",
2684 context->current_text_controls->character_rotation));
2685 TRACE(("character slant: %d (%.5f pixels per line)\n",
2686 slant, slant_dx / (double) ROT_SHEAR_SCALE));
2687 }
2688
2689 TRACE(("str_shear: %.5f, %.5f (sign=%d)\n",
2690 str_shear_x / (double) ROT_SHEAR_SCALE,
2691 str_shear_y / (double) ROT_SHEAR_SCALE,
2692 str_invert));
2693 TRACE(("chr_shear: %.5f, %.5f (xsign=%d,%d, ysign=%d,%d)\n",
2694 chr_shear_x / (double) ROT_SHEAR_SCALE,
2695 chr_shear_y / (double) ROT_SHEAR_SCALE,
2696 chr_x_sign_x, chr_x_sign_y,
2697 chr_y_sign_x, chr_y_sign_y));
2698 TRACE(("character_inc: %d,%d\n",
2699 context->current_text_controls->character_inc_x, context->current_text_controls->character_inc_y));
2700
2701 rx = 0;
2702 ry = 0;
2703 for (ii = 0U; ii < strlen(str); ii++) {
2704 switch (str[ii]) {
2705 case '\r':
2706 rx = 0;
2707 break;
2708 case '\n':
2709 ry += (int) context->current_text_controls->character_display_h;
2710 break;
2711 case '\b':
2712 rx -= context->current_text_controls->character_inc_x;
2713 ry -= context->current_text_controls->character_inc_y;
2714 break;
2715 case '\t':
2716 rx += context->current_text_controls->character_inc_x;
2717 ry += context->current_text_controls->character_inc_y;
2718 break;
2719 default:
2720 ox = str_invert * rx + (str_shear_x * ry) / ROT_SHEAR_SCALE;
2721 oy = str_invert * ry + (str_shear_y * ox) / ROT_SHEAR_SCALE;
2722 ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
2723 TRACE(("during text output updating position to %d,%d + %+d,%+d for '%c'\n",
2724 begin_x, begin_y, ox, oy, str[ii]));
2725 context->graphics_output_cursor_x = begin_x + ox;
2726 context->graphics_output_cursor_y = begin_y + oy;
2727 draw_character(context, str[ii], slant_dx,
2728 chr_shear_x, chr_shear_y,
2729 chr_x_sign_x, chr_x_sign_y,
2730 chr_y_sign_x, chr_y_sign_y);
2731 rx += context->current_text_controls->character_inc_x;
2732 ry += context->current_text_controls->character_inc_y;
2733 }
2734 }
2735
2736 ox = str_invert * rx + (str_shear_x * ry) / ROT_SHEAR_SCALE;
2737 oy = str_invert * ry + (str_shear_y * ox) / ROT_SHEAR_SCALE;
2738 ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
2739 TRACE(("after text output updating position to %d,%d + %+d,%+d\n",
2740 begin_x, begin_y, ox, oy));
2741 context->graphics_output_cursor_x = begin_x + ox;
2742 context->graphics_output_cursor_y = begin_y + oy;
2743
2744 #ifndef ENABLE_DISTORTIONLESS_ROTATION
2745 if (context->current_text_controls->character_rotation != 0 &&
2746 context->current_text_controls->character_rotation != 90 &&
2747 context->current_text_controls->character_rotation != 180 &&
2748 context->current_text_controls->character_rotation != 270) {
2749 context->current_text_controls = old_text_controls;
2750 }
2751 #endif
2752
2753 context->destination_graphic->dirty = True;
2754 return;
2755 }
2756
2757 /*
2758 * standard character cell sizes
2759 * number disp cell unit cell offset
2760 * S0 [ 9, 10] [ 8, disp_h] [disp_w, 0]
2761 * S1 [ 9, 20] [ 8, disp_h] [disp_w, 0]
2762 * S2 [ 18, 30] [ 16, disp_h] [disp_w, 0]
2763 * S3 [ 27, 45] [ 24, disp_h] [disp_w, 0]
2764 * S4 [ 36, 60] [ 32, disp_h] [disp_w, 0]
2765 * S5 [ 45, 75] [ 40, disp_h] [disp_w, 0]
2766 * S6 [ 54, 90] [ 48, disp_h] [disp_w, 0]
2767 * S7 [ 63,105] [ 56, disp_h] [disp_w, 0]
2768 * S8 [ 72,120] [ 64, disp_h] [disp_w, 0]
2769 * S9 [ 81,135] [ 72, disp_h] [disp_w, 0]
2770 * S10 [ 90,150] [ 80, disp_h] [disp_w, 0]
2771 * S11 [ 99,165] [ 88, disp_h] [disp_w, 0]
2772 * S12 [108,180] [ 96, disp_h] [disp_w, 0]
2773 * S13 [117,195] [104, disp_h] [disp_w, 0]
2774 * S14 [126,210] [112, disp_h] [disp_w, 0]
2775 * S15 [135,225] [120, disp_h] [disp_w, 0]
2776 * S16 [144,240] [128, disp_h] [disp_w, 0]
2777 */
2778 static int
2779 get_standard_character_size(int standard, unsigned *disp_w, unsigned
2780 *disp_h, unsigned *unit_w, unsigned *unit_h,
2781 int *off_x, int *off_y)
2782 {
2783 switch (standard) {
2784 case 0:
2785 *disp_w = 9U;
2786 *disp_h = 10U;
2787 *unit_w = 8U;
2788 break;
2789 case 1:
2790 *disp_w = 9U;
2791 *disp_h = 20U;
2792 *unit_w = 8U;
2793 break;
2794 case 2:
2795 *disp_w = 18U;
2796 *disp_h = 30U;
2797 *unit_w = 16U;
2798 break;
2799 case 3:
2800 *disp_w = 27U;
2801 *disp_h = 45U;
2802 *unit_w = 24U;
2803 break;
2804 case 4:
2805 *disp_w = 36U;
2806 *disp_h = 60U;
2807 *unit_w = 32U;
2808 break;
2809 case 5:
2810 *disp_w = 45U;
2811 *disp_h = 75U;
2812 *unit_w = 40U;
2813 break;
2814 case 6:
2815 *disp_w = 54U;
2816 *disp_h = 90U;
2817 *unit_w = 48U;
2818 break;
2819 case 7:
2820 *disp_w = 63U;
2821 *disp_h = 105U;
2822 *unit_w = 56U;
2823 break;
2824 case 8:
2825 *disp_w = 72U;
2826 *disp_h = 120U;
2827 *unit_w = 64U;
2828 break;
2829 case 9:
2830 *disp_w = 81U;
2831 *disp_h = 135U;
2832 *unit_w = 72U;
2833 break;
2834 case 10:
2835 *disp_w = 90U;
2836 *disp_h = 150U;
2837 *unit_w = 80U;
2838 break;
2839 case 11:
2840 *disp_w = 99U;
2841 *disp_h = 165U;
2842 *unit_w = 88U;
2843 break;
2844 case 12:
2845 *disp_w = 108U;
2846 *disp_h = 180U;
2847 *unit_w = 96U;
2848 break;
2849 case 13:
2850 *disp_w = 117U;
2851 *disp_h = 195U;
2852 *unit_w = 104U;
2853 break;
2854 case 14:
2855 *disp_w = 126U;
2856 *disp_h = 210U;
2857 *unit_w = 112U;
2858 break;
2859 case 15:
2860 *disp_w = 135U;
2861 *disp_h = 225U;
2862 *unit_w = 120U;
2863 break;
2864 case 16:
2865 *disp_w = 144U;
2866 *disp_h = 240U;
2867 *unit_w = 128U;
2868 break;
2869 default:
2870 return 1;
2871 }
2872 *unit_h = *disp_h;
2873 *off_x = (int) *disp_w;
2874 *off_y = 0;
2875
2876 return 0;
2877 }
2878
2879 static void
2880 init_fragment(RegisDataFragment *fragment, char const *str)
2881 {
2882 assert(fragment);
2883 assert(str);
2884
2885 fragment->start = str;
2886 fragment->len = (unsigned) strlen(str);
2887 fragment->pos = 0U;
2888 }
2889
2890 static void
2891 copy_fragment(RegisDataFragment *dst, RegisDataFragment const *src)
2892 {
2893 assert(dst);
2894 assert(src);
2895
2896 dst->start = src->start;
2897 dst->len = src->len;
2898 dst->pos = src->pos;
2899 }
2900
2901 static char
2902 peek_fragment(RegisDataFragment const *fragment)
2903 {
2904 assert(fragment);
2905
2906 if (fragment->pos < fragment->len) {
2907 return fragment->start[fragment->pos];
2908 }
2909 return '\0';
2910 }
2911
2912 static char
2913 pop_fragment(RegisDataFragment *fragment)
2914 {
2915 assert(fragment);
2916
2917 if (fragment->pos < fragment->len) {
2918 return fragment->start[fragment->pos++];
2919 }
2920 return '\0';
2921 }
2922
2923 static char
2924 get_fragment(RegisDataFragment const *fragment, unsigned pos)
2925 {
2926 assert(fragment);
2927
2928 if (fragment->pos + pos < fragment->len) {
2929 return fragment->start[fragment->pos + pos];
2930 }
2931 return '\0';
2932 }
2933
2934 #define fragment_length(f) (f)->len
2935
2936 static unsigned
2937 fragment_remaining(RegisDataFragment const *fragment)
2938 {
2939 assert(fragment);
2940
2941 if (fragment->pos > fragment->len)
2942 return 0U;
2943 return fragment->len - fragment->pos;
2944 }
2945
2946 static int
2947 fragment_consumed(RegisDataFragment const *fragment)
2948 {
2949 assert(fragment);
2950
2951 return fragment->pos >= fragment->len;
2952 }
2953
2954 static void
2955 fragment_to_string(RegisDataFragment const *fragment, char *out,
2956 unsigned outlen)
2957 {
2958 unsigned remaininglen;
2959 unsigned endpos;
2960
2961 assert(fragment);
2962 assert(out);
2963
2964 if (!outlen)
2965 return;
2966 remaininglen = fragment->len - fragment->pos;
2967 if (remaininglen < outlen - 1U) {
2968 endpos = remaininglen;
2969 } else {
2970 endpos = outlen - 1U;
2971 }
2972 strncpy(out, &fragment->start[fragment->pos], (size_t) endpos);
2973 out[endpos] = '\0';
2974 }
2975
2976 #define MAX_FRAG 1024
2977 static char const *
2978 fragment_to_tempstr(RegisDataFragment const *fragment)
2979 {
2980 static char tempstr[MAX_FRAG];
2981
2982 assert(fragment);
2983
2984 fragment_to_string(fragment, tempstr, MAX_FRAG);
2985 return tempstr;
2986 }
2987
2988 static int
2989 skip_regis_whitespace(RegisDataFragment *input)
2990 {
2991 int skipped = 0;
2992
2993 assert(input);
2994
2995 while (!fragment_consumed(input)) {
2996 char ch = peek_fragment(input);
2997 if (ch != ',' && !IsSpace(ch)) {
2998 break;
2999 }
3000 if (ch == '\n') {
3001 TRACE(("end of input line\n\n"));
3002 }
3003 skipped = 1;
3004 pop_fragment(input);
3005 }
3006
3007 if (skipped)
3008 return 1;
3009 return 0;
3010 }
3011
3012 static int
3013 extract_regis_extent(RegisDataFragment *input, RegisDataFragment *output)
3014 {
3015 char ch;
3016
3017 assert(input);
3018 assert(output);
3019
3020 output->start = &input->start[input->pos];
3021 output->len = 0U;
3022 output->pos = 0U;
3023
3024 if (input->pos >= input->len)
3025 return 0;
3026
3027 ch = input->start[input->pos];
3028 if (ch != '[')
3029 return 0;
3030 input->pos++;
3031 output->start++;
3032
3033 /* FIXME: truncate to 16 bit signed integers */
3034 for (; input->pos < input->len; input->pos++, output->len++) {
3035 ch = input->start[input->pos];
3036 if (ch == ';') {
3037 TRACE(("DATA_ERROR: end of input before closing bracket\n"));
3038 break;
3039 }
3040 if (ch == ']')
3041 break;
3042 }
3043 if (ch == ']')
3044 input->pos++;
3045
3046 return 1;
3047 }
3048
3049 static int
3050 extract_regis_num(RegisDataFragment *input, RegisDataFragment *output)
3051 {
3052 char ch = 0;
3053 int has_digits = 0;
3054
3055 assert(input);
3056 assert(output);
3057
3058 output->start = &input->start[input->pos];
3059 output->len = 0U;
3060 output->pos = 0U;
3061
3062 if (input->start[input->pos] == '-' ||
3063 input->start[input->pos] == '+') {
3064 input->pos++;
3065 output->len++;
3066 }
3067
3068 for (; input->pos < input->len; input->pos++, output->len++) {
3069 ch = input->start[input->pos];
3070 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
3071 ch != '4' && ch != '5' && ch != '6' && ch != '7' &&
3072 ch != '8' && ch != '9') {
3073 break;
3074 }
3075 has_digits = 1;
3076 }
3077
3078 /* FIXME: what degenerate forms should be accepted ("E10" "1E" "1e" "1." "1ee10")? */
3079 /* FIXME: the terminal is said to support "floating point values", truncating to int... what do these look like? */
3080 if (has_digits && ch == 'E') {
3081 input->pos++;
3082 output->len++;
3083 for (; input->pos < input->len; input->pos++, output->len++) {
3084 ch = input->start[input->pos];
3085 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
3086 ch != '4' && ch != '5' && ch != '6' && ch != '7' &&
3087 ch != '8' && ch != '9') {
3088 break;
3089 }
3090 }
3091 }
3092
3093 return has_digits;
3094 }
3095
3096 static int
3097 extract_regis_pixelvector(RegisDataFragment *input, RegisDataFragment *output)
3098 {
3099 char ch;
3100 int has_digits;
3101
3102 assert(input);
3103 assert(output);
3104
3105 output->start = &input->start[input->pos];
3106 output->len = 0U;
3107 output->pos = 0U;
3108
3109 if (input->pos < input->len) {
3110 ch = input->start[input->pos];
3111 if (ch == '+' || ch == '-') {
3112 input->pos++;
3113 output->len++;
3114 }
3115 }
3116
3117 has_digits = 0;
3118 for (; input->pos < input->len; input->pos++, output->len++) {
3119 ch = input->start[input->pos];
3120 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
3121 ch != '4' && ch != '5' && ch != '6' && ch != '7') {
3122 break;
3123 }
3124 has_digits = 1;
3125 }
3126
3127 return has_digits;
3128 }
3129
3130 static int
3131 extract_regis_command(RegisDataFragment *input, char *command)
3132 {
3133 char ch;
3134
3135 assert(input);
3136 assert(command);
3137
3138 if (input->pos >= input->len)
3139 return 0;
3140
3141 ch = input->start[input->pos];
3142 if (ch == '\0' || ch == ';') {
3143 return 0;
3144 }
3145 if (!islower(CharOf(ch)) && !isupper(CharOf(ch)) && ch != '@') {
3146 return 0;
3147 }
3148 *command = ch;
3149 input->pos++;
3150
3151 return 1;
3152 }
3153
3154 static int
3155 extract_regis_string(RegisDataFragment *input, char *out, unsigned maxlen)
3156 {
3157 char open_quote_ch;
3158 char ch;
3159 unsigned outlen;
3160
3161 assert(input);
3162 assert(out);
3163 assert(maxlen > 0U);
3164
3165 if (input->pos >= input->len)
3166 return 0;
3167
3168 ch = peek_fragment(input);
3169 if (ch != '\'' && ch != '"')
3170 return 0;
3171 open_quote_ch = ch;
3172 outlen = 0U;
3173 pop_fragment(input);
3174
3175 ch = '\0';
3176 while (!fragment_consumed(input)) {
3177 char prev_ch = ch;
3178 ch = peek_fragment(input);
3179 /* ';' (resync) and '@' (macrograph) are not recognized in strings */
3180 if (prev_ch == open_quote_ch) {
3181 if (ch == open_quote_ch) {
3182 if (outlen < maxlen) {
3183 out[outlen] = ch;
3184 }
3185 outlen++;
3186 pop_fragment(input);
3187 ch = '\0';
3188 continue;
3189 }
3190 if (outlen < maxlen)
3191 out[outlen] = '\0';
3192 else
3193 out[maxlen] = '\0';
3194 return 1;
3195 }
3196 if (ch == '\0')
3197 break;
3198 if (ch != open_quote_ch) {
3199 if (outlen < maxlen)
3200 out[outlen] = ch;
3201 outlen++;
3202 }
3203 pop_fragment(input);
3204 }
3205 if (ch == open_quote_ch) {
3206 pop_fragment(input);
3207 if (outlen < maxlen)
3208 out[outlen] = '\0';
3209 else
3210 out[maxlen] = '\0';
3211 return 1;
3212 }
3213 /* FIXME: handle multiple strings concatenated with commas */
3214
3215 TRACE(("DATA_ERROR: end of input before closing quote\n"));
3216 return 0;
3217 }
3218
3219 static int
3220 extract_regis_parenthesized_data(RegisDataFragment *input,
3221 RegisDataFragment *output)
3222 {
3223 char ch;
3224 char open_quote_ch;
3225 int nesting;
3226
3227 assert(input);
3228 assert(output);
3229
3230 output->start = &input->start[input->pos];
3231 output->len = 0U;
3232 output->pos = 0U;
3233
3234 if (input->pos >= input->len)
3235 return 0;
3236
3237 ch = input->start[input->pos];
3238 if (ch != '(')
3239 return 0;
3240 input->pos++;
3241 output->start++;
3242 nesting = 1;
3243 open_quote_ch = '\0';
3244
3245 ch = '\0';
3246 for (; input->pos < input->len; input->pos++, output->len++) {
3247 char prev_ch = ch;
3248 ch = input->start[input->pos];
3249 if (ch == '\'' || ch == '"') {
3250 if (open_quote_ch == '\0') {
3251 open_quote_ch = ch;
3252 } else {
3253 if (ch == prev_ch && prev_ch == open_quote_ch) {
3254 ch = '\0';
3255 } else if (ch == open_quote_ch) {
3256 open_quote_ch = '\0';
3257 }
3258 }
3259 continue;
3260 }
3261 if (open_quote_ch != '\0')
3262 continue;
3263
3264 if (ch == ';') {
3265 TRACE(("leaving parenthesized data nested %d levels deep due to command termination character\n",
3266 nesting));
3267 break;
3268 }
3269 if (ch == '(')
3270 nesting++;
3271 if (ch == ')') {
3272 nesting--;
3273 if (nesting == 0) {
3274 input->pos++;
3275 return 1;
3276 }
3277 }
3278 }
3279
3280 TRACE(("DATA_ERROR: end of input before closing paren (%d levels deep)\n",
3281 nesting));
3282 return 0;
3283 }
3284
3285 static int
3286 extract_regis_option(RegisDataFragment *input,
3287 char *option,
3288 RegisDataFragment *output)
3289 {
3290 char ch;
3291 int paren_level, bracket_level;
3292 char open_quote_ch;
3293
3294 assert(input);
3295 assert(option);
3296 assert(output);
3297
3298 /* LETTER suboptions* value? */
3299 /*
3300 * FIXME: what are the rules for using separate parens vs. sharing between
3301 * options?
3302 */
3303
3304 output->start = &input->start[input->pos];
3305 output->len = 0U;
3306 output->pos = 0U;
3307
3308 if (input->pos >= input->len) {
3309 return 0;
3310 }
3311
3312 ch = input->start[input->pos];
3313 /* FIXME: are options always letters or are some special characters ok? */
3314 if (ch == ';' || ch == ',' ||
3315 ch == '(' || ch == ')' ||
3316 ch == '[' || ch == ']' ||
3317 ch == '"' || ch == '\'' ||
3318 isdigit(CharOf(ch))) {
3319 return 0;
3320 }
3321 *option = ch;
3322 input->pos++;
3323 output->start++;
3324 paren_level = 0;
3325 bracket_level = 0;
3326
3327 open_quote_ch = '\0';
3328 for (; input->pos < input->len; input->pos++, output->len++) {
3329 ch = input->start[input->pos];
3330 TRACE(("looking at char '%c' in option '%c'\n", ch, *option));
3331 /* FIXME: any special rules for commas? */
3332 /* FIXME: handle escaped quotes */
3333 if (ch == '\'' || ch == '"') {
3334 if (open_quote_ch == ch) {
3335 open_quote_ch = '\0';
3336 } else {
3337 open_quote_ch = ch;
3338 }
3339 continue;
3340 }
3341 if (open_quote_ch != '\0')
3342 continue;
3343 if (ch == '(') {
3344 paren_level++;
3345 }
3346 if (ch == ')') {
3347 paren_level--;
3348 if (paren_level < 0) {
3349 TRACE(("DATA_ERROR: found ReGIS option has value with too many close parens \"%c\"\n",
3350 *option));
3351 return 0;
3352 }
3353 }
3354 if (ch == '[') {
3355 bracket_level++;
3356 }
3357 if (ch == ']') {
3358 bracket_level--;
3359 if (bracket_level < 0) {
3360 TRACE(("DATA_ERROR: found ReGIS option has value with too many close brackets \"%c\"\n",
3361 *option));
3362 return 0;
3363 }
3364 }
3365 if (paren_level == 0 && bracket_level == 0) {
3366 /*
3367 * Top-level commas indicate the end of this option and the start of
3368 * another.
3369 */
3370 if (ch == ',')
3371 break;
3372 /*
3373 * Top-level command/option/suboption names also indicate the end of
3374 * this option. "E" is valid as the exponent indicator in a numeric
3375 * parameter.
3376 */
3377 if (ch != 'E' && ch != 'e' &&
3378 ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')))
3379 break;
3380 }
3381 if (ch == ';')
3382 break;
3383 }
3384 if (paren_level != 0) {
3385 TRACE(("DATA_ERROR: mismatched parens in argument to ReGIS option \"%c\"\n",
3386 *option));
3387 return 0;
3388 }
3389 if (bracket_level != 0) {
3390 TRACE(("DATA_ERROR: mismatched brackets in argument to ReGIS option \"%c\"\n",
3391 *option));
3392 return 0;
3393 }
3394
3395 TRACE(("found ReGIS option and value \"%c\" \"%s\"\n",
3396 *option,
3397 fragment_to_tempstr(output)));
3398 return 1;
3399 }
3400
3401 static int
3402 regis_num_to_int(RegisDataFragment const *input, int *out)
3403 {
3404 char ch;
3405
3406 assert(input);
3407 assert(out);
3408
3409 /* FIXME: handle exponential notation and rounding */
3410 /* FIXME: check for junk after the number */
3411 ch = peek_fragment(input);
3412 if (!isdigit(CharOf(ch)) &&
3413 ch != '+' &&
3414 ch != '-') {
3415 *out = 0;
3416 return 0;
3417 }
3418
3419 TRACE(("converting \"%s\" to an int\n", fragment_to_tempstr(input)));
3420 *out = atoi(fragment_to_tempstr(input));
3421 return 1;
3422 }
3423
3424 static int
3425 load_regis_colorspec(RegisGraphicsContext const *context,
3426 RegisDataFragment const *input,
3427 short *r_out, short *g_out, short *b_out)
3428 {
3429 RegisDataFragment colorspec;
3430 short r = -1, g = -1, b = -1;
3431 short l = -1;
3432 int simple;
3433
3434 assert(context);
3435 assert(input);
3436 assert(r_out);
3437 assert(g_out);
3438 assert(b_out);
3439
3440 copy_fragment(&colorspec, input);
3441 TRACE(("colorspec option: \"%s\"\n", fragment_to_tempstr(&colorspec)));
3442
3443 skip_regis_whitespace(&colorspec);
3444 simple = 0;
3445 if (fragment_remaining(&colorspec) == 1U) {
3446 simple = 1;
3447 } else if (fragment_remaining(&colorspec) > 1U) {
3448 char after = get_fragment(&colorspec, 1U);
3449 if (IsSpace(after))
3450 simple = 1;
3451 }
3452 if (simple) {
3453 char ch = pop_fragment(&colorspec);
3454
3455 TRACE(("got ReGIS RGB colorspec pattern '%c' with arguments: \"%s\"\n",
3456 ch, fragment_to_tempstr(&colorspec)));
3457 switch (ch) {
3458 case 'D':
3459 case 'd':
3460 r = 0;
3461 g = 0;
3462 b = 0;
3463 l = 0;
3464 break;
3465 case 'R':
3466 case 'r':
3467 r = 100;
3468 g = 0;
3469 b = 0;
3470 l = 46;
3471 break;
3472 case 'G':
3473 case 'g':
3474 r = 0;
3475 g = 100;
3476 b = 0;
3477 l = 50;
3478 break;
3479 case 'B':
3480 case 'b':
3481 r = 0;
3482 g = 0;
3483 b = 100;
3484 l = 50;
3485 break;
3486 case 'C':
3487 case 'c':
3488 r = 0;
3489 g = 100;
3490 b = 100;
3491 l = 50;
3492 break;
3493 case 'Y':
3494 case 'y':
3495 r = 100;
3496 g = 100;
3497 b = 0;
3498 l = 50;
3499 break;
3500 case 'M':
3501 case 'm':
3502 r = 100;
3503 g = 0;
3504 b = 100;
3505 l = 50;
3506 break;
3507 case 'W':
3508 case 'w':
3509 r = 100;
3510 g = 100;
3511 b = 100;
3512 l = 100;
3513 break;
3514 default:
3515 TRACE(("DATA_ERROR: unknown RGB color name: \"%c\"\n", ch));
3516 return 0;
3517 }
3518 } else {
3519 RegisDataFragment num;
3520 int max, val;
3521 char comp;
3522 short h = -1;
3523 short s = -1;
3524
3525 while (!fragment_consumed(&colorspec)) {
3526 if (skip_regis_whitespace(&colorspec))
3527 continue;
3528
3529 comp = pop_fragment(&colorspec);
3530 switch (comp) {
3531 case ',':
3532 /* not sure if this is valid, but it is easy to handle */
3533 continue;
3534 case 'H':
3535 case 'h':
3536 max = 360;
3537 comp = 'H';
3538 break;
3539 case 'L':
3540 case 'l':
3541 max = 100;
3542 comp = 'L';
3543 break;
3544 case 'S':
3545 case 's':
3546 max = 100;
3547 comp = 'S';
3548 break;
3549 #ifdef ENABLE_RGB_COLORSPECS
3550 case 'R': /* RLogin extension */
3551 case 'r':
3552 max = 100;
3553 comp = 'R';
3554 break;
3555 case 'G': /* RLogin extension */
3556 case 'g':
3557 max = 100;
3558 comp = 'G';
3559 break;
3560 case 'B': /* RLogin extension */
3561 case 'b':
3562 max = 100;
3563 comp = 'B';
3564 break;
3565 #endif
3566 default:
3567 TRACE(("DATA_ERROR: unrecognized component in colorspec: '%c'\n",
3568 comp));
3569 return 0;
3570 }
3571
3572 skip_regis_whitespace(&colorspec);
3573 if (!extract_regis_num(&colorspec, &num)) {
3574 TRACE(("DATA_ERROR: expected int after '%c' component in colorspec: \"%s\"\n",
3575 comp, fragment_to_tempstr(&colorspec)));
3576 return 0;
3577 }
3578 if (!regis_num_to_int(&num, &val)) {
3579 TRACE(("DATA_ERROR: component value %s is not a number\n",
3580 fragment_to_tempstr(&num)));
3581 return 0;
3582 }
3583 /* FIXME: error, truncate, wrap, ...? */
3584 if (val < 0 || val > max) {
3585 TRACE(("DATA_ERROR: component value %d out of range\n", val));
3586 return 0;
3587 }
3588
3589 switch (comp) {
3590 case 'H':
3591 h = (short) val;
3592 break;
3593 case 'L':
3594 l = (short) val;
3595 break;
3596 case 'S':
3597 s = (short) val;
3598 break;
3599 case 'R':
3600 r = (short) val;
3601 break;
3602 case 'G':
3603 g = (short) val;
3604 break;
3605 case 'B':
3606 b = (short) val;
3607 break;
3608 }
3609 }
3610
3611 if (h >= 0 && l >= 0 && s >= 0 && r < 0 && g < 0 && b < 0) {
3612 TRACE(("found HLS colorspec to be converted: %hd,%hd,%hd\n",
3613 h, l, s));
3614 hls2rgb(h, l, s, &r, &g, &b);
3615 TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b));
3616 } else if (h < 0 && l < 0 && s < 0 && r >= 0 && g >= 0 && b >= 0) {
3617 TRACE(("found RGB colorspec: %hd,%hd,%hd\n", r, g, b));
3618 l = (short) ((MIN3(r, g, b) + MAX3(r, g, b)) / 2);
3619 TRACE(("calculated L: %d\n", l));
3620 } else if (h < 0 && l >= 0 && s < 0 && r < 0 && g < 0 && b < 0) {
3621 TRACE(("found L colorspec to be converted: %hd,%hd,%hd\n",
3622 h, l, s));
3623 hls2rgb(0, l, 0, &r, &g, &b);
3624 TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b));
3625 } else {
3626 TRACE(("DATA_ERROR: unrecognized colorspec format\n"));
3627 return 0;
3628 }
3629 }
3630
3631 /*
3632 * The VT240 and VT330 models convert to the closest grayscale value.
3633 */
3634 if (context->graphics_termid == 240 || context->graphics_termid == 330) {
3635 hls2rgb(0, l, 0, &r, &g, &b);
3636 TRACE(("converted to grayscale: %hd,%hd,%hd\n", r, g, b));
3637 }
3638
3639 *r_out = r;
3640 *g_out = g;
3641 *b_out = b;
3642
3643 skip_regis_whitespace(&colorspec);
3644 if (!fragment_consumed(&colorspec)) {
3645 char skip;
3646
3647 skip = pop_fragment(&colorspec);
3648 (void) skip; /* variable needed only if tracing */
3649 TRACE(("DATA_ERROR: ignoring unexpected character in ReGIS colorspec \"%c\"\n",
3650 skip));
3651 }
3652
3653 return 1;
3654 }
3655
3656 static int
3657 load_regis_regnum_or_colorspec(RegisGraphicsContext const *context,
3658 RegisDataFragment const *input,
3659 RegisterNum *out)
3660 {
3661 int val;
3662 RegisDataFragment colorspec;
3663 RegisDataFragment num;
3664 RegisDataFragment coloroption;
3665
3666 copy_fragment(&colorspec, input);
3667 TRACE(("looking at colorspec pattern: \"%s\"\n",
3668 fragment_to_tempstr(&colorspec)));
3669
3670 skip_regis_whitespace(&colorspec);
3671
3672 if (extract_regis_num(&colorspec, &num)) {
3673 if (!regis_num_to_int(&num, &val)) {
3674 TRACE(("DATA_ERROR: colorspec value %s is not a valid register\n",
3675 fragment_to_tempstr(&num)));
3676 return 0;
3677 }
3678 if (val < 0) {
3679 /* FIXME: error, truncate, wrap, ...? */
3680 TRACE(("DATA_ERROR: ignoring negative colorspec value: %d\n", val));
3681 return 0;
3682 }
3683 if (val >= (int) context->destination_graphic->valid_registers) {
3684 /* FIXME: error, truncate, wrap, ...? */
3685 TRACE(("DATA_ERROR: colorspec value %d is too big; wrapping\n",
3686 val));
3687 val %= (int) context->destination_graphic->valid_registers;
3688 }
3689
3690 TRACE(("colorspec contains index for register %u\n", val));
3691 *out = (RegisterNum) val;
3692
3693 skip_regis_whitespace(&colorspec);
3694 if (!fragment_consumed(&colorspec)) {
3695 char skip;
3696
3697 skip = pop_fragment(&colorspec);
3698 (void) skip; /* variable needed only if tracing */
3699 TRACE(("DATA_ERROR: unexpected character after register \"%c\"\n",
3700 skip));
3701 return 0;
3702 }
3703
3704 return 1;
3705 }
3706
3707 if (extract_regis_parenthesized_data(&colorspec, &coloroption)) {
3708 short r, g, b;
3709
3710 if (!load_regis_colorspec(context, &coloroption, &r, &g, &b)) {
3711 TRACE(("unable to parse colorspec\n"));
3712 return 0;
3713 }
3714
3715 *out = find_color_register(context->destination_graphic->color_registers,
3716 r, g, b);
3717 TRACE(("colorspec maps to closest register %u\n", *out));
3718
3719 return 1;
3720 }
3721
3722 TRACE(("expected register number or colorspec, but found: \"%s\"\n",
3723 fragment_to_tempstr(&colorspec)));
3724 return 0;
3725 }
3726
3727 static int
3728 to_scaled_int(char const *num, int scale, int *value)
3729 {
3730 unsigned long whole, frac;
3731 char *end;
3732
3733 /* FIXME: handle whitespace? how about trailing junk? */
3734 whole = strtoul(num, &end, 10);
3735 if (end[0] == '.') {
3736 char temp[5] = "0000";
3737
3738 if (end[1] != '\0') {
3739 temp[0] = end[1];
3740 if (end[2] != '\0') {
3741 temp[1] = end[2];
3742 if (end[3] != '\0') {
3743 temp[2] = end[3];
3744 if (end[4] != '\0') {
3745 temp[3] = end[4];
3746 }
3747 }
3748 }
3749 }
3750 frac = strtoul(temp, NULL, 10);
3751 } else if (end[0] == '\0' || end[0] == ',' || IsSpace(end[0])) {
3752 frac = 0;
3753 } else {
3754 TRACE(("unexpected character %c in number %s\n", end[0], num));
3755 return 0;
3756 }
3757
3758 *value = (int) (whole * (unsigned) scale +
3759 (frac * (unsigned) scale) / 10000);
3760
3761 return 1;
3762 }
3763
3764 static int
3765 load_regis_raw_extent(char const *extent, int *relx, int *rely,
3766 int *xloc, int *yloc, int scale)
3767 {
3768 int xsign, ysign;
3769 char const *xpart;
3770 char const *ypart;
3771
3772 xpart = extent;
3773 if ((ypart = strchr(extent, ','))) {
3774 ypart++;
3775 } else {
3776 ypart = "";
3777 }
3778
3779 while (IsSpace(xpart[0]))
3780 xpart++;
3781 while (IsSpace(ypart[0]))
3782 ypart++;
3783
3784 if (xpart[0] == '-') {
3785 xsign = -1;
3786 xpart++;
3787 } else if (xpart[0] == '+') {
3788 xsign = +1;
3789 xpart++;
3790 } else {
3791 xsign = 0;
3792 }
3793 if (ypart[0] == '-') {
3794 ysign = -1;
3795 ypart++;
3796 } else if (ypart[0] == '+') {
3797 ysign = +1;
3798 ypart++;
3799 } else {
3800 ysign = 0;
3801 }
3802
3803 if (xpart[0] == '\0' || xpart[0] == ',') {
3804 *relx = 1;
3805 *xloc = 0;
3806 } else if (xsign == 0) {
3807 int val;
3808
3809 if (!to_scaled_int(xpart, scale, &val))
3810 return 0;
3811 *relx = 0;
3812 *xloc = val;
3813 } else {
3814 int val;
3815
3816 if (!to_scaled_int(xpart, scale, &val))
3817 return 0;
3818 *relx = 1;
3819 *xloc = xsign * val;
3820 }
3821 if (ypart[0] == '\0') {
3822 *rely = 1;
3823 *yloc = 0;
3824 } else if (ysign == 0) {
3825 int val;
3826
3827 if (!to_scaled_int(ypart, scale, &val))
3828 return 0;
3829 *rely = 0;
3830 *yloc = val;
3831 } else {
3832 int val;
3833
3834 if (!to_scaled_int(ypart, scale, &val))
3835 return 0;
3836 *rely = 1;
3837 *yloc = ysign * val;
3838 }
3839
3840 return 1;
3841 }
3842
3843 static int
3844 load_regis_mult_extent(char const *extent, int *w, int *h)
3845 {
3846 int relx, rely;
3847 int px, py;
3848
3849 if (!load_regis_raw_extent(extent, &relx, &rely, &px, &py, 1)) {
3850 TRACE(("invalid coordinates in extent %s\n", extent));
3851 return 0;
3852 }
3853 if (relx | rely) {
3854 TRACE(("invalid relative value in multiplier extent %s\n", extent));
3855 return 0;
3856 }
3857
3858 *w = px;
3859 *h = py;
3860
3861 return 1;
3862 }
3863
3864 static int
3865 load_regis_pixel_extent(char const *extent, int origx, int origy,
3866 int *xloc, int *yloc)
3867 {
3868 int relx, rely;
3869 int px, py;
3870
3871 if (!load_regis_raw_extent(extent, &relx, &rely, &px, &py, 1)) {
3872 TRACE(("invalid coordinates in extent %s\n", extent));
3873 return 0;
3874 }
3875
3876 *xloc = px;
3877 *yloc = py;
3878
3879 if (relx)
3880 *xloc += origx;
3881 if (rely)
3882 *yloc += origy;
3883
3884 return 1;
3885 }
3886
3887 #define COORD_SCALE 1000
3888
3889 static int
3890 load_regis_coord_extent(RegisGraphicsContext const *context, char const *extent,
3891 int origx, int origy, int *xloc, int *yloc)
3892 {
3893 int relx, rely;
3894 int ux, uy;
3895
3896 if (!load_regis_raw_extent(extent, &relx, &rely, &ux, &uy, COORD_SCALE)) {
3897 TRACE(("invalid coordinates in extent %s\n", extent));
3898 return 0;
3899 }
3900
3901 if (relx) {
3902 const int px = SCALE_XCOORD(context, ux, COORD_SCALE);
3903 TRACE(("converted relative user X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n",
3904 ux / (double) COORD_SCALE, px, context->width,
3905 context->x_off, context->x_div));
3906 *xloc = origx + px;
3907 } else {
3908 const int px = TRANSLATE_XCOORD(context, ux, COORD_SCALE);
3909 TRACE(("converted absolute user X coord %.03f to absolute pixel X coord %d\n",
3910 ux / (double) COORD_SCALE, px));
3911 *xloc = px;
3912 }
3913 if (rely) {
3914 const int py = SCALE_YCOORD(context, uy, COORD_SCALE);
3915 TRACE(("converted relative user Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n",
3916 uy / (double) COORD_SCALE, py, context->height,
3917 context->y_off, context->y_div));
3918 *yloc = origy + py;
3919 } else {
3920 const int py = TRANSLATE_YCOORD(context, uy, COORD_SCALE);
3921 TRACE(("converted absolute user Y coord %.03f to absolute pixel Y coord %d\n",
3922 uy / (double) COORD_SCALE, py));
3923 *yloc = py;
3924 }
3925
3926 return 1;
3927 }
3928
3929 static int
3930 load_regis_raw_pixelvector_digit(char const *pixelvector,
3931 unsigned *offset,
3932 int *dx, int *dy, int mul)
3933 {
3934 switch (pixelvector[*offset]) {
3935 case '0':
3936 *dx += mul;
3937 break;
3938 case '1':
3939 *dx += mul;
3940 *dy -= mul;
3941 break;
3942 case '2':
3943 *dy -= mul;
3944 break;
3945 case '3':
3946 *dx -= mul;
3947 *dy -= mul;
3948 break;
3949 case '4':
3950 *dx -= mul;
3951 break;
3952 case '5':
3953 *dx -= mul;
3954 *dy += mul;
3955 break;
3956 case '6':
3957 *dy += mul;
3958 break;
3959 case '7':
3960 *dx += mul;
3961 *dy += mul;
3962 break;
3963 default:
3964 return 0;
3965 }
3966
3967 (*offset)++;
3968 return 1;
3969 }
3970
3971 static int
3972 load_regis_pixel_pixelvector(char const *pixelvector,
3973 int mul,
3974 int origx, int origy,
3975 int *xloc, int *yloc)
3976 {
3977 int found = 0;
3978 int px = 0, py = 0;
3979 unsigned offset = 0U;
3980 while (load_regis_raw_pixelvector_digit(pixelvector, &offset,
3981 &px, &py,
3982 mul))
3983 found = 1;
3984 if (pixelvector[offset] != '\0') {
3985 TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n",
3986 &pixelvector[offset]));
3987 }
3988
3989 *xloc = origx + px;
3990 *yloc = origy + py;
3991
3992 return found;
3993 }
3994
3995 static int
3996 load_regis_coord_pixelvector(RegisGraphicsContext const *context,
3997 char const *pixelvector,
3998 int origx, int origy,
3999 int *xloc, int *yloc)
4000 {
4001 const int mul = (int) (context->temporary_write_controls.pv_multiplier
4002 * COORD_SCALE);
4003 int found = 0;
4004 int ux = 0, uy = 0;
4005 unsigned offset = 0U;
4006
4007 while (load_regis_raw_pixelvector_digit(pixelvector, &offset,
4008 &ux, &uy,
4009 mul))
4010 found = 1;
4011 if (pixelvector[offset] != '\0') {
4012 TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n",
4013 &pixelvector[offset]));
4014 } {
4015 const int px = SCALE_XCOORD(context, ux, COORD_SCALE);
4016 const int py = SCALE_YCOORD(context, uy, COORD_SCALE);
4017
4018 TRACE(("converted relative X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n",
4019 ux / (double) COORD_SCALE, px, context->width,
4020 context->x_off, context->x_div));
4021 *xloc = origx + px;
4022
4023 TRACE(("converted relative Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n",
4024 uy / (double) COORD_SCALE, py, context->height,
4025 context->y_off, context->y_div));
4026 *yloc = origy + py;
4027 }
4028
4029 return found;
4030 }
4031
4032 static int
4033 load_regis_coord_pixelvector_step(RegisGraphicsContext const *context,
4034 char const *pixelvector,
4035 unsigned *offset,
4036 int origx, int origy,
4037 int *xloc, int *yloc)
4038 {
4039 const int mul = (int) (context->temporary_write_controls.pv_multiplier
4040 * COORD_SCALE);
4041 int found = 0;
4042 int ux = 0, uy = 0;
4043 if (load_regis_raw_pixelvector_digit(pixelvector, offset, &ux, &uy, mul))
4044 found = 1;
4045 if (!found && pixelvector[*offset] != '\0') {
4046 TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n",
4047 &pixelvector[*offset]));
4048 } {
4049 const int px = SCALE_XCOORD(context, ux, COORD_SCALE);
4050 const int py = SCALE_YCOORD(context, uy, COORD_SCALE);
4051
4052 TRACE(("converted relative X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n",
4053 ux / (double) COORD_SCALE, px, context->width,
4054 context->x_off, context->x_div));
4055 *xloc = origx + px;
4056
4057 TRACE(("converted relative Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n",
4058 uy / (double) COORD_SCALE, py, context->height,
4059 context->y_off, context->y_div));
4060 *yloc = origy + py;
4061 }
4062
4063 return found;
4064 }
4065
4066 static int
4067 load_regis_write_control(RegisParseState *state,
4068 RegisGraphicsContext const *context,
4069 int cur_x, int cur_y,
4070 int option,
4071 RegisDataFragment *arg,
4072 RegisWriteControls *out)
4073 {
4074 TRACE(("checking write control option \"%c\" with arg \"%s\"\n",
4075 option, fragment_to_tempstr(arg)));
4076 switch (option) {
4077 case 'A':
4078 case 'a':
4079 TRACE(("write control alternate display method \"%s\"\n",
4080 fragment_to_tempstr(arg)));
4081 {
4082 int val;
4083 if (!regis_num_to_int(arg, &val) || val < 0 || val >= 1) {
4084 TRACE(("DATA_ERROR: interpreting out of range value as 0 FIXME\n"));
4085 break;
4086 }
4087 if (val == 1) {
4088 TRACE(("ERROR: blink display method not supported FIXME\n"));
4089 }
4090 }
4091 break;
4092 case 'C':
4093 case 'c':
4094 TRACE(("write control compliment writing mode \"%s\"\n",
4095 fragment_to_tempstr(arg)));
4096 out->write_style = WRITE_STYLE_COMPLEMENT;
4097 break;
4098 case 'E':
4099 case 'e':
4100 TRACE(("write control erase writing mode \"%s\"\n",
4101 fragment_to_tempstr(arg)));
4102 out->write_style = WRITE_STYLE_ERASE;
4103 break;
4104 case 'F':
4105 case 'f':
4106 TRACE(("write control plane write mask \"%s\"\n",
4107 fragment_to_tempstr(arg)));
4108 {
4109 int val;
4110 if (!regis_num_to_int(arg, &val) ||
4111 val < 0 || val >= (int) context->destination_graphic->valid_registers) {
4112 TRACE(("DATA_ERROR: interpreting out of range value as 0 FIXME\n"));
4113 out->plane_mask = 0U;
4114 } else {
4115 out->plane_mask = (unsigned) val;
4116 }
4117 }
4118 break;
4119 case 'I':
4120 case 'i':
4121 TRACE(("write control foreground color \"%s\"\n",
4122 fragment_to_tempstr(arg)));
4123 if (!load_regis_regnum_or_colorspec(context, arg, &out->foreground)) {
4124 TRACE(("DATA_ERROR: write control foreground color specifier not recognized: \"%s\"\n",
4125 fragment_to_tempstr(arg)));
4126 return 0;
4127 }
4128 break;
4129 case 'L':
4130 case 'l':
4131 TRACE(("write control line width \"%s\" (FIXME: currently ignored)\n",
4132 fragment_to_tempstr(arg)));
4133 {
4134 int val;
4135 if (!regis_num_to_int(arg, &val) ||
4136 val < 0 || val >= (int) 9) {
4137 TRACE(("interpreting out of range value as 1 FIXME\n"));
4138 out->line_width = 1U;
4139 } else {
4140 out->line_width = (unsigned) val;
4141 }
4142 }
4143 break;
4144 case 'M':
4145 case 'm':
4146 TRACE(("write control found pixel multiplication factor \"%s\"\n",
4147 fragment_to_tempstr(arg)));
4148 {
4149 int val;
4150 if (!regis_num_to_int(arg, &val) || val <= 0) {
4151 TRACE(("interpreting out of range value %d as 1 FIXME\n", val));
4152 out->pv_multiplier = 1U;
4153 } else {
4154 out->pv_multiplier = (unsigned) val;
4155 }
4156 }
4157 break;
4158 case 'N':
4159 case 'n':
4160 TRACE(("write control negative pattern control \"%s\"\n",
4161 fragment_to_tempstr(arg)));
4162 {
4163 int val;
4164 if (!regis_num_to_int(arg, &val)) {
4165 val = -1;
4166 }
4167 switch (val) {
4168 default:
4169 TRACE(("interpreting out of range value %d as 0 FIXME\n", val));
4170 out->invert_pattern = 0U;
4171 break;
4172 case 0:
4173 out->invert_pattern = 0U;
4174 break;
4175 case 1:
4176 out->invert_pattern = 1U;
4177 break;
4178 }
4179 }
4180 break;
4181 case 'P':
4182 case 'p':
4183 TRACE(("write control found pattern control \"%s\"\n",
4184 fragment_to_tempstr(arg)));
4185 {
4186 RegisDataFragment suboptionset;
4187 RegisDataFragment suboptionarg;
4188 RegisDataFragment item;
4189 char suboption;
4190
4191 while (!fragment_consumed(arg)) {
4192 if (skip_regis_whitespace(arg))
4193 continue;
4194
4195 TRACE(("looking for option in \"%s\"\n",
4196 fragment_to_tempstr(arg)));
4197 if (extract_regis_parenthesized_data(arg, &suboptionset)) {
4198 TRACE(("got write pattern suboptionset: \"%s\"\n",
4199 fragment_to_tempstr(&suboptionset)));
4200 while (!fragment_consumed(&suboptionset)) {
4201 skip_regis_whitespace(&suboptionset);
4202 if (extract_regis_option(&suboptionset, &suboption,
4203 &suboptionarg)) {
4204 skip_regis_whitespace(&suboptionarg);
4205 TRACE(("inspecting write pattern suboption \"%c\" with value \"%s\"\n",
4206 suboption,
4207 fragment_to_tempstr(&suboptionarg)));
4208 switch (suboption) {
4209 case 'M':
4210 case 'm':
4211 TRACE(("found pattern multiplier \"%s\"\n",
4212 fragment_to_tempstr(&suboptionarg)));
4213 {
4214 RegisDataFragment num;
4215 int val;
4216
4217 if (extract_regis_num(&suboptionarg,
4218 &num)) {
4219 if (!regis_num_to_int(&num, &val)
4220 || val < 1) {
4221 TRACE(("interpreting out of range pattern multiplier \"%s\" as 2 FIXME\n",
4222 fragment_to_tempstr(&num)));
4223 out->pattern_multiplier = 2U;
4224 } else {
4225 out->pattern_multiplier =
4226 (unsigned) val;
4227 }
4228 skip_regis_whitespace(&suboptionarg);
4229 }
4230
4231 if (!fragment_consumed(&suboptionarg)) {
4232 TRACE(("DATA_ERROR: unknown content after pattern multiplier \"%s\"\n",
4233 fragment_to_tempstr(&suboptionarg)));
4234 return 0;
4235 }
4236 }
4237 break;
4238 default:
4239 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n",
4240 suboption,
4241 fragment_to_tempstr(&suboptionarg)));
4242 return 0;
4243 }
4244 continue;
4245 }
4246
4247 TRACE(("DATA_ERROR: skipping unknown token in pattern control suboptionset (expecting option): \"%s\"\n",
4248 fragment_to_tempstr(&suboptionset)));
4249 pop_fragment(&suboptionset);
4250 }
4251 continue;
4252 }
4253
4254 TRACE(("looking for int in \"%s\"\n",
4255 fragment_to_tempstr(arg)));
4256 if (extract_regis_num(arg, &item)) {
4257 if (peek_fragment(&item) == '0' ||
4258 peek_fragment(&item) == '1') {
4259 unsigned pattern = 0U;
4260 unsigned bitcount;
4261
4262 TRACE(("converting pattern bits \"%s\"\n",
4263 fragment_to_tempstr(&item)));
4264 for (bitcount = 0;; bitcount++) {
4265 char ch = pop_fragment(&item);
4266 if (ch == '\0')
4267 break;
4268 switch (ch) {
4269 case '0':
4270 if (bitcount < MAX_PATTERN_BITS) {
4271 pattern <<= 1U;
4272 }
4273 break;
4274 case '1':
4275 if (bitcount < MAX_PATTERN_BITS) {
4276 pattern <<= 1U;
4277 pattern |= 1U;
4278 }
4279 break;
4280 default:
4281 TRACE(("DATA_ERROR: unknown ReGIS write pattern bit value \"%c\"\n",
4282 ch));
4283 return 0;
4284 }
4285 }
4286
4287 if (bitcount > 0U) {
4288 unsigned extrabits;
4289
4290 for (extrabits = 0;
4291 bitcount + extrabits < MAX_PATTERN_BITS;
4292 extrabits++) {
4293 if (pattern & (1U << (bitcount - 1U))) {
4294 pattern <<= 1U;
4295 pattern |= 1U;
4296 } else {
4297 pattern <<= 1U;
4298 }
4299 }
4300 }
4301
4302 out->pattern = pattern;
4303 } else {
4304 int val;
4305
4306 TRACE(("converting pattern id \"%s\"\n",
4307 fragment_to_tempstr(&item)));
4308 if (!regis_num_to_int(&item, &val))
4309 val = -1;
4310 switch (val) { /* FIXME: exponential allowed? */
4311 case 0:
4312 out->pattern = 0x00; /* solid bg */
4313 break;
4314 case 1:
4315 out->pattern = 0xff; /* solid fg */
4316 break;
4317 case 2:
4318 out->pattern = 0xf0; /* dash */
4319 break;
4320 case 3:
4321 out->pattern = 0xe4; /* dash dot */
4322 break;
4323 case 4:
4324 out->pattern = 0xaa; /* dot */
4325 break;
4326 case 5:
4327 out->pattern = 0xea; /* dash dot dot */
4328 break;
4329 case 6:
4330 out->pattern = 0x88; /* sparse dot */
4331 break;
4332 case 7:
4333 out->pattern = 0x84; /* asymmetric sparse dot */
4334 break;
4335 case 8:
4336 out->pattern = 0xc8; /* sparse dash dot */
4337 break;
4338 case 9:
4339 out->pattern = 0x86; /* sparse dot dash */
4340 break;
4341 default:
4342 TRACE(("DATA_ERROR: unknown ReGIS standard write pattern \"%d\"\n",
4343 val));
4344 return 0;
4345 }
4346 }
4347
4348 TRACE(("final pattern is %02x\n", out->pattern));
4349 continue;
4350 }
4351 skip_regis_whitespace(arg);
4352
4353 TRACE(("DATA_ERROR: skipping unknown token in pattern suboption: \"%s\"\n",
4354 fragment_to_tempstr(arg)));
4355 pop_fragment(arg);
4356 }
4357 }
4358 break;
4359 case 'R':
4360 case 'r':
4361 TRACE(("write control switch to replacement writing mode \"%s\"\n",
4362 fragment_to_tempstr(arg)));
4363 out->write_style = WRITE_STYLE_REPLACE;
4364 break;
4365 case 'S':
4366 case 's':
4367 TRACE(("write control shading control \"%s\"\n",
4368 fragment_to_tempstr(arg)));
4369 {
4370 RegisDataFragment suboptionset;
4371 RegisDataFragment suboptionarg;
4372 RegisDataFragment item;
4373 char suboption;
4374 char shading_character = '\0';
4375 unsigned reference_dim = WRITE_SHADING_REF_Y;
4376 /* FIXME: are relative offsets additive? */
4377 int ref_x = cur_x, ref_y = cur_y;
4378 int shading_enabled = 0;
4379
4380 while (!fragment_consumed(arg)) {
4381 if (skip_regis_whitespace(arg))
4382 continue;
4383
4384 if (extract_regis_string(arg, state->temp, state->templen)) {
4385 TRACE(("found fill char \"%s\"\n", state->temp));
4386 /* FIXME: allow longer strings, ignore extra chars, or treat as error? */
4387 if (strlen(state->temp) != 1) {
4388 TRACE(("DATA_ERROR: expected exactly one char in fill string FIXME\n"));
4389 return 0;
4390 }
4391 shading_character = state->temp[0];
4392 shading_enabled = 1;
4393 TRACE(("shading character is: '%c' (%d)\n",
4394 shading_character, (int) shading_character));
4395 continue;
4396 }
4397
4398 if (extract_regis_parenthesized_data(arg, &suboptionset)) {
4399 skip_regis_whitespace(&suboptionset);
4400 TRACE(("got shading control suboptionset: \"%s\"\n",
4401 fragment_to_tempstr(&suboptionset)));
4402 while (!fragment_consumed(&suboptionset)) {
4403 if (skip_regis_whitespace(&suboptionset))
4404 continue;
4405 if (extract_regis_option(&suboptionset, &suboption,
4406 &suboptionarg)) {
4407 TRACE(("inspecting write shading suboption \"%c\" with value \"%s\"\n",
4408 suboption,
4409 fragment_to_tempstr(&suboptionarg)));
4410 switch (suboption) {
4411 case 'X':
4412 case 'x':
4413 TRACE(("found horizontal shading suboption \"%s\"\n",
4414 fragment_to_tempstr(&suboptionarg)));
4415 if (!fragment_consumed(&suboptionarg)) {
4416 TRACE(("DATA_ERROR: unexpected value to horizontal shading suboption FIXME\n"));
4417 return 0;
4418 }
4419 reference_dim = WRITE_SHADING_REF_X;
4420 shading_enabled = 1;
4421 break;
4422 default:
4423 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n",
4424 suboption,
4425 fragment_to_tempstr(&suboptionarg)));
4426 return 0;
4427 }
4428 continue;
4429 }
4430
4431 TRACE(("DATA_ERROR: skipping unknown token in shading control suboptionset (expecting option): \"%s\"\n",
4432 fragment_to_tempstr(&suboptionset)));
4433 pop_fragment(&suboptionset);
4434 }
4435 continue;
4436 }
4437
4438 if (extract_regis_extent(arg, &item)) {
4439 TRACE(("found extent in shading option curr=%d,%d ref=%d,%d\n",
4440 cur_x, cur_y, ref_x, ref_y));
4441 if (!load_regis_coord_extent(context,
4442 fragment_to_tempstr(&item),
4443 ref_x, ref_y,
4444 &ref_x, &ref_y)) {
4445 TRACE(("DATA_ERROR: unable to parse extent in write shading option '%c': \"%s\"\n",
4446 option, fragment_to_tempstr(&item)));
4447 return 0;
4448 }
4449 TRACE(("shading reference = %d,%d (%s)\n", ref_x, ref_y,
4450 ((reference_dim == WRITE_SHADING_REF_X)
4451 ? "X"
4452 : "Y")));
4453 continue;
4454 }
4455
4456 if (extract_regis_num(arg, &item)) {
4457 if (!regis_num_to_int(&item, &shading_enabled)) {
4458 TRACE(("DATA_ERROR: unable to parse int in write shading option '%c': \"%s\"\n",
4459 option, fragment_to_tempstr(&item)));
4460 return 0;
4461 }
4462 if (shading_enabled < 0 || shading_enabled > 1) {
4463 TRACE(("interpreting out of range value %d as 0 FIXME\n",
4464 shading_enabled));
4465 shading_enabled = 0;
4466 }
4467 TRACE(("shading enabled = %d\n", shading_enabled));
4468 continue;
4469 }
4470
4471 if (skip_regis_whitespace(arg)) {
4472 continue;
4473 }
4474
4475 TRACE(("DATA_ERROR: skipping unknown token in shade suboption: \"%s\"\n",
4476 fragment_to_tempstr(arg)));
4477 pop_fragment(arg);
4478 }
4479
4480 if (shading_enabled) {
4481 out->shading_enabled = 1U;
4482 out->shading_reference_dim = reference_dim;
4483 out->shading_reference = ((reference_dim == WRITE_SHADING_REF_X)
4484 ? ref_x
4485 : ref_y);
4486 out->shading_character = shading_character;
4487 TRACE(("final shading state: enabled, dim=%d ref=%d, char=%c\n",
4488 out->shading_reference_dim, out->shading_reference,
4489 out->shading_character));
4490 } else {
4491 /* FIXME: confirm there is no effect if shading isn't enabled
4492 * in the same command
4493 */
4494 out->shading_enabled = 0U;
4495 TRACE(("final shading state: shading disabled\n"));
4496 }
4497 }
4498 break;
4499 case 'V':
4500 case 'v':
4501 TRACE(("write control switch to overlay writing mode \"%s\"\n",
4502 fragment_to_tempstr(arg)));
4503 out->write_style = WRITE_STYLE_OVERLAY;
4504 break;
4505 default:
4506 TRACE(("DATA_ERROR: ignoring unknown ReGIS write option \"%c\" arg \"%s\"\n",
4507 option, fragment_to_tempstr(arg)));
4508 return 0;
4509 }
4510
4511 return 1;
4512 }
4513
4514 static int
4515 load_regis_write_control_set(RegisParseState *state,
4516 RegisGraphicsContext const *context,
4517 int cur_x, int cur_y,
4518 RegisDataFragment *controls,
4519 RegisWriteControls *out)
4520 {
4521 RegisDataFragment optionset;
4522 RegisDataFragment arg;
4523 char option;
4524
4525 while (!fragment_consumed(controls)) {
4526 if (skip_regis_whitespace(controls))
4527 continue;
4528
4529 if (extract_regis_parenthesized_data(controls, &optionset)) {
4530 TRACE(("got write control optionset: \"%s\"\n",
4531 fragment_to_tempstr(&optionset)));
4532 while (!fragment_consumed(&optionset)) {
4533 skip_regis_whitespace(&optionset);
4534 if (extract_regis_option(&optionset, &option, &arg)) {
4535 skip_regis_whitespace(&arg);
4536 TRACE(("got write control option and value: \"%c\" \"%s\"\n",
4537 option, fragment_to_tempstr(&arg)));
4538 if (!load_regis_write_control(state, context,
4539 cur_x, cur_y,
4540 option, &arg, out)) {
4541 return 0;
4542 }
4543 continue;
4544 }
4545
4546 TRACE(("DATA_ERROR: skipping unknown token in write control optionset (expecting option): \"%s\"\n",
4547 fragment_to_tempstr(&optionset)));
4548 pop_fragment(&optionset);
4549 }
4550 continue;
4551 }
4552
4553 TRACE(("DATA_ERROR: skipping unknown token in write controls (expecting optionset): \"%s\"\n",
4554 fragment_to_tempstr(controls)));
4555 pop_fragment(controls);
4556 }
4557
4558 return 1;
4559 }
4560
4561 static void
4562 init_regis_write_controls(int graphics_termid, unsigned all_planes,
4563 RegisWriteControls *controls)
4564 {
4565 controls->pv_multiplier = 1U;
4566 controls->pattern = 0xff; /* solid */
4567 controls->pattern_multiplier = 2U;
4568 controls->invert_pattern = 0U;
4569 controls->plane_mask = all_planes;
4570 controls->write_style = WRITE_STYLE_OVERLAY;
4571 switch (graphics_termid) {
4572 case 125: /* FIXME: verify */
4573 case 240: /* FIXME: verify */
4574 case 241: /* FIXME: verify */
4575 case 330:
4576 controls->foreground = 3U;
4577 break;
4578 case 340:
4579 default:
4580 controls->foreground = 7U;
4581 break;
4582 case 382:
4583 controls->foreground = 1U; /* FIXME: verify */
4584 break;
4585 }
4586 controls->shading_enabled = 0U;
4587 controls->shading_character = '\0';
4588 controls->shading_reference = 0; /* no meaning if shading is disabled */
4589 controls->shading_reference_dim = WRITE_SHADING_REF_NONE;
4590 controls->line_width = 1U;
4591 /* FIXME: add the rest */
4592 }
4593
4594 static void
4595 map_regis_graphics_pages(XtermWidget xw, RegisGraphicsContext *context)
4596 {
4597 const int charrow = 0;
4598 const int charcol = 0;
4599 unsigned old_display_id = ~0U;
4600
4601 if (context->destination_graphic)
4602 context->destination_graphic->hidden = True;
4603 if (context->display_graphic) {
4604 context->display_graphic->hidden = True;
4605 old_display_id = context->display_graphic->id;
4606 }
4607
4608 context->destination_graphic =
4609 get_new_or_matching_graphic(xw,
4610 charrow, charcol,
4611 context->width,
4612 context->height,
4613 context->destination_page);
4614 if (context->destination_graphic) {
4615 context->destination_graphic->hidden = True;
4616 context->destination_graphic->valid = True;
4617 }
4618
4619 context->display_graphic =
4620 get_new_or_matching_graphic(xw,
4621 charrow, charcol,
4622 context->width,
4623 context->height,
4624 context->display_page);
4625 if (context->display_graphic) {
4626 context->display_graphic->hidden = False;
4627 if (old_display_id != context->display_graphic->id) {
4628 if (!context->display_graphic->valid) {
4629 draw_solid_rectangle(context->display_graphic, 0, 0,
4630 context->width, context->height,
4631 context->background);
4632 }
4633 context->display_graphic->dirty = True;
4634 context->force_refresh = True;
4635 /* FIXME: This isn't really enough. If there are holes in the new
4636 * graphic they should be cleared and set to the text from the same
4637 * page. But we don't have pages for text in xterm (the alt buffer
4638 * is similar though).
4639 */
4640 }
4641 context->display_graphic->valid = True;
4642 }
4643
4644 TRACE(("using graphics destination=[%d -> %u] display=[%d -> %u]\n",
4645 context->destination_page,
4646 (context->destination_graphic
4647 ? context->destination_graphic->id
4648 : 0U),
4649 context->display_page,
4650 (context->display_graphic
4651 ? context->display_graphic->id
4652 : 0U)));
4653 }
4654
4655 static void
4656 copy_regis_write_controls(RegisWriteControls const *src,
4657 RegisWriteControls *dst)
4658 {
4659 dst->pv_multiplier = src->pv_multiplier;
4660 dst->pattern = src->pattern;
4661 dst->pattern_multiplier = src->pattern_multiplier;
4662 dst->invert_pattern = src->invert_pattern;
4663 dst->foreground = src->foreground;
4664 dst->plane_mask = src->plane_mask;
4665 dst->write_style = src->write_style;
4666 dst->shading_enabled = src->shading_enabled;
4667 dst->shading_character = src->shading_character;
4668 dst->shading_reference = src->shading_reference;
4669 dst->shading_reference_dim = src->shading_reference_dim;
4670 dst->line_width = src->line_width;
4671 }
4672
4673 static void
4674 init_regis_text_controls(RegisTextControls *controls)
4675 {
4676 controls->alphabet_num = 0U; /* built-in */
4677 controls->character_set_l = 0U; /* ASCII */
4678 controls->character_set_r = 0U; /* Latin-1 */
4679 get_standard_character_size(1, &controls->character_display_w,
4680 &controls->character_display_h,
4681 &controls->character_unit_cell_w,
4682 &controls->character_unit_cell_h,
4683 &controls->character_inc_x,
4684 &controls->character_inc_y);
4685 controls->string_rotation = 0;
4686 controls->character_rotation = 0;
4687 controls->slant = 0;
4688 }
4689
4690 static void
4691 copy_regis_text_controls(RegisTextControls const *src, RegisTextControls *dst)
4692 {
4693 dst->alphabet_num = src->alphabet_num;
4694 dst->character_set_l = src->character_set_l;
4695 dst->character_set_r = src->character_set_r;
4696 dst->character_display_w = src->character_display_w;
4697 dst->character_display_h = src->character_display_h;
4698 dst->character_unit_cell_w = src->character_unit_cell_w;
4699 dst->character_unit_cell_h = src->character_unit_cell_h;
4700 dst->character_inc_x = src->character_inc_x;
4701 dst->character_inc_y = src->character_inc_y;
4702 dst->string_rotation = src->string_rotation;
4703 dst->character_rotation = src->character_rotation;
4704 dst->slant = src->slant;
4705 }
4706
4707 static void
4708 init_regis_alphabets(RegisGraphicsContext *context)
4709 {
4710 unsigned alphabet_index;
4711
4712 for (alphabet_index = 0U; alphabet_index < MAX_REGIS_ALPHABETS;
4713 alphabet_index++) {
4714 context->alphabets[alphabet_index].alphabet_num = INVALID_ALPHABET_NUM;
4715 context->alphabets[alphabet_index].pixw = 0U;
4716 context->alphabets[alphabet_index].pixh = 0U;
4717 context->alphabets[alphabet_index].name[0] = '\0';
4718 context->alphabets[alphabet_index].fontname[0] = '\0';
4719 context->alphabets[alphabet_index].use_font = 0;
4720 context->alphabets[alphabet_index].bytes = NULL;
4721 }
4722 }
4723
4724 static void
4725 init_regis_graphics_context(int graphics_termid, int width, int height,
4726 unsigned max_colors, const char *builtin_font,
4727 RegisGraphicsContext *context)
4728 {
4729 context->destination_graphic = NULL;
4730 context->display_graphic = NULL;
4731 context->display_page = 0U;
4732 context->destination_page = 0U;
4733 context->graphics_termid = graphics_termid;
4734
4735 /* reset addressing / clear user coordinates */
4736 context->width = width;
4737 context->height = height;
4738 context->x_off = 0;
4739 context->y_off = 0;
4740 context->x_div = width - 1;
4741 context->y_div = height - 1;
4742
4743 /*
4744 * Generate a mask covering all valid color register address bits
4745 * (but don't bother past 2**16).
4746 */
4747 context->all_planes = max_colors;
4748 context->all_planes--;
4749 context->all_planes |= 1U;
4750 context->all_planes |= context->all_planes >> 1U;
4751 context->all_planes |= context->all_planes >> 2U;
4752 context->all_planes |= context->all_planes >> 4U;
4753 context->all_planes |= context->all_planes >> 8U;
4754
4755 context->builtin_font = builtin_font;
4756
4757 init_regis_write_controls(graphics_termid, context->all_planes,
4758 &context->persistent_write_controls);
4759 copy_regis_write_controls(&context->persistent_write_controls,
4760 &context->temporary_write_controls);
4761
4762 init_regis_text_controls(&context->persistent_text_controls);
4763 context->current_text_controls = &context->persistent_text_controls;
4764 init_regis_alphabets(context);
4765
4766 context->multi_input_mode = 0;
4767 /* FIXME: coordinates */
4768 /* FIXME: scrolling */
4769 context->background = 0U;
4770 /* FIXME: input cursor location */
4771 /* FIXME: input cursor style */
4772 context->graphics_output_cursor_x = 0;
4773 context->graphics_output_cursor_y = 0;
4774 /* FIXME: output cursor style */
4775
4776 context->force_refresh = False;
4777 }
4778
4779 static int
4780 parse_regis_command(RegisParseState *state)
4781 {
4782 char ch;
4783
4784 if (!extract_regis_command(&state->input, &ch))
4785 return 0;
4786
4787 switch (ch) {
4788 case 'C':
4789 case 'c':
4790 /* Curve
4791
4792 * C
4793 * (A) # set the arc length in degrees (+ or nothing for
4794 * # counter-clockwise, - for clockwise, rounded to the
4795 * # closest integer degree)
4796 * (B) # begin closed curve sequence (must have at least two
4797 * # values; this option can not be nested)
4798 * (C) # position is the center, current location is the
4799 * # circumference (stays in effect until next command)
4800 * (E) # end curve sequence (drawing is performed here)
4801 * (S) # begin open curve sequence
4802 * (W) # temporary write options (see write command)
4803 * [<center, circumference position>] # center if (C), otherwise point on circumference
4804 * [<point in curve sequence>]... # if between (B) and (E)
4805 * <pv>... # if between (B) and (E)
4806 */
4807 TRACE(("found ReGIS command \"%c\" (curve)\n", ch));
4808 state->command = 'c';
4809 state->curve_mode = CURVE_POSITION_ARC_EDGE;
4810 state->arclen = 360;
4811 state->num_points = 0U;
4812 break;
4813 case 'F':
4814 case 'f':
4815 /* Fill
4816
4817 * F
4818 * (V) # polygon (see vector command)
4819 * (C) # curve (see curve command)
4820 * (W) # temporary write options (see write command)
4821 */
4822 TRACE(("found ReGIS command \"%c\" (filled polygon)\n", ch));
4823 state->command = 'f';
4824 break;
4825 case 'L':
4826 case 'l':
4827 /* Load
4828
4829 * L
4830 * (A) # set alphabet number or name
4831 * (F)"fontname" # load from font (xterm extension)
4832 * (S)[w,h] # set glyph size (xterm extension)
4833 * "ascii"xx,xx,xx,xx,xx,xx,xx,xx # pixel values
4834 */
4835 TRACE(("found ReGIS command \"%c\" (load charset)\n", ch));
4836 state->command = 'l';
4837 break;
4838 case 'P':
4839 case 'p':
4840 /* Position
4841
4842 * P
4843 * (B) # begin bounded position stack (last point returns to first)
4844 * (E) # end position stack
4845 * (P) # select graphics page for the input and output cursors
4846 * (S) # begin unbounded position stack
4847 * (W) # temporary write options (see write command)
4848 * <pv> # move: 0 == right, 1 == upper right, ..., 7 == lower right
4849 * [<position>] # move to position (X, Y, or both)
4850 *
4851 * Note the stack does not need to be ended before the next command
4852 * Note: maximum depth is 16 levels
4853 */
4854 TRACE(("found ReGIS command \"%c\" (position)\n", ch));
4855 state->command = 'p';
4856 break;
4857 case 'R':
4858 case 'r':
4859 /* Report
4860
4861 * R
4862 * (E) # parse error
4863 * (I<val>) # set input mode (0 == one-shot, 1 == multiple) (always returns CR)
4864 * (L) # current alphabet number and name
4865 * (M(<name>) # macrograph contents
4866 * (M(=) # macrograph storage (free bytes of total bytes)
4867 * (P) # absolute output cursor position
4868 * (P(I)) # interactive locator mode (in one-shot or multiple mode)
4869 * (P(I[xmul,ymul])) # interactive locator mode with arrow key movement multipliers
4870 */
4871 TRACE(("found ReGIS command \"%c\" (report status)\n", ch));
4872 state->command = 'r';
4873 break;
4874 case 'S':
4875 case 's':
4876 /* Screen
4877
4878 * S
4879 * (A[<upper left>][<lower right>]) # adjust screen coordinates
4880 * (C<setting> # 0 (cursor output off), 1 (cursor output on)
4881 * (E) # erase to background color, resets shades, curves, and stacks
4882 * (F) # print the graphic and erase the screen (DECprint extension)
4883 * (H(P<printer offset>)[<print area cornet>][<print area corner>)
4884 * (I<color register>) # set the background to a specific register
4885 * (I(<rgbcode>)) # set the background to the register closest to an "RGB" color
4886 * (I(R<r>G<g>B<b>)) # set the background to the register closest to an RGB triplet (RLogin extension)
4887 * (I(H<h>L<l>S<s>)) # set the background to the register closest to an HLS triplet
4888 * (I(L<l>)) # set the background to the register closest to a grayscale value
4889 * (M<color index to set>(<rgbcode>)...) # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color and grayscale registers)
4890 * (M<color index to set>(A<rgbcode>)...) # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color registers only)
4891 * (M<color index to set>(R<red>G<green>B<blue>)...) # 0..100, 0..100, 0..100 (sets color and grayscale registers) (RLogin extension)
4892 * (M<color index to set>(AR<red>G<green>B<blue>)...) # 0..100, 0..100, 0..100 (sets color registers only) (RLogin extension)
4893 * (M<color index to set>(H<hue>L<lightness>S<saturation>)...) # 0..360, 0..100, 0..100 (sets color and grayscale registers)
4894 * (M<color index to set>(AH<hue>L<lightness>S<saturation>)...) # 0..360, 0..100, 0..100 (sets color registers only)
4895 * (M<color index to set>(L<mono level>)...) # level is 0 ... 100 (sets grayscale registers only)
4896 * (P<graphics page number>) # 0 (default) or 1
4897 * (S(<scale>) # scale screen output by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME
4898 * (S(X<scale>) # scale screen output horizontally by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME
4899 * (S(Y<scale>) # scale screen output vertically by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME
4900 * (T(<time delay ticks>) # delay (60 ticks is one second, up to 32767 ticks)
4901 * (N<setting>) # 0 == normal video, 1 == negative/reverse video (not supported on VT3x0)
4902 * (W(M<factor>) # PV multiplier
4903 * <PV scroll offset> # scroll data so given coordinate is at the upper-left
4904 * [scroll offset] # scroll data so given coordinate is at the upper-left
4905 */
4906 TRACE(("found ReGIS command \"%c\" (screen)\n", ch));
4907 state->command = 's';
4908 break;
4909 case 'T':
4910 case 't':
4911 /* Text
4912
4913 * T
4914 * (A) # specify which alphabet/font to select glyphs from (0==builtin)
4915 * (A0L"<designator>")) # specify a built-in set for GL via two-char designator
4916 * (A0R"<designator>")) # specify a built-in set for GR via two-char or three-char designator
4917 * (A<num>R"<designator>")) # specify a user-loaded (1-3) set for GR via two-char or three-char designator
4918 * (B) # begin temporary text control
4919 * (D<char angle>) # specify a character tilt
4920 * (D<str angle>S<size id>) # specify a string tilt
4921 * (D<str angle>S<size id>D<char angle>) # specify a string and character tilt
4922 * (E) # end temporary text control
4923 * (H<factor>) # select a height multiplier (GIGI:1-16, VT340:1-256)
4924 * (I<angle>) # italic/oblique: no slant (0), lean forward (-1 though -45), lean back (+1 through +45)
4925 * (M[width factor,height factor]) # select size multipliers (width 1-16) (height 1-256)
4926 * (S<size id>) # select one of the 17 standard character sizes
4927 * (S[dimensions]) # set a custom display cell size (char with border)
4928 * (U[dimensions]) # set a custom unit cell size (char size)
4929 * (W<write command>) # temporary write options (see write command)
4930 * [<char offset>] # optional manual offset between characters
4931 * <PV spacing> # move half-increments for subscripts and superscripts
4932 * '<text>' # optional
4933 * "<text>" # optional
4934 */
4935 TRACE(("found ReGIS command \"%c\" (text)\n", ch));
4936 state->command = 't';
4937 state->text_tilt_state = TEXT_TILT_STATE_READY;
4938 break;
4939 case 'V':
4940 case 'v':
4941 /* Vector
4942
4943 * V
4944 * (B) # begin bounded position stack (last point returns to first)
4945 * (E) # end position stack
4946 * (S) # begin unbounded position stack
4947 * (W) # temporary write options (see write command)
4948 * <pv> # draw a line to the pixel vector
4949 * [] # draw a dot at the current location
4950 * [<position>] # draw a line to position
4951 */
4952 TRACE(("found ReGIS command \"%c\" (vector)\n", ch));
4953 state->command = 'v';
4954 break;
4955 case 'W':
4956 case 'w':
4957 /* Write
4958
4959 * W
4960 * (A<setting>) # 0 == disable alternate, 1 == enable alternate/blink FIXME
4961 * (C) # complement writing mode
4962 * (E) # erase writing mode
4963 * (F<plane>) # set the plane mask to control which pixel bits are updated
4964 * (I<color register>) # set the foreground to a specific register
4965 * (I(<rgbcode>)) # set the foreground to the register closest to an "RGB" color
4966 * (I(R<r>G<g>B<b>)) # set the foreground to the register closest to an RGB triplet (RLogin extension)
4967 * (I(H<h>L<l>S<s>)) # set the foreground to the register closest to an HLS triplet
4968 * (I(L<l>)) # set the foreground to the register closest to a grayscale value
4969 * (L<width>) # set the line width (RLogin extension) FIXME
4970 * (M<pixel vector multiplier>) # set the multiplication factor
4971 * (N<setting>) # 0 == negative patterns disabled, 1 == negative patterns enabled
4972 * (P<pattern number>) # 0..9: 0 == none, 1 == solid, 2 == 50% dash, 3 == dash-dot
4973 * (P<pattern bits>) # 2 to 8 bits represented as a 0/1 sequence
4974 * (P<(M<pattern multiplier>)) # set the pattern multiplier
4975 * (R) # replacement writing mode
4976 * (S'<character>') # set shading character
4977 * (S<setting>) # 0 == disable shading, 1 == enable shading
4978 * (S[reference point]) # set a horizontal reference line including this point (X ignored)
4979 * (S(X)[reference point]) # set a vertical reference line including this point
4980 * (V) # overlay writing mode
4981 */
4982 TRACE(("found ReGIS command \"%c\" (write parameters)\n", ch));
4983 state->command = 'w';
4984 break;
4985 case '@':
4986 /* Macrograph
4987
4988 * . # clear all macrographs
4989 * :<letter> ...@; # define macrograph for letter
4990 * <letter> # expand macrograph for letter
4991 */
4992 TRACE(("found ReGIS macrograph command\n"));
4993 state->command = '@';
4994 break;
4995 default:
4996 TRACE(("DATA_ERROR: unknown ReGIS command %04x (%c), setting to '_'\n",
4997 (int) ch, ch));
4998 state->command = '_';
4999 state->option = '_';
5000 return 0;
5001 }
5002
5003 state->option = '_';
5004
5005 return 1;
5006 }
5007
5008 static int
5009 parse_regis_option(RegisParseState *state, RegisGraphicsContext *context)
5010 {
5011 RegisDataFragment optionarg;
5012
5013 if (!extract_regis_option(&state->input, &state->option, &optionarg))
5014 return 0;
5015 skip_regis_whitespace(&optionarg);
5016
5017 TRACE(("found ReGIS option '%c', parsing argument \"%s\"\n",
5018 state->option, fragment_to_tempstr(&optionarg)));
5019
5020 switch (state->command) {
5021 case 'c':
5022 TRACE(("inspecting curve option \"%c\" with value \"%s\"\n",
5023 state->option, fragment_to_tempstr(&optionarg)));
5024 switch (state->option) {
5025 case 'A':
5026 case 'a':
5027 TRACE(("found arc length \"%s\"\n",
5028 fragment_to_tempstr(&optionarg)));
5029 {
5030 RegisDataFragment arclen;
5031
5032 if (!extract_regis_num(&optionarg, &arclen)) {
5033 TRACE(("DATA_ERROR: expected int in curve arclen option: \"%s\"\n",
5034 fragment_to_tempstr(&optionarg)));
5035 break;
5036 }
50