34

I have a multi-browser page that shows vertical text.

As an ugly hack to get text to render vertically in all browsers I've created a custom page handler that returns a PNG with the text drawn vertically.

Here's my basic code (C#3, but small changes to any other version down to 1):

Font f = GetSystemConfiguredFont();
//this sets the text to be rotated 90deg clockwise (i.e. down)
StringFormat stringFormat = new StringFormat { FormatFlags = StringFormatFlags.DirectionVertical };

SizeF size;
// creates 1Kx1K image buffer and uses it to find out how bit the image needs to be to fit the text
using ( Image imageg = (Image) new Bitmap( 1000, 1000 ) )
    size = Graphics.FromImage( imageg ).
        MeasureString( text, f, 25, stringFormat );

using ( Bitmap image = new Bitmap( (int) size.Width, (int) size.Height ) )
{
    Graphics g = Graphics.FromImage( (Image) image );
    g.FillRectangle( Brushes.White, 0f, 0f, image.Width, image.Height );
    g.TranslateTransform( image.Width, image.Height );
    g.RotateTransform( 180.0F ); //note that we need the rotation as the default is down

    // draw text
    g.DrawString( text, f, Brushes.Black, 0f, 0f, stringFormat );

    //make be background transparent - this will be an index (rather than an alpha) transparency
    image.MakeTransparent( Color.White );

    //note that this image has to be a PNG, as GDI+'s gif handling renders any transparency as black.
    context.Response.AddHeader( "ContentType", "image/png" );
    using ( MemoryStream memStream = new MemoryStream() )
    {
        image.Save( memStream, ImageFormat.Png );
        memStream.WriteTo( context.Response.OutputStream );
    }
}

This creates an image that looks how I want it to, except that the transparency is index based. As I'm returning a PNG it could support a proper alpha transparency.

Is there any way to do this in .net?


Thanks to Vlix (see comments) I've made some changes, though it still isn't right:

using ( Bitmap image = new Bitmap( (int) size.Width, (int) size.Height, PixelFormat.Format32bppArgb ) )
{
    Graphics g = Graphics.FromImage( (Image) image );
    g.TranslateTransform( image.Width, image.Height );
    g.RotateTransform( 180.0F ); //note that we need the rotation as the default is down

    // draw text
    g.DrawString( text, f, Brushes.Black, 0f, 0f, stringFormat );

    //note that this image has to be a PNG, as GDI+'s gif handling renders any transparency as black.
    context.Response.AddHeader( "ContentType", "image/png" );
    using ( MemoryStream memStream = new MemoryStream() )
    {
        //note that context.Response.OutputStream doesn't support the Save, but does support WriteTo
        image.Save( memStream, ImageFormat.Png );
        memStream.WriteTo( context.Response.OutputStream );
    }
}

Now the alpha appears to work, but the text appears blocky - as if it still has the jaggie edges but against a black background.

Is this some bug with .Net/GDI+? I've already found that it fails for even index transparencies for gifs, so I don't have much confidence it it.

This image shows the two ways this goes wrong:

vertical text comparison

The top image shows it with no white background or MakeTransparent call. The second with the background filled with white and then MakeTransparent called to add the index transparency.

Neither of these is correct - the second image has white aliasing jaggies that I don't want, the first appears to be solidly aliased against black.

Adaline Simonian
  • 4,596
  • 2
  • 24
  • 35
Keith
  • 150,284
  • 78
  • 298
  • 434

9 Answers9

14

To fix the text "blockiness", can't you just do...

g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

after this line...

Graphics g = Graphics.FromImage( (Image) image );
Keith
  • 150,284
  • 78
  • 298
  • 434
pmcilreavy
  • 3,076
  • 2
  • 28
  • 37
  • Wouldn't that just add the cleartype red/blue aliasing? It would also be an issue if they have CRT screens, or vertical monitors, or non-standard DPIs? – Keith Jun 17 '09 at 20:46
  • 3
    Actually this lead to the answer - by default this TextRenderingHint was read from the system, so while it did this on the server (with anti aliasing as the default) on some developer machines it applied clear-type instead. Setting this to TextRenderingHint.SingleBitPerPixelGridFit fixed my problem. – Keith Sep 04 '09 at 13:35
6

IIRC you need to specify the PixelFormat in the bitmap constructor.

leppie
  • 115,091
  • 17
  • 196
  • 297
  • You mean like: new Bitmap( (int) size.Width, (int) size.Height, PixelFormat.Format32bppArgb ) ? That doesn't seem to make any difference. – Keith Dec 23 '08 at 11:47
  • 2
    It will if you try to draw transparent pixels. The picture is filled with non-transparent pixels in the beginning. – Vilx- Dec 23 '08 at 12:01
  • 3
    In fact, you're filling it yourself with "g.FillRectangle( Brushes.White, 0f, 0f, image.Width, image.Height );" That's a non-transparent white color. Change that to a transparent one, and you'll have it. – Vilx- Dec 23 '08 at 12:01
  • Cheers Vilx - sorry I can't give you any rep. That fixes the alpha issue but creates an odd blocky effect instead, as if the text is aliased against black – Keith Dec 23 '08 at 12:07
6

You can use the LockBits method on your Bitmap to return a BitmapData object, and set the Alpha for each pixel yourself (you could also use GetPixel and SetPixel, but these methods are insanely slow). See this answer.

I know this works for sure, because when I first started using the technique I was setting the alpha value to 0, so none of the colors I was setting were visible.

Edit: here is a sample of a fisheye lens effect, done entirely in .NET (using LockBits):

alt text http://www.freeimagehosting.net/uploads/21ca8300cc.jpg

Community
  • 1
  • 1
MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • Thanks, but that does seem a really messy way to do it, especially as I need to generate loads of these images. Also I would need to come up with my own algorithm for the aliasing - grey pixels 50%, dark grey 75% and so on. – Keith Dec 23 '08 at 12:45
  • It's really surprisingly easy to work with the BitmapData class, and not messy at all. Once you have total control over your image on a pixel-by-pixel basis, all sorts of graphics techniques become easy and simple. For example, I just finished writing a realtime fisheye lens effect, pure .NET. – MusiGenesis Dec 23 '08 at 13:10
  • To be fair, the fisheye lens effect was NOT easy and simple, although the pixel reading and writing part was. – MusiGenesis Dec 23 '08 at 13:12
  • 1
    I did because someone else upvoted it and I wanted this question to appear unanswered until I had a complete answer. Once there were too many ups I switched to an up also. - I think you deserve an up vote for the answer, but I was hoping for an answer that didn't involve me developing my own alphas. – Keith Dec 24 '08 at 10:56
5

For text antialiasing play around with values in Graphics.TextRenderingHint. I'd advise not to use ClearType, because that will only show up nice on LCD monitors (not CRT), and might not work due to the rotation.

P.S. It's Vilx-, not Vlix. :D

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • Sorry for the mix up. I don't think cleartype would work at all, fonts really have to be designed to take advantage of it (horizontally) so I don't think it would really work, even if I rolled my own. I'd be happy with normal aliased text. – Keith Dec 23 '08 at 14:35
1

Maybe you can find the answer in the source code of this CodeProject article:

Per Pixel Alpha Blend in C#

splattne
  • 102,760
  • 52
  • 202
  • 249
  • Thanks, that code uses unmanaged win32 calls to change how the image is rendered. Really I'm looking to save the actual PNG with the alpha transparency, rather than index transparency. – Keith Dec 23 '08 at 12:02
1

Sorry, I didn't read your question carefully enough.

I know that I have successfully drawn rotated text on a colored background without seeing either of the aliasing effects in your examples. When I went back to look at my code, I realized that I was drawing the rotated text using a logical font, which is nicely wrapped in the Microsoft.WindowsCE.Forms namespace. This technique sets the drawing angle as a property of the logical font (so you don't call TranslateTransform or RotateTransform).

Outside of the Windows CE world, you have to PInvoke to create a logical font (I've never done this and I couldn't find a good example quickly). I don't know how well this would perform, but I know for sure that it will draw rotated text without the weird aliasing effects you're seeing. I think for sure this is a bug in GDI+ (or just something they didn't think anyone would ever really use).

An alternative approach that might even perform better than the logical font is to draw the text normally on a colored rectangle background (just large enough to hold the text), then rotate-and-copy the rectangle onto your main image. Graphics.DrawImage doesn't appear to be able to do rotation, but this effect is easy to implement using LockBits.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
0

It turns out that Microsoft have a new dynamic image library for the web as part of the Asp.Net content on CodePlex:

http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16449

Unfortunately it's a little bit much for what I want. Using unsafe code I can roll my own alpha transparency, but that's far too slow given this code's use as part of a high-usage web application.

Is this just a bug in MS's GDI+ .Net implementation?

Keith
  • 150,284
  • 78
  • 298
  • 434
0

You should at least specify a 32bits pixel format (ex: PixelFormat.Format32bppArgb) when creating your bitmap, otherwise GDI+ cannot manage transparency.

To tune the resulting file when saving, you can use Save with ImageCodecInfo and EncoderParameters.

thinkbeforecoding
  • 6,668
  • 1
  • 29
  • 31
0

Or, you could just render the text to a non-transparent PNG, but use a background color (instead of white) that matches the background color of the cell.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • That's a good idea - it's currently the fall-back. I have row and column highlights on the table, so I don't really want to add image swaps too. I'll try that logical font idea out... – Keith Jan 02 '09 at 09:42