~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/gdiplus/graphics.c

Version: ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Copyright (C) 2007 Google (Evan Stade)
  3  *
  4  * This library is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2.1 of the License, or (at your option) any later version.
  8  *
  9  * This library is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with this library; if not, write to the Free Software
 16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 17  */
 18 
 19 #include <stdarg.h>
 20 #include <math.h>
 21 #include <limits.h>
 22 
 23 #include "windef.h"
 24 #include "winbase.h"
 25 #include "winuser.h"
 26 #include "wingdi.h"
 27 #include "wine/unicode.h"
 28 
 29 #define COBJMACROS
 30 #include "objbase.h"
 31 #include "ocidl.h"
 32 #include "olectl.h"
 33 #include "ole2.h"
 34 
 35 #include "winreg.h"
 36 #include "shlwapi.h"
 37 
 38 #include "gdiplus.h"
 39 #include "gdiplus_private.h"
 40 #include "wine/debug.h"
 41 
 42 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
 43 
 44 /* looks-right constants */
 45 #define ANCHOR_WIDTH (2.0)
 46 #define MAX_ITERS (50)
 47 
 48 /* Converts angle (in degrees) to x/y coordinates */
 49 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
 50 {
 51     REAL radAngle, hypotenuse;
 52 
 53     radAngle = deg2rad(angle);
 54     hypotenuse = 50.0; /* arbitrary */
 55 
 56     *x = x_0 + cos(radAngle) * hypotenuse;
 57     *y = y_0 + sin(radAngle) * hypotenuse;
 58 }
 59 
 60 /* Converts from gdiplus path point type to gdi path point type. */
 61 static BYTE convert_path_point_type(BYTE type)
 62 {
 63     BYTE ret;
 64 
 65     switch(type & PathPointTypePathTypeMask){
 66         case PathPointTypeBezier:
 67             ret = PT_BEZIERTO;
 68             break;
 69         case PathPointTypeLine:
 70             ret = PT_LINETO;
 71             break;
 72         case PathPointTypeStart:
 73             ret = PT_MOVETO;
 74             break;
 75         default:
 76             ERR("Bad point type\n");
 77             return 0;
 78     }
 79 
 80     if(type & PathPointTypeCloseSubpath)
 81         ret |= PT_CLOSEFIGURE;
 82 
 83     return ret;
 84 }
 85 
 86 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
 87 {
 88     HPEN gdipen;
 89     REAL width;
 90     INT save_state = SaveDC(graphics->hdc), i, numdashes;
 91     GpPointF pt[2];
 92     DWORD dash_array[MAX_DASHLEN];
 93 
 94     EndPath(graphics->hdc);
 95 
 96     if(pen->unit == UnitPixel){
 97         width = pen->width;
 98     }
 99     else{
100         /* Get an estimate for the amount the pen width is affected by the world
101          * transform. (This is similar to what some of the wine drivers do.) */
102         pt[0].X = 0.0;
103         pt[0].Y = 0.0;
104         pt[1].X = 1.0;
105         pt[1].Y = 1.0;
106         GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
107         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
108                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
109 
110         width *= pen->width * convert_unit(graphics->hdc,
111                               pen->unit == UnitWorld ? graphics->unit : pen->unit);
112     }
113 
114     if(pen->dash == DashStyleCustom){
115         numdashes = min(pen->numdashes, MAX_DASHLEN);
116 
117         TRACE("dashes are: ");
118         for(i = 0; i < numdashes; i++){
119             dash_array[i] = roundr(width * pen->dashes[i]);
120             TRACE("%d, ", dash_array[i]);
121         }
122         TRACE("\n and the pen style is %x\n", pen->style);
123 
124         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
125                               numdashes, dash_array);
126     }
127     else
128         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
129 
130     SelectObject(graphics->hdc, gdipen);
131 
132     return save_state;
133 }
134 
135 static void restore_dc(GpGraphics *graphics, INT state)
136 {
137     DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
138     RestoreDC(graphics->hdc, state);
139 }
140 
141 /* This helper applies all the changes that the points listed in ptf need in
142  * order to be drawn on the device context.  In the end, this should include at
143  * least:
144  *  -scaling by page unit
145  *  -applying world transformation
146  *  -converting from float to int
147  * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
148  * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
149  * gdi to draw, and these functions would irreparably mess with line widths.
150  */
151 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
152     GpPointF *ptf, INT count)
153 {
154     REAL unitscale;
155     GpMatrix *matrix;
156     int i;
157 
158     unitscale = convert_unit(graphics->hdc, graphics->unit);
159 
160     /* apply page scale */
161     if(graphics->unit != UnitDisplay)
162         unitscale *= graphics->scale;
163 
164     GdipCloneMatrix(graphics->worldtrans, &matrix);
165     GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
166     GdipTransformMatrixPoints(matrix, ptf, count);
167     GdipDeleteMatrix(matrix);
168 
169     for(i = 0; i < count; i++){
170         pti[i].x = roundr(ptf[i].X);
171         pti[i].y = roundr(ptf[i].Y);
172     }
173 }
174 
175 /* GdipDrawPie/GdipFillPie helper function */
176 static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
177     REAL height, REAL startAngle, REAL sweepAngle)
178 {
179     GpPointF ptf[4];
180     POINT pti[4];
181 
182     ptf[0].X = x;
183     ptf[0].Y = y;
184     ptf[1].X = x + width;
185     ptf[1].Y = y + height;
186 
187     deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
188     deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
189 
190     transform_and_round_points(graphics, pti, ptf, 4);
191 
192     Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
193         pti[2].y, pti[3].x, pti[3].y);
194 }
195 
196 /* Draws the linecap the specified color and size on the hdc.  The linecap is in
197  * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
198  * should not be called on an hdc that has a path you care about. */
199 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
200     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
201 {
202     HGDIOBJ oldbrush = NULL, oldpen = NULL;
203     GpMatrix *matrix = NULL;
204     HBRUSH brush = NULL;
205     HPEN pen = NULL;
206     PointF ptf[4], *custptf = NULL;
207     POINT pt[4], *custpt = NULL;
208     BYTE *tp = NULL;
209     REAL theta, dsmall, dbig, dx, dy = 0.0;
210     INT i, count;
211     LOGBRUSH lb;
212     BOOL customstroke;
213 
214     if((x1 == x2) && (y1 == y2))
215         return;
216 
217     theta = gdiplus_atan2(y2 - y1, x2 - x1);
218 
219     customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
220     if(!customstroke){
221         brush = CreateSolidBrush(color);
222         lb.lbStyle = BS_SOLID;
223         lb.lbColor = color;
224         lb.lbHatch = 0;
225         pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
226                            PS_JOIN_MITER, 1, &lb, 0,
227                            NULL);
228         oldbrush = SelectObject(graphics->hdc, brush);
229         oldpen = SelectObject(graphics->hdc, pen);
230     }
231 
232     switch(cap){
233         case LineCapFlat:
234             break;
235         case LineCapSquare:
236         case LineCapSquareAnchor:
237         case LineCapDiamondAnchor:
238             size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
239             if(cap == LineCapDiamondAnchor){
240                 dsmall = cos(theta + M_PI_2) * size;
241                 dbig = sin(theta + M_PI_2) * size;
242             }
243             else{
244                 dsmall = cos(theta + M_PI_4) * size;
245                 dbig = sin(theta + M_PI_4) * size;
246             }
247 
248             ptf[0].X = x2 - dsmall;
249             ptf[1].X = x2 + dbig;
250 
251             ptf[0].Y = y2 - dbig;
252             ptf[3].Y = y2 + dsmall;
253 
254             ptf[1].Y = y2 - dsmall;
255             ptf[2].Y = y2 + dbig;
256 
257             ptf[3].X = x2 - dbig;
258             ptf[2].X = x2 + dsmall;
259 
260             transform_and_round_points(graphics, pt, ptf, 4);
261             Polygon(graphics->hdc, pt, 4);
262 
263             break;
264         case LineCapArrowAnchor:
265             size = size * 4.0 / sqrt(3.0);
266 
267             dx = cos(M_PI / 6.0 + theta) * size;
268             dy = sin(M_PI / 6.0 + theta) * size;
269 
270             ptf[0].X = x2 - dx;
271             ptf[0].Y = y2 - dy;
272 
273             dx = cos(- M_PI / 6.0 + theta) * size;
274             dy = sin(- M_PI / 6.0 + theta) * size;
275 
276             ptf[1].X = x2 - dx;
277             ptf[1].Y = y2 - dy;
278 
279             ptf[2].X = x2;
280             ptf[2].Y = y2;
281 
282             transform_and_round_points(graphics, pt, ptf, 3);
283             Polygon(graphics->hdc, pt, 3);
284 
285             break;
286         case LineCapRoundAnchor:
287             dx = dy = ANCHOR_WIDTH * size / 2.0;
288 
289             ptf[0].X = x2 - dx;
290             ptf[0].Y = y2 - dy;
291             ptf[1].X = x2 + dx;
292             ptf[1].Y = y2 + dy;
293 
294             transform_and_round_points(graphics, pt, ptf, 2);
295             Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
296 
297             break;
298         case LineCapTriangle:
299             size = size / 2.0;
300             dx = cos(M_PI_2 + theta) * size;
301             dy = sin(M_PI_2 + theta) * size;
302 
303             ptf[0].X = x2 - dx;
304             ptf[0].Y = y2 - dy;
305             ptf[1].X = x2 + dx;
306             ptf[1].Y = y2 + dy;
307 
308             dx = cos(theta) * size;
309             dy = sin(theta) * size;
310 
311             ptf[2].X = x2 + dx;
312             ptf[2].Y = y2 + dy;
313 
314             transform_and_round_points(graphics, pt, ptf, 3);
315             Polygon(graphics->hdc, pt, 3);
316 
317             break;
318         case LineCapRound:
319             dx = dy = size / 2.0;
320 
321             ptf[0].X = x2 - dx;
322             ptf[0].Y = y2 - dy;
323             ptf[1].X = x2 + dx;
324             ptf[1].Y = y2 + dy;
325 
326             dx = -cos(M_PI_2 + theta) * size;
327             dy = -sin(M_PI_2 + theta) * size;
328 
329             ptf[2].X = x2 - dx;
330             ptf[2].Y = y2 - dy;
331             ptf[3].X = x2 + dx;
332             ptf[3].Y = y2 + dy;
333 
334             transform_and_round_points(graphics, pt, ptf, 4);
335             Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
336                 pt[2].y, pt[3].x, pt[3].y);
337 
338             break;
339         case LineCapCustom:
340             if(!custom)
341                 break;
342 
343             count = custom->pathdata.Count;
344             custptf = GdipAlloc(count * sizeof(PointF));
345             custpt = GdipAlloc(count * sizeof(POINT));
346             tp = GdipAlloc(count);
347 
348             if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
349                 goto custend;
350 
351             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
352 
353             GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
354             GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
355                              MatrixOrderAppend);
356             GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
357             GdipTransformMatrixPoints(matrix, custptf, count);
358 
359             transform_and_round_points(graphics, custpt, custptf, count);
360 
361             for(i = 0; i < count; i++)
362                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
363 
364             if(custom->fill){
365                 BeginPath(graphics->hdc);
366                 PolyDraw(graphics->hdc, custpt, tp, count);
367                 EndPath(graphics->hdc);
368                 StrokeAndFillPath(graphics->hdc);
369             }
370             else
371                 PolyDraw(graphics->hdc, custpt, tp, count);
372 
373 custend:
374             GdipFree(custptf);
375             GdipFree(custpt);
376             GdipFree(tp);
377             GdipDeleteMatrix(matrix);
378             break;
379         default:
380             break;
381     }
382 
383     if(!customstroke){
384         SelectObject(graphics->hdc, oldbrush);
385         SelectObject(graphics->hdc, oldpen);
386         DeleteObject(brush);
387         DeleteObject(pen);
388     }
389 }
390 
391 /* Shortens the line by the given percent by changing x2, y2.
392  * If percent is > 1.0 then the line will change direction.
393  * If percent is negative it can lengthen the line. */
394 static void shorten_line_percent(REAL x1, REAL  y1, REAL *x2, REAL *y2, REAL percent)
395 {
396     REAL dist, theta, dx, dy;
397 
398     if((y1 == *y2) && (x1 == *x2))
399         return;
400 
401     dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
402     theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
403     dx = cos(theta) * dist;
404     dy = sin(theta) * dist;
405 
406     *x2 = *x2 + dx;
407     *y2 = *y2 + dy;
408 }
409 
410 /* Shortens the line by the given amount by changing x2, y2.
411  * If the amount is greater than the distance, the line will become length 0.
412  * If the amount is negative, it can lengthen the line. */
413 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
414 {
415     REAL dx, dy, percent;
416 
417     dx = *x2 - x1;
418     dy = *y2 - y1;
419     if(dx == 0 && dy == 0)
420         return;
421 
422     percent = amt / sqrt(dx * dx + dy * dy);
423     if(percent >= 1.0){
424         *x2 = x1;
425         *y2 = y1;
426         return;
427     }
428 
429     shorten_line_percent(x1, y1, x2, y2, percent);
430 }
431 
432 /* Draws lines between the given points, and if caps is true then draws an endcap
433  * at the end of the last line. */
434 static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
435     GDIPCONST GpPointF * pt, INT count, BOOL caps)
436 {
437     POINT *pti = NULL;
438     GpPointF *ptcopy = NULL;
439     GpStatus status = GenericError;
440 
441     if(!count)
442         return Ok;
443 
444     pti = GdipAlloc(count * sizeof(POINT));
445     ptcopy = GdipAlloc(count * sizeof(GpPointF));
446 
447     if(!pti || !ptcopy){
448         status = OutOfMemory;
449         goto end;
450     }
451 
452     memcpy(ptcopy, pt, count * sizeof(GpPointF));
453 
454     if(caps){
455         if(pen->endcap == LineCapArrowAnchor)
456             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
457                              &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
458         else if((pen->endcap == LineCapCustom) && pen->customend)
459             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
460                              &ptcopy[count-1].X, &ptcopy[count-1].Y,
461                              pen->customend->inset * pen->width);
462 
463         if(pen->startcap == LineCapArrowAnchor)
464             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
465                              &ptcopy[0].X, &ptcopy[0].Y, pen->width);
466         else if((pen->startcap == LineCapCustom) && pen->customstart)
467             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
468                              &ptcopy[0].X, &ptcopy[0].Y,
469                              pen->customstart->inset * pen->width);
470 
471         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
472                  pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
473         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
474                          pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);
475     }
476 
477     transform_and_round_points(graphics, pti, ptcopy, count);
478 
479     if(Polyline(graphics->hdc, pti, count))
480         status = Ok;
481 
482 end:
483     GdipFree(pti);
484     GdipFree(ptcopy);
485 
486     return status;
487 }
488 
489 /* Conducts a linear search to find the bezier points that will back off
490  * the endpoint of the curve by a distance of amt. Linear search works
491  * better than binary in this case because there are multiple solutions,
492  * and binary searches often find a bad one. I don't think this is what
493  * Windows does but short of rendering the bezier without GDI's help it's
494  * the best we can do. If rev then work from the start of the passed points
495  * instead of the end. */
496 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
497 {
498     GpPointF origpt[4];
499     REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
500     INT i, first = 0, second = 1, third = 2, fourth = 3;
501 
502     if(rev){
503         first = 3;
504         second = 2;
505         third = 1;
506         fourth = 0;
507     }
508 
509     origx = pt[fourth].X;
510     origy = pt[fourth].Y;
511     memcpy(origpt, pt, sizeof(GpPointF) * 4);
512 
513     for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
514         /* reset bezier points to original values */
515         memcpy(pt, origpt, sizeof(GpPointF) * 4);
516         /* Perform magic on bezier points. Order is important here.*/
517         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
518         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
519         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
520         shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
521         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
522         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
523 
524         dx = pt[fourth].X - origx;
525         dy = pt[fourth].Y - origy;
526 
527         diff = sqrt(dx * dx + dy * dy);
528         percent += 0.0005 * amt;
529     }
530 }
531 
532 /* Draws bezier curves between given points, and if caps is true then draws an
533  * endcap at the end of the last line. */
534 static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
535     GDIPCONST GpPointF * pt, INT count, BOOL caps)
536 {
537     POINT *pti;
538     GpPointF *ptcopy;
539     GpStatus status = GenericError;
540 
541     if(!count)
542         return Ok;
543 
544     pti = GdipAlloc(count * sizeof(POINT));
545     ptcopy = GdipAlloc(count * sizeof(GpPointF));
546 
547     if(!pti || !ptcopy){
548         status = OutOfMemory;
549         goto end;
550     }
551 
552     memcpy(ptcopy, pt, count * sizeof(GpPointF));
553 
554     if(caps){
555         if(pen->endcap == LineCapArrowAnchor)
556             shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
557         else if((pen->endcap == LineCapCustom) && pen->customend)
558             shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
559                                FALSE);
560 
561         if(pen->startcap == LineCapArrowAnchor)
562             shorten_bezier_amt(ptcopy, pen->width, TRUE);
563         else if((pen->startcap == LineCapCustom) && pen->customstart)
564             shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
565 
566         /* the direction of the line cap is parallel to the direction at the
567          * end of the bezier (which, if it has been shortened, is not the same
568          * as the direction from pt[count-2] to pt[count-1]) */
569         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
570             pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
571             pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
572             pt[count - 1].X, pt[count - 1].Y);
573 
574         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
575             pt[0].X - (ptcopy[0].X - ptcopy[1].X),
576             pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
577     }
578 
579     transform_and_round_points(graphics, pti, ptcopy, count);
580 
581     PolyBezier(graphics->hdc, pti, count);
582 
583     status = Ok;
584 
585 end:
586     GdipFree(pti);
587     GdipFree(ptcopy);
588 
589     return status;
590 }
591 
592 /* Draws a combination of bezier curves and lines between points. */
593 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
594     GDIPCONST BYTE * types, INT count, BOOL caps)
595 {
596     POINT *pti = GdipAlloc(count * sizeof(POINT));
597     BYTE *tp = GdipAlloc(count);
598     GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
599     INT i, j;
600     GpStatus status = GenericError;
601 
602     if(!count){
603         status = Ok;
604         goto end;
605     }
606     if(!pti || !tp || !ptcopy){
607         status = OutOfMemory;
608         goto end;
609     }
610 
611     for(i = 1; i < count; i++){
612         if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
613             if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
614                 || !(types[i + 1] & PathPointTypeBezier)){
615                 ERR("Bad bezier points\n");
616                 goto end;
617             }
618             i += 2;
619         }
620     }
621 
622     memcpy(ptcopy, pt, count * sizeof(GpPointF));
623 
624     /* If we are drawing caps, go through the points and adjust them accordingly,
625      * and draw the caps. */
626     if(caps){
627         switch(types[count - 1] & PathPointTypePathTypeMask){
628             case PathPointTypeBezier:
629                 if(pen->endcap == LineCapArrowAnchor)
630                     shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
631                 else if((pen->endcap == LineCapCustom) && pen->customend)
632                     shorten_bezier_amt(&ptcopy[count - 4],
633                                        pen->width * pen->customend->inset, FALSE);
634 
635                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
636                     pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
637                     pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
638                     pt[count - 1].X, pt[count - 1].Y);
639 
640                 break;
641             case PathPointTypeLine:
642                 if(pen->endcap == LineCapArrowAnchor)
643                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
644                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
645                                      pen->width);
646                 else if((pen->endcap == LineCapCustom) && pen->customend)
647                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
648                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
649                                      pen->customend->inset * pen->width);
650 
651                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
652                          pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
653                          pt[count - 1].Y);
654 
655                 break;
656             default:
657                 ERR("Bad path last point\n");
658                 goto end;
659         }
660 
661         /* Find start of points */
662         for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
663             == PathPointTypeStart); j++);
664 
665         switch(types[j] & PathPointTypePathTypeMask){
666             case PathPointTypeBezier:
667                 if(pen->startcap == LineCapArrowAnchor)
668                     shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
669                 else if((pen->startcap == LineCapCustom) && pen->customstart)
670                     shorten_bezier_amt(&ptcopy[j - 1],
671                                        pen->width * pen->customstart->inset, TRUE);
672 
673                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
674                     pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
675                     pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
676                     pt[j - 1].X, pt[j - 1].Y);
677 
678                 break;
679             case PathPointTypeLine:
680                 if(pen->startcap == LineCapArrowAnchor)
681                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
682                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
683                                      pen->width);
684                 else if((pen->startcap == LineCapCustom) && pen->customstart)
685                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
686                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
687                                      pen->customstart->inset * pen->width);
688 
689                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
690                          pt[j].X, pt[j].Y, pt[j - 1].X,
691                          pt[j - 1].Y);
692 
693                 break;
694             default:
695                 ERR("Bad path points\n");
696                 goto end;
697         }
698     }
699 
700     transform_and_round_points(graphics, pti, ptcopy, count);
701 
702     for(i = 0; i < count; i++){
703         tp[i] = convert_path_point_type(types[i]);
704     }
705 
706     PolyDraw(graphics->hdc, pti, tp, count);
707 
708     status = Ok;
709 
710 end:
711     GdipFree(pti);
712     GdipFree(ptcopy);
713     GdipFree(tp);
714 
715     return status;
716 }
717 
718 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
719 {
720     TRACE("(%p, %p)\n", hdc, graphics);
721 
722     return GdipCreateFromHDC2(hdc, NULL, graphics);
723 }
724 
725 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
726 {
727     GpStatus retval;
728 
729     TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
730 
731     if(hDevice != NULL) {
732         FIXME("Don't know how to hadle parameter hDevice\n");
733         return NotImplemented;
734     }
735 
736     if(hdc == NULL)
737         return OutOfMemory;
738 
739     if(graphics == NULL)
740         return InvalidParameter;
741 
742     *graphics = GdipAlloc(sizeof(GpGraphics));
743     if(!*graphics)  return OutOfMemory;
744 
745     if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
746         GdipFree(*graphics);
747         return retval;
748     }
749 
750     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
751         GdipFree((*graphics)->worldtrans);
752         GdipFree(*graphics);
753         return retval;
754     }
755 
756     (*graphics)->hdc = hdc;
757     (*graphics)->hwnd = WindowFromDC(hdc);
758     (*graphics)->smoothing = SmoothingModeDefault;
759     (*graphics)->compqual = CompositingQualityDefault;
760     (*graphics)->interpolation = InterpolationModeDefault;
761     (*graphics)->pixeloffset = PixelOffsetModeDefault;
762     (*graphics)->compmode = CompositingModeSourceOver;
763     (*graphics)->unit = UnitDisplay;
764     (*graphics)->scale = 1.0;
765     (*graphics)->busy = FALSE;
766     (*graphics)->textcontrast = 4;
767 
768     return Ok;
769 }
770 
771 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
772 {
773     GpStatus ret;
774 
775     TRACE("(%p, %p)\n", hwnd, graphics);
776 
777     if((ret = GdipCreateFromHDC(GetDC(hwnd), graphics)) != Ok)
778         return ret;
779 
780     (*graphics)->hwnd = hwnd;
781 
782     return Ok;
783 }
784 
785 /* FIXME: no icm handling */
786 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
787 {
788     TRACE("(%p, %p)\n", hwnd, graphics);
789 
790     return GdipCreateFromHWND(hwnd, graphics);
791 }
792 
793 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
794     GpMetafile **metafile)
795 {
796     static int calls;
797 
798     if(!hemf || !metafile)
799         return InvalidParameter;
800 
801     if(!(calls++))
802         FIXME("not implemented\n");
803 
804     return NotImplemented;
805 }
806 
807 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
808     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
809 {
810     IStream *stream = NULL;
811     UINT read;
812     BYTE* copy;
813     HENHMETAFILE hemf;
814     GpStatus retval = GenericError;
815 
816     TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
817 
818     if(!hwmf || !metafile || !placeable)
819         return InvalidParameter;
820 
821     *metafile = NULL;
822     read = GetMetaFileBitsEx(hwmf, 0, NULL);
823     if(!read)
824         return GenericError;
825     copy = GdipAlloc(read);
826     GetMetaFileBitsEx(hwmf, read, copy);
827 
828     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
829     GdipFree(copy);
830 
831     read = GetEnhMetaFileBits(hemf, 0, NULL);
832     copy = GdipAlloc(read);
833     GetEnhMetaFileBits(hemf, read, copy);
834     DeleteEnhMetaFile(hemf);
835 
836     if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
837         ERR("could not make stream\n");
838         GdipFree(copy);
839         goto err;
840     }
841 
842     *metafile = GdipAlloc(sizeof(GpMetafile));
843     if(!*metafile){
844         retval = OutOfMemory;
845         goto err;
846     }
847 
848     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
849         (LPVOID*) &((*metafile)->image.picture)) != S_OK)
850         goto err;
851 
852 
853     (*metafile)->image.type = ImageTypeMetafile;
854     (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
855     (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Right) / ((REAL) placeable->Inch);
856     (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
857                     - placeable->BoundingBox.Left)) / ((REAL) placeable->Inch);
858     (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
859                    - placeable->BoundingBox.Top)) / ((REAL) placeable->Inch);
860     (*metafile)->unit = UnitInch;
861 
862     if(delete)
863         DeleteMetaFile(hwmf);
864 
865     return Ok;
866 
867 err:
868     GdipFree(*metafile);
869     IStream_Release(stream);
870     return retval;
871 }
872 
873 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
874     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
875 {
876     HMETAFILE hmf = GetMetaFileW(file);
877 
878     TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
879 
880     if(!hmf) return InvalidParameter;
881 
882     return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
883 }
884 
885 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
886     UINT access, IStream **stream)
887 {
888     DWORD dwMode;
889     HRESULT ret;
890 
891     TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
892 
893     if(!stream || !filename)
894         return InvalidParameter;
895 
896     if(access & GENERIC_WRITE)
897         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
898     else if(access & GENERIC_READ)
899         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
900     else
901         return InvalidParameter;
902 
903     ret = SHCreateStreamOnFileW(filename, dwMode, stream);
904 
905     return hresult_to_status(ret);
906 }
907 
908 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)