Visualizzazione dei risultati da 1 a 5 su 5
  1. #1
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308

    [C#] Copia Marshall di una sotto-bitmap da una bitmap di base su file

    PREMESSO CHE:

    Una bitmap MxN esiste come una matrice di pixel ad N righe, ed ogni riga è costituita da 3*M byte colore + un certo numero di byte riservati di valore ininfluente tanti quanti occorrono per portare la riga ad avere un numero di byte che sia multiplo di 4. Es. una bitmap 5x4 avrà 4 righe ognuna di 3*5 + 1 byte perchè 3*5 fa 15 e 15 non è multiplo di 4, serve un altro byte per arrivare a 16.

    PREMESSO CHE:

    La mappatura dei byte su filestream e quella in memoria sono uguali, cambia solo una cosa: nello stream si comincia dalla riga in basso e si procede da sx a dx salendo verso l'alto, nella memoria si segue invece ilo normale flusso dei pixel (dall'alto verso il basso e da sx a dx).

    Detto questo

    OGGETTO: ho la necessità di copiare in memoria una sotto-bitmap b2 (regione rettangolare) da una bitmap base b1 fuori memoria (su stream).

    In precedenza ho presentato un codice che fa esattamente l'operazione inversa cioè copia una bitmap b2 in memoria in una bitmap base b1 fuori memoria e funziona, ma non c'è verso di fare funzionare il codice che ho scritto per l'operazione inversa come da oggetto.

    Mi dite cosa ho sbagliato?

    codice:
             //Preleva una porzione da una bitmap su disco in base ai dati di origine x0,y0
            //e le dimensioni dimx,dimy e restituisce una bitmap in memoria.
            //La copia avviene per porzioni di riga e usa il Marshalling
            public static Bitmap MarshalGetBmp(string path, int x0, int y0, int dimx, int dimy)
            {
                //Legge i dati di intestazione della base b1
                Vector4 Data = GetData(path);
                int Dim1 = (int)Data.X;
                int Dim2 = (int)Data.Y;
                int Bits = (int)Data.Z;
                int Ofs = (int)Data.W;
    
                Bitmap b2 = new Bitmap(dimx, dimy);
    
                //bmdata contiene tutti i byte della clip
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, dimx, dimy);
                System.Drawing.Imaging.BitmapData bmdata = b2.LockBits(rect, 
    System.Drawing.Imaging.ImageLockMode.ReadWrite,
                    b2.PixelFormat);
    
                // Puntatore ad inizio bitmap clip b2
                IntPtr Ptr = bmdata.Scan0;
    
                //Lunghezza fisica di row della base
                int truewidth1 = (int)((Bits * Dim1 + 31) / 32) * 4;
    
                //Lunghezza fisica di row della b2
                int truewidth2 = (int)((Bits * dimx + 31) / 32) * 4;
    
                //n. byte da copiare (= byte x row)
                int bytes = 3 * dimx;
    
                //array dei byte colore pixel di b2
                byte[] a = new byte[bytes];
    
                // Stream della base
                FileStream f = File.Open(path, FileMode.OpenOrCreate);
    
                int sp; //puntatore stream base
    
                //ciclo dei pixel nella regione di sovrascrittura
                for (int y = y0; y < y0 + dimy; y++)
                {
                    //puntatore stream ad inizio row (x0,y) su base
                    sp = Ofs + (Bits / 8) * x0 + truewidth1 * (Dim2 - y - 1);
    
                    //aggiorna puntatore stream 
                    f.Seek(sp, SeekOrigin.Begin);
    
                    //legge i byte-pixel nello stream
                    f.Read(a, 0, bytes);
    
                    //copia byte array nella memoria b2
                    System.Runtime.InteropServices.Marshal.Copy(a, 0, Ptr, bytes);
    
                    //aggiorna il puntatore bmdata alla prossima riga di b2
                    Ptr = new IntPtr((int)IntPtr.Add(Ptr, truewidth2));
                }
    
                // Chiusura   
                b2.UnlockBits(bmdata);
                f.Close();
    
                return b2;
            }
    codice:
           //Legge in un file bitmap e restituisce Dim1, Dim2, bits e offset
            public static Vector4 GetData(string path) 
            {
                long len;
                byte[] b = new byte[30];
                using (FileStream x = File.Open(path, FileMode.OpenOrCreate))
                {
                    x.Seek(0, SeekOrigin.Begin); //Ptr a Width (4 byte)
                    
                    x.Read(b, 0, b.Length);
                    len = x.Length;
                    x.Close();
     
                }
               
                int Ofs = Convert.ToInt32(b[10]);
                int Dim1 = Convert.ToInt32(b[18]) + Convert.ToInt32(b[19]) * 256;
                int Dim2 = Convert.ToInt32(b[22]) + Convert.ToInt32(b[23]) * 256;
                int Bits = Convert.ToInt32(b[28]);
                 
                long bmpbytes = len - Ofs;
                if (bmpbytes < Dim1 * Dim2)
                {
                    Dim1 = Convert.ToInt32(b[18]) * 256 + Convert.ToInt32(b[19]);
                    Dim2 = Convert.ToInt32(b[22]) * 256 + Convert.ToInt32(b[23]);
                }
                
                if (Ofs == 0)
                    Ofs = 54;
                if (Bits == 0)
                    Bits = 24;
    
                return new Vector4(Dim1, Dim2, Bits, Ofs);
            }
    Ultima modifica di escocat; 21-02-2015 a 18:19

  2. #2
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    URKA ho scoperto una cosa sconvolgente che nn mi aspettavo: io codice funziona ma quando il mio programma crea un crop da una bitmap (che è a 24 bit) mi crea una sotto-bitmap a 32 bit! Mi sa che dovrò "appesantire" questo metoduccio così snello.... ma come? Eppure è strano perchè esplorando la memoria Marshal i dati sono rimasti a 24 bit, forse è il b2.Save che avviene a 32 bit? boh
    Ultima modifica di escocat; 21-02-2015 a 19:40

  3. #3
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ecco, ho corretto il codice in questo modo che oserei definire SELVAGGIO ma funziona....(ma dov'è finito MItaly?

    codice:
             //Preleva una porzione da una bitmap su disco in base ai dati di origine x0,y0
            //e le dimensioni dimx,dimy e restituisce una bitmap in memoria.
            //La copia avviene per porzioni di riga e usa il Marshalling
            public static Bitmap MarshalGetBmp(string path, int x0, int y0, int dimx, int dimy)
            {
               //Legge i dati di intestazione della base
                Vector4 Data = GetData(path);
                int Dim1 = (int)Data.X;
                int Dim2 = (int)Data.Y;
                int Bits = (int)Data.Z;
                int Ofs = (int)Data.W;
    
                Bitmap b2 = new Bitmap(dimx, dimy);
                
                //bmdata contiene tutti i byte della clip
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, dimx, dimy);
                System.Drawing.Imaging.BitmapData bmdata = b2.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
                    b2.PixelFormat);
    
                // Puntatore ad inizio bitmap clip b2
                IntPtr Ptr = bmdata.Scan0;
    
                //Lunghezza fisica di row della base
                int truewidth1 = (int)((Bits * Dim1 + 31) / 32) * 4;  // = bmdata.Stride
    
                int Bits2 = 32; //pare che si debba fare così
    
                //Lunghezza fisica di row della b2
                int truewidth2 = (int)((Bits2 * dimx + 31) / 32) * 4;
    
                //n. byte da copiare (= byte x row)
                int bytes = 3*dimx;
                
                //array dei values pixel
                byte[] a = new byte[bytes];
    
                // Stream della base
                FileStream f = File.Open(path, FileMode.OpenOrCreate);
    
                int sp; //puntatore stream base
    
                //array a 32 bit che incorpora l'array in lettura pixel
                //quarto byte 0
                byte[] b = new byte[truewidth2];
    
                //ciclo dei pixel regione di sovrascrittura
                for (int y = y0; y < y0 + dimy; y++)
                {
                    //puntatore stream ad inizio row (x0,y0) su base
                    sp = Ofs + (Bits / 8) * x0 + truewidth1 * (Dim2 - y - 1);
    
                    //aggiorna puntatore stream 
                    f.Seek(sp, SeekOrigin.Begin);
    
                    //preleva ibyte-pixel nello stream
                    f.Read(a, 0, bytes);
    
                    //copia bytes byte della memory b2 in array
                    //Dobbiamo usare un formato a 32 bit per il truewidth2
                    //aggiungendo un byte nullo ogni 3 byte colore
                    //il numero di byte diventa bytes2=truewidth2 
                    int count=0;
                    for (int i = 0; i < bytes; i += 3)
                    {
                        b[count] = a[i];
                        b[count+1] = a[i+1];
                        b[count+2] = a[i+2];
                        b[count+3] = 0;
                        count += 4;
                    }
                    
                    System.Runtime.InteropServices.Marshal.Copy(b, 0, Ptr, truewidth2);
                    //aggiorna il puntatore bmdata ai prossimi 3 byte
                    Ptr = new IntPtr((int)IntPtr.Add(Ptr, truewidth2));
                }
    
                // Chiusura   
                b2.UnlockBits(bmdata);
                f.Close();
    
                return b2;
            }

  4. #4
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    TROVATO! Il codice valido è il primo che ho scritto, con questa correzione:


    System.Drawing.Imaging.BitmapData bmdata = b.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
    System.Drawing.Imaging.
    PixelFormat.Format24bppRgb);





    Fatto un test di velocità: il mio portatile per 1000 esecuzioni del vecchio GetBmp che usava GetPixel e SetPixel ci mette 49 secondi, per le stesse 1000 copie col nuovo in Marshallin ci mette ...... 0,76 secondi!
    Ultima modifica di escocat; 21-02-2015 a 22:14

  5. #5
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ancora meglio:

    codice:
        //Preleva una porzione da una bitmap su disco in base ai dati di origine x0,y0
            //e le dimensioni dimx,dimy e restituisce una bitmap in memoria.
            //La copia avviene per porzioni di riga e usa il Marshalling
            public static Bitmap MarshalGetBmp(string path, int x0, int y0, int dimx, int dimy)
            {
                //Legge i dati di intestazione della base
                Vector4 Data = GetData(path);
                int Dim1 = (int)Data.X;
                int Dim2 = (int)Data.Y;
                int Bits = (int)Data.Z;
                int Ofs = (int)Data.W;
    
                Bitmap b = new Bitmap(dimx, dimy);
    
                //Impostazioni forzate a !overedge
                if (x0 < 0)
                    x0 = 0;
                if (y0 < 0)
                    y0 = 0;
                if (x0 + dimx > Dim1)
                    x0 -= dimx + x0 - Dim1;
                if (y0 + dimy > Dim2)
                    y0 -= dimy + y0 - Dim2;
                
                //bmdata modulato ai bit della base
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, dimx, dimy);
                System.Drawing.Imaging.PixelFormat format;
    
                if (Bits == 24)
                    format = System.Drawing.Imaging.PixelFormat.Format24bppRgb;
                else if (Bits == 32)
                    format = System.Drawing.Imaging.PixelFormat.Format32bppRgb;
                else
                {
                    MessageBox.Show("! Format ERROR !");
                    return b;
                }
    
                System.Drawing.Imaging.BitmapData bmdata = b.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, format);
    
                // Puntatore ad inizio bitmap clip b2
                IntPtr Ptr = bmdata.Scan0;
    
                //Lunghezza fisica di row della base
                int truewidth1 = (int)((Bits * Dim1 + 31) / 32) * 4;  // = bmdata.Stride
    
                //Lunghezza fisica di row della b2
                int truewidth2 = (int)((Bits * dimx + 31) / 32) * 4;
    
                //n. byte da copiare (= byte x row)
                int bytes = (Bits/8) * dimx;
    
                //array dei values pixel
                byte[] a = new byte[bytes];
    
                // Stream della base
                FileStream f = File.Open(path, FileMode.OpenOrCreate);
    
                int sp; //puntatore stream base
    
                //ciclo dei pixel regione di sovrascrittura
                for (int y = y0; y < y0 + dimy; y++)
                {
                    //puntatore stream ad inizio row (x0,y0) su base
                    sp = Ofs + (Bits / 8) * x0 + truewidth1 * (Dim2 - y - 1);
    
                    //aggiorna puntatore stream 
                    f.Seek(sp, SeekOrigin.Begin);
    
                    //preleva ibyte-pixel nello stream
                    f.Read(a, 0, bytes);
    
                    System.Runtime.InteropServices.Marshal.Copy(a, 0, Ptr, truewidth2);
    
                    //aggiorna il puntatore bmdata ai prossimi 3 byte
                    Ptr = new IntPtr((int)IntPtr.Add(Ptr, truewidth2));
                }
    
                // Chiusura   
                b.UnlockBits(bmdata);
                f.Close();
    
                return b;
            }
    Ultima modifica di escocat; 21-02-2015 a 23:16

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.