How to use direct 2d to draw text in the printer DC? - printing

I use the direct 2d API to draw text in the GDI dc correctly,
but When I use the same code to draw text in the printer DC, it failed,
The printer hdc is create as below,
hdc = CreateDC("WINSPOOL", printerName, "", pdevMode);
The Direct 2d draw text code snippet is as below
if (p_d2dFactory == NULL) {
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &p_d2dFactory);
}
if (SUCCEEDED(hr) && p_dcTarget == NULL) {
hr = p_d2dFactory->CreateDCRenderTarget(&props, &p_dcTarget);
}
if (SUCCEEDED(hr)) {
hr = p_dcTarget->BindDC(hdc, &rc);
}
if (SUCCEEDED(hr)) {
p_dcTarget->BeginDraw();
p_dcTarget->Clear(NULL);
p_dcTarget->DrawTextLayout(origin, g_pTextLayout,p_dbrush);
hr= p_dcTarget->EndDraw();
}
Unfortunately, the p_dcTarget can not bind the hdc correctly, so there is nothing printed out,
but if the hdc is the GDI dc to draw text in the window, it will bind successfully and then draw the correct text out.
Is there anything different when binding the printer DC?
Any suggestion for this trouble? Thanks.

According to this discussion on MSDN, you can't print directly from Direct2D to a printer DC. Instead, you'll need to render to an in-memory bitmap then copy that bitmap to the printer with BitBlt or StretchBlt.
The MSDN documentation on GDI and Direct2D interoperability points out that, even if Direct2D did work with printer DCs, it would be doing this internally:
When you use an ID2D1DCRenderTarget, it renders Direct2D content to an internal bitmap, and then renders the bitmap to the DC with GDI.

Related

Can't blit MSAA FBO into default FBO due to dimensions mismatch

I am working on a C++ project cross-compiled with emscripten to WebAssembly. I am using OpenGL subset on the C++ side compatible with WebGL2.0. The renderer performs first render pass into MSAA FBO,then blits the results into default FBO (screen). Below are the blit errors I am receiving when testing on Chrome and Firefox:
Chrome:
[.WebGL-0000494000387800] GL_INVALID_OPERATION: Attempt to blit from a
multisampled framebuffer and the bounds or format of the color buffer
don't match with the draw framebuffer.
Firefox
WebGL warning: blitFramebuffer: If the source is multisampled, then
the source and dest regions must match exactly.
I am not sure what I am missing here. I have used the following code (courtesy of SOKOL_SAMPLES project):
void emsc_init(const char* canvas_name, int flags) {
_emsc_canvas_name = canvas_name;
_emsc_is_webgl2 = false;
emscripten_get_element_css_size(canvas_name, &_emsc_width, &_emsc_height);
//force our offscreen fbo size also for canvas
_emsc_width = 1920;
_emsc_height = 1080;
emscripten_set_canvas_element_size(canvas_name, _emsc_width, _emsc_height);
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _emsc_size_changed);
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
EmscriptenWebGLContextAttributes attrs;
emscripten_webgl_init_context_attributes(&attrs);
attrs.antialias = flags & EMSC_ANTIALIAS;
if (flags & EMSC_TRY_WEBGL2) {
attrs.majorVersion = 2;
}
ctx = emscripten_webgl_create_context(canvas_name, &attrs);
if ((flags & EMSC_TRY_WEBGL2) && ctx) {
_emsc_is_webgl2 = true;
}
emscripten_webgl_make_context_current(ctx);
}
After the canvas sizes are set,it reports my sizes correctly.The size of the offscreen multi-sampled FBO is the same. Is it possible that the canvas size doesn't resize the context related FBO attachment automatically? Or is it something else.
I was trying to reproduce the issue with vanilla JS and WebGL and yes, I was getting the same errors if the src and dst values of glBlitFramebuffer didn't match.

How to copy a selection from Direct2DCanvas to clipboard?

I made a small program to draw geometry figures on a D2DBox (with Direct2DCanvas and RenderTarget) and I need to be able to copy a rectangle from it to the clipboard. Tried this, but I'm stuck with the source handle parameter:
bm := TBitmap.Create;
try
bm.SetSize(maxx-minx, maxy-miny);
BitBlt(bm.Canvas.Handle, minx, miny, maxx, maxy, ??? , 0, 0, SRCCOPY);
Clipboard.Assign(bm);
finally
bm.Free;
end;
Any idea where to get a handle from? Or the whole thing is done a different way? Thanx!
BitBlt() requires a GDI HDC to copy from, but TDirect2DCanvas does not have an HDC of its own, and it cannot directly render to an off-screen HDC/TCanvas, such as TBitmap.Canvas, per its documentation:
TDirect2DCanvas will only work for on-screen device contexts. You cannot use the TDirect2DCanvas to draw on a printer device context, for example.
And you can't associate a custom RenderTarget (such as one created with ID2D1Factory.CreateDCRenderTarget() and ID2D1DCRenderTarget.BindDC()) since the TDirect2DCanvas.RenderTarget property is read-only.
So, you will likely have to go a long way to get what you want. Based on code I found in Direct2d Desktop printing C++, which demonstrates copying an arbitrary ID2D1RenderTarget to an arbitrary HDC, you can try the following:
Create a Direct2D ID2D1Bitmap that is bound to the canvas's current RenderTarget using one of the target's CreateBitmap() methods.
Copy pixels from the canvas into the bitmap using the bitmap's CopyFromRenderTarget() method.
Create an IWICBitmap (you can probably use the VCL's TWICImage for this) and render the Direct2D bitmap to it using one of the ID2D1Factory.CreateWicBitmapRenderTarget() methods with one of the ID2D1RenderTarget.DrawBitmap() methods.
Create a GDI DIB bitmap and render the IWICBitmap to it using the WIC bitmap's CopyPixels() method.
Finally, you use the DIB however you needed, such as select/copy it to your final HDC, or you can simply place its contents directly on the clipboard using the CF_DIB format.
Here is the code (sorry, it is in C++, I'm not going to translate it to Delphi):
void Direct2DRender::RenderToDC(HDC hDC, UINT uiWidth, UINT uiHeight)
{
HRESULT hr = S_OK;
IWICImagingFactory *pImageFactory = WICImagingFactory::GetInstance().GetFactory();
CComPtr<IWICBitmap> wicBitmap;
hr = pImageFactory->CreateBitmap( uiWidth, uiHeight, GUID_WICPixelFormat32bppBGR, WICBitmapCacheOnLoad, &wicBitmap);
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU( uiWidth, uiHeight);
float dpiX, dpiY;
m_pRenderTarget->GetDpi( &dpiX, &dpiY);
CComPtr<ID2D1Bitmap> d2dBitmap;
hr = m_pRenderTarget->CreateBitmap( bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
dpiX, dpiY), &d2dBitmap );
D2D1_POINT_2U dest = D2D1::Point2U(0,0);
D2D1_RECT_U src = D2D1::RectU(0, 0, uiWidth, uiHeight);
hr = d2dBitmap->CopyFromRenderTarget(&dest, m_pRenderTarget, &src);
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
CComPtr<ID2D1RenderTarget> wicRenderTarget;
hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget( wicBitmap, rtProps, &wicRenderTarget);
wicRenderTarget->BeginDraw();
wicRenderTarget->DrawBitmap(d2dBitmap);
hr = wicRenderTarget->EndDraw();
// Render the image to a GDI device context
HBITMAP hDIBBitmap = NULL;
try
{
// Get a DC for the full screen
HDC hdcScreen = GetDC(NULL);
if (!hdcScreen)
throw 1;
BITMAPINFO bminfo;
ZeroMemory(&bminfo, sizeof(bminfo));
bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bminfo.bmiHeader.biWidth = uiWidth;
bminfo.bmiHeader.biHeight = -(LONG)uiHeight;
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 32;
bminfo.bmiHeader.biCompression = BI_RGB;
void* pvImageBits = nullptr; // Freed with DeleteObject(hDIBBitmap)
hDIBBitmap = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0);
if (!hDIBBitmap)
throw 2;
ReleaseDC(NULL, hdcScreen);
// Calculate the number of bytes in 1 scanline
UINT nStride = DIB_WIDTHBYTES(uiWidth * 32);
// Calculate the total size of the image
UINT nImage = nStride * uiHeight;
// Copy the pixels to the DIB section
hr = wicBitmap->CopyPixels(nullptr, nStride, nImage, reinterpret_cast<BYTE*>(pvImageBits));
// Copy the bitmap to the target device context
::SetDIBitsToDevice(hDC, 0, 0, uiWidth, uiHeight, 0, 0, 0, uiHeight, pvImageBits, &bminfo, DIB_RGB_COLORS);
DeleteObject(hDIBBitmap);
}
catch (...)
{
if (hDIBBitmap)
DeleteObject(hDIBBitmap);
// Rethrow the exception, so the client code can handle it
throw;
}
}
In that same discussion, another alternative is described:
Create a DIB section, and select it into a DC
Create a DC render target
Bind the render target to the DC that corresponds to the DIB section
Draw using Direct2D. After calling EndDraw the DIB contains what was rendered.
The final step is to draw the dib where you need it.
So, try moving your drawing code to its own function that takes an ID2D1RenderTarget as input and draws on it as needed. Then, you can create an HDC-based RenderTarget when you want to place a bitmap on the clipboard, and use TDirect2DCanvas.RenderTarget when you want to draw on your D2DBox.

GDI - Clipping is not working when used on a printer device context

I'm using the Embarcadero RAD Studio C++ builder XE7 compiler. In an application project, I'm using the both Windows GDI and GDI+ to draw on several device contexts.
My drawing content is something like that:
On the above sample the text background and the user picture are drawn with GDI+. The user picture is also clipped with a rounded path. All the other items (the text and the emojis) are drawn with the GDI.
When I draw to the screen DC, all works fine.
Now I want to draw on a printer device context. Whichever I use for my tests is the new "Export to PDF" printer device available in Windows 10. I prepare my device context to draw on an A4 viewport this way:
HDC GetPrinterDC(HWND hWnd) const
{
// initialize the print dialog structure, set PD_RETURNDC to return a printer device context
::PRINTDLG pd = {0};
pd.lStructSize = sizeof(pd);
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDC;
// get the printer DC to use
::PrintDlg(&pd);
return pd.hDC;
}
...
void Print()
{
HDC hDC = NULL;
try
{
hDC = GetPrinterDC(Application->Handle);
const TSize srcPage(793, 1123);
const TSize dstPage(::GetDeviceCaps(hDC, PHYSICALWIDTH), ::GetDeviceCaps(hDC, PHYSICALHEIGHT));
const TSize pageMargins(::GetDeviceCaps(hDC, PHYSICALOFFSETX), ::GetDeviceCaps(hDC, PHYSICALOFFSETY));
::SetMapMode(hDC, MM_ISOTROPIC);
::SetWindowExtEx(hDC, srcPage.Width, srcPage.Height, NULL);
::SetViewportExtEx(hDC, dstPage.Width, dstPage.Height, NULL);
::SetViewportOrgEx(hDC, -pageMargins.Width, -pageMargins.Height, NULL);
::DOCINFO di = {sizeof(::DOCINFO), config.m_FormattedTitle.c_str()};
::StartDoc (hDC, &di);
// ... the draw function is executed here ...
::EndDoc(hDC);
return true;
}
__finally
{
if (hDC)
::DeleteDC(hDC);
}
}
The draw function executed between the StartDoc() and EndDoc() functions is exactly the same as whichever I use to draw on the screen. The only difference is that I added a global clipping rect on my whole page, to avoid the drawing to overlaps on the page margins when the size is too big, e.g. when I repeat the above drawing several times under the first one. (This is experimental, later I will add a page cutting process, but this is not the question for now)
Here are my clipping functions:
int Clip(const TRect& rect, HDC hDC)
{
// save current device context state
int savedDC = ::SaveDC(hDC);
HRGN pClipRegion = NULL;
try
{
// reset any previous clip region
::SelectClipRgn(hDC, NULL);
// create clip region
pClipRegion = ::CreateRectRgn(rect.Left, rect.Top, rect.Right, rect.Bottom);
// select new canvas clip region
if (::SelectClipRgn(hDC, pClipRegion) == ERROR)
{
DWORD error = ::GetLastError();
::OutputDebugString(L"Unable to select clip region - error - " << ::IntToStr(error));
}
}
__finally
{
// delete clip region (it was copied internally by the SelectClipRgn())
if (pClipRegion)
::DeleteObject(pClipRegion);
}
return savedDC;
}
void ReleaseClip(int savedDC, HDC hDC)
{
if (!savedDC)
return;
if (!hDC)
return;
// restore previously saved device context
::RestoreDC(hDC, savedDC);
}
As mentioned above, I expected a clipping around my page. However the result is just a blank page. If I bypass the clipping functions, all is printed correctly, except that the draw may overlap on the page margins. On the other hands, if I apply the clipping on an arbitrary rect on my screen, all works fine.
What I'm doing wrong with my clipping? Why the page is completely broken when I enables it?
So I found what was the issue. Niki was close to the solution. The clipping functions seem always applied to the page in pixels, ignoring the coordinate system and the units defined by the viewport.
In my case, the values passed to the CreateRectRgn() function were wrong, because they remained untransformed by the viewport, although the clipping was applied after the viewport was set in the device context.
This turned the identification of the issue difficult, because the clipping appeared as transformed while the code was read, as it was applied after the viewport, just before the drawing was processed.
I don't know if this is a GDI bug or a wished behavior, but unfortunately I never seen this detail mentioned in all the documents I read about the clipping. Although it seems to me important to know that the clipping isn't affected by the viewport.

How do I let the user draw (like MS Paint) a picture in the given space (50x50) and saving that picture as a texture2D? XNA

I was thinking of making a windows form with a 50x50 space somewhere on it (bitmap?) and having the user draw (like MS Paint) inside the square. When the user is done, the picture can be saved by clicking on the "save" button and it will be updated in Game1 (for collision purposes of my game). I've seen some tutorials on here on how to draw on screen like MS Paint, but I can't seem to figure out how to SAVE that picture as a Texture2D/Rectangle. And how do I get a bitmap onto a windows form?
To save a bitmap as a png:
private void SaveBmpAsPNG(Bitmap bm)
{
bm.Save(#"c:\button.png", ImageFormat.Png);
}
To write a texture2d to a file:
using (Stream stream = File.OpenWrite("picture.png"))
{
texture.SaveAsPng(stream, texture.Width, texture.Height);
}
To read a .png into a texture2d:
using(Stream stream = File.OpenRead("picture.png"))
{
texture = Texture2D.FromStream(GraphicsDevice, stream);
}

How to draw images to viewport in Max SDK

I want to be able to draw images to the viewport in my 3d Max Plugin,
The GraphicsWindow Class has functions for drawing 3d objects in the viewport but these drawing calls are limited by the current viewport and graphics render limits.
This is undesirable as the image I want to draw should always be drawn no matter what graphics mode 3d max is in and or hardware is used, futher i am only drawing 2d images so there is no need to draw it in a 3d context.
I have managed to get the HWND of the viewport and the max sdk has the function
DrawIconButton();
and i have tried using this function but it does not function properly, the image flickers randomly with user interaction, but disappears when there is no interactivity.
i Have implemented this function in the
RedrawViewsCallback function, however the DrawIconButton() function is not documented and i am not sure if this is the correct way to implemented it.
Here is the code i am using to draw the image:
void Sketch_RedrawViewsCallback::proc (Interface * ip)
{
Interface10* ip10 = GetCOREInterface10();
ViewExp* viewExp = ip10->GetActiveViewport();
ViewExp10* currentViewport;
if (viewExp != NULL)
{
currentViewport = reinterpret_cast<ViewExp10*>(viewExp->Execute(ViewExp::kEXECUTE_GET_VIEWEXP_10));
} else {
return;
}
GraphicsWindow* gw = currentViewport->getGW();
HWND ViewportWindow = gw->getHWnd();
HDC hdc = GetDC(ViewportWindow);
HBITMAP bitmapImage = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
Rect rbox(IPoint2(0,0),IPoint2(48,48));
DrawIconButton(hdc, bitmapImage, rbox, rbox, true);
ReleaseDC(ViewportWindow, hdc);
ip->ReleaseViewport(currentViewport);
};
I could not find a way to draw directly to the view-port window, however I have solved the problem by using a transparent modeless dialog box.
May be a complete redraw will solve the issue. ForceCompleteRedraw

Resources