How to keep a alpha-channel, save and resize a bitmap

How to keep a alpha-channel, save and resize a bitmap

Postby ukoenig » Sat Jun 21, 2014 1:32 pm

Hello,

saving a bitmap and testing the possible combinations, using :

oDrawImg:SaveImage( cFilename, nExport )

the alphachannel will be lost.
Is there another solution, saving a painted image ( BMP or PNG ) with a included alphachannel ?
I think something from FREEIMAGE.dll is missing in class TImage.

Testing with sample : Testimg.prg

Load a alphablended BMP and change the button from JPG to BMP, to view the result :

@ 5, 28 BUTTON "Save" SIZE 50, 10 OF oDlg ;
ACTION ( oImage:SaveImage( "SAVED.BMP", 0, 25 ), MsgInfo( "saved as saved.bmp" ) )



The painting ( two merged alphablended bmp's ) :

Image

The preview of the EXPORT-result ( with lost alpha-channel ) :

Image

I can use NCONVERT for saving ( with possible resize ),
but it only saves the MAIN-images ( file ) without the painted / merged second one or any other paintings.
The alphachannel is saved as well with this solution.

cScript := '-quiet -o ' + cNewFile + ' -ratio -rtype lanczos -resize ' + ;
ALLTRIM(STR(nExpWidth)) + ' ' + ;
ALLTRIM(STR(nExpHeight)) + ' -overwrite ' + ALLTRIM(cImagepath) + cWorkfile

CursorWait()
WAITRUN ( "NConvert " + cScript, 0 )


Using a NON alphablended BMP as MAIN-image, works fine !

The painting ( a merged alphablended bmp on JPG ) :

Image

The preview of the exported image( BMP with alpha-channel on JPG ) :

Image

Best regards
Uwe :?:
Last edited by ukoenig on Mon Jun 23, 2014 3:04 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
ukoenig
 
Posts: 4043
Joined: Wed Dec 19, 2007 6:40 pm
Location: Germany

Re: How to keep a existing alpha-channel, saving a bitmap ?

Postby Antonio Linares » Sun Jun 22, 2014 12:34 am

Uwe,

I think that FWH function DibFromBitmap() may not be properly saving the alpha channel.

You can review its source code here:
source\winapi\dibbmp.c

I guess we will have to google about it, or maybe FreeImage has a function that may help us...
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: How to keep a existing alpha-channel, saving a bitmap ?

Postby ukoenig » Sun Jun 22, 2014 7:54 am

Antonio,

the freeimage-sections about transparent
( maybe something can be used ) :

FreeImage_GetTransparencyTable
1 4 8
DLL_API BYTE * DLL_CALLCONV FreeImage_GetTransparencyTable(FIBITMAP *dib);
Returns a pointer to the bitmap’s transparency table. Only palletised bitmaps
have a transparency table. High-color bitmaps store the transparency values
directly in the bitmap
bits. FreeImage_GetTransparencyTable returns NULL for these bitmaps.

FreeImage_SetTransparencyTable
1 4 8
DLL_API void DLL_CALLCONV FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table,
int count);
Set the bitmap’s transparency table. Only palletised bitmaps have a transparency table.
Highcolor bitmaps store the transparency values directly in the bitmap bits.
FreeImage_SetTransparencyTable does nothing for these bitmaps.


#include "FreeImage.h"
int main(int argc, char* argv[]) {
FIBITMAP *hDIB24bpp = FreeImage_Load(FIF_BMP, "test.bmp", 0);
if (hDIB24bpp) {
// color-quantize 24bpp (results in a 8bpp bitmap to set transparency)
FIBITMAP *hDIB8bpp = FreeImage_ColorQuantize(hDIB24bpp, FIQ_WUQUANT);
// get palette and find bright green
RGBQUAD *Palette = FreeImage_GetPalette(hDIB8bpp);
BYTE Transparency[256];
for (unsigned i = 0; i < 256; i++) {
Transparency[i] = 0xFF;
if (Palette[i].rgbGreen >= 0xFE &&
Palette[i].rgbBlue == 0x00 &&
Palette[i].rgbRed == 0x00) {
Transparency[i] = 0x00;
}
}
// set the tranparency table
FreeImage_SetTransparencyTable(hDIB8bpp, Transparency, 256);
// save 8bpp image as transparent PNG
FreeImage_Save(FIF_PNG, hDIB8bpp, "test.png", 0);
FreeImage_Unload(hDIB24bpp);
FreeImage_Unload(hDIB8bpp);
}
return 0;
}

FreeImage_SetTransparent
1 4 8 32
DLL_API void DLL_CALLCONV FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled);
Tells FreeImage if it should make use of the transparency table or
the alpha channel that may accompany a bitmap
.
When calling this function with a
bitmap whose bitdepth is different from
1-, 4-, 8- or 32-bit, transparency is disabled whatever the value of the Boolean parameter.

FreeImage_IsTransparent
DLL_API BOOL DLL_CALLCONV FreeImage_IsTransparent(FIBITMAP *dib);
Returns TRUE when the transparency table is enabled (1-, 4- or 8-bit images) or when the
input dib contains alpha values (32-bit images, RGBA16 or RGBAF images). Returns FALSE
otherwise.
FreeImage_SetTransparentIndex

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.
User avatar
ukoenig
 
Posts: 4043
Joined: Wed Dec 19, 2007 6:40 pm
Location: Germany

Re: How to keep a existing alpha-channel, saving a bitmap ?

Postby bosibila » Sun Jun 22, 2014 8:45 am

Hello Uwe,

I am also in this "film" (few days) and I try to load transparent patern "input.png" on dialog, put some text on it and save to "output.png". I found this modified tImage example which save PNG corectly, but when I try to put some text on bitmap and save, nothing changed on "output.png". I saw, that function FISAVEIMG do not save image from screen, but input.png loaded from disk convert to output.png:
*--------------------------------------------------------------------------------------------------------------------------------
hDib = FILOAD( nSrcFormat, cSrcFile, 0 ) // load "input.png" !!??
lOk = FISAVE( nDstFormat, hDib, cDstFile, 0 ) // when PNG is loaded from disk, save to "output.png" is OK.
*--------------------------------------------------------------------------------------------------------------------------------

I don't have your skils in graphic programing, but function FISAVE must be changed, to save modified bitmap from screen.

Code: Select all  Expand view  RUN
#include "FiveWin.ch"
#include "Constant.ch"
#include "Inkey.ch"

#define GW_CHILD      5
#define GW_HWNDNEXT   2
#define RT_BITMAP     2


#ifdef __XPP__
   #define New        _New
   #define Super      ::TBitmap
#endif


STATIC hLib


FUNCTION MAIN()

    LOCAL oDlg, oImg

    DEFINE DIALOG oDlg;
           SIZE 700, 500

*   @ 1, 1 IMAGE oImg FILE "input.png" of oDlg  ADJUST
    @ 40, 20 IMAGE oImg SIZE 300, 200 OF oDlg FILENAME NIL PIXEL SCROLL

    ///////////////////////////////////////////////////////////////////////////////////////
    oImg:lTransparent := .t.
    oImg:LoadImage( , "input.png" )
    oImg:lTransparent := .t.
    oImg:bPainted = { ||  SetBkMode( oImg:hDC, 1 ),;
                 SetTextColor( oImg:hDC, CLR_WHITE ),;
                 TextOut( oImg:hDC, 5, 15, time() )  }
    ///////////////////////////////////////////////////////////////////////////////////////

    @ 0, 1 BUTTON "Save"  ACTION MsgInfo( oImg:SaveImage( "output.png", 13 ) )
    @ 0, 7 BUTTON "Print" ACTION PRINT( oImg )

    ACTIVATE DIALOG oDlg CENTER

    RETURN NIL


STATIC FUNCTION PRINT( oImg )

    LOCAL oPrn

    PRINT oPrn PREVIEW
        PAGE
            oPrn:SayImage( 0, 0, oImg )
        ENDPAGE
    ENDPRINT

    RETURN NIL


//----------------------------------------------------------------------------//

CLASS TImage FROM TBitmap

   CLASSDATA lRegistered AS LOGICAL

   METHOD New( nTop, nLeft, nWidth, nHeight, cResName, cBmpFile, lNoBorder,;
               oWnd, bLClicked, bRClicked, lScroll, lStretch, oCursor,;
               cMsg, lUpdate, bWhen, lPixel, bValid, lDesign ) CONSTRUCTOR

   METHOD Define( cResName, cBmpFile, oWnd ) CONSTRUCTOR

   METHOD LoadImage( cResName, cBmpFile )

   METHOD SaveImage( cFile, nFormat, nFlag )


ENDCLASS

//----------------------------------------------------------------------------//

METHOD New( nTop, nLeft, nWidth, nHeight, cResName, cBmpFile, lNoBorder,;
            oWnd, bLClicked, bRClicked, lScroll, lStretch, oCursor,;
            cMsg, lUpdate, bWhen, lPixel, bValid, lDesign ) CLASS TImage

   #ifdef __XPP__
      ::lRegistered = .f.
   #endif

   Super:New( nTop, nLeft, nWidth, nHeight, cResName, cBmpFile, lNoBorder, ;
              oWnd, bLClicked, bRClicked, lScroll, lStretch, oCursor,      ;
              cMsg, lUpdate, bWhen, lPixel, bValid, lDesign )
return Self

//----------------------------------------------------------------------------//
// This method does not create a control, it just creates a bitmap object to
// be used somewhere else.
METHOD Define( cResName, cBmpFile, oWnd ) CLASS TImage

   local aBmpPal

   DEFAULT oWnd := GetWndDefault()

   ::oWnd     = oWnd
   ::nZoom    = 1
   ::hWnd     = 0
   ::hBitmap  = 0
   ::hPalette = 0

   if ! Empty( cResName )
      aBmpPal    = PalBmpLoad( cResName )
      ::hBitmap  = aBmpPal[ 1 ]
      ::hPalette = aBmpPal[ 2 ]
      cBmpFile  = nil
   endif

   if ! Empty( cBmpFile ) .and. File( cBmpFile )
      ::cBmpFile = cBmpFile
      ::hBitmap = FILoadImg( AllTrim( cBmpFile ) )
   endif

   if ::hBitmap != 0
      PalBmpNew( 0, ::hBitmap, ::hPalette )
   endif

return Self

//----------------------------------------------------------------------------//

METHOD LoadImage( cResName, cBmpFile ) CLASS TImage

   local lChanged := .f.
   local hOldBmp  := ::hBitmap
   local hOldPal  := ::hPalette
   local aBmpPal

   DEFAULT cResName := ::cResName, cBmpFile := ::cBmpFile

   if ! Empty( cResName )
      aBmpPal    = PalBmpLoad( cResName )
      ::hBitmap  = aBmpPal[ 1 ]
      ::hPalette = aBmpPal[ 2 ]
      lChanged   = .t.
      cBmpFile   = nil
   elseif File( cBmpFile )
      ::hBitmap = FILoadImg( AllTrim( cBmpFile ) )
      lChanged  := .t.
      cResName  := nil
   endif

   if lChanged

      ::cResName = cResName
      ::cBmpFile = cBmpFile

      if ! Empty( hOldBmp )
         PalBmpFree( hOldBmp, hOldPal )
      endif

      PalBmpNew( ::hWnd, ::hBitmap, ::hPalette )

   endif

return lChanged

//----------------------------------------------------------------------------//

METHOD SaveImage( cFile, nFormat ) CLASS TImage

   //   0 -> Bmp
   //   2 -> Jpg
   //  13 -> Png

return FISaveImg( ::cBmpFile, cFile, nFormat )

//----------------------------------------------------------------------------//

#define CBM_INIT 4

#define DIB_RGB_COLORS 0


FUNCTION FILOADIMG( cFile )

    LOCAL nFormat, hDib, hInfoH, hInfo, hBits, hWnd, hDC, hBmp

#ifdef __CLIPPER__
    hLib = LOADLIB32( "freeimage.dll" )
#else
    hLib = LOADLIBRARY( "freeimage.dll" )
#endif

    if hLib <= 32
        MsgStop( "Cannot load FreeImage.dll" )
        return 0
    endif

    nFormat = FIGETFILETYPE( cFile, 0 )
    hDib    = FILOAD( nFormat, cFile, 0 )
    hInfoH  = FIGETINFOHEADER( hDib )
    hInfo   = FIGETINFO( hDib )
    hBits   = FIGETBITS( hDib )
    hWnd    = GETDESKTOPWINDOW()

#ifdef __CLIPPER__
    hDC = GETDC32( hWnd )
#else
    hDC = GETDC( hWnd )
#endif

    hBmp = CREATEDIBITMAP( hDC, hInfoH, CBM_INIT, hBits, hInfo, DIB_RGB_COLORS )

#ifdef __CLIPPER__
    RELEASEDC32( hWnd, hDC )
#else
    RELEASEDC( hWnd, hDC )
#endif

    FIUNLOAD( hDib )

#ifdef __CLIPPER__
    FREELIB32( hLib )
#else
    FREELIBRARY( hLib )
#endif

#ifdef __CLIPPER__
    hBmp = NLOWORD( WOWHANDLE16( hBmp, 8 ) )
#endif

    RETURN hBmp


FUNCTION FISAVEIMG( cSrcFile, cDstFile, nDstFormat )

    LOCAL nSrcFormat, hDib, lOk

#ifdef __CLIPPER__
    hLib = LOADLIB32( "freeimage.dll" )
#else
    hLib = LOADLIBRARY( "freeimage.dll" )
#endif

    nSrcFormat = FIGETFILETYPE( cSrcFile, 0 )

    hDib = FILOAD( nSrcFormat, cSrcFile, 0 )

    lOk = FISAVE( nDstFormat, hDib, cDstFile, 0 )

#ifdef __CLIPPER__
    FREELIB32( hLib )
#else
    FREELIBRARY( hLib )
#endif

    RETURN lOk

//----------------------------------------------------------------------------//

DLL32 STATIC FUNCTION FIGETFILETYPE( cFileName AS LPSTR, nSize AS LONG ) AS LONG;
      PASCAL FROM "_FreeImage_GetFileType@8" LIB hLib

DLL32 STATIC FUNCTION FILOAD( nFormat AS LONG, cFileName AS LPSTR, nFlags AS LONG ) AS LONG;
      PASCAL FROM "_FreeImage_Load@12" LIB hLib

DLL32 STATIC FUNCTION FISAVE( nFormat AS LONG, hDib AS LONG, cFileName AS LPSTR, nFlags AS LONG ) AS BOOL;
      PASCAL FROM "_FreeImage_Save@16" LIB hLib

DLL32 STATIC FUNCTION FIUNLOAD( hDib AS LONG ) AS VOID;
      PASCAL FROM "_FreeImage_Unload@4" LIB hLib

DLL32 STATIC FUNCTION FIGETINFOHEADER( hDib AS LONG ) AS LONG;
      PASCAL FROM "_FreeImage_GetInfoHeader@4" LIB hLib

DLL32 STATIC FUNCTION FIGETINFO( hDib AS LONG ) AS LONG;
      PASCAL FROM "_FreeImage_GetInfo@4" LIB hLib

DLL32 STATIC FUNCTION FIGETBITS( hDib AS LONG ) AS LONG;
      PASCAL FROM "_FreeImage_GetBits@4" LIB hLib

DLL32 STATIC FUNCTION GETDC32( hWnd AS LONG ) AS LONG;
      PASCAL FROM "GetDC" LIB "user32.dll"

DLL32 STATIC FUNCTION RELEASEDC32( hWnd AS LONG ) AS LONG;
      PASCAL FROM "ReleaseDC" LIB "user32.dll"

DLL32 STATIC FUNCTION CREATEDIBITMAP( hDC AS LONG, hInfoH AS LONG, nFlags AS LONG, hBits AS LONG, hInfo AS LONG, nUsage AS LONG ) AS LONG;
      PASCAL FROM "CreateDIBitmap" LIB "gdi32.dll"

DLL32 FUNCTION WOWHANDLE16( nHandle AS LONG, nHandleType AS LONG ) AS LONG;
      PASCAL FROM "WOWHandle16" LIB "wow32.dll"

//----------------------------------------------------------------------------//

 
Boris (FWH 20.07, xHarbour 1.2.3, Harbour 3.2.0, BCC74, MySql 5.7)
User avatar
bosibila
 
Posts: 53
Joined: Wed Aug 06, 2008 5:27 pm
Location: Osijek, Croatia

Re: How to keep a existing alpha-channel, saving a bitmap ?

Postby ukoenig » Mon Jun 23, 2014 10:56 am

Just a little change in class TImage ( nothing new to be included ) and it works :
( I will add the source of a new sample )
I still have to test, saving a resized image.

The loaded alphablended BMP using sample TESTIMG.prg

Image

The result of the saved BMP with a still existing alpha-channel
before, there was a black background


Image

Alphablended works ONLY with 32 bpp !!!

We can check, if there is a alpha-channel
if NO = hDib2 = FICNV24( hDib )
if YES = hDib2 = FICONVTO32( hDib )


the needed changes in class TImage :

METHOD SaveImage( cFile, nFormat, nQuality ) CLASS TImage
local hDib := DibFromBitmap( ::hBitmap, ::hPalette )
local cTempFile := cTempFile()
local lSaved, lAlpha := ::HasAlpha()
DibWrite( cTempFile, hDib )
GloBalFree( hDib )
lSaved = FIConvertImageFile( cTempFile, cFile, nFormat, nQuality, lAlpha )
FErase( cTempFile )
return lSaved

//----------

function FIConvertImageFile( cSrcFile, cDstFile, nDstFormat, nQuality, lAlpha )
local nSrcFormat, hDib, hDib2, lOk := .f.
DEFAULT nQuality := 0
if LoadFreeImage() > 32
nSrcFormat = FIGETFILETYPE( cSrcFile, 0 )
hDib = FILOAD( nSrcFormat, cSrcFile, 0 )
if lAlpha
hDib2 = FICONVTO32( hDib )
else
hDib2 = FICNV24( hDib )
endif

lOk = FISAVE( nDstFormat, hDib2, cDstFile, nQuality )
endif
return lOk


The results of ALPHA ON / OFF

Image

Image

Now it is possible, to keep the alpha-channel on a resized image !

Image

@ 110, 230 BUTTON "Save resized" SIZE 50, 10 OF oDlg PIXEL FONT oFont1 ;
ACTION IIF( !Empty(gcFile), ( hBmp := oImage:hBitmap, ;
oImage:hBitmap := ResizeImg( hBmp, nExpWidth, nExpHeight ), ;
DeleteObject( hBmp ), ;
oImage:SaveImage( c_path2 + "SAVED.BMP", 0, 25 ), ;
MsgInfo( "saved as saved.bmp" ) ), ;
MsgAlert( "NO image to save !", "Error" ) )


Another test with these changes : make color transparent :

Image

Image

Image

A PNG - test

Image

Image

best regards
Uwe :lol:
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: 4043
Joined: Wed Dec 19, 2007 6:40 pm
Location: Germany

Re: How to keep a alpha-channel, save and resize a bitmap

Postby Antonio Linares » Mon Jun 23, 2014 9:31 pm

Uwe,

Excellent! :-)

You are a real master using images, thanks!
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain


Return to FiveWin for Harbour/xHarbour

Who is online

Users browsing this forum: Google [Bot] and 17 guests