[Webinar] Streamline your web hosting managementRegister Today

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 803
  • Last Modified:

using CIMAGE.LIB to make JPEGs

It's been asked 100 times already:  
How to write a bitmap to a JPEG file?
But I can't make it work either.
I can generate .BMPs without difficulty, but not .JPEGs.
An example of the contrast between my BMP and JPEG result
can be seen by following this link:

http://www.pointcover.com/snapshot.htm   

It seems like  most advice on this subject directs developers to CIMAGE.LIB.
CIMAGE.LIB is available from many sources around the web.
(e.g. http://www.anthemion.co.uk/code.htm )

Following the brief (too brief!) tutorial, I compiled the libraries
and added some simple code to my application to export a snapshot
of my CWnd subclass to a jpeg file.

Oddly, there is no problem when exporting to .BMP format,
but hideous artifacts are present in the JPEG format.
How could such a popular library have such hideous bugs?
There must be a problem with my very simple code.

Do I need to convert DDB to DIB or something?
What the hell is going wrong here?

      // device context for painting
      CPaintDC dc(this);
      CDC     memDC;
      CBitmap *pOldBitmap;
      CRect rect; GetClientRect(rect);
      CSize dcSize = rect.Size();
      // Need a memory DC
      memDC.CreateCompatibleDC(&dc);
      // Select in the bitmap
      CBitmap mBitmap;
      mBitmap.CreateCompatibleBitmap(&dc,
            dcSize.cx, dcSize.cy);
      pOldBitmap =  memDC.SelectObject(&mBitmap);
      :
      :
      :
      // Jpeg stuff
      CImage myJpegImage(&mBitmap);
      CString filename = "C:\\WINDOWS\\Desktop\\snapshot.jpg";
      int imageType = CIMAGE_FORMAT_JPEG;
      myJpegImage.SaveFile(filename, imageType);

      // Bmp stuff
      CImage myBmpImage(&mBitmap);
      filename = "C:\\WINDOWS\\Desktop\\snapshot.bmp";
      imageType = CIMAGE_FORMAT_BMP;
      myBmpImage.SaveFile(filename, imageType);

// This did not work wither
//      CImage myBmpImage(&mBitmap);
//      int imageType = CIMAGE_FORMAT_BMP;
//      filename = "C:\\WINDOWS\\Desktop\\snapshot.bmp";
//      myBmpImage.SaveFile(filename, imageType);
//      CImage myJpegImage;
//      myJpegImage.ReadFile(filename, CIMAGE_FORMAT_BMP);
//      imageType = CIMAGE_FORMAT_JPEG;
//      filename = "C:\\WINDOWS\\Desktop\\snapshot.jpg";
//      myJpegImage.SaveFile(filename, imageType);
   
      // Clean up
      memDC.SelectObject(pOldBitmap);
0
cternoey
Asked:
cternoey
  • 6
  • 4
1 Solution
 
nil_dibCommented:
seems to be color problem ... if this is a screenshot try play with the display color setting (set to 256 color depth..)

nil_dib
0
 
zoferCommented:
Maybe you are using the wrong libarary...

I recommend Intel's Jpeg Libarary.
Although its API is in C, It is easy
to figure out what is going on.

Can be found at:
http://developer.intel.com/vtune/perflibst/
0
 
cternoeyAuthor Commented:
to: nil_dib
Thanks for the suggestion. Changing the settings of my monitor to 256 did improve the situation. This is a great clue. However, it degrades the image significantly. And I can not expect users to accomdate this requirement. Any more tips along this line?
0
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
cternoeyAuthor Commented:
to: zofer
I was not aware of the intel library. Thanks for pointing it out. It looks very powerful. Hwever, my drawing code uses CDC methods to manipulate a CBitmap object. And I can not see any way to initialize their data structures with a CBitmap object or access their data structures thru a CDC interface. If you know a way, please tell me. Thanks again for just pointing out this interesting library.
0
 
cternoeyAuthor Commented:
Edited text of question.
0
 
nil_dibCommented:
look for the evaluation of the colors.
I use the following code to capture a window:


unsigned short CScreenCapture::DIBNumColors(char * lpDIB)
{
      unsigned short wBitCount;  // DIB bit count
      unsigned long dwClrUsed;
      
      dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed;
      if (dwClrUsed)
            return (WORD)dwClrUsed;
      
      //      Calculate the number of colors in the color table based on
      //      the number of bits per pixel for the DIB.
      
      wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
      
      // return number of colors based on bits per pixel
      switch (wBitCount)
      {
      case 1:
            return 2;
      case 4:
            return 16;
      case 8:
            return 256;
      default:
            return 0;
      }
}


int CScreenCapture::WriteDIB( )
{
      BITMAPFILEHEADER hdr;
      LPBITMAPINFOHEADER lpbi;
             
      if (!m_pvDIB)
            return 0;
             
      CFile file;
      if( !file.Open( m_pszFilename, CFile::modeWrite|CFile::modeCreate) )
            return 0;
             
      lpbi = (LPBITMAPINFOHEADER)m_pvDIB;
      int nColors = DIBNumColors((char*)m_pvDIB);
             
      // Fill in the fields of the file header
      hdr.bfType                    = ((WORD) ('M' << 8) | 'B');        // is always "BM"
      hdr.bfSize                    = GlobalSize (m_pvDIB) + sizeof( hdr );
      hdr.bfReserved1         = 0;
      hdr.bfReserved2         = 0;
      hdr.bfOffBits              = (DWORD) (sizeof( hdr ) + lpbi->biSize +
            nColors * sizeof(RGBQUAD));
             
      // Write the file header
      file.Write( &hdr, sizeof(hdr) );
             
      // Write the DIB header and the bits
      file.Write( lpbi, GlobalSize(m_pvDIB) );
      file.Close();       
             
      return 1;
}




void * CScreenCapture::DDBToDIB( CBitmap& bitmap, DWORD dwCompression, CPalette* pPal )
{
      BITMAP                              bm;
      BITMAPINFOHEADER            bi;
      LPBITMAPINFOHEADER            lpbi;
      DWORD                              dwLen;
      void*                              hDIB;
      void*                              handle;
      HDC                               hDC;
      HPALETTE                        hPal;
      
      ASSERT( bitmap.GetSafeHandle() );
      
      // The function has no arg for bitfields
      if( dwCompression == BI_BITFIELDS )
            return 0;
      
      // If a palette has not been supplied use defaul palette
      hPal = (HPALETTE) pPal->GetSafeHandle();
      if (hPal==0)
            hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
      
      // Get bitmap information
      bitmap.GetObject(sizeof(bm),(char*)&bm);
      
      // calculate bits per pixel
      bi.biBitCount = bm.bmPlanes * bm.bmBitsPixel;
      
      // make sure bits per pixel is valid
      if (bi.biBitCount <= 1)
            bi.biBitCount = 1;
      else if (bi.biBitCount <= 4)
            bi.biBitCount = 4;
      else if (bi.biBitCount <= 8)
            bi.biBitCount = 8;
      else // if greater than 8-bit, force to 24-bit
            bi.biBitCount = 24;
      // Initialize the bitmapinfoheader
      bi.biSize                        = sizeof(BITMAPINFOHEADER);
      bi.biWidth                        = bm.bmWidth;
      bi.biHeight                   = bm.bmHeight;
      bi.biPlanes                   = 1;
      //      bi.biBitCount                  = bm.bmPlanes * bm.bmBitsPixel;
      bi.biCompression            = dwCompression;
      bi.biSizeImage                  = 0;
      bi.biXPelsPerMeter            = 0;
      bi.biYPelsPerMeter            = 0;
      bi.biClrUsed                  = 0;
      bi.biClrImportant            = 0;
      
      // Compute the size of the      infoheader and the color table
      dwLen  = bi.biSize + DIBNumColors((char*)&bi) * sizeof(RGBQUAD);
      
      // We need a device context to get the DIB from
      hDC = GetDC(0);
      hPal = SelectPalette(hDC,hPal,0);
      RealizePalette(hDC);
      
      // Allocate enough memory to hold bitmapinfoheader and color table
      hDIB = GlobalAlloc(GMEM_FIXED,dwLen);
      
      if (!hDIB)
      {
            SelectPalette(hDC,hPal,0);
            ReleaseDC(0,hDC);
            return 0;
      }
      
      lpbi = (LPBITMAPINFOHEADER)hDIB;
      
      *lpbi = bi;
      
      // Call GetDIBits with a 0 lpBits param, so the device driver
      // will calculate the biSizeImage field
      GetDIBits(hDC, (HBITMAP)bitmap.GetSafeHandle(), 0L, (DWORD)bi.biHeight,
            (LPBYTE)0, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS);
      
      bi = *lpbi;
      
      // If the driver did not fill in the biSizeImage field, then compute it
      // Each scan line of the image is aligned on a DWORD (32bit) boundary
      if (bi.biSizeImage == 0)
      {
            bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8)
                  * bi.biHeight;
            
            // If a compression scheme is used the result may infact be larger
            // Increase the size to account for this.
            if (dwCompression != BI_RGB)
                  bi.biSizeImage = (bi.biSizeImage * 3) / 2;
      }
      
      // Realloc the buffer so that it can hold all the bits
      dwLen += bi.biSizeImage;
      if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE))
            hDIB = handle;
      else
      {
            GlobalFree(hDIB);
            
            // Reselect the original palette
            SelectPalette(hDC,hPal,0);
            ReleaseDC(0,hDC);
            return 0;
      }
      
      // Get the bitmap bits
      lpbi = (LPBITMAPINFOHEADER)hDIB;
      
      // FINALLY get the DIB
      int bGotBits = GetDIBits( hDC, (HBITMAP)bitmap.GetSafeHandle(),
            0L,                                           // Start scan line
            (DWORD)bi.biHeight,                   // # of scan lines
            (LPBYTE)lpbi                              // address for bitmap bits
            + (bi.biSize + DIBNumColors((char*)&bi) * sizeof(RGBQUAD)),
            (LPBITMAPINFO)lpbi,                   // address of bitmapinfo
            (DWORD)DIB_RGB_COLORS);             // Use RGB for color table
      
      if( !bGotBits )
      {
            GlobalFree(hDIB);
            SelectPalette(hDC,hPal,0);
            ReleaseDC(0,hDC);
            return 0;
      }
      
      SelectPalette(hDC,hPal,0);
      ReleaseDC(0,hDC);
      return hDIB;
}


int CScreenCapture::Capture( CWnd *pWnd )
{
      CBitmap   bitmap;
      CWindowDC  dc(pWnd);
      CDC memDC;
      CRect rect;
             
      // Free the memory allocated by DDBToDIB for the DIB
      if (m_pvDIB)
            GlobalFree( m_pvDIB );
      m_pvDIB = 0;

      memDC.CreateCompatibleDC(&dc);
      pWnd->GetWindowRect(rect);
      bitmap.CreateCompatibleBitmap(&dc, rect.Width(),rect.Height() );                                    
      CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
      memDC.BitBlt(0, 0, rect.Width(),rect.Height(), &dc, 0, 0, SRCCOPY);
             
      // Create logical palette if device support a palette
      CPalette pal;
      if( dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE )
      {
            unsigned nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
            LOGPALETTE *pLP = (LOGPALETTE *) new unsigned char[nSize];
            pLP->palVersion = 0x300;
            pLP->palNumEntries = GetSystemPaletteEntries( dc, 0, 255, pLP->palPalEntry );
            
            // Create the palette
            pal.CreatePalette( pLP );
            delete[] pLP;
      }
        
      memDC.SelectObject(pOldBitmap);
             
      // Convert the bitmap to a DIB
      m_pvDIB = DDBToDIB( bitmap, BI_RGB, &pal );
      if( m_pvDIB == 0 )
            return 0;
             
      return 1;
}


nil_dib
0
 
cternoeyAuthor Commented:
nil_dib:
Thanks!
0
 
nil_dibCommented:
you got it?
0
 
cternoeyAuthor Commented:
nil_dib:
yes, want some points?
0
 
cternoeyAuthor Commented:
nil_dib:
Sorry, I am fumbling a bit with the system as a newbie to the process...
0
 
nil_dibCommented:
no prob ;-)
thanks
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 6
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now