Sign in to follow this  
wong_bo

[MDX2]DirectX Print/Print Preview

Recommended Posts

I am really surprised that I cannot find any DirectX printing posts/articles on the Internet, so I am trying to write my own solution. I am using MDX2 and trying to add print/print preview in my application. DotNet and C# provided a very handy way to do print/print preview. As long as you got an Image object, you can call Graphics.DrawImage() to send it to a printer. With DirectX printing, my problem is to create the Image object in the memory. As printing requires hi-resolution, I switched the device type to DeviceType.Reference and set the device back buffer size to the desired resolution. Then I call my render code to do the rendering. Then I call
Surface backbuffer = device.GetBackBuffer(0, 0);
With the backbuffer, I can save the image to the disk with Surface.Save() MDX2 method. How am I going to save it in the memory as an Image object so I can use it for printing? Also the Surface.Save() or Surface.SaveToMemory() throws InvalidCallException when the image size is big (say 10000 x 8000). Is there a way to save a large Surface as an Image object? Any suggestion is appreciated.

Share this post


Link to post
Share on other sites
Even on today's video cards, I think you'll find the largest framebuffer size you can use is either 2048x2048 or 4096x4096. Most 3D engines perform something called "tile rendering" in order to increase the resolution of a screenshot. Multiple screenshots are taken at a standard resolution (say, 1024x768) and then pieced back together into a large bitmap.

The idea behind tile rendering is to simply shift your view frustum by a subpixel amount, grab the screen, and repeat. For example, if you wanted to create a 2048x1536 (2x up from 1024x768), you would take four screen grabs, each with the following frustum offsets:

1) xoffs = 0.0f, yoffs = 0.0f
2) xoffs = 0.5f, yoffs = 0.0f
3) xoffs = 0.0f, yoffs = 0.5f
4) xoffs = 0.5f, yoffs = 0.5f

You then merge the pixels together in a final bitmap like so:

12 12 12 12 12...
34 34 34 34 34...

12 12 12 12 12...
34 34 34 34 34...

...
...

There is probably some code available on the net which implements this, but it's really quite easy to do it yourself.

One more thing: Windows has an internal buffer limitation of ~300Mb when sending data to the print spooler. Keep this in mind when trying to send high DPI images.

Good luck!

Share this post


Link to post
Share on other sites
Thanks for the reply, bpoint.

I found that if Hardware device was used, I can use 3000x3000 before DirectX throw exceptions. Using Reference(Software) device, I can go about 10000x8000. But print to A1 or A0 paper with 600DPI (20000x15000) will throw out of memory exception even with Reference device.

I intend to go with the "tile rendering" technique. But I cannot find any sample code to convert the back buffer surface to Image object. Can anyone help me?

Share this post


Link to post
Share on other sites
Quote:
Original post by wong_bo
I found that if Hardware device was used, I can use 3000x3000 before DirectX throw exceptions. Using Reference(Software) device, I can go about 10000x8000. But print to A1 or A0 paper with 600DPI (20000x15000) will throw out of memory exception even with Reference device.

Even if you created a 20000x15000 bitmap with tile rendering, you wouldn't be able to send it to the printer. The print spooler just does not handle data that large. If you downsampled to 8bpp, you might be able to get it through, but you're still awfully close to that 300Mb limit.

When you start getting to A0 and 36" paper sizes with high DPIs, your BitBlt calls will just fail with 'Insufficent Memory'. I had this problem a while back myself -- the thread is here. In the end I found that there was almost no visible printed difference between 600DPI and 300DPI, so I simply shrunk the bitmap down by half and used StretchBlt instead.

Quote:
But I cannot find any sample code to convert the back buffer surface to Image object. Can anyone help me?

Sorry... I don't use Direct3D and unfortunately can't help there. :(

Share this post


Link to post
Share on other sites
I worked out how to convert from a Surface object to Image object. Here is the code(C#/MDX2) in case someone is having the same problem as me.


Surface backBuffer = device.GetBackBuffer(0, 0);
GraphicsBuffer gb = backBuffer.SaveToMemory(ImageFileFormat.Png);
byte[] data = new byte[gb.SizeInBytes];
gb.Read(data);
MemoryStream stream = new MemoryStream(data);
Image img = Image.FromStream(stream);
backBuffer.Dispose();

Share this post


Link to post
Share on other sites
For future reference, a somewhat easier (MDX1.1) alternative would be:

using(GraphicsStream gStr = SurfaceLoader.SaveToStream( ImageFileFormat.Bmp, yourSurfaceGoesHere ))
{
return (Bitmap)Bitmap.FromStream( gStr );
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this