|
Mark, I am not sure about what format you are talking. The image that I have
am trying to "capture" is in a device-specific format (in this case, printer's). I know that printer's DC is different from screen. This is why I am using DC-compatible bitmap and all this. Problem is that GetDIBits() works perfect with screen-compatible DC, but returns all pixels as black with printer-compatible DC. So far, I don't see where my mistake is. It could be that it's "normal" for printer DC, but then GetDIBits() (or any other function) does not return any error code, and GetDeviceCaps() says this capability is supported. Could it be that MSDN is not telling the truth?
|
|
|
|
|
Correct. Does this mean that GetDIBits() returns me data in some different format? I am calling
ret = GetDIBits(hCaptureDC,hCaptureBitmap,0,bmpInfo.bmiHeader.biHeight,pBuf, &bmpInfo, DIB_RGB_COLORS);
where
bmpInfo.bmiHeader.biBitCount = 32 and
bmpInfo.bmiHeader.biCompression = BI_RGB.
It returns me 187 (number of lines (height) in the image), which is a successful return code. When I look at the pBuf array though, all pixels are set to black.
|
|
|
|
|
hmm I'm going to back up to the beginning on this one. I may have careened way off course here
The more I think about it the more I think it should be working as you have it so one of us
is missing something.
If you do this...
HDC hCaptureDC = CreateCompatibleDC(hDC);
HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDC, img->width, img->height);
HGDIOBJ h = SelectObject(hCaptureDC, hCaptureBitmap);
BITMAP bitmap;
::GetObject(hCaptureBitmap, sizeof(BITMAP), &bitmap);
... then what are the values in "BITMAP bitmap"?
I'd like to test it out with the same kind of bitmap.
Mark
|
|
|
|
|
Mark, I have a piece of code for you so that you could just copy & paste into your program and try it. To test, call PrintBmp () function passing it a valid window handle (HWND). The function prints a small rectangle in the top left corner of the page; rectangle has white and gray pixels. Function GetPrinterBits() then gets the pixels from the same rectangle. It's easy to see that they are all returned as black (all zero), i.e. valid_image is FALSE. Let me know if you have any questions.
#include "commdlg.h"
#include "stdio.h"
typedef struct tagIMAGE {
LONG width;
LONG height;
UINT pixdepth;
DWORD imgbytes;
BYTE *bmpbits;
} IMAGE;
void SetFlagLong (DWORD &flags, DWORD flagToSet, bool onOff=true)
{
if (onOff)
flags |= flagToSet;
else
flags &= ~flagToSet;
}
BYTE * GetPrinterBits (HDC hDC, IMAGE *img, int x, int y, int &ret)
{
BITMAPINFO bmpInfo;
BYTE *pBuf = NULL;
bool valid_image = false;
HDC hCaptureDC = CreateCompatibleDC(hDC);
HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDC, img->width, img->height);
HGDIOBJ h = SelectObject(hCaptureDC, hCaptureBitmap);
ret = BitBlt(hCaptureDC, 0, 0, img->width, img->height, hDC, x, y, SRCCOPY);
memset (&bmpInfo, 0, sizeof(bmpInfo));
bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biCompression = BI_RGB;
SelectObject (hCaptureDC, h);
ret = GetDIBits(hCaptureDC,hCaptureBitmap,0,0,NULL,&bmpInfo,DIB_RGB_COLORS);
if (ret == 0)
{
ret = GetLastError();
goto exit_proc;
}
if (bmpInfo.bmiHeader.biBitCount != 32 || bmpInfo.bmiHeader.biCompression != BI_RGB)
{
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biSizeImage = 0;
}
if(bmpInfo.bmiHeader.biSizeImage<=0)
{
bmpInfo.bmiHeader.biSizeImage=bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*((bmpInfo.bmiHeader.biBitCount+7)/8);
}
if((pBuf = (BYTE *)malloc(bmpInfo.bmiHeader.biSizeImage))==NULL)
{
//MessageBox( NULL, "Unable to Allocate Bitmap Memory", "Error", MB_OK|MB_ICONERROR);
ret = -1;
goto exit_proc;
}
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biBitCount = 32;
ret = GetDIBits(hCaptureDC,hCaptureBitmap,0,bmpInfo.bmiHeader.biHeight,pBuf, &bmpInfo, DIB_RGB_COLORS);
if (ret == 0)
{
ret = GetLastError();
goto exit_proc;
}
else
{
for (int i=0; i<bmpinfo.bmiheader.bisizeimage;i++)
{
="" if="" (*(pbuf="" +="" i)="" !="0)
" valid_image="true;
" break;
="" }
="" (!valid_image)=""
="" free="" (pbuf);
="" pbuf="NULL;
" }
exit_proc:
="" deletedc(hcapturedc);
="" deleteobject(hcapturebitmap);
="" return="" pbuf;
}
int="" printpicture="" (hdc="" hdc,="" image="" *img,="" int="" x,="" y)
{
="" unsigned="" long="" ulpngwidth="img-">width;
unsigned long ulPngHeight = img->height;
unsigned long ulDstX=x, ulDstY=y;
unsigned long ulDstWidth, ulDstHeight;
long imgbytes=0;
HDC hDCScreen = GetDC(NULL);
int nScreenX = GetDeviceCaps(hDCScreen, LOGPIXELSX);
int nScreenY = GetDeviceCaps(hDCScreen, LOGPIXELSY);
ReleaseDC(NULL,hDCScreen);
double x_ratio = (double)GetDeviceCaps (hdc, LOGPIXELSX)/nScreenX;
double y_ratio = (double)GetDeviceCaps (hdc, LOGPIXELSY)/nScreenY;
ulDstWidth = (double)ulPngWidth * x_ratio;
ulDstHeight = (double)ulPngHeight * y_ratio;
int iRet=0;
BITMAPINFO bmi;
int ret2;
IMAGE img2;
img2.height = img->height * y_ratio;
img2.width = img->width * x_ratio;
img2.pixdepth = img->pixdepth;
img2.bmpbits = NULL;
img2.imgbytes = 0;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = ulPngWidth;
bmi.bmiHeader.biHeight = ulPngHeight; // top-down image
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = img->pixdepth;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = img->imgbytes;
iRet = StretchDIBits(
hdc, // handle to DC
ulDstX, // x-coord of destination upper-left corner
ulDstY, // y-coord of destination upper-left corner
ulDstWidth, // width of destination rectangle
ulDstHeight, // height of destination rectangle
0, // x-coord of source upper-left corner
0, // y-coord of source upper-left corner
img->width, // width of source rectangle
img->height, // height of source rectangle
img->bmpbits, // bitmap bits
&bmi, // bitmap data
DIB_RGB_COLORS, // usage options
SRCCOPY);
BYTE * pix = GetPrinterBits (hdc, &img2, x, y, ret2);
int ret = 0;
if (pix)
{
free (pix);
ret = 1;
}
return ret;
}
void PrintBmp (HWND hWnd)
{
PRINTDLG printDev;
IMAGE img;
img.width = 35;
img.height = 38;
img.pixdepth = 32;
img.imgbytes = img.width * img.height * 4;
img.bmpbits = (unsigned char *)malloc(img.imgbytes);
memset (img.bmpbits, 0, img.imgbytes);
BYTE *ptr = img.bmpbits;
for (int i = 0; i < img.height; i++)
for (int j = 0; j < img.width; j++)
{
*ptr = ((i + j) & 1) ? 128 : 256;
*(ptr + 1) = ((i + j) & 1) ? 128 : 256;
*(ptr + 2) = ((i + j)& 1) ? 128 : 256;
*(ptr + 3) = 0;
ptr += 4;
}
memset(&printDev, 0, sizeof(PRINTDLG));
printDev.lStructSize = sizeof(PRINTDLG);
printDev.Flags = PD_RETURNDC | PD_HIDEPRINTTOFILE | PD_NOPAGENUMS |
PD_NOSELECTION |
PD_USEDEVMODECOPIES;
SetFlagLong (printDev.Flags, PD_PRINTSETUP, false); // Turn off print setup
SetFlagLong (printDev.Flags, PD_RETURNDEFAULT, false);
SetFlagLong (printDev.Flags, PD_RETURNDC, true);
printDev.hwndOwner = hWnd;
if (!PrintDlg(&printDev))
return;
DOCINFO docInfo;
docInfo.cbSize = sizeof (docInfo);
docInfo.lpszDocName = "test";
docInfo.lpszOutput = NULL;
docInfo.lpszDatatype = NULL;
docInfo.fwType = 0;
int ret = 0;
ret = StartDoc(printDev.hDC, &docInfo);
if (0 < ret)
{
if (StartPage(printDev.hDC) >= 0)
{
PrintPicture (printDev.hDC, &img, 0, 0);
EndPage (printDev.hDC);
}
EndDoc(printDev.hDC);
}
else
{
ret = GetLastError();
}
DeleteDC(printDev.hDC);
free (img.bmpbits);
}
|
|
|
|
|
Thanks. Can you modify your post to ignore html tags? Lost some < in there
I may be able to figure 'em out.
|
|
|
|
|
Really? I don't see anything lost. One correction though:
*ptr = ((i + j) & 1) ? 128 : 256;
*(ptr + 1) = ((i + j) & 1) ? 128 : 256;
*(ptr + 2) = ((i + j)& 1) ? 128 : 256;
*(ptr + 3) = 0;
ptr += 4;
should be
*ptr = ((i + j) & 1) ? 128 : 255;
*(ptr + 1) = ((i + j) & 1) ? 128 : 255;
*(ptr + 2) = ((i + j)& 1) ? 128 : 255;
*(ptr + 3) = 0;
ptr += 4;
to make half of the pixels white. It's not that important, just to be accurate.
|
|
|
|
|
there was just one < in a for-loop.
|
|
|
|
|
Yes, now I see it. It was:
for (int i=0; i<bmpInfo.bmiHeader.biSizeImage; i++)
{
if (*(pBuf + i) != 0)
{
valid_image = true;
break;
}
}
|
|
|
|
|
OK
I've been over this code for hours
The only thing I can figure is that printer driver doesn't handle BitBlt FROM its DC.
Everything checks out until the BitBlt. I've saved DIBs and Bitmaps at all points so I could
look at them externally and they are all fine until the BitBlt() then it's all black.
(by the way, i figured/assumed the missing for-loop part, thanks )
|
|
|
|
|
It looks like this, even though it reports this capability (via GetDeviceCaps()). Now, the question is: how can I print a transparent image? AlphaBlend() is not supported on printer. The only other way to do this, as I mentioned before, is to get the pixels from a printer's DC. But they are always returned as black with BitBlt() and GetPixel() returns CLR_INVALID for any pixel for printer's DC. It's a puzzle to me.
|
|
|
|
|
I don't know if this works on printer DCs...
ImageList_Create() with ILC_COLOR32|ILC_MASK flags
ImageList_AddMasked()
ImageList_Draw()
|
|
|
|
|
I doubt it. Can you try it in your app?
I have a feeling it's a common problem with PNG images with partial transparency. Just type "print transparent image" in Google and see how many complaints about bad or simply missing functionality all kinds of programs have in this regard.
|
|
|
|
|
I doubt it too. If you can't read (blt) from a printer DC why would masked operations work.
I'm going to try both (image mask and PNG image) though
I'll be back...
|
|
|
|
|
GDI+ draws transparent/alpha blend PNGs to the printer DC.
Using the PNG from here[^] I replaced the StretchDIBits() call
in PrintPicture() with
Gdiplus::Graphics graphics(hdc);
Gdiplus::Bitmap SrcBitmap(L"C:\\test.png", FALSE);
graphics.DrawImage(&SrcBitmap, 10,10,400,400);
and it worked fine.
My initial test with an image list didn't draw anything. It should have at least drawn without
transparency. I'm looking into this further.
Using masked blts with XOR/AND may work too but I'm too lazy to try....maybe I will
-- modified at 12:07 Wednesday 3rd January, 2007 - Fixed typing
|
|
|
|
|
Actually, I tried GDI+ before: it did not print anything at all.
Let me look into it again.
|
|
|
|
|
I tried this code using GDI+. It works ok on screen, but prints nothing
|
|
|
|
|
hmm it works on Epson Stylus Photo 1280 - I haven't tried on laserjet yet.
|
|
|
|
|
Using an image list I added this
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP2);
CImageList imagelist;
imagelist.Create(400, 400, ILC_COLOR24|ILC_MASK, 1, 1);
imagelist.Add(&bitmap, RGB(0x00,0xFF,0x00));
ImageList_Draw(imagelist, 0, hdc, 360, 10, ILD_TRANSPARENT);
where IDB_BITMAP2 is a 24-bit bitmap with background RGB(0x00,0xFF,0x00).
It draws transparently fine BUT always uses the background color of the DC (it doesn't blend
with what's already drawn on the dc).
So, drawing the imagelist bitmap on top of the PNG image leaves a white rect around the bitmap.
Drawing the PNG image on top of the imagelist bitmap works correctly - it alpha blends the PNG
image with the underlying bitmap.
|
|
|
|
|
Well, in this case you _know_ your background: IDB_BITMAP2 represents it. But
problem is, in case of printer's DC, I don't have such a bitmap (that's what I was trying to get with BitBlt). And it's not a monochrome background (that'd make my life easy!) so it just does not work for me.
I am still strugling to print a PNG (or even a bitmap) with GDI+. It won't
|
|
|
|
|
Yuriy2003 wrote: Well, in this case you _know_ your background: IDB_BITMAP2 represents it.
Right. I was just reporting my findings I didn't even think it would draw on the printer's
dc transparently, but it does.
Yuriy2003 wrote: I am still strugling to print a PNG (or even a bitmap) with GDI+. It won't
And it prints to the screen fine, with alpha blending?
|
|
|
|
|
Yes, on screen DC it works perfectly. Always did.
|
|
|
|
|
I've seen that the BitBlt from the dc isn't going to work but using GDI+ to draw a PNG
on the printer dc isn't working? If not then it's a lame driver. It should at minimum
draw without blending/transparency.
|
|
|
|
|
Yes. There's something wrong between my GDI+ and printer. Problem is,
graphics.DrawImage() is void, does not return any values, so I can't check for errors. Is there a special GDI+ initialization for printer device, maybe?
All initialization I am doing is calling GdiplusStartup:
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
|
|
|
|
|
Graphics::DrawImage should return a Status enum == 0 (Gdiplus::Ok) on success.
GDI+ Initialization should be fine as you have it (that's what I'm using):
Gdiplus::GdiplusStartupInput StartupInput();
if (Gdiplus::Ok != Gdiplus::GdiplusStartup(&GdiPlusToken, &StartupInput, NULL))
{
...failed
}
You are using a Graphics object constructed using the printer DC right?
Mark
|
|
|
|
|
Yes, I am passing it a printer's DC.
P.S. Now I am trying to build an separate executable that uses GDI+ (I suspect the image might be not printed because of clipping or something else I don't control, although GDI functions work (wrongly)), and I can't even compile it. Gives me ton of compiler errors. The other project is a DLL, and it compiles fine. Same IDE, Visual Studio 2003. I am having a bad day.
|
|
|
|
|