Added support of loading of IPicture data from non-statable

IStreams. Added testcases for this.
This commit is contained in:
Marcus Meissner 2005-11-07 11:09:10 +00:00 committed by Alexandre Julliard
parent a533bd3056
commit c49f5f0095
2 changed files with 401 additions and 61 deletions

View File

@ -1056,6 +1056,7 @@ static int _gif_inputfunc(GifFileType *gif, GifByteType *data, int len) {
static HRESULT WINAPI OLEPictureImpl_Load(IPersistStream* iface,IStream*pStm) {
HRESULT hr = E_FAIL;
BOOL headerisdata = FALSE;
BOOL statfailed = FALSE;
ULONG xread, toread;
BYTE *xbuf;
DWORD header[2];
@ -1065,19 +1066,29 @@ static HRESULT WINAPI OLEPictureImpl_Load(IPersistStream* iface,IStream*pStm) {
TRACE("(%p,%p)\n",This,pStm);
/****************************************************************************************
* Part 1: Load the data
*/
/* Sometimes we have a header, sometimes we don't. Apply some guesses to find
* out whether we do.
*
* UPDATE: the IStream can be mapped to a plain file instead of a stream in a
* compound file. This may explain most, if not all, of the cases of "no header",
* and the header validation should take this into account. At least in Visual Basic 6,
* resource streams, valid headers are
* compound file. This may explain most, if not all, of the cases of "no
* header", and the header validation should take this into account.
* At least in Visual Basic 6, resource streams, valid headers are
* header[0] == "lt\0\0",
* header[1] == length_of_stream.
*
* Also handle streams where we do not have a working "Stat" method by
* reading all data until the end of the stream.
*/
hr=IStream_Stat(pStm,&statstg,STATFLAG_NONAME);
if (hr)
FIXME("Stat failed with hres %lx\n",hr);
if (hr) {
TRACE("stat failed with hres %lx, proceeding to read all data.\n",hr);
statfailed = TRUE;
/* we will read at least 8 byte ... just right below */
statstg.cbSize.QuadPart = 8;
}
hr=IStream_Read(pStm,header,8,&xread);
if (hr || xread!=8) {
FIXME("Failure while reading picture header (hr is %lx, nread is %ld).\n",hr,xread);
@ -1089,42 +1100,77 @@ static HRESULT WINAPI OLEPictureImpl_Load(IPersistStream* iface,IStream*pStm) {
if (!memcmp(&(header[0]),"lt\0\0", 4) && (header[1] <= statstg.cbSize.QuadPart-8)) {
toread = header[1];
} else {
if (!memcmp(&(header[0]), "GIF8", 4) || /* GIF header */
!memcmp(&(header[0]), "BM", 2) || /* BMP header */
!memcmp(&(header[0]), "\xff\xd8", 2) || /* JPEG header */
(header[1] > statstg.cbSize.QuadPart) || /* invalid size */
(header[1]==0)
if (!memcmp(&(header[0]), "GIF8", 4) || /* GIF header */
!memcmp(&(header[0]), "BM", 2) || /* BMP header */
!memcmp(&(header[0]), "\xff\xd8", 2) || /* JPEG header */
(header[1] > statstg.cbSize.QuadPart)|| /* invalid size */
(header[1]==0)
) {/* Incorrect header, assume none. */
headerisdata = TRUE;
toread = statstg.cbSize.QuadPart-8;
xread = 8;
xread = 8;
} else {
FIXME("Unknown stream header magic: %08lx\n", header[0]);
FIXME("Unknown stream header magic: %08lx\n", header[0]);
toread = header[1];
}
}
This->datalen = toread+(headerisdata?8:0);
xbuf = This->data = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, This->datalen);
if (statfailed) { /* we dont know the size ... read all we get */
int sizeinc = 4096;
int origsize = sizeinc;
ULONG nread = 42;
if (headerisdata)
memcpy (xbuf, &header, 8);
TRACE("Reading all data from stream.\n");
xbuf = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, origsize);
if (headerisdata)
memcpy (xbuf, &header, 8);
while (1) {
while (xread < origsize) {
hr = IStream_Read(pStm,xbuf+xread,origsize-xread,&nread);
xread+=nread;
if (hr || !nread)
break;
}
if (!nread || hr) /* done, or error */
break;
if (xread == origsize) {
origsize += sizeinc;
sizeinc = 2*sizeinc; /* exponential increase */
xbuf = HeapReAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, xbuf, origsize);
}
}
if (hr)
TRACE("hr in no-stat loader case is %08lx\n", hr);
TRACE("loaded %ld bytes.\n", xread);
This->datalen = xread;
This->data = xbuf;
} else {
This->datalen = toread+(headerisdata?8:0);
xbuf = This->data = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, This->datalen);
while (xread < This->datalen) {
ULONG nread;
hr = IStream_Read(pStm,xbuf+xread,This->datalen-xread,&nread);
xread+=nread;
if (hr || !nread)
break;
if (headerisdata)
memcpy (xbuf, &header, 8);
while (xread < This->datalen) {
ULONG nread;
hr = IStream_Read(pStm,xbuf+xread,This->datalen-xread,&nread);
xread+=nread;
if (hr || !nread)
break;
}
if (xread != This->datalen)
FIXME("Could only read %ld of %d bytes out of stream?\n",xread,This->datalen);
}
if (xread != This->datalen)
FIXME("Could only read %ld of %d bytes out of stream?\n",xread,This->datalen);
if (This->datalen == 0) { /* Marks the "NONE" picture */
if (This->datalen == 0) { /* Marks the "NONE" picture */
This->desc.picType = PICTYPE_NONE;
return S_OK;
}
/****************************************************************************************
* Part 2: Process the loaded data
*/
magic = xbuf[0] + (xbuf[1]<<8);
switch (magic) {
case 0x4947: { /* GIF */

View File

@ -102,17 +102,24 @@ static const unsigned char gif4pixel[42] = {
0x02,0x00,0x00,0x02,0x03,0x14,0x16,0x05,0x00,0x3b
};
static void
test_pic(const unsigned char *imgdata, unsigned int imgsize)
struct NoStatStreamImpl
{
const IStreamVtbl *lpVtbl;
LONG ref;
HGLOBAL supportHandle;
ULARGE_INTEGER streamSize;
ULARGE_INTEGER currentPosition;
};
typedef struct NoStatStreamImpl NoStatStreamImpl;
static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal);
static void
test_pic_with_stream(LPSTREAM stream, unsigned int imgsize)
{
LPBYTE data;
LPSTREAM stream;
IPicture* pic = NULL;
HRESULT hres;
LPVOID pvObj = NULL;
HGLOBAL hglob;
ULARGE_INTEGER newpos1;
LARGE_INTEGER seekto;
OLE_HANDLE handle, hPal;
OLE_XSIZE_HIMETRIC width;
OLE_YSIZE_HIMETRIC height;
@ -120,18 +127,6 @@ test_pic(const unsigned char *imgdata, unsigned int imgsize)
DWORD attr;
ULONG res;
/* let the fun begin */
hglob = GlobalAlloc (0, imgsize);
data = GlobalLock (hglob);
memcpy(data, imgdata, imgsize);
hres = CreateStreamOnHGlobal (hglob, TRUE, &stream);
ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08lx\n", hres);
memset(&seekto,0,sizeof(seekto));
hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08lx\n", hres);
pvObj = NULL;
hres = pOleLoadPicture(stream, imgsize, TRUE, &IID_IPicture, &pvObj);
pic = pvObj;
@ -141,20 +136,6 @@ test_pic(const unsigned char *imgdata, unsigned int imgsize)
if (pic == NULL)
return;
#if 0
hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos2);
ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08lx\n", hres);
/* The stream position here is some bytes after the image, in both wine and
* native. But not at the correct end. -Marcus
*/
fprintf(stderr,"newpos1 %ld, newpos2 %ld\n", newpos1.LowPart, newpos2.LowPart);
ok ( newpos2.LowPart == imgsize,
"seeked after end of gifimage (offset %d instead of expected %d)\n",
newpos2.LowPart, imgsize
);
#endif
pvObj = NULL;
hres = IPicture_QueryInterface (pic, &IID_IPicture, &pvObj);
@ -195,7 +176,35 @@ test_pic(const unsigned char *imgdata, unsigned int imgsize)
ok(hPal == 0, "IPicture_get_hPal returns %ld, but it should be 0.\n", (long)hPal);
res = IPicture_Release (pic);
ok (res == 0, "refcount after release is %ld, but should be 1?\n", res);
ok (res == 0, "refcount after release is %ld, but should be 0?\n", res);
}
static void
test_pic(const unsigned char *imgdata, unsigned int imgsize)
{
LPSTREAM stream;
HGLOBAL hglob;
LPBYTE data;
HRESULT hres;
LARGE_INTEGER seekto;
ULARGE_INTEGER newpos1;
/* Let the fun begin */
hglob = GlobalAlloc (0, imgsize);
data = GlobalLock (hglob);
memcpy(data, imgdata, imgsize);
hres = CreateStreamOnHGlobal (hglob, FALSE, &stream);
ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08lx\n", hres);
memset(&seekto,0,sizeof(seekto));
hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08lx\n", hres);
test_pic_with_stream(stream, imgsize);
/* again with Non Statable and Non Seekable stream */
stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob);
test_pic_with_stream(stream, 0);
}
static void test_empty_image(void) {
@ -288,9 +297,294 @@ START_TEST(olepicture)
test_pic(jpgimage, sizeof(jpgimage));
test_pic(bmpimage, sizeof(bmpimage));
test_pic(gif4pixel, sizeof(gif4pixel));
/* no PNG support yet here or in older Windows...
/* No PNG support yet here or in older Windows...
test_pic(pngimage, sizeof(pngimage));
*/
test_empty_image();
test_empty_image_2();
}
/* Helper functions only ... */
static void NoStatStreamImpl_Destroy(NoStatStreamImpl* This)
{
GlobalFree(This->supportHandle);
This->supportHandle=0;
HeapFree(GetProcessHeap(), 0, This);
}
static ULONG WINAPI NoStatStreamImpl_AddRef(
IStream* iface)
{
NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
return InterlockedIncrement(&This->ref);
}
static HRESULT WINAPI NoStatStreamImpl_QueryInterface(
IStream* iface,
REFIID riid, /* [in] */
void** ppvObject) /* [iid_is][out] */
{
NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
if (ppvObject==0) return E_INVALIDARG;
*ppvObject = 0;
if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
{
*ppvObject = (IStream*)This;
}
else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
{
*ppvObject = (IStream*)This;
}
if ((*ppvObject)==0)
return E_NOINTERFACE;
NoStatStreamImpl_AddRef(iface);
return S_OK;
}
static ULONG WINAPI NoStatStreamImpl_Release(
IStream* iface)
{
NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
ULONG newRef = InterlockedDecrement(&This->ref);
if (newRef==0)
NoStatStreamImpl_Destroy(This);
return newRef;
}
static HRESULT WINAPI NoStatStreamImpl_Read(
IStream* iface,
void* pv, /* [length_is][size_is][out] */
ULONG cb, /* [in] */
ULONG* pcbRead) /* [out] */
{
NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
void* supportBuffer;
ULONG bytesReadBuffer;
ULONG bytesToReadFromBuffer;
if (pcbRead==0)
pcbRead = &bytesReadBuffer;
bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
supportBuffer = GlobalLock(This->supportHandle);
memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
*pcbRead = bytesToReadFromBuffer;
GlobalUnlock(This->supportHandle);
if(*pcbRead == cb)
return S_OK;
return S_FALSE;
}
static HRESULT WINAPI NoStatStreamImpl_Write(
IStream* iface,
const void* pv, /* [size_is][in] */
ULONG cb, /* [in] */
ULONG* pcbWritten) /* [out] */
{
NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
void* supportBuffer;
ULARGE_INTEGER newSize;
ULONG bytesWritten = 0;
if (pcbWritten == 0)
pcbWritten = &bytesWritten;
if (cb == 0)
return S_OK;
newSize.u.HighPart = 0;
newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
if (newSize.u.LowPart > This->streamSize.u.LowPart)
IStream_SetSize(iface, newSize);
supportBuffer = GlobalLock(This->supportHandle);
memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
This->currentPosition.u.LowPart+=cb;
*pcbWritten = cb;
GlobalUnlock(This->supportHandle);
return S_OK;
}
static HRESULT WINAPI NoStatStreamImpl_Seek(
IStream* iface,
LARGE_INTEGER dlibMove, /* [in] */
DWORD dwOrigin, /* [in] */
ULARGE_INTEGER* plibNewPosition) /* [out] */
{
NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
ULARGE_INTEGER newPosition;
switch (dwOrigin)
{
case STREAM_SEEK_SET:
newPosition.u.HighPart = 0;
newPosition.u.LowPart = 0;
break;
case STREAM_SEEK_CUR:
newPosition = This->currentPosition;
break;
case STREAM_SEEK_END:
newPosition = This->streamSize;
break;
default:
return STG_E_INVALIDFUNCTION;
}
if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart)
return STG_E_INVALIDFUNCTION;
newPosition.QuadPart += dlibMove.QuadPart;
if (plibNewPosition) *plibNewPosition = newPosition;
This->currentPosition = newPosition;
return S_OK;
}
static HRESULT WINAPI NoStatStreamImpl_SetSize(
IStream* iface,
ULARGE_INTEGER libNewSize) /* [in] */
{
NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
HGLOBAL supportHandle;
if (libNewSize.u.HighPart != 0)
return STG_E_INVALIDFUNCTION;
if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
return S_OK;
supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
if (supportHandle == 0)
return STG_E_MEDIUMFULL;
This->supportHandle = supportHandle;
This->streamSize.u.LowPart = libNewSize.u.LowPart;
return S_OK;
}
static HRESULT WINAPI NoStatStreamImpl_CopyTo(
IStream* iface,
IStream* pstm, /* [unique][in] */
ULARGE_INTEGER cb, /* [in] */
ULARGE_INTEGER* pcbRead, /* [out] */
ULARGE_INTEGER* pcbWritten) /* [out] */
{
HRESULT hr = S_OK;
BYTE tmpBuffer[128];
ULONG bytesRead, bytesWritten, copySize;
ULARGE_INTEGER totalBytesRead;
ULARGE_INTEGER totalBytesWritten;
if ( pstm == 0 )
return STG_E_INVALIDPOINTER;
totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
while ( cb.u.LowPart > 0 )
{
if ( cb.u.LowPart >= 128 )
copySize = 128;
else
copySize = cb.u.LowPart;
IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
totalBytesRead.u.LowPart += bytesRead;
IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
totalBytesWritten.u.LowPart += bytesWritten;
if (bytesRead != bytesWritten)
{
hr = STG_E_MEDIUMFULL;
break;
}
if (bytesRead!=copySize)
cb.u.LowPart = 0;
else
cb.u.LowPart -= bytesRead;
}
if (pcbRead)
{
pcbRead->u.LowPart = totalBytesRead.u.LowPart;
pcbRead->u.HighPart = totalBytesRead.u.HighPart;
}
if (pcbWritten)
{
pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
}
return hr;
}
static HRESULT WINAPI NoStatStreamImpl_Commit(IStream* iface,DWORD grfCommitFlags)
{
return S_OK;
}
static HRESULT WINAPI NoStatStreamImpl_Revert(IStream* iface) { return S_OK; }
static HRESULT WINAPI NoStatStreamImpl_LockRegion(
IStream* iface,
ULARGE_INTEGER libOffset, /* [in] */
ULARGE_INTEGER cb, /* [in] */
DWORD dwLockType) /* [in] */
{
return S_OK;
}
static HRESULT WINAPI NoStatStreamImpl_UnlockRegion(
IStream* iface,
ULARGE_INTEGER libOffset, /* [in] */
ULARGE_INTEGER cb, /* [in] */
DWORD dwLockType) /* [in] */
{
return S_OK;
}
static HRESULT WINAPI NoStatStreamImpl_Stat(
IStream* iface,
STATSTG* pstatstg, /* [out] */
DWORD grfStatFlag) /* [in] */
{
return E_NOTIMPL;
}
static HRESULT WINAPI NoStatStreamImpl_Clone(
IStream* iface,
IStream** ppstm) /* [out] */
{
return E_NOTIMPL;
}
static const IStreamVtbl NoStatStreamImpl_Vtbl;
static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal)
{
NoStatStreamImpl* newStream;
newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(NoStatStreamImpl));
if (newStream!=0)
{
newStream->lpVtbl = &NoStatStreamImpl_Vtbl;
newStream->ref = 0;
newStream->supportHandle = hGlobal;
if (!newStream->supportHandle)
newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
GMEM_SHARE, 0);
newStream->currentPosition.u.HighPart = 0;
newStream->currentPosition.u.LowPart = 0;
newStream->streamSize.u.HighPart = 0;
newStream->streamSize.u.LowPart = GlobalSize(newStream->supportHandle);
}
return newStream;
}
static const IStreamVtbl NoStatStreamImpl_Vtbl =
{
NoStatStreamImpl_QueryInterface,
NoStatStreamImpl_AddRef,
NoStatStreamImpl_Release,
NoStatStreamImpl_Read,
NoStatStreamImpl_Write,
NoStatStreamImpl_Seek,
NoStatStreamImpl_SetSize,
NoStatStreamImpl_CopyTo,
NoStatStreamImpl_Commit,
NoStatStreamImpl_Revert,
NoStatStreamImpl_LockRegion,
NoStatStreamImpl_UnlockRegion,
NoStatStreamImpl_Stat,
NoStatStreamImpl_Clone
};