web-dev-qa-db-fra.com

Pouvez-vous créer un PNG transparent alpha avec C #?

J'ai une page multi-navigateur qui affiche du texte vertical.

Comme un vilain hack pour obtenir du texte à rendre verticalement dans tous les navigateurs, j'ai créé un gestionnaire de page personnalisé qui renvoie un PNG avec le texte dessiné verticalement.

Voici mon code de base (C # 3, mais de petites modifications à toute autre version jusqu'à 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 );
    }
}

Cela crée une image qui ressemble à ce que je veux, sauf que la transparence est basée sur un index. Comme je renvoie un PNG, il pourrait prendre en charge une transparence alpha appropriée.

Existe-t-il un moyen de le faire dans .net?


Merci à Vlix (voir commentaires), j'ai fait quelques changements, mais ce n'est toujours pas correct:

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 );
    }
}

Maintenant, l'alpha semble fonctionner, mais le texte apparaît en bloc - comme s'il avait toujours les bords dentelés mais sur un fond noir.

Est-ce un bug avec .Net/GDI +? J'ai déjà constaté qu'il échoue même pour les transparents d'index pour les gifs, donc je n'ai pas beaucoup confiance en lui.

Cette image montre les deux façons dont cela se passe mal:

vertical text comparison

L'image du haut la montre sans fond blanc ni appel MakeTransparent. Le second avec l'arrière-plan rempli de blanc puis MakeTransparent appelé pour ajouter la transparence de l'index.

Aucun de ces éléments n'est correct - la deuxième image a des alias de crénelage blancs que je ne veux pas, la première semble être solidement aliasée contre le noir.

34
Keith

Pour corriger le texte "blockiness", ne pouvez-vous pas simplement le faire ...

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

après cette ligne ...

Graphics g = Graphics.FromImage( (Image) image );
14
pmcilreavy

IIRC vous devez spécifier le PixelFormat dans le constructeur bitmap.

6
leppie

Vous pouvez utiliser la méthode LockBits sur votre Bitmap pour renvoyer un objet BitmapData et définir vous-même l'Alpha pour chaque pixel (vous pouvez également utiliser GetPixel et SetPixel, mais ces méthodes sont incroyablement lentes). Voir cette réponse .

Je sais que cela fonctionne, car lorsque j'ai commencé à utiliser la technique, je définissais la valeur alpha sur 0, donc aucune des couleurs que je définissais n'était visible.

Edit: voici un échantillon d'un effet de lentille fisheye, entièrement réalisé en .NET (en utilisant LockBits):

texte alternatif http://www.freeimagehosting.net/uploads/21ca8300cc.jpg

6
MusiGenesis

Pour l'anticrénelage de texte, jouez avec les valeurs de Graphics.TextRenderingHint. Je conseillerais de ne pas utiliser ClearType, car cela ne s'affichera que sur Nice LCD moniteurs (pas CRT), et pourrait ne pas fonctionner en raison de la rotation.

P.S. C'est Vilx-, pas Vlix. :RÉ

5
Vilx-

Vous pouvez peut-être trouver la réponse dans le code source de cet article CodeProject:

mélange alpha par pixel en C #

1
splattne

Désolé, je n'ai pas lu votre question assez attentivement.

Je sais que j'ai réussi à dessiner du texte pivoté sur un arrière-plan coloré sans voir aucun des effets d'alias dans vos exemples. Quand je suis retourné pour regarder mon code, j'ai réalisé que je dessinais le texte pivoté à l'aide d'une police logique, qui est bien enveloppée dans l'espace de noms Microsoft.WindowsCE.Forms. Cette technique définit l'angle de dessin en tant que propriété de la police logique (vous n'appelez donc pas TranslateTransform ou RotateTransform).

En dehors du monde Windows CE, vous devez PInvoke pour créer une police logique (je ne l'ai jamais fait et je n'ai pas pu trouver un bon exemple rapidement). Je ne sais pas à quel point cela fonctionnerait, mais je suis sûr qu'il dessinera du texte pivoté sans les effets de pseudonyme étranges que vous voyez. Je pense que c'est un bug dans GDI + (ou juste quelque chose qu'ils ne pensaient pas que quelqu'un utiliserait vraiment).

Une approche alternative qui pourrait même mieux fonctionner que la police logique consiste à dessiner le texte normalement sur un arrière-plan de rectangle coloré (juste assez grand pour contenir le texte), puis à faire pivoter et copier le rectangle sur votre image principale. Graphics.DrawImage ne semble pas pouvoir effectuer de rotation, mais cet effet est facile à implémenter à l'aide de LockBits.

1
MusiGenesis

Ou, vous pouvez simplement rendre le texte dans un PNG non transparent, mais utiliser une couleur d'arrière-plan (au lieu du blanc) qui correspond à la couleur d'arrière-plan de la cellule.

0
MusiGenesis

Il s'avère que Microsoft a une nouvelle bibliothèque d'images dynamiques pour le Web dans le cadre du contenu Asp.Net sur CodePlex:

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

Malheureusement, c'est un peu beaucoup pour ce que je veux. En utilisant du code dangereux, je peux rouler ma propre transparence alpha, mais c'est beaucoup trop lent étant donné l'utilisation de ce code dans le cadre d'une application Web à haute utilisation.

Est-ce juste un bogue dans l'implémentation GDI + .Net de MS?

0
Keith

Vous devez au moins spécifier un format de pixel 32 bits (ex: PixelFormat.Format32bppArgb) lors de la création de votre bitmap, sinon GDI + ne peut pas gérer la transparence.

Pour régler le fichier résultant lors de l'enregistrement, vous pouvez utiliser Enregistrer avec ImageCodecInfo et EncoderParameters.

0
thinkbeforecoding