I need to render a large PDF document into a Windows form control. For this to be performant, it would make sense to prerender the entire PDF into a graphics buffer and then blit the relevant part of that buffer in OnPaint.
However, BufferedGraphics.Render does not accept any arguments other than the target Graphics. How can I make it only render a certain region?
Use the bounding Clip on the Graphics
MSDN Graphics.Clip Property
Another option is to Import the old BitBlt function.
Related
In my WinForm app I draw into a System.Drawing.Bitmap. I create fonts from a LOGFONT and draw using the GDI function ExtTextOutW. However the output is terrible. It has bad jaggies and looks like the antialiaser was on LSD. Reading around this seems a common issue - is there a resolution?
If I use:
lf.lfQuality = FontQuality.NONANTIALIASED_QUALITY
when I create the font then the horrible jaggies go away but of course there is no antialiasing.
Is there a way to create smooth text in a Bitmap with ExtTextOutW?
It is possible but a bit tricky and it can't have transparent background.
You will need to:
Create in-memory bitmap buffer that is compatible with display device context (IntPtr.Zero handle)
Fill the buffer background with solid color or other background
Render the text into the memory bitmap
Copy from in-memory bitmap to image device context (BitBlt)
See GDI text rendering to image for more details.
The Layout:
I'm using third party controls to scan documents. I have an event that occurs when I scan an image (mostly used to add annotations the image). The event provides me with only one property named .PAhDC. This property is a handle to the DC that stores the image before it is written to a file. Thus I can make changes to the image before it gets written to a file.
The Expected Results:
I would like to simply add a 1/4 inch (lets say 100 pixels) of white space line on the very top of the image. If my original image is 200x200 (WxH) then my new final image would need to be 200x300.
Question And Other Thoughts:
How can I alter an existing image with only knowing it's DC handle? I was thinking of doing something like the following...
Create a new DC.
Create a new Bitmap 100 pixels taller than the original image.
Use that new bitmap in the new DC.
Copy the original image to the new bitmap (100 pixels from the top as a start point).
Then use something like SelectObject to replace the old bitmap in the original hDC with the new one and then destroy the old bitmap object.
Note: I would like to do this with MANAGED CODE as much as possible. Using SelectObject() was the only way I could think of but it's of course unmanaged code... :/
You can't without cooperation with the owner of the bitmap and DC.
The DeviceContext is purely a viewport onto an underlying DIB/bitmap and has no concept of size or dimensions (beyond the clipping region) While you can create a new bitmap and select it into the DC, it's highly likely that the application will just ignore what you've done and use the DIB that it has created.
The end result of this will be GDI object leaks and no change to the underlying image.
To do what you you ask, you will need full cooperation with the other code and them adding a method that allows you to replace the underlying data.
Sure, you can do this in managed code. All P/Invoke declarations are readily available from any decent search engine.
When creating a new DC, make sure it's a DC compatible with the original one
When creating a new bitmap, make sure it's compatible with the DC
I'm looking for analog of BitBlt functionality, which was available in WinAPI.
Check out method BitmapSource.CopyPixels (it also has one more overload).
Where do you want to copy that part to? Another image?
In WPF, you create a Graphics context on an empty Image, then you draw the Image into this Graphics context, where you can specify parameters such as the location and width/height of the source image to draw.
I want to create a filter over a specific area of the screen to perform filtering opertions.
Examples what a filtering opertion might be:
- inverting (e.g. change black pixel to white pixels, red to cyan)
- masking pixels (e.g. mask = ff0000; input c79001 -> c70000)
- operations like photoshop's layer effects
Here is an example of what it should look like:
http://img443.imageshack.us/img443/1462/overlayk.png
Does anyone know how to perform this under Windows OS.
(my prefered language is C#)
Thanks!
Depending on how fast you need the "filter" to update, a quick and hacky way is just to get a screenshot using CopyFromScreen while your filter window is invisible, apply the filter to the image data, and then set the filter window to display the image data.
If you want to do it without having to hide the window first, you'll probably need to do something like http://www.codeproject.com/KB/system/snapshot.aspx where you capture individual windows.
An even trickier but potentially faster thing to do, and requiring nearly complete use of p/invoke win32 calls, would be to not have a window at all, get the required capture windows based on their coordinates, capture the images as above, and then draw directly to the screen DC.
To clarify: you want an area of the desktop, not just within the bounds of your window, to be under your control allowing you to apply a per-pixel filter. If that's the case, I think what you need is DirectDraw using the XNA libraries. WPF MAY get you what you need, but WinForms will most likely not. There are third party tools as well.
If you want this capability only within the bounds of your application's window, for instance in a drawing application, it gets far easier. Every class in the Windows.Forms namespace that inherits from Control exposes a CreateGraphics() method. This method returns an object representing a drawing surface covering the screen area of the control, and is the basis for just about anything you want to do on a window involving custom graphics (and internally, it's used to draw the controls in the first place).
Once you have the Graphics object, you can draw Images on it. A popular method of drawing custom graphics like animations or games is to do the actual drawing on a Bitmap object (derived from the abstract Image) and then when you're done, draw the Bitmap on the Graphics area. This is done to reduce flicker; if the graphics area is shown to the user while it is being drawn on, the user will only see the complete image for a split second before it is "wiped" and redrawn, and shapes drawn halfway through will be there one moment and gone the next as they wait to be drawn. Drawing to a bitmap, then showing the Bitmap on the screen when you're done, means the user sees a complete image at a time.
You can extend this using transparency features to create multi-layered images. Have a Bitmap for every layer you wish to manipulate. Work on them seperately, then draw each of them, in their proper order from back to front, onto a master Bitmap, and draw that Bitmap on the screen. This allows you those PhotoShop-type manipulations where a part of the image is one layer, and can be manipulated independently of all others.
As for per-pixel filtering, Bitmap objects expose GetPixel() and SetPixel() methods, which allow you to grab the color of a single pixel, perform a filter calculation, and re-draw it. This process will be totally unaccelerated, and so limited by your CPU speed, but allow very fine control of your image, or repetitive tasks like your filters.
In normal C# it is easy to draw to a bitmap using the Grpahics.DrawString() method. Silverlight seems to have done away with Bitmap objects and Graphics is no longer available either. So...How am I meant to manipulate/create a bitmap when using Silverlight? If it helps, I am using Silverlight 3.
Let me tell you what I am doing. I am being given a template, basically a pre-rendered image. The user is then able to select from multiple images and enter the deisred text. I then render it to the image, adjusting size etc... within bounds and centering it in the pre-defined area of the image. If I can calculate the size (as in the MeasureString method) and then draw the string (as in the Graphics.DrawString method) that would be fine. The real question, no matter why I want to be able to do this, is can it be done?
The question is: why do you want to? Why not just use a TextBlock?
If you are trying to dynamically generate an image, use standard Silverlight/WPF controls (including TextBlock) and render them to a WritableBitmap.
Edit:
Ok, you've updated and expanded, which gives me more to go on. Unfortunately, you're not going to like the answer. First, keep in mind that Silverlight and WPF in general are vector based, and intended to be used as such. Although the Canvas allows you to do pseudo-pixel manipulations, you cannot be nearly as pixel-accurate as old-school GDI. This is a factor of your medium. If you absolutely have to measure things the way you want to measure them, I suggest you build your images on a remote server and transmit them to your Silverlight app.
You can calculate the size on-screen of the text rendered via a TextBlock using the ActualWidth and ActualHeight properties. But it only works on an already rendered control. Something like MeasureString is simply not available in Silverlight. Based on your description of your app, some user interaction could accomplish what you want. The user selects the image, enters the text, and is shown a preview. The user can then adjust the width and height of the various text areas until satisfied, at which point you can take a snapshot using the render method I liked to above.
The following may work, its a bit nebulous because I haven't tried yet myself.
The object you are looking for is the WritableBitmap.
You create a Visual tree, for example create your self a Grid or Canvas (you're not adding this to the UI). Add to it the selected image and a TextBlock positioned and sized as you prefer.
Create a new WritableBitmap either of a specific size or using the selected image to initialize it.
Use the WritableBitmap Render method passing the above root Grid or Canvas to it.
Now you have a bitmap which you should able to use to do whatever its you needed to do that required all this hoop jumping in the first place.