Pagina 3 di 3 primaprima 1 2 3
Visualizzazione dei risultati da 21 a 27 su 27
  1. #21
    Quote Originariamente inviata da escocat Visualizza il messaggio
    Che bello. La storia è molto più affascinante di quanto pensassi.
    1) o prendiamo la parte intera di (Bits * Dim1 + 31) / 32 che sarebbe il numero intero che precede e
    non eccede quel valore float,
    2) o prendiamo la funzione soffitto ovvero il Math.Ceiling di Bits * Dim1 / 32 dunque
    il numero intero che segue quel valore float. Le due versioni forniscono gli stessi valori.
    Di fatto si usa la prima, e per evitare eventuali casini di arrotondamento con i double, e perché è più veloce (in linea di massima, o c'è un perché o l'aritmetica FP di norma si usa poco).
    Qui sono ancora al buio.
    Tutti i .BMP "normali" sono in formato little-endian (prima byte meno significativo, poi più significativo), non so dove tu abbia letto l'opposto.
    Questa è la parte "affascinante" e credo di aver capito quanto segue:
    ...
    Onestamente, non capisco dove stia tutta la complicazione: un BMP è formato da un header seguito da una serie di scanline di pixel.
    Ogni scanline è grande width*bpp/8 bytes, arrotondati per eccesso ai 4 byte successivi. Per cui, volendo accedere ad un pixel, semplicemente prima calcoli dove inizia la riga che ti interessa (considerando che le righe normalmente sono memorizzate in ordine opposto al normale), quindi calcoli l'offset (x * bpp / 8).

    bpp è il numero di bit per pixel, che è memorizzato nell'header BITMAPINFOHEADER, nel membro biBitCount. La maggior parte delle bitmap sono a 24 bpp (3 byte per pixel).

    Qui
    https://bitbucket.org/mitalia/irfanp...on.h?at=master
    https://bitbucket.org/mitalia/irfanp....cpp?at=master
    trovi una classe (C++) che ho scritto un bel po' di tempo fa per la gestione di DIB in memoria, tolta la parte di gestione della DibSection e dei memory mapped file, c'è tutto quello che serve per capire come si accede ai pixel.

    La bitmap in cui vai a copiare, invece, non è detto che funzioni in questa maniera, anzi, mi aspetto che sia un po' più logica e (1) non abbia una stride "stramba" (sarà dimensioni pixel * larghezza) e (2) le righe siano memorizzate nell'ordine corretto. Naturalmente prima di scriverci dentro dovrai impostarla alle dimensioni desiderate (e con il formato di pixel che ti interessa).
    Amaro C++, il gusto pieno dell'undefined behavior.

  2. #22
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ecco il codice che ho scritto per il metodo GetBmp che usa la Marshal al posto di PSet, l'ho adattato a quello che avevo scritto prima e quindi ancora copia tre byte per volta, mi ero prefissato di testarlo per poi avanzare una nuova versione che copia intere row MA NON FUNZIONA e non capisco perchè, a me sembra giusto oltre che BELLO. Puoi vedere dove ho sbagliato prima che mi butto dal balcone (5° piano) insieme al mio portatile?


    codice:
           public static Bitmap MarshalGetBmp(string path, int x0, int y0, int dimx, int dimy)
            {
                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);
    
                //Costruzione BitmapData associata a b
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, dimx, dimy);
                System.Drawing.Imaging.BitmapData bmdata = b.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, b.PixelFormat);
    
                IntPtr Ptr = bmdata.Scan0;
    
                int truewidth1 = (int)((Bits * Dim1 + 31) / 32) * 4;
                int truewidth2 = (int)((Bits * dimx + 31) / 32) * 4;
    
                FileStream f = File.Open(path, FileMode.OpenOrCreate);
    
                byte[] a = new byte[3]; 
                int sp;
    
                for (int y = y0; y < y0 + dimy; y++)
                    for (int x = x0; x < x0 + dimx; x++)
                    {
                        sp = Ofs + (Bits / 8) * x + truewidth1 * (Dim2 - y - 1);
    
                        f.Seek(sp, SeekOrigin.Begin);
                        f.Read(a, 0, 3);
    
                        System.Runtime.InteropServices.Marshal.Copy(a, 0, Ptr, 3);
    
                        //sposta il puntatore in memoria b di un offset pari ai 3 byte letti 
                        Ptr = new IntPtr((int)IntPtr.Add(Ptr, 3));
                    }
    
                f.Close();
                b.UnlockBits(bmdata);
    
                return b;
            }

    Ah, ti giuro che in alcune intestazioni il byte basso è in alto, mi succede quando ottengo una bitmap da file scaricati o da altri formati, con le bitmap che creo io questo non succede.
    Ultima modifica di escocat; 19-02-2015 a 03:31

  3. #23
    codice:
    using System;
    using System.IO;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;
    
    // ...
    
    public static Bitmap LoadClippedBitmap(Stream fs, Rectangle viewport)
    {
        BinaryReader br = new BinaryReader (fs);
        // Check magic number iniziale
        if (br.ReadUInt16 () != 0x4d42)
            throw new InvalidDataException ("Invalid BMP file");
        fs.Seek (0x0a, SeekOrigin.Begin);
        uint dataStart = br.ReadUInt32 ();
        uint headerSize = br.ReadUInt32 ();
        // Controlla se stiamo leggendo un BITMAPINFOHEADER/BITMAPV4HEADER/BITMAPV5HEADER
        if (headerSize != 40 && headerSize != 108 && headerSize != 124)
            throw new InvalidDataException ("Unsupported bitmap header version");
        fs.Seek (0x12, SeekOrigin.Begin);
        int width = br.ReadInt32 ();
        int height = br.ReadInt32 ();
        // Clippa il viewport se necessario
        viewport.Intersect (new Rectangle (0, 0, width, height));
        if (br.ReadInt16() != 1)
            throw new InvalidDataException ("Invalid bit planes count");
        short bpp = br.ReadInt16 ();
        if (br.ReadUInt32 () != 0)
            throw new InvalidDataException ("Unsupported compression method");
        // Calcola la stride direttamente in bit
        int stride = (width * bpp + 31) / 32 * 4;
        PixelFormat fmt;
        switch (bpp)
        {
        case 1:
            fmt = PixelFormat.Format1bppIndexed;
            break;
        case 8:
            fmt = PixelFormat.Format8bppIndexed;
            break;
        case 24:
            fmt = PixelFormat.Format24bppRgb;
            break;
        default:
            // In teoria si potrebbe avere anche 4, ma GDI+ non lo supporta e dovremmo
            // fare upsampling al volo
            throw new InvalidDataException ("Unsupported bit depth");
        }
        // Costruisce la bitmap da restituire
        Bitmap ret = new Bitmap (viewport.Width, viewport.Height, fmt);
        // Carica la palette
        fs.Seek (0x2e, SeekOrigin.Begin);
        uint paletteSize = br.ReadUInt32 ();
        if (paletteSize != 0)
        {
            if (bpp > 8)
                throw new InvalidDataException ("Error: palette unsupported for images with bpp > 8");
            ColorPalette palette = ret.Palette;
            fs.Seek (0x0e + headerSize, SeekOrigin.Begin);
            for (int i=0; i<paletteSize; ++i)
                palette.Entries [i] = Color.FromArgb(br.ReadInt32 ());
            ret.Palette = palette;
        }
    
        // Blocca i pixel dell'immagine in memoria
        BitmapData bd = ret.LockBits (new Rectangle (0, 0, viewport.Width, viewport.Height), ImageLockMode.ReadWrite,  fmt);
        int readLen = (viewport.Width * bpp + 7) / 8;
        // Andiamo nel verso "sbagliato", in modo da andare in avanti nel file (aiuta la cache IO)
        for (int y = viewport.Bottom-1; y>= viewport.Top; y--)
        {
            // Punto di partenza nel file
            fs.Seek (dataStart + (height - 1 - y) * stride + (viewport.Left * bpp + 7) / 8, SeekOrigin.Begin);
            // Indirizzo di destinazione in ret
            IntPtr target = IntPtr.Add(bd.Scan0, (y-viewport.Top) * bd.Stride);
            // Legge i dati e li copia nella bitmap target
            byte[] data = br.ReadBytes (readLen);
            Marshal.Copy (data, 0, target, data.Length);
        }
        // Sblocca i pixel
        ret.UnlockBits (bd);
        return ret;
    }
    Ultima modifica di MItaly; 19-02-2015 a 10:09
    Amaro C++, il gusto pieno dell'undefined behavior.

  4. #24
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    No aspetta MItalyyyyyyyyyyyy nn mi aiutare troppo io devo capire, e forse ci siamo, questo codice copia una piccola bitmap (clip) b2 su una più grande (base) b1 in una posizione x0,y0 (sulla base). Il risultato c'è solo che mi "stiracchia" la clip....puoi dirmi dove ho sbagliato? E' un codice facile da testare.....
    codice:
               
     Bitmap b1 = new Bitmap("base.bmp");//es. 500x500           
     Bitmap b2 = new Bitmap("clip.bmp");//es. 200x100            
    
     int x0 = 150;           
     int y0 = 250;            
    
    //Imposta i dati di bmdata e blocca in memoria. bmdata1 contiene i byte della regione di b1 da sovrascrivere           
     System.Drawing.Rectangle rect1 = new System.Drawing.Rectangle(x0, y0, b2.Width, b2.Height);       
        System.Drawing.Imaging.BitmapData bmdata1 = b1.LockBits(rect1, System.Drawing.Imaging.ImageLockMode.ReadWrite, b1.PixelFormat);           
    
     //bmdata2 contiene tutti i byte della clip            
    System.Drawing.Rectangle rect2 = new System.Drawing.Rectangle(0, 0, b2.Width, b2.Height);            System.Drawing.Imaging.BitmapData bmdata2 = b2.LockBits(rect2, System.Drawing.Imaging.ImageLockMode.ReadWrite, b2.PixelFormat);           
    
     //Le due bmdata hanno la stessa estensione : b2.Height righe di 3*b2.Width byte  
     int truewidth = (int)((24 * b2.Width + 31) / 32) * 4;            
    
    //bytes da copiare           
     int bytes = Math.Abs(bmdata2.Stride) * b2.Height; //oppure 3*b2.Width*b2.Height byte             byte[] values = new byte[bytes];            
    
    // Puntatori ad inizio bmdata           
     IntPtr Ptr1 = bmdata1.Scan0;            
    IntPtr Ptr2 = bmdata2.Scan0;          
    
      // Load dei pixel b2 in array           
     //non serve caricare i byte della regione di b1 da sovrascrivere  
     System.Runtime.InteropServices.Marshal.Copy(Ptr2, values, 0, bytes);          
    
      //visto che dobbiamo copiare tutti i values b2 in b1 pari pari così come sono            
     //non c'è bisogno del loop in x e y si fa direttamente la copia dei values in bmdata1!!!!!!           
     // Copia array su b1            
    System.Runtime.InteropServices.Marshal.Copy(values, 0, Ptr1, bytes);            
    
    // Chiusura               
    b.UnlockBits(bmdata1);           
     b2.UnlockBits(bmdata1);            
    
    pictureBox1.Image = b;
    Ultima modifica di escocat; 19-02-2015 a 13:26

  5. #25
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ah non posso caricare solo la regione della base da sostituire, la base deve essere caricata tutta in memoria perchè poi la regione come la rimetto al posto giusto in x0,y0? Vabbè torno subito tanto ormai sono entrato nel meccanismo della magica Marshal.....

  6. #26
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    MYTALY. Ce l'ho fatta!!!!!!!!!!
    Questo è il codice di un metodo che copia una bitmap in memoria su una bitmap base esterna e usa la Marshal. Per adesso uso lo scanning pixelbypixel ma l'importante è che funziona perfettamente. Ora cerco di perfezionare questo codice per uno scanning rowbyrow. Poi passerò all'altro metodo che mi serve per Xna cioè la copia di una porzione di bitmap esterna in una bitmap in memoria. Spero che non mi abbandonerai. Scusa se non ho voluto il tuo codice mi interessa di più la correzione di un codice che sento "mio". Tu dimmi pure eventuali bug o ineleganze.

    codice:
            //Copia Marshal di una bitmap clip b2 su una bitmap base b1 su disco nella posizione x0, y0. 
            public static void MarshalCopy(string path, int x0, int y0, System.Drawing.Bitmap b2)
            {
                //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;
    
                //bmdata contiene tutti i byte della clip
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, b2.Width, b2.Height);
                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 truewidth = (int)((24 * Dim1 + 31) / 32) * 4;
    
                //array dei values pixel
                byte[] a = new byte[3];
    
                // 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 + b2.Height; y++)
                    for (int x = x0; x < x0 + b2.Width; x++)
                    {
                        //puntatore stream al pixel (x,y) su base
                        sp = Ofs + (Bits / 8) * x + truewidth * (Dim2 - y - 1);
    
                        //copia tre byte-pixel della memory b2 in array
                        System.Runtime.InteropServices.Marshal.Copy(Ptr, a, 0, 3);
    
                        //aggiorna puntatore stream 
                        f.Seek(sp, SeekOrigin.Begin);
    
                        //sovrascrive i 3 byte-pixel nello stream
                        f.Write(a, 0, 3);
    
                        //aggiorna il puntatore bmdata ai prossimi 3 byte
                        Ptr = new IntPtr((int)IntPtr.Add(Ptr, 3));
                    }
    
                // Chiusura   
                b2.UnlockBits(bmdata);
                f.Close();
                 
            }
    AH c'è sempre quel problema del byte basso - byte alto nelle dimensioni scritte in intestazione. Ho creato una bitmap base tutta verde e non solo mi ha invertito i byte della dimensione ma mi scrive 0 nella codifica (invece di 24) e pure nell'l'offset invece di 54. MAH.....
    Ultima modifica di escocat; 19-02-2015 a 20:45

  7. #27
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ecco qui la versione rowbyrow della MarshalCopy, in versione "overload" con il parametro di overedge.

    codice:
            //Copia Marshal di una bitmap clip b2 su una bitmap base b1 su disco nella posizione x0, y0. 
            //Il parametro 'edge' decide se inibire una copia fuori base (edge = false) oppure procedere comunque
            //facendo però rientrare l'origine del rectangle destinazione in modo da poter copiare la b2 intera 
            //(edge = true).
           public static void MarshalCopy(string path, int x0, int y0, System.Drawing.Bitmap b2, bool edge)
            {
                if (b2 == null)
                    return;
    
                //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;
    
                if (edge)
                {
                    if (x0 < 0)
                        x0 = 0;
                    if (y0 < 0)
                        y0 = 0;
                    if (x0 + b2.Width > Dim1)
                        x0 -= x0 + b2.Width - Dim1;
                    if (y0 + b2.Height > Dim2)
                        y0 -= y0 + b2.Height - Dim2;
                }
                else
                {
                    if (x0 < 0 | y0 < 0 | x0 + b2.Width > Dim1 | y0 + b2.Height > Dim2)
                        return;
                }
    
                //bmdata contiene tutti i byte della clip
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, b2.Width, b2.Height);
                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 truewidth = (int)((24 * Dim1 + 31) / 32) * 4;
    
                //n. byte da copiare (= byte x row)
                int bytes = bmdata.Stride;  
    
                //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 + b2.Height; y++)
                {
                    //puntatore stream ad inizio row (x0,y0) su base
                    sp = Ofs + (Bits / 8) * x0 + truewidth * (Dim2 - y - 1);
    
                    //copia bytes byte della memory b2 in array
                    System.Runtime.InteropServices.Marshal.Copy(Ptr, a, 0, bytes);
    
                    //aggiorna puntatore stream 
                    f.Seek(sp, SeekOrigin.Begin);
    
                    //sovrascrive i 3 byte-pixel nello stream
                    f.Write(a, 0, bytes);
    
                    //aggiorna il puntatore bmdata ai prossimi bytes 
                    Ptr = new IntPtr((int)IntPtr.Add(Ptr, bytes));
                }
    
                // Chiusura   
                b2.UnlockBits(bmdata);
                f.Close();
            }
    Ultima modifica di escocat; 20-02-2015 a 00:43

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.