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