1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 /* Lefteris Koutsofios - AT&T Labs Research */
15 
16 #include "common.h"
17 #include "g.h"
18 #include "gcommon.h"
19 #include "mem.h"
20 
21 #define WCU widget->u.c
22 #define WINDOW widget->u.c->window
23 #define GC widget->u.c->gc
24 #define ISVISIBLE(r) ( \
25     (r.o.x <= WCU->clip.c.x) && (r.c.x >= WCU->clip.o.x) && \
26     (r.o.y <= WCU->clip.c.y) && (r.c.y >= WCU->clip.o.y) \
27 )
28 
29 #define max(a, b) (((a) >= (b)) ? (a) : (b))
30 #define min(a, b) (((a) <= (b)) ? (a) : (b))
31 
32 static long gstyles[5] = {
33     /* G_SOLID */       PS_SOLID,
34     /* G_DASHED */      PS_DASH,
35     /* G_DOTTED */      PS_DOT,
36     /* G_LONGDASHED */  PS_DASH,
37     /* G_SHORTDASHED */ PS_DASH,
38 };
39 
40 static char grays[][4] = {
41     { 0x00, 0x00, 0x00, 0x00 },
42     { 0x08, 0x00, 0x00, 0x00 },
43     { 0x08, 0x00, 0x02, 0x00 },
44     { 0x0A, 0x00, 0x02, 0x00 },
45     { 0x0A, 0x00, 0x0A, 0x00 },
46     { 0x0A, 0x04, 0x0A, 0x00 },
47     { 0x0A, 0x04, 0x0A, 0x01 },
48     { 0x0A, 0x05, 0x0A, 0x01 },
49     { 0x0A, 0x05, 0x0A, 0x05 },
50     { 0x0E, 0x05, 0x0A, 0x05 },
51     { 0x0E, 0x05, 0x0B, 0x05 },
52     { 0x0F, 0x05, 0x0B, 0x05 },
53     { 0x0F, 0x05, 0x0F, 0x05 },
54     { 0x0F, 0x0D, 0x0F, 0x05 },
55     { 0x0F, 0x0D, 0x0F, 0x07 },
56     { 0x0F, 0x0F, 0x0F, 0x07 },
57     { 0x0F, 0x0F, 0x0F, 0x0F }
58 };
59 
60 static int curcursori = -1;
61 
62 static void bezier (PIXpoint_t, PIXpoint_t, PIXpoint_t, PIXpoint_t);
63 static HFONT findfont (char *, int);
64 static int scalebitmap (Gwidget_t *, Gbitmap_t *, Gsize_t, int, int);
65 static void setgattr (Gwidget_t *, Ggattr_t *);
66 
67 static PIXrect_t  rdrawtopix (Gwidget_t *, Grect_t);
68 static PIXpoint_t pdrawtopix (Gwidget_t *, Gpoint_t);
69 static PIXsize_t  sdrawtopix (Gwidget_t *, Gsize_t);
70 static Gsize_t    spixtodraw (Gwidget_t *, PIXsize_t);
71 static Grect_t    rpixtodraw (Gwidget_t *, PIXrect_t);
72 static PIXrect_t  rdrawtobpix (Gbitmap_t *, Grect_t);
73 static PIXpoint_t pdrawtobpix (Gbitmap_t *, Gpoint_t);
74 
GCcreatewidget(Gwidget_t * parent,Gwidget_t * widget,int attrn,Gwattr_t * attrp)75 int GCcreatewidget (
76     Gwidget_t *parent, Gwidget_t *widget, int attrn, Gwattr_t *attrp
77 ) {
78     PIXsize_t ps;
79     /* the 2 here is to provide enough space for palPalEntry[0] and [1] */
80     LOGPALETTE pal[2];
81 
82     HBRUSH brush;
83     HPEN pen;
84     HBITMAP bmap;
85     HCURSOR cursor;
86     DWORD wflags;
87     int color, ai, i;
88 
89     if (!parent) {
90         Gerr (POS, G_ERRNOPARENTWIDGET);
91         return -1;
92     }
93     wflags = WS_CHILDWINDOW;
94     WCU->func = NULL;
95     WCU->needredraw = FALSE;
96     WCU->buttonsdown = 0;
97     WCU->bstate[0] = WCU->bstate[1] = WCU->bstate[2] = 0;
98     ps.x = ps.y = MINCWSIZE;
99     for (ai = 0; ai < attrn; ai++) {
100         switch (attrp[ai].id) {
101         case G_ATTRSIZE:
102             GETSIZE (attrp[ai].u.s, ps, MINCWSIZE);
103             break;
104         case G_ATTRBORDERWIDTH:
105             wflags |= WS_BORDER;
106             break;
107         case G_ATTRCURSOR:
108             /* will do it after the widget is created */
109             break;
110         case G_ATTRCOLOR:
111             /* will do it after the widget is created */
112             break;
113         case G_ATTRVIEWPORT:
114             /* will do it after the widget is created */
115             break;
116         case G_ATTRWINDOW:
117             /* will do it after the widget is created */
118             break;
119         case G_ATTRWINDOWID:
120             Gerr (POS, G_ERRCANNOTSETATTR1, "windowid");
121             return -1;
122         case G_ATTREVENTCB:
123             WCU->func = attrp[ai].u.func;
124             break;
125         case G_ATTRUSERDATA:
126             widget->udata = attrp[ai].u.u;
127             break;
128         default:
129             Gerr (POS, G_ERRBADATTRID, attrp[ai].id);
130             return -1;
131         }
132     }
133     Gadjustwrect (parent, &ps);
134     WCU->wrect.o.x = 0.0, WCU->wrect.o.y = 0.0;
135     WCU->wrect.c.x = 1.0, WCU->wrect.c.y = 1.0;
136     WCU->vsize.x = ps.x, WCU->vsize.y = ps.y;
137     if (!(widget->w = CreateWindow (
138         "CanvasClass", "canvas", wflags, 0, 0,
139         ps.x, ps.y, parent->w, (HMENU) (widget - &Gwidgets[0]),
140         hinstance, NULL
141     ))) {
142         Gerr (POS, G_ERRCANNOTCREATEWIDGET);
143         return -1;
144     }
145     ShowWindow (widget->w, SW_SHOW);
146     UpdateWindow (widget->w);
147     SetCursor (LoadCursor ((HINSTANCE) NULL, IDC_ARROW));
148     GC = GetDC (widget->w);
149     WCU->ncolor = 2;
150     pal[0].palVersion = 0x300; /* HA HA HA */
151     pal[0].palNumEntries = 2;
152     pal[0].palPalEntry[0].peRed = 255;
153     pal[0].palPalEntry[0].peGreen = 255;
154     pal[0].palPalEntry[0].peBlue = 255;
155     pal[0].palPalEntry[0].peFlags = 0;
156     pal[0].palPalEntry[1].peRed = 0;
157     pal[0].palPalEntry[1].peGreen = 0;
158     pal[0].palPalEntry[1].peBlue = 0;
159     pal[0].palPalEntry[1].peFlags = 0;
160     WCU->cmap = CreatePalette (&pal[0]);
161     WCU->colors[0].color = pal[0].palPalEntry[0];
162     for (i = 1; i < G_MAXCOLORS; i++)
163         WCU->colors[i].color = pal[0].palPalEntry[1];
164     SelectPalette (GC, WCU->cmap, FALSE);
165     RealizePalette (GC);
166     WCU->colors[0].inuse = TRUE;
167     WCU->colors[1].inuse = TRUE;
168     for (i = 2; i < G_MAXCOLORS; i++)
169         WCU->colors[i].inuse = FALSE;
170     WCU->gattr.color = 1;
171     brush = CreateSolidBrush (PALETTEINDEX (1));
172     SelectObject (GC, brush);
173     pen = CreatePen (PS_SOLID, 1, PALETTEINDEX (1));
174     SelectObject (GC, pen);
175     SetTextColor (GC, PALETTEINDEX (1));
176     SetBkMode (GC, TRANSPARENT);
177     WCU->gattr.width = 0;
178     WCU->gattr.mode = G_SRC;
179     WCU->gattr.fill = 0;
180     WCU->gattr.style = 0;
181     WCU->defgattr = WCU->gattr;
182     WCU->font = NULL;
183     if (Gdepth == 1) {
184         for (i = 0; i < 17; i++) {
185             if (!(bmap = CreateBitmap (4, 4, 1, 1, &grays[i][0])))
186                 continue;
187             WCU->grays[i] = CreatePatternBrush (bmap);
188         }
189     }
190     for (ai = 0; ai < attrn; ai++) {
191         switch (attrp[ai].id) {
192         case G_ATTRCURSOR:
193             if (strcmp (attrp[ai].u.t, "watch") == 0) {
194                 curcursori = 1;
195                 cursor = LoadCursor ((HINSTANCE) NULL, IDC_WAIT);
196             } else if (strcmp (attrp[ai].u.t, "default") == 0) {
197                 curcursori = -1;
198                 cursor = LoadCursor ((HINSTANCE) NULL, IDC_ARROW);
199             } else {
200                 Gerr (POS, G_ERRNOSUCHCURSOR, attrp[ai].u.t);
201                 return -1;
202             }
203             SetCursor (cursor);
204             break;
205         case G_ATTRCOLOR:
206             color = attrp[ai].u.c.index;
207             if (color < 0 || color > G_MAXCOLORS) {
208                 Gerr (POS, G_ERRBADCOLORINDEX, color);
209                 return -1;
210             }
211             WCU->colors[color].color.peRed = attrp[ai].u.c.r;
212             WCU->colors[color].color.peGreen = attrp[ai].u.c.g;
213             WCU->colors[color].color.peBlue = attrp[ai].u.c.b;
214             WCU->colors[color].color.peFlags = 0;
215             if (color >= WCU->ncolor)
216                 ResizePalette (WCU->cmap, color + 1), WCU->ncolor = color + 1;
217             SetPaletteEntries (
218                 WCU->cmap, (int) color, 1, &WCU->colors[color].color);
219             RealizePalette (GC);
220             WCU->colors[color].inuse = TRUE;
221             if (color == WCU->gattr.color)
222                 WCU->gattr.color = -1;
223             break;
224         case G_ATTRVIEWPORT:
225             if (attrp[ai].u.s.x == 0)
226                 attrp[ai].u.s.x = 1;
227             if (attrp[ai].u.s.y == 0)
228                 attrp[ai].u.s.y = 1;
229             WCU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
230             WCU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
231             SetWindowPos (
232                 widget->w, (HWND) NULL, 0, 0, WCU->vsize.x,
233                 WCU->vsize.y, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE
234             );
235             break;
236         case G_ATTRWINDOW:
237             if (attrp[ai].u.r.o.x == attrp[ai].u.r.c.x)
238                 attrp[ai].u.r.c.x = attrp[ai].u.r.o.x + 1;
239             if (attrp[ai].u.r.o.y == attrp[ai].u.r.c.y)
240                 attrp[ai].u.r.c.y = attrp[ai].u.r.o.y + 1;
241             WCU->wrect = attrp[ai].u.r;
242             break;
243         }
244     }
245     if (parent && parent->type == G_ARRAYWIDGET)
246         Gawinsertchild (parent, widget);
247     Gadjustclip (widget);
248     return 0;
249 }
250 
GCsetwidgetattr(Gwidget_t * widget,int attrn,Gwattr_t * attrp)251 int GCsetwidgetattr (Gwidget_t *widget, int attrn, Gwattr_t *attrp) {
252     HCURSOR cursor;
253     Gwidget_t *parent;
254     PIXsize_t ps;
255     DWORD wflags1;
256     int ai, color;
257 
258     parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
259     wflags1 = SWP_NOMOVE | SWP_NOZORDER;
260     for (ai = 0; ai < attrn; ai++) {
261         switch (attrp[ai].id) {
262         case G_ATTRSIZE:
263             GETSIZE (attrp[ai].u.s, ps, MINCWSIZE);
264             Gadjustwrect (parent, &ps);
265             SetWindowPos (widget->w, (HWND) NULL, 0, 0, ps.x, ps.y, wflags1);
266             break;
267         case G_ATTRBORDERWIDTH:
268             Gerr (POS, G_ERRCANNOTSETATTR2, "borderwidth");
269             return -1;
270         case G_ATTRCURSOR:
271             if (strcmp (attrp[ai].u.t, "watch") == 0) {
272                 curcursori = 1;
273                 cursor = LoadCursor ((HINSTANCE) NULL, IDC_WAIT);
274             } else if (strcmp (attrp[ai].u.t, "default") == 0) {
275                 curcursori = -1;
276                 cursor = LoadCursor ((HINSTANCE) NULL, IDC_ARROW);
277             } else {
278                 Gerr (POS, G_ERRNOSUCHCURSOR, attrp[ai].u.t);
279                 return -1;
280             }
281             SetCursor (cursor);
282             break;
283         case G_ATTRCOLOR:
284             color = attrp[ai].u.c.index;
285             if (color < 0 || color > G_MAXCOLORS) {
286                 Gerr (POS, G_ERRBADCOLORINDEX, color);
287                 return -1;
288             }
289             WCU->colors[color].color.peRed = attrp[ai].u.c.r;
290             WCU->colors[color].color.peGreen = attrp[ai].u.c.g;
291             WCU->colors[color].color.peBlue = attrp[ai].u.c.b;
292             WCU->colors[color].color.peFlags = 0;
293             if (color >= WCU->ncolor)
294                 ResizePalette (WCU->cmap, color + 1), WCU->ncolor = color + 1;
295             SetPaletteEntries (
296                 WCU->cmap, (int) color, 1, &WCU->colors[color].color
297             );
298             RealizePalette (GC);
299             WCU->colors[color].inuse = TRUE;
300             if (color == WCU->gattr.color)
301                 WCU->gattr.color = -1;
302             break;
303         case G_ATTRVIEWPORT:
304             if (attrp[ai].u.s.x == 0)
305                 attrp[ai].u.s.x = 1;
306             if (attrp[ai].u.s.y == 0)
307                 attrp[ai].u.s.y = 1;
308             WCU->vsize.x = (int) (attrp[ai].u.s.x + 0.5);
309             WCU->vsize.y = (int) (attrp[ai].u.s.y + 0.5);
310             ps.x = WCU->vsize.x, ps.y = WCU->vsize.y;
311             Gadjustwrect (&Gwidgets[widget->pwi], &ps);
312             SetWindowPos (
313                 widget->w, (HWND) NULL, 0, 0, ps.x,
314                 ps.y, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE
315             );
316             Gadjustclip (widget);
317             break;
318         case G_ATTRWINDOW:
319             if (attrp[ai].u.r.o.x == attrp[ai].u.r.c.x)
320                 attrp[ai].u.r.c.x = attrp[ai].u.r.o.x + 1;
321             if (attrp[ai].u.r.o.y == attrp[ai].u.r.c.y)
322                 attrp[ai].u.r.c.y = attrp[ai].u.r.o.y + 1;
323             WCU->wrect = attrp[ai].u.r;
324             Gadjustclip (widget);
325             break;
326         case G_ATTRWINDOWID:
327             Gerr (POS, G_ERRCANNOTSETATTR2, "windowid");
328             return -1;
329         case G_ATTREVENTCB:
330             WCU->func = attrp[ai].u.func;
331             break;
332         case G_ATTRUSERDATA:
333             widget->udata = attrp[ai].u.u;
334             break;
335         default:
336             Gerr (POS, G_ERRBADATTRID, attrp[ai].id);
337             return -1;
338         }
339     }
340     return 0;
341 }
342 
GCgetwidgetattr(Gwidget_t * widget,int attrn,Gwattr_t * attrp)343 int GCgetwidgetattr (Gwidget_t *widget, int attrn, Gwattr_t *attrp) {
344     PALETTEENTRY *cp;
345     RECT r;
346     int color, ai;
347 
348     for (ai = 0; ai < attrn; ai++) {
349         switch (attrp[ai].id) {
350         case G_ATTRSIZE:
351             GetWindowRect (widget->w, &r);
352             attrp[ai].u.s.x = r.right - r.left;
353             attrp[ai].u.s.y = r.bottom - r.top;
354             break;
355         case G_ATTRBORDERWIDTH:
356             Gerr (POS, G_ERRCANNOTGETATTR, "borderwidth");
357             return -1;
358         case G_ATTRCURSOR:
359             attrp[ai].u.t = (curcursori == -1) ? "default" : "watch";
360             break;
361         case G_ATTRCOLOR:
362             color = attrp[ai].u.c.index;
363             if (color < 0 || color > G_MAXCOLORS) {
364                 Gerr (POS, G_ERRBADCOLORINDEX, color);
365                 return -1;
366             }
367             if (WCU->colors[color].inuse) {
368                 cp = &WCU->colors[color].color;
369                 attrp[ai].u.c.r = cp->peRed;
370                 attrp[ai].u.c.g = cp->peGreen;
371                 attrp[ai].u.c.b = cp->peBlue;
372             } else {
373                 attrp[ai].u.c.r = -1;
374                 attrp[ai].u.c.g = -1;
375                 attrp[ai].u.c.b = -1;
376             }
377             break;
378         case G_ATTRVIEWPORT:
379             attrp[ai].u.s = WCU->vsize;
380             break;
381         case G_ATTRWINDOW:
382             attrp[ai].u.r = WCU->wrect;
383             break;
384         case G_ATTRWINDOWID:
385             sprintf (&Gbufp[0], "0x%lx", widget->w);
386             attrp[ai].u.t = &Gbufp[0];
387             break;
388         case G_ATTREVENTCB:
389             attrp[ai].u.func = WCU->func;
390             break;
391         case G_ATTRUSERDATA:
392             attrp[ai].u.u = widget->udata;
393             break;
394         default:
395             Gerr (POS, G_ERRBADATTRID, attrp[ai].id);
396             return -1;
397         }
398     }
399     return 0;
400 }
401 
GCdestroywidget(Gwidget_t * widget)402 int GCdestroywidget (Gwidget_t *widget) {
403     Gwidget_t *parent;
404 
405     parent = (widget->pwi == -1) ? NULL : &Gwidgets[widget->pwi];
406     if (parent && parent->type == G_ARRAYWIDGET)
407         Gawdeletechild (parent, widget);
408     DestroyWindow (widget->w);
409     return 0;
410 }
411 
GCcanvasclear(Gwidget_t * widget)412 int GCcanvasclear (Gwidget_t *widget) {
413     Ggattr_t attr;
414     RECT r;
415     HBRUSH brush, pbrush;
416 
417     attr.flags = 0;
418     setgattr (widget, &attr);
419     brush = CreateSolidBrush (PALETTEINDEX (0));
420     pbrush = SelectObject (GC, brush);
421     GetClientRect (widget->w, &r);
422     Rectangle (GC, r.left, r.top, r.right, r.bottom);
423     SelectObject (GC, pbrush);
424     DeleteObject (brush);
425     WCU->needredraw = FALSE;
426     return 0;
427 }
428 
GCsetgfxattr(Gwidget_t * widget,Ggattr_t * ap)429 int GCsetgfxattr (Gwidget_t *widget, Ggattr_t *ap) {
430     setgattr (widget, ap);
431     WCU->defgattr = WCU->gattr;
432     return 0;
433 }
434 
GCgetgfxattr(Gwidget_t * widget,Ggattr_t * ap)435 int GCgetgfxattr (Gwidget_t *widget, Ggattr_t *ap) {
436     if ((ap->flags & G_GATTRCOLOR))
437         ap->color = WCU->gattr.color;
438     if ((ap->flags & G_GATTRWIDTH))
439         ap->width = WCU->gattr.width;
440     if ((ap->flags & G_GATTRMODE))
441         ap->mode = WCU->gattr.mode;
442     if ((ap->flags & G_GATTRFILL))
443         ap->fill = WCU->gattr.fill;
444     if ((ap->flags & G_GATTRSTYLE))
445         ap->style = WCU->gattr.style;
446     return 0;
447 }
448 
GCarrow(Gwidget_t * widget,Gpoint_t gp1,Gpoint_t gp2,Ggattr_t * ap)449 int GCarrow (Gwidget_t *widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t *ap) {
450     PIXpoint_t pp1, pp2, pa, pb, pd;
451     Grect_t gr;
452     double tangent, l;
453 
454     if (gp1.x < gp2.x)
455         gr.o.x = gp1.x, gr.c.x = gp2.x;
456     else
457         gr.o.x = gp2.x, gr.c.x = gp1.x;
458     if (gp1.y < gp2.y)
459         gr.o.y = gp1.y, gr.c.y = gp2.y;
460     else
461         gr.o.y = gp2.y, gr.c.y = gp1.y;
462     if (!ISVISIBLE (gr))
463         return 1;
464     pp1 = pdrawtopix (widget, gp1), pp2 = pdrawtopix (widget, gp2);
465     pd.x = pp1.x - pp2.x, pd.y = pp1.y - pp2.y;
466     if (pd.x == 0 && pd.y == 0)
467         return 0;
468     tangent = atan2 ((double) pd.y, (double) pd.x);
469     if ((l = sqrt ((double) (pd.x * pd.x + pd.y * pd.y))) > 30)
470         l = 30;
471     pa.x = l * cos (tangent + M_PI / 7) + pp2.x;
472     pa.y = l * sin (tangent + M_PI / 7) + pp2.y;
473     pb.x = l * cos (tangent - M_PI / 7) + pp2.x;
474     pb.y = l * sin (tangent - M_PI / 7) + pp2.y;
475     setgattr (widget, ap);
476     MoveToEx (GC, pp1.x, pp1.y, NULL), LineTo (GC, pp2.x, pp2.y);
477     MoveToEx (GC, pa.x, pa.y, NULL), LineTo (GC, pp2.x, pp2.y);
478     MoveToEx (GC, pb.x, pb.y, NULL), LineTo (GC, pp2.x, pp2.y);
479     return 0;
480 }
481 
GCline(Gwidget_t * widget,Gpoint_t gp1,Gpoint_t gp2,Ggattr_t * ap)482 int GCline (Gwidget_t *widget, Gpoint_t gp1, Gpoint_t gp2, Ggattr_t *ap) {
483     PIXpoint_t pp1, pp2;
484     Grect_t gr;
485 
486     if (gp1.x < gp2.x)
487         gr.o.x = gp1.x, gr.c.x = gp2.x;
488     else
489         gr.o.x = gp2.x, gr.c.x = gp1.x;
490     if (gp1.y < gp2.y)
491         gr.o.y = gp1.y, gr.c.y = gp2.y;
492     else
493         gr.o.y = gp2.y, gr.c.y = gp1.y;
494     if (!ISVISIBLE (gr))
495         return 1;
496     pp1 = pdrawtopix (widget, gp1), pp2 = pdrawtopix (widget, gp2);
497     setgattr (widget, ap);
498     MoveToEx (GC, pp1.x, pp1.y, NULL);
499     LineTo (GC, pp2.x, pp2.y);
500     return 0;
501 }
502 
GCbox(Gwidget_t * widget,Grect_t gr,Ggattr_t * ap)503 int GCbox (Gwidget_t *widget, Grect_t gr, Ggattr_t *ap) {
504     PIXrect_t pr;
505     Grect_t gr2;
506 
507     if (gr.o.x <= gr.c.x)
508         gr2.o.x = gr.o.x, gr2.c.x = gr.c.x;
509     else
510         gr2.o.x = gr.c.x, gr2.c.x = gr.o.x;
511     if (gr.o.y <= gr.c.y)
512         gr2.o.y = gr.o.y, gr2.c.y = gr.c.y;
513     else
514         gr2.o.y = gr.c.y, gr2.c.y = gr.o.y;
515     if (!ISVISIBLE (gr2))
516         return 1;
517     pr = rdrawtopix (widget, gr);
518     setgattr (widget, ap);
519     if (WCU->gattr.fill)
520         Rectangle (GC, pr.o.x, pr.o.y, pr.c.x, pr.c.y);
521     else {
522         Gppp[0].x = pr.o.x, Gppp[0].y = pr.o.y;
523         Gppp[1].x = pr.c.x, Gppp[1].y = pr.o.y;
524         Gppp[2].x = pr.c.x, Gppp[2].y = pr.c.y;
525         Gppp[3].x = pr.o.x, Gppp[3].y = pr.c.y;
526         Gppp[4].x = pr.o.x, Gppp[4].y = pr.o.y;
527         Polyline (GC, Gppp, 5);
528     }
529     return 0;
530 }
531 
GCpolygon(Gwidget_t * widget,int gpn,Gpoint_t * gpp,Ggattr_t * ap)532 int GCpolygon (Gwidget_t *widget, int gpn, Gpoint_t *gpp, Ggattr_t *ap) {
533     Grect_t gr;
534     int n, i;
535 
536     if (gpn == 0)
537         return 0;
538     gr.o = gpp[0], gr.c = gpp[0];
539     for (i = 1; i < gpn; i++) {
540         gr.o.x = min (gr.o.x, gpp[i].x);
541         gr.o.y = min (gr.o.y, gpp[i].y);
542         gr.c.x = max (gr.c.x, gpp[i].x);
543         gr.c.y = max (gr.c.y, gpp[i].y);
544     }
545     if (!ISVISIBLE (gr))
546         return 1;
547     if (gpn + 1 > Gppn) {
548         n = (((gpn + 1) + PPINCR - 1) / PPINCR) * PPINCR;
549         Gppp = Marraygrow (Gppp, (long) n * PPSIZE);
550         Gppn = n;
551     }
552     for (i = 0; i < gpn; i++)
553         Gppp[i] = pdrawtopix (widget, gpp[i]);
554     setgattr (widget, ap);
555     if (WCU->gattr.fill) {
556         if (Gppp[gpn - 1].x != Gppp[0].x || Gppp[gpn - 1].y != Gppp[0].y)
557             Gppp[gpn] = Gppp[0], gpn++;
558         Polygon (GC, Gppp, (int) gpn);
559     } else
560         Polyline (GC, Gppp, (int) gpn);
561     return 0;
562 }
563 
GCsplinegon(Gwidget_t * widget,int gpn,Gpoint_t * gpp,Ggattr_t * ap)564 int GCsplinegon (Gwidget_t *widget, int gpn, Gpoint_t *gpp, Ggattr_t *ap) {
565     PIXpoint_t p0, p1, p2, p3;
566     Grect_t gr;
567     int n, i;
568 
569     if (gpn == 0)
570         return 0;
571     gr.o = gpp[0], gr.c = gpp[0];
572     for (i = 1; i < gpn; i++) {
573         gr.o.x = min (gr.o.x, gpp[i].x);
574         gr.o.y = min (gr.o.y, gpp[i].y);
575         gr.c.x = max (gr.c.x, gpp[i].x);
576         gr.c.y = max (gr.c.y, gpp[i].y);
577     }
578     if (!ISVISIBLE (gr))
579         return 1;
580     Gppi = 1;
581     if (Gppi >= Gppn) {
582         n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
583         Gppp = Marraygrow (Gppp, (long) n * PPSIZE);
584         Gppn = n;
585     }
586     Gppp[0] = p3 = pdrawtopix (widget, gpp[0]);
587     for (i = 1; i < gpn; i += 3) {
588         p0 = p3;
589         p1 = pdrawtopix (widget, gpp[i]);
590         p2 = pdrawtopix (widget, gpp[i + 1]);
591         p3 = pdrawtopix (widget, gpp[i + 2]);
592         bezier (p0, p1, p2, p3);
593     }
594     setgattr (widget, ap);
595     if (WCU->gattr.fill) {
596         if (Gppp[Gppi - 1].x != Gppp[0].x || Gppp[Gppi - 1].y != Gppp[0].y)
597             Gppp[Gppi] = Gppp[0], Gppi++;
598         Polygon (GC, Gppp, (int) Gppi);
599     } else
600         Polyline (GC, Gppp, (int) Gppi);
601     return 0;
602 }
603 
bezier(PIXpoint_t p0,PIXpoint_t p1,PIXpoint_t p2,PIXpoint_t p3)604 static void bezier (
605     PIXpoint_t p0, PIXpoint_t p1, PIXpoint_t p2, PIXpoint_t p3
606 ) {
607     Gpoint_t gp0, gp1, gp2;
608     Gsize_t s;
609     PIXpoint_t p;
610     double t;
611     int n, i, steps;
612 
613     if ((s.x = p3.x - p0.x) < 0)
614         s.x = - s.x;
615     if ((s.y = p3.y - p0.y) < 0)
616         s.y = - s.y;
617     if (s.x > s.y)
618         steps = s.x / 5 + 1;
619     else
620         steps = s.y / 5 + 1;
621     for (i = 0; i <= steps; i++) {
622         t = i / (double) steps;
623         gp0.x = p0.x + t * (p1.x - p0.x);
624         gp0.y = p0.y + t * (p1.y - p0.y);
625         gp1.x = p1.x + t * (p2.x - p1.x);
626         gp1.y = p1.y + t * (p2.y - p1.y);
627         gp2.x = p2.x + t * (p3.x - p2.x);
628         gp2.y = p2.y + t * (p3.y - p2.y);
629         gp0.x = gp0.x + t * (gp1.x - gp0.x);
630         gp0.y = gp0.y + t * (gp1.y - gp0.y);
631         gp1.x = gp1.x + t * (gp2.x - gp1.x);
632         gp1.y = gp1.y + t * (gp2.y - gp1.y);
633         p.x = gp0.x + t * (gp1.x - gp0.x) + 0.5;
634         p.y = gp0.y + t * (gp1.y - gp0.y) + 0.5;
635         if (Gppi >= Gppn) {
636             n = (((Gppi + 1) + PPINCR - 1) / PPINCR) * PPINCR;
637             Gppp = Marraygrow (Gppp, (long) n * PPSIZE);
638             Gppn = n;
639         }
640         Gppp[Gppi++] = p;
641     }
642 }
643 
GCarc(Gwidget_t * widget,Gpoint_t gc,Gsize_t gs,double ang1,double ang2,Ggattr_t * ap)644 int GCarc (
645     Gwidget_t *widget, Gpoint_t gc, Gsize_t gs, double ang1,
646     double ang2, Ggattr_t *ap
647 ) {
648     PIXpoint_t pc;
649     PIXsize_t ps;
650     Grect_t gr;
651     double a1, a2;
652 
653     gr.o.x = gc.x - gs.x, gr.o.y = gc.y - gs.y;
654     gr.c.x = gc.x + gs.x, gr.c.y = gc.y + gs.y;
655     if (!ISVISIBLE (gr))
656         return 1;
657     pc = pdrawtopix (widget, gc), ps = sdrawtopix (widget, gs);
658     setgattr (widget, ap);
659     a1 = ang1 * M_PI / 180, a2 = ang2 * M_PI / 180;
660     if (WCU->gattr.fill)
661         Chord (
662             GC, pc.x - ps.x, pc.y - ps.y, pc.x + ps.x, pc.y + ps.y,
663             (int) (cos (a1) * ps.x), (int) (sin (a1) * ps.x),
664             (int) (cos (a2) * ps.x), (int) (sin (a2) * ps.x)
665         );
666     else
667         Arc (
668             GC, pc.x - ps.x, pc.y - ps.y, pc.x + ps.x, pc.y + ps.y,
669             (int) (cos (a1) * ps.x), (int) (sin (a1) * ps.x),
670             (int) (cos (a2) * ps.x), (int) (sin (a2) * ps.x)
671         );
672     return 0;
673 }
674 
675 #define YSCALE ((WCU->vsize.y) / (WCU->wrect.c.y - WCU->wrect.o.y))
676 
GCtext(Gwidget_t * widget,Gtextline_t * tlp,int n,Gpoint_t go,char * fn,double fs,char * justs,Ggattr_t * ap)677 int GCtext (
678     Gwidget_t *widget, Gtextline_t *tlp, int n, Gpoint_t go,
679     char *fn, double fs, char *justs, Ggattr_t *ap
680 ) {
681     Gsize_t gs;
682     PIXpoint_t po;
683     PIXsize_t ps;
684     PIXrect_t pr;
685     Grect_t gr;
686     HFONT font;
687     TEXTMETRIC tm;
688     SIZE size;
689     RECT r;
690     int x, y, w, h, i;
691 
692     po = pdrawtopix (widget, go);
693     gs.x = 0, gs.y = fs;
694     ps = sdrawtopix (widget, gs);
695     if (!(font = findfont (fn, ps.y))) {
696         Rectangle (GC, po.x, po.y, po.x + 1, po.y + 1);
697         return 0;
698     }
699     setgattr (widget, ap);
700     SETFONT (font);
701     GetTextMetrics (GC, &tm);
702     for (w = h = 0, i = 0; i < n; i++) {
703         if (tlp[i].n)
704             GetTextExtentPoint32 (GC, tlp[i].p, (int) tlp[i].n, &size);
705         else
706             GetTextExtentPoint32 (GC, "M", (int) 1, &size);
707         tlp[i].w = size.cx, tlp[i].h = size.cy;
708         w = max (w, size.cx), h += size.cy;
709     }
710     switch (justs[0]) {
711     case 'l': po.x += w / 2; break;
712     case 'r': po.x -= w / 2; break;
713     }
714     switch (justs[1]) {
715     case 'd': po.y -= h; break;
716     case 'b': po.y -= (h - tm.tmDescent); break;
717     case 'c': po.y -= h / 2; break;
718     }
719     pr.o.x = po.x - w / 2, pr.o.y = po.y;
720     pr.c.x = po.x + w / 2, pr.c.y = po.y + h;
721     gr = rpixtodraw (widget, pr);
722     if (!ISVISIBLE (gr))
723         return 1;
724     for (i = 0; i < n; i++) {
725         switch (tlp[i].j) {
726         case 'l': x = po.x - w / 2; break;
727         case 'n': x = po.x - tlp[i].w / 2; break;
728         case 'r': x = po.x - (tlp[i].w - w / 2); break;
729         }
730         y = po.y + i * tlp[i].h;
731         r.left = x, r.top = y;
732         r.right = x + tlp[i].w, r.bottom = y + tlp[i].h;
733         DrawText (GC, tlp[i].p, (int) tlp[i].n, &r, DT_LEFT | DT_TOP);
734     }
735     return 0;
736 }
737 
GCgettextsize(Gwidget_t * widget,Gtextline_t * tlp,int n,char * fn,double fs,Gsize_t * gsp)738 int GCgettextsize (
739     Gwidget_t *widget, Gtextline_t *tlp, int n, char *fn,
740     double fs, Gsize_t *gsp
741 ) {
742     Gsize_t gs;
743     PIXsize_t ps;
744     HFONT font;
745     int i;
746     SIZE size;
747 
748     gs.x = 0, gs.y = fs;
749     ps = sdrawtopix (widget, gs);
750     if (!(font = findfont (fn, ps.y))) {
751         gsp->x = 1, gsp->y = 1;
752         return 0;
753     }
754     SETFONT (font);
755     for (ps.x = ps.y = 0, i = 0; i < n; i++) {
756         GetTextExtentPoint32 (GC, tlp[i].p, (int) tlp[i].n, &size);
757         ps.x = max (ps.x, size.cx), ps.y += size.cy;
758     }
759     *gsp = spixtodraw (widget, ps);
760     return 0;
761 }
762 
findfont(char * name,int size)763 static HFONT findfont (char *name, int size) {
764     HFONT font;
765     int fi;
766 
767     if (name[0] == '\000')
768         return Gfontp[0].font;
769 
770     sprintf (&Gbufp[0], name, size);
771     for (fi = 0; fi < Gfontn; fi++)
772         if (strcmp (&Gbufp[0], Gfontp[fi].name) == 0 && Gfontp[fi].size == size)
773             return Gfontp[fi].font;
774     font = CreateFont (
775         (int) size, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &Gbufp[0]
776     );
777     if (!font)
778         font = Gfontp[0].font;
779 
780     Gfontp = Marraygrow (Gfontp, (long) (Gfontn + 1) * FONTSIZE);
781     Gfontp[Gfontn].name = strdup (&Gbufp[0]);
782     Gfontp[Gfontn].size = size;
783     Gfontp[Gfontn].font = font;
784     Gfontn++;
785     return font;
786 }
787 
GCcreatebitmap(Gwidget_t * widget,Gbitmap_t * bitmap,Gsize_t s)788 int GCcreatebitmap (Gwidget_t *widget, Gbitmap_t *bitmap, Gsize_t s) {
789     if (!widget) {
790         Gerr (POS, G_ERRNOPARENTWIDGET);
791         return -1;
792     }
793     if (!bitmap) {
794         Gerr (POS, G_ERRNOBITMAP);
795         return -1;
796     }
797     if (!(bitmap->u.bmap.orig = CreateBitmap (
798         (int) s.x, (int) s.y, 1, Gdepth, NULL
799     ))) {
800         Gerr (POS, G_ERRCANNOTCREATEBITMAP);
801         return -1;
802     }
803     bitmap->u.bmap.scaled = 0;
804     bitmap->scale.x = bitmap->scale.y = 1;
805     bitmap->ctype = widget->type;
806     bitmap->canvas = widget - &Gwidgets[0];
807     bitmap->size = s;
808     return 0;
809 }
810 
GCdestroybitmap(Gbitmap_t * bitmap)811 int GCdestroybitmap (Gbitmap_t *bitmap) {
812     if (!bitmap) {
813         Gerr (POS, G_ERRNOBITMAP);
814         return -1;
815     }
816     DeleteObject (bitmap->u.bmap.orig);
817     if (bitmap->u.bmap.scaled)
818         DeleteObject (bitmap->u.bmap.scaled);
819     return 0;
820 }
821 
GCreadbitmap(Gwidget_t * widget,Gbitmap_t * bitmap,FILE * fp)822 int GCreadbitmap (Gwidget_t *widget, Gbitmap_t *bitmap, FILE *fp) {
823     Gsize_t s;
824     HDC gc;
825     char bufp[2048];
826     unsigned int rgb[3];
827     char *s1, *s2;
828     char c;
829     int bufn, bufi, step, x, y, k;
830 
831     if (!widget) {
832         Gerr (POS, G_ERRNOPARENTWIDGET);
833         return -1;
834     }
835     if (!bitmap) {
836         Gerr (POS, G_ERRNOBITMAP);
837         return -1;
838     }
839     step = 0;
840     while (step < 3) {
841 l1:
842         if (!fgets (bufp, 2048, fp)) {
843             Gerr (POS, G_ERRCANNOTREADBITMAP);
844             return -1;
845         }
846         s1 = &bufp[0];
847 l2:
848         for (; *s1 && isspace (*s1); s1++)
849             ;
850         if (!*s1 || *s1 == '#')
851             goto l1;
852         switch (step) {
853         case 0:
854             if (strncmp (s1, "P6", 2) != 0) {
855                 Gerr (POS, G_ERRCANNOTREADBITMAP);
856                 return -1;
857             }
858             step++, s1 += 2;
859             goto l2;
860         case 1:
861             for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++)
862                 ;
863             c = *s2, *s2 = 0;
864             if (s2 == s1 || (s.x = atoi (s1)) <= 0) {
865                 *s2 = c, Gerr (POS, G_ERRCANNOTREADBITMAP);
866                 return -1;
867             }
868             *s2 = c, step++, s1 = s2;
869             goto l2;
870         case 2:
871             for (s2 = s1; *s2 && *s2 >= '0' && *s2 <= '9'; s2++)
872                 ;
873             c = *s2, *s2 = 0;
874             if (s2 == s1 || (s.y = atoi (s1)) <= 0) {
875                 *s2 = c, Gerr (POS, G_ERRCANNOTREADBITMAP);
876                 return -1;
877             }
878             *s2 = c, step++, s1 = s2;
879             goto l2;
880         }
881     }
882     if (!(bitmap->u.bmap.orig = CreateBitmap (
883         (int) s.x, (int) s.y, 1, Gdepth, NULL
884     ))) {
885         Gerr (POS, G_ERRCANNOTCREATEBITMAP);
886         return -1;
887     }
888     gc = CreateCompatibleDC (GC);
889     SelectObject (gc, bitmap->u.bmap.orig);
890     bitmap->u.bmap.scaled = 0;
891     bitmap->scale.x = bitmap->scale.y = 1;
892     bitmap->ctype = widget->type;
893     bitmap->canvas = widget - &Gwidgets[0];
894     bitmap->size = s;
895     bufi = bufn = 0;
896     bufp[bufi] = 0;
897     for (y = 0; y < s.y; y++) {
898         for (x = 0; x < s.x; x++) {
899             for (k = 0; k < 3; k++) {
900                 if (bufi == bufn) {
901                     if ((bufn = fread (bufp, 1, 2047, fp)) == 0) {
902                         if (ferror (fp))
903                             bufn = -1;
904                         DeleteDC (gc);
905                         DeleteObject (bitmap->u.bmap.orig);
906                         Gerr (POS, G_ERRCANNOTCREATEBITMAP);
907                         return -1;
908                     }
909                     bufi = 0;
910                 }
911                 rgb[k] = (unsigned char) bufp[bufi++];
912             }
913             SetPixel (gc, x, y, RGB (rgb[0], rgb[1], rgb[2]));
914         }
915     }
916     DeleteDC (gc);
917     return 0;
918 }
919 
GCwritebitmap(Gbitmap_t * bitmap,FILE * fp)920 int GCwritebitmap (Gbitmap_t *bitmap, FILE *fp) {
921     Gwidget_t *widget;
922     HDC gc;
923     COLORREF color;
924     char bufp[2048];
925     int bufi, x, y, w, h;
926 
927     if (!bitmap) {
928         Gerr (POS, G_ERRNOBITMAP);
929         return -1;
930     }
931     if (
932         bitmap->canvas < 0 || bitmap->canvas >= Gwidgetn ||
933         !Gwidgets[bitmap->canvas].inuse
934     ) {
935         Gerr (POS, G_ERRBADWIDGETID, bitmap->canvas);
936         return -1;
937     }
938     widget = &Gwidgets[bitmap->canvas];
939     if (widget->type != G_CANVASWIDGET && widget->type != G_PCANVASWIDGET) {
940         Gerr (POS, G_ERRNOTACANVAS, bitmap->canvas);
941         return -1;
942     }
943     gc = CreateCompatibleDC (GC);
944     SelectObject (gc, bitmap->u.bmap.orig);
945     fprintf (fp, "P6\n%d %d 255\n", (int) bitmap->size.x, (int) bitmap->size.y);
946     bufi = 0;
947     w = bitmap->size.x;
948     h = bitmap->size.y;
949     for (y = 0; y < h; y++) {
950         for (x = 0; x < w; x++) {
951             color = GetPixel (gc, x, y);
952             bufp[bufi++] = GetRValue (color);
953             bufp[bufi++] = GetGValue (color);
954             bufp[bufi++] = GetBValue (color);
955             if (bufi + 3 >= 2048) {
956                 fwrite (bufp, 1, bufi, fp);
957                 bufi = 0;
958             }
959         }
960     }
961     if (bufi > 0)
962         fwrite (bufp, 1, bufi, fp);
963     DeleteDC (gc);
964     return 0;
965 }
966 
GCbitblt(Gwidget_t * widget,Gpoint_t gp,Grect_t gr,Gbitmap_t * bitmap,char * mode,Ggattr_t * ap)967 int GCbitblt (
968     Gwidget_t *widget, Gpoint_t gp, Grect_t gr, Gbitmap_t *bitmap,
969     char *mode, Ggattr_t *ap
970 ) {
971     PIXrect_t pr, r;
972     PIXpoint_t pp;
973     PIXsize_t s;
974     Gsize_t scale;
975     Gxy_t p;
976     HBITMAP pix;
977     HDC gc;
978     double tvx, tvy, twx, twy;
979 
980     if (gr.o.x > gr.c.x)
981         p.x = gr.o.x, gr.o.x = gr.c.x, gr.c.x = p.x;
982     if (gr.o.y > gr.c.y)
983         p.y = gr.o.y, gr.o.y = gr.c.y, gr.c.y = p.y;
984     if (strcmp (mode, "b2c") == 0) {
985         if (!ISVISIBLE (gr))
986             return 1;
987         tvx = WCU->vsize.x, tvy = WCU->vsize.y;
988         twx = WCU->wrect.c.x - WCU->wrect.o.x;
989         twy = WCU->wrect.c.y - WCU->wrect.o.y;
990         scale.x = tvx / twx, scale.y = tvy / twy;
991         if (scale.x == 1 && scale.y == 1)
992             pix = bitmap->u.bmap.orig;
993         else {
994             if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
995                 scalebitmap (widget, bitmap, scale, TRUE, 1);
996             pix = bitmap->u.bmap.scaled;
997         }
998         pr = rdrawtopix (widget, gr);
999         pp = pdrawtobpix (bitmap, gp);
1000         s.x = pr.c.x - pr.o.x + 1, s.y = pr.c.y - pr.o.y + 1;
1001         r.o.x = pp.x, r.o.y = pp.y - s.y + 1;
1002         r.c.x = r.o.x + s.x - 1, r.c.y = r.o.y + s.y - 1;
1003         if (r.o.x < 0)
1004             pr.o.x -= r.o.x, r.o.x = 0;
1005         if (r.o.y < 0)
1006             pr.o.y -= r.o.y, r.o.y = 0;
1007         if (r.c.x >= bitmap->size.x * scale.x) {
1008             pr.c.x -= (r.c.x + 1 - bitmap->size.x * scale.x);
1009             r.c.x = bitmap->size.x * scale.x - 1;
1010         }
1011         if (r.c.y >= bitmap->size.y * scale.y) {
1012             pr.c.y -= (r.c.y + 1 - bitmap->size.y * scale.y);
1013             r.c.y = bitmap->size.y * scale.y - 1;
1014         }
1015         if (pr.o.x < 0)
1016             r.o.x -= pr.o.x, pr.o.x = 0;
1017         if (pr.o.y < 0)
1018             r.o.y -= pr.o.y, pr.o.y = 0;
1019         setgattr (widget, ap);
1020         gc = CreateCompatibleDC (GC);
1021         SelectObject (gc, pix);
1022         BitBlt (
1023             GC, pr.o.x, pr.o.y, r.c.x - r.o.x + 1, r.c.y - r.o.y + 1,
1024             gc, r.o.x, r.o.y, (WCU->gattr.mode == G_SRC) ? SRCCOPY : SRCINVERT
1025         );
1026         DeleteDC (gc);
1027     } else if (strcmp (mode, "c2b") == 0) {
1028         tvx = WCU->vsize.x, tvy = WCU->vsize.y;
1029         twx = WCU->wrect.c.x - WCU->wrect.o.x;
1030         twy = WCU->wrect.c.y - WCU->wrect.o.y;
1031         scale.x = tvx / twx, scale.y = tvy / twy;
1032         if (scale.x == 1 && scale.y == 1)
1033             pix = bitmap->u.bmap.orig;
1034         else {
1035             if (scale.x != bitmap->scale.x || scale.y != bitmap->scale.y)
1036                 scalebitmap (widget, bitmap, scale, FALSE, 1);
1037             pix = bitmap->u.bmap.scaled;
1038         }
1039         pr = rdrawtobpix (bitmap, gr);
1040         pp = pdrawtopix (widget, gp);
1041         s.x = pr.c.x - pr.o.x + 1, s.y = pr.c.y - pr.o.y + 1;
1042         r.o.x = pp.x, r.o.y = pp.y - s.y + 1;
1043         r.c.x = r.o.x + s.x - 1, r.c.y = r.o.y + s.y - 1;
1044         if (pr.o.x < 0)
1045             r.o.x -= pr.o.x, pr.o.x = 0;
1046         if (pr.o.y < 0)
1047             r.o.y -= pr.o.y, pr.o.y = 0;
1048         if (pr.c.x >= bitmap->size.x * scale.x) {
1049             r.c.x -= (pr.c.x + 1 - bitmap->size.x * scale.x);
1050             pr.c.x = bitmap->size.x * scale.x - 1;
1051         }
1052         if (pr.c.y >= bitmap->size.y * scale.y) {
1053             r.c.y -= (pr.c.y + 1 - bitmap->size.y * scale.y);
1054             pr.c.y = bitmap->size.y * scale.y - 1;
1055         }
1056         if (r.o.x < 0)
1057             pr.o.x -= r.o.x, r.o.x = 0;
1058         if (r.o.y < 0)
1059             pr.o.y -= r.o.y, r.o.y = 0;
1060         setgattr (widget, ap);
1061         gc = CreateCompatibleDC (GC);
1062         SelectObject (gc, pix);
1063         BitBlt (
1064             gc, pr.o.x, pr.o.y, r.c.x - r.o.x + 1, r.c.y - r.o.y + 1,
1065             GC, r.o.x, r.o.y, (WCU->gattr.mode == G_SRC) ? SRCCOPY : SRCINVERT
1066         );
1067         if (pix != bitmap->u.bmap.orig)
1068             scalebitmap (widget, bitmap, scale, TRUE, -1);
1069         DeleteDC (gc);
1070     }
1071     return 0;
1072 }
1073 
scalebitmap(Gwidget_t * widget,Gbitmap_t * bitmap,Gsize_t scale,int copybits,int dir)1074 static int scalebitmap (
1075     Gwidget_t *widget, Gbitmap_t *bitmap, Gsize_t scale,
1076     int copybits, int dir
1077 ) {
1078     Gsize_t nsize, o2n;
1079     HBITMAP opix, spix;
1080     COLORREF color;
1081     HDC gc1, gc2;
1082     int x, y, x2, y2, xp, yp;
1083     double prod, rgb[3], xr2, yr2, xl2, yl2, xf2, yf2, xr, yr, xl, yl;
1084 
1085     if (!copybits) {
1086         if (dir == 1) {
1087             nsize.x = (int) (bitmap->size.x * scale.x);
1088             nsize.y = (int) (bitmap->size.y * scale.y);
1089             if (!(spix = CreateBitmap (
1090                 (int) nsize.x, (int) nsize.y, 1, Gdepth, NULL
1091             ))) {
1092                 Gerr (POS, G_ERRCANNOTCREATEBITMAP);
1093                 return -1;
1094             }
1095             if (bitmap->u.bmap.scaled)
1096                 DeleteObject (bitmap->u.bmap.scaled);
1097             bitmap->u.bmap.scaled = spix;
1098             bitmap->scale = scale;
1099         }
1100         return 0;
1101     }
1102     if (dir == 1) {
1103         nsize.x = (int) (bitmap->size.x * scale.x);
1104         nsize.y = (int) (bitmap->size.y * scale.y);
1105         o2n.x = 1 / scale.x, o2n.y = 1 / scale.y;
1106         if (!(spix = CreateBitmap (
1107             (int) nsize.x, (int) nsize.y, 1, Gdepth, NULL
1108         ))) {
1109             Gerr (POS, G_ERRCANNOTCREATEBITMAP);
1110             return -1;
1111         }
1112         opix = bitmap->u.bmap.orig;
1113     } else {
1114         nsize.x = (int) bitmap->size.x;
1115         nsize.y = (int) bitmap->size.y;
1116         o2n.x = scale.x, o2n.y = scale.y;
1117         spix = bitmap->u.bmap.orig;
1118         opix = bitmap->u.bmap.scaled;
1119     }
1120     gc1 = CreateCompatibleDC (GC);
1121     SelectObject (gc1, opix);
1122     gc2 = CreateCompatibleDC (GC);
1123     SelectObject (gc2, spix);
1124     prod = o2n.x * o2n.y;
1125     y = 0;
1126     yr = o2n.y;
1127     yl = 0;
1128     for (yp = 0; yp < nsize.y; yp++) {
1129         x = 0;
1130         xr = o2n.x;
1131         xl = 0;
1132         for (xp = 0; xp < nsize.x; xp++) {
1133             y2 = y;
1134             yr2 = yr;
1135             yl2 = yl;
1136             rgb[0] = rgb[1] = rgb[2] = 0;
1137             do {
1138                 x2 = x;
1139                 xr2 = xr;
1140                 xl2 = xl;
1141                 yf2 = (yl2 + yr2 > 1) ? 1 - yl2 : yr2, yr2 -= yf2;
1142                 do {
1143                     xf2 = (xl2 + xr2 > 1) ? 1 - xl2 : xr2, xr2 -= xf2;
1144                     color = GetPixel (gc1, x2, y2);
1145                     rgb[0] += (GetRValue (color) * xf2 * yf2 / prod);
1146                     rgb[1] += (GetGValue (color) * xf2 * yf2 / prod);
1147                     rgb[2] += (GetBValue (color) * xf2 * yf2 / prod);
1148                     xl2 += xf2;
1149                     if (xl2 >= 1)
1150                         x2++, xl2 -= 1;
1151                 } while (xr2 > 0);
1152                 xr2 = o2n.x;
1153                 yl2 += yf2;
1154                 if (yl2 >= 1)
1155                     y2++, yl2 -= 1;
1156             } while (yr2 > 0);
1157             yr2 = o2n.y;
1158             SetPixel (gc2, xp, yp, RGB (rgb[0], rgb[1], rgb[2]));
1159             x = x2;
1160             xr = xr2;
1161             xl = xl2;
1162         }
1163         y = y2;
1164         yr = yr2;
1165         yl = yl2;
1166     }
1167     DeleteDC (gc1);
1168     DeleteDC (gc2);
1169     if (dir == 1) {
1170         if (bitmap->u.bmap.scaled)
1171             DeleteObject (bitmap->u.bmap.scaled);
1172         bitmap->u.bmap.scaled = spix;
1173         bitmap->scale = scale;
1174     }
1175     return 0;
1176 }
1177 
GCgetmousecoords(Gwidget_t * widget,Gpoint_t * gpp,int * count)1178 int GCgetmousecoords (Gwidget_t *widget, Gpoint_t *gpp, int *count) {
1179     PIXpoint_t pp;
1180     POINT p;
1181     int n1, n2, n3;
1182 
1183     GetCursorPos (&p);
1184     ScreenToClient (widget->w, &p);
1185     pp.x = p.x, pp.y = p.y;
1186     *gpp = ppixtodraw (widget, pp);
1187     n1 = GetAsyncKeyState (VK_LBUTTON);
1188     n2 = GetAsyncKeyState (VK_MBUTTON);
1189     n3 = GetAsyncKeyState (VK_RBUTTON);
1190     *count = (n1 < 0 ? 1 : 0) + (n2 < 0 ? 1 : 0) + (n3 < 0 ? 1 : 0);
1191     return 0;
1192 }
1193 
setgattr(Gwidget_t * widget,Ggattr_t * ap)1194 static void setgattr (Gwidget_t *widget, Ggattr_t *ap) {
1195     HBRUSH brush, pbrush;
1196     HPEN pen, ppen;
1197     PALETTEENTRY *colorp;
1198     long color, mode, style, width, flag, pati;
1199     double intens;
1200 
1201     if (!(ap->flags & G_GATTRCOLOR))
1202         ap->color = WCU->defgattr.color;
1203     if (!(ap->flags & G_GATTRWIDTH))
1204         ap->width = WCU->defgattr.width;
1205     if (!(ap->flags & G_GATTRMODE))
1206         ap->mode = WCU->defgattr.mode;
1207     if (!(ap->flags & G_GATTRFILL))
1208         ap->fill = WCU->defgattr.fill;
1209     if (!(ap->flags & G_GATTRSTYLE))
1210         ap->style = WCU->defgattr.style;
1211     flag = FALSE;
1212     mode = ap->mode;
1213     if (mode != WCU->gattr.mode) {
1214         WCU->gattr.mode = mode;
1215         SetROP2 (GC, (int) mode);
1216     }
1217     WCU->gattr.fill = ap->fill;
1218     color = ap->color;
1219     if (color >= G_MAXCOLORS || !(WCU->colors[color].inuse))
1220         color = 1;
1221     if (color != WCU->gattr.color)
1222         WCU->gattr.color = color, flag = TRUE;
1223     width = ap->width;
1224     if (width != WCU->gattr.width)
1225         WCU->gattr.width = width, flag = TRUE;
1226     style = ap->style;
1227     if (style != WCU->gattr.style)
1228         WCU->gattr.style = style, flag = TRUE;
1229 
1230     if (!flag)
1231         return;
1232     WCU->gattr.color = color;
1233     if (Gdepth == 1) {
1234         colorp = &WCU->colors[color].color;
1235         intens = (
1236             0.3 * colorp->peBlue + 0.59 * colorp->peRed +
1237             0.11 * colorp->peGreen
1238         ) / 255.0;
1239         pati = (intens <= 0.0625) ? 16 : -16.0 * (log (intens) / 2.7725887222);
1240         brush = WCU->grays[pati];
1241     } else
1242         brush = CreateSolidBrush (PALETTEINDEX (WCU->gattr.color));
1243     pbrush = SelectObject (GC, brush);
1244     if (Gdepth != 1)
1245         DeleteObject (pbrush);
1246     pen = CreatePen (
1247         (int) gstyles[WCU->gattr.style], WCU->gattr.width,
1248         PALETTEINDEX (WCU->gattr.color)
1249     );
1250     ppen = SelectObject (GC, pen);
1251     DeleteObject (ppen);
1252     SetTextColor (GC, PALETTEINDEX (WCU->gattr.color));
1253 }
1254 
rdrawtopix(Gwidget_t * widget,Grect_t gr)1255 static PIXrect_t rdrawtopix (Gwidget_t *widget, Grect_t gr) {
1256     PIXrect_t pr;
1257     double tvx, tvy, twx, twy;
1258 
1259     tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
1260     twx = WCU->wrect.c.x - WCU->wrect.o.x;
1261     twy = WCU->wrect.c.y - WCU->wrect.o.y;
1262     pr.o.x = tvx * (gr.o.x - WCU->wrect.o.x) / twx + 0.5;
1263     pr.o.y = tvy * (1.0  - (gr.c.y - WCU->wrect.o.y) / twy) + 0.5;
1264     pr.c.x = tvx * (gr.c.x - WCU->wrect.o.x) / twx + 0.5;
1265     pr.c.y = tvy * (1.0  - (gr.o.y - WCU->wrect.o.y) / twy) + 0.5;
1266     return pr;
1267 }
1268 
pdrawtopix(Gwidget_t * widget,Gpoint_t gp)1269 static PIXpoint_t pdrawtopix (Gwidget_t *widget, Gpoint_t gp) {
1270     PIXpoint_t pp;
1271     double tvx, tvy, twx, twy;
1272 
1273     tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
1274     twx = WCU->wrect.c.x - WCU->wrect.o.x;
1275     twy = WCU->wrect.c.y - WCU->wrect.o.y;
1276     pp.x = tvx * (gp.x - WCU->wrect.o.x) / twx + 0.5;
1277     pp.y = tvy * (1.0  - (gp.y - WCU->wrect.o.y) / twy) + 0.5;
1278     return pp;
1279 }
1280 
sdrawtopix(Gwidget_t * widget,Gsize_t gs)1281 static PIXsize_t sdrawtopix (Gwidget_t *widget, Gsize_t gs) {
1282     PIXsize_t ps;
1283     double tvx, tvy, twx, twy;
1284 
1285     tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
1286     twx = WCU->wrect.c.x - WCU->wrect.o.x;
1287     twy = WCU->wrect.c.y - WCU->wrect.o.y;
1288     ps.x = tvx * (gs.x - 1) / twx + 1.5;
1289     ps.y = tvy * (gs.y - 1) / twy + 1.5;
1290     return ps;
1291 }
1292 
ppixtodraw(Gwidget_t * widget,PIXpoint_t pp)1293 Gpoint_t ppixtodraw (Gwidget_t *widget, PIXpoint_t pp) {
1294     Gpoint_t gp;
1295     double tvx, tvy, twx, twy;
1296 
1297     tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
1298     twx = WCU->wrect.c.x - WCU->wrect.o.x;
1299     twy = WCU->wrect.c.y - WCU->wrect.o.y;
1300     gp.x = (pp.x / tvx) * twx + WCU->wrect.o.x;
1301     gp.y = (1.0 - pp.y / tvy) * twy + WCU->wrect.o.y;
1302     return gp;
1303 }
1304 
spixtodraw(Gwidget_t * widget,PIXsize_t ps)1305 static Gsize_t spixtodraw (Gwidget_t *widget, PIXsize_t ps) {
1306     Gsize_t gs;
1307     double tvx, tvy, twx, twy;
1308 
1309     tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
1310     twx = WCU->wrect.c.x - WCU->wrect.o.x;
1311     twy = WCU->wrect.c.y - WCU->wrect.o.y;
1312     gs.x = ((ps.x - 1) / tvx) * twx + 1;
1313     gs.y = ((ps.y - 1) / tvy) * twy + 1;
1314     return gs;
1315 }
1316 
rpixtodraw(Gwidget_t * widget,PIXrect_t pr)1317 static Grect_t rpixtodraw (Gwidget_t *widget, PIXrect_t pr) {
1318     Grect_t gr;
1319     double tvx, tvy, twx, twy, n;
1320 
1321     tvx = WCU->vsize.x - 1, tvy = WCU->vsize.y - 1;
1322     twx = WCU->wrect.c.x - WCU->wrect.o.x;
1323     twy = WCU->wrect.c.y - WCU->wrect.o.y;
1324     gr.o.x = (pr.o.x / tvx) * twx + WCU->wrect.o.x;
1325     gr.o.y = (1.0 - pr.c.y / tvy) * twy + WCU->wrect.o.y;
1326     gr.c.x = (pr.c.x / tvx) * twx + WCU->wrect.o.x;
1327     gr.c.y = (1.0 - pr.o.y / tvy) * twy + WCU->wrect.o.y;
1328     if (gr.o.x > gr.c.x)
1329         n = gr.o.x, gr.o.x = gr.c.x, gr.c.x = n;
1330     if (gr.o.y > gr.c.y)
1331         n = gr.o.y, gr.o.y = gr.c.y, gr.c.y = n;
1332     return gr;
1333 }
1334 
rdrawtobpix(Gbitmap_t * bitmap,Grect_t gr)1335 static PIXrect_t rdrawtobpix (Gbitmap_t *bitmap, Grect_t gr) {
1336     PIXrect_t pr;
1337     double tvy;
1338 
1339     tvy = (int) ((bitmap->size.y - 1) * bitmap->scale.y);
1340     pr.o.x = gr.o.x + 0.5;
1341     pr.o.y = tvy - gr.c.y + 0.5;
1342     pr.c.x = gr.c.x + 0.5;
1343     pr.c.y = tvy - gr.o.y + 0.5;
1344     return pr;
1345 }
1346 
pdrawtobpix(Gbitmap_t * bitmap,Gpoint_t gp)1347 static PIXpoint_t pdrawtobpix (Gbitmap_t *bitmap, Gpoint_t gp) {
1348     PIXpoint_t pp;
1349     double tvy;
1350 
1351     tvy = (int) ((bitmap->size.y - 1) * bitmap->scale.y);
1352     pp.x = gp.x + 0.5;
1353     pp.y = tvy - gp.y + 0.5;
1354     return pp;
1355 }
1356 
Gadjustclip(Gwidget_t * widget)1357 void Gadjustclip (Gwidget_t *widget) {
1358     Gwidget_t *parent;
1359     PIXrect_t pr;
1360     RECT r1, r2, r3;
1361 
1362     parent = &Gwidgets[widget->pwi];
1363     GetWindowRect (widget->w, &r1);
1364     GetClientRect (parent->w, &r2);
1365     GetWindowRect (parent->w, &r3);
1366     pr.o.x = max (0, -(r1.left - r3.left));
1367     pr.o.y = max (0, -(r1.top - r3.top));
1368     pr.c.x = min (r1.right - r1.left, pr.o.x + r2.right - r2.left);
1369     pr.c.y = min (r1.bottom - r1.top, pr.o.y + r2.bottom - r2.top);
1370     pr.c.x = max (pr.o.x, pr.c.x);
1371     pr.c.y = max (pr.o.y, pr.c.y);
1372     WCU->clip = rpixtodraw (widget, pr);
1373 }
1374