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