|
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.
|
|
|
|
|
Have some news. I was able to compile and build the code using GDI+ in a standalone app. It actually printed the image (the DLL using the same code is not printing), not the size I expected, but on a b/w printer it actually printed it with a shadow! On the color printer though, it still ignored background for all pixels with alpha (transparency) values greater than 0. It's still a big step forward. Now I am looking into why it's not working off the DLL, and why it's printing image of a diffferent size.
|
|
|
|
|
Cool
I forgot to mention - are you using StretchBlt or StretchDiBits anywhere still? You may want to
set the stretch mode properly if necessary and restore it after the stretch operation. I noticed
that in the code I've been testing from (although it made no difference in the problem).
To get a blend of more than one alpha-blend image you may need to do it all in a memory DC then
do one blt to the printer DC - I think GDI+ can handle that. I've been unable to get a blend on
a printer DC. The printer DCs behave write-only so any operations requiring reading colors
already there fail...
|
|
|
|
|
Mark, just want to let you know that I finally was able to print PNG with GDI+ correctly (that is, correct size). There's still a minor problem with shadows on color printers, but I can live with it. Apparently, the size of image that I have to pass to DrawImage() is different from that used in StretchDIBits() as destination coordinates. Looks like DGI+ does its own stretching. This is why my original code using GDI+ was returning successful code but did not print: the image was beyond the page size. Thank you for your help.
P.S. Where are you located (physically)?
|
|
|
|
|