2010-10-02 3 views
4

J'essaie de convertir un Drawing.Bitmap en un Imaging.Metafile afin d'insérer le métafichier dans un Forms.RichTextBox. (Pour référence, l'intégration d'un bitmap dans un métafichier est la pratique recommandée pour placer un bitmap dans richtext (voir la version 1.9.1 de la spécification RTF (Rich Text Format), page 149.) Malheureusement, il semble également être le seul moyen d'intégrer une image dans un Forms.RichTextBox, car je ne peux obtenir aucune méthode dépendant du périphérique ou du périphérique pour insérer une image bitmap dans un RichTextBox.)Convertir Drawing.Bitmap en Imaging.Metafile - Pixel Perfect

Plus tard, je dois récupérer les données de pixels du métafichier . Je demande que les pixels du métafichier correspondent exactement à ceux du bitmap. Lorsque j'effectue la conversion, cependant, les pixels sont légèrement modifiés. (? Peut-être en raison de l'image GDI de gestion des couleurs (ICM))

est ma technique ici:

public static Imaging.Metafile BitmapToMetafileViaGraphicsDrawImage(Forms.RichTextBox rtfBox, Drawing.Bitmap bitmap) 
{ 
    Imaging.Metafile metafile; 
    using (IO.MemoryStream stream = new IO.MemoryStream()) 
    using (Drawing.Graphics rtfBoxGraphics = rtfBox.CreateGraphics()) 
    { 
     IntPtr pDeviceContext = rtfBoxGraphics.GetHdc(); 

     metafile = new Imaging.Metafile(stream, pDeviceContext); 
     using (Drawing.Graphics imageGraphics = Drawing.Graphics.FromImage(metafile)) 
     { 
      //imageGraphics.DrawImage(bitmap, new Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height)); 
      imageGraphics.DrawImageUnscaled(bitmap, new Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height)); 
     } 
     rtfBoxGraphics.ReleaseHdc(pDeviceContext); 
    } 
    return metafile; 
} 

Dans ce cas, j'accéder aux pixels du métafichier de cette façon:

metafile.Save(stream, Imaging.ImageFormat.Png); 
Bitmap bitmap = new Bitmap(stream, false); 
bitmap.GetPixel(x, y); 

J'ai également essayé d'utiliser une technique BitBlt sans succès.

technique BitBlt:

[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")] 
static extern int BitBlt(
    IntPtr hdcDest,  // handle to destination DC (device context) 
    int nXDest,   // x-coord of destination upper-left corner 
    int nYDest,   // y-coord of destination upper-left corner 
    int nWidth,   // width of destination rectangle 
    int nHeight,  // height of destination rectangle 
    IntPtr hdcSrc,  // handle to source DC 
    int nXSrc,   // x-coordinate of source upper-left corner 
    int nYSrc,   // y-coordinate of source upper-left corner 
    System.Int32 dwRop // raster operation code 
); 

public static Imaging.Metafile BitmapToMetafileViaBitBlt(Forms.RichTextBox rtfBox, Drawing.Bitmap bitmap) 
{ 
    const int SrcCopy = 0xcc0020; 

    Graphics bitmapGraphics = Graphics.FromImage(bitmap); 
    IntPtr pBitmapDeviceContext = bitmapGraphics.GetHdc(); 

    RectangleF rect = new RectangleF(new PointF(0, 0), new SizeF(bitmap.Width, bitmap.Height)); 
    Imaging.Metafile metafile = new Imaging.Metafile(pBitmapDeviceContext, rect); 
    Graphics metafileGraphics = Graphics.FromImage(metafile); 
    IntPtr metafileDeviceContext = metafileGraphics.GetHdc(); 

    BitBlt(pBitmapDeviceContext, 0, 0, bitmap.Width, bitmap.Height, 
     metafileDeviceContext, 0, 0, SrcCopy); 

    return metafile; 
} 

Je ne suis même pas sûr que cette technique est la copie correctement les données pixel. Cette technique échoue lorsque je tente d'accéder aux données dans le métafichier plus tard:

IntPtr h = metafile.GetHenhmetafile(); // ArgumentException "Parameter is not valid." 
byte[] data; 
uint size = GetEnhMetaFileBits(h, 0, out data); 
data = new byte[size]; 
GetEnhMetaFileBits(h, size, out data); 
stream = new IO.MemoryStream(data); 

Comment puis-je convertir un bitmap dans un métafichier sans altérer les pixels, puis récupérer les données pixel plus tard? Je vous remercie!


Réglage de la résolution Bitmap

Voici comment je tente de définir la résolution bitmap afin que la résolution du métafichier correspond:

Drawing.Bitmap bitmap = new Drawing.Bitmap(width, height, 
Imaging.PixelFormat.Format32bppArgb); // Use 32-bit pixels so that each component (ARGB) matches up with a byte 
// Try setting the resolution to see if that helps with conversion to/from metafiles 
Drawing.Graphics rtfGraphics = rtfBox.CreateGraphics(); 
bitmap.SetResolution(rtfGraphics.DpiX, rtfGraphics.DpiY); 

// Set the pixel data 
... 

// Return the bitmap 
return bitmap; 

Le rtfBox est le même que celui envoyé à BitmapToMetafileViaGraphicsDrawImage.

+0

Avez-vous réussi à obtenir ce que vous vouliez ou avez-vous besoin d'aide supplémentaire? Si l'une des réponses était utile, n'oubliez pas de l'accepter avant l'expiration de la prime (la laisser expirer ne vous renvoie pas les points, elle serait simplement perdue - voir http://stackoverflow.com/faq#bounty pour les détails à ce sujet). – Lucero

Répondre

5

Vous pouvez créer et traiter le métafichier manuellement (par exemple, enumérer ses enregistrements), auquel cas vous pouvez réellement insérer un bitmap avec ses données exactes dans le flux de métafichier. Cependant, ce n'est pas aussi simple que cela puisse paraître. Lors de la lecture d'un métafichier GDI, toutes les opérations sont converties en interne en GDI +, qui est en fait une API complètement différente et qui gère beaucoup de choses différemment. Malheureusement, la façon intégrée de le faire dès que le métafichier a une certaine complexité ou que la sortie doit être transformée est de laisser GDI rendre le métafichier sur un bitmap basse résolution puis de le dessiner, ce qui ne vous donne jamais les résultats cherchons.

Pour un projet (closed-source - alors ne vous embêtez pas à demander la source;)) J'ai dû implémenter un moteur de lecture GDI-GDI + complet similaire à la fonctionnalité de EMFExplorer, mais en utilisant du code managé et avec un seul caractère de réalignement pour une sortie textuelle exacte. C'est seulement pour dire que cela peut être fait, si vous êtes prêt à consacrer du temps à traiter tous les enregistrements de métafichiers dont vous avez besoin (ce qui devrait être un petit sous-ensemble, mais quand même).


Modifier pour répondre aux questions posées dans les commentaires:

Le métafichier est rien, mais une série d'instructions de dessin, essentiellement un enregistrement des opérations GDI (+) effectuées. Vous devriez donc être capable de construire un métafichier en tant que données binaires ne comprenant essentiellement que l'en-tête et le bitmap, et dans ce cas (puisque vous ne passez pas par les opérations de dropping mais manipulez toujours le bitmap au niveau binaire) pour récupérer exactement les mêmes données du métafichier que vous avez écrit dedans.

Heureusement, depuis quelques années, Microsoft documente et met à la disposition du public leurs formats de fichiers et leurs protocoles, de sorte qu'une documentation précise du format de fichier WMF/EMF est disponible. Le format de métafichier est expliqué par MS ici: http://msdn.microsoft.com/en-us/library/cc230514(PROT.10).aspx

Sa structure est décrite ici: http://msdn.microsoft.com/en-us/library/cc230516(v=PROT.10).aspx

Les enregistrements bitmap sont décrits ici: http://msdn.microsoft.com/en-us/library/cc231160(v=PROT.10).aspx

En utilisant cette information, vous devriez être en mesure de mettre ensemble des données binaires (peut-être en utilisant un BinaryWriter), puis charger/énumérer le fichier pour récupérer les données (par exemple, retrouver le bitmap dans le métafichier et en extraire les données requises).

+0

Pouvez-vous élaborer sur "vous pouvez réellement insérer un bitmap avec ses données exactes dans le flux de métafichier"?Quelles sont les méthodes ou les appels gdi32 que j'utiliserais pour effectuer cela? Qu'entendez-vous par "réalignement d'un seul caractère pour une sortie textuelle exacte"? Est-ce similaire à ce que je tente avec la conversion pixel-parfait entre Drawing.Bitmap et Imaging.Metafile? Je vous remercie. –

+0

"la manière intégrée de le faire ... rendre le métafichier sur un bitmap basse résolution" Je pense que cela pourrait expliquer les erreurs que je vois, car les transformations de pixels pour le métafichier sont identiques à celles entre deux bitmaps si je n'utilisez pas useIcm = false dans le constructeur de l'image. Pouvez-vous penser à un moyen de contourner cela? Note: pour mes fins, je ne me soucie pas vraiment de la façon dont le métafichier est rendu; mais quand j'accède à ses données d'octet dans le texte enrichi, j'exige que les données soient parfaites pour que je puisse récupérer les données qui ont été encodées dans les pixels de l'image. Je vous remercie. –

+0

J'ai édité la réponse concernant les questions à portée de main. Puisque c'est hors-sujet, voici la réponse à la question de «ré-alignement de personnage unique». GDI + rend le texte complètement différent de GDI, même en utilisant la même police et les mêmes paramètres. Le crénage est différent, et le positionnement exact des caractères. Maintenant, les métafichiers que j'ai à convertir contenaient beaucoup de chaînes de texte, et ils étaient mal alignés car le rendu GDI + ne correspondait pas au positionnement des blocs de texte. J'ai donc implémenté un calcul de position char-by-char, en utilisant le crénage de GDI + mais en les positionnant correctement. – Lucero

0

Ceci est juste un coup dans le noir, mais jetez un oeil à certaines des valeurs d'indicateur ImageFlags et voir si elles peuvent être intégrées dans la génération ou le rendu du métafichier.

+0

Je les ai regardés et aucun d'eux n'a semblé aider selon leurs noms. Je vous remercie. –

0

c'est une drôle de façon de le résoudre, mais si tout ce que vous avez besoin est de le mettre à l'intérieur RichTextBox - vous pouvez utiliser le Presse-papiers:

private void button1_Click(object sender, EventArgs e) 
    { 
     System.Drawing.Bitmap bmp = new Bitmap("g.bmp"); 
     Clipboard.SetData(DataFormats.Bitmap, bmp); 

     richTextBox1.Paste(); 
    } 

mais je ne suis pas sûr de la manière opposée (lire la bitmap du texte RTF)

+1

Merci mais je ne dois pas déranger le contenu du presse-papiers. –

+0

horreurs presse-papiers !! ... soupir! – Nayan

2

Il y a un certain nombre de réponses possibles ici, les deux coupables les plus probables étant l'aliasing et la résolution. Ma conjecture est la résolution parce que quand vous enregistrez un bitmap sans spécifier une résolution, je crois qu'il est réglé à 120 DPI automatiquement, ce qui pourrait affecter l'alias.

Avant que quelqu'un remarque que la résolution n'a pas d'importance et que DPI est utilisé uniquement pour l'impression et bla bla, s'il vous plaît résister à l'envie de faire ce commentaire. Je suis heureux d'avoir ce débat avec vous sur votre propre question.

Définissez la résolution du bitmap à convertir avec la résolution de votre métafichier.

+0

Bonjour J'ai ajouté le code où j'ai défini la résolution du bitmap. Cette méthode n'a pas fonctionné malheureusement. Ai-je correctement défini la résolution (à partir de RichTextBox.CreateGraphics(). DpiX et .DpiY) de sorte qu'elle corresponde au métafichier? Je ne suis pas sûr comment le métafichier obtient sa résolution, mais j'ai posté tout mon code de création de métafichier alors peut-être que vous pouvez le dire? Je suppose qu'il reçoit sa résolution d'être créé basé sur le RichTextBox.CreateGraphics(). GetHdc(). Je vous remercie. –

+0

Etes-vous sûr que cela ne vient pas d'ICM? La raison pour laquelle je suggère ceci est que pendant le test j'ai encodé des pixels dans un bitmap, converti le bitmap en bitmap (comme je le ferais avec un métafichier plus tard), et lu les données de pixel. Les données de pixels ont été transformées entre les bitmaps jusqu'à ce que j'utilise le paramètre useIcm = false du constructeur Bitmap: newBitmap = new Drawing.Bitmap (oldBitmapStream, false). La transformation des pixels dans le cas avec useIcm = true était identique à la transformation lorsque je créais le métafichier à partir du bitmap via Graphics.DrawImage (...). Je vous remercie. –

+0

@DGGenuine: Je ferai d'autres recherches à ce sujet et vous le ferai savoir bientôt. –