1 /* Part of XPCE --- The SWI-Prolog GUI toolkit
2
3 Author: Jan Wielemaker and Anjo Anjewierden
4 E-mail: jan@swi.psy.uva.nl
5 WWW: http://www.swi.psy.uva.nl/projects/xpce/
6 Copyright (c) 1995-2013, University of Amsterdam
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in
18 the documentation and/or other materials provided with the
19 distribution.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include "include.h"
36 #include "mswin.h"
37 #include <h/text.h>
38 #include <math.h>
39 #ifndef M_PI
40 #define M_PI (3.141593)
41 #endif
42
43 #define MAX_CLIP_DEPTH (50) /* clip nesting depth */
44 #define MAX_CTX_DEPTH (10) /* Max draw context depth */
45
46 typedef struct
47 { PceWindow window; /* Pce's notion of the window */
48 HWND hwnd; /* current Windows window */
49 HBITMAP hbitmap; /* current Image */
50 PAINTSTRUCT ps; /* paint structure */
51 HDC hdc; /* device context */
52 HPEN hpen; /* Current created pen */
53 HRGN hrgn; /* Current created region */
54 HBRUSH hbrush; /* Currently selected brush */
55
56 HPEN ohpen; /* Original pen */
57 HRGN ohrgn; /* Original region */
58 HBITMAP ohbitmap; /* Original bitmap */
59 HBRUSH ohbrush; /* Original brush */
60 int stockpen; /* Pen comes from GetStockObject() */
61 HFONT ohfont; /* Original font */
62 HPALETTE hpal; /* selected palette */
63 HPALETTE ohpal; /* Original palette */
64 HPALETTE ochpal; /* Original cache bitmap palette */
65
66 HBITMAP cache; /* background drawing */
67 HDC cached_hdc; /* hdc of original device */
68 int cache_x; /* X-corner of cache */
69 int cache_y; /* Y-corner of cache */
70 int cache_w; /* Width of cache */
71 int cache_h; /* Height of cache */
72 HBITMAP cache_ohbitmap; /* original bitmap handle */
73
74 int offset_x; /* d_offset(), r_offset() */
75 int offset_y; /* same */
76 int r_offset_x; /* r_offset() */
77 int r_offset_y; /* r_offset() */
78 int fill_offset_x; /* filling offset */
79 int fill_offset_y; /* filling offset */
80 int fixed_colours; /* The colours are fixed */
81
82 int open; /* is context opened? */
83 int invert; /* paint inverted */
84
85 Image fill_pattern; /* PCE fill-pattern image */
86 Colour colour; /* Current colour */
87 Colour default_colour; /* The default colour */
88 Any background; /* Background colour */
89 Any default_background; /* @default background */
90 COLORREF rgb; /* RGB of colour */
91 COLORREF background_rgb; /* RBG of background */
92 int thickness; /* Current pen */
93 Name texture; /* Current dash-pattern */
94 int modified_pen; /* Pen is modified */
95 FontObj font; /* Currently mounted font */
96 WsFont wsf; /* Window System Font reference */
97 Any device; /* XPCE device in use */
98 DisplayObj display; /* The XPCE display */
99 int depth; /* # bits/pixel */
100 int transformed; /* A scaling-mapping is active */
101 int compatible; /* HDC is compatible to screen */
102
103 Elevation elevation; /* current elevation context */
104 HPEN relief_pen; /* standing-edge pen */
105 HPEN shadow_pen; /* falling edge pen */
106
107
108 struct
109 { RECT orect; /* old clipping rect */
110 } clip_stack[MAX_CLIP_DEPTH];
111 int clip_depth; /* #entries on clip stack */
112
113 } wdraw_context, *WDrawContext;
114
115 #ifdef __WINDOWS__
116 #define MoveTo(hdc, x, y) MoveToEx((hdc), (x), (y), NULL);
117 #define SetWindowOrg(hdc, x, y) SetWindowOrgEx((hdc), (x), (y), NULL);
118 #define SetViewportOrg(hdc, x, y) SetViewportOrgEx((hdc), (x), (y), NULL);
119 #endif /*__WINDOWS__*/
120
121 static int cache = 1; /* Do or don't */
122 static int quick; /* Prefer speed */
123 static int has_cmap; /* Do we have a colourmap? */
124 static wdraw_context context; /* current context */
125 static wdraw_context ctx_stack[MAX_CTX_DEPTH]; /* Context stack */
126 static int ctx_stacked; /* Saved frames */
127 static os_platform platform; /* For platform hacks */
128
129 static HDC default_hdc; /* Default context */
130 static HBITMAP default_hdc_hbitmap; /* Memory for default_hdc */
131 static HBITMAP default_hdc_ohbitmap; /* Original memory */
132 static DisplayObj TheDisplay; /* @display */
133 static int display_depth; /* depth of the display */
134
135 static void r_update_pen(void); /* Update the pen context */
136 static void r_default_background(Any bg);
137 static void push_context(void);
138 static void empty_brush_cache(void);
139 static void make_default_context(void);
140 static HBRUSH r_fillbrush(Any fill);
141 static int needWin95FillHack(int x, int y, Any fill);
142
143 #include <gra/graphstate.c>
144
145 static void
reset_context()146 reset_context()
147 { context.fill_pattern = WHITE_IMAGE;
148 context.font = NIL;
149 context.thickness = 1;
150 context.texture = NAME_none;
151 context.hbrush = 0;
152 context.ohbrush = 0;
153 context.ohpen = 0;
154 context.ohfont = 0;
155 context.hpal = NULL;
156 context.ohpal = NULL;
157 context.ochpal = NULL;
158 context.hpen = 0;
159 context.stockpen = FALSE;
160 context.colour = BLACK_COLOUR;
161 context.background = WHITE_COLOUR; /* is this true? */
162 context.default_background = WHITE_COLOUR;
163 context.rgb = RGB(0, 0, 0);
164 context.background_rgb = RGB(255, 255, 255);
165 context.hwnd = 0;
166 context.window = NIL;
167 context.hbitmap = 0;
168 context.modified_pen = FALSE;
169 context.open = 0;
170 context.invert = FALSE;
171 context.hdc = default_hdc;
172 context.display = TheDisplay;
173 context.cache = 0;
174 context.elevation = NIL;
175 context.relief_pen = 0;
176 context.shadow_pen = 0;
177 context.depth = display_depth;
178 context.r_offset_x = 0;
179 context.r_offset_y = 0;
180 context.fixed_colours = 0;
181 context.compatible = TRUE;
182 }
183
184
185 void
initDraw()186 initDraw()
187 { platform = ws_platform();
188 make_default_context();
189 if ( !TheDisplay )
190 TheDisplay = CurrentDisplay(NIL);
191 if ( !display_depth )
192 { display_depth = ws_depth_display(TheDisplay);
193 has_cmap = ws_has_colourmap(TheDisplay);
194 }
195
196 resetDraw();
197
198 at_pce_exit(exitDraw, ATEXIT_FILO);
199 }
200
201
202 void
resetDraw()203 resetDraw()
204 { context.open = 0;
205 ctx_stacked = 0;
206
207 reset_context();
208 }
209
210
211 static void
make_default_context()212 make_default_context()
213 { if ( !default_hdc )
214 { if ( !(default_hdc = CreateCompatibleDC(NULL)) ||
215 !(default_hdc_hbitmap = ZCreateCompatibleBitmap(default_hdc,16,16)) ||
216 !(default_hdc_ohbitmap = ZSelectObject(default_hdc,
217 default_hdc_hbitmap)) )
218 Cprintf("WARNING: Failed to make scratch context");
219 /* TBD: Must be fatal error */
220 }
221
222 resetDraw();
223 }
224
225
226 static void
remove_default_context()227 remove_default_context()
228 { if ( default_hdc )
229 { ZSelectObject(default_hdc, default_hdc_ohbitmap);
230 ZDeleteObject(default_hdc_hbitmap);
231 DeleteDC(default_hdc);
232
233 default_hdc = NULL;
234 }
235 }
236
237
238 void
exitDraw(int rval)239 exitDraw(int rval)
240 { DisplayObj d = TheDisplay;
241
242 remove_default_context();
243 /* Windows frames and windows */
244 if ( d && notNil(d) )
245 { Cell cell;
246
247 for_cell(cell, d->frames)
248 send(cell->value, NAME_uncreate, EAV);
249 }
250
251 SetCursor(LoadCursor(NULL, IDC_WAIT));
252
253 closeAllXrefs();
254 resetDraw();
255 }
256
257
258 void
d_offset(int x,int y)259 d_offset(int x, int y)
260 { DEBUG(NAME_cache, Cprintf("d_offset(%d, %d)\n", x, y));
261
262 context.offset_x = x;
263 context.offset_y = y;
264
265 x = -x;
266 y = -y;
267
268 if ( context.cache )
269 { SetWindowOrg(context.cached_hdc, x, y);
270 } else
271 SetWindowOrg(context.hdc, x, y);
272 }
273
274
275 void
r_offset(int x,int y)276 r_offset(int x, int y)
277 { if ( x == 0 && y == 0 )
278 return; /* very common! */
279
280 if ( !context.cache )
281 { d_offset(context.offset_x + x, context.offset_y + y);
282 } else
283 { POINT old;
284 int rval;
285
286 context.r_offset_x += x;
287 context.r_offset_y += y;
288
289 DEBUG(NAME_offset, Cprintf("r_offset(%d, %d): vp-offset %d, %d\n",
290 x, y,
291 context.r_offset_x - context.cache_x,
292 context.r_offset_y - context.cache_y));
293
294 rval = SetViewportOrgEx(context.hdc,
295 context.r_offset_x - context.cache_x,
296 context.r_offset_y - context.cache_y,
297 &old);
298 assert(rval);
299 DEBUG(NAME_offset, Cprintf("\told = %d, %d\n", old.x, old.y));
300 }
301 }
302
303
304 static void
d_set_filloffset()305 d_set_filloffset()
306 { int tsx = (context.fill_offset_x - context.cache_x);
307 int tsy = (context.fill_offset_y - context.cache_y);
308
309 if ( context.cache )
310 { tsx += context.r_offset_x;
311 tsy += context.r_offset_y;
312 } else
313 { tsx += context.offset_x;
314 tsy += context.offset_y;
315 }
316
317 SetBrushOrgEx(context.hdc, tsx, tsy, NULL);
318 }
319
320
321 void
r_filloffset(Point offset,int x0,int y0,fill_state * state)322 r_filloffset(Point offset, int x0, int y0, fill_state *state)
323 { state->x = context.fill_offset_x;
324 state->y = context.fill_offset_y;
325
326 if ( notNil(offset) )
327 { context.fill_offset_x = valInt(offset->x) + x0;
328 context.fill_offset_y = valInt(offset->y) + y0;
329
330 d_set_filloffset();
331 }
332 }
333
334
335 void
r_fillrestore(fill_state * state)336 r_fillrestore(fill_state *state)
337 { if ( state->x != context.fill_offset_x ||
338 state->y != context.fill_offset_y )
339 { context.fill_offset_x = state->x;
340 context.fill_offset_y = state->y;
341
342 d_set_filloffset();
343 }
344 }
345
346
347 DisplayObj
d_display(DisplayObj d)348 d_display(DisplayObj d)
349 { DisplayObj old = context.display;
350
351 if ( isDefault(d) )
352 d = CurrentDisplay(NIL);
353
354 if ( context.display != d )
355 { openDisplay(d);
356 context.display = d;
357 quick = (d->quick_and_dirty == ON);
358 }
359
360
361 return old;
362 }
363
364
365 void
d_ensure_display()366 d_ensure_display()
367 { if ( context.display == NULL )
368 d_display(CurrentDisplay(NIL));
369 }
370
371
372 void
d_flush(void)373 d_flush(void)
374 {
375 }
376
377
378 status
d_mswindow(PceWindow sw,IArea a,int clear)379 d_mswindow(PceWindow sw, IArea a, int clear)
380 { HPALETTE hpal = window_palette(sw);
381
382 push_context();
383
384 context.window = sw;
385 context.hwnd = getHwndWindow(sw);
386 context.hdc = BeginPaint(context.hwnd, &context.ps);
387 context.device = sw;
388 context.default_colour = sw->colour;
389 context.hpal = hpal;
390 context.open++;
391
392 if ( !IsRectEmpty(&context.ps.rcPaint) )
393 { RECT *r = &context.ps.rcPaint;
394
395 a->x = r->left - valInt(sw->scroll_offset->x);
396 a->y = r->top - valInt(sw->scroll_offset->y);
397 a->w = r->right - r->left;
398 a->h = r->bottom - r->top;
399
400 if ( hpal )
401 { int mapped;
402 context.ohpal = SelectPalette(context.hdc, hpal, FALSE);
403 if ( !context.ohpal )
404 Cprintf("Failed to select palette: %s\n",
405 strName(WinStrError(GetLastError())));
406 mapped = RealizePalette(context.hdc);
407 DEBUG(NAME_colourMap,
408 Cprintf("%s: added %d colours\n", pp(sw), mapped));
409 if ( mapped == GDI_ERROR )
410 Cprintf("RealizePalette(): %s\n", strName(WinStrError(GetLastError())));
411 }
412
413 if ( cache && clear )
414 { context.cached_hdc = context.hdc;
415 context.ochpal = context.ohpal;
416 context.cache_x = a->x;
417 context.cache_y = a->y;
418 context.cache_w = a->w + 1;
419 context.cache_h = a->h + 1;
420 context.cache = ZCreateCompatibleBitmap(context.hdc,
421 context.cache_w,
422 context.cache_h);
423 context.hdc = CreateCompatibleDC(context.hdc);
424 context.cache_ohbitmap = ZSelectObject(context.hdc, context.cache);
425 if ( context.hpal )
426 context.ohpal = SelectPalette(context.hdc, hpal, FALSE);
427
428 r_default_background(sw->background);
429 SetViewportOrg(context.hdc, -context.cache_x, -context.cache_y);
430 SetBrushOrgEx(context.hdc, -context.cache_x, -context.cache_y, NULL);
431
432 r_clear(context.cache_x, context.cache_y,
433 context.cache_w, context.cache_h);
434
435 DEBUG(NAME_cache, Cprintf("%s: created cache for %d %d %d %d\n",
436 pp(sw),
437 context.cache_x, context.cache_y,
438 context.cache_w, context.cache_h));
439 } else
440 { context.cache_x = 0;
441 context.cache_y = 0;
442 r_default_background(sw->background);
443 // SetViewportOrg(context.hdc, 0, 0);
444 // SetBrushOrgEx(context.hdc, 0, 0, NULL);
445 if ( clear )
446 r_clear(a->x, a->y, a->w, a->h);
447 }
448
449 SetBkMode(context.hdc, TRANSPARENT);
450
451 succeed;
452 } else
453 { DEBUG(NAME_redraw, Cprintf("Empty rect: (ltrb) %d-%d %d-%d\n",
454 context.ps.rcPaint.left,
455 context.ps.rcPaint.top,
456 context.ps.rcPaint.right,
457 context.ps.rcPaint.bottom));
458 }
459
460 fail;
461 }
462
463
464 status
d_window(PceWindow sw,int x,int y,int w,int h,int clear,int limit)465 d_window(PceWindow sw, int x, int y, int w, int h, int clear, int limit)
466 { DisplayObj d = getDisplayGraphical((Graphical)sw);
467
468 if ( !d )
469 fail;
470
471 d_display(d);
472
473 if ( !context.open++ )
474 { push_context();
475
476 context.window = sw;
477 context.hwnd = getHwndWindow(sw);
478 context.hdc = BeginPaint(context.hwnd, &context.ps);
479 context.default_colour = sw->colour;
480 context.background = sw->background;
481 context.default_background = sw->background;
482
483 if ( clear )
484 r_clear(x, y, w, h);
485 }
486
487 succeed;
488 }
489
490
491 static void
push_context()492 push_context()
493 { if ( context.open )
494 ctx_stack[ctx_stacked++] = context;
495 if ( ctx_stacked >= MAX_CTX_DEPTH )
496 Cprintf("**************** ERROR: Draw Context Stack overflow\n");
497
498 reset_context();
499 }
500
501
502 void
d_image(Image i,int x,int y,int w,int h)503 d_image(Image i, int x, int y, int w, int h)
504 { Colour background;
505 DisplayObj d = CurrentDisplay(NIL);
506 HPALETTE hpal;
507
508 if ( notNil(d->colour_map) && notDefault(d->colour_map) )
509 hpal = getPaletteColourMap(d->colour_map);
510 else
511 hpal = NULL;
512
513 push_context();
514
515 DEBUG(NAME_redraw, Cprintf("d_image(%s, %d, %d, %d, %d)\n",
516 pp(i), x, y, w, h));
517
518 context.open++;
519 d_display(notNil(i->display) ? i->display : DEFAULT);
520 context.device = i;
521
522 context.hbitmap = (HBITMAP) getXrefObject(i, context.display);
523 context.hdc = CreateCompatibleDC(NULL);
524 context.ohbitmap = ZSelectObject(context.hdc, context.hbitmap);
525 context.depth = valInt(i->depth);
526 context.hpal = hpal;
527
528 if ( hpal )
529 { int mapped;
530 context.ohpal = SelectPalette(context.hdc, hpal, FALSE);
531 if ( !context.ohpal )
532 Cprintf("Failed to select palette: %s\n",
533 strName(WinStrError(GetLastError())));
534 mapped = RealizePalette(context.hdc);
535 DEBUG(NAME_colourMap,
536 Cprintf("%s: added %d colours\n", pp(i), mapped));
537 if ( mapped == GDI_ERROR )
538 Cprintf("RealizePalette(): %s\n", strName(WinStrError(GetLastError())));
539 }
540
541 if ( x != 0 || y != 0 || w != valInt(i->size->w) || h != valInt(i->size->h) )
542 { HRGN clip_region = ZCreateRectRgn(x, y, x+w, y+h);
543
544 ZSelectObject(context.hdc, clip_region);
545 ZDeleteObject(clip_region);
546 }
547
548 if ( notDefault(i->foreground) )
549 context.default_colour = i->foreground;
550 else
551 { if ( i->kind == NAME_bitmap )
552 context.default_colour = BLACK_COLOUR;
553 else
554 context.default_colour = context.display->foreground;
555 }
556
557 if ( notDefault(i->background) )
558 background = i->background;
559 else
560 { if ( i->kind == NAME_bitmap )
561 background = WHITE_COLOUR;
562 else
563 background = context.display->background;
564 }
565
566 SetBkMode(context.hdc, TRANSPARENT);
567 r_default_background(background);
568 r_colour(DEFAULT);
569 }
570
571
572 void
d_hdc(HDC hdc,Colour fg,Colour bg,int compatible)573 d_hdc(HDC hdc, Colour fg, Colour bg, int compatible)
574 { push_context();
575
576 d_display(DEFAULT);
577 if ( isDefault(fg) )
578 fg = context.display->foreground;
579 if ( isDefault(bg) )
580 bg = context.display->background;
581
582 context.open++;
583 context.hdc = hdc;
584 context.device = NIL; /* anonymous device */
585 context.compatible = compatible;
586
587 if ( notNil(bg) )
588 r_default_background(bg);
589 if ( notNil(fg) )
590 r_default_colour(fg);
591 }
592
593
594 void
d_screen(DisplayObj d)595 d_screen(DisplayObj d)
596 { HDC hdc = GetDC(NULL);
597
598 d_hdc(hdc, DEFAULT, DEFAULT, TRUE);
599 }
600
601
602 status
d_winmf(const wchar_t * fn,int x,int y,int w,int h,const wchar_t * descr)603 d_winmf(const wchar_t *fn, int x, int y, int w, int h, const wchar_t *descr)
604 { HDC hdc;
605 RECT bb;
606 HDC refdc = GetDC(NULL);
607 int hmm = GetDeviceCaps(refdc, HORZSIZE);
608 int hpxl = GetDeviceCaps(refdc, HORZRES);
609 int vmm = GetDeviceCaps(refdc, VERTSIZE);
610 int vpxl = GetDeviceCaps(refdc, VERTRES);
611
612 bb.left = (x*hmm*100)/hpxl; /* bb in .01 mm units */
613 bb.right = ((x+w)*hmm*100+hpxl+20)/hpxl; /* bit too big for MS bugs */
614 bb.top = (y*vmm*100)/vpxl;
615 bb.bottom = ((y+h)*vmm*100+vpxl+20)/vpxl;
616
617 DEBUG(NAME_winMetafile,
618 Cprintf("BB in pixels = %d %d %d %d\n"
619 "hmm = %d, hpxl = %d, vmm = %d, vpxl = %d\n"
620 "BB in .01 mm = %d,%d,%d,%d\n",
621 x, y, x+w, y+h,
622 hmm, hpxl, vmm, vpxl,
623 bb.left, bb.top, bb.right, bb.bottom));
624
625 DEBUG(NAME_winMetafile,
626 Cprintf("BB in .01 mm = %d,%d,%d,%d\n",
627 bb.left, bb.top, bb.right, bb.bottom));
628
629 if ( (hdc = CreateEnhMetaFileW(refdc, /* HDC reference */
630 fn, /* name of the metafile */
631 &bb, /* bounding box */
632 // NULL, /* or let GDI compute BB */
633 descr)) )
634 { ReleaseDC(NULL, refdc);
635 d_hdc(hdc, DEFAULT, DEFAULT, FALSE);
636 SetBkMode(hdc, TRANSPARENT);
637
638 succeed;
639 }
640
641 Cprintf("CreateEnhMetaFile() failed\n");
642 fail;
643 }
644
645
646 HENHMETAFILE
d_winmfdone()647 d_winmfdone()
648 { HENHMETAFILE hmf = CloseEnhMetaFile(context.hdc);
649
650 d_done();
651
652 return hmf;
653 }
654
655
656 HDC
d_current_hdc()657 d_current_hdc()
658 { return context.hdc;
659 }
660
661
662 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
663 Clipping is nasty in MS-Windows. The task of the pair r_clip(x,y,w,h)
664 and r_clip_done() is to set the clipping area of the device (given in
665 the current coordinate system determined by d_offset() and r_offset())
666 and revert it back to the old clipping area. Clipping is used both by
667 class device if `device<-clip_area' is present and by class text to
668 handle scrolled text.
669
670 In Windows, clipping is established by creating a region and selecting
671 it into the current device context. The region is in *device*
672 coordinates. Unlike for the other graphical attributes, selecting a
673 clipping region does *not* return a handle to the old situation.
674 Therefore we use GetClipBox() to find the clipping region before we
675 started clipping. But ... the region returned by GetClipBox() is in
676 *logical* coordinates! Hence all the offsets.
677 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
678
679 void
d_clip(int x,int y,int w,int h)680 d_clip(int x, int y, int w, int h)
681 { if ( context.clip_depth < MAX_CLIP_DEPTH )
682 { RECT *rect = &context.clip_stack[context.clip_depth].orect;
683 /* HRGN hrgn; */
684
685 GetClipBox(context.hdc, rect);
686
687 DEBUG(NAME_clip, { Cprintf("d_clip(%d %d %d %d): ", x, y, w, h);
688 Cprintf("ClipBox = %d %d %d %d --> ",
689 rect->left, rect->top,
690 rect->right - rect->left,
691 rect->bottom - rect->top);
692 });
693
694 #if 1
695 { POINT pts[5];
696 pts[0].x = x; pts[0].y = y;
697 pts[1].x = x+w; pts[1].y = y;
698 pts[2].x = x+w; pts[2].y = y+h;
699 pts[3].x = x; pts[3].y = y+h;
700 pts[4].x = x; pts[4].y = y;
701
702 if ( !BeginPath(context.hdc) ||
703 !Polygon(context.hdc, pts, 5) ||
704 !EndPath(context.hdc) ||
705 !SelectClipPath(context.hdc, RGN_AND) )
706 Cprintf("Failed to set clip-path\n");
707 }
708 #else
709 if ( context.cache )
710 { x += context.r_offset_x - context.cache_x; /* TRY ... */
711 y += context.r_offset_y - context.cache_y;
712 } else if ( context.hwnd )
713 { POINT offset;
714
715 GetWindowOrgEx(context.hdc, &offset);
716 x -= offset.x;
717 y -= offset.y;
718 }
719
720 hrgn = ZCreateRectRgn(x, y, x+w, y+h);
721 ZSelectObject(context.hdc, hrgn);
722 ZDeleteObject(hrgn);
723 #endif
724
725 DEBUG(NAME_clip, { RECT nrect;
726 GetClipBox(context.hdc, &nrect);
727 Cprintf("%d %d %d %d\n",
728 nrect.left, nrect.top,
729 nrect.right - nrect.left,
730 nrect.bottom - nrect.top);
731 });
732
733 context.clip_depth++;
734 } else
735 sysPce("Too many levels of clipping");
736 }
737
738
739 void
d_done(void)740 d_done(void)
741 { if ( --context.open == 0 )
742 { DEBUG(NAME_redraw,
743 Cprintf("d_done(%s)\n",
744 context.window ? pp(context.window) : "(image)"));
745
746 if ( context.ohbrush )
747 { ZSelectObject(context.hdc, context.ohbrush);
748 context.hbrush = 0;
749 context.ohbrush = 0;
750 }
751 empty_brush_cache();
752 if ( context.hpen )
753 { ZSelectObject(context.hdc, context.ohpen);
754 if ( !context.stockpen )
755 ZDeleteObject(context.hpen);
756 context.hpen = 0;
757 }
758 if ( context.ohfont )
759 { ZSelectObject(context.hdc, context.ohfont);
760 context.ohfont = 0;
761 }
762 if ( context.relief_pen )
763 { ZDeleteObject(context.relief_pen);
764 context.relief_pen = 0;
765 }
766 if ( context.shadow_pen )
767 { ZDeleteObject(context.shadow_pen);
768 context.shadow_pen = 0;
769 }
770
771 if ( context.cache )
772 { DEBUG(NAME_cache, Cprintf("Writing cache to window\n"));
773 SetViewportOrg(context.hdc, 0, 0);
774 BitBlt(context.cached_hdc,
775 context.cache_x, context.cache_y,
776 context.cache_w, context.cache_h,
777 context.hdc, 0, 0, SRCCOPY);
778 ZSelectObject(context.hdc, context.cache_ohbitmap);
779 ZDeleteObject(context.cache);
780 if ( context.ochpal )
781 { SelectPalette(context.cached_hdc, context.ochpal, FALSE);
782 context.ochpal = NULL;
783 }
784 if ( context.ohpal )
785 { SelectPalette(context.hdc, context.ohpal, FALSE);
786 context.ohpal = NULL;
787 }
788 DeleteDC(context.hdc);
789 } else
790 { if ( context.ohpal )
791 { SelectPalette(context.hdc, context.ohpal, FALSE);
792 context.ohpal = NULL;
793 }
794 }
795
796 if ( instanceOfObject(context.device, ClassWindow) )
797 { EndPaint(context.hwnd, &context.ps);
798 } else if ( instanceOfObject(context.device, ClassImage) )
799 { ZSelectObject(context.hdc, context.ohbitmap);
800 DeleteDC(context.hdc);
801 } else /* d_hdc() context */
802 { ;
803 }
804
805 if ( ctx_stacked )
806 context = ctx_stack[--ctx_stacked];
807 else
808 reset_context();
809 }
810 }
811
812
813 void
d_clip_done(void)814 d_clip_done(void)
815 { RECT *rect;
816 /*HRGN hrgn;*/
817
818 if ( context.clip_depth-- < 0 )
819 sysPce("Clip stack underfow!");
820
821 rect = &context.clip_stack[context.clip_depth].orect;
822 DEBUG(NAME_clip, Cprintf("d_clip_done(%d %d %d %d) --> ",
823 rect->left, rect->top,
824 rect->right - rect->left,
825 rect->bottom - rect->top));
826
827 #if 1
828 { POINT pts[5];
829 pts[0].x = rect->left; pts[0].y = rect->top;
830 pts[1].x = rect->right; pts[1].y = rect->top;
831 pts[2].x = rect->right; pts[2].y = rect->bottom;
832 pts[3].x = rect->left; pts[3].y = rect->bottom;
833 pts[4].x = rect->left; pts[4].y = rect->top;
834
835 if ( !BeginPath(context.hdc) ||
836 !Polygon(context.hdc, pts, 5) ||
837 !EndPath(context.hdc) ||
838 !SelectClipPath(context.hdc, RGN_COPY) )
839 Cprintf("Failed to set UN-clip-path\n");
840 }
841
842 #else
843 if ( context.cache )
844 { ox = context.r_offset_x - context.cache_x;
845 oy = context.r_offset_y - context.cache_y;
846 } else if ( context.hwnd )
847 { POINT offset;
848
849 GetWindowOrgEx(context.hdc, &offset);
850 ox = -offset.x;
851 oy = -offset.y;
852 }
853 rect->left += ox;
854 rect->top += oy;
855 rect->right += ox;
856 rect->bottom += oy;
857
858 hrgn = ZCreateRectRgnIndirect(rect);
859 ZSelectObject(context.hdc, hrgn);
860 ZDeleteObject(hrgn);
861 #endif
862
863 DEBUG(NAME_clip, { RECT nrect;
864 GetClipBox(context.hdc, &nrect);
865 Cprintf("%d %d %d %d\n",
866 nrect.left, nrect.top,
867 nrect.right, nrect.bottom);
868 });
869 }
870
871
872 void
intersection_iarea(IArea a,IArea b)873 intersection_iarea(IArea a, IArea b)
874 { int x, y, w, h;
875
876 x = (a->x > b->x ? a->x : b->x);
877 y = (a->y > b->y ? a->y : b->y);
878 w = (a->x + a->w < b->x + b->w ? a->x + a->w : b->x + b->w) - x;
879 h = (a->y + a->h < b->y + b->h ? a->y + a->h : b->y + b->h) - y;
880
881 if ( w < 0 ) w = 0;
882 if ( h < 0 ) h = 0;
883
884 a->x = x;
885 a->y = y;
886 a->w = w;
887 a->h = h;
888 }
889
890
891 void
r_clear(int x,int y,int w,int h)892 r_clear(int x, int y, int w, int h)
893 { r_fill(x, y, w, h, context.background);
894 }
895
896
897 void
r_complement(int x,int y,int w,int h)898 r_complement(int x, int y, int w, int h)
899 { RECT rect;
900
901 rect.left = x;
902 rect.right = x + w;
903 rect.top = y;
904 rect.bottom = y + h;
905
906 InvertRect(context.hdc, &rect);
907 }
908
909
910 void
r_and(int x,int y,int w,int h,Image pattern)911 r_and(int x, int y, int w, int h, Image pattern)
912 { HBITMAP bm = (HBITMAP) getXrefObject(pattern, context.display);
913 HBRUSH brush = ZCreatePatternBrush(bm);
914 HBRUSH obrush = ZSelectObject(context.hdc, brush);
915
916 PatBlt(context.hdc, x, y, w, h, 0xFA0089); /* P|D */
917
918 ZSelectObject(context.hdc, obrush);
919 ZDeleteObject(brush);
920 }
921
922 static const DWORD dotted[] = { 1, 2 };
923 static const DWORD dashed[] = { 7, 7 };
924 static const DWORD dashdot[] = { 7, 3, 1, 7 };
925 static const DWORD dashdotted[] = { 9, 3, 1, 3, 1, 3, 1, 3 };
926 static const DWORD longdash[] = { 13, 7 };
927
928 static struct dashpattern
929 { Name dash;
930 DWORD style; /* non-PS_USERSTYLE style */
931 const DWORD *dash_list;
932 int dash_list_length;
933 } dash_patterns[] =
934 { { NAME_none, PS_SOLID, NULL, 0},
935 { NAME_dotted, PS_ALTERNATE, dotted, 2},
936 { NAME_dashed, PS_DOT, dashed, 2},
937 { NAME_dashdot, PS_DASHDOT, dashdot, 4},
938 { NAME_dashdotted, PS_DASHDOTDOT, dashdotted, 8},
939 { NAME_longdash, PS_DASH, longdash, 2},
940 { 0, PS_SOLID, NULL, 0},
941 };
942
943
944 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
945 It appears you cannot create a NULL_PEN using CreatePen(). As we need to
946 use stock pens anyway, I decided to use one for the very common solid
947 1-thick black pen too, assuming GetStockObject() will use less
948 resources. In this implementation, we will call DeleteObject() for
949 stock-objects. Should this be done or not?
950 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
951
952 static void
r_update_pen()953 r_update_pen()
954 { if ( context.modified_pen )
955 { HPEN org, old = (!context.stockpen ? context.hpen : 0);
956
957 if ( context.thickness <= 0 )
958 { context.hpen = GetStockObject(NULL_PEN);
959 context.stockpen = TRUE;
960 } else
961 { if ( context.texture == NAME_none &&
962 context.thickness == 1 &&
963 context.rgb == RGB(0,0,0) &&
964 !context.transformed )
965 { context.hpen = GetStockObject(BLACK_PEN);
966 context.stockpen = TRUE;
967 } else
968 { struct dashpattern *pat;
969
970 context.stockpen = FALSE;
971
972 for(pat=dash_patterns; pat->dash; pat++)
973 { if ( pat->dash == context.texture )
974 break;
975 }
976
977 if ( ( context.thickness <= 1 &&
978 !context.transformed &&
979 pat->style != PS_ALTERNATE ) ||
980 ( pat->style == PS_SOLID ) ||
981 ( platform < NT && (context.thickness > 1 ||
982 context.transformed) ) )
983 { DEBUG(NAME_pen, Cprintf("Using CreatePen()\n"));
984 context.hpen = ZCreatePen(pat->style,
985 context.thickness,
986 context.rgb);
987 } else
988 { LOGBRUSH lbrush;
989 int style;
990
991 lbrush.lbStyle = BS_SOLID;
992 lbrush.lbColor = context.rgb;
993 lbrush.lbHatch = 0L;
994
995 if ( platform < NT )
996 { if ( pat->style == PS_ALTERNATE )
997 { context.hpen = ZCreatePen(PS_DOT,
998 context.thickness,
999 context.rgb);
1000 } else
1001 { style = PS_COSMETIC;
1002 style |= pat->style;
1003
1004 context.hpen = ZExtCreatePen(style,
1005 1,
1006 &lbrush,
1007 0,
1008 NULL);
1009 }
1010 } else
1011 { style = PS_GEOMETRIC|PS_ENDCAP_FLAT;
1012
1013 if ( pat->dash_list_length )
1014 style |= PS_USERSTYLE;
1015 else
1016 style |= pat->style;
1017
1018 context.hpen = ZExtCreatePen(style,
1019 context.thickness,
1020 &lbrush,
1021 pat->dash_list_length,
1022 pat->dash_list);
1023 }
1024 }
1025 }
1026 }
1027
1028 org = ZSelectObject(context.hdc, context.hpen);
1029 if ( !context.ohpen )
1030 context.ohpen = org;
1031
1032 if ( old )
1033 ZDeleteObject(old);
1034
1035 context.modified_pen = FALSE;
1036 }
1037 }
1038
1039
1040 void
r_thickness(int pen)1041 r_thickness(int pen)
1042 { if ( context.thickness != pen )
1043 { context.modified_pen = TRUE;
1044 context.thickness = pen;
1045 }
1046 }
1047
1048
1049 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1050 Indicate there is a non-unit mapping to the device. This is used to
1051 avoid speedups based on this assumption, for example the usage of
1052 cosmetic pens over geometric pens.
1053 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1054
1055 int
r_transformed(int val)1056 r_transformed(int val)
1057 { int old = context.transformed;
1058
1059 context.transformed = (val ? TRUE : FALSE);
1060
1061 return old;
1062 }
1063
1064
1065 void
r_dash(Name dash)1066 r_dash(Name dash)
1067 { if ( context.texture != dash )
1068 { context.modified_pen = TRUE;
1069 context.texture = dash;
1070 }
1071 }
1072
1073
1074 Any
r_colour(Any colour)1075 r_colour(Any colour)
1076 { Any old = context.colour;
1077
1078 if ( context.fixed_colours )
1079 return old;
1080
1081 if ( isDefault(colour) )
1082 { assert(notDefault(context.default_colour));
1083 colour = context.default_colour;
1084 } else if ( !instanceOfObject(colour, ClassColour) )
1085 colour = getReplacementColourPixmap(colour);
1086
1087 if ( context.colour != colour )
1088 { context.modified_pen = TRUE;
1089 context.colour = colour;
1090 context.rgb = cref_colour(colour);
1091 SetTextColor(context.hdc, context.rgb);
1092 }
1093
1094 return old;
1095 }
1096
1097 #define BRUSH_CACHE_SIZE 5
1098
1099 typedef struct
1100 { Any object; /* object for brush */
1101 HBRUSH brush; /* associated brush */
1102 int times; /* # times used */
1103 } brush_cache_element;
1104
1105 brush_cache_element brush_cache[BRUSH_CACHE_SIZE];
1106
1107 static HBRUSH
lookup_brush(Any fill)1108 lookup_brush(Any fill)
1109 { int i;
1110 brush_cache_element *e;
1111
1112 for(i=0, e=brush_cache; i<BRUSH_CACHE_SIZE; i++, e++)
1113 { if ( e->object == fill )
1114 { e->times++;
1115 return e->brush;
1116 }
1117 }
1118
1119 return (HBRUSH)0;
1120 }
1121
1122
1123 static void
empty_brush_cache()1124 empty_brush_cache()
1125 { int i;
1126 brush_cache_element *e;
1127
1128 DEBUG(NAME_fill, Cprintf("empty_brush_cache()\n"));
1129
1130 for(i=0, e=brush_cache; i<BRUSH_CACHE_SIZE; i++, e++)
1131 { if ( e->brush )
1132 { ZDeleteObject(e->brush);
1133 e->brush = (HBRUSH)0;
1134 }
1135 e->object = NULL;
1136 e->times = 0;
1137 }
1138 }
1139
1140
1141 static void
add_brush(Any fill,HBRUSH brush)1142 add_brush(Any fill, HBRUSH brush)
1143 { int i;
1144 brush_cache_element *e;
1145 int leastusage;
1146 brush_cache_element *leastused;
1147
1148 DEBUG(NAME_fill, Cprintf("add_brush(%s, 0x%x) ... ", pp(fill), brush));
1149
1150 for(i=0, e=brush_cache; i<BRUSH_CACHE_SIZE; i++, e++)
1151 { if ( !e->object )
1152 { e->object = fill;
1153 e->brush = brush;
1154 e->times = 1;
1155 DEBUG(NAME_fill, Cprintf("ok\n"));
1156 return;
1157 }
1158 }
1159
1160 leastusage = 1000000;
1161 leastused = NULL;
1162
1163 for(i=0, e=brush_cache; i<BRUSH_CACHE_SIZE; i++, e++)
1164 { if ( e->brush != context.hbrush )
1165 { if ( !leastused || e->times < leastusage )
1166 { leastusage = e->times;
1167 leastused = e;
1168 }
1169 }
1170 }
1171
1172 assert(leastused);
1173 DEBUG(NAME_fill, Cprintf("replaced %s --> 0x%x\n",
1174 pp(leastused->object), leastused->brush));
1175
1176 ZDeleteObject(leastused->brush);
1177 leastused->object = fill;
1178 leastused->brush = brush;
1179 leastused->times = 1;
1180 }
1181
1182
1183 static HashTable
winBrushTable()1184 winBrushTable()
1185 { static HashTable table;
1186
1187 if ( !table )
1188 { table = createHashTable(toInt(16), NAME_none);
1189
1190 declareWindowsBrush(NIL, GetStockObject(NULL_BRUSH));
1191 declareWindowsBrush(DEFAULT, GetStockObject(NULL_BRUSH)); /* play safe */
1192 }
1193
1194 return table;
1195 }
1196
1197
1198 void
declareWindowsBrush(Any obj,HBRUSH brush)1199 declareWindowsBrush(Any obj, HBRUSH brush)
1200 { Int b = toInt((intptr_t)brush);
1201
1202 assert((HBRUSH) valInt(b) == brush);
1203 appendHashTable(winBrushTable(), obj, b);
1204 }
1205
1206
1207 HBRUSH
standardWindowsBrush(Any obj)1208 standardWindowsBrush(Any obj)
1209 { Int b;
1210
1211 if ( (b = getMemberHashTable(winBrushTable(), obj)) )
1212 return (HBRUSH) valInt(b);
1213
1214 return 0;
1215 }
1216
1217
1218 static HBRUSH
r_fillbrush(Any fill)1219 r_fillbrush(Any fill)
1220 { HBRUSH hbrush;
1221
1222 if ( isDefault(fill) )
1223 fill = context.colour;
1224 else if ( fill == NAME_current )
1225 fill = context.fill_pattern;
1226
1227 if ( !(hbrush = standardWindowsBrush(fill)) )
1228 { if ( !(hbrush = lookup_brush(fill)) )
1229 { if ( instanceOfObject(fill, ClassImage) )
1230 { Image img = fill;
1231 HBITMAP bm = (HBITMAP) getXrefObject(fill, context.display);
1232
1233 if ( (valInt(img->size->w) < 8 || valInt(img->size->h) < 8) &&
1234 platform < NT )
1235 { int sw = valInt(img->size->w);
1236 int sh = valInt(img->size->h);
1237 int fw, fh;
1238 HBITMAP bm2;
1239 HDC hdc = CreateCompatibleDC(context.hdc);
1240 HDC mhdc = CreateCompatibleDC(context.hdc);
1241 HPALETTE opal1 = 0, opal2 = 0;
1242 HBITMAP obm1, obm2;
1243 int x = 0, y = 0;
1244
1245 if ( platform == WIN95 )
1246 { fw = fh = 8;
1247 } else
1248 { fw = max(sw, 8);
1249 fh = max(sh, 8);
1250 }
1251 bm2 = ZCreateCompatibleBitmap(context.hdc, fw, fh);
1252
1253 DEBUG(NAME_fill, Cprintf("Created %dx%d fill image\n", fw, fh));
1254
1255 if ( context.hpal )
1256 { opal1 = SelectPalette(hdc, context.hpal, FALSE);
1257 opal2 = SelectPalette(mhdc, context.hpal, FALSE);
1258 }
1259 obm1 = ZSelectObject(hdc, bm2);
1260 obm2 = ZSelectObject(mhdc, bm);
1261
1262 for(x=0; x<fw; x+=sw)
1263 { for(y=0; y<fh; y+=sh)
1264 BitBlt(hdc, x, y, sw, sh, mhdc, 0, 0, SRCCOPY);
1265 }
1266 ZSelectObject(hdc, obm1);
1267 ZSelectObject(mhdc, obm2);
1268 if ( context.hpal )
1269 { SelectPalette(hdc, opal1, FALSE);
1270 SelectPalette(mhdc, opal2, FALSE);
1271 }
1272 DeleteDC(hdc);
1273 DeleteDC(mhdc);
1274 hbrush = ZCreatePatternBrush(bm2);
1275 ZDeleteObject(bm2);
1276 } else
1277 hbrush = ZCreatePatternBrush(bm);
1278
1279 if ( !hbrush )
1280 Cprintf("Warning: failed to get brush\n");
1281 } else /* instanceOfObject(fill, ClassColour) */
1282 { COLORREF rgb = cref_colour(fill);
1283
1284 hbrush = ZCreateSolidBrush(rgb);
1285 }
1286
1287 add_brush(fill, hbrush);
1288 }
1289 }
1290
1291 return hbrush;
1292 }
1293
1294
1295 void
r_fillpattern(Any fill,Name which)1296 r_fillpattern(Any fill, Name which) /* colour or image */
1297 { if ( context.fixed_colours && !instanceOfObject(fill, ClassImage) )
1298 fill = (which == NAME_foreground ? context.colour
1299 : context.background);
1300
1301 if ( isDefault(fill) )
1302 fill = context.colour;
1303 else if ( fill == NAME_current )
1304 return;
1305
1306 if ( context.fill_pattern != fill )
1307 { HBRUSH new, old;
1308
1309 new = r_fillbrush(fill);
1310 context.hbrush = new;
1311 old = ZSelectObject(context.hdc, new);
1312 if ( !context.ohbrush )
1313 context.ohbrush = old;
1314
1315 context.fill_pattern = fill;
1316 }
1317 }
1318
1319
1320 void
r_arcmode(Name mode)1321 r_arcmode(Name mode)
1322 { /* handled by r_msarc() itself */
1323 }
1324
1325
1326 void
r_fix_colours(Any fg,Any bg,ColourContext ctx)1327 r_fix_colours(Any fg, Any bg, ColourContext ctx)
1328 { ctx->foreground = context.colour;
1329 ctx->background = context.background;
1330 ctx->lock = context.fixed_colours;
1331
1332 if ( !context.fixed_colours )
1333 { if ( !fg || isNil(fg) ) fg = DEFAULT;
1334 if ( !bg || isNil(bg) ) bg = DEFAULT;
1335
1336 r_default_colour(fg);
1337 r_background(bg);
1338 }
1339
1340 context.fixed_colours++;
1341 }
1342
1343
1344 void
r_unfix_colours(ColourContext ctx)1345 r_unfix_colours(ColourContext ctx)
1346 { if ( (context.fixed_colours = ctx->lock) == 0 )
1347 { r_default_colour(ctx->foreground);
1348 r_background(ctx->background);
1349 }
1350 }
1351
1352
1353 Any
r_default_colour(Any c)1354 r_default_colour(Any c)
1355 { Any old = context.default_colour;
1356
1357 if ( !context.fixed_colours )
1358 { if ( notDefault(c) )
1359 { if ( !instanceOfObject(c, ClassColour) )
1360 c = getReplacementColourPixmap(c);
1361
1362 context.default_colour = c;
1363 }
1364
1365 r_colour(context.default_colour);
1366 }
1367
1368 return old;
1369 }
1370
1371
1372 Any
r_background(Any c)1373 r_background(Any c)
1374 { Any old = context.background;
1375
1376 if ( context.fixed_colours )
1377 return old;
1378
1379 if ( isDefault(c) )
1380 { c = context.default_background;
1381 DEBUG(NAME_background, Cprintf("Using default background %s\n", pp(c)));
1382 }
1383
1384 if ( context.background != c )
1385 { COLORREF rgb;
1386
1387 if ( instanceOfObject(c, ClassColour) )
1388 { rgb = cref_colour(c);
1389 } else
1390 { Colour colour = getReplacementColourPixmap(c);
1391 rgb = cref_colour(colour);
1392 }
1393
1394 SetBkColor(context.hdc, rgb);
1395 context.background = c;
1396 context.background_rgb = rgb;
1397 }
1398
1399 return old;
1400 }
1401
1402
1403 static void
r_default_background(Any bg)1404 r_default_background(Any bg)
1405 { r_background(bg);
1406 context.default_background = context.background;
1407
1408 DEBUG(NAME_background, Cprintf("r_default_background(%s)\n", pp(bg)));
1409 }
1410
1411
1412 void
r_swap_background_and_foreground(void)1413 r_swap_background_and_foreground(void)
1414 { Colour tc = context.background;
1415
1416 r_background(context.colour);
1417 r_colour(tc);
1418 }
1419
1420
1421 BoolObj
r_subwindow_mode(BoolObj val)1422 r_subwindow_mode(BoolObj val)
1423 { return OFF;
1424 }
1425
1426
1427 void
r_invert_mode(BoolObj val)1428 r_invert_mode(BoolObj val)
1429 { context.invert = (val == ON);
1430 }
1431
1432
1433 void
r_translate(int x,int y,int * ox,int * oy)1434 r_translate(int x, int y, int *ox, int *oy)
1435 {
1436 }
1437
1438
1439 void
r_box(int x,int y,int w,int h,int r,Any fill)1440 r_box(int x, int y, int w, int h, int r, Any fill)
1441 { int maxr = min(abs(w), abs(h))/2;
1442
1443 r = min(r, maxr);
1444
1445 if ( notNil(fill) && r == 0 && needWin95FillHack(x, y, fill) )
1446 { r_fill(x, y, w, h, fill);
1447 fill = NIL;
1448 }
1449
1450 if ( context.thickness > 0 || notNil(fill) )
1451 { if ( context.thickness > 0 || r > 1 )
1452 { int da = context.thickness / 2;
1453 int db = max(0, (context.thickness - 1) / 2);
1454
1455 DEBUG(NAME_pen, Cprintf("context.thickness = %d\n", context.thickness));
1456 x += da; y += da;
1457 w -= da+db; h -= da+db;
1458
1459 if ( w < 2 || h < 2 )
1460 return; /* TBD: too small (make line) */
1461
1462 r_fillpattern(fill, NAME_background);
1463 r_update_pen();
1464
1465 if ( r == 0 )
1466 { Rectangle(context.hdc, x, y, x+w, y+h);
1467 } else
1468 { RoundRect(context.hdc, x, y, x+w, y+h, r*2, r*2);
1469 }
1470 } else
1471 { r_fill(x, y, w, h, fill);
1472 }
1473 }
1474 }
1475
1476
1477 void
r_shadow_box(int x,int y,int w,int h,int r,int shadow,Image fill)1478 r_shadow_box(int x, int y, int w, int h, int r, int shadow, Image fill)
1479 { NormaliseArea(x,y,w,h);
1480
1481 if ( !shadow )
1482 { r_box(x, y, w, h, r, fill);
1483 } else
1484 { if ( shadow > h ) shadow = h;
1485 if ( shadow > w ) shadow = w;
1486
1487 r_colour(BLACK_COLOUR);
1488 r_box(x+shadow, y+shadow, w-shadow, h-shadow, r, BLACK_IMAGE);
1489 r_colour(DEFAULT);
1490 r_box(x, y, w-shadow, h-shadow, r, isNil(fill) ? WHITE_IMAGE : fill);
1491 }
1492 }
1493
1494
1495 COLORREF
cref_colour(Colour c)1496 cref_colour(Colour c)
1497 { COLORREF r;
1498
1499 if ( !(r = (COLORREF)(intptr_t) getExistingXrefObject(c, context.display)) )
1500 { r = (COLORREF)(intptr_t) getXrefObject(c, context.display);
1501 if ( context.hpal )
1502 RealizePalette(context.hdc);
1503 }
1504
1505 if ( r & EXACT_COLOUR_MASK )
1506 return r &= ~EXACT_COLOUR_MASK;
1507 else
1508 { if ( context.hpal )
1509 { int n = GetNearestPaletteIndex(context.hpal, r);
1510
1511 return PALETTEINDEX(n);
1512 }
1513
1514 return GetNearestColor(context.hdc, r);
1515 }
1516 }
1517
1518
1519 Any
r_elevation_shadow(Elevation e)1520 r_elevation_shadow(Elevation e)
1521 { if ( isDefault(e->shadow) )
1522 { Any bg = context.background;
1523
1524 if ( instanceOfObject(bg, ClassColour) && context.depth != 1 )
1525 return getReduceColour(bg, DEFAULT);
1526 else
1527 return BLACK_COLOUR;
1528 } else
1529 return e->shadow;
1530 }
1531
1532 static Any
r_elevation_relief(Elevation e)1533 r_elevation_relief(Elevation e)
1534 { if ( isDefault(e->relief) )
1535 { Any bg = context.background;
1536
1537 if ( instanceOfObject(bg, ClassColour) && context.depth != 1 )
1538 return getHiliteColour(bg, DEFAULT);
1539 else
1540 return WHITE_COLOUR;
1541 } else
1542 return e->relief;
1543 }
1544
1545 static void
r_elevation(Elevation e)1546 r_elevation(Elevation e)
1547 { if ( context.elevation != e )
1548 { Any relief = r_elevation_relief(e);
1549 Any shadow = r_elevation_shadow(e);
1550
1551 if ( context.relief_pen )
1552 ZDeleteObject(context.relief_pen);
1553 if ( context.shadow_pen )
1554 ZDeleteObject(context.shadow_pen);
1555
1556 context.relief_pen = ZCreatePen(PS_SOLID, 1, cref_colour(relief));
1557 context.shadow_pen = ZCreatePen(PS_SOLID, 1, cref_colour(shadow));
1558
1559 DEBUG(NAME_elevation, Cprintf("ok\n"));
1560
1561 context.elevation = e;
1562 }
1563 }
1564
1565 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1566 r_elevation_fillpattern(Elevation e, int up)
1567 Sets the fill-pattern for the interior of elevated areas and returns
1568 TRUE if the interior needs to be filled. Returns FALSE otherwise.
1569 The special colours `reduced' and `hilited' are interpreted as relative
1570 colours to the background.
1571 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1572
1573 static int
r_elevation_fillpattern(Elevation e,int up)1574 r_elevation_fillpattern(Elevation e, int up)
1575 { Any fill = NIL;
1576
1577 if ( up && notDefault(e->colour) )
1578 { fill = e->colour;
1579 } else if ( !up && notDefault(e->background) )
1580 { fill = e->background;
1581 }
1582
1583 if ( isNil(fill) )
1584 fail;
1585
1586 if ( fill == NAME_reduced || fill == NAME_hilited )
1587 { Any bg = context.background;
1588
1589 if ( instanceOfObject(bg, ClassColour) && context.depth != 1 )
1590 { if ( fill == NAME_reduced )
1591 fill = getReduceColour(bg, DEFAULT);
1592 else
1593 fill = getHiliteColour(bg, DEFAULT);
1594 } else
1595 fail;
1596 }
1597
1598 r_fillpattern(fill, NAME_background);
1599
1600 succeed;
1601 }
1602
1603
1604 typedef struct
1605 { int x1, y1; /* start of line */
1606 int x2, y2; /* end of line */
1607 } lsegment;
1608
1609
1610 typedef struct
1611 { int x, y;
1612 int width, height;
1613 int angle1, angle2;
1614 } larc;
1615
1616
1617 static void
draw_segments(lsegment * s,int n,HPEN pen)1618 draw_segments(lsegment *s, int n, HPEN pen)
1619 { HPEN old = ZSelectObject(context.hdc, pen);
1620
1621 for( ; n > 0; n--, s++ )
1622 { MoveTo(context.hdc, s->x1, s->y1);
1623 LineTo(context.hdc, s->x2, s->y2);
1624 }
1625
1626 ZSelectObject(context.hdc, old);
1627 }
1628
1629
1630 void
r_3d_segments(int n,ISegment s,Elevation e,int light)1631 r_3d_segments(int n, ISegment s, Elevation e, int light)
1632 { HPEN old;
1633 int i, x = 0, y = 0;
1634
1635 r_elevation(e);
1636
1637 old = ZSelectObject(context.hdc, light ? context.relief_pen
1638 : context.shadow_pen);
1639 for(i=0; i<n; i++, s++)
1640 { if ( i == 0 || x != s->x1 || y != s->y1 )
1641 MoveTo(context.hdc, s->x1, s->y1);
1642 LineTo(context.hdc, s->x2, s->y2);
1643 x = s->x2; y = s->y2;
1644 }
1645
1646 ZSelectObject(context.hdc, old);
1647 }
1648
1649
1650 static short costable[91];
1651
1652 static int
cos64(int angle,int radius)1653 cos64(int angle, int radius)
1654 { int f;
1655
1656 angle /= 64;
1657
1658 if ( angle <= 90 )
1659 f = 1;
1660 else if ( angle <= 180 )
1661 angle = 180 - angle, f = -1;
1662 else if ( angle <= 270 )
1663 angle = angle - 180, f = -1;
1664 else /* if ( angle <= 360 ) */
1665 angle = 360 - angle, f = 1;
1666
1667 if ( !costable[angle] && angle != 90 )
1668 { costable[angle] = rfloat(1024.0 * cos(((float)angle * M_PI)/M_PI));
1669 DEBUG(NAME_arc, Cprintf("Adding cos(%d) = %d (%f)\n",
1670 angle,
1671 costable[angle],
1672 (float) costable[angle] * 1024.0));
1673 }
1674
1675 return (radius * costable[angle] * f) / 1024;
1676 }
1677
1678
1679 static int
sin64(int angle,int radius)1680 sin64(int angle, int radius)
1681 { angle -= 90*64;
1682 if ( angle < 0 )
1683 angle += 360*64;
1684
1685 return cos64(angle, radius);
1686 }
1687
1688
1689 void
r_arc(int x,int y,int w,int h,int s,int e,Any fill)1690 r_arc(int x, int y, int w, int h, int s, int e, Any fill)
1691 { Cprintf("r_arc() not implemented yet\n");
1692 }
1693
1694
1695 static void
draw_arcs(larc * a,int n,HPEN pen)1696 draw_arcs(larc *a, int n, HPEN pen)
1697 { HPEN old = ZSelectObject(context.hdc, pen);
1698
1699 for( ; n > 0; n--, a++ )
1700 { int x1, y1, x2, y2;
1701 int cx = a->x + a->width/2;
1702 int cy = a->y + a->height/2;
1703
1704 x1 = cx + cos64(a->angle1, a->width)/2;
1705 y1 = cy - sin64(a->angle1, a->height)/2;
1706 x2 = cx + cos64(a->angle1 + a->angle2, a->width)/2;
1707 y2 = cy - sin64(a->angle1 + a->angle2, a->height)/2;
1708
1709 DEBUG(NAME_arc, Cprintf("%d %d %d %d (%d --> %d): "
1710 "%d,%d --> %d, %d\n",
1711 a->x, a->y, a->width, a->height,
1712 a->angle1/64, a->angle2/64,
1713 x1, y1, x2, y2));
1714
1715 Arc(context.hdc,
1716 a->x, a->y, a->x + a->width, a->y + a->height,
1717 x1, y1, x2, y2);
1718 }
1719
1720 ZSelectObject(context.hdc, old);
1721 }
1722
1723 #define MAX_SHADOW 10
1724
1725 void
r_3d_box(int x,int y,int w,int h,int radius,Elevation e,int up)1726 r_3d_box(int x, int y, int w, int h, int radius, Elevation e, int up)
1727 { int pen = 1;
1728 int shadow = valInt(e->height);
1729 int fill; /* fill interior */
1730 HPEN top_left_pen, bottom_right_pen;
1731
1732 if ( e->kind == NAME_shadow )
1733 { lsegment s[2 * MAX_SHADOW];
1734 int is = 0; /* # segments */
1735 int xt, yt, os;
1736
1737 r_elevation(e);
1738
1739 shadow = abs(shadow);
1740 shadow = min(shadow, min(w, h));
1741 if ( shadow > MAX_SHADOW )
1742 shadow = MAX_SHADOW;
1743
1744 r_box(x, y, w-shadow, h-shadow, max(0, radius-shadow), e->colour);
1745
1746 xt = x, yt = y;
1747
1748 if ( radius > 0 )
1749 { int r = min(radius, min(w, h));
1750 larc as[MAX_SHADOW * 3];
1751 int ns = 0;
1752
1753 w--, h--;
1754 for( os=0; os < shadow; os++ )
1755 { int ar = r - shadow + os;
1756 int ang /*= 90/(os+1) */;
1757
1758 s[is].x1 = xt+w-os; s[is].y1 = yt+r-shadow; /* vert */
1759 s[is].x2 = xt+w-os; s[is].y2 = yt+h-r;
1760 is++;
1761 s[is].x1 = xt+r-shadow; s[is].y1 = yt+h-os; /* hor */
1762 s[is].x2 = xt+w-r; s[is].y2 = yt+h-os;
1763 is++;
1764 /* bottom-right at xt+w-r, yt+h-r */
1765 as[ns].x = xt+w-r-ar+1; as[ns].y = yt+h-r-ar+1;
1766 as[ns].width = as[ns].height = ar*2;
1767 as[ns].angle1 = 270*64; as[ns].angle2 = 90*64;
1768 ns++;
1769 /* top-right around xt+w-r, yt+r */
1770 ang = 90;
1771 as[ns].x = xt+w-2*ar-os; as[ns].y = yt;
1772 as[ns].width = as[ns].height = ar*2;
1773 as[ns].angle1 = 0*64; as[ns].angle2 = ang*64;
1774 ns++;
1775 /* bottom-left around xt+r, yt+h-r */
1776 as[ns].x = xt; as[ns].y = yt+h-2*ar-os;
1777 as[ns].width = as[ns].height = ar*2;
1778 as[ns].angle1 = (270-ang)*64; as[ns].angle2 = ang*64;
1779 ns++;
1780 }
1781
1782 draw_arcs(as, ns, context.shadow_pen);
1783 } else
1784 { w -= shadow;
1785 h -= shadow;
1786
1787 for( os=0; os < shadow; os++ )
1788 { s[is].x1 = xt+w+os; s[is].y1 = yt+shadow;
1789 s[is].x2 = xt+w+os; s[is].y2 = yt+h+os;
1790 is++;
1791 s[is].x1 = xt+shadow; s[is].y1 = yt+h+os;
1792 s[is].x2 = xt+w+os; s[is].y2 = yt+h+os;
1793 is++;
1794 }
1795 }
1796
1797 draw_segments(s, is, context.shadow_pen);
1798
1799 return;
1800 }
1801
1802 if ( !up )
1803 shadow = -shadow;
1804 fill = r_elevation_fillpattern(e, up);
1805
1806 if ( shadow )
1807 { r_elevation(e);
1808
1809 if ( shadow > 0 )
1810 { bottom_right_pen = context.shadow_pen;
1811 top_left_pen = context.relief_pen;
1812 } else
1813 { bottom_right_pen = context.relief_pen;
1814 top_left_pen = context.shadow_pen;
1815 shadow = -shadow;
1816 }
1817
1818 if ( shadow > MAX_SHADOW )
1819 shadow = MAX_SHADOW;
1820
1821 if ( radius > 0 ) /* coloured elevation, radius > 0 */
1822 { lsegment sr[MAX_SHADOW * 2]; /* top, left */
1823 larc ar[MAX_SHADOW * 3]; /* idem */
1824 lsegment ss[MAX_SHADOW * 2]; /* bottom, right */
1825 larc as[MAX_SHADOW * 3]; /* item */
1826 int is=0, ir=0, ns=0, nr=0; /* # items */
1827 int os;
1828 int xt = x, yt = y;
1829
1830 w--, h--;
1831
1832 for(os=0; os<shadow; os++)
1833 { int r = radius-os;
1834 short wh = r*2;
1835
1836 sr[ir].x1 = os+xt+r; sr[ir].y1 = os+yt; /* top */
1837 sr[ir].x2 = -os+xt+w-r; sr[ir].y2 = os+yt;
1838 ir++;
1839 sr[ir].x1 = os+xt; sr[ir].y1 = os+yt+r; /* left */
1840 sr[ir].x2 = os+xt; sr[ir].y2 = -os+yt+h-r;
1841 ir++;
1842
1843 ss[is].x1 = -os+xt+w; ss[is].y1 = os+yt+r; /* right */
1844 ss[is].x2 = -os+xt+w; ss[is].y2 = -os+yt+h-r;
1845 is++;
1846 ss[is].x1 = os+xt+r; ss[is].y1 = -os+yt+h; /* bottom */
1847 ss[is].x2 = os+xt+w-r; ss[is].y2 = -os+yt+h;
1848 is++;
1849
1850 ar[nr].x = os+xt; ar[nr].y = os+yt; /* top-left */
1851 ar[nr].width = wh; ar[nr].height = wh;
1852 ar[nr].angle1 = 90*64; ar[nr].angle2 = 90*64;
1853 nr++;
1854 ar[nr].x = -os+xt+w-wh; ar[nr].y = os+yt; /* top-right */
1855 ar[nr].width = wh; ar[nr].height = wh;
1856 ar[nr].angle1 = 45*64; ar[nr].angle2 = 45*64;
1857 nr++;
1858 ar[nr].x = os+xt; ar[nr].y = -os+yt+h-wh; /* bottom-left */
1859 ar[nr].width = wh; ar[nr].height = wh;
1860 ar[nr].angle1 = 180*64; ar[nr].angle2 = 45*64;
1861 nr++;
1862
1863 as[ns].x = -os+xt+w-wh; as[ns].y = -os+yt+h-wh; /* bottom-right */
1864 as[ns].width = wh; as[ns].height = wh;
1865 as[ns].angle1 = 270*64; as[ns].angle2 = 90*64;
1866 ns++;
1867 as[ns].x = -os+xt+w-wh; as[ns].y = os+yt; /* top-right */
1868 as[ns].width = wh; as[ns].height = wh;
1869 as[ns].angle1 = 0*64; as[ns].angle2 = 45*64;
1870 ns++;
1871 as[ns].x = os+xt; as[ns].y = -os+yt+h-wh; /* bottom-left */
1872 as[ns].width = wh; as[ns].height = wh;
1873 as[ns].angle1 = 225*64; as[ns].angle2 = 45*64;
1874 ns++;
1875 }
1876
1877 draw_segments(sr, ir, top_left_pen);
1878 draw_segments(ss, is, bottom_right_pen);
1879 draw_arcs( ar, nr, top_left_pen);
1880 draw_arcs( as, ns, bottom_right_pen);
1881 } else /* coloured elevation, radius == 0 */
1882 { lsegment s[2 * MAX_SHADOW];
1883 int i, os;
1884 int xt = x, yt = y;
1885
1886 for(i=0, os=0; os < shadow; os += pen)
1887 { s[i].x1 = xt+os; s[i].y1 = yt+os; /* top-side */
1888 s[i].x2 = xt+w-1-os; s[i].y2 = yt+os;
1889 i++;
1890 s[i].x1 = xt+os; s[i].y1 = yt+os; /* left-side */
1891 s[i].x2 = xt+os; s[i].y2 = yt+h-1-os;
1892 i++;
1893 }
1894 draw_segments(s, i, top_left_pen);
1895
1896 for(i=0, os=0; os < shadow; os += pen)
1897 { s[i].x1 = xt+os; s[i].y1 = yt+h-1-os; /* bottom-side */
1898 s[i].x2 = xt+w/*-1*/-os; s[i].y2 = yt+h-1-os;
1899 i++;
1900 s[i].x1 = xt+w-1-os; s[i].y1 = yt+os; /* right-side */
1901 s[i].x2 = xt+w-1-os; s[i].y2 = yt+h-1-os;
1902 i++;
1903 }
1904 draw_segments(s, i, bottom_right_pen);
1905 }
1906 }
1907
1908 if ( fill )
1909 r_fill(x+shadow, y+shadow, w-2*shadow, h-2*shadow, NAME_current);
1910 }
1911
1912
1913 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1914 Draw a 3-d rectangle a-la Windows 95. The colours are hard to figure
1915 out. The first z elements array colours contain the highlighted side.
1916 The middle one the top and the final z the side in the shadow.
1917 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1918
1919 void
r_3d_rectangle(int x,int y,int w,int h,int z,COLORREF * colours)1920 r_3d_rectangle(int x, int y, int w, int h, int z, COLORREF *colours)
1921 { RECT rect;
1922 HBRUSH hbrush;
1923 HDC hdc = context.hdc;
1924 int i;
1925 /* fill the top */
1926 hbrush = ZCreateSolidBrush(colours[z]);
1927 rect.left = x + z;
1928 rect.right = x + w - z;
1929 rect.top = y + z;
1930 rect.bottom = y + h - z;
1931
1932 FillRect(context.hdc, &rect, hbrush);
1933 ZDeleteObject(hbrush);
1934
1935 for(i=0; i < z; i++)
1936 { HPEN pen = ZCreatePen(PS_SOLID, 1, colours[i]);
1937 HPEN old = ZSelectObject(hdc, pen);
1938
1939 MoveTo(hdc, x+i, y+i); LineTo(hdc, x+w-1-i, y+i); /* top-side */
1940 MoveTo(hdc, x+i, y+i); LineTo(hdc, x+i, y+h-1-i); /* left-side */
1941 ZSelectObject(hdc, old);
1942 ZDeleteObject(pen);
1943 }
1944
1945 for(i=0; i < z; i++)
1946 { HPEN pen = ZCreatePen(PS_SOLID, 1, colours[z*2-i]);
1947 HPEN old = ZSelectObject(hdc, pen);
1948
1949 MoveTo(hdc, x+i, y+h-1-i); LineTo(hdc, x+w-i, y+h-1-i); /* bottom-side */
1950 MoveTo(hdc, x+w-1-i, y+i); LineTo(hdc, x+w-1-i, y+h-1-i); /* right-side */
1951 ZSelectObject(hdc, old);
1952 ZDeleteObject(pen);
1953 }
1954 }
1955
1956
1957 void
r_3d_line(int x1,int y1,int x2,int y2,Elevation e,int up)1958 r_3d_line(int x1, int y1, int x2, int y2, Elevation e, int up)
1959 { lsegment s[MAX_SHADOW];
1960 int i;
1961 int z = valInt(e->height);
1962
1963 r_elevation(e);
1964
1965 if ( z < 0 )
1966 { z = -z;
1967 up = !up;
1968 }
1969
1970 if ( z > MAX_SHADOW )
1971 z = MAX_SHADOW;
1972
1973 if ( y1 == y2 )
1974 { y1 -= z; y2 -= z;
1975 } else
1976 { x1 -= z; x2 -= z;
1977 }
1978
1979 for(i=0; i<z; i++)
1980 { s[i].x1 = x1, s[i].x2 = x2, s[i].y1 = y1, s[i].y2 = y2;
1981 if ( y1 == y2 )
1982 y1++, y2++;
1983 else
1984 x1++, x2++;
1985 }
1986 draw_segments(s, i, up ? context.relief_pen : context.shadow_pen);
1987
1988 for(i=0; i<z; i++)
1989 { s[i].x1 = x1, s[i].x2 = x2, s[i].y1 = y1, s[i].y2 = y2;
1990 if ( y1 == y2 )
1991 y1++, y2++;
1992 else
1993 x1++, x2++;
1994 }
1995 draw_segments(s, i, up ? context.shadow_pen : context.relief_pen);
1996 }
1997
1998
1999 #define X(x) x /* less changes! */
2000 #define Y(y) y
2001
2002 void
r_3d_triangle(int x1,int y1,int x2,int y2,int x3,int y3,Elevation e,int up,int map)2003 r_3d_triangle(int x1, int y1, int x2, int y2, int x3, int y3,
2004 Elevation e, int up, int map)
2005 { lsegment s[3];
2006 HPEN top_pen, bot_pen;
2007 int z = valInt(e->height);
2008
2009 if ( !e || isNil(e) )
2010 { r_triangle(x1, y1, x2, y2, x3, y3, up ? NIL : BLACK_COLOUR);
2011 return;
2012 }
2013
2014 r_elevation(e);
2015
2016 if ( !up )
2017 z = -z;
2018
2019 if ( z > 0 )
2020 { top_pen = context.relief_pen;
2021 bot_pen = context.shadow_pen;
2022 } else
2023 { top_pen = context.shadow_pen;
2024 bot_pen = context.relief_pen;
2025 }
2026
2027 s[0].x1 = X(x1); s[0].y1 = Y(y1); s[0].x2 = X(x2); s[0].y2 = Y(y2);
2028 s[1].x1 = s[0].x2; s[1].y1 = s[0].y2; s[1].x2 = X(x3); s[1].y2 = Y(y3);
2029 s[2].x1 = s[1].x2; s[2].y1 = s[1].y2; s[2].x2 = s[0].x1; s[2].y2 = s[0].y1;
2030
2031 draw_segments(s, 2, top_pen);
2032 draw_segments(&s[2], 1, bot_pen);
2033 }
2034
2035
2036 void
r_3d_diamond(int x,int y,int w,int h,Elevation e,int up)2037 r_3d_diamond(int x, int y, int w, int h, Elevation e, int up)
2038 { HPEN top_pen, bot_pen;
2039 int z = valInt(e->height);
2040 int nox, noy, wex, wey, sox, soy, eax, eay;
2041
2042 r_elevation(e);
2043 r_thickness(1);
2044
2045 if ( !up )
2046 z = -z;
2047
2048 if ( z > 0 )
2049 { top_pen = context.relief_pen;
2050 bot_pen = context.shadow_pen;
2051 } else
2052 { top_pen = context.shadow_pen;
2053 bot_pen = context.relief_pen;
2054 z = -z;
2055 }
2056
2057 z = (z*3)/2; /* actually sqrt(2) */
2058
2059 DEBUG(NAME_3dDiamond,
2060 Cprintf("r_3d_diamond(%d, %d, %d, %d, %s, %d) -->\n\t",
2061 x, y, w, h, pp(e), up));
2062
2063 nox = X(x) + w/2; noy = Y(y);
2064 wex = X(x) + w; wey = Y(y) + h/2;
2065 sox = nox; soy = Y(y) + h;
2066 eax = X(x); eay = wey;
2067
2068 DEBUG(NAME_3dDiamond,
2069 Cprintf("(%d, %d) (%d, %d) (%d, %d) (%d, %d)\n",
2070 nox, noy, wex, wey, sox, soy, eax, eay));
2071
2072 for(; z > 0; z--)
2073 { lsegment s[4];
2074
2075 s[0].x1 = eax; s[0].y1 = eay; s[0].x2 = nox; s[0].y2 = noy;
2076 s[1].x1 = nox; s[1].y1 = noy; s[1].x2 = wex; s[1].y2 = wey;
2077 s[2].x1 = wex; s[2].y1 = wey; s[2].x2 = sox; s[2].y2 = soy;
2078 s[3].x1 = sox; s[3].y1 = soy; s[3].x2 = eax; s[3].y2 = eay;
2079
2080 draw_segments(s, 2, top_pen);
2081 draw_segments(&s[2], 2, bot_pen);
2082
2083 noy++;
2084 soy--;
2085 wex--;
2086 eax++;
2087 }
2088
2089 if ( (up && notDefault(e->colour)) || (!up && notDefault(e->background)))
2090 { POINT p[4];
2091 HPEN oldpen = ZSelectObject(context.hdc, GetStockObject(NULL_PEN));
2092
2093 p[0].x = wex; p[0].y = wey;
2094 p[1].x = nox; p[1].y = noy;
2095 p[2].x = eax; p[2].y = eay;
2096 p[3].x = sox; p[3].y = soy;
2097
2098 r_fillpattern(up ? e->colour : e->background, NAME_background);
2099 Polygon(context.hdc, p, 4);
2100 ZSelectObject(context.hdc, oldpen);
2101 }
2102 }
2103
2104
2105 void
r_3d_ellipse(int x,int y,int w,int h,Elevation z,int up)2106 r_3d_ellipse(int x, int y, int w, int h, Elevation z, int up)
2107 { int shadow;
2108
2109 if ( !z || isNil(z) )
2110 r_ellipse(x, y, w, h, NIL);
2111
2112 shadow = valInt(z->height);
2113 if ( !up )
2114 shadow = -shadow;
2115
2116 if ( shadow > MAX_SHADOW )
2117 shadow = MAX_SHADOW;
2118
2119 if ( shadow )
2120 { HPEN top_pen, bottom_pen;
2121 int xt=x, yt=y;
2122 larc a[MAX_SHADOW*2];
2123 int an, os;
2124
2125 r_elevation(z);
2126
2127 if ( shadow > 0 )
2128 { top_pen = context.relief_pen;
2129 bottom_pen = context.shadow_pen;
2130 } else
2131 { top_pen = context.shadow_pen;
2132 bottom_pen = context.relief_pen;
2133 shadow = -shadow;
2134 }
2135
2136 for(an=0, os=0; os<shadow && w>=1 && h>=1; os++)
2137 { a[an].x = xt+os; a[an].y = yt+os;
2138 a[an].width = w-2*os; a[an].height = h-2*os;
2139 a[an].angle1 = 45*64; a[an].angle2 = 180*64;
2140 an++;
2141 }
2142 draw_arcs(a, an, top_pen);
2143
2144 for(an=0, os=0; os<shadow && w>=1 && h>=1; os++)
2145 { a[an].x = xt+os; a[an].y = yt+os;
2146 a[an].width = w-2*os; a[an].height = h-2*os;
2147 a[an].angle1 = 225*64; a[an].angle2 = 180*64;
2148 an++;
2149 }
2150 draw_arcs(a, an, bottom_pen);
2151 }
2152
2153 if ( notDefault(z->colour) )
2154 { r_thickness(0);
2155 r_ellipse(x+shadow, y+shadow, w-2*shadow, h-2*shadow, z->colour);
2156 }
2157 }
2158
2159
2160 void
r_msarc(int x,int y,int w,int h,int sx,int sy,int ex,int ey,int large,Name close,Any fill)2161 r_msarc(int x, int y, int w, int h, /* bounding box */
2162 int sx, int sy, /* starting point */
2163 int ex, int ey, /* end point */
2164 int large, /* abs(size_angle) > 180.0 */
2165 Name close, /* none,pie_slice,chord */
2166 Any fill) /* @nil or fill pattern */
2167 { r_update_pen();
2168
2169 DEBUG(NAME_arc, Cprintf("r_msarc(%d,%d,%d,%d, %d,%d, %d,%d, %s)\n",
2170 x, y, w, h, sx, sy, ex, ey, pp(close)));
2171
2172 if ( close == NAME_none )
2173 { if ( sx == ex && sy == ey )
2174 { if ( large == TRUE )
2175 r_ellipse(x, y, w, h, fill);
2176 } else
2177 { Arc(context.hdc, x, y, x+w, y+h, sx, sy, ex, ey);
2178 }
2179 } else if ( close == NAME_pieSlice )
2180 { r_fillpattern(fill, NAME_background);
2181 if ( sx == ex && sy == ey )
2182 { if ( large == FALSE )
2183 { if ( context.thickness > 0 )
2184 { r_line(x+w/2, y+h/2, sx, sy);
2185 }
2186 } else
2187 { r_ellipse(x, y, w, h, fill);
2188 }
2189 } else
2190 { Pie(context.hdc, x, y, x+w, y+h, sx, sy, ex, ey);
2191 }
2192 } else /* if ( close == NAME_chord ) */
2193 { r_fillpattern(fill, NAME_background);
2194 Chord(context.hdc, x, y, x+w, y+h, sx, sy, ex, ey);
2195 }
2196 }
2197
2198
2199 void
r_ellipse(int x,int y,int w,int h,Any fill)2200 r_ellipse(int x, int y, int w, int h, Any fill)
2201 { r_fillpattern(fill, NAME_background);
2202 r_update_pen();
2203
2204 DEBUG(NAME_redraw, Cprintf("r_ellipse(%d, %d, %d, %d, %s)\n",
2205 x, y, w, h, pp(fill)));
2206 Ellipse(context.hdc, x, y, x+w, y+h);
2207 }
2208
2209
2210 void
r_line(int x1,int y1,int x2,int y2)2211 r_line(int x1, int y1, int x2, int y2)
2212 { if ( context.invert )
2213 { int l = context.thickness / 2;
2214
2215 if ( x1 == x2 )
2216 { r_complement(x1-l, y1, context.thickness, y2-y1);
2217 return;
2218 } else if ( y1 == y2 )
2219 { r_complement(x1, y1-l, x2-x1, context.thickness);
2220 return;
2221 }
2222 }
2223
2224 r_update_pen();
2225 MoveTo(context.hdc, x1, y1);
2226 LineTo(context.hdc, x2, y2);
2227 }
2228
2229
2230 void
r_polygon(IPoint in,int n,int close)2231 r_polygon(IPoint in, int n, int close)
2232 { POINT *points = (POINT *)alloca((n+1) * sizeof(POINT));
2233 POINT *p = points;
2234 IPoint q = in;
2235 int i;
2236
2237 for(i=0; i<n; i++, p++, q++)
2238 { p->x = q->x;
2239 p->y = q->y;
2240 }
2241
2242 r_update_pen();
2243
2244 if ( close )
2245 Polygon(context.hdc, points, n);
2246 else
2247 Polyline(context.hdc, points, n);
2248 }
2249
2250
2251 void
r_path(Chain points,int ox,int oy,int radius,int closed,Image fill)2252 r_path(Chain points, int ox, int oy, int radius, int closed, Image fill)
2253 { Cell cell;
2254 int npoints = valInt(getSizeChain(points));
2255
2256 if ( npoints < 2 )
2257 return;
2258
2259 if ( radius == 0 )
2260 { POINT *pts = (POINT *)alloca((npoints+1) * sizeof(POINT));
2261 int i=0;
2262
2263 for_cell(cell, points)
2264 { Point p = cell->value;
2265 pts[i].x = valInt(p->x) + ox;
2266 pts[i].y = valInt(p->y) + oy;
2267 i++;
2268 }
2269
2270 r_update_pen();
2271 if ( closed || notNil(fill) )
2272 { r_fillpattern(fill, NAME_background);
2273 Polygon(context.hdc, pts, i);
2274 } else
2275 Polyline(context.hdc, pts, i);
2276 } else
2277 { Cprintf("radius > 0 not yet implemented (r_path())\n");
2278 }
2279 }
2280
2281
2282 void
r_op_image(Image image,int sx,int sy,int x,int y,int w,int h,Name op)2283 r_op_image(Image image, int sx, int sy, int x, int y, int w, int h, Name op)
2284 { HBITMAP bm = (HBITMAP) getXrefObject(image, context.display);
2285 HDC mhdc = CreateCompatibleDC(context.hdc);
2286 HBITMAP obm;
2287 DWORD rop;
2288
2289 if ( op == NAME_copy )
2290 rop = SRCCOPY;
2291 else if ( op == NAME_or )
2292 rop = SRCAND;
2293 else if ( op == NAME_and )
2294 rop = SRCPAINT;
2295 else /*if ( op == NAME_xor )*/
2296 rop = SRCINVERT;
2297
2298 DEBUG(NAME_redraw,
2299 Cprintf("r_op_image(%s, %d, %d, %d, %d, %d, %d, %s) "
2300 "(bm=%p, mhdc=%p)\n",
2301 pp(image), sx, sy, x, y, w, h, pp(op), bm, mhdc));
2302 obm = ZSelectObject(mhdc, bm);
2303 BitBlt(context.hdc, x, y, w, h, mhdc, sx, sy, rop);
2304 if ( op == NAME_xor )
2305 BitBlt(context.hdc, x, y, w, h, mhdc, sx, sy, DSTINVERT);
2306 ZSelectObject(mhdc, obm);
2307 DeleteDC(mhdc);
2308 }
2309
2310 #ifdef O_IMGLIB
2311 #include "imglib.h"
2312 #endif
2313
2314
2315 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2316 void *getDIBImage(Image, BITMAPINFO *dib)
2317 This function creates a DIB from Image. The DIB is needed as an
2318 intermediate if Image needs to be painted on a HDC of different
2319 kind, such as a printer or metafile. If Image has a mask, all
2320 masked pixels are set to white (255,255,255), to (generally)
2321 improve drawing on devices that do not deal with transparency,
2322 such as PostScript printers (at least, their Windows drivers :-)
2323 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2324
2325 static void *
getDIBImage(Image image,BITMAPINFO * dib)2326 getDIBImage(Image image, BITMAPINFO *dib)
2327 { HDC hdc;
2328 HBITMAP bm = (HBITMAP) getXrefObject(image, context.display);
2329 int delete = FALSE;
2330 void *data = NULL;
2331 int w = valInt(image->size->w);
2332 int h = valInt(image->size->h);
2333 HPALETTE ohpal = NULL;
2334
2335 hdc = CreateCompatibleDC(NULL);
2336 if ( context.hpal )
2337 ohpal = SelectPalette(hdc, context.hpal, FALSE);
2338
2339 if ( notNil(image->mask) )
2340 { HBITMAP mask = (HBITMAP) getXrefObject(image->mask, context.display);
2341 HBITMAP copy = CopyImage(bm, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG);
2342 HDC mskhdc = CreateCompatibleDC(hdc);
2343 HBITMAP obm = ZSelectObject(hdc, copy);
2344 HBITMAP mobm;
2345 COLORREF oldbg, oldfg;
2346 HPALETTE ohpal2 = NULL;
2347
2348 if ( context.hpal )
2349 ohpal2 = SelectPalette(mskhdc, context.hpal, FALSE);
2350 mobm = ZSelectObject(mskhdc, mask);
2351
2352 oldbg = SetBkColor(hdc, RGB(255,255,255));
2353 oldfg = SetTextColor(hdc, RGB(0,0,0));
2354 if ( !BitBlt(hdc, 0, 0, w, h, mskhdc, 0, 0, SRCPAINT) )
2355 Cprintf("BitBlt() failed\n");
2356 SetTextColor(hdc, oldfg);
2357 SetBkColor(hdc, oldbg);
2358
2359 ZSelectObject(mskhdc, mobm);
2360 if ( ohpal2 )
2361 SelectPalette(mskhdc, ohpal2, FALSE);
2362 DeleteDC(mskhdc);
2363 ZSelectObject(hdc, obm);
2364
2365 bm = copy;
2366 delete = TRUE;
2367 }
2368
2369 memset(dib, 0, sizeof(*dib));
2370 dib->bmiHeader.biSize = sizeof(dib->bmiHeader);
2371 dib->bmiHeader.biWidth = w;
2372 dib->bmiHeader.biHeight = -h; /* work top-down */
2373 dib->bmiHeader.biPlanes = 1;
2374 dib->bmiHeader.biBitCount = 24;
2375 dib->bmiHeader.biCompression = BI_RGB; /* from WINGDI, this is 0 */
2376
2377 if ( GetDIBits(hdc, bm, 0, h, NULL, dib, DIB_RGB_COLORS) )
2378 { data = pceMalloc(dib->bmiHeader.biSizeImage);
2379
2380 if ( !GetDIBits(hdc, bm, 0, h, data, dib, DIB_RGB_COLORS) )
2381 { pceFree(data);
2382 data = NULL;
2383 }
2384 }
2385
2386 if ( delete )
2387 DeleteObject(bm);
2388 if ( ohpal )
2389 SelectPalette(hdc, ohpal, FALSE);
2390 DeleteDC(hdc);
2391
2392 if ( !data )
2393 DEBUG(NAME_image, Cprintf("Failed to get DIB\n"));
2394
2395 return data;
2396 }
2397
2398
2399 void
r_image(Image image,int sx,int sy,int x,int y,int w,int h,BoolObj transparent)2400 r_image(Image image,
2401 int sx, int sy,
2402 int x, int y, int w, int h,
2403 BoolObj transparent)
2404 { DEBUG(NAME_image, Cprintf("r_image(%s, %d, %d, %d+%d, %dx%d, %s)\n",
2405 pp(image), sx, sy, x, y, w, h, pp(transparent)));
2406
2407 if ( w > 0 && h > 0 &&
2408 image->size->w != ZERO && image->size->h != ZERO )
2409 { if ( (transparent == ON && image->kind == NAME_bitmap) ||
2410 context.compatible )
2411 { HBITMAP obm, bm = (HBITMAP) getXrefObject(image, context.display);
2412 HDC mhdc = CreateCompatibleDC(context.hdc);
2413 HPALETTE ohpal = NULL;
2414
2415 if ( context.hpal )
2416 ohpal = SelectPalette(mhdc, context.hpal, FALSE);
2417 obm = ZSelectObject(mhdc, bm);
2418
2419 if ( transparent == ON && image->kind == NAME_bitmap )
2420 { HBRUSH hbrush = ZCreateSolidBrush(context.rgb);
2421 HBRUSH oldbrush = ZSelectObject(context.hdc, hbrush);
2422 COLORREF oldbk = SetBkColor(context.hdc, RGB(255,255,255));
2423 COLORREF oldtx = SetTextColor(context.hdc, RGB(0,0,0));
2424
2425 BitBlt(context.hdc, x, y, w, h, mhdc, sx, sy, 0xB8074AL);
2426 /* ROP from "Programming Windows3.1" */
2427 /* 3-rd edition, page 633 */
2428 SetTextColor(context.hdc, oldtx);
2429 SetBkColor(context.hdc, oldbk);
2430
2431 ZSelectObject(context.hdc, oldbrush);
2432 ZDeleteObject(hbrush);
2433 } else if ( context.compatible )
2434 { if ( notNil(image->mask) )
2435 { HBITMAP msk = (HBITMAP) getXrefObject(image->mask, context.display);
2436 HDC chdc = CreateCompatibleDC(context.hdc);
2437 HBITMAP obm2 = ZSelectObject(chdc, msk);
2438 COLORREF oldbg, oldfg;
2439
2440 DEBUG(NAME_image, Cprintf("Using BitBlt\n"));
2441
2442 /* Make non-mask part black */
2443 oldbg = SetBkColor(context.hdc, RGB(255,255,255));
2444 oldfg = SetTextColor(context.hdc, RGB(0,0,0));
2445 BitBlt(context.hdc, x, y, w, h, chdc, sx, sy, SRCAND);
2446 SetBkColor(context.hdc, oldbg);
2447 SetTextColor(context.hdc, oldfg);
2448
2449 if ( image->kind == NAME_bitmap )
2450 { HDC hdc = CreateCompatibleDC(context.hdc);
2451 HBITMAP otbm, tbm = ZCreateCompatibleBitmap(context.hdc, w, h);
2452 otbm = ZSelectObject(hdc, tbm);
2453
2454 oldbg = SetBkColor(hdc, context.background_rgb);
2455 oldfg = SetTextColor(hdc, context.rgb);
2456 BitBlt(hdc, 0, 0, w, h, mhdc, sx, sy, SRCCOPY);
2457 SetBkColor(hdc, RGB(0,0,0));
2458 SetTextColor(hdc, RGB(255,255,255));
2459 BitBlt(hdc, 0, 0, w, h, chdc, sx, sy, SRCAND);
2460 SetBkColor(hdc, oldbg);
2461 SetTextColor(hdc, oldfg);
2462 BitBlt(context.hdc, x, y, w, h, hdc, 0, 0, SRCPAINT);
2463 ZSelectObject(hdc, otbm);
2464 ZDeleteObject(tbm);
2465 DeleteDC(hdc);
2466 } else
2467 { BitBlt(context.hdc, x, y, w, h, mhdc, sx, sy, SRCPAINT);
2468 }
2469 ZSelectObject(chdc, obm2);
2470 DeleteDC(chdc);
2471 } else
2472 { BitBlt(context.hdc, x, y, w, h, mhdc, sx, sy, SRCCOPY);
2473 }
2474 }
2475
2476 ZSelectObject(mhdc, obm);
2477 if ( ohpal )
2478 SelectPalette(mhdc, ohpal, FALSE);
2479 DeleteDC(mhdc);
2480 } else /* incompatible HDC, make DIB */
2481 { void *data;
2482 BITMAPINFO dib;
2483
2484 DEBUG(NAME_mask, Cprintf("Using DIB\n"));
2485 if ( (data = getDIBImage(image, &dib)) )
2486 { StretchDIBits(context.hdc, x, y, w, h,
2487 sx, sy, w, h,
2488 data, &dib, DIB_RGB_COLORS, SRCCOPY);
2489
2490 pceFree(data);
2491 }
2492 }
2493 }
2494 }
2495
2496
2497 void
r_winmf(HENHMETAFILE hmf,int x,int y,int w,int h)2498 r_winmf(HENHMETAFILE hmf, int x, int y, int w, int h)
2499 { RECT r;
2500
2501 r.left = x;
2502 r.top = y;
2503 r.bottom = y + h - 1;
2504 r.right = x + w - 1;
2505
2506 if ( !PlayEnhMetaFile(context.hdc, hmf, &r) )
2507 Cprintf("PlayEnhMetaFile() failed\n"); /* TBD */
2508 }
2509
2510
2511 void
r_copy(int xf,int yf,int xt,int yt,int w,int h)2512 r_copy(int xf, int yf, int xt, int yt, int w, int h)
2513 { RECT toclear, source, dest;
2514
2515 source.left = xf;
2516 source.right = xf + w;
2517 source.top = yf;
2518 source.bottom = yf + h;
2519
2520 dest.left = xt;
2521 dest.right = xt + w;
2522 dest.top = yt;
2523 dest.bottom = yt + h;
2524
2525 SubtractRect(&toclear, &source, &dest);
2526
2527 BitBlt(context.hdc, xt, yt, w, h, context.hdc, xf, yf, SRCCOPY);
2528 r_clear(toclear.left,
2529 toclear.top,
2530 toclear.right - toclear.left,
2531 toclear.bottom - toclear.top);
2532 }
2533
2534
2535 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2536 Windows 95/98 and probably also ME doesn't support filling using images
2537 with width or height > 8 pixels. As we need that for gradients, we
2538 patched r_fill() do the filling by hand. This is used for boxes without
2539 radius that have the point(0,0) as fill_offset.
2540 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2541
2542 static int
needWin95FillHack(int x,int y,Any fill)2543 needWin95FillHack(int x, int y, Any fill)
2544 { if ( platform < NT && instanceOfObject(fill, ClassImage) &&
2545 x == context.fill_offset_x &&
2546 y == context.fill_offset_y )
2547 { Image img = fill;
2548 int w = valInt(img->size->w);
2549 int h = valInt(img->size->h);
2550
2551 if ( w > 8 || (8%w) != 0 ||
2552 h > 8 || (8%h) != 0 )
2553 return TRUE;
2554 }
2555
2556 return FALSE;
2557 }
2558
2559
2560 void
r_fill(int x,int y,int w,int h,Any fill)2561 r_fill(int x, int y, int w, int h, Any fill)
2562 { if ( needWin95FillHack(x, y, fill) )
2563 { Image img = fill;
2564 HBITMAP obm, bm = (HBITMAP) getXrefObject(img, context.display);
2565 HDC mhdc = CreateCompatibleDC(context.hdc);
2566 HPALETTE ohpal = NULL;
2567 int sw = valInt(img->size->w);
2568 int sh = valInt(img->size->h);
2569 int cx, cy;
2570
2571 DEBUG(NAME_fill, Cprintf("Filling rectangle by hand\n"));
2572
2573 if ( context.hpal )
2574 ohpal = SelectPalette(mhdc, context.hpal, FALSE);
2575 obm = ZSelectObject(mhdc, bm);
2576
2577 for(cx=x; cx<x+w; cx += sw)
2578 { if ( cx+sw > x+w )
2579 sw = x+w-cx;
2580
2581 for(cy=y; cy<y+h; cy += sh)
2582 { int bh;
2583
2584 if ( cy+sh > y+h )
2585 bh = y+h-cy;
2586 else
2587 bh = sh;
2588
2589 BitBlt(context.hdc, cx, cy, sw, bh, mhdc, 0, 0, SRCCOPY);
2590 }
2591 }
2592
2593 ZSelectObject(mhdc, obm);
2594 if ( ohpal )
2595 SelectPalette(mhdc, ohpal, FALSE);
2596 DeleteDC(mhdc);
2597 } else
2598 { HBRUSH hbrush = r_fillbrush(fill);
2599 RECT rect;
2600
2601 rect.left = x;
2602 rect.right = x + w;
2603 rect.top = y;
2604 rect.bottom = y + h;
2605
2606 FillRect(context.hdc, &rect, hbrush);
2607 }
2608 }
2609
2610
2611 void
r_fill_polygon(IPoint pts,int n)2612 r_fill_polygon(IPoint pts, int n)
2613 { POINT *points = alloca(sizeof(POINT) * n);
2614 HPEN oldpen = ZSelectObject(context.hdc, GetStockObject(NULL_PEN));
2615 int i;
2616
2617 for(i=0; i<n; i++)
2618 { points[i].x = pts[i].x;
2619 points[i].y = pts[i].y;
2620 }
2621
2622 Polygon(context.hdc, points, n);
2623
2624 ZSelectObject(context.hdc, oldpen);
2625 }
2626
2627
2628 void
r_caret(int cx,int cy,FontObj font)2629 r_caret(int cx, int cy, FontObj font)
2630 { int ch, cb, ah, cw2;
2631 int cw = valInt(getExFont(font));
2632 ipoint pts[3];
2633
2634 if ( cw < 4 )
2635 cw = 4;
2636 else if ( cw > 10 )
2637 cw = 10;
2638
2639 ch = valInt(getHeightFont(font));
2640 cw2 = cw/2;
2641 cb = cy + ch-1;
2642 ah = (ch+2)/3;
2643
2644 r_thickness(1);
2645 r_dash(NAME_none);
2646 r_line(cx, cb-2, cx, cb-ch);
2647
2648 pts[0].x = cx - cw2;
2649 pts[0].y = cb;
2650 pts[1].x = cx + cw2;
2651 pts[1].y = cb;
2652 pts[2].x = cx;
2653 pts[2].y = cb-ah;
2654
2655 r_fillpattern(BLACK_IMAGE, NAME_foreground);
2656 r_fill_polygon(pts, 3);
2657 }
2658
2659
2660 void
r_fill_triangle(int x1,int y1,int x2,int y2,int x3,int y3)2661 r_fill_triangle(int x1, int y1, int x2, int y2, int x3, int y3)
2662 { POINT pt[3];
2663 HPEN oldpen = ZSelectObject(context.hdc, GetStockObject(NULL_PEN));
2664
2665 pt[0].x = x1; pt[0].y = y1;
2666 pt[1].x = x2; pt[1].y = y2;
2667 pt[2].x = x3; pt[2].y = y3;
2668
2669 Polygon(context.hdc, pt, 3);
2670 ZSelectObject(context.hdc, oldpen);
2671 }
2672
2673
2674 void
r_triangle(int x1,int y1,int x2,int y2,int x3,int y3,Any fill)2675 r_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Any fill)
2676 { POINT pts[3];
2677
2678 pts[0].x = x1;
2679 pts[0].y = y1;
2680 pts[1].x = x2;
2681 pts[1].y = y2;
2682 pts[2].x = x3;
2683 pts[2].y = y3;
2684
2685 r_fillpattern(fill, NAME_foreground);
2686 Polygon(context.hdc, pts, sizeof(pts)/sizeof(POINT));
2687 }
2688
2689
2690 void
r_pixel(int x,int y,Any val)2691 r_pixel(int x, int y, Any val)
2692 { COLORREF c;
2693
2694 if ( isBoolean(val) )
2695 { if ( val == ON )
2696 c = RGB(0, 0, 0);
2697 else
2698 c = RGB(255, 255, 255);
2699 } else
2700 c = cref_colour(val);
2701
2702 SetPixel(context.hdc, x, y, c);
2703 }
2704
2705
2706 void
r_complement_pixel(int x,int y)2707 r_complement_pixel(int x, int y)
2708 { r_complement(x, y, 1, 1);
2709 }
2710
2711
2712 void
d_modify(void)2713 d_modify(void)
2714 {
2715 }
2716
2717
2718 unsigned long
r_get_pixel(int x,int y)2719 r_get_pixel(int x, int y)
2720 { return (unsigned long) GetPixel(context.hdc, x, y);
2721 }
2722
2723
2724 int
r_get_mono_pixel(int x,int y)2725 r_get_mono_pixel(int x, int y)
2726 { COLORREF p = GetPixel(context.hdc, x, y);
2727
2728 DEBUG(NAME_pixel, Cprintf("r_get_mono_pixel(%d, %d) --> %ld, bg = %ld\n",
2729 x, y, p, context.background_rgb));
2730
2731 return p == context.background_rgb ? FALSE : TRUE;
2732 }
2733
2734
2735 static void
s_font(FontObj font)2736 s_font(FontObj font)
2737 { if ( context.font != font )
2738 { WsFont wsf;
2739 HFONT org;
2740
2741 if ( !context.hdc )
2742 { DEBUG(NAME_redraw, Cprintf("!! Making default context\n"));
2743 make_default_context();
2744 }
2745 wsf = getXrefObject(font, context.display);
2746
2747 DEBUG(NAME_font,
2748 Cprintf("s_font(%s) (hfont = 0x%x)%s\n",
2749 pp(font), (int)(intptr_t)wsf->hfont,
2750 context.hdc == default_hdc ? " (default_hdc)" : ""));
2751
2752 context.wsf = wsf;
2753 org = ZSelectObject(context.hdc, wsf->hfont);
2754 if ( !context.ohfont )
2755 { context.ohfont = org;
2756 }
2757
2758 context.font = font;
2759 }
2760 }
2761
2762
2763 int
s_has_char(FontObj f,unsigned int c)2764 s_has_char(FontObj f, unsigned int c)
2765 { succeed;
2766 }
2767
2768
2769 void
f_domain(FontObj f,Name which,int * x,int * y)2770 f_domain(FontObj f, Name which, int *x, int *y)
2771 { *x = 0;
2772 *y = 255;
2773 }
2774
2775
2776 int
s_default_char(FontObj font)2777 s_default_char(FontObj font)
2778 { return ' ';
2779 }
2780
2781
2782 int
s_ascent(FontObj f)2783 s_ascent(FontObj f)
2784 { s_font(f);
2785
2786 return context.wsf->ascent;
2787 }
2788
2789
2790 int
s_descent(FontObj f)2791 s_descent(FontObj f)
2792 { s_font(f);
2793
2794 return context.wsf->descent;
2795 }
2796
2797
2798 int
s_height(FontObj f)2799 s_height(FontObj f)
2800 { s_font(f);
2801
2802 return context.wsf->ascent + context.wsf->descent;
2803 }
2804
2805
2806 int
c_width(wint_t c,FontObj font)2807 c_width(wint_t c, FontObj font)
2808 { INT w[1];
2809
2810 s_font(font);
2811 if ( GetCharWidth32W(context.hdc, c, c, w) )
2812 return w[0];
2813 else if ( GetCharWidth32W(context.hdc, 'x', 'x', w) )
2814 return w[0];
2815 else
2816 return 10; /* avoid crashes */
2817 }
2818
2819
2820 static inline int
s_width_(PceString s,int from,int to)2821 s_width_(PceString s, int from, int to)
2822 { if ( !context.wsf )
2823 { return 0; /* TBD */
2824 } else
2825 { SIZE size;
2826
2827 if ( isstrA(s) )
2828 { GetTextExtentPoint32A(context.hdc, (char*)s->s_textA+from, to-from, &size);
2829 } else
2830 { GetTextExtentPoint32W(context.hdc, s->s_textW+from, to-from, &size);
2831 }
2832
2833 return size.cx;
2834 }
2835 }
2836
2837
2838 int
str_width(PceString s,int from,int to,FontObj f)2839 str_width(PceString s, int from, int to, FontObj f)
2840 { if ( from < 0 )
2841 from = 0;
2842 if ( from >= s->s_size || to <= from )
2843 return 0;
2844 if ( to > s->s_size )
2845 to = s->s_size;
2846
2847 s_font(f);
2848 return s_width_(s, from, to);
2849 }
2850
2851
2852 int
str_advance(PceString s,int from,int to,FontObj f)2853 str_advance(PceString s, int from, int to, FontObj f)
2854 { if ( !f )
2855 f = context.font;
2856
2857 return str_width(s, from, to, f);
2858 }
2859
2860
2861 void
s_printA(charA * s,int l,int x,int y,FontObj f)2862 s_printA(charA *s, int l, int x, int y, FontObj f)
2863 { if ( l > 0 )
2864 { s_font(f);
2865 y -= context.wsf->ascent;
2866 TextOut(context.hdc, x, y, (char*)s, l);
2867 }
2868 }
2869
2870
2871 void
s_printW(charW * s,int l,int x,int y,FontObj f)2872 s_printW(charW *s, int l, int x, int y, FontObj f)
2873 { if ( l > 0 )
2874 { s_font(f);
2875 y -= context.wsf->ascent;
2876 TextOutW(context.hdc, x, y, s, l);
2877 }
2878 }
2879
2880
2881 void
s_print(PceString s,int x,int y,FontObj f)2882 s_print(PceString s, int x, int y, FontObj f)
2883 { if ( isstrA(s) )
2884 s_printA(s->s_textA, s->s_size, x, y, f);
2885 else
2886 s_printW(s->s_textW, s->s_size, x, y, f);
2887 }
2888
2889
2890 void
s_print_aligned(PceString s,int x,int y,FontObj f)2891 s_print_aligned(PceString s, int x, int y, FontObj f)
2892 { s_print(s, x, y, f); /* same on Win32: no {l,r}bearing */
2893 } /* in font metrics */
2894
2895
2896 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2897 str_text() is only called from str_label, using SetTextAlign() to use
2898 the baseline.
2899 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2900
2901 static void
str_text(PceString s,int x,int y)2902 str_text(PceString s, int x, int y)
2903 { if ( isstrA(s) )
2904 TextOutA(context.hdc, x, y, (char*)s->s_textA, s->s_size);
2905 else
2906 TextOutW(context.hdc, x, y, s->s_textW, s->s_size);
2907 }
2908
2909
2910 void
str_size(PceString s,FontObj font,int * width,int * height)2911 str_size(PceString s, FontObj font, int *width, int *height)
2912 { if ( s->s_size > 0 )
2913 { RECT rect;
2914 UINT flags = DT_CALCRECT|DT_EXTERNALLEADING|DT_NOCLIP|DT_NOPREFIX;
2915 int rval;
2916
2917 rect.left = 0;
2918 rect.top = 0;
2919 rect.right = 0;
2920 rect.bottom = 0;
2921
2922 s_font(font);
2923 if ( isstrA(s) )
2924 rval = DrawTextA(context.hdc, (char*)s->s_textA, s->s_size, &rect, flags);
2925 else
2926 rval = DrawTextW(context.hdc, s->s_textW, s->s_size, &rect, flags);
2927
2928 DEBUG(NAME_font,
2929 { char buf[32];
2930 int n = min(s->s_size, 25);
2931 strncpy(buf, (char*)s->s_textA, n);
2932 buf[n] = EOS;
2933 if ( s->s_size > 25 )
2934 strcat(buf, " ...");
2935
2936 Cprintf("DrawText(\"%s\") --> %d\n", buf, rval);
2937 });
2938
2939 *width = rect.right;
2940 } else
2941 *width = 0; /* looks like a Windows bug ... */
2942
2943 *height = valInt(getHeightFont(font)) *
2944 (str_count_chr(s, 0, s->s_size, '\n')+1);
2945 }
2946
2947
2948
2949 /********************************
2950 * MULTILINE TEXT *
2951 ********************************/
2952
2953 #define MAX_TEXT_LINES 200 /* lines in a text object */
2954
2955 typedef struct
2956 { int x; /* origin x offset */
2957 int y; /* origin y offset */
2958 int width; /* pixel width of line */
2959 int height; /* pixel height of line */
2960 string text; /* text of the line */
2961 } strTextLine;
2962
2963
2964 static void
str_break_into_lines(PceString s,strTextLine * line,int * nlines,int maxlines)2965 str_break_into_lines(PceString s, strTextLine *line, int *nlines, int maxlines)
2966 { int here = 0;
2967 int size = s->s_size;
2968 int nls = 0;
2969
2970 *nlines = 0;
2971
2972 if ( size == 0 ) /* totally empty: report one line */
2973 { str_cphdr(&line->text, s);
2974 line->text.s_text = s->s_text;
2975 line->text.s_size = 0;
2976 *nlines = 1;
2977 return;
2978 }
2979
2980 for( ; here < size && nls < maxlines; line++, nls++ )
2981 { int el;
2982
2983 str_cphdr(&line->text, s);
2984 line->text.s_text = str_textp(s, here);
2985
2986 if ( (el = str_next_index(s, here, '\n')) >= 0 )
2987 { line->text.s_size = el - here;
2988 here = el + 1;
2989 if ( here == size ) /* last char is newline: add a line */
2990 { line++, nls++;
2991 str_cphdr(&line->text, s);
2992 line->text.s_text = str_textp(s, here);
2993 line->text.s_size = 0;
2994 }
2995 } else
2996 { line->text.s_size = size - here;
2997 here = size;
2998 }
2999 }
3000
3001 *nlines = nls;
3002 }
3003
3004
3005 static void
str_compute_lines(strTextLine * lines,int nlines,FontObj font,int x,int y,int w,int h,Name hadjust,Name vadjust)3006 str_compute_lines(strTextLine *lines, int nlines, FontObj font,
3007 int x, int y, int w, int h,
3008 Name hadjust, Name vadjust)
3009 { int cy;
3010 int th = s_height(font);
3011 strTextLine *line;
3012 int n;
3013
3014 if ( vadjust == NAME_top )
3015 cy = y;
3016 else if ( vadjust == NAME_center )
3017 cy = y + (h - nlines*th)/2;
3018 else /*if ( vadjust == NAME_bottom )*/
3019 cy = y + h - nlines*th;
3020
3021 for( n = 0, line = lines; n++ < nlines; line++, cy += th )
3022 { line->y = cy;
3023 line->height = th;
3024 line->width = str_width(&line->text, 0, line->text.s_size, font);
3025
3026 if ( hadjust == NAME_left )
3027 line->x = x;
3028 else if ( hadjust == NAME_center )
3029 line->x = x + (w - line->width)/2;
3030 else /*if ( hadjust == NAME_right )*/
3031 line->x = x + w - line->width;
3032 }
3033 }
3034
3035
3036 void
ps_string(PceString s,FontObj font,int x,int y,int w,Name format,int flags)3037 ps_string(PceString s, FontObj font, int x, int y, int w, Name format, int flags)
3038 { strTextLine lines[MAX_TEXT_LINES];
3039 strTextLine *line;
3040 int nlines, n;
3041 int baseline;
3042
3043 if ( s->s_size == 0 )
3044 return;
3045
3046 s_font(font);
3047 ps_font(font);
3048
3049 baseline = s_ascent(font);
3050 str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES);
3051 str_compute_lines(lines, nlines, font, x, y, w, 0, format, NAME_top);
3052
3053 for(n=0, line = lines; n++ < nlines; line++)
3054 { if ( line->text.s_size > 0 )
3055 { ps_output("~D ~D 0 ~D ~a text\n",
3056 line->x, line->y+baseline,
3057 line->width, &line->text);
3058 if ( flags & TXT_UNDERLINED )
3059 { ps_output("nodash 1 ~D ~D ~D ~D linepath draw\n",
3060 line->x, line->y+baseline+2, line->width, 0);
3061 }
3062 }
3063 }
3064 }
3065
3066
3067 void
str_string(PceString s,FontObj font,int x,int y,int w,int h,Name hadjust,Name vadjust,int flags)3068 str_string(PceString s, FontObj font,
3069 int x, int y, int w, int h,
3070 Name hadjust, Name vadjust, int flags)
3071 { strTextLine lines[MAX_TEXT_LINES];
3072 int nlines;
3073 UINT oalign;
3074 strTextLine *line;
3075 int n;
3076 int baseline;
3077
3078 if ( s->s_size == 0 )
3079 return;
3080
3081 s_font(font);
3082 baseline = s_ascent(font);
3083 str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES);
3084 str_compute_lines(lines, nlines, font, x, y, w, h, hadjust, vadjust);
3085
3086 oalign = SetTextAlign(context.hdc, TA_BASELINE|TA_LEFT|TA_NOUPDATECP);
3087
3088 for(n=0, line = lines; n++ < nlines; line++)
3089 { str_text(&line->text, line->x, line->y+baseline);
3090
3091 if ( flags & TXT_UNDERLINED )
3092 { int ly = line->y+baseline+1;
3093
3094 r_line(line->x, ly, line->x + line->width, ly);
3095 }
3096 }
3097
3098 SetTextAlign(context.hdc, oalign);
3099 }
3100
3101
3102 static void
str_draw_text_lines(int acc,FontObj font,int nlines,strTextLine * lines,int ox,int oy)3103 str_draw_text_lines(int acc, FontObj font,
3104 int nlines, strTextLine *lines,
3105 int ox, int oy)
3106 { strTextLine *line;
3107 int n;
3108 int baseline = s_ascent(font);
3109
3110 for(n=0, line = lines; n++ < nlines; line++)
3111 { str_text(&line->text, line->x+ox, line->y+baseline+oy);
3112
3113 if ( acc )
3114 { int cx = line->x;
3115 int cn;
3116
3117 for(cn=0; cn<line->text.s_size; cn++)
3118 { wint_t c = str_fetch(&line->text, cn);
3119 int cw = c_width(c, font);
3120
3121 if ( tolower(c) == acc )
3122 { r_line(cx+ox, line->y+baseline+1+oy,
3123 cx+cw+ox, line->y+baseline+1+oy);
3124 acc = 0;
3125 break;
3126 }
3127
3128 cx += cw;
3129 }
3130 }
3131 }
3132 }
3133
3134
3135 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3136 Avoid fixed_colours and speedup a little.
3137 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3138
3139 static Any
r_text_colour(Any colour)3140 r_text_colour(Any colour)
3141 { Any old = context.colour;
3142
3143 if ( old != colour )
3144 { context.modified_pen = TRUE; /* for the line! */
3145 context.colour = colour;
3146 context.rgb = cref_colour(colour);
3147 SetTextColor(context.hdc, context.rgb);
3148 }
3149
3150 return old;
3151 }
3152
3153
3154 void
str_label(PceString s,int acc,FontObj font,int x,int y,int w,int h,Name hadjust,Name vadjust,int flags)3155 str_label(PceString s, int acc, FontObj font, int x, int y, int w, int h,
3156 Name hadjust, Name vadjust, int flags)
3157 { strTextLine lines[MAX_TEXT_LINES];
3158 int nlines;
3159 UINT oalign;
3160
3161 if ( s->s_size == 0 )
3162 return;
3163
3164 s_font(font);
3165 str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES);
3166 str_compute_lines(lines, nlines, font, x, y, w, h, hadjust, vadjust);
3167
3168 oalign = SetTextAlign(context.hdc, TA_BASELINE|TA_LEFT|TA_NOUPDATECP);
3169
3170 if ( flags & LABEL_INACTIVE )
3171 { Any old = r_text_colour(WHITE_COLOUR);
3172
3173 str_draw_text_lines(acc, font, nlines, lines, 1, 1);
3174 r_text_colour(ws_3d_grey());
3175 str_draw_text_lines(acc, font, nlines, lines, 0, 0);
3176 r_text_colour(old);
3177 } else
3178 str_draw_text_lines(acc, font, nlines, lines, 0, 0);
3179
3180 SetTextAlign(context.hdc, oalign);
3181 }
3182
3183
3184 static void
str_stext(PceString s,int f,int len,Style style)3185 str_stext(PceString s, int f, int len, Style style)
3186 { if ( len > 0 )
3187 { Any ofg = NULL;
3188 int w = 0; /* make compiler happy */
3189 POINT here;
3190
3191 if ( notNil(style) )
3192 { w = s_width_(s, f, f+len);
3193 MoveToEx(context.hdc, 0, 0, &here);
3194
3195 if ( notDefault(style->background) )
3196 { int a = context.wsf->ascent;
3197 int b = context.wsf->descent;
3198
3199 r_fill(here.x, here.y-a, w, b+a, style->background);
3200 MoveToEx(context.hdc, here.x, here.y, NULL);
3201 }
3202 if ( notDefault(style->colour) )
3203 ofg = r_colour(style->colour);
3204 }
3205
3206 if ( isstrA(s) )
3207 { TextOut(context.hdc, 0, 0, (char*)s->s_textA+f, len);
3208 } else
3209 { TextOutW(context.hdc, 0, 0, s->s_textW+f, len);
3210 }
3211
3212 if ( notNil(style) && (style->attributes & TXT_UNDERLINED) )
3213 r_line(here.x, here.y, here.x+w, here.y);
3214
3215 if ( ofg )
3216 r_colour(ofg);
3217 }
3218 }
3219
3220
3221 void
str_selected_string(PceString s,FontObj font,int f,int t,Style style,int x,int y,int w,int h,Name hadjust,Name vadjust)3222 str_selected_string(PceString s, FontObj font,
3223 int f, int t, Style style, /* selection parameters */
3224 int x, int y, int w, int h,
3225 Name hadjust, Name vadjust)
3226 { strTextLine lines[MAX_TEXT_LINES];
3227 strTextLine *line;
3228 int nlines, n;
3229 int baseline;
3230 int here = 0;
3231 UINT oalign;
3232
3233 if ( s->s_size == 0 )
3234 return;
3235
3236 s_font(font);
3237 baseline = context.wsf->ascent;
3238 str_break_into_lines(s, lines, &nlines, MAX_TEXT_LINES);
3239 str_compute_lines(lines, nlines, font, x, y, w, h, hadjust, vadjust);
3240
3241 oalign = SetTextAlign(context.hdc, TA_BASELINE|TA_LEFT|TA_UPDATECP);
3242
3243 for(n=0, line = lines; n++ < nlines; line++)
3244 { int len = line->text.s_size;
3245
3246 MoveToEx(context.hdc, line->x, line->y+baseline, NULL);
3247
3248 if ( t <= here || f >= here+len || f == t ) /* outside */
3249 str_stext(&line->text, 0, len, NIL);
3250 else
3251 { int sf, sl;
3252
3253 sf = (f <= here ? 0 : f-here);
3254 sl = (t >= here+len ? len-sf : t-here-sf);
3255
3256 str_stext(&line->text, 0, sf, NIL);
3257 str_stext(&line->text, sf, sl, style);
3258 if ( sf+sl < len )
3259 { int a = sf+sl;
3260 str_stext(&line->text, a, len-a, NIL);
3261 }
3262 }
3263
3264 here += len + 1; /* 1 for the newline */
3265 }
3266
3267 SetTextAlign(context.hdc, oalign);
3268 }
3269