2009-03-18 5 views
0

Ceci est un suivi de Rendering to a single Bitmap object from multiple threadsCopier un bitmap sur une plus grande Bitmap en utilisant sans utiliser Graphics.DrawImage

Quelle im essayant d'atteindre est de prendre une image bitmap de dire 50x50 pixels et dessiner sur un bitmap plus grand (100x100 pixels) à n'importe quel point de l'image plus grande, en utilisant la fonction LockBits de bitmaps ou tout autre graphisme que NOT.DrawImage. Mes raisons pour ne pas vouloir utiliser DrawImage sont indiquées dans l'autre thread.

J'ai réussi à obtenir quelque chose en utilisant Marshal.Copy de la source BitmapData à la destination BitmapData mais en créant une image carrelée, étirée horizontalement.

+0

Une suite? Je répète la même question. – Guffa

+0

C'est une question plus ciblée et jusqu'à présent, je reçois des réponses plus ciblées. –

Répondre

0

Je recommande de jeter un oeil à the internal bitmap memory structure.

La meilleure approche, je pense, serait d'essayer de ne pas fixer directement le BitmapData. Au lieu de cela, je voudrais créer un seul tableau d'octets partagé de la taille appropriée et définir le tableau d'octets directement à partir de vos petites images. Une fois que vous comprenez votre plus grande image, vous pouvez prendre le tableau d'octets final et directement make a Bitmap à partir des données d'octets. Ceci a l'avantage de vous permettre de contrôler la gestion de la mémoire, d'effectuer les opérations, etc., comme vous le vouliez dans votre message d'origine. Cela devrait aussi être très rapide pour l'accès aux données.

4

Vous pouvez manipuler l'image en mémoire sans avoir recours aux appels système. Si vous creusez dans le format sous-jacent d'un fichier .BMP, vous pouvez créer votre propre classe Bitmap Device Independent qui "comprend" le format bas niveau d'un fichier .BMP. Par exemple, une image de 8 bits par pixel est essentiellement un tableau d'octets bidimensionnel (chaque octet est de 1 pixel) plus une simple table de couleurs. grosso modo (ce qui est très très rude):

byte[,] bColors = new byte[3,256]; // 256 RGB colors 
byte[,] bImage = new byte[25,50]; // 25 x 50 pixels 

L'astuce est (comme toujours) obtenir une prise des données brutes des pixels, en faisant le traitement, puis mettre à jour les données brutes des pixels avec vos modifications. Dans le passé, j'ai abordé cette question en convertissant un GDI HBITMAP en un DIB 24bpp, en effectuant mon traitement d'image génial sur les pixels bruts (3 octets par pixels, cela est plus facile), puis en convertissant le DIB en HBITMAP. Tout cela utilisait juste GDI classique (pré GDI + même, et encore moins C#). En utilisant cette approche, vous pouvez concevoir une structure de contrôle permettant à plusieurs auteurs d'accéder à différentes sections de votre image beaucoup plus grande. Toutefois, les appels GDI BitBlt de bas niveau sont probablement beaucoup plus efficaces que tout ce que vous pouvez faire. Si j'étais vous, je ferais en sorte que de faire 50 ou 100 bitblt d'affilée serait trop lent (vous devrez probablement le faire en C++).

Les plus ennuyeux défis à traiter DIB de sont:

  1. Conversion d'un DIB à une réelle « image » prêt pour l'affichage et
  2. Conversion d'une « image » réelle dans un DIB
  3. Enregistrement d'un DIB comme quelque chose d'autre que a.BMP

références fondamentales quand je commencé à apprendre la "horreur" que les images sont en réalité:

Comment allez-vous arriver à/à partir de .NET Image ... bien ... c'est une bonne question :)

1

Cela devrait fonctionner correctement avec LockBits/BitmapData, si vous utilisez un format de pixel 32bpp [P] ARGB. L'astuce est que vous devrez copier les données une ligne à la fois afin qu'elle s'aligne aux bons endroits. Vous devriez être en mesure de le faire en utilisant quelque chose comme:

Rectangle srcArea = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height); 
BitmapData srcData = srcBitmap.LockBits(srcArea, ImageLockMode.ReadOnly, destBitmap.PixelFormat); 
Rectangle destArea = new Rectangle(25, 25, srcBitmap.Width, srcBitmap.Height); 
BitmapData destData = destBitmap.LockBits(destArea, ImageLockMode.WriteOnly, destBitmap.PixelFormat); 

IntPtr srcPtr = srcData.Scan0; 
IntPtr destPtr = destData.Scan0; 
byte[] buffer = new byte[srcData.Stride]; 
for (int i = 0; i < srcData.Height; ++i) 
{ 
    Marshal.Copy(srcPtr, buffer, 0, buffer.Length); 
    Marshal.Copy(buffer, 0, destPtr, buffer.Length); 

    srcPtr += srcData.Stride; 
    destPtr += destData.Stride; 
} 

srcBitmap.UnlockBits(srcData); 
destBitmap.UnlockBits(destData); 

En guise d'avertissement, ce code ne fonctionnera pas parce que je ne suis pas sûr de ce que les bonnes incantations sont pour incrémenter de IntPtr. J'ai déjà fait ce genre de chose auparavant, mais en C++. En outre, je ne sais pas s'il existe un moyen de copier directement les données au lieu d'utiliser un tampon intermédiaire.

Une mise en garde supplémentaire: l'appel LockBits srcBitmap et le dimensionnement du tampon supposent que srcBitmap sera complètement inclus dans destBitmap. Si ce n'est pas le cas (une partie de l'image bitmap sera coupée), la zone verrouillée et la taille du tampon devront être ajustées. Si vous n'utilisez pas un format de pixel 32bpp (c'est-à-dire 24bpp), ce sera plus difficile. La foulée de votre source BitmapData peut inclure une certaine quantité de remplissage qui ne doit pas être copiée. Vous pouvez contourner ce problème en calculant la quantité de données de pixels réelles dans une ligne source et en copiant cette quantité. Les formats de pixels indexés seraient encore plus de travail.