1 /*
2  * Unit test suite for pens (and init)
3  *
4  * Copyright (C) 2007 Google (Evan Stade)
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 <math.h>
22 
23 #include "objbase.h"
24 #include "gdiplus.h"
25 #include "wine/test.h"
26 
27 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
28 #define expectf(expected, got) ok(fabs(got - expected) < 0.1, "Expected %.2f, got %.2f\n", expected, got)
29 
30 static void test_startup(void)
31 {
32     GpPen *pen = NULL;
33     Status status;
34     struct GdiplusStartupInput gdiplusStartupInput;
35     ULONG_PTR gdiplusToken;
36     int gpversion;
37 
38     gdiplusStartupInput.DebugEventCallback          = NULL;
39     gdiplusStartupInput.SuppressBackgroundThread    = 0;
40     gdiplusStartupInput.SuppressExternalCodecs      = 0;
41 
42     for (gpversion=1; gpversion<256; gpversion++)
43     {
44         gdiplusStartupInput.GdiplusVersion = gpversion;
45         status = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
46         ok(status == Ok || status == UnsupportedGdiplusVersion,
47             "GdiplusStartup returned %x\n", status);
48         GdiplusShutdown(gdiplusToken);
49         if (status != Ok)
50         {
51             gpversion--;
52             break;
53         }
54     }
55 
56     ok(gpversion > 0 && gpversion <= 2, "unexpected gdiplus version %i\n", gpversion);
57     trace("gdiplus version is %i\n", gpversion);
58 
59     status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
60 
61     todo_wine
62         expect(GdiplusNotInitialized, status);
63 
64     GdipDeletePen(pen);
65 }
66 
67 static void test_constructor_destructor(void)
68 {
69     GpStatus status;
70     GpPen *pen = NULL;
71 
72     status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, NULL);
73     expect(InvalidParameter, status);
74     ok(pen == NULL, "Expected pen to be NULL\n");
75 
76     status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
77     expect(Ok, status);
78     ok(pen != NULL, "Expected pen to be initialized\n");
79 
80     status = GdipDeletePen(NULL);
81     expect(InvalidParameter, status);
82 
83     status = GdipDeletePen(pen);
84     expect(Ok, status);
85 }
86 
87 static void test_constructor_destructor2(void)
88 {
89     GpStatus status;
90     GpPen *pen = NULL;
91     GpBrush *brush = NULL;
92     GpPointF points[2];
93 
94     status = GdipCreatePen2(NULL, 10.0f, UnitPixel, &pen);
95     expect(InvalidParameter, status);
96     ok(pen == NULL, "Expected pen to be NULL\n");
97 
98     points[0].X = 7.0;
99     points[0].Y = 11.0;
100     points[1].X = 13.0;
101     points[1].Y = 17.0;
102 
103     status = GdipCreateLineBrush(&points[0], &points[1], (ARGB)0xffff00ff,
104                     (ARGB)0xff0000ff, WrapModeTile, (GpLineGradient **)&brush);
105     expect(Ok, status);
106     ok(brush != NULL, "Expected brush to be initialized\n");
107 
108     status = GdipCreatePen2(brush, 10.0f, UnitPixel, &pen);
109     expect(Ok, status);
110     ok(pen != NULL, "Expected pen to be initialized\n");
111 
112     status = GdipDeletePen(pen);
113     expect(Ok, status);
114 
115     status = GdipDeleteBrush(brush);
116     expect(Ok, status);
117 }
118 
119 static void test_brushfill(void)
120 {
121     GpStatus status;
122     GpPen *pen;
123     GpBrush *brush, *brush2;
124     GpBrushType type;
125     ARGB color = 0;
126 
127     /* default solid */
128     GdipCreatePen1(0xdeadbeef, 4.5, UnitWorld, &pen);
129     status = GdipGetPenBrushFill(pen, &brush);
130     expect(Ok, status);
131     GdipGetBrushType(brush, &type);
132     expect(BrushTypeSolidColor, type);
133     GdipGetPenColor(pen, &color);
134     expect(0xdeadbeef, color);
135     GdipDeleteBrush(brush);
136 
137     /* color controlled by brush */
138     GdipCreateSolidFill(0xabaddeed, (GpSolidFill**)&brush);
139     status = GdipSetPenBrushFill(pen, brush);
140     expect(Ok, status);
141     GdipGetPenColor(pen, &color);
142     expect(0xabaddeed, color);
143     GdipDeleteBrush(brush);
144     color = 0;
145 
146     /* get returns a clone, not a reference */
147     GdipGetPenBrushFill(pen, &brush);
148     GdipSetSolidFillColor((GpSolidFill*)brush, 0xbeadfeed);
149     GdipGetPenBrushFill(pen, &brush2);
150     ok(brush != brush2, "Expected to get a clone, not a copy of the reference\n");
151     GdipGetSolidFillColor((GpSolidFill*)brush2, &color);
152     expect(0xabaddeed, color);
153     GdipDeleteBrush(brush);
154     GdipDeleteBrush(brush2);
155 
156     /* brush cannot be NULL */
157     status = GdipSetPenBrushFill(pen, NULL);
158     expect(InvalidParameter, status);
159 
160     GdipDeletePen(pen);
161 }
162 
163 static void test_dasharray(void)
164 {
165     GpPen *pen;
166     GpDashStyle style;
167     GpStatus status;
168     REAL dashes[12];
169 
170     GdipCreatePen1(0xdeadbeef, 10.0, UnitWorld, &pen);
171     dashes[0] = 10.0;
172     dashes[1] = 11.0;
173     dashes[2] = 12.0;
174     dashes[3] = 13.0;
175     dashes[4] = 14.0;
176     dashes[5] = -100.0;
177     dashes[6] = -100.0;
178     dashes[7] = dashes[8] = dashes[9] = dashes[10] = dashes[11] = 0.0;
179 
180     /* setting the array sets the type to custom */
181     GdipGetPenDashStyle(pen, &style);
182     expect(DashStyleSolid, style);
183     status = GdipSetPenDashArray(pen, dashes, 2);
184     expect(Ok, status);
185     GdipGetPenDashStyle(pen, &style);
186     expect(DashStyleCustom, style);
187 
188     /* Getting the array on a non-custom pen returns invalid parameter (unless
189      * you are getting 0 elements).*/
190     GdipSetPenDashStyle(pen, DashStyleSolid);
191     status = GdipGetPenDashArray(pen, &dashes[5], 2);
192     expect(InvalidParameter, status);
193     status = GdipGetPenDashArray(pen, &dashes[5], 0);
194     expect(Ok, status);
195 
196     /* What does setting DashStyleCustom do to the array length? */
197     GdipSetPenDashArray(pen, dashes, 2);
198     GdipSetPenDashStyle(pen, DashStyleCustom);
199     status = GdipGetPenDashArray(pen, &dashes[5], 2);
200     expect(Ok, status);
201     expectf(10.0, dashes[5]);
202     expectf(11.0, dashes[6]);
203 
204     /* Set the array, then get with different sized buffers. */
205     status = GdipSetPenDashArray(pen, dashes, 5);
206     expect(Ok, status);
207     dashes[5] = -100.0;
208     dashes[6] = -100.0;
209     status = GdipGetPenDashArray(pen, &dashes[5], 1);
210     expect(Ok, status); /* not InsufficientBuffer! */
211     expectf(10.0, dashes[5]);
212     expectf(-100.0, dashes[6]);
213     dashes[5] = -100.0;
214     status = GdipGetPenDashArray(pen, &dashes[5], 6);
215     expect(InvalidParameter, status); /* not Ok! */
216     expectf(-100.0, dashes[5]);
217     expectf(-100.0, dashes[6]);
218 
219     /* Some invalid array values. */
220     status = GdipSetPenDashArray(pen, &dashes[7], 5);
221     expect(InvalidParameter, status);
222     dashes[9] = -1.0;
223     status = GdipSetPenDashArray(pen, &dashes[7], 5);
224     expect(InvalidParameter, status);
225 
226     /* Try to set with count = 0. */
227     GdipSetPenDashStyle(pen, DashStyleDot);
228     if (0)  /* corrupts stack on 64-bit Vista */
229     {
230     status = GdipSetPenDashArray(pen, dashes, 0);
231     ok(status == OutOfMemory || status == InvalidParameter,
232        "Expected OutOfMemory or InvalidParameter, got %.8x\n", status);
233     }
234     status = GdipSetPenDashArray(pen, dashes, -1);
235     ok(status == OutOfMemory || status == InvalidParameter,
236        "Expected OutOfMemory or InvalidParameter, got %.8x\n", status);
237     GdipGetPenDashStyle(pen, &style);
238     expect(DashStyleDot, style);
239 
240     GdipDeletePen(pen);
241 }
242 
243 static void test_customcap(void)
244 {
245     GpPen *pen;
246     GpStatus status;
247     GpCustomLineCap *custom;
248 
249     status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
250     expect(Ok, status);
251 
252     /* NULL args */
253     status = GdipGetPenCustomStartCap(NULL, NULL);
254     expect(InvalidParameter, status);
255     status = GdipGetPenCustomStartCap(pen, NULL);
256     expect(InvalidParameter, status);
257     status = GdipGetPenCustomStartCap(NULL, &custom);
258     expect(InvalidParameter, status);
259 
260     status = GdipGetPenCustomEndCap(NULL, NULL);
261     expect(InvalidParameter, status);
262     status = GdipGetPenCustomEndCap(pen, NULL);
263     expect(InvalidParameter, status);
264     status = GdipGetPenCustomEndCap(NULL, &custom);
265     expect(InvalidParameter, status);
266 
267     /* native crashes on pen == NULL, custom != NULL */
268     status = GdipSetPenCustomStartCap(NULL, NULL);
269     expect(InvalidParameter, status);
270     status = GdipSetPenCustomStartCap(pen, NULL);
271     expect(InvalidParameter, status);
272 
273     status = GdipSetPenCustomEndCap(NULL, NULL);
274     expect(InvalidParameter, status);
275     status = GdipSetPenCustomEndCap(pen, NULL);
276     expect(InvalidParameter, status);
277 
278     /* get without setting previously */
279     custom = (GpCustomLineCap*)0xdeadbeef;
280     status = GdipGetPenCustomEndCap(pen, &custom);
281     expect(Ok, status);
282     ok(custom == NULL,"Expect CustomCap == NULL\n");
283 
284     custom = (GpCustomLineCap*)0xdeadbeef;
285     status = GdipGetPenCustomStartCap(pen, &custom);
286     expect(Ok, status);
287     ok(custom == NULL,"Expect CustomCap == NULL\n");
288 
289     GdipDeletePen(pen);
290 }
291 
292 static void test_penfilltype(void)
293 {
294     GpPen *pen;
295     GpSolidFill *solid;
296     GpLineGradient *line;
297     GpPointF a, b;
298     GpStatus status;
299     GpPenType type;
300 
301     /* NULL */
302     status = GdipGetPenFillType(NULL, NULL);
303     expect(InvalidParameter, status);
304 
305     status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
306     expect(Ok, status);
307     status = GdipGetPenFillType(pen, NULL);
308     expect(InvalidParameter, status);
309 
310     /* created with GdipCreatePen1() */
311     status = GdipGetPenFillType(pen, &type);
312     expect(Ok, status);
313     expect(PenTypeSolidColor, type);
314     GdipDeletePen(pen);
315 
316     /* based on SolidBrush */
317     status = GdipCreateSolidFill((ARGB)0xffff00ff, &solid);
318     expect(Ok, status);
319     status = GdipCreatePen2((GpBrush*)solid, 10.0f, UnitPixel, &pen);
320     expect(Ok, status);
321     status = GdipGetPenFillType(pen, &type);
322     expect(Ok, status);
323     expect(PenTypeSolidColor, type);
324     GdipDeletePen(pen);
325     GdipDeleteBrush((GpBrush*)solid);
326 
327     /* based on LinearGradientBrush */
328     a.X = a.Y = 0.0;
329     b.X = b.Y = 10.0;
330     status = GdipCreateLineBrush(&a, &b, (ARGB)0xffff00ff, (ARGB)0xffff0000,
331                                  WrapModeTile, &line);
332     expect(Ok, status);
333     status = GdipCreatePen2((GpBrush*)line, 10.0f, UnitPixel, &pen);
334     expect(Ok, status);
335     status = GdipGetPenFillType(pen, &type);
336     expect(Ok, status);
337     expect(PenTypeLinearGradient, type);
338     GdipDeletePen(pen);
339     GdipDeleteBrush((GpBrush*)line);
340 }
341 
342 static void test_compoundarray(void)
343 {
344     GpStatus status;
345     GpPen *pen;
346     static const REAL testvalues[] = {0.2, 0.4, 0.6, 0.8};
347     INT count;
348 
349     status = GdipSetPenCompoundArray(NULL, testvalues, 4);
350     expect(InvalidParameter, status);
351 
352     status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
353     expect(Ok, status);
354 
355     status = GdipGetPenCompoundCount(NULL, NULL);
356     expect(InvalidParameter, status);
357 
358     status = GdipGetPenCompoundCount(pen, NULL);
359     expect(InvalidParameter, status);
360 
361     count = 10;
362     status = GdipGetPenCompoundCount(pen, &count);
363 todo_wine {
364     expect(Ok, status);
365     ok(count == 0, "Unexpected compound count %d\n", count);
366 }
367     status = GdipSetPenCompoundArray(pen, NULL, 0);
368     expect(InvalidParameter, status);
369     status = GdipSetPenCompoundArray(pen, NULL, 4);
370     expect(InvalidParameter, status);
371     status = GdipSetPenCompoundArray(pen, testvalues, 3);
372     expect(InvalidParameter, status);
373     status = GdipSetPenCompoundArray(pen, testvalues, 0);
374     expect(InvalidParameter, status);
375     status = GdipSetPenCompoundArray(pen, testvalues, -2);
376     expect(InvalidParameter, status);
377 
378     status = GdipSetPenCompoundArray(pen, testvalues, 4);
379     todo_wine expect(Ok, status);
380     status = GdipSetPenCompoundArray(pen, NULL, 0);
381     expect(InvalidParameter, status);
382 
383     count = 0;
384     status = GdipGetPenCompoundCount(pen, &count);
385 todo_wine {
386     expect(Ok, status);
387     ok(count == 4, "Unexpected compound count %d\n", count);
388 }
389     GdipDeletePen(pen);
390 }
391 
392 static void test_transform(void)
393 {
394     GpStatus status;
395     GpPen *pen;
396     GpMatrix *matrix, *matrix2;
397     REAL values[6];
398 
399     status = GdipCreatePen1((ARGB)0xffff00ff, 10.0f, UnitPixel, &pen);
400     expect(Ok, status);
401 
402     status = GdipCreateMatrix(&matrix);
403     expect(Ok, status);
404 
405     status = GdipGetPenTransform(pen, matrix);
406     expect(Ok, status);
407 
408     status = GdipGetMatrixElements(matrix, values);
409     expect(Ok, status);
410 
411     expectf(1.0, values[0]);
412     expectf(0.0, values[1]);
413     expectf(0.0, values[2]);
414     expectf(1.0, values[3]);
415     expectf(0.0, values[4]);
416     expectf(0.0, values[5]);
417 
418     GdipCreateMatrix2(3.0, -2.0, 5.0, 2.0, 6.0, 3.0, &matrix2);
419     status = GdipSetPenTransform(pen, matrix2);
420     expect(Ok, status);
421     GdipDeleteMatrix(matrix2);
422 
423     status = GdipGetPenTransform(pen, matrix);
424     expect(Ok, status);
425     status = GdipGetMatrixElements(matrix, values);
426     expect(Ok, status);
427     expectf(3.0,  values[0]);
428     expectf(-2.0,  values[1]);
429     expectf(5.0,  values[2]);
430     expectf(2.0, values[3]);
431     expectf(6.0, values[4]);
432     expectf(3.0,  values[5]);
433 
434     /* Translate */
435     status = GdipTranslatePenTransform(NULL, 1.0, -2.0, MatrixOrderAppend);
436     expect(InvalidParameter, status);
437 
438     status = GdipTranslatePenTransform(pen, 1.0, -2.0, MatrixOrderAppend);
439     expect(Ok, status);
440 
441     status = GdipGetPenTransform(pen, matrix);
442     expect(Ok, status);
443     status = GdipGetMatrixElements(matrix, values);
444     expect(Ok, status);
445     expectf(3.0,  values[0]);
446     expectf(-2.0,  values[1]);
447     expectf(5.0,  values[2]);
448     expectf(2.0, values[3]);
449     expectf(7.0, values[4]);
450     expectf(1.0,  values[5]);
451 
452     status = GdipTranslatePenTransform(pen, -3.0, 5.0, MatrixOrderPrepend);
453     expect(Ok, status);
454 
455     status = GdipGetPenTransform(pen, matrix);
456     expect(Ok, status);
457     status = GdipGetMatrixElements(matrix, values);
458     expect(Ok, status);
459     expectf(3.0,  values[0]);
460     expectf(-2.0,  values[1]);
461     expectf(5.0,  values[2]);
462     expectf(2.0, values[3]);
463     expectf(23.0, values[4]);
464     expectf(17.0,  values[5]);
465 
466     status = GdipResetPenTransform(pen);
467     expect(Ok, status);
468 
469     status = GdipGetPenTransform(pen, matrix);
470     expect(Ok, status);
471     status = GdipGetMatrixElements(matrix, values);
472     expect(Ok, status);
473 
474     expectf(1.0, values[0]);
475     expectf(0.0, values[1]);
476     expectf(0.0, values[2]);
477     expectf(1.0, values[3]);
478     expectf(0.0, values[4]);
479     expectf(0.0, values[5]);
480 
481     GdipDeletePen(pen);
482 
483     GdipDeleteMatrix(matrix);
484 }
485 
486 START_TEST(pen)
487 {
488     struct GdiplusStartupInput gdiplusStartupInput;
489     ULONG_PTR gdiplusToken;
490     HMODULE hmsvcrt;
491     int (CDECL * _controlfp_s)(unsigned int *cur, unsigned int newval, unsigned int mask);
492 
493     /* Enable all FP exceptions except _EM_INEXACT, which gdi32 can trigger */
494     hmsvcrt = LoadLibraryA("msvcrt");
495     _controlfp_s = (void*)GetProcAddress(hmsvcrt, "_controlfp_s");
496     if (_controlfp_s) _controlfp_s(0, 0, 0x0008001e);
497 
498     test_startup();
499 
500     gdiplusStartupInput.GdiplusVersion              = 1;
501     gdiplusStartupInput.DebugEventCallback          = NULL;
502     gdiplusStartupInput.SuppressBackgroundThread    = 0;
503     gdiplusStartupInput.SuppressExternalCodecs      = 0;
504 
505     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
506 
507     test_constructor_destructor();
508     test_constructor_destructor2();
509     test_brushfill();
510     test_dasharray();
511     test_customcap();
512     test_penfilltype();
513     test_compoundarray();
514     test_transform();
515 
516     GdiplusShutdown(gdiplusToken);
517 }
518