NO

Author Topic: Gdi+ image operation  (Read 250 times)

Offline liut

  • Member
  • *
  • Posts: 45
Gdi+ image operation
« on: May 09, 2017, 08:49:42 AM »
Hello, I'm trying to use this lib (author by JohnF) for some image operations:

http://www.johnfindlay.plus.com/pellesc/GdiPlus/GdiPlusLibLib.zip

I can use the image decoder and encoder for an image's format conversion, and can also get the width/height of the image. However, I can't find a way to scale an image and save to another. I look into the h files, maybe lack of some api for bitmap/graphics class.

Can anybody show me an example code with this lib? Tks!!

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 1571
Re: Gdi+ image operation
« Reply #1 on: May 09, 2017, 10:03:40 AM »
You may want switch to a native GDI+ usage using fGdiPlusFlat.h, that is also included in fsdk .
You can get the manual for fGdiPlusFlat.h here.
For an example of image cropping and scaling see chapter 7.5 "Cropping and Scaling Images" page 37.
Anyway under GdiPlusLibLib you should use:
Code: [Select]
GpStatus GpGraphics_DrawImageRcRcI(GP_GRAPHICS * this, GP_IMAGE * image, const RECTI * destRect, INT srcx, INT srcy, INT
                                                          srcwidth, INT srcheight, GpUnit srcUnit, const GP_IMAGEATTRIBUTES * imageAttributes,
                                                          DrawImageAbort callback, VOID* callbackData)
Or
Code: [Select]
GpStatus GpGraphics_DrawImagePtsRcI(GP_GRAPHICS * this, GP_IMAGE * image,
                                                   const POINTI * destPoints, INT count, INT srcx, INT srcy,
                                                   INT srcwidth, INT srcheight, GpUnit srcUnit, const GP_IMAGEATTRIBUTES * imageAttributes,
                                                   DrawImageAbort callback, VOID* callbackData)
« Last Edit: May 09, 2017, 10:21:48 AM by frankie »

Offline liut

  • Member
  • *
  • Posts: 45
Re: Gdi+ image operation
« Reply #2 on: May 10, 2017, 02:58:02 AM »
Thanks Frankie. Actually I have read the manual of gdi+flat before and compared with gdi+liblib, their exported apis are similar. The actual problems:
1. No exported api for creating a graphic object via an existing bitmap object. I refered others' C++ code, it should like this:
     Image image(L"d:\\abc.jpg");   // create an image object, it can be done
     Bitmap bitmap(100, 100, PixelFormat32bppARGB);   // create a bitmap object with specified width/height/color, it can be done
     Graphics graph(&bitmap);   // create a graphic object via bitmap object,  no api for this
     graph.DrawImage(&image, Rect(0,0,100,100));   // draw image to this graphic object, it can be done

2. No exported api for saving a bitmap object to file. I check gdi+ manual of Microsoft, bitmap class is inherited from image class, so it should support the save method too. But I can't find api for this (only we have GpImage_SaveFile()):
    bitmap.Save(L"d:\\new_abc.jpg",   &encoderClsid,   &encoderParameters); 

BTW, in the manual the content for "cropping and scaling images", the code purpose is mainly for displaying an image, it should not be enough for me.
If no more idea, I may consider using gdi/hdc as an intermediate way to convert them, but it looks quite stupid :)

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 1571
Re: Gdi+ image operation
« Reply #3 on: May 10, 2017, 01:46:14 PM »
Hello liut.
Actually I have read the manual of gdi+flat before and compared with gdi+liblib, their exported apis are similar. The actual problems:
1. No exported api for creating ...
2. No exported api for saving a bitmap object to file...
fGdiPlusFlat.h is not a library. It doesn't contain any helper class or function.
NeverthelessfGdiPlusFlat.h  exports the whole GDI+ API. All functions existent in GDI+ are available in fGdiPlusFlat.h.

I check gdi+ manual of Microsoft, bitmap class is inherited from image class, so it a graphic object via an existing bitmap object. I refered others' C++ code, it should like this:
     Image image(L"d:\\abc.jpg");   // create an image object, it can be done
     Bitmap bitmap(100, 100, PixelFormat32bppARGB);   // create a bitmap object with specified width/height/color, it can be done
     Graphics graph(&bitmap);   // create a graphic object via bitmap object,  no api for this
     graph.DrawImage(&image, Rect(0,0,100,100));   // draw image to this graphic object, it can be done
Everything can be done. GpBitmap and GPImage can be used indifferently.
Code: [Select]
GpImage *image = NULL;
GdipLoadImageFromFile(L"d:\\abc.jpg", &image); // create an image object
GpBitmap *bitmap = NULL;
GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); // create a bitmap object with specified width/height/color
GpGraphics *graph;
GdipGetImageGraphicsContext(bitmap, &graph); // create a graphic object via bitmap object
GdipDrawImageI(graph, image, 100, 100); // draw image to this graphic object, it can be done

2. No exported api for saving a bitmap object to file. I check gdi+ manual of Microsoft, bitmap class is inherited from image class, so it should support the save method too. But I can't find api for this (only we have GpImage_SaveFile()):
    bitmap.Save(L"d:\\new_abc.jpg",   &encoderClsid,   &encoderParameters); 
Have you took a look to the fGdiPlusFlat.h? Or on MSDN?
You can use GdipSaveImageToFile and image format got from GetImageEncoders, you can find a sample here.

   GdipSaveImageToFile(bitmap, L"mybitmap.bmp", &ImageFormatBMP, NULL);   //save bitmap in bitmap format
   GdipSaveImageToFile(bitmap, L"mybitmap.png", &ImageFormatPNG, NULL);   //save bitmap in PNG format
   GdipSaveImageToFile(image,  L"myimage.bmp",  &ImageFormatBMP, NULL);   //save bitmap in bitmap format
   GdipSaveImageToFile(image,  L"myimage.png",  &ImageFormatPNG, NULL);   //save bitmap in PNG format


BTW, in the manual the content for "cropping and scaling images", the code purpose is mainly for displaying an image, it should not be enough for me.
As I told the file fGdiPlusFlat.h is the complete GDI+ flat API, so everything that can be done in C++ with GDI+ can be done in C. Of course it is not straightforward, and you have to practice to use it (is a bare naked API).
The first page of the manual try to give an idea of how to use them, following the base rules and consulting the reference online it should be not so difficult to program with it.
« Last Edit: May 12, 2017, 04:26:11 PM by frankie »

Offline liut

  • Member
  • *
  • Posts: 45
Re: Gdi+ image operation
« Reply #4 on: May 11, 2017, 08:48:47 AM »
Thanks again frankie!! Finally I make it works! Here is the complete code for reference:

#include <windows.h>
#include <stdio.h>
#include <wchar.h>

#include "fGdiPlusFlat.h"

// The helper function code comes from Microsoft guideline without modification
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = NULL;

   GdipGetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GdipGetImageEncoders(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;  // Failure
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
   GpStatus stat;

   ULONG_PTR gdiplusToken;

   GdiplusStartup(&gdiplusToken, &gdiplusStartupInputDef, NULL);

   GpImage *image=NULL;
   GdipLoadImageFromFile(L"d:\\Runner.jpg",&image);

   UINT width,height;
   GdipGetImageWidth(image,&width);
   GdipGetImageHeight(image,&height);

   PixelFormat pixfmt;
   GdipGetImagePixelFormat(image, &pixfmt);

   if(image){
      GpBitmap *bitmap=NULL;
      GdipCreateBitmapFromScan0(width/2, height/2, 0, pixfmt, NULL, &bitmap);

      if(bitmap){
         GpGraphics *graph=NULL;
         GdipGetImageGraphicsContext(bitmap, &graph);

         if(graph){
            GdipDrawImageRectI(graph, image, 0, 0, width/2, height/2);

            // Draw a line
            //GpPen *pen;
            //GdipCreatePen1(MakeARGB(255, 0, 0, 255), 2.0, UnitPixel, &pen);
            //GdipDrawLineI(graph, pen, 0, 0, 60, 30);

            CLSID encoderClsid;
            GetEncoderClsid(L"image/jpeg", &encoderClsid);

            // For jpeg encoder
            EncoderParameters para;
            ULONG quality;
            
            para.Count = 1;
            para.Parameter[0].Guid = EncoderQuality;
            para.Parameter[0].Type = EncoderParameterValueTypeLong;
            para.Parameter[0].NumberOfValues = 1;
            quality = 95;  // jpg quality level 95.
            para.Parameter[0].Value = &quality;

            //stat=GdipSaveImageToFile(bitmap, L"d:\\mybitmap.bmp", &ImageFormatBMP, NULL);  // Always return 10 (FileNotFound?) if directly using const ImageFormatXXX...
            stat=GdipSaveImageToFile(bitmap, L"d:\\mybitmap.jpg", &encoderClsid, &para);  // para for jpeg, NULL for bmp
            if(stat==Ok){
               MessageBox(NULL,"ok","ok",MB_OK);
            }
            else{
               char text[100];

               sprintf(text,"%d",stat);
               MessageBox(NULL,text,"GdipSaveImageToFile",MB_OK);
            }

            GdipDeleteGraphics(graph);
         }
      }

       GdipDisposeImage(image);
   }

   GdiplusShutdown(gdiplusToken);

   return 0;
}


There is still an issue: I can't directly use the const ImageFormatXXX in GdipSaveImageToFile(), and still need to use the helper function to get the clsid for the encoder. Anyway it's good enough for me!!

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 1554
Re: Gdi+ image operation
« Reply #5 on: May 11, 2017, 11:34:08 AM »
Maybe those are constants.
Code: [Select]
const CLSID CLSID_image_bmp =  {0x557CF400,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};
const CLSID CLSID_image_jpeg = {0x557CF401,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};
const CLSID CLSID_image_gif =  {0x557CF402,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};
const CLSID CLSID_image_tiff = {0x557CF405,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};
const CLSID CLSID_image_png =  {0x557CF406,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};
const CLSID CLSID_image_ico =  {0x557CF407,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};
EDIT: with one variable
Code: [Select]
CLSID CLSID_image =  {0x557CF400,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};just change one byte like
Code: [Select]
*(BYTE*)&CLSID_image = 6; // PNG
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objbase.h>
#pragma comment(lib, "ole32.lib")

CLSID CLSID_image =  {0x557CF400,0x1A04,0x11D3,0x9A,0x73,0x00,0x00,0xF8,0x1E,0xF3,0x2E};

void __cdecl WinMainCRTStartup(void)
{
wchar_t *pwc, wcTmp[555];
pwc = wcTmp;
for (char i=0; i<=7;i++) {
*(BYTE*)&CLSID_image = i;
pwc += StringFromGUID2(&CLSID_image, pwc, 39);
*(pwc-1) = 10;
}
MessageBoxW(0, wcTmp, 0, 0);
ExitProcess(0);
}
« Last Edit: May 13, 2017, 09:40:51 AM by TimoVJL »
May the source be with you

Offline liut

  • Member
  • *
  • Posts: 45
Re: Gdi+ image operation
« Reply #6 on: May 12, 2017, 02:32:53 AM »
Thanks Timo. It works with these const perfectly!

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 1571
Re: Gdi+ image operation
« Reply #7 on: May 12, 2017, 04:28:25 PM »
Sorry I was wrong with use of GdipSaveImageToFile.
I edited the post above.