Link to home
Start Free TrialLog in
Avatar of chual
chual

asked on

It does not look like CopyImage help at all, Dan

Dan:
It does not look like CopyImage help at all. I do appreciate your answer.
I use the following method to calculate each scan line width:
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)*lineWidth
is this not suitable for 16bpp?

chua
Avatar of DanRollins
DanRollins
Flag of United States of America image

the answer: No

Each 16-bit pixel describes its own color in an "abridged" RGB format.  So no palette is involved.

Is there a specific task you want to accomplish?  If so, state it and I will try to help.

-- Dan
Avatar of jizhang
jizhang

It depends on what you are planning to do.

For 16 bit DIB, need no palette.

DIB can be  transported in 3 formats:
 Metafile in StretchDIBits()
 BMP file
 CF_DIB -- Clipboard format.

But If you want to create CBitmap class object, or HBITMAP object,
(both are DDB ) and then save to .bmp file.

  if( dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE ) {
       you have to create a logical Palette
      with 256 colors
};

Ji Zhang
Avatar of chual

ASKER

I am actually trying to get screen capture and save into a bitmap file. Somehow, the clipboard operation doesn not work for me. The steps I have done are:
1. created a compatible bitmap,
2. select the bitmap into a memory dc,
3. BitBlt to copy the captured data into the bitmap,
4. GetBitmapBits to copy each scan line into a buffer.
5. then save this buffer into a file.
I can make it work for 256 color using the RGBQUAD
for 16bpp 24bpp or 32bpp, I store their actually color bits into the file. It works fine for 24bpp and 32bpp, but the color get distorted on 16bpp. So looks like for 16bpp, simply saving the pixels into the file is not good enough.
Thanks
Looking for an quick solution... what if you use CopyImage to create a DIB?  There is a chance that this will end up with an acceptable image.

-- Dan
>>define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)*lineWidth
>>is this not suitable for 16bpp?

If the image looks OK, but only the colors are distorted, then your calculations should be fine.

Note that colors are certain to be a little bit off -- simply because the system will need to interpolate from the HighColor 6-5-5 RGB format to the 8-8-8 of TrueColor.

-- Dan

P.S.  In the future, please don't modify the question or the question title.  Just add a new post to the end.  That will avoid several kinds of confusion.
Avatar of chual

ASKER

To Dan:
Thanx, So is there a way to interpolate from HighColor to TrueColor or is there a different way of saving the bitmap such that windows will handle this situation for me?

chua
>>define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)>>*lineWidth
>>is this not suitable for 16bpp?

I guess:

define WIDTHBYTES(bits) (((bits * lineWidth) + 31) / 32 * 4)

so that
DWORD aligned for each scan line.

because of one DWORD == 32 bits == 4 BYTEs


I think you are creating a "compatible" bitmap.  But if you create one that specifies the desired size and color depth, then perhaps when you bitblit the data to it, it will do the interpolation.  Just guessing tho.

-- Dan
hi chual,
Do you have any additional questions?  Do any comments need clarification?

-- Dan
Avatar of chual

ASKER

hi Dan,
Don't know yet. I have tried playing around with the BITMAPINFO structure. MSDN's documentation regarding 16bpp's implementation doesnot help at all. Looks like I have done nothing wrong with my code. But just don't know why it doesnot work.
I have also tried using clipboard operation, but even these simplest method could not sent the captured screen to the clipboard, too.
But the PRINT SCREEN key together with MS Paint is able to get it work. So I don't know how it was done. You have any idea?
chual
One thing about Ms Paint -- its Image Properties only let you choose color or mono ... when you choos colors, it sets up to use settings compatible with your current scree settings.  Thus, if you are currently set to 256-colors, and paste a screen dump into MS Paint, you will have a 256-color bmp.  If your screen is set to Hi-COlor (16bpp) then you will have a 16bpp bmp.

You can work around such oddities by changing the screen resolution Control Panel.  Grab an image into MS Paint, and save it.  Then switch resolutions and read the image back into MS Paint and save it with a new name.  Now you have the same image in two different bpp's for experimentation.

-- Dan
Have you tried RGB as 565 and 555, default is 555.

Set 65636 colors in control-panel-> display-settings->color palette
before doing capture may simplify your problem. color-depth would
 = 16bit -- need no change color depth.

for clipboard, I use:
       pWnd->OpenClipboard() ;
        EmptyClipboard() ;
        SetClipboardData (CF_BITMAP, bitmap.GetSafeHandle() ) ;
        CloseClipboard () ;
The key is use keyword "CF_BITMAP".
Avatar of chual

ASKER

I have tried both format for the RGB. None of them works. My application allows user to print a screen capture of the application. And I can't just tell them to change their screen resolution when using the feature. The clipboard operations I have tried out is exactly the same as what you show up there. But it did not work either. Anyway, thanx for the suggestionn. I will keep trying.
chual
Avatar of chual

ASKER

Or do you happen to know how to convert a captured 16bpp bitmap in memory into other resolution like 4, 8, 24, or 32bpp. Then that will solve my problem. Thanx.
chual
Avatar of chual

ASKER

Maybe I should say how to map each 16bpp pixel into a pixel of different bit. Thanx
Brian
There is a class BitmapLib on codeguru site, which
can change BMP file color depth. I tried (from 32bit to 24 bit),
it works on Windows NT and 2000.
   ChangeColorDepth(bmpfilein, bmpfileout, depth, NULL);

The bpp of captured bitmap is depends on Device, in
my case, if set display to true color, I get 32 bit,
if set to 16 bit, I get 16 bit. CBitmap or HBITMAP are DDB.

For not compressed BMP, it should be easy to map 16 bit to 24,32
and write BMP file.

Resolution Bits per color Color Mask (RGB)
16bpp       5,5,5           0x00007c00 0x000003e0 0x0000001f
16bpp       5,6,5           0x0000f800 0x000007e0 0x0000001f
32bpp       8,8,8           0x00ff0000 0x0000ff00 0x000000ff

From 16 bit to lower, the colors could be lost, need to find "important"
colors and put into color table.

BMP struct
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD          aColors[];   may not have. table should appear in order of importance.
BYTE             aBitmapBits[];

scanline width should be:  integer * 4 BYTES, if not, fill zeros.
Avatar of chual

ASKER

-Dan, But Paint Brush will not let you save the file in 16bpp. You only get the 16bpp in memory.

-jiZhang, How do we make use of these masks anyway. Are we supposed to use the color mask with its own bpp? In 16bpp, If you AND each color component with the 16bpp mask, you just get your original value. while AND will set all pixel to WHITE. or is it that if i want to convert from 16bpp to 32 bpp, I will have to mask each color component(16bpp) with the 32bpp mask. But it does not make sense either (or should SHIFT operation be involved in this case?).

Thanx

chual
Avatar of chual

ASKER

-jiZhang, I meant OR the components of each 16bpp pixel with their respective masks with result in all WHITE pixel.
chual
Avatar of chual

ASKER

I think these masks are just for retrieving each color component from each pixel.
chual
If you use BYTE for the bitmapbits, convert 16 555 to 24 888:
color scale:  0 -> 0,  0x1f -> 0xff.

  loop scan line.
   get   Byte16[0] and Byte16[1]  (cast to int for calculation )
     Blue = (Byte16[1] & 0x1f) * 0xff / 0x1f
     Red = ((Byte16[0] & 0x7c) >> 2 ) * 0xff / 0x1f
     Green = {[(Byte16[0] & 0x03) << 3] + [(Byte16[1] & 0xe0 >> 5)]} * 0xff / 0x1f
   put   Byte24[0], Byte24[1], Byte24[2] with value Red, Green, Blue

   in this way, get each 2 BYTEs from 16 bitmap, convert to 3 BYTEs for 24 bit bitmap.

for 32 bit, output one more zero BYTE.
You really don't want (or need) to be messing with the raw image data.  

Just create an empty bitmap that is the right size and color depth, then use StretchBlit to copy it intelligently to the destination.  It will preserve whatever color it can.

Please restate your original question.  Describe exactly what you are trying to do -- that is, the *end result* (what the end user will see.)  Also increase the points.

-- Dan
Avatar of chual

ASKER

jizhang- i have to work on other stuff last weeks. Just had time to try out your algorithm yesterday. Your algorithm is wonderful. Theoretically, it should work great. The color is closer than what I had before but still too pale and I have to xchange Red and Green component ie. Byte24[0] = Green, Byte24[1] = Red, and Byte24[2] = Blue, in order to get green and blue displayed properly. otherwise green will be shown as blue and blue will be shown as green???

I was wondering if i should convert the color bit and map the color at all?
Here is my original codes (without converting the color bit), maybe you guys can figure out if there is something i have missed. Thanks.

// for color with 16bit and up
if (bm.CreateCompatibleBitmap(&dc, width, height))
{
  CBitmap* pOldBmp = dcMem.SelectObject(&bm);
  int iLnWidth = WIDTHBYTES((DWORD)width * Info.bmiHeader.biBitCount);
  bmInfo.bmiHeader.biCompression = BI_RGB;
  bmInfo.bmiHeader.biPlanes = 1;
  bmFH.bfType = DIB_HEADER_MARKER; //"BM"
  bmFH.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) +  sizeof(BITMAPINFO);
  bmFH.bfSize = bmFH.bfOffBits + WIDTHBYTES((DWORD)width * bmInfo.bmiHeader.biBitCount) * height;      
  file.Write((LPSTR)&bmFH, sizeof(bmFH));
  file.Write(&bmInfo.bmiHeader, sizeof(bmInfo.bmiHeader));

  LPVOID pBits = new BYTE[iLnWidth];
  for (int i = rect.Height()-1; i >= 0; i--)
  {
     dcMem.BitBlt(0, 0, width, 1, &dc, 0, i, SRCCOPY);
     bm.GetBitmapBits(iLnWidth, pBits);
     file.Write(pBits, iLnWidth);
  }
  ....
}
file.Close();

>>I was wondering if i should convert the color bit and map the color at all?

No, you should not.  Messing around with the raw image data is inadvisable and rarely needed.  Let the system handle it, as I have explained.

-- Dan
> I was wondering if i should convert the color bit and map the color at all?

The color depth of the BMP you saved depends on DC, because
CBitmap object is DDB when you call CreateCompatibleBitmap,
the color depth is "compatible" with the DC.

If you want to get 16 bit depth, you need to set display-setting as 16 bit.

if a user use true color, then he/she will get 32 bit.
If you do not want to ask users to set display and stil want user  to get 16 bit,
then you have to deal with the converting.

I used to do is convert 32 bit to 24 bit (if the depth is 32 bit) only, because many
softwares can not display 32 bit bmp. In the 1992 bmp definition, it mentioned
1,4,8,24 bit, and said that would add 16 bit "in furtue", but did not mention 32 bit
at all.

Ji Zhang

 
For 24 bit bmp, the BYTE order is BGR,
the scanline is started from left-bottom corner.

Here is a prog in C  to write a 72x72 pixels with 24 color-bit BMP:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned char img[15552];  /* 72 x 3 x 72  */

FILE * fout;


main()
{
char nameout[128];
BITMAPFILEHEADER  hdr;
BITMAPINFOHEADER  bi;
int i,j,k;

strcpy(nameout,"a24.bmp");
k =0;
/* BGR */
  for (j=0;j<24;j++) {
  for (i=0;i<24;i++) {
  img[k]= 0xff; img[k+1] = 0x00; img[k+2] = 0xff; k = k + 3;
  };
  for (i=0;i<24;i++) {
  img[k]= 0x00; img[k+1] = 0xff; img[k+2] = 0x80; k = k + 3;
  };
    for (i=0;i<24;i++) {
  img[k]= 0x00; img[k+1] = 0x80; img[k+2] = 0xff; k = k + 3;
  };
};

for (j=0;j<24;j++) {
  for (i=0;i<24;i++) {
  img[k]= 0xff; img[k+1] = 0xff; img[k+2] = 0x00; k = k + 3;
  };
  for (i=0;i<24;i++) {
  img[k]= 0x00; img[k+1] = 0x00; img[k+2] = 0x00; k = k + 3;
  };
    for (i=0;i<24;i++) {
  img[k]= 0x80; img[k+1] = 0xAf; img[k+2] = 0x40; k = k + 3;
  };
};
 

 for (j=0;j<24;j++) {
  for (i=0;i<24;i++) {
  img[k]= 0x00; img[k+1] = 0x00; img[k+2] = 0xff; k = k + 3;
  };
  for (i=0;i<24;i++) {
  img[k]= 0x00; img[k+1] = 0xff; img[k+2] = 0xff; k = k + 3;
  };
    for (i=0;i<24;i++) {
  img[k]= 0xff; img[k+1] = 0x00; img[k+2] = 0x00; k = k + 3;
  };
};

    hdr.bfType    = ((WORD) ('M' << 8) | 'B');    /* is always "BM"    */
    hdr.bfSize    = (DWORD)( 15552 + sizeof(BITMAPFILEHEADER) +  sizeof(BITMAPINFOHEADER));  
    hdr.bfReserved1  = 0;
    hdr.bfReserved2  = 0;
    hdr.bfOffBits  = (DWORD)( sizeof(BITMAPFILEHEADER) +  sizeof(BITMAPINFOHEADER));

      bi.biSize      = sizeof(BITMAPINFOHEADER);
      bi.biWidth    = 72;
      bi.biHeight   = 72;
      bi.biPlanes   = 1;
      bi.biBitCount       = 24 ;
      bi.biCompression   = BI_RGB ;
      bi.biSizeImage          = 0;
      bi.biXPelsPerMeter      = 0;  
      bi.biYPelsPerMeter      = 0;
      bi.biClrUsed            = 0;
      bi.biClrImportant       = 0;

   if ( (fout = fopen(nameout,"wb")) ==NULL ){      
      printf("\007Cann't open output file: %s ", nameout);
      exit(1);
      };
  fwrite( &hdr, sizeof(hdr),1,fout );
  fwrite( &bi, sizeof(bi),1,fout);
  fwrite( &img, sizeof(unsigned char),15552,fout);
  fclose(fout);
  printf("\007Output in %s\n",nameout);
 return 0;
}

Ji Zhang
ASKER CERTIFIED SOLUTION
Avatar of jizhang
jizhang

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of chual

ASKER

Thanks for you all you guys' help. All the information has been very helpful. But I have to work on other thing for now. I have been trying to convert 16bpp to 24bpp. Though the color is still a bit off and turn pale. But I think I am on the right path and I will stick to it later on. Thanks again for your help.