winmm: Fix a failing mixer test on 98 and ME.
[wine] / dlls / oleaut32 / tests / olepicture.c
1 /*
2  * OLEPICTURE test program
3  *
4  * Copyright 2005 Marcus Meissner
5  *
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <math.h>
25 #include <float.h>
26 #include <time.h>
27
28 #define COBJMACROS
29
30 #include "wine/test.h"
31 #include <windef.h>
32 #include <winbase.h>
33 #include <winuser.h>
34 #include <wingdi.h>
35 #include <winnls.h>
36 #include <winerror.h>
37 #include <winnt.h>
38
39 #include <wtypes.h>
40 #include <olectl.h>
41 #include <objidl.h>
42
43 #define expect_eq(expr, value, type, format) { type ret = (expr); ok((value) == ret, #expr " expected " format " got " format "\n", value, ret); }
44
45 #define ole_expect(expr, expect) { \
46     HRESULT r = expr; \
47     ok(r == (expect), #expr " returned %x, expected %s (%x)\n", r, #expect, expect); \
48 }
49
50 #define ole_check(expr) ole_expect(expr, S_OK);
51
52 static HMODULE hOleaut32;
53
54 static HRESULT (WINAPI *pOleLoadPicture)(LPSTREAM,LONG,BOOL,REFIID,LPVOID*);
55 static HRESULT (WINAPI *pOleCreatePictureIndirect)(PICTDESC*,REFIID,BOOL,LPVOID*);
56
57 #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr)
58
59 /* 1x1 pixel gif */
60 static const unsigned char gifimage[35] = {
61 0x47,0x49,0x46,0x38,0x37,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x00,0xff,0xff,0xff,
62 0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x02,0x44,
63 0x01,0x00,0x3b
64 };
65
66 /* 1x1 pixel jpg */
67 static const unsigned char jpgimage[285] = {
68 0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x01,0x2c,
69 0x01,0x2c,0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x05,0x03,0x04,0x04,0x04,0x03,0x05,
70 0x04,0x04,0x04,0x05,0x05,0x05,0x06,0x07,0x0c,0x08,0x07,0x07,0x07,0x07,0x0f,0x0b,
71 0x0b,0x09,0x0c,0x11,0x0f,0x12,0x12,0x11,0x0f,0x11,0x11,0x13,0x16,0x1c,0x17,0x13,
72 0x14,0x1a,0x15,0x11,0x11,0x18,0x21,0x18,0x1a,0x1d,0x1d,0x1f,0x1f,0x1f,0x13,0x17,
73 0x22,0x24,0x22,0x1e,0x24,0x1c,0x1e,0x1f,0x1e,0xff,0xdb,0x00,0x43,0x01,0x05,0x05,
74 0x05,0x07,0x06,0x07,0x0e,0x08,0x08,0x0e,0x1e,0x14,0x11,0x14,0x1e,0x1e,0x1e,0x1e,
75 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
76 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
77 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0xff,0xc0,
78 0x00,0x11,0x08,0x00,0x01,0x00,0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,
79 0x01,0xff,0xc4,0x00,0x15,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
80 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xff,0xc4,0x00,0x14,0x10,0x01,0x00,0x00,
81 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xc4,
82 0x00,0x14,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
83 0x00,0x00,0x00,0x00,0xff,0xc4,0x00,0x14,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
84 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xda,0x00,0x0c,0x03,0x01,
85 0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xb2,0xc0,0x07,0xff,0xd9
86 };
87
88 /* 1x1 pixel png */
89 static const unsigned char pngimage[285] = {
90 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
91 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53,
92 0xde,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,0x13,0x00,0x00,0x0b,
93 0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4d,0x45,0x07,0xd5,
94 0x06,0x03,0x0f,0x07,0x2d,0x12,0x10,0xf0,0xfd,0x00,0x00,0x00,0x0c,0x49,0x44,0x41,
95 0x54,0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59,
96 0xe7,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
97 };
98
99 /* 1x1 pixel bmp */
100 static const unsigned char bmpimage[66] = {
101 0x42,0x4d,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x28,0x00,
102 0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,
103 0x00,0x00,0x04,0x00,0x00,0x00,0x12,0x0b,0x00,0x00,0x12,0x0b,0x00,0x00,0x02,0x00,
104 0x00,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0x00,
105 0x00,0x00
106 };
107
108 /* 2x2 pixel gif */
109 static const unsigned char gif4pixel[42] = {
110 0x47,0x49,0x46,0x38,0x37,0x61,0x02,0x00,0x02,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,
111 0x39,0x62,0xfc,0xff,0x1a,0xe5,0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x02,0x00,
112 0x02,0x00,0x00,0x02,0x03,0x14,0x16,0x05,0x00,0x3b
113 };
114
115 /* APM with an empty metafile with some padding zeros - looks like under Window the
116  * metafile data should be at least 20 bytes */
117 static const unsigned char apmdata[] = {
118 0xd7,0xcd,0xc6,0x9a, 0x00,0x00,0x00,0x00, 0x00,0x00,0xee,0x02, 0xb1,0x03,0xa0,0x05,
119 0x00,0x00,0x00,0x00, 0xee,0x53,0x01,0x00, 0x09,0x00,0x00,0x03, 0x13,0x00,0x00,0x00,
120 0x01,0x00,0x05,0x00, 0x00,0x00,0x00,0x00, 0x03,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
121 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00
122 };
123
124 /* MF_TEXTOUT_ON_PATH_BITS from gdi32/tests/metafile.c */
125 static const unsigned char metafile[] = {
126     0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x19, 0x00,
127     0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
128     0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x32, 0x0a,
129     0x16, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x00,
130     0x54, 0x65, 0x73, 0x74, 0x03, 0x00, 0x05, 0x00,
131     0x08, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00,
132     0x00, 0x00
133 };
134
135 /* EMF_TEXTOUT_ON_PATH_BITS from gdi32/tests/metafile.c */
136 static const unsigned char enhmetafile[] = {
137     0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
138     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
140     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141     0xe7, 0xff, 0xff, 0xff, 0xe9, 0xff, 0xff, 0xff,
142     0x20, 0x45, 0x4d, 0x46, 0x00, 0x00, 0x01, 0x00,
143     0xf4, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
144     0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146     0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
147     0x40, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
148     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149     0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x04, 0x00,
150     0x80, 0xa9, 0x03, 0x00, 0x3b, 0x00, 0x00, 0x00,
151     0x08, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
152     0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153     0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
154     0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
155     0x00, 0x00, 0xc8, 0x41, 0x00, 0x80, 0xbb, 0x41,
156     0x0b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
157     0x04, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
158     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159     0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
160     0xff, 0xff, 0xff, 0xff, 0x54, 0x00, 0x00, 0x00,
161     0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
162     0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
163     0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
164     0x3c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
165     0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
166     0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
167     0x14, 0x00, 0x00, 0x00
168 };
169
170
171 struct NoStatStreamImpl
172 {
173         const IStreamVtbl       *lpVtbl;   
174         LONG                    ref;
175
176         HGLOBAL                 supportHandle;
177         ULARGE_INTEGER          streamSize;
178         ULARGE_INTEGER          currentPosition;
179 };
180 typedef struct NoStatStreamImpl NoStatStreamImpl;
181 static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal);
182
183 static void
184 test_pic_with_stream(LPSTREAM stream, unsigned int imgsize)
185 {
186         IPicture*       pic = NULL;
187         HRESULT         hres;
188         LPVOID          pvObj = NULL;
189         OLE_HANDLE      handle, hPal;
190         OLE_XSIZE_HIMETRIC      width;
191         OLE_YSIZE_HIMETRIC      height;
192         short           type;
193         DWORD           attr;
194         ULONG           res;
195
196         pvObj = NULL;
197         hres = pOleLoadPicture(stream, imgsize, TRUE, &IID_IPicture, &pvObj);
198         pic = pvObj;
199
200         ok(hres == S_OK,"OLP (NULL,..) does not return 0, but 0x%08x\n",hres);
201         ok(pic != NULL,"OLP (NULL,..) returns NULL, instead of !NULL\n");
202         if (pic == NULL)
203                 return;
204
205         pvObj = NULL;
206         hres = IPicture_QueryInterface (pic, &IID_IPicture, &pvObj);
207
208         ok(hres == S_OK,"IPicture_QI does not return S_OK, but 0x%08x\n", hres);
209         ok(pvObj != NULL,"IPicture_QI does return NULL, instead of a ptr\n");
210
211         IPicture_Release ((IPicture*)pvObj);
212
213         handle = 0;
214         hres = IPicture_get_Handle (pic, &handle);
215         ok(hres == S_OK,"IPicture_get_Handle does not return S_OK, but 0x%08x\n", hres);
216         ok(handle != 0, "IPicture_get_Handle returns a NULL handle, but it should be non NULL\n");
217
218         width = 0;
219         hres = IPicture_get_Width (pic, &width);
220         ok(hres == S_OK,"IPicture_get_Width does not return S_OK, but 0x%08x\n", hres);
221         ok(width != 0, "IPicture_get_Width returns 0, but it should not be 0.\n");
222
223         height = 0;
224         hres = IPicture_get_Height (pic, &height);
225         ok(hres == S_OK,"IPicture_get_Height does not return S_OK, but 0x%08x\n", hres);
226         ok(height != 0, "IPicture_get_Height returns 0, but it should not be 0.\n");
227
228         type = 0;
229         hres = IPicture_get_Type (pic, &type);
230         ok(hres == S_OK,"IPicture_get_Type does not return S_OK, but 0x%08x\n", hres);
231         ok(type == PICTYPE_BITMAP, "IPicture_get_Type returns %d, but it should be PICTYPE_BITMAP(%d).\n", type, PICTYPE_BITMAP);
232
233         attr = 0;
234         hres = IPicture_get_Attributes (pic, &attr);
235         ok(hres == S_OK,"IPicture_get_Attributes does not return S_OK, but 0x%08x\n", hres);
236         ok(attr == 0, "IPicture_get_Attributes returns %d, but it should be 0.\n", attr);
237
238         hPal = 0;
239         hres = IPicture_get_hPal (pic, &hPal);
240         ok(hres == S_OK,"IPicture_get_hPal does not return S_OK, but 0x%08x\n", hres);
241         /* a single pixel b/w image has no palette */
242         ok(hPal == 0, "IPicture_get_hPal returns %d, but it should be 0.\n", hPal);
243
244         res = IPicture_Release (pic);
245         ok (res == 0, "refcount after release is %d, but should be 0?\n", res);
246 }
247
248 static void
249 test_pic(const unsigned char *imgdata, unsigned int imgsize)
250 {
251         LPSTREAM        stream;
252         HGLOBAL         hglob;
253         LPBYTE          data;
254         HRESULT         hres;
255         LARGE_INTEGER   seekto;
256         ULARGE_INTEGER  newpos1;
257         DWORD *         header;
258         unsigned int    i,j;
259
260         /* Let the fun begin */
261         hglob = GlobalAlloc (0, imgsize);
262         data = GlobalLock (hglob);
263         memcpy(data, imgdata, imgsize);
264         GlobalUnlock(hglob); data = NULL;
265
266         hres = CreateStreamOnHGlobal (hglob, FALSE, &stream);
267         ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08x\n", hres);
268
269         memset(&seekto,0,sizeof(seekto));
270         hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
271         ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
272         test_pic_with_stream(stream, imgsize);
273         
274         IStream_Release(stream);
275
276         /* again with Non Statable and Non Seekable stream */
277         stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob);
278         hglob = 0;  /* Non-statable impl always deletes on release */
279         test_pic_with_stream(stream, 0);
280
281         IStream_Release(stream);
282         for (i = 1; i <= 8; i++) {
283                 /* more fun!!! */
284                 hglob = GlobalAlloc (0, imgsize + i * (2 * sizeof(DWORD)));
285                 data = GlobalLock (hglob);
286                 header = (DWORD *)data;
287
288                 /* multiple copies of header */
289                 memcpy(data,"lt\0\0",4);
290                 header[1] = imgsize;
291                 for (j = 2; j <= i; j++) {
292                         memcpy(&(header[2 * (j - 1)]), header, 2 * sizeof(DWORD));
293                 }
294                 memcpy(data + i * (2 * sizeof(DWORD)), imgdata, imgsize);
295                 GlobalUnlock(hglob); data = NULL;
296
297                 hres = CreateStreamOnHGlobal (hglob, FALSE, &stream);
298                 ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08x\n", hres);
299
300                 memset(&seekto,0,sizeof(seekto));
301                 hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
302                 ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
303                 test_pic_with_stream(stream, imgsize);
304         
305                 IStream_Release(stream);
306
307                 /* again with Non Statable and Non Seekable stream */
308                 stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob);
309                 hglob = 0;  /* Non-statable impl always deletes on release */
310                 test_pic_with_stream(stream, 0);
311
312                 IStream_Release(stream);
313         }
314 }
315
316 static void test_empty_image(void) {
317         LPBYTE          data;
318         LPSTREAM        stream;
319         IPicture*       pic = NULL;
320         HRESULT         hres;
321         LPVOID          pvObj = NULL;
322         HGLOBAL         hglob;
323         OLE_HANDLE      handle;
324         ULARGE_INTEGER  newpos1;
325         LARGE_INTEGER   seekto;
326         short           type;
327
328         /* Empty image. Happens occasionally in VB programs. */
329         hglob = GlobalAlloc (0, 8);
330         data = GlobalLock (hglob);
331         memcpy(data,"lt\0\0",4);
332         ((DWORD*)data)[1] = 0;
333         hres = CreateStreamOnHGlobal (hglob, TRUE, &stream);
334         ok (hres == S_OK, "CreatestreamOnHGlobal failed? doubt it... hres 0x%08x\n", hres);
335
336         memset(&seekto,0,sizeof(seekto));
337         hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
338         ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
339
340         pvObj = NULL;
341         hres = pOleLoadPicture(stream, 8, TRUE, &IID_IPicture, &pvObj);
342         pic = pvObj;
343         ok(hres == S_OK,"empty picture not loaded, hres 0x%08x\n", hres);
344         ok(pic != NULL,"empty picture not loaded, pic is NULL\n");
345
346         hres = IPicture_get_Type (pic, &type);
347         ok (hres == S_OK,"empty picture get type failed with hres 0x%08x\n", hres);
348         ok (type == PICTYPE_NONE,"type is %d, but should be PICTYPE_NONE(0)\n", type);
349
350         hres = IPicture_get_Handle (pic, &handle);
351         ok (hres == S_OK,"empty picture get handle failed with hres 0x%08x\n", hres);
352         ok (handle == 0, "empty picture get handle did not return 0, but 0x%08x\n", handle);
353         IPicture_Release (pic);
354 }
355
356 static void test_empty_image_2(void) {
357         LPBYTE          data;
358         LPSTREAM        stream;
359         IPicture*       pic = NULL;
360         HRESULT         hres;
361         LPVOID          pvObj = NULL;
362         HGLOBAL         hglob;
363         ULARGE_INTEGER  newpos1;
364         LARGE_INTEGER   seekto;
365         short           type;
366
367         /* Empty image at random stream position. */
368         hglob = GlobalAlloc (0, 200);
369         data = GlobalLock (hglob);
370         data += 42;
371         memcpy(data,"lt\0\0",4);
372         ((DWORD*)data)[1] = 0;
373         hres = CreateStreamOnHGlobal (hglob, TRUE, &stream);
374         ok (hres == S_OK, "CreatestreamOnHGlobal failed? doubt it... hres 0x%08x\n", hres);
375
376         memset(&seekto,0,sizeof(seekto));
377         seekto.u.LowPart = 42;
378         hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
379         ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
380
381         pvObj = NULL;
382         hres = pOleLoadPicture(stream, 8, TRUE, &IID_IPicture, &pvObj);
383         pic = pvObj;
384         ok(hres == S_OK,"empty picture not loaded, hres 0x%08x\n", hres);
385         ok(pic != NULL,"empty picture not loaded, pic is NULL\n");
386
387         hres = IPicture_get_Type (pic, &type);
388         ok (hres == S_OK,"empty picture get type failed with hres 0x%08x\n", hres);
389         ok (type == PICTYPE_NONE,"type is %d, but should be PICTYPE_NONE(0)\n", type);
390
391         IPicture_Release (pic);
392 }
393
394 static void test_Invoke(void)
395 {
396     IPictureDisp *picdisp;
397     HRESULT hr;
398     VARIANTARG vararg;
399     DISPPARAMS dispparams;
400     VARIANT varresult;
401     IStream *stream;
402     HGLOBAL hglob;
403     void *data;
404
405         hglob = GlobalAlloc (0, sizeof(gifimage));
406         data = GlobalLock(hglob);
407         memcpy(data, gifimage, sizeof(gifimage));
408     GlobalUnlock(hglob);
409
410         hr = CreateStreamOnHGlobal (hglob, FALSE, &stream);
411     ok_ole_success(hr, "CreateStreamOnHGlobal");
412
413         hr = pOleLoadPicture(stream, sizeof(gifimage), TRUE, &IID_IPictureDisp, (void **)&picdisp);
414     IStream_Release(stream);
415     ok_ole_success(hr, "OleLoadPicture");
416
417     V_VT(&vararg) = VT_BOOL;
418     V_BOOL(&vararg) = VARIANT_FALSE;
419     dispparams.cNamedArgs = 0;
420     dispparams.rgdispidNamedArgs = NULL;
421     dispparams.cArgs = 1;
422     dispparams.rgvarg = &vararg;
423     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_IPictureDisp, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
424     ok(hr == DISP_E_UNKNOWNNAME, "IPictureDisp_Invoke should have returned DISP_E_UNKNOWNNAME instead of 0x%08x\n", hr);
425     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_IUnknown, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
426     ok(hr == DISP_E_UNKNOWNNAME, "IPictureDisp_Invoke should have returned DISP_E_UNKNOWNNAME instead of 0x%08x\n", hr);
427
428     dispparams.cArgs = 0;
429     dispparams.rgvarg = NULL;
430     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
431     ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr);
432
433     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYPUT, NULL, NULL, NULL, NULL);
434     ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr);
435
436     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, NULL, NULL, NULL);
437     ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr);
438
439     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, &varresult, NULL, NULL);
440     ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr);
441
442     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
443     ok_ole_success(hr, "IPictureDisp_Invoke");
444     ok(V_VT(&varresult) == VT_I4, "V_VT(&varresult) should have been VT_UINT instead of %d\n", V_VT(&varresult));
445
446     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL);
447     ok(hr == DISP_E_MEMBERNOTFOUND, "IPictureDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr);
448
449     hr = IPictureDisp_Invoke(picdisp, 0xdeadbeef, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
450     ok(hr == DISP_E_MEMBERNOTFOUND, "IPictureDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr);
451
452     dispparams.cArgs = 1;
453     dispparams.rgvarg = &vararg;
454     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
455     ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr);
456
457     dispparams.cArgs = 1;
458     dispparams.rgvarg = &vararg;
459     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
460     ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr);
461
462     IPictureDisp_Release(picdisp);
463 }
464
465 static void test_OleCreatePictureIndirect(void)
466 {
467     IPicture *pict;
468     HRESULT hr;
469     short type;
470     OLE_HANDLE handle;
471
472     if(!pOleCreatePictureIndirect)
473     {
474         skip("Skipping OleCreatePictureIndirect tests\n");
475         return;
476     }
477
478     hr = pOleCreatePictureIndirect(NULL, &IID_IPicture, TRUE, (void**)&pict);
479     ok(hr == S_OK, "hr %08x\n", hr);
480
481     hr = IPicture_get_Type(pict, &type);
482     ok(hr == S_OK, "hr %08x\n", hr);
483     ok(type == PICTYPE_UNINITIALIZED, "type %d\n", type);
484
485     hr = IPicture_get_Handle(pict, &handle);
486     ok(hr == S_OK, "hr %08x\n", hr);
487     ok(handle == 0, "handle %08x\n", handle);
488
489     IPicture_Release(pict);
490 }
491
492 static void test_apm()
493 {
494     OLE_HANDLE handle;
495     LPSTREAM stream;
496     IPicture *pict;
497     HGLOBAL hglob;
498     LPBYTE *data;
499     LONG cxy;
500     BOOL keep;
501     short type;
502
503     hglob = GlobalAlloc (0, sizeof(apmdata));
504     data = GlobalLock(hglob);
505     memcpy(data, apmdata, sizeof(apmdata));
506
507     ole_check(CreateStreamOnHGlobal(hglob, TRUE, &stream));
508     ole_check(OleLoadPictureEx(stream, sizeof(apmdata), TRUE, &IID_IPicture, 100, 100, 0, (LPVOID *)&pict));
509
510     ole_check(IPicture_get_Handle(pict, &handle));
511     ok(handle != 0, "handle is null\n");
512
513     ole_check(IPicture_get_Type(pict, &type));
514     expect_eq(type, PICTYPE_METAFILE, short, "%d");
515
516     ole_check(IPicture_get_Height(pict, &cxy));
517     expect_eq(cxy,  1667, LONG, "%d");
518
519     ole_check(IPicture_get_Width(pict, &cxy));
520     expect_eq(cxy,  1323, LONG, "%d");
521
522     ole_check(IPicture_get_KeepOriginalFormat(pict, &keep));
523     todo_wine expect_eq(keep, FALSE, LONG, "%d");
524
525     ole_expect(IPicture_get_hPal(pict, &handle), E_FAIL);
526     IPicture_Release(pict);
527     IStream_Release(stream);
528     GlobalUnlock(hglob);
529     GlobalFree(hglob);
530 }
531
532 static void test_metafile(void)
533 {
534     LPSTREAM stream;
535     IPicture *pict;
536     HGLOBAL hglob;
537     LPBYTE *data;
538
539     hglob = GlobalAlloc (0, sizeof(metafile));
540     data = GlobalLock(hglob);
541     memcpy(data, metafile, sizeof(metafile));
542
543     ole_check(CreateStreamOnHGlobal(hglob, TRUE, &stream));
544     /* Windows does not load simple metafiles */
545     ole_expect(OleLoadPictureEx(stream, sizeof(metafile), TRUE, &IID_IPicture, 100, 100, 0, (LPVOID *)&pict), E_FAIL);
546
547     IStream_Release(stream);
548     GlobalUnlock(hglob);
549     GlobalFree(hglob);
550 }
551
552 static void test_enhmetafile(void)
553 {
554     OLE_HANDLE handle;
555     LPSTREAM stream;
556     IPicture *pict;
557     HGLOBAL hglob;
558     LPBYTE *data;
559     LONG cxy;
560     BOOL keep;
561     short type;
562
563     hglob = GlobalAlloc (0, sizeof(enhmetafile));
564     data = GlobalLock(hglob);
565     memcpy(data, enhmetafile, sizeof(enhmetafile));
566
567     ole_check(CreateStreamOnHGlobal(hglob, TRUE, &stream));
568     ole_check(OleLoadPictureEx(stream, sizeof(enhmetafile), TRUE, &IID_IPicture, 10, 10, 0, (LPVOID *)&pict));
569
570     ole_check(IPicture_get_Handle(pict, &handle));
571     ok(handle != 0, "handle is null\n");
572
573     ole_check(IPicture_get_Type(pict, &type));
574     expect_eq(type, PICTYPE_ENHMETAFILE, short, "%d");
575
576     ole_check(IPicture_get_Height(pict, &cxy));
577     expect_eq(cxy, -23, LONG, "%d");
578
579     ole_check(IPicture_get_Width(pict, &cxy));
580     expect_eq(cxy, -25, LONG, "%d");
581
582     ole_check(IPicture_get_KeepOriginalFormat(pict, &keep));
583     todo_wine expect_eq(keep, FALSE, LONG, "%d");
584
585     IPicture_Release(pict);
586     IStream_Release(stream);
587     GlobalUnlock(hglob);
588     GlobalFree(hglob);
589 }
590
591 START_TEST(olepicture)
592 {
593         hOleaut32 = GetModuleHandleA("oleaut32.dll");
594         pOleLoadPicture = (void*)GetProcAddress(hOleaut32, "OleLoadPicture");
595         pOleCreatePictureIndirect = (void*)GetProcAddress(hOleaut32, "OleCreatePictureIndirect");
596         if (!pOleLoadPicture)
597         {
598             skip("OleLoadPicture is not available\n");
599             return;
600         }
601
602         /* Test regular 1x1 pixel images of gif, jpg, bmp type */
603         test_pic(gifimage, sizeof(gifimage));
604         test_pic(jpgimage, sizeof(jpgimage));
605         test_pic(bmpimage, sizeof(bmpimage));
606         test_pic(gif4pixel, sizeof(gif4pixel));
607         /* FIXME: No PNG support yet in Wine or in older Windows... */
608         if (0) test_pic(pngimage, sizeof(pngimage));
609         test_empty_image();
610         test_empty_image_2();
611         test_apm();
612         test_metafile();
613         test_enhmetafile();
614
615         test_Invoke();
616         test_OleCreatePictureIndirect();
617 }
618
619
620 /* Helper functions only ... */
621
622
623 static void NoStatStreamImpl_Destroy(NoStatStreamImpl* This)
624 {
625   GlobalFree(This->supportHandle);
626   This->supportHandle=0;
627   HeapFree(GetProcessHeap(), 0, This);
628 }
629
630 static ULONG WINAPI NoStatStreamImpl_AddRef(
631                 IStream* iface)
632 {
633   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
634   return InterlockedIncrement(&This->ref);
635 }
636
637 static HRESULT WINAPI NoStatStreamImpl_QueryInterface(
638                   IStream*     iface,
639                   REFIID         riid,        /* [in] */
640                   void**         ppvObject)   /* [iid_is][out] */
641 {
642   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
643   if (ppvObject==0) return E_INVALIDARG;
644   *ppvObject = 0;
645   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
646   {
647     *ppvObject = (IStream*)This;
648   }
649   else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
650   {
651     *ppvObject = (IStream*)This;
652   }
653
654   if ((*ppvObject)==0)
655     return E_NOINTERFACE;
656   NoStatStreamImpl_AddRef(iface);
657   return S_OK;
658 }
659
660 static ULONG WINAPI NoStatStreamImpl_Release(
661                 IStream* iface)
662 {
663   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
664   ULONG newRef = InterlockedDecrement(&This->ref);
665   if (newRef==0)
666     NoStatStreamImpl_Destroy(This);
667   return newRef;
668 }
669
670 static HRESULT WINAPI NoStatStreamImpl_Read(
671                   IStream*     iface,
672                   void*          pv,        /* [length_is][size_is][out] */
673                   ULONG          cb,        /* [in] */
674                   ULONG*         pcbRead)   /* [out] */
675 {
676   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
677   void* supportBuffer;
678   ULONG bytesReadBuffer;
679   ULONG bytesToReadFromBuffer;
680
681   if (pcbRead==0)
682     pcbRead = &bytesReadBuffer;
683   bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
684   supportBuffer = GlobalLock(This->supportHandle);
685   memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
686   This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
687   *pcbRead = bytesToReadFromBuffer;
688   GlobalUnlock(This->supportHandle);
689   if(*pcbRead == cb)
690     return S_OK;
691   return S_FALSE;
692 }
693
694 static HRESULT WINAPI NoStatStreamImpl_Write(
695                   IStream*     iface,
696                   const void*    pv,          /* [size_is][in] */
697                   ULONG          cb,          /* [in] */
698                   ULONG*         pcbWritten)  /* [out] */
699 {
700   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
701   void*          supportBuffer;
702   ULARGE_INTEGER newSize;
703   ULONG          bytesWritten = 0;
704
705   if (pcbWritten == 0)
706     pcbWritten = &bytesWritten;
707   if (cb == 0)
708     return S_OK;
709   newSize.u.HighPart = 0;
710   newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
711   if (newSize.u.LowPart > This->streamSize.u.LowPart)
712    IStream_SetSize(iface, newSize);
713
714   supportBuffer = GlobalLock(This->supportHandle);
715   memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
716   This->currentPosition.u.LowPart+=cb;
717   *pcbWritten = cb;
718   GlobalUnlock(This->supportHandle);
719   return S_OK;
720 }
721
722 static HRESULT WINAPI NoStatStreamImpl_Seek(
723                   IStream*      iface,
724                   LARGE_INTEGER   dlibMove,         /* [in] */
725                   DWORD           dwOrigin,         /* [in] */
726                   ULARGE_INTEGER* plibNewPosition) /* [out] */
727 {
728   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
729   ULARGE_INTEGER newPosition;
730   switch (dwOrigin)
731   {
732     case STREAM_SEEK_SET:
733       newPosition.u.HighPart = 0;
734       newPosition.u.LowPart = 0;
735       break;
736     case STREAM_SEEK_CUR:
737       newPosition = This->currentPosition;
738       break;
739     case STREAM_SEEK_END:
740       newPosition = This->streamSize;
741       break;
742     default:
743       return STG_E_INVALIDFUNCTION;
744   }
745   if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart)
746       return STG_E_INVALIDFUNCTION;
747   newPosition.QuadPart += dlibMove.QuadPart;
748   if (plibNewPosition) *plibNewPosition = newPosition;
749   This->currentPosition = newPosition;
750   return S_OK;
751 }
752
753 static HRESULT WINAPI NoStatStreamImpl_SetSize(
754                                      IStream*      iface,
755                                      ULARGE_INTEGER  libNewSize)   /* [in] */
756 {
757   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
758   HGLOBAL supportHandle;
759   if (libNewSize.u.HighPart != 0)
760     return STG_E_INVALIDFUNCTION;
761   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
762     return S_OK;
763   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
764   if (supportHandle == 0)
765     return STG_E_MEDIUMFULL;
766   This->supportHandle = supportHandle;
767   This->streamSize.u.LowPart = libNewSize.u.LowPart;
768   return S_OK;
769 }
770
771 static HRESULT WINAPI NoStatStreamImpl_CopyTo(
772                                     IStream*      iface,
773                                     IStream*      pstm,         /* [unique][in] */
774                                     ULARGE_INTEGER  cb,           /* [in] */
775                                     ULARGE_INTEGER* pcbRead,      /* [out] */
776                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
777 {
778   HRESULT        hr = S_OK;
779   BYTE           tmpBuffer[128];
780   ULONG          bytesRead, bytesWritten, copySize;
781   ULARGE_INTEGER totalBytesRead;
782   ULARGE_INTEGER totalBytesWritten;
783
784   if ( pstm == 0 )
785     return STG_E_INVALIDPOINTER;
786   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
787   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
788
789   while ( cb.u.LowPart > 0 )
790   {
791     if ( cb.u.LowPart >= 128 )
792       copySize = 128;
793     else
794       copySize = cb.u.LowPart;
795     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
796     totalBytesRead.u.LowPart += bytesRead;
797     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
798     totalBytesWritten.u.LowPart += bytesWritten;
799     if (bytesRead != bytesWritten)
800     {
801       hr = STG_E_MEDIUMFULL;
802       break;
803     }
804     if (bytesRead!=copySize)
805       cb.u.LowPart = 0;
806     else
807       cb.u.LowPart -= bytesRead;
808   }
809   if (pcbRead)
810   {
811     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
812     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
813   }
814
815   if (pcbWritten)
816   {
817     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
818     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
819   }
820   return hr;
821 }
822
823 static HRESULT WINAPI NoStatStreamImpl_Commit(IStream* iface,DWORD grfCommitFlags)
824 {
825   return S_OK;
826 }
827 static HRESULT WINAPI NoStatStreamImpl_Revert(IStream* iface) { return S_OK; }
828
829 static HRESULT WINAPI NoStatStreamImpl_LockRegion(
830                   IStream*       iface,
831                   ULARGE_INTEGER libOffset,   /* [in] */
832                   ULARGE_INTEGER cb,          /* [in] */
833                   DWORD          dwLockType)  /* [in] */
834 {
835   return S_OK;
836 }
837
838 static HRESULT WINAPI NoStatStreamImpl_UnlockRegion(
839                   IStream*       iface,
840                   ULARGE_INTEGER libOffset,   /* [in] */
841                   ULARGE_INTEGER cb,          /* [in] */
842                   DWORD          dwLockType)  /* [in] */
843 {
844   return S_OK;
845 }
846
847 static HRESULT WINAPI NoStatStreamImpl_Stat(
848                   IStream*     iface,
849                   STATSTG*     pstatstg,     /* [out] */
850                   DWORD        grfStatFlag)  /* [in] */
851 {
852   return E_NOTIMPL;
853 }
854
855 static HRESULT WINAPI NoStatStreamImpl_Clone(
856                   IStream*     iface,
857                   IStream**    ppstm) /* [out] */
858 {
859   return E_NOTIMPL;
860 }
861 static const IStreamVtbl NoStatStreamImpl_Vtbl;
862
863 /*
864     Build an object that implements IStream, without IStream_Stat capabilities.
865     Receives a memory handle with data buffer. If memory handle is non-null,
866     it is assumed to be unlocked, otherwise an internal memory handle is allocated.
867     In any case the object takes ownership of memory handle and will free it on
868     object release.
869  */
870 static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal)
871 {
872   NoStatStreamImpl* newStream;
873
874   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(NoStatStreamImpl));
875   if (newStream!=0)
876   {
877     newStream->lpVtbl = &NoStatStreamImpl_Vtbl;
878     newStream->ref    = 1;
879     newStream->supportHandle = hGlobal;
880
881     if (!newStream->supportHandle)
882       newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
883                                              GMEM_SHARE, 0);
884     newStream->currentPosition.u.HighPart = 0;
885     newStream->currentPosition.u.LowPart = 0;
886     newStream->streamSize.u.HighPart = 0;
887     newStream->streamSize.u.LowPart  = GlobalSize(newStream->supportHandle);
888   }
889   return newStream;
890 }
891
892
893 static const IStreamVtbl NoStatStreamImpl_Vtbl =
894 {
895     NoStatStreamImpl_QueryInterface,
896     NoStatStreamImpl_AddRef,
897     NoStatStreamImpl_Release,
898     NoStatStreamImpl_Read,
899     NoStatStreamImpl_Write,
900     NoStatStreamImpl_Seek,
901     NoStatStreamImpl_SetSize,
902     NoStatStreamImpl_CopyTo,
903     NoStatStreamImpl_Commit,
904     NoStatStreamImpl_Revert,
905     NoStatStreamImpl_LockRegion,
906     NoStatStreamImpl_UnlockRegion,
907     NoStatStreamImpl_Stat,
908     NoStatStreamImpl_Clone
909 };