VC2008 Read data to stream GDI+ - c++

Code to save jpeg in disk:
fwrite( dataPosition, 1, BufferSize, hFileImage );
That code work good.
But something is wrong when I try read data to stream:
HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED, BufferSize);
CComPtr<IStream> spStream;
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &spStream);
ULONG pcbWritten;//don't understand what it is
spStream->Write(dataPosition, BufferSize, &pcbWritten);
pImage = new Image(spStream, FALSE);
After that it seems that stream (and pImage) is empty. I am not sure what I am doing wrong?

After you wrote to the stream, you perhaps should IStream::Seek the stream to its beginning so that following Image constructor could read the data, and not immediately reach end of stream instead.
static const ULONGLONG g_nZero = 0;
HRESULT nSeekResult = pStream->Seek(reinterpret_cast<const LARGE_INTEGER&>(g_nZero),
STREAM_SEEK_SET, NULL);

Related

How to write an image to STDOUT with Wincodec.h (WIC)

I came across this example and couldn't figure out how to change stream->InitializeFromFilename to something that outputs to STDOUT, inside the HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, LPWSTR filePath, const GUID &format) function. Tried using stream->InitializeFromMemory but hit a wall due to minimal C++ experience.
Reasons behind this is that i'm building something and would like to pipe the stdout of the example using something else.
Regards in advance
Thanks to #SimonMourier, I was able to update his code to achieve what was needed.
SavePixelsToFile32bppPBGRA now writes the data to an IStream pointer, streamOut. It also invokes a new function at the end, to output data.
HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, const GUID& format)
{
if (!pixels)
return E_INVALIDARG;
HRESULT hr = S_OK;
IWICImagingFactory* factory = nullptr;
IWICBitmapEncoder* encoder = nullptr;
IWICBitmapFrameEncode* frame = nullptr;
IWICStream* streamOut = nullptr;
IStream * streamIn = nullptr;
GUID pf = GUID_WICPixelFormat32bppPBGRA;
BOOL coInit = CoInitialize(nullptr);
HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)));
HRCHECK(factory->CreateStream(&streamOut));
HRCHECK(CreateStreamOnHGlobal(NULL, true, &streamIn));
HRCHECK(streamOut->InitializeFromIStream(streamIn));
HRCHECK(factory->CreateEncoder(format, nullptr, &encoder));
HRCHECK(encoder->Initialize(streamOut, WICBitmapEncoderNoCache));
HRCHECK(encoder->CreateNewFrame(&frame, nullptr)); // we don't use options here
HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here
HRCHECK(frame->SetSize(width, height));
HRCHECK(frame->SetPixelFormat(&pf));
HRCHECK(frame->WritePixels(height, stride, stride * height, pixels));
HRCHECK(frame->Commit());
HRCHECK(encoder->Commit());
sendIStreamToOutput(streamOut);
cleanup:
RELEASE(streamIn);
RELEASE(streamOut);
RELEASE(frame);
RELEASE(encoder);
RELEASE(factory);
if (coInit) CoUninitialize();
return hr;
}
sendIStreamToOutput reads the data from the IStream and writes it to std::cout (with binary mode active).
void sendIStreamToOutput (IStream * pIStream) {
STATSTG sts;
pIStream->Stat(&sts, STATFLAG_DEFAULT);
ULARGE_INTEGER uli = sts.cbSize;
LARGE_INTEGER zero;
zero.QuadPart = 0;
ULONG size = (ULONG)uli.QuadPart;
char* bits = new char[size];
ULONG written;
pIStream->Seek(zero, STREAM_SEEK_SET, NULL);
pIStream->Read(bits, size, &written);
std::ostream& lhs = std::cout;
_setmode(_fileno(stdout), _O_BINARY);
lhs.write(bits, size);
fflush(stdout);
delete[] bits;
}

Serialize: CArchive a CImage

I am currently trying to figure out how to properly store a CImage file within a CArchive (JPEG). My current approach to this is the following (pseudo) code:
BOOL CPicture::Serialize(CArchive &ar)
{
IStream *pStream = NULL;
HRESULT hr;
CImage *img = GetImage();
if (ar.IsLoading())
{
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, 54262);
hr = CreateStreamOnHGlobal(hMem, FALSE, &pStream);
if(SUCCEEDED(hr))
{
ar.Read(pStream, 54262);
img->Load(pStream);
pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);
}
}
else
{
hr = CreateStreamOnHGlobal(0, TRUE, &pStream);
if (SUCCEEDED(hr))
{
hr = img->Save(pStream, Gdiplus::ImageFormatJPEG);
if (SUCCEEDED(hr))
ar.Write(pStream, 54262);
}
}
...
I am just now getting back into C++ and have only done a little with it in the past. Any help would be very much appreciated.
Thank you very much in advance.
I'm not an expert on IStream, but I think you may not be using it correctly. The following code seems to work. It currently archives in png format, but it can easily be changed through Gdiplus::ImageFormatPNG.
It owes a lot to the tutorial "Embracing IStream as just a stream of bytes" by S. Arman on CodeProject.
void ImageArchive(CImage *pImage, CArchive &ar)
{
HRESULT hr;
if (ar.IsStoring())
{
// create a stream
IStream *pStream = SHCreateMemStream(NULL, 0);
ASSERT(pStream != NULL);
if (pStream == NULL)
return;
// write the image to a stream rather than file (the stream in this case is just a chunk of memory automatically allocated by the stream itself)
pImage->Save(pStream, Gdiplus::ImageFormatPNG); // Note: IStream will automatically grow up as necessary.
// find the size of memory written (i.e. the image file size)
STATSTG statsg;
hr = pStream->Stat(&statsg, STATFLAG_NONAME);
ASSERT(hr == S_OK);
ASSERT(statsg.cbSize.QuadPart < ULONG_MAX);
ULONG nImgBytes = ULONG(statsg.cbSize.QuadPart); // any image that can be displayed had better not have more than MAX_ULONG bytes
// go to the start of the stream
LARGE_INTEGER seekPos;
seekPos.QuadPart = 0;
hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
ASSERT(hr == S_OK);
// get data in stream into a standard byte array
char *bytes = new char[nImgBytes];
ULONG nRead;
hr = pStream->Read(bytes, nImgBytes, &nRead); // read the data from the stream into normal memory. nRead should be equal to statsg.cbSize.QuadPart.
ASSERT(hr == S_OK);
ASSERT(nImgBytes == nRead);
// write data to archive and finish
ar << nRead; // need to save the amount of memory needed for the file, since we will need to read this amount later
ar.Write(bytes, nRead); // write the data to the archive file from the stream memory
pStream->Release();
delete[] bytes;
}
else
{
// get the data from the archive
ULONG nBytes;
ar >> nBytes;
char *bytes = new char[nBytes]; // ordinary memory to hold data from archive file
UINT nBytesRead = ar.Read(bytes, nBytes); // read the data from the archive file
ASSERT(nBytesRead == UINT(nBytes));
// make the stream
IStream *pStream = SHCreateMemStream(NULL, 0);
ASSERT(pStream != NULL);
if (pStream == NULL)
return;
// put the archive data into the stream
ULONG nBytesWritten;
pStream->Write(bytes, nBytes, &nBytesWritten);
ASSERT(nBytes == nBytesWritten);
if (nBytes != nBytesWritten)
return;
// go to the start of the stream
LARGE_INTEGER seekPos;
seekPos.QuadPart = 0;
hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
ASSERT(hr == S_OK);
// load the stream into CImage and finish
pImage->Load(pStream); // pass the archive data to CImage
pStream->Release();
delete[] bytes;
}
}

How to use GDI+ library to decode a jpeg in memory?

GDI+ provides a Image class, and you can use this class to read a image file with one format and then save this file to another format. But if I want to just decode a jpeg file (already loaded into memory), how can I do it?
You can use SHCreateMemStream and Gdiplus::Image::FromStream
#include <Window.h>
#include <Gdiplus.h>
#include <Shlwapi.h>
#include <atlbase.h>
...
CComPtr<IStream> stream;
stream.Attach(SHCreateMemStream(buf, bufsize));
Gdiplus::Image *image = Gdiplus::Image::FromStream(stream);
Where buf contains jpeg data (or any other compatible image format) and bufsize is the length of that data.
SHCreateMemStream needs "Shlwapi.lib" library.
Example:
void foo(HDC hdc)
{
//Read jpeg from input file in to buf:
HANDLE hfile = CreateFile(L"test.jpg",
GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (!hfile) return;
DWORD bufsize = GetFileSize(hfile, NULL);
BYTE *buf = new BYTE[bufsize];
DWORD temp;
ReadFile(hfile, buf, bufsize, &temp, 0);
//convert buf to IStream
CComPtr<IStream> stream;
stream.Attach(SHCreateMemStream(buf, bufsize));
//Read from IStream
Gdiplus::Bitmap *image = Gdiplus::Bitmap::FromStream(stream);
if (image)
{
Gdiplus::Graphics g(hdc);
g.DrawImage(image, 0, 0);
delete image;
}
delete[]buf;
CloseHandle(hfile);
}
Edit: easier method as mentioned in comments:
IStream* stream = SHCreateMemStream(buf, bufsize);
Gdiplus::Image *image = Gdiplus::Image::FromStream(stream);
...
stream->Release();

How to save IDirect3DSurface9 or LPD3DXBUFFER to memory as JPG

I'm trying to capture game screenshot, i have that code:
LPDIRECT3DDEVICE9 Device;
D3DSURFACE_DESC screenDescription;
...
void Capture(){
IDirect3DSurface9* pRenderTarget;
IDirect3DSurface9* pDestTarget;
Device->GetRenderTarget(0, &pRenderTarget);
pRenderTarget->GetDesc(&screenDescription);
Device->CreateOffscreenPlainSurface(screenDescription.Width, screenDescription.Height, screenDescription.Format, D3DPOOL_SYSTEMMEM, &pDestTarget, NULL);
Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_FORCE_DWORD, &pDestTarget);
char tName[200];
sprintf(tName, "%s_%d.jpg", "C:\\test", GetTickCount());
D3DXSaveSurfaceToFileA(tName, D3DXIFF_JPG, pDestTarget, NULL, NULL);
//LPD3DXBUFFER buffer;
//D3DXSaveSurfaceToFileInMemory(&buffer, D3DXIFF_JPG, pDestTarget, NULL, NULL);
pRenderTarget->Release();
pDestTarget->Release();
isCapturing = false;
}
Saving to file with D3DXSaveSurfaceToFileA works perfectly, but i want to save captured image and write them to end of another file on disk, not creating a new once.
Is there a way how convert IDirect3DSurface9 or LPD3DXBUFFER to JPG bytes?
Ok, i'm found this code and he works:
LPD3DXBUFFER buffer;
D3DXSaveSurfaceToFileInMemory(&buffer, D3DXIFF_JPG, pDestTarget, NULL, NULL);
DWORD imSize = buffer->GetBufferSize();
void* imgBuffer = buffer->GetBufferPointer();
std::fstream out;
out.open(tName, std::ios_base::binary | std::ios_base::out);
out.write((char*)imgBuffer, imSize);
out.clear();
out.close();

How can I create a Base64-Encoded string from a GDI+ Image in C++?

I asked a question recently, How can I create an Image in GDI+ from a Base64-Encoded string in C++?, which got a response that led me to the answer.
Now I need to do the opposite - I have an Image in GDI+ whose image data I need to turn into a Base64-Encoded string. Due to its nature, it's not straightforward.
The crux of the issue is that an Image in GDI+ can save out its data to either a file or an IStream*. I don't want to save to a file, so I need to use the resulting stream. Problem is, this is where my knowledge breaks down.
This first part is what I figured out in the other question
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// I have this decode function from elsewhere
std::string decodedImage = base64_decode(Base64EncodedImage);
// Allocate the space for the stream
DWORD imageSize = decodedImage.length();
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, decodedImage.c_str(), imageSize);
// Create the stream
IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);
// Create the image from the stream
Image image(pStream);
// Cleanup
pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);
(Base64 code)
And now I'm going to perform an operation on the resulting image, in this case rotating it, and now I want the Base64-equivalent string when I'm done.
// Perform operation (rotate)
image.RotateFlip(Gdiplus::Rotate180FlipNone);
IStream* oStream = NULL;
CLSID tiffClsid;
GetEncoderClsid(L"image/tiff", &tiffClsid); // Function defined elsewhere
image.Save(oStream, &tiffClsid);
// And here's where I'm stumped.
(GetEncoderClsid)
So what I wind up with at the end is an IStream* object. But here's where both my knowledge and Google break down for me. IStream shouldn't be an object itself, it's an interface for other types of streams. I'd go down the road from getting string->Image in reverse, but I don't know how to determine the size of the stream, which appears to be key to that route.
How can I go from an IStream* to a string (which I will then Base64-Encode)? Or is there a much better way to go from a GDI+ Image to a string?
Got it
std::string RotateImage(const std::string &Base64EncodedImage)
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
std::string decodedImage = base64_decode(Base64EncodedImage);
DWORD imageSize = decodedImage.length();
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, decodedImage.c_str(), imageSize);
IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);
Image image(pStream);
image.RotateFlip(Gdiplus::Rotate180FlipNone);
pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);
IStream* oStream = NULL;
CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&oStream);
CLSID tiffClsid;
GetEncoderClsid(L"image/tiff", &tiffClsid);
image.Save(oStream, &tiffClsid);
ULARGE_INTEGER ulnSize;
LARGE_INTEGER lnOffset;
lnOffset.QuadPart = 0;
oStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize);
oStream->Seek(lnOffset, STREAM_SEEK_SET, NULL);
char *pBuff = new char[(unsigned int)ulnSize.QuadPart];
ULONG ulBytesRead;
oStream->Read(pBuff, (ULONG)ulnSize.QuadPart, &ulBytesRead);
std::string rotated_string = base64_encode((const unsigned char*)pBuff, ulnSize.QuadPart);
return rotated_string;
}
The trick, as inspired by what I got from this article, is knowing the method to finding out the size of the stream, and having it read it into a character array. Then I can feed that array to the base64_encode function and voilĂ .