2017-04-08 5 views
0

sortieimage n'est pas rembourré correctement

enter image description here

Je pense que le code suivant ne donne pas le résultat correct.

Quel est le problème avec le code suivant?

public class ImagePadder 
{ 
    public static Bitmap Pad(Bitmap image, int newWidth, int newHeight) 
    { 
     int width = image.Width; 
     int height = image.Height; 

     if (width >= newWidth) throw new Exception("New width must be larger than the old width"); 
     if (height >= newHeight) throw new Exception("New height must be larger than the old height"); 

      Bitmap paddedImage = Grayscale.CreateGrayscaleImage(newWidth, newHeight); 

      BitmapLocker inputImageLocker = new BitmapLocker(image); 
      BitmapLocker paddedImageLocker = new BitmapLocker(paddedImage); 

      inputImageLocker.Lock(); 
      paddedImageLocker.Lock(); 

      //Reading row by row 
      for (int y = 0; y < image.Height; y++) 
      { 
       for (int x = 0; x < image.Width; x++) 
       {  
        Color col = inputImageLocker.GetPixel(x, y); 

        paddedImageLocker.SetPixel(x, y, col); 
       } 
      } 

      string str = string.Empty;     

      paddedImageLocker.Unlock(); 
      inputImageLocker.Unlock(); 

      return paddedImage;    
    } 
} 

pertinent Code Source:

public class BitmapLocker : IDisposable 
{ 
    //private properties 
    Bitmap _bitmap = null; 
    BitmapData _bitmapData = null; 
    private byte[] _imageData = null; 

    //public properties 
    public bool IsLocked { get; set; } 
    public IntPtr IntegerPointer { get; private set; } 
    public int Width { get { return _bitmap.Width; } } 
    public int Height { get { return _bitmap.Height; } } 
    public int Stride { get { return _bitmapData.Stride; } } 
    public int ColorDepth { get { return Bitmap.GetPixelFormatSize(_bitmap.PixelFormat); } } 
    public int Channels { get { return ColorDepth/8; } } 
    public int PaddingOffset { get { return _bitmapData.Stride - (_bitmap.Width * Channels); } } 
    public PixelFormat ImagePixelFormat { get { return _bitmap.PixelFormat; } } 
    public bool IsGrayscale { get { return Grayscale.IsGrayscale(_bitmap); } } 

    //Constructor 
    public BitmapLocker(Bitmap source) 
    { 
     IsLocked = false; 
     IntegerPointer = IntPtr.Zero; 
     this._bitmap = source; 
    } 

    /// Lock bitmap 
    public void Lock() 
    { 
     if (IsLocked == false) 
     { 
      try 
      { 
       // Lock bitmap (so that no movement of data by .NET framework) and return bitmap data 
       _bitmapData = _bitmap.LockBits(
               new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), 
               ImageLockMode.ReadWrite, 
               _bitmap.PixelFormat); 

       // Create byte array to copy pixel values 
       int noOfBitsNeededForStorage = _bitmapData.Stride * _bitmapData.Height; 

       int noOfBytesNeededForStorage = noOfBitsNeededForStorage/8; 

       _imageData = new byte[noOfBytesNeededForStorage * ColorDepth];//# of bytes needed for storage 

       IntegerPointer = _bitmapData.Scan0; 

       // Copy data from IntegerPointer to _imageData 
       Marshal.Copy(IntegerPointer, _imageData, 0, _imageData.Length); 

       IsLocked = true; 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
     } 
     else 
     { 
      throw new Exception("Bitmap is already locked."); 
     } 
    } 

    /// Unlock bitmap 
    public void Unlock() 
    { 
     if (IsLocked == true) 
     { 
      try 
      { 
       // Copy data from _imageData to IntegerPointer 
       Marshal.Copy(_imageData, 0, IntegerPointer, _imageData.Length); 

       // Unlock bitmap data 
       _bitmap.UnlockBits(_bitmapData); 

       IsLocked = false; 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
     } 
     else 
     { 
      throw new Exception("Bitmap is not locked."); 
     } 
    } 

    public Color GetPixel(int x, int y) 
    { 
     Color clr = Color.Empty; 

     // Get color components count 
     int cCount = ColorDepth/8; 

     // Get start index of the specified pixel 
     int i = (Height - y - 1) * Stride + x * cCount; 

     int dataLength = _imageData.Length - cCount; 

     if (i > dataLength) 
     { 
      throw new IndexOutOfRangeException(); 
     } 

     if (ColorDepth == 32) // For 32 bpp get Red, Green, Blue and Alpha 
     { 
      byte b = _imageData[i]; 
      byte g = _imageData[i + 1]; 
      byte r = _imageData[i + 2]; 
      byte a = _imageData[i + 3]; // a 
      clr = Color.FromArgb(a, r, g, b); 
     } 
     if (ColorDepth == 24) // For 24 bpp get Red, Green and Blue 
     { 
      byte b = _imageData[i]; 
      byte g = _imageData[i + 1]; 
      byte r = _imageData[i + 2]; 
      clr = Color.FromArgb(r, g, b); 
     } 
     if (ColorDepth == 8) 
     // For 8 bpp get color value (Red, Green and Blue values are the same) 
     { 
      byte c = _imageData[i]; 
      clr = Color.FromArgb(c, c, c); 
     } 
     return clr; 
    } 

    public void SetPixel(int x, int y, Color color) 
    { 

     // Get color components count 
     int cCount = ColorDepth/8; 

     // Get start index of the specified pixel 
     int i = (Height - y - 1) * Stride + x * cCount; 

     try 
     { 
      if (ColorDepth == 32) // For 32 bpp set Red, Green, Blue and Alpha 
      { 
       _imageData[i] = color.B; 
       _imageData[i + 1] = color.G; 
       _imageData[i + 2] = color.R; 
       _imageData[i + 3] = color.A; 
      } 
      if (ColorDepth == 24) // For 24 bpp set Red, Green and Blue 
      { 
       _imageData[i] = color.B; 
       _imageData[i + 1] = color.G; 
       _imageData[i + 2] = color.R; 
      } 
      if (ColorDepth == 8) 
      // For 8 bpp set color value (Red, Green and Blue values are the same) 
      { 
       _imageData[i] = color.B; 
      } 
     } 
     catch (Exception ex) 
     { 
      throw new Exception("(" + x + ", " + y + "), " + _imageData.Length + ", " + ex.Message + ", i=" + i); 
     } 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      // free managed resources 
      _bitmap = null; 
      _bitmapData = null; 
      _imageData = null; 
      IntegerPointer = IntPtr.Zero; 
     } 
    } 
} 

Répondre

2

La mise en page d'un bitmap Windows est différente de celle que vous pourriez attendre. La ligne inférieure de l'image est la première ligne en mémoire, et continue à partir de là. Il peut également être disposé dans l'autre sens lorsque la hauteur est négative, mais ceux-ci ne sont pas souvent rencontrés.

Votre calcul d'un décalage dans le bitmap semble en tenir compte, donc votre problème doit être plus subtil.

int i = (Height - y - 1) * Stride + x * cCount; 

Le problème est que la classe BitmapData prend déjà en compte et tente de le corriger pour vous. L'image bitmap décrite ci-dessus est une image bitmap ascendante. De la documentation pour BitmapData.Stride:

Le pas est la largeur d'une seule rangée de pixels (une ligne de balayage), arrondie à une frontière de quatre octets. Si la foulée est positive, le bitmap est top-down. Si la foulée est négative, l'image bitmap est ascendante.

Il est destiné à être utilisé avec la propriété Scan0 pour accéder au bitmap de manière cohérente, que ce soit de haut en bas ou de bas en haut.