I made a print preview control. I used GDI+ for pictures. But PNG's transparency Part and none AntiAlias edge got weird effect when draw on Meta DC which created from Printer.hDC.
Public Sub gdipStretchPicture(ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal Width As Long, ByVal Height As Long, hImage As Long, ByVal srcX As Long, ByVal srcY As Long, ByVal srcWidth As Long, ByVal srcHeight As Long, Optional ByVal srcUnit As GpUnit = UnitPixel,Optional Attributes as Long = 0)
If hImage <> 0 Then
Dim hGraphics As Long
GdipCreateFromHDC hdc, hGraphics
GdipDrawImageRectRectI hGraphics, hImage, x, y, Width, Height, srcX, srcY, srcWidth, srcHeight, srcUnit, Attributes
GdipReleaseDC hGraphics, hdc
GdipDeleteGraphics hGraphics
End If
End Sub
Does anybody have any pointers on how to successfully draw a bitmap that has an alpha channel using Graphics: DrawImage() when the Graphics context is created based on a printer HDC? The printer drivers don't generally support alpha blending - so is there an alternative to rendering everything to an offscreen bitmap and just sending that to the printer. This is often not feasible, especially for high res printing to large format printers.
Our program needs to generate vector graphics, and we chose EMF for that. However, it seems that other programs render these images non-antialiased. I found that SVG format does have a flag to indicate that some/all objects should be antialiased.
I also tried to play back metafiles on Win7 but some of them weren't rendered correctly, some shapes or colors were missing.
The DrawImage method is also an option, but the problem is that this method cannot render the metafiles with antialiasing, you can set the Graphics.SmoothingMode = SmoothingMode.AntiAlias but it has no effect on metafiles.
Arnaud Bouchez answered Jan 20 '11 at 18:48
EMF file is a list of GDI commands. So it won't be anti-aliaised, even if under GDI+, you put a SmoothingMode() call before the drawing. You'll have to enumerate the GDI commands, then translate it into GDI+ commands.
Under Vista/Seven, you can use GDI+ 1.1 function named GdipConvertToEmfPlus/ConvertToEmfPlus. If you want your program to work with XP, you should write your own enumeration, then conversion to GDI+ commands.
The GDI enumeration then conversion to GDI+ is possible has been done by emfexplorer, but I've written some code perhaps more easy to follow, even if it's written in Delphi.
I'm putting this answer just now (I'm late), because I spent a lot of time finding out a solution using ConvertToEmfPlus, and writing some tuned open source code in case this method is not available.
So, I have to convert to standard picture object then draw...
Last edited by Jonney; Aug 29th, 2015 at 08:59 PM.
What's the output when you use (as additional CodeLines, applied directly after your ImageLoad-Line in Step 2.)
Code:
Dim hDIB As Long
GdipCreateHBITMAPFromBitmap hImage, hDIB, vbWhite
If hDIB Then
GdipDisposeImage hImage
GdipCreateBitmapFromHBITMAP hDIB, 0, hImage
DeleteObject hDIB
End If
Here's the API-Declares for these additional 3 lines:
Code:
Private Declare Function GdipCreateHBITMAPFromBitmap Lib "gdiplus" (ByVal hImage As Long, hDIBReturn As Long, ByVal BackColor As Long) As Long
Private Declare Function GdipCreateBitmapFromHBITMAP Lib "gdiplus" (ByVal hDIB As Long, ByVal hPal As Long, hImageReturn As Long) As Long
Private Declare Function DeleteObject& Lib "gdi32" (ByVal hGDIObject&)
Re: PNG transparency weird: GDI+ Draw into Meta DC
Originally Posted by Schmidt
What's the output when you use (as additional CodeLines, applied directly after your ImageLoad-Line in Step 2.)
Code:
Dim hDIB As Long
GdipCreateHBITMAPFromBitmap hImage, hDIB, vbWhite
If hDIB Then
GdipDisposeImage hImage
GdipCreateBitmapFromHBITMAP hDIB, 0, hImage
DeleteObject hDIB
End If
Here's the API-Declares for these additional 3 lines:
Code:
Private Declare Function GdipCreateHBITMAPFromBitmap Lib "gdiplus" (ByVal hImage As Long, hDIBReturn As Long, ByVal BackColor As Long) As Long
Private Declare Function GdipCreateBitmapFromHBITMAP Lib "gdiplus" (ByVal hDIB As Long, ByVal hPal As Long, hImageReturn As Long) As Long
Private Declare Function DeleteObject& Lib "gdi32" (ByVal hGDIObject&)
Olaf
Now it looks OK though GdipCreateHBITMAPFromBitmap API fill transparency with a color. For Printing case, I will fill Grid1.Cell(Row,Col).BackColor.
Public Sub gdipStretchPictureEx(ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal Width As Long, ByVal Height As Long, hImage As Long, ByVal srcX As Long, ByVal srcY As Long, ByVal srcWidth As Long, ByVal srcHeight As Long, Optional ByVal gdipAttributes As Long, Optional ByVal srcUnit As GpUnit = UnitPixel, Optional FillColor As OLE_COLOR = vbWhite)
If hImage <> 0 Then
Dim hDIB As Long
Dim hgdipImage As Long
GdipCreateHBITMAPFromBitmap hImage, hDIB, RGBtoARGB(FillColor, 255)
If hDIB Then
'GdipDisposeImage hImage 'don't dispose Image.gdipImage,Let Image Class take care Dispose!
GdipCreateBitmapFromHBITMAP hDIB, 0, hgdipImage
DeleteObject hDIB
End If
Dim hGraphics As Long
GdipCreateFromHDC hdc, hGraphics
GdipDrawImageRectRectI hGraphics, hgdipImage, x, y, Width, Height, srcX, srcY, srcWidth, srcHeight, srcUnit, gdipAttributes
GdipReleaseDC hGraphics, hdc
GdipDeleteGraphics hGraphics
GdipDisposeImage hgdipImage 'draw done,Dispose it!
End If
End Sub
Thank you sir.
Edited: The picture got light gray background though I fill with VBWhite.
I convert to Picture Object. Then I got white background.
Code:
Public Function gdipImageToPicture(ByVal hImage As Long, Optional ByVal FillColor As OLE_COLOR = vbWhite) As StdPicture
'Convert the GDI+ bitmap to a standard Windows hBitmap for GDI-friendly and VB6 picturebox
Dim hBitmap As Long
Dim GDIPlusReturn As Long
GDIPlusReturn = GdipCreateHBITMAPFromBitmap(hImage, hBitmap, RGBtoARGB(FillColor, 255))
If GDIPlusReturn = 0 Then
' ' Create the StdPicture object
Set gdipImageToPicture = HandleToPicture(hBitmap, vbPicTypeBitmap)
' ' Dispose the GDI+ image
' 'dipDisposeImage hImage
'Else
' ' Dispose the GDI+ image
' GdipDisposeImage hImage
End If
End Function
Last edited by Jonney; Aug 30th, 2015 at 12:09 AM.
Re: PNG transparency weird: GDI+ Draw into Meta DC
Originally Posted by dilettante
That or avoid synthetic "pixel" metrics and stick with absolute coordinate systems whenever possible (twips, inches, cm, himetric, etc.).
Agreed. But I stick with pixel because GDI+ like pixel.
My GDI code is draw on hMetaDC. GDI+ code is to draw on hGraphics which created from hMetaDC.
hMetaDC = CreateEnhMetaFile(Printer.hdc, vbNullString, rct, "gdiApp") ---> GDI
GdipCreateFromHDC hMetaDC, hGraphics --->GDI+
My bullshit describes GDI hMetaDC (or any hdc) and GDI+ hGraphics is a piece of Canvas or cloth. hMetaDC's Weave density is 600 dpi. hGraphics's Weave density is 96 dpi. So hGraphics have to condense.
Can seniors give me logic scientific explanation and solution? Maybe one API works so that there's no need to play with dpi ratio 600/96.
Edited: a demo named flexprint by guru Schmidt shows the concept using MetaDC for printing. #3 shows the coordinates problem using GDI+.
Last edited by Jonney; Oct 20th, 2015 at 03:58 AM.
Re: PNG transparency weird: GDI+ Draw into Meta DC
Well many bitmap graphics file formats have DPI settings as well. It is very common for cameras to record JPEG images at 72x72 DPI and when the image file has no DPI values set within it 72 DPI is Earth Standard (default).
A great deal of software will ignore this entirely and map one pixel to one dot. However a 100x100 image recorded at 72 DPI x 72 DPI should really take 133x133 screen dots (pixels) if displayed properly. Printed on a 600x600 DPI printer it should take 833x833 printer dots.
If you ignore scaling and map one image dot to one device dot then the picture will be smaller than it should be, because most common devices have greater than 72 DPI resolution. The alternative requires you to scale the image to fit the rendering canvas.
That will keep a 100x100 "pixel" image (100/72 inches by 100/72 inches) the proper size (1 1/3 inches square).
Re: PNG transparency weird: GDI+ Draw into Meta DC
Originally Posted by dilettante
Well many bitmap graphics file formats have DPI settings as well. It is very common for cameras to record JPEG images at 72x72 DPI and when the image file has no DPI values set within it 72 DPI is Earth Standard (default).
A great deal of software will ignore this entirely and map one pixel to one dot. However a 100x100 image recorded at 72 DPI x 72 DPI should really take 133x133 screen dots (pixels) if displayed properly. Printed on a 600x600 DPI printer it should take 833x833 printer dots.
If you ignore scaling and map one image dot to one device dot then the picture will be smaller than it should be, because most common devices have greater than 72 DPI resolution. The alternative requires you to scale the image to fit the rendering canvas.
That will keep a 100x100 "pixel" image (100/72 inches by 100/72 inches) the proper size (1 1/3 inches square).
I am not sure #1 issue is related to Scale. The shadow part is blur and has Sawtooth. My current solution is to convert to StdPicture. But I will investigate further.
I would say the metaDC created by CreateEnhMetaFile ( hMetaDC = CreateEnhMetaFile(lhPrinterDC, vbNullString, rct, "gdiApp") ) has funny behavior when using GDI+, that I don't fully understand currently. Seemed it related to DPI or EMF file format.
It describes several different mechanisms for making GDI and GDI+ coordinates identical, depending on the target DC.
It might be helpful to remember GDI and GDI+ were designed for different purposes. GDI is meant as a low-level, "catch all" graphics system that can be applied to any arbitrary device. GDI+ was designed primarily for screen usage. So the two architectures favor different approaches.
Generally, GDI+ won't help much during printing, because antialiasing is not required when printing. Antialiasing is just a "hack" to make lines and shapes look better on screens with low resolution. (On high-DPI screens, you generally don't need to antialias, either.) If you are printing at 600 DPI, the difference between antialiased and non-antialised lines and text is indistinguishable, so when printing, GDI itself should be fine if you already have working code for it.
So maybe you could just use GDI+ for the print preview version, and leave the actual GDI printing code untouched...?
Tanner,Thanks you very much for the link.
Code:
'sets the unit of measure for this Graphics object from UnitDisplay to UnitPixel
GdipSetPageUnit hGraphics, UnitPixel
GdipSetSmoothingMode hGraphics, SmoothingModeAntiAlias
Seemed Preview/Printing is OK now w/o changing anything but the print preview has poor effective with sawtooth,though Printing is AntiAlias and Chart in Grid Control is AntiAlias.
Last edited by Jonney; Oct 20th, 2015 at 10:36 AM.
Re: PNG transparency weird: GDI+ Draw into Meta DC
I am just guessing (because I haven't actually tried this), but I think you may be on the right track with GdipConvertToEmfPlus. Note that the function will only work on Vista or later, or even better, you can test for GDI+ v1.1 prior to calling the function.
The declaration I use for GdipConvertToEmfPlus is this:
Code:
'EMFs can be moved between various formats. GDI+ prefers "EMF+", which supports GDI primitives as well as newer GDI+ commands
Private Enum MetafileType
MetafileTypeInvalid 'Invalid metafile
MetafileTypeWmf 'Standard WMF
MetafileTypeWmfPlaceable 'Placeable WMF
MetafileTypeEmf 'Plain EMF (not EMF+)
MetafileTypeEmfPlusOnly 'EMF+ without dual down-level records
MetafileTypeEmfPlusDual 'EMF+ with dual down-level records
End Enum
Private Enum EMFType
EmfTypeEmfOnly = MetafileTypeEmf 'Only the old GDI EMF format
EmfTypeEmfPlusOnly = MetafileTypeEmfPlusOnly 'Only the new GDI+ EMF+ format
EmfTypeEmfPlusDual = MetafileTypeEmfPlusDual 'Store duplicate commands, in both EMF and EMF+ format
End Enum
Private Declare Function GdipConvertToEmfPlus Lib "gdiplus" (ByVal refGraphics As Long, ByVal oldMetafilePtr As Long, ByRef conversionSuccess As Long, ByVal typeOfEMF As EMFType, ByVal descriptionPointer As Long, ByRef newMetafilePtr As Long) As Long
A couple notes...
- "refGraphics" is a GDI+ Graphics object that contains all the Graphics settings you want (like antialiasing). Apply your desired settings to this object *prior to conversion*, obviously.
- "oldMetafilePtr" is a GDI+ Image object of type "GpMetafile". I *think* this is what you get from GdipCreateFromHDC(), if the target DC is a metafile DC. (Called "hImage" in your code above.)
- "typeOfEMF" should be "EmfTypeEmfPlusOnly" or "EmfTypeEmfPlusDual"
- "descriptionPointer" should be 0.
- "newMetafilePtr" is a Long-type variable that will hold a brand-new GDI+ Image object, with the converted EMF+ data inside. Remember to discard the old Image object you supplied as "oldMetafilePtr" (e.g. "GdipDisposeImage hImage") if the new Image object is created successfully. You will then use this new Image object for all subsequent drawing.
Sorry for the dumb parameter names; they were the result of a lot of testing. You can obviously change them to something more sensible for your code!
Anyway, you should be able to use that function to convert your existing EMF DC to the new EMF+ format. I have not done any testing with EMF+ and printing, so I don't know how it will affect the final, printed result. Just to be safe, you may want to convert to EMF+ only for displaying on the screen, and use the regular EMF DC when printing. (e.g. something like "If PrintPreviewMode, then ConvertEMFDCtoEMFPlusDC...")
Some trial-and-error may be required to make it work with your code, but I've tested GdipConvertToEmfPlus on metafiles loaded from standalone EMF files, and it works great for enabling transparency, antialiasing, and other GDI+ features.
Re: PNG transparency weird: GDI+ Draw into Meta DC
Originally Posted by Tanner_H
I am just guessing (because I haven't actually tried this), but I think you may be on the right track with GdipConvertToEmfPlus. Note that the function will only work on Vista or later, or even better, you can test for GDI+ v1.1 prior to calling the function.
The declaration I use for GdipConvertToEmfPlus is this:
Code:
'EMFs can be moved between various formats. GDI+ prefers "EMF+", which supports GDI primitives as well as newer GDI+ commands
Private Enum MetafileType
MetafileTypeInvalid 'Invalid metafile
MetafileTypeWmf 'Standard WMF
MetafileTypeWmfPlaceable 'Placeable WMF
MetafileTypeEmf 'Plain EMF (not EMF+)
MetafileTypeEmfPlusOnly 'EMF+ without dual down-level records
MetafileTypeEmfPlusDual 'EMF+ with dual down-level records
End Enum
Private Enum EMFType
EmfTypeEmfOnly = MetafileTypeEmf 'Only the old GDI EMF format
EmfTypeEmfPlusOnly = MetafileTypeEmfPlusOnly 'Only the new GDI+ EMF+ format
EmfTypeEmfPlusDual = MetafileTypeEmfPlusDual 'Store duplicate commands, in both EMF and EMF+ format
End Enum
Private Declare Function GdipConvertToEmfPlus Lib "gdiplus" (ByVal refGraphics As Long, ByVal oldMetafilePtr As Long, ByRef conversionSuccess As Long, ByVal typeOfEMF As EMFType, ByVal descriptionPointer As Long, ByRef newMetafilePtr As Long) As Long
A couple notes...
- "refGraphics" is a GDI+ Graphics object that contains all the Graphics settings you want (like antialiasing). Apply your desired settings to this object *prior to conversion*, obviously.
- "oldMetafilePtr" is a GDI+ Image object of type "GpMetafile". I *think* this is what you get from GdipCreateFromHDC(), if the target DC is a metafile DC. (Called "hImage" in your code above.)
- "typeOfEMF" should be "EmfTypeEmfPlusOnly" or "EmfTypeEmfPlusDual"
- "descriptionPointer" should be 0.
- "newMetafilePtr" is a Long-type variable that will hold a brand-new GDI+ Image object, with the converted EMF+ data inside. Remember to discard the old Image object you supplied as "oldMetafilePtr" (e.g. "GdipDisposeImage hImage") if the new Image object is created successfully. You will then use this new Image object for all subsequent drawing.
Sorry for the dumb parameter names; they were the result of a lot of testing. You can obviously change them to something more sensible for your code!
Anyway, you should be able to use that function to convert your existing EMF DC to the new EMF+ format. I have not done any testing with EMF+ and printing, so I don't know how it will affect the final, printed result. Just to be safe, you may want to convert to EMF+ only for displaying on the screen, and use the regular EMF DC when printing. (e.g. something like "If PrintPreviewMode, then ConvertEMFDCtoEMFPlusDC...")
Some trial-and-error may be required to make it work with your code, but I've tested GdipConvertToEmfPlus on metafiles loaded from standalone EMF files, and it works great for enabling transparency, antialiasing, and other GDI+ features.
I know this API and used it before. But I don't know how to use it for this case:
Code:
'Print Preview and Printing Core Code:
Dim hMetaDC As Long
hMetaDC = CreateEnhMetaFile(lhPrinterDC, vbNullString, rct, "gdiApp")
renderPage hMetaDC '---->ALL Draw Done here
If MFHdl Then DeleteEnhMetaFile MFHdl: MFHdl = 0
MFHdl = CloseEnhMetaFile(hMetaDC)
DeleteDC hMetaDC
Code:
'Preview:
If MFHdl Then
PlayEnhMetaFile picPaper.hdc, MFHdl, rc
End If
Code:
'Printing:
If MFHdl Then
PlayEnhMetaFile Printer.hDC, MFHdl, rct
End If
The hGraphics we can create by CreateBitmap and GdipGetImageGraphicsContext APIs.
The problem is to how to supply parameters for API arguments hImage. I don't think it is MFHdl which is GDI type:
Re: PNG transparency weird: GDI+ Draw into Meta DC
Rather using old GDI EMF, why not use GDI+ GdipRecordMetafile? I will consider this technique.
Code:
Dim rctF As RECTF, metafile As Long,hGraphics As Long,Meta_DC As Long
rctF.Width = paperWidthInMillimeter: rctF.Height = paperHeightInMillimeter
Dim lR As Long
lR = GdipRecordMetafile(Printer.hdc, EmfTypeEmfPlusOnly, rctF, MetafileFrameUnitMillimeter, StrPtr("EMFPlus"), metafile) 'or hdc = CreateCompatibleDC(0)
If lR= 0 Then
GdipGetImageGraphicsContext metafile, hGraphics
GdipGetDC hGraphics, Meta_DC
End If
Edited:
I draw something on the Meta_DC, seemed nothing showing. Don't know why.
GdipGetHemfFromMetafile metafile,hEmf also doesn't work.
OK, I will open another new thread regarding GdipRecordMetafile. But if you like to post something below,go ahead. I will keep follow-up. Thanks.
Last edited by Jonney; Oct 21st, 2015 at 05:00 AM.
Re: PNG transparency weird: GDI+ Draw into Meta DC
Originally Posted by Jonney
Rather using old GDI EMF, why not use GDI+ GdipRecordMetafile? I will consider this technique.
Code:
Dim rctF As RECTF, metafile As Long,hGraphics As Long,Meta_DC As Long
rctF.Width = paperWidthInMillimeter: rctF.Height = paperHeightInMillimeter
Dim lR As Long
lR = GdipRecordMetafile(Printer.hdc, EmfTypeEmfPlusOnly, rctF, MetafileFrameUnitMillimeter, StrPtr("EMFPlus"), metafile) 'or hdc = CreateCompatibleDC(0)
If lR= 0 Then
GdipGetImageGraphicsContext metafile, hGraphics
GdipGetDC hGraphics, Meta_DC
End If
Edited:
I draw something on the Meta_DC, seemed nothing showing. Don't know why.
GdipGetHemfFromMetafile metafile,hEmf also doesn't work.
OK, I will open another new thread regarding GdipRecordMetafile. But if you like to post something below,go ahead. I will keep follow-up. Thanks.
do you finished for create emf++ emf plus files?
can you upload a full code sample?thank you
Last edited by xiaoyao; Jun 12th, 2025 at 10:00 PM.