Drawing on a TImage and save [Solved]

User avatar
Enrico Maria Giordano
Posts: 7355
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Drawing on a TImage and save [Solved]

Post by Enrico Maria Giordano »

Dear friends, I need to draw on a TImage control and save the result. I already tried using SetPixel() on TImage hDC but it saves the original image without my new drawings. This is a sample:

Code: Select all

#include "Fivewin.ch"


FUNCTION MAIN()

    LOCAL oDlg, oImg

    DEFINE DIALOG oDlg;
           SIZE 800, 600

    @ 0, 0 IMAGE oImg;
           FILE "TEST.JPG"

    @ 15, 0 BUTTON "Draw";
            ACTION DRAWIMG( oImg )

    @ 15, 20 BUTTON "Save";
             ACTION oImg:SaveImage( "MYIMAGETEST.JPG", 2 )

    ACTIVATE DIALOG oDlg;
             CENTER

    RETURN NIL


STATIC FUNCTION DRAWIMG( oImg )

    LOCAL hDC := oImg:GetDC()

    LOCAL x, y

    FOR y = 10 TO 50
        FOR x = 10 TO 50
            SETPIXEL( hDC, x, y, CLR_HRED )
        NEXT
    NEXT

    oImg:ReleaseDC()

    RETURN NIL
Any ideas?

Thank you in advance.

EMG
User avatar
cnavarro
Posts: 5792
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: Drawing on a TImage and save

Post by cnavarro »

Enrico
I understand what you're trying, but I have not found a simple way
I used a function of Daniel Garcia-Gil found in this forum but I make the bitmap in black
I modified the function found in Microsoft MSDN and running, but my knowledge does not allow me to save the file as JPG and BMP only as

Entiendo lo que intentas, pero no he encontrado una forma sencilla
He usado una function de Daniel Garcia-Gil encontrada en este foro pero me crea el bitmap en negro
He modificado la function encontrada en el MSDN de Microsoft y funciona correctamente, pero mis conocimientos no me permiten guardar el fichero como JPG y solo como BMP

Code: Select all

#include "Fivewin.ch"

#define SRCCOPY          0x00CC0020
#define SM_CXSCREEN      0 
#define SM_CYSCREEN      1

#define HALFTONE  4

FUNCTION MAIN()

    LOCAL oDlg, oImg

    DEFINE DIALOG oDlg;
           SIZE 800, 600

    @ 0, 0 IMAGE oImg;
           FILE "WORD.JPG" OF oDlg

    @ 15, 5 BUTTON "Draw";
            ACTION ( DRAWIMG( oImg ) ) //, oImg:Refresh() )
            
    @ 15, 25 BUTTON "Save FW";
             ACTION SaveMiBmp( oDlg, oImg:nTop, oImg:nLeft, ;
                         oImg:nWidth, oImg:nHeight, "IMAGENFW.BMP" ) //.JPG" )
            //ACTION oImg:SaveImage( "IMGENFW.JPG", 2 )


    @ 15, 40 BUTTON "Save C";
             ACTION CaptureImage( oDlg:hWnd, oImg:nTop, oImg:nLeft, ;
                    oImg:nWidth, oImg:nHeight, "IMAGENC.BMP" )

    @ 15, 55 BUTTON "Exit" ACTION oDlg:End()

    ACTIVATE DIALOG oDlg;
             CENTER 

    RETURN NIL


STATIC FUNCTION DRAWIMG( oImg )

    LOCAL hDC := oImg:GetDC()
    Local uImg

    LOCAL x, y

    FOR y = 10 TO 50
        FOR x = 10 TO 50
            SETPIXEL( hDC, x, y, CLR_HRED )
        NEXT
    NEXT

    oImg:ReleaseDC()
RETURN uImg

//-----------------------------------------------------------------------------
//http://forums.fivetechsupport.com/viewtopic.php?f=6&t=19403&p=137661&hilit=SaveToBmp#p102241
// SaveToBmp2 de Daniel Garcia-Gil con modificaciones
// Cristobal Navarro
//-----------------------------------------------------------------------------

Function SaveMiBmp( oWnd, nTop, nLeft, nWidth, nHeight, cBmpFile )

   local hParDC   := oWnd:GetDC()
   local hBmp
   local hOldBmp
   local hDib
   local hDC      := CreateCompatibleDC( hParDC )

    //This is the best stretch mode
    SetStretchBltMode(hParDC,HALFTONE)

    //The source DC is the entire screen and the destination DC is the current window (HWND)
    StretchBlt(hParDC,; 
               0,0,; 
               nWidth, nHeight,; 
               hDC,; 
               0,0,;
               GetSysMetrics( nWidth - nLeft ),;  //GetSystemMetrics (SM_CXSCREEN),
               GetSysMetrics( nHeight - nTop ),;  //GetSystemMetrics (SM_CYSCREEN),
               SRCCOPY)

   hBmp      := CreateCompatibleBitmap( hDC, nWidth, nHeight )
   hOldBmp   := SelectObject( hDC, hBmp )

   BitBlt( hDC, 0, 0, nWidth, nHeight, hParDC, 0, 0, SRCCOPY )

   SelectObject( hDC, hOldBmp )
   DeleteDC( hDC )
   hDib = DibFromBitmap( hBmp )
   DibWrite( cBmpFile, hDib )
   GloBalFree( hDib )
   DeleteObject( hBmp )
   oWnd:ReleaseDC()   

Return ( File( cBmpFile ) )

//-----------------------------------------------------------------------------
// Daniel Garcia - Gil  -  Original
//http://forums.fivetechsupport.com/viewtopic.php?f=6&t=19403&p=137661&hilit=SaveToBmp#p102241
//-----------------------------------------------------------------------------

Function SaveToBmp2( oWnd, cBmpFile )
   
   local hDeskDC := GetDC( GetDesktopWindow() )
   local hDC  := CreateCompatibleDC( hDeskDC )
   local hOldBmp
   local hDib 
   local hBmp
   local nWidth //:= GetSysMetrics( SM_CXSCREEN )
   local nHeight //:= GetSysMetrics( SM_CYSCREEN )
   
      
   hBmp = CreateCompatibleBitmap( hDeskDC, nWidth, nHeight )

   hOldBmp = SelectObject( hDC, hBmp )
   
   // No // PrintWindow( oWnd:hWnd, hDC )
   
   BitBlt( hDC, 0, 0, nWidth, nHeight, hDeskDC, 0, 0, SRCCOPY )
   
   SelectObject( hDC, hOldBmp )
   DeleteDC( hDC )
   hDib = DibFromBitmap( hBmp )
   DibWrite( cBmpFile, hDib )
   GloBalFree( hDib )
   DeleteObject( hBmp )

return ( File( cBmpFile ) )
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

#pragma BEGINDUMP

//#define STRICT
#include "stdafx.h"
#include "windows.h"
#include "hbapi.h"

//-----------------------------------------------------------------------------
//http://msdn.microsoft.com/en-us/library/windows/desktop/dd183402(v=vs.85).aspx
// Modificada por Cristobal Navarro
//-----------------------------------------------------------------------------

int CaptureAnImage(HWND hWnd,
                   int nTop, int nLeft,  
                   int nW , int nH , LPCTSTR pszFichero )
{
    HDC hdcScreen;
    HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    hdcScreen = GetDC(NULL);
    hdcWindow = GetDC(hWnd);

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcWindow); 

    if(!hdcMemDC)
    {
        MessageBox(hWnd, TEXT("CreateCompatibleDC has failed"), TEXT("Failed"), MB_OK);
        goto done;
    }

    // Get the client area for size calculation
    RECT rcClient;
    rcClient.top = nTop; //0;
        rcClient.left = nLeft; //0;
        rcClient.right = nW; //200;
        rcClient.bottom = nH; //100;

    //GetWindowRect(hWnd, &rcClient );
    //GetClientRect(hWnd, &rcClient );

    //This is the best stretch mode
    SetStretchBltMode(hdcWindow,HALFTONE);

    //The source DC is the entire screen and the destination DC is the current window (HWND)
    if(!StretchBlt(hdcWindow, 
               0,0, 
               rcClient.right, rcClient.bottom, 
               hdcScreen, 
               0,0,
               GetSystemMetrics( rcClient.right - rcClient.left ),  //GetSystemMetrics (SM_CXSCREEN),
               GetSystemMetrics( rcClient.bottom - rcClient.top ),  //GetSystemMetrics (SM_CYSCREEN),
               SRCCOPY))
    {
        MessageBox(hWnd, TEXT("StretchBlt has failed"),TEXT("Failed"), MB_OK);
        goto done;
    }
    
    // Create a compatible bitmap from the Window DC
    hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
    
    if(!hbmScreen)
    {
        MessageBox(hWnd, TEXT("CreateCompatibleBitmap Failed"),TEXT("Failed"), MB_OK);
        goto done;
    }

    // Select the compatible bitmap into the compatible memory DC.
    SelectObject(hdcMemDC,hbmScreen);
    
    // Bit block transfer into our compatible memory DC.
    if(!BitBlt(hdcMemDC, 
               0,0, 
               rcClient.right-rcClient.left, 
               rcClient.bottom-rcClient.top, 
               hdcWindow, 
               0,0,
               SRCCOPY))
    {
        MessageBox(hWnd, TEXT("BitBlt has failed"), TEXT("Failed"), MB_OK);
        goto done;
    }

    // Get the BITMAP from the HBITMAP
    GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
     
    BITMAPFILEHEADER   bmfHeader;    
    BITMAPINFOHEADER   bi;
     
    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = bmpScreen.bmWidth;    
    bi.biHeight = bmpScreen.bmHeight;  
    bi.biPlanes = 1;    
    bi.biBitCount = 32;    
    bi.biCompression = BI_RGB;    
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;    
    bi.biYPelsPerMeter = 0;    
    bi.biClrUsed = 0;    
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
    // have greater overhead than HeapAlloc.
    HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize); 
    char *lpbitmap = (char *)GlobalLock(hDIB);    

    // Gets the "bits" from the bitmap and copies them into a buffer 
    // which is pointed to by lpbitmap.
    GetDIBits(hdcWindow, hbmScreen, 0,
        (UINT)bmpScreen.bmHeight,
        lpbitmap,
        (BITMAPINFO *)&bi, DIB_RGB_COLORS);

    // A file is created, this is where we will save the screen capture.
    HANDLE hFile = CreateFile( pszFichero , //TEXT("capture.bmp"),
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, NULL);   
    
    // Add the size of the headers to the size of the bitmap to get the total file size
    DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
 
    //Offset to where the actual bitmap bits start.
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); 
    
    //Size of the file
    bmfHeader.bfSize = dwSizeofDIB; 
    
    //bfType must always be BM for Bitmaps
    bmfHeader.bfType = 0x4D42; //BM   
 
    DWORD dwBytesWritten = 0;
    WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
    
    //Unlock and Free the DIB from the heap
    GlobalUnlock(hDIB);    
    GlobalFree(hDIB);

    //Close the handle for the file that was created
    CloseHandle(hFile);
       
    //Clean up
done:
    DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    ReleaseDC(NULL,hdcScreen);
    ReleaseDC(hWnd,hdcWindow);

    return 0;
}


HB_FUNC( CAPTUREIMAGE )
{
  CaptureAnImage( (HWND) hb_parnl( 1 ), hb_parnl( 2 ), hb_parnl( 3 ), 
                   hb_parnl( 4 ), hb_parnl( 5 ), hb_parc( 6 ) ) ;
}


#pragma ENDDUMP


 
C. Navarro
Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo
Si alguien te dice que algo no se puede hacer, recuerda que esta hablando de sus limitaciones, no de las tuyas.
User avatar
Enrico Maria Giordano
Posts: 7355
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Re: Drawing on a TImage and save

Post by Enrico Maria Giordano »

Cristobal,
cnavarro wrote:Enrico
I understand what you're trying, but I have not found a simple way
I can't compile your sample, sorry. I'm searching for an easier way... :-)

EMG
User avatar
cnavarro
Posts: 5792
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: Drawing on a TImage and save

Post by cnavarro »

This tested with VS2012
C. Navarro
Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo
Si alguien te dice que algo no se puede hacer, recuerda que esta hablando de sus limitaciones, no de las tuyas.
User avatar
Enrico Maria Giordano
Posts: 7355
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Re: Drawing on a TImage and save

Post by Enrico Maria Giordano »

Cristobal,
cnavarro wrote:This tested with VS2012
I'm using BCC581. Thank you anyway.

EMG
User avatar
cnavarro
Posts: 5792
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: Drawing on a TImage and save

Post by cnavarro »

If you want to try it put an image is called word.jpg
Last edited by cnavarro on Sun Mar 30, 2014 11:14 pm, edited 1 time in total.
C. Navarro
Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo
Si alguien te dice que algo no se puede hacer, recuerda que esta hablando de sus limitaciones, no de las tuyas.
User avatar
cnavarro
Posts: 5792
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: Drawing on a TImage and save

Post by cnavarro »

I'll try to compile with BCC582
C. Navarro
Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo
Si alguien te dice que algo no se puede hacer, recuerda que esta hablando de sus limitaciones, no de las tuyas.
User avatar
Enrico Maria Giordano
Posts: 7355
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Re: Drawing on a TImage and save

Post by Enrico Maria Giordano »

Cristobal,
cnavarro wrote:https://www.dropbox.com/s/kwk4hd470beg5j8/IMAGEN.zip

If you want to try it put an image is called word.jpg
Thank you. Unfortunately, the image is out of alignment (ie. is slightly shifted).

EMG
User avatar
ukoenig
Posts: 3981
Joined: Wed Dec 19, 2007 6:40 pm
Location: Germany
Contact:

Re: Drawing on a TImage and save

Post by ukoenig »

Enrico,
my first drawing-tests.

Do You want to paint freehand or lines ?
Maybe do You want also saving a RESIZED-result of the original ?

Drawing :
Image

Result of the saved image with a external image-editor :

Image

Best regards
Uwe :?:
Last edited by ukoenig on Tue Apr 01, 2014 9:11 pm, edited 3 times in total.
Since 1995 ( the first release of FW 1.9 )
i work with FW.
If you have any questions about special functions, maybe i can help.
User avatar
Enrico Maria Giordano
Posts: 7355
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Re: Drawing on a TImage and save

Post by Enrico Maria Giordano »

Uwe,
ukoenig wrote:Enrico,
my first tests.
Thank you.
ukoenig wrote:Do You want to paint freehand or lines ?
Lines. I'm doing some experiment too. I'd like to assign the new bitmap to the TImage control. It seems you have to play with hDCs (CreateCompatible...).

EMG
User avatar
Enrico Maria Giordano
Posts: 7355
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Re: Drawing on a TImage and save

Post by Enrico Maria Giordano »

Solved!

Edit: not solved yet as drawing on the stretched image is not the same as drawing on the original one. :-(

Code: Select all

#include "Fivewin.ch"


#define SRCCOPY 13369376


FUNCTION MAIN()

    LOCAL oDlg, oImg

    DEFINE DIALOG oDlg;
           SIZE 800, 600

    @ 0, 0 IMAGE oImg;
           FILE "SFONDO.JPG";
           ADJUST

    @ 15, 0 BUTTON "Draw";
            ACTION DRAWIMG( oImg )

    @ 15, 20 BUTTON "Save";
             ACTION oImg:SaveImage( "MYIMAGETEST.JPG", 2 )

    ACTIVATE DIALOG oDlg;
             CENTER

    RETURN NIL


STATIC FUNCTION DRAWIMG( oImg )

    LOCAL hDC := oImg:GetDC()

    LOCAL x, y

    FOR y = 10 TO 50
        FOR x = 10 TO 50
            SETPIXEL( hDC, x, y, CLR_HRED )
        NEXT
    NEXT

    UPDATEIMG( oImg, hDC )

    oImg:ReleaseDC()

    RETURN NIL


STATIC FUNCTION UPDATEIMG( oImg, hDC )

    LOCAL nWidth  := oImg:Super:Super:nWidth()
    LOCAL nHeight := oImg:Super:Super:nHeight()

    LOCAL hMemDC := CREATECOMPATIBLEDC( hDC )

    LOCAL hMemBmp := CREATECOMPATIBLEBITMAP( hDC, nWidth, nHeight )

    LOCAL hBmpOld := SELECTOBJECT( hMemDC, hMemBmp )

    LOCAL hBitmap  := oImg:hBitmap
    LOCAL hPalette := oImg:hPalette

    BITBLT( hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCCOPY )

    SELECTOBJECT( hMemDC, hBmpOld )

    DELETEDC( hMemDC )

    oImg:hBitmap = hMemBmp

    PALBMPFREE( hBitmap, hPalette )

    PALBMPNEW( oImg:hWnd, oImg:hBitmap, oImg:hPalette )

    RETURN NIL
EMG
User avatar
ukoenig
Posts: 3981
Joined: Wed Dec 19, 2007 6:40 pm
Location: Germany
Contact:

Re: Drawing on a TImage and save [Solved]

Post by ukoenig »

I added some options :

1. The dialog is adjusted to the image-size.

2. change LINESTYLE ( change from FREEHAND to LINES )
selecting lines : first click = startposition, second click = line-end.

3. change at runtime PEN-size and color

still have to add, moving a line

Image

Best regards
Uwe :lol:
Last edited by ukoenig on Mon Mar 31, 2014 6:07 pm, edited 9 times in total.
Since 1995 ( the first release of FW 1.9 )
i work with FW.
If you have any questions about special functions, maybe i can help.
User avatar
ukoenig
Posts: 3981
Joined: Wed Dec 19, 2007 6:40 pm
Location: Germany
Contact:

Re: Drawing on a TImage and save [Solved]

Post by ukoenig »

Enrico,

it is really fun, working with it.
do we still need to paint on a RESIZED image ?
added image-change and showing the used Pensize inside the combobox, You are drawing with.
A new selected image is adjusted inside the dialog with the new size.
Some more options will follow, like to define the EXPORT-image-name.

free painting on a image with different colors and pensizes
poor Olga :(

Image

The external Editor-preview of the saved result
We could do this from inside the program with a extra dialog.

Image

Added RESIZE and save to INI

Image

Added PREVIEW of the RESIZED EXPORT-image :
I think it is completed now ? or still something missing ?

Image

Best regards
Uwe :?:
Since 1995 ( the first release of FW 1.9 )
i work with FW.
If you have any questions about special functions, maybe i can help.
Post Reply