Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS Tech Support » CExtIconEditDlg and two color bitmaps Collapse All
Subject Author Date
Hans Bergmeister Aug 10, 2007 - 4:06 AM

Hello,

we want to use CExtIconEditDlg to edit two color bitmaps.

We managed to get CExtIconEditDlg to work properly with a two color palette.

But we have problems to extract a two color HIBTMAP (bitmap with one bit per pixel) from the resulting m_icon member. What is the right way to do this?

Technical Support Aug 10, 2007 - 6:06 AM

First of all you should create a mono bitmap:

INT nWidth = . . .
INT nHeight = . . .
CBitmap bmpMono;
      VERIFY( bmpMono.CreateBitmap( nWidth, nHeight, 1, 1, NULL ) );
Then you should create a DC for it:
CDC dcMono;
      VERIFY( dcMono.CreateCompatibleDC( NULL ) );
CBitmap * pOldBitmapFromMonoDC = dcMono.SelectObject( & bmpMono );
You also have some bitmap from the image editor window. You should also create a DC for it and select this bitmap in its DC. Then you should draw using the BitBlt(),/code> API from the second DC into <code>dcMono. Finally you should restore the old selected bitmaps in both DCs and destroy DCs. As a result, you have a 2-BPP bitmap in bmpMono.

Hans Bergmeister Aug 10, 2007 - 7:19 AM

Hello,

many thanks for your response.

This is what we have done already:

CExtIconEditDlg dlg;
INT nWidth = . . .
INT nHeight = . . .

CBitmap bmpMono;
VERIFY( bmpMono.CreateBitmap( nWidth, nHeight, 1, 1, NULL ) );

dlg.m_icon.AssignFromHBITMAP((HBITMAP)bmpMono.GetSafeHandle(), (COLORREF)-1);

if (dlg.DoModal() == IDOK)
{
CDC dcMono;
VERIFY( dcMono.CreateCompatibleDC( NULL ) );
CBitmap * pOldBitmapFromMonoDC = dcMono.SelectObject( & bmpMono );

CBitmap oBmpDlg;
CBmpDlg.Attach(dlg.m_icon.m_bmpNormal.CreateBitmap(NULL, (COLORREF)-1));

CDC dcMemDlg;
dcMemDlg.CreateCompatibleDC(NULL);
CBitmap* poOldBitmapDlg = dcMemDlg.SelectObject(&oBmpDlg);

dcMono.BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &dcMemDlg, 0, 0, SRCCOPY);

dcMemDlg.SelectObject(poOldBitmapDlg);
dcMono.SelectObject(pOldBitmapFromMonoDC);
}

but everything, what we finally receive in dcMono is a completely black bitmap, regardless, which image we created in the editor. What is wrong?

Technical Support Aug 11, 2007 - 12:21 PM

We are sure we provided you with the correct way of creating one bit per pixel bitmaps. Please update the CMainDlg::OnButtonEdit() method in the IconEditor sample.

void CMainDlg::OnButtonEdit() 
{
int nSel = m_wndList.GetCurSel();
      if( nSel < 0 )
      {
            ::AfxMessageBox(
                  _T("Please select icon in list"),
                  MB_OK|MB_ICONINFORMATION
                  );
            return;
      }
CExtCmdIcon * pIcon = m_wndList.GetIconPtr( nSel );
      ASSERT( pIcon != NULL && (! pIcon->IsEmpty()) );
CExtIconEditDlg dlg( this );
      dlg.m_icon.AssignFromOther( *pIcon );
      ASSERT( ! dlg.m_icon.IsEmpty() );
      dlg.m_strProfileSection = _T("ResizableDialogs");
      dlg.m_strProfileEntryWindow = _T("IconEditor-Window");
      dlg.m_strProfileEntryIconEditor = _T("IconEditor-EditorControl");
CSize _sizeIcon = pIcon->GetSize();
CString strColors;
INT nBitsPerPixel = pIcon->m_bmpNormal.GetBPP();
      if( nBitsPerPixel >= 24 )
            strColors = _T("16.777.216 (16M)");
      else if( nBitsPerPixel == 16 )
            strColors = _T("65.536 (64K)");
      else if( nBitsPerPixel == 15 )
            strColors = _T("32.768 (32K)");
      else
            strColors.Format( _T("%ld"), 1L << nBitsPerPixel );
      dlg.m_strAlternateCaption.Format(
            _T("Icon Editor - %dx%d, %s colors"),
            _sizeIcon.cx, _sizeIcon.cy,
            (LPCTSTR)strColors
            );
//    dlg.m_bEatTransparentColor = false;
      if( dlg.DoModal() != IDOK )
      {
///////           return;
      }
      ASSERT( ! dlg.m_icon.IsEmpty() );
      pIcon->AssignFromOther( dlg.m_icon );
      m_wndList.RecalcListLayout();

HBITMAP hBitmapColored = NULL;
COLORREF clrTransparent = COLORREF(-1L);
      VERIFY( dlg.m_icon.ExtractEditableBitmap( hBitmapColored, clrTransparent ) );
      ASSERT( hBitmapColored != NULL );
      VERIFY( CExtPaintManager::stat_SerializeBitmap( _T("C:\\1.bmp"), false, &hBitmapColored ) );

CExtBitmap bmpColored;
      bmpColored.FromBitmap( hBitmapColored );
      ::DeleteObject( hBitmapColored ); // we don’t need it anymore
CSize sizeBmp = bmpColored.GetSize();
int nCountOfBytesPerLine = sizeBmp.cx / 8 + ( ( ( sizeBmp.cx % 8 ) != 0 ) ? 1 : 0 );
int nCountOfBytesInBitmap = nCountOfBytesPerLine * sizeBmp.cy;
BYTE * pBinarySurface = new BYTE [ nCountOfBytesInBitmap ];
int nCounter, nX, nY;
      for( nCounter = 0; nCounter < nCountOfBytesInBitmap; nCounter ++ )
            pBinarySurface[ nCounter ] = BYTE(0xFF); // make each pixel white
double lfHue, lfSaturation, lfLuminance;
BYTE * pCurrSurfacePtr = pBinarySurface;
      for( nY = 0; nY < sizeBmp.cy; nY++ )
      {
            for( nX = 0; nX < sizeBmp.cx; nX++ )
            {
                  COLORREF clrColored = bmpColored.GetPixel( nX, nY );
                  if(         clrTransparent != COLORREF(-1L)
                        &&    clrTransparent == clrColored
                        )
                        continue;
                  CExtBitmap::stat_RGBtoHSL( clrColored, &lfHue, &lfSaturation, &lfLuminance );
                  if( lfLuminance > .5 )
                        continue;
                  int nByteIndex = nX / 8;
                  BYTE & _byte = pCurrSurfacePtr[ nByteIndex ];
                  int nBitIndex = nX % 8;
                  nBitIndex = 7 - nBitIndex;
                  BYTE nMaskValue = BYTE( 1 << nBitIndex ); 
                  _byte &= ~nMaskValue; // make this pixel black
            }
            pCurrSurfacePtr += nCountOfBytesPerLine;
      }
//HBITMAP hBitmapMono = CreateBitmap( sizeBmp.cx, sizeBmp.cy, 1, 1, pBinarySurface );
                  BITMAP bi;
                        bi.bmType = 0;
                        bi.bmWidth = sizeBmp.cx;
                        bi.bmHeight = sizeBmp.cy;
                        bi.bmWidthBytes = nCountOfBytesPerLine;
                        bi.bmPlanes = 1;
                        bi.bmBitsPixel = 1;
                        bi.bmBits = pBinarySurface;
                  HBITMAP hBitmapMono = CreateBitmapIndirect( &bi );
                        ASSERT( hBitmapMono != NULL );
                        delete [] pBinarySurface;

      VERIFY( CExtPaintManager::stat_SerializeBitmap( _T("C:\\2.bmp"), false, &hBitmapMono ) );
CExtBitmap bmpTmp;
bmpTmp.FromBitmap( hBitmapMono );
bmpTmp.Make32();
bmpTmp.SaveBMP_File( _T("c:\\3.bmp") );
      ::DeleteObject( hBitmapMono ); // we don’t need it anymore

}
Then please run it and open the C:\ folder with Windows Explorer and select the Thumbnails view for this folder. You can double click any list box item in the IconEditor sample and then click OK in the opened icon editor dialog, In the C:\ folder, you will see three bitmaps created in it. 1.bmp (the hBitmapColored and bmpColored variables in the code above) is created from the icon surface and uses some random color instead of transparent pixels. 2.bmp (the hBitmapMono) is absolutely an valid one pit per pixel bitmap file created from the previous bitmap but it is displayed absolutely incorrect for most of icons from the list box in the main dialog of the IconEditor sample. 3.bmp (the bmpTmp) is created from the second bitmap. This third bitmap is a 32 bit per pixel bitmap without alpha channel and contains black and white opaque pixels only. This bitmap is displayed correctly in the Windows Explorer.

We would like to ask you to provide more details about your task so we can find correct bitmap implementation for you.

Hans Bergmeister Aug 12, 2007 - 4:10 AM

Hello,

many thanks for your suggestion.

You wrote:
>>
This third bitmap is a 32 bit per pixel bitmap without alpha channel and contains black and white opaque pixels only.
<<
This is not exactly, what we need. We need a 1 bit per pixel bitmap as result.

Something like this:

HBITMAP hbmpSourceMono; // given bitmap with 1 bit per pixel
CExtIconEditDlg dlg( this );

dlg.m_icon.AssignFromHBITMAP(hbmpSourceMono, (COLORREF)-1);

if (dlg.DoModal() == IDOK)
{
HBITMAP hbmpDestMono = dlg.m_icon.Extract_HBITMAP_with_1_Bit_per_Pixel();
}

We are looking for that "Extract_HBITMAP_with_1_Bit_per_Pixel()" function, that results in a HBITMAP with 1 bit per pixel.

Or in other words: the complete procedure of editing bitmaps with CExtIconEditDlg should maintain the original color depth.


Technical Support Aug 12, 2007 - 10:39 AM

The source code in our previous message allows you to convert any source bitmap into a destination 1BPP bitmap (1 bit per pixel bitmap). If the source bitmap contains only RGB(0,0,0) and RGB(255,255,255) pixels, the destination bitmap will be exactly the same. We believe the generated 1BPP bitmap is correct. The only real problem is that it cannot be painted correctly by Windows GDI APIs for bitmap painting and even Windows Explorer does not display it correctly. Prof-UIS uses the same technique for generating 1 BPP bitmaps used for painting docked toolbar grippers in Office XP style. But our 1BPP bitmap is not painted using BitBlt() API. We use it for creating a pattern brush and fill the gripper area using it. The 1BPP pattern is painted correctly using this approach. So, we can offer you the following solution:

1) We will create a function from our code for converting any bitmap into a 1BPP bitmap. You could use it for converting after editing. So, you will keep only 1BPP bitmaps in memory.

2) Before painting and editing, you will convert 1BPP bitmaps into 32BPP bitmaps like it is demonstrated in the last lines of our code which generate the third bitmap.

Hans Bergmeister Aug 12, 2007 - 10:56 AM

Hello,

many thanks for your support.

>>
1) We will create a function from our code for converting any bitmap into a 1BPP bitmap. You could use it for converting after editing. So, you will keep only 1BPP bitmaps in memory.
<<
This would be great. Thank you very much in advance.

Woud it be also possible, to make the function more flexible and to allow converting any bitmap into a xBPP bitmap? In this way one could input any xBPP bitmap into CExtIconEditDlg and could output the resulting bitmap after DoModal() in the same color format.

Technical Support Aug 14, 2007 - 2:58 AM

Here is the function for creating a 1 BPP bitmap

HBITMAP ConvertBitmapToMono(
      HBITMAP hBitmapColored, // source bitmap of any format, must be not NULL
      bool bDeleteColoredBitmapOnSuccess = false, // delete hBitmapColored if the returned handle is not NULL
      double lfMinWhiteLuminance = 0.5 // luminance margin between 0.0 (black) and 1.0 (white)
      )
{
      if(         hBitmapColored == NULL
              ||  lfMinWhiteLuminance <= 0.0
              ||  lfMinWhiteLuminance >= 1.0
              )
        {
              ASSERT( FALSE );
              return NULL; // invalid parameter
        }
COLORREF clrTransparent = COLORREF(-1L);
CExtBitmap bmpColored;
      if( ! bmpColored.FromBitmap( hBitmapColored ) )
              return NULL;
CSize sizeBmp = bmpColored.GetSize();
int nCountOfBytesPerLine = sizeBmp.cx / 8 + ( ( ( sizeBmp.cx % 8 ) != 0 ) ? 1 : 0 );
int nCountOfBytesInBitmap = nCountOfBytesPerLine * sizeBmp.cy;
BYTE * pBinarySurface = NULL;
      try
      {
            pBinarySurface = new BYTE [ nCountOfBytesInBitmap ];
      }
      catch( CException * e )
      {
            ASSERT( FALSE );
            e->Delete();
            return NULL; // memory allocation error
      }
      catch( ... )
      {
            ASSERT( FALSE );
            return NULL; // memory allocation error
      }
      ASSERT( pBinarySurface != NULL );
int nCounter, nX, nY;
      for( nCounter = 0; nCounter < nCountOfBytesInBitmap; nCounter ++ )
            pBinarySurface[ nCounter ] = BYTE(0xFF); // make each pixel white
double lfHue, lfSaturation, lfLuminance;
BYTE * pCurrSurfacePtr = pBinarySurface;
      for( nY = 0; nY < sizeBmp.cy; nY++ )
      {
            for( nX = 0; nX < sizeBmp.cx; nX++ )
            {
                  COLORREF clrColored = bmpColored.GetPixel( nX, nY );
                  if(         clrTransparent != COLORREF(-1L)
                        &&    clrTransparent == clrColored
                        )
                        continue;
                  CExtBitmap::stat_RGBtoHSL( clrColored, &lfHue, &lfSaturation, &lfLuminance );
                  if( lfLuminance >= lfMinWhiteLuminance )
                        continue;
                  int nByteIndex = nX / 8;
                  BYTE & _byte = pCurrSurfacePtr[ nByteIndex ];
                  int nBitIndex = nX % 8;
                  nBitIndex = 7 - nBitIndex;
                  BYTE nMaskValue = BYTE( 1 << nBitIndex ); 
                  _byte &= ~nMaskValue;
            }
            pCurrSurfacePtr += nCountOfBytesPerLine;
      }
BITMAP bi;
    bi.bmType = 0;
    bi.bmWidth = sizeBmp.cx;
    bi.bmHeight = sizeBmp.cy;
    bi.bmWidthBytes = nCountOfBytesPerLine;
    bi.bmPlanes = 1;
    bi.bmBitsPixel = 1;
    bi.bmBits = pBinarySurface;
HBITMAP hBitmapMono = ::CreateBitmapIndirect( &bi );
    delete [] pBinarySurface;
    if( hBitmapMono == NULL )
      {
            ASSERT( FALSE );
            return NULL; // mono bitmap handle creation error
      }
      if( bDeleteColoredBitmapOnSuccess )
            ::DeleteObject( hBitmapColored );
      return hBitmapMono; // success
}
It is not difficult task to convert any BPP to any higher BPP. More difficult task is conversion to a lower BPP, especially when using bitmaps with indexed color format. The conversion into color BPP is called color quantization. It is performed by libraries like CxImage:

http://www.codeproject.com/bitmap/cximage.asp
http://www.codeproject.com/bitmap/cquantizer.asp

Hans Bergmeister Aug 14, 2007 - 3:30 AM

Hello,

many thanks four the code. I really appreciate your support. The code will be useful for our task.

One final question: is my assumption correct, that - regardless from the bmp format being loaded into CExtIconEditDlg - the format of the bitmap stored in CExtIconEditDlg::m_icon after returning from CExtIconEditDlg ::DoModal() is always 32 BPP ?

Technical Support Aug 14, 2007 - 1:09 PM

The format of the returned bitmap is the same as the format of the bitmap you started editing. The bitmap cloning during editing in the undo-redo buffer of image editor control is performed by the CExtBitmap::stat_CloneBitmap() method.

Hans Bergmeister Aug 15, 2007 - 1:14 AM

Hello,

I am afraid now, that we begin to go round in circles.

Once more:

Originally I wrote, that we do the following:

HBITMAP hbmpSourceMono; // given bitmap with 1 BPP

CExtIconEditDlg dlg( this );

dlg.m_icon.AssignFromHBITMAP(hbmpSourceMono, (COLORREF)-1);

if (dlg.DoModal() == IDOK)
{
...
}

with hbmpSourceMono being a monochrome bitmap with 1 BPP. I told you, that we have problems to extract a bitmap in the original 1BPP format from dlg.m_icon after calling DoModal(). Then you provided us with comprehensive source code, that fullfills this task. Finally you stated, that the format of the returned bitmap is the same as the format of the bitmap we started editing.

I am lost now. If the format of the returned bitmap is the same as the original bitmap (which was 1 BPP), why do we have those problems to extract a bitmap with 1 BPP and why do we need the comprehensive code, you provided us before???




Technical Support Aug 19, 2007 - 10:05 AM

We have checked the image editor’s code again. It seems it returns the same image format as assigned in all cases except 1 for BPP bitmaps. Please use the function we wrote for you for adjusting 1BPP bitmaps.

Hans Bergmeister Aug 21, 2007 - 12:19 PM

Hello,

understood, many thanks.

Your code will help to overcome this restriction. Do you plan to extend CExtIconEditDlg to make it treat all bitmap formats (including 1BPP consistently)?

Technical Support Aug 22, 2007 - 12:22 PM

Frankly speaking, we think 1BPP bitmaps with regard to Ui is relic of past. Even if you need some simple two color (black and white for instance) graphic images, you’d better use 32-bit images with alpha channel because they can contain very smooth edges of graphic shapes. It would be extremely interesting to know why and for which purposes you are using so low color images in your project?

Hans Bergmeister Aug 23, 2007 - 1:11 AM

Hello,

>>
It would be extremely interesting to know why and for which purposes you are using so low color images in your project?
<<
We need 1BPP bitmaps for a very specific project: these bitmaps are actually displayed on external hardware devices with small b/w displays. The bitmaps can be edited by the end user and transferred to the external hardware device. The format of the bitmaps transferred and displayed on the external hardware is 1 BPP. These bitmaps are also stored as 1BPP bitmaps in our software.

In the past we used the bitmap editor of Microsoft MSDN for these 1BPP bitmaps. This editor does not have any problems to edit such bitmaps and to maintain their format properly. Even more: it provides a specific "color palette" for 1BPP bitmaps. Instead of different colors that palette provides a selection of different b/w patterns, when editing 1BPP bitmaps.

We replaced the MSDN editor with CExtIconEditDlg and found several limitations in CExtIconEditDlg compared to MSDN. Among others we were astonished, that CExtIconEditDlg is by default not able to edit 1BPP bitmaps, that the palette is restricted to the colors already contained in the bitmap (which makes it very hard for the end user to add new colors) and that it does not provide any means for the end user to define custom colors. In the meantime we overcame all these limitations and built a neat editor. Just a few additional source code lines were needed to accomplish this.

I am a bit astonished, however, that your developers didn’t spend this small effort to make CExtIconEditDlg much more powerful and attracting.

Case closed.