msi: Don't set the shortcut's working directory if it's NULL.
[wine] / dlls / wineps.drv / graphics.c
1 /*
2  *      PostScript driver graphics functions
3  *
4  *      Copyright 1998  Huw D M Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <math.h>
26 #if defined(HAVE_FLOAT_H)
27 # include <float.h>
28 #endif
29 #if !defined(PI)
30 # define PI M_PI
31 #endif
32 #include "psdrv.h"
33 #include "wine/debug.h"
34 #include "winspool.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
37
38 /***********************************************************************
39  *           PSDRV_XWStoDS
40  *
41  * Performs a world-to-viewport transformation on the specified width.
42  */
43 INT PSDRV_XWStoDS( PSDRV_PDEVICE *physDev, INT width )
44 {
45     POINT pt[2];
46
47     pt[0].x = 0;
48     pt[0].y = 0;
49     pt[1].x = width;
50     pt[1].y = 0;
51     LPtoDP( physDev->hdc, pt, 2 );
52     return pt[1].x - pt[0].x;
53 }
54
55 /***********************************************************************
56  *           PSDRV_YWStoDS
57  *
58  * Performs a world-to-viewport transformation on the specified height.
59  */
60 INT PSDRV_YWStoDS( PSDRV_PDEVICE *physDev, INT height )
61 {
62     POINT pt[2];
63
64     pt[0].x = 0;
65     pt[0].y = 0;
66     pt[1].x = 0;
67     pt[1].y = height;
68     LPtoDP( physDev->hdc, pt, 2 );
69     return pt[1].y - pt[0].y;
70 }
71
72 /***********************************************************************
73  *           PSDRV_DrawLine
74  */
75 static void PSDRV_DrawLine( PSDRV_PDEVICE *physDev )
76 {
77     if(physDev->pathdepth)
78         return;
79
80     if (physDev->pen.style == PS_NULL)
81         PSDRV_WriteNewPath(physDev);
82     else
83         PSDRV_WriteStroke(physDev);
84 }
85
86 /***********************************************************************
87  *           PSDRV_LineTo
88  */
89 BOOL PSDRV_LineTo(PSDRV_PDEVICE *physDev, INT x, INT y)
90 {
91     POINT pt[2];
92
93     TRACE("%d %d\n", x, y);
94
95     GetCurrentPositionEx( physDev->hdc, pt );
96     pt[1].x = x;
97     pt[1].y = y;
98     LPtoDP( physDev->hdc, pt, 2 );
99
100     PSDRV_SetPen(physDev);
101
102     PSDRV_SetClip(physDev);
103     PSDRV_WriteMoveTo(physDev, pt[0].x, pt[0].y );
104     PSDRV_WriteLineTo(physDev, pt[1].x, pt[1].y );
105     PSDRV_DrawLine(physDev);
106     PSDRV_ResetClip(physDev);
107
108     return TRUE;
109 }
110
111
112 /***********************************************************************
113  *           PSDRV_Rectangle
114  */
115 BOOL PSDRV_Rectangle( PSDRV_PDEVICE *physDev, INT left, INT top, INT right, INT bottom )
116 {
117     RECT rect;
118
119     TRACE("%d %d - %d %d\n", left, top, right, bottom);
120
121     rect.left = left;
122     rect.top = top;
123     rect.right = right;
124     rect.bottom = bottom;
125     LPtoDP( physDev->hdc, (POINT *)&rect, 2 );
126
127     /* Windows does something truly hacky here.  If we're in passthrough mode
128        and our rop is R2_NOP, then we output the string below.  This is used in
129        Office 2k when inserting eps files */
130     if(physDev->job.in_passthrough && !physDev->job.had_passthrough_rect && GetROP2(physDev->hdc) == R2_NOP) {
131       char buf[256];
132       sprintf(buf, "N %ld %ld %ld %ld B\n", rect.right - rect.left, rect.bottom - rect.top, rect.left, rect.top);
133       WriteSpool16(physDev->job.hJob, buf, strlen(buf));
134       physDev->job.had_passthrough_rect = TRUE;
135       return TRUE;
136     }
137
138     PSDRV_SetPen(physDev);
139
140     PSDRV_SetClip(physDev);
141     PSDRV_WriteRectangle(physDev, rect.left, rect.top, rect.right - rect.left,
142                          rect.bottom - rect.top );
143     PSDRV_Brush(physDev,0);
144     PSDRV_DrawLine(physDev);
145     PSDRV_ResetClip(physDev);
146     return TRUE;
147 }
148
149
150 /***********************************************************************
151  *           PSDRV_RoundRect
152  */
153 BOOL PSDRV_RoundRect( PSDRV_PDEVICE *physDev, INT left, INT top, INT right,
154                       INT bottom, INT ell_width, INT ell_height )
155 {
156     RECT rect[2];
157
158     rect[0].left   = left;
159     rect[0].top    = top;
160     rect[0].right  = right;
161     rect[0].bottom = bottom;
162     rect[1].left   = 0;
163     rect[1].top    = 0;
164     rect[1].right  = ell_width;
165     rect[1].bottom = ell_height;
166     LPtoDP( physDev->hdc, (POINT *)rect, 4 );
167
168     left   = rect[0].left;
169     top    = rect[0].top;
170     right  = rect[0].right;
171     bottom = rect[0].bottom;
172     if (left > right) { INT tmp = left; left = right; right = tmp; }
173     if (top > bottom) { INT tmp = top; top = bottom; bottom = tmp; }
174
175     ell_width  = rect[1].right - rect[1].left;
176     ell_height = rect[1].bottom - rect[1].top;
177     if (ell_width > right - left) ell_width = right - left;
178     if (ell_height > bottom - top) ell_height = bottom - top;
179
180     PSDRV_WriteSpool(physDev, "%RoundRect\n",11);
181     PSDRV_SetPen(physDev);
182
183     PSDRV_SetClip(physDev);
184     PSDRV_WriteMoveTo( physDev, left, top + ell_height/2 );
185     PSDRV_WriteArc( physDev, left + ell_width/2, top + ell_height/2, ell_width,
186                     ell_height, 90.0, 180.0);
187     PSDRV_WriteLineTo( physDev, right - ell_width/2, top );
188     PSDRV_WriteArc( physDev, right - ell_width/2, top + ell_height/2, ell_width,
189                     ell_height, 0.0, 90.0);
190     PSDRV_WriteLineTo( physDev, right, bottom - ell_height/2 );
191     PSDRV_WriteArc( physDev, right - ell_width/2, bottom - ell_height/2, ell_width,
192                     ell_height, -90.0, 0.0);
193     PSDRV_WriteLineTo( physDev, right - ell_width/2, bottom);
194     PSDRV_WriteArc( physDev, left + ell_width/2, bottom - ell_height/2, ell_width,
195                     ell_height, 180.0, -90.0);
196     PSDRV_WriteClosePath( physDev );
197
198     PSDRV_Brush(physDev,0);
199     PSDRV_DrawLine(physDev);
200     PSDRV_ResetClip(physDev);
201     return TRUE;
202 }
203
204 /***********************************************************************
205  *           PSDRV_DrawArc
206  *
207  * Does the work of Arc, Chord and Pie. lines is 0, 1 or 2 respectively.
208  */
209 static BOOL PSDRV_DrawArc( PSDRV_PDEVICE *physDev, INT left, INT top,
210                            INT right, INT bottom, INT xstart, INT ystart,
211                            INT xend, INT yend, int lines )
212 {
213     INT x, y, h, w;
214     double start_angle, end_angle, ratio;
215     RECT rect;
216     POINT start, end;
217
218     rect.left = left;
219     rect.top = top;
220     rect.right = right;
221     rect.bottom = bottom;
222     LPtoDP( physDev->hdc, (POINT *)&rect, 2 );
223     start.x = xstart;
224     start.y = ystart;
225     end.x = xend;
226     end.y = yend;
227     LPtoDP( physDev->hdc, &start, 1 );
228     LPtoDP( physDev->hdc, &end, 1 );
229
230     x = (rect.left + rect.right) / 2;
231     y = (rect.top + rect.bottom) / 2;
232     w = rect.right - rect.left;
233     h = rect.bottom - rect.top;
234
235     if(w < 0) w = -w;
236     if(h < 0) h = -h;
237     ratio = ((double)w)/h;
238
239     /* angle is the angle after the rectangle is transformed to a square and is
240        measured anticlockwise from the +ve x-axis */
241
242     start_angle = atan2((double)(y - start.y) * ratio, (double)(start.x - x));
243     end_angle = atan2((double)(y - end.y) * ratio, (double)(end.x - x));
244
245     start_angle *= 180.0 / PI;
246     end_angle *= 180.0 / PI;
247
248     PSDRV_WriteSpool(physDev,"%DrawArc\n", 9);
249     PSDRV_SetPen(physDev);
250
251     PSDRV_SetClip(physDev);
252     if(lines == 2) /* pie */
253         PSDRV_WriteMoveTo(physDev, x, y);
254     else
255         PSDRV_WriteNewPath( physDev );
256
257     PSDRV_WriteArc(physDev, x, y, w, h, start_angle, end_angle);
258     if(lines == 1 || lines == 2) { /* chord or pie */
259         PSDRV_WriteClosePath(physDev);
260         PSDRV_Brush(physDev,0);
261     }
262     PSDRV_DrawLine(physDev);
263     PSDRV_ResetClip(physDev);
264
265     return TRUE;
266 }
267
268
269 /***********************************************************************
270  *           PSDRV_Arc
271  */
272 BOOL PSDRV_Arc( PSDRV_PDEVICE *physDev, INT left, INT top, INT right, INT bottom,
273                 INT xstart, INT ystart, INT xend, INT yend )
274 {
275     return PSDRV_DrawArc( physDev, left, top, right, bottom, xstart, ystart, xend, yend, 0 );
276 }
277
278 /***********************************************************************
279  *           PSDRV_Chord
280  */
281 BOOL PSDRV_Chord( PSDRV_PDEVICE *physDev, INT left, INT top, INT right, INT bottom,
282                   INT xstart, INT ystart, INT xend, INT yend )
283 {
284     return PSDRV_DrawArc( physDev, left, top, right, bottom, xstart, ystart, xend, yend, 1 );
285 }
286
287
288 /***********************************************************************
289  *           PSDRV_Pie
290  */
291 BOOL PSDRV_Pie( PSDRV_PDEVICE *physDev, INT left, INT top, INT right, INT bottom,
292                 INT xstart, INT ystart, INT xend, INT yend )
293 {
294     return PSDRV_DrawArc( physDev, left, top, right, bottom, xstart, ystart, xend, yend, 2 );
295 }
296
297
298 /***********************************************************************
299  *           PSDRV_Ellipse
300  */
301 BOOL PSDRV_Ellipse( PSDRV_PDEVICE *physDev, INT left, INT top, INT right, INT bottom)
302 {
303     INT x, y, w, h;
304     RECT rect;
305
306     TRACE("%d %d - %d %d\n", left, top, right, bottom);
307
308     rect.left   = left;
309     rect.top    = top;
310     rect.right  = right;
311     rect.bottom = bottom;
312     LPtoDP( physDev->hdc, (POINT *)&rect, 2 );
313
314     x = (rect.left + rect.right) / 2;
315     y = (rect.top + rect.bottom) / 2;
316     w = rect.right - rect.left;
317     h = rect.bottom - rect.top;
318
319     PSDRV_WriteSpool(physDev, "%Ellipse\n", 9);
320     PSDRV_SetPen(physDev);
321
322     PSDRV_SetClip(physDev);
323     PSDRV_WriteNewPath(physDev);
324     PSDRV_WriteArc(physDev, x, y, w, h, 0.0, 360.0);
325     PSDRV_WriteClosePath(physDev);
326     PSDRV_Brush(physDev,0);
327     PSDRV_DrawLine(physDev);
328     PSDRV_ResetClip(physDev);
329     return TRUE;
330 }
331
332
333 /***********************************************************************
334  *           PSDRV_PolyPolyline
335  */
336 BOOL PSDRV_PolyPolyline( PSDRV_PDEVICE *physDev, const POINT* pts, const DWORD* counts,
337                          DWORD polylines )
338 {
339     DWORD polyline, line, total;
340     POINT *dev_pts, *pt;
341
342     TRACE("\n");
343
344     for (polyline = total = 0; polyline < polylines; polyline++) total += counts[polyline];
345     if (!(dev_pts = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*dev_pts) ))) return FALSE;
346     memcpy( dev_pts, pts, total * sizeof(*dev_pts) );
347     LPtoDP( physDev->hdc, dev_pts, total );
348
349     pt = dev_pts;
350
351     PSDRV_WriteSpool(physDev, "%PolyPolyline\n",14);
352     PSDRV_SetPen(physDev);
353     PSDRV_SetClip(physDev);
354
355     for(polyline = 0; polyline < polylines; polyline++) {
356         PSDRV_WriteMoveTo(physDev, pt->x, pt->y);
357         pt++;
358         for(line = 1; line < counts[polyline]; line++, pt++)
359             PSDRV_WriteLineTo(physDev, pt->x, pt->y);
360     }
361     HeapFree( GetProcessHeap(), 0, dev_pts );
362
363     PSDRV_DrawLine(physDev);
364     PSDRV_ResetClip(physDev);
365     return TRUE;
366 }
367
368
369 /***********************************************************************
370  *           PSDRV_Polyline
371  */
372 BOOL PSDRV_Polyline( PSDRV_PDEVICE *physDev, const POINT* pt, INT count )
373 {
374     return PSDRV_PolyPolyline( physDev, pt, (LPDWORD) &count, 1 );
375 }
376
377
378 /***********************************************************************
379  *           PSDRV_PolyPolygon
380  */
381 BOOL PSDRV_PolyPolygon( PSDRV_PDEVICE *physDev, const POINT* pts, const INT* counts,
382                         UINT polygons )
383 {
384     DWORD polygon, total;
385     INT line;
386     POINT *dev_pts, *pt;
387
388     TRACE("\n");
389
390     for (polygon = total = 0; polygon < polygons; polygon++) total += counts[polygon];
391     if (!(dev_pts = HeapAlloc( GetProcessHeap(), 0, total * sizeof(*dev_pts) ))) return FALSE;
392     memcpy( dev_pts, pts, total * sizeof(*dev_pts) );
393     LPtoDP( physDev->hdc, dev_pts, total );
394
395     pt = dev_pts;
396
397     PSDRV_WriteSpool(physDev, "%PolyPolygon\n",13);
398     PSDRV_SetPen(physDev);
399     PSDRV_SetClip(physDev);
400
401     for(polygon = 0; polygon < polygons; polygon++) {
402         PSDRV_WriteMoveTo(physDev, pt->x, pt->y);
403         pt++;
404         for(line = 1; line < counts[polygon]; line++, pt++)
405             PSDRV_WriteLineTo(physDev, pt->x, pt->y);
406         PSDRV_WriteClosePath(physDev);
407     }
408     HeapFree( GetProcessHeap(), 0, dev_pts );
409
410     if(GetPolyFillMode( physDev->hdc ) == ALTERNATE)
411         PSDRV_Brush(physDev, 1);
412     else /* WINDING */
413         PSDRV_Brush(physDev, 0);
414
415     PSDRV_DrawLine(physDev);
416     PSDRV_ResetClip(physDev);
417     return TRUE;
418 }
419
420
421 /***********************************************************************
422  *           PSDRV_Polygon
423  */
424 BOOL PSDRV_Polygon( PSDRV_PDEVICE *physDev, const POINT* pt, INT count )
425 {
426      return PSDRV_PolyPolygon( physDev, pt, &count, 1 );
427 }
428
429
430 /***********************************************************************
431  *           PSDRV_SetPixel
432  */
433 COLORREF PSDRV_SetPixel( PSDRV_PDEVICE *physDev, INT x, INT y, COLORREF color )
434 {
435     PSCOLOR pscolor;
436     POINT pt;
437
438     pt.x = x;
439     pt.y = y;
440     LPtoDP( physDev->hdc, &pt, 1 );
441
442     PSDRV_SetClip(physDev);
443     /* we bracket the setcolor in gsave/grestore so that we don't trash
444        the current pen colour */
445     PSDRV_WriteGSave(physDev);
446     PSDRV_WriteRectangle( physDev, pt.x, pt.y, 0, 0 );
447     PSDRV_CreateColor( physDev, &pscolor, color );
448     PSDRV_WriteSetColor( physDev, &pscolor );
449     PSDRV_WriteFill( physDev );
450     PSDRV_WriteGRestore(physDev);
451     PSDRV_ResetClip(physDev);
452     return color;
453 }
454
455 /***********************************************************************
456  *           PSDRV_PaintRgn
457  */
458 BOOL PSDRV_PaintRgn( PSDRV_PDEVICE *physDev, HRGN hrgn )
459 {
460     
461     RGNDATA *rgndata = NULL;
462     RECT *pRect;
463     DWORD size, i;
464
465     TRACE("hdc=%p\n", physDev->hdc);
466
467     size = GetRegionData(hrgn, 0, NULL);
468     rgndata = HeapAlloc( GetProcessHeap(), 0, size );
469     if(!rgndata) {
470         ERR("Can't allocate buffer\n");
471         return FALSE;
472     }
473     
474     GetRegionData(hrgn, size, rgndata);
475     if (rgndata->rdh.nCount == 0)
476         goto end;
477
478     LPtoDP(physDev->hdc, (POINT*)rgndata->Buffer, rgndata->rdh.nCount * 2);
479
480     PSDRV_SetClip(physDev);
481     PSDRV_WriteNewPath(physDev);
482     for(i = 0, pRect = (RECT*)rgndata->Buffer; i < rgndata->rdh.nCount; i++, pRect++)
483         PSDRV_WriteRectangle(physDev, pRect->left, pRect->top, pRect->right - pRect->left, pRect->bottom - pRect->top);
484
485     PSDRV_Brush(physDev, 0);
486     PSDRV_ResetClip(physDev);
487
488  end:
489     HeapFree(GetProcessHeap(), 0, rgndata);
490     return TRUE;
491 }