Page 1 of 5

Drawing on a TImage and save [Solved]

PostPosted: Sun Mar 30, 2014 6:13 pm
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  Expand view
#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

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 9:33 pm
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  Expand view

#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


 

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 9:45 pm
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

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 9:46 pm
by cnavarro
This tested with VS2012

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 9:48 pm
by Enrico Maria Giordano
Cristobal,

cnavarro wrote:This tested with VS2012


I'm using BCC581. Thank you anyway.

EMG

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 9:50 pm
by cnavarro
If you want to try it put an image is called word.jpg

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 9:53 pm
by cnavarro
I'll try to compile with BCC582

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 10:12 pm
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

Re: Drawing on a TImage and save

PostPosted: Sun Mar 30, 2014 10:12 pm
by Enrico Maria Giordano
Cristobal,

cnavarro wrote:I'll try to compile with BCC582


Thank you.

EMG

Re: Drawing on a TImage and save

PostPosted: Mon Mar 31, 2014 12:06 pm
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 :?:

Re: Drawing on a TImage and save

PostPosted: Mon Mar 31, 2014 12:11 pm
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

Re: Drawing on a TImage and save

PostPosted: Mon Mar 31, 2014 12:48 pm
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  Expand view
#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

Re: Drawing on a TImage and save [Solved]

PostPosted: Mon Mar 31, 2014 1:39 pm
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:

Re: Drawing on a TImage and save [Solved]

PostPosted: Mon Mar 31, 2014 1:42 pm
by Enrico Maria Giordano
Uwe,

you're a war-machine with your sample! :-)

EMG

Re: Drawing on a TImage and save [Solved]

PostPosted: Mon Mar 31, 2014 6:06 pm
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 :?: