Cannot delete GDI+ Image - c++

I am programming a basic image converter to convert an image to a BMP. I clear up the Image at the end to avoid memory leaks. However, when I try to compile it, this error comes up:
type 'class Gdiplus::Image' argument given to 'delete', expected pointer
I have checked multiple websites but when I use their examples, it still comes up with that compiler error. Even Microsoft's examples come up with that error! I saw a website containing a way to delete the Image but I can't remember the link or the way that they deleted the Image.
My code:
#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
using namespace Gdiplus; UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return 0;
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
CLSID bmpClsid;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Image picture(L"TEST.GIF");
GetEncoderClsid(L"image/bmp", &bmpClsid);
picture.Save(L"Mosaic2.bmp", &bmpClsid, NULL);
delete picture;
GdiplusShutdown(gdiplusToken);
return 0;
}
I will put you in the credits of the program if you give me an answer that works.
Thank you!

Well, delete only works on pointers and your "picture" is an object (unless it is overloaded in some way). Moreover, since it's a local object it should call destructor at the end of main (which should release related memory, including loaded image). But in case memory needs to be released before GdiplusShutdown(gdiplusToken); you can adapt your code to use pointers:
Image *picture = new Image (L"TEST.GIF");
GetEncoderClsid(L"image/bmp", &bmpClsid);
picture->Save(L"Mosaic2.bmp", &bmpClsid, NULL);
delete picture;

Related

GDI+ Image::SetPropertyItem not working as expected

I am trying to use SetPropertyItem to set a Date Taken property to a file (click here for MSDN docs description).
I have tried assigning a newly initialized FILETIME to an input image with no success (or error messages). To ensure that it was not an issue with Date Taken, I also tried following this MSDN example to no avail.
Currently, I am attempting to extract a Date Taken property item from one input file (works fine) and attempting to set it to a different file. This approach does not work either, and the Status code returned is always 0 (Ok).
The code I am using is below. I can only assume I am making a simple mistake or perhaps misunderstanding what SetPropertyItem is supposed to do. I thought that SetPropertyItem changed the metadata value such that it can be viewed through the Windows properties menu, like in this screenshot.
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#include <iostream>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Image* image = new Image(L"FakePhoto.jpg"); // input image
UINT totalBufferSize;
UINT numProperties; // setup the buffer
image->GetPropertySize(&totalBufferSize, &numProperties);
// extract all metadata property items
PropertyItem* pAllItems = (PropertyItem*)malloc(totalBufferSize);
image->GetAllPropertyItems(totalBufferSize, numProperties, pAllItems);
for (UINT j = 0; j < numProperties; ++j)
{ // loop through each property
if (pAllItems[j].id == PropertyTagExifDTOrig)
{ // if it's the Date Taken property
PropertyItem* propItem = new PropertyItem;
Image* newImage = new Image(L"Test2.jpg");
Status status; // second image
propItem->id = PropertyTagExifDTOrig;
propItem->length = pAllItems[j].length;
propItem->type = PropertyTagTypeASCII;
propItem->value = pAllItems[j].value;
// create a new property item with the input photo Date Taken metadata
status = newImage->SetPropertyItem(propItem);
if (status == Ok)
std::cout << "No errors.";
}
}
free(pAllItems);
delete image;
GdiplusShutdown(gdiplusToken);
}
Any help is greatly appreciated. Also, I apologise about any obvious/potential errors. I am still learning the ropes as this is my first time using C++.
You code works fine, but you must save the image back, for example like this
...
newImage->SetPropertyItem(propItem);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
newImage->Save(L"Test2.jpg", &clsid);
...
BOOL GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0;
UINT size = 0;
ImageCodecInfo* info = NULL;
ZeroMemory(pClsid, sizeof(CLSID));
GetImageEncodersSize(&num, &size);
if (size == 0)
return FALSE;
info = (ImageCodecInfo*)(malloc(size));
if (info == NULL)
return FALSE;
GetImageEncoders(num, size, info);
for (UINT j = 0; j < num; ++j)
{
if (!wcscmp(info[j].MimeType, format))
{
*pClsid = info[j].Clsid;
free(info);
return TRUE;
}
}
free(info);
return FALSE;
}

How to load GDI::Image (GIF) from Resource in c++(winapi)?

I'm making a window c++ with winapi(non-MFC) and showing gif. For this Im using GDI++. I'm loading a gif to GDI::Image from the path, but I want to load it from resources. How can I do it?
hMWDC = GetDC(hWnd);
pGphcs = new Graphics(hMWDC);
WCHAR path[MAX_PATH];
GetModuleFileNameW(NULL, path, MAX_PATH);
PathRemoveFileSpecW(path);
PathAppendW(path, L"gifs\\test.gif");
pImg = new Image(path);
if (pImg) {
nFrmCnt = pImg->GetFrameCount(&FrameDimensionTime);
SetTimer(hWnd, DRAW_ANIM, 100, NULL);
}
case WM_TIMER:
if (wParam == DRAW_ANIM)
{
pImg->SelectActiveFrame(&FrameDimensionTime, nFrm);
Rect DRC(0, 0, pImg->GetWidth(), pImg->GetHeight());
pGphcs->Clear(Color(128, 128, 128));
pGphcs->DrawImage(pImg, DRC);
if (nFrm < (nFrmCnt - 1)) nFrm++; else nFrm = 0;
}
break;
There is an Image constructor that accepts an IStream*.
You can create a stream by calling SHCreateMemStream on the raw buffer of a resource, which can be obtained by calling FindResource/LoadResource/LockResource and SizeOfResource.
Add the GIF file to your app's resources at compile time. For instance, by compiling an .rc file similar to below into a .res file that you can then link into your executable (some compilers/IDEs have tools to automate this step):
Resources.rh
#define MY_GIF_ID 100
Resources.rc
#include "Resources.rh"
MY_GIF_ID RCDATA "gifs\\test.gif"
Then, you can obtain a pointer to the raw bytes of the resource at runtime.
#include "Resources.rh"
HMODULE hMod = GetModuleHandle(NULL);
HRSRC hRes = FindResource(hMod, MAKEINTRESOURCE(MY_GIF_ID), RT_RCDATA);
if (!hRes) { ... error handling ... }
HGLOBAL hGlobal = LoadResource(hMod, hRes);
if (!hGlobal) { ... error handling ... }
void *pResData = LockResource(hGlobal);
if (!pResData) { ... error handling ... }
DWORD dwResData = SizeofResource(hMod, hRes);
See MSDN for more details:
Introduction to Resources
Finding and Loading Resources
And then finally, pass the resource bytes to the Image constructor that takes an IStream* as input:
#include <shlwapi.h>
IStream *pStream = SHCreateMemStream((BYTE*)pResData, dwResData);
if (!pStream) { ... error handling ... }
pImg = new Image(pStream);
pStream->Release();

Modify EXIF Data using C++

We are building a quad copter robot for a competition, and one of the requirements is that we have to capture photos using the camera that is installed on the quad copter.
I wrote a simple OpenCV program that is able to capture them in .jpg format, but my camera or my program are unable to save "latitude" and "longitude" as EXIF on my images.
I've added to my robot a GPS module that can retrieve GPS data while capturing photos and save them to a text file.
So my main problem is adding these data from the text file to the pictures separately while they are captured.
I tried many libraries like:
easyexif
Exiv2
Phil Harvey
Also I tried them in:
Visual C++ .net 2015
Dev C++ (GCC)
Code Blocks (GCC)
and also I worked on PHP but I just can read and extract EIXFs but I can't write new data on my pictures.
How can I solve this problem?
you can use this code. I used Image::GetPropertyIdList method and Reading and Writing Metadata as a material for this code. unfortunately i didnt find any function in opencv that can manipulate Exif.
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#include <Math.h>
#pragma comment(lib,"gdiplus.lib")
using namespace Gdiplus;
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) //i copy this function from Doc.microsoft.com
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; }
int main(){
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Status stat;
CLSID clsid;
Bitmap* bitmap = new Bitmap(L"Source.bmp"); // you can use any Image Format as Source
PropertyItem* propertyItem = new PropertyItem;
// Get the CLSID of the JPEG encoder.
GetEncoderClsid(L"image/jpeg", &clsid);
double dlan = 35.715298; // we supposed that your GPS data is Double if its not skip this step
// convert double to unsigned long array
double coord = dlan;
int sec = (int)round(coord * 3600);
int deg = sec / 3600;
sec = abs(sec % 3600);
int min = sec / 60;
sec %= 60;
unsigned long ulExifCoordFormatLan[6] = { deg, 1, min, 1, sec, 1 };
propertyItem->id = PropertyTagGpsLatitude;
propertyItem->length = sizeof(long) * 2 * 3;
propertyItem->type = PropertyTagTypeRational;
propertyItem->value = ulExifCoordFormatLan;
Status s = bitmap->SetPropertyItem(propertyItem);// saving image to the destination
stat = bitmap->Save(L"Dest.jpg", &clsid, NULL);
if (s == Ok && stat==Ok)
printf("Dest.jpg saved successfully .\n");
delete propertyItem;
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;}

Visual Studio errors and getting a device GUID and path name?

Evening everyone I wondered if some could answer me 2 quick questions.
I made an app to communicate with arm device which works fine, however when I move PC etc, I need to reconfigure the device path. Its a long one like below.
Path: \\?\usb#vid_045e&pid_0040#6&ff454f2&0&3#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
I done some reading up and discovered it's two features SetupDiGetClassDevs and SetupDiGetDeviceInstanceId I need. My question is am I looking in the right place i.e will these 2 functions return the path like above. Also what is the techinical name for this path?
I found what I think is a nice example (always learn better from example) on Microsoft's website cut and paste below but this throws up error C2440: '=' : cannot convert from 'HLOCAL' to 'LPTSTR'
which is a new one on me a pointer error?
This is the code
#include <stdio.h>
#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>
int main( int argc, char *argv[ ], char *envp[ ] )
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(NULL,
0, // Enumerator
0,
DIGCF_PRESENT | DIGCF_ALLCLASSES );
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
//
// Call function with null to begin with,
// then use the returned buffer size (doubled)
// to Alloc the buffer. Keep calling until
// success or an unknown failure.
//
// Double the returned buffersize to correct
// for underlying legacy CM functions that
// return an incorrect buffersize value on
// DBCS/MBCS systems.
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_DEVICEDESC,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = LocalAlloc(LPTR,buffersize * 2); // ERROR LINE
}
else
{
// Insert error handling here.
break;
}
}
printf("Result:[%s]\n",buffer);
if (buffer) LocalFree(buffer);
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
return 1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}
Hope its an easy one thank you.
You need to typecast the return value from LocalAlloc():
buffer = (LPSTR) LocalAlloc(LPTR,buffersize * 2);
For more info, see the LocalAlloc() documentation on MSDN.

Does GDI+ have standard image encoder CLSIDs?

The GDI+ Image::Save method requires a CLSID parameter to specify the encoder to use. The documentation points to some sample code for getting the encoder associated with a particular MIME type, such as image/jpeg or image/png. However I'm balking at the thought of copying a half-page function just to support a 1-line debugging aid where I save an intermediate result out to disk.
Shouldn't there be a list of standard CLSIDs for the standard encoders? Where would I find such a list? I haven't been able to find one by searching Microsoft's include files.
There isn't one. I think they intended the codec list to be extensible and support plugins, but never got around to it. Given that they haven't made any changes to GDI+ in quite some time, they likely won't anytime soon. You could probably get away with generating your own hard coded list based on an enumeration of Gdiplus::GetImageEncoders.
That is:
image/bmp : {557cf400-1a04-11d3-9a73-0000f81ef32e}
image/jpeg : {557cf401-1a04-11d3-9a73-0000f81ef32e}
image/gif : {557cf402-1a04-11d3-9a73-0000f81ef32e}
image/tiff : {557cf405-1a04-11d3-9a73-0000f81ef32e}
image/png : {557cf406-1a04-11d3-9a73-0000f81ef32e}
Here's the function I routinely cut&paste between projects for getting at the CLSID of the encoder. You could modify it to be a table lookup.
#include <windows.h>
#include <gdiplus.h>
#include <string>
#include <vector>
HRESULT GetGdiplusEncoderClsid(const std::wstring& format, GUID* pGuid)
{
HRESULT hr = S_OK;
UINT nEncoders = 0; // number of image encoders
UINT nSize = 0; // size of the image encoder array in bytes
std::vector<BYTE> spData;
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::Status status;
bool found = false;
if (format.empty() || !pGuid)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
*pGuid = GUID_NULL;
status = Gdiplus::GetImageEncodersSize(&nEncoders, &nSize);
if ((status != Gdiplus::Ok) || (nSize == 0))
{
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
spData.resize(nSize);
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)&spData.front();
status = Gdiplus::GetImageEncoders(nEncoders, nSize, pImageCodecInfo);
if (status != Gdiplus::Ok)
{
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
for (UINT j = 0; j < nEncoders && !found; j++)
{
if (pImageCodecInfo[j].MimeType == format)
{
*pGuid = pImageCodecInfo[j].Clsid;
found = true;
}
}
hr = found ? S_OK : E_FAIL;
}
return hr;
}
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
GUID guidBmp = {};
GUID guidJpeg = {};
GUID guidGif = {};
GUID guidTiff = {};
GUID guidPng = {};
GetGdiplusEncoderClsid(L"image/bmp", &guidBmp);
GetGdiplusEncoderClsid(L"image/jpeg", &guidJpeg);
GetGdiplusEncoderClsid(L"image/gif", &guidGif);
GetGdiplusEncoderClsid(L"image/tiff", &guidTiff);
GetGdiplusEncoderClsid(L"image/png", &guidPng);
return 0;
}
If you just want to write a PNG, this appears to work:
// image/png : {557cf406-1a04-11d3-9a73-0000f81ef32e}
const CLSID pngEncoderClsId = { 0x557cf406, 0x1a04, 0x11d3,{ 0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e } };
stat = image->Save(L"test.png", &pngEncoderClsId, NULL);
Note the reformatting of the hex values.
From:
How to initialize a constant CLSID
You will probably want to use ImageCodecInfo with GetImageEncodersSize() and GetImageEncoders() I'm not aware of any easier way.
EDIT: If you know specifically what you want and damn all the rest you can get away with doing something like this ...
CLSID pngClsid;
GetEncoderClsid("image/png", &pngClsid);
image.Save("imagename.png", &pngClsid, NULL);