2017-04-08 99 views
0

輸出圖像沒有被正確地填充

enter image description here

我認爲下面的代碼是不給正確的結果。

以下代碼出現了什麼問題?

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;    
    } 
} 

相關的源代碼:

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; 
     } 
    } 
} 

回答

2

一個Windows位圖的佈局是比你想象的不同。 底部的圖像的行是第一行內存中的行,並從那裏繼續向後。當高度爲負值時,也可以通過其他方式進行佈置,但不常遇到這種情況。

您對位圖偏移量的計算似乎考慮到了這一點,所以您的問題必須更細微。

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

的問題是BitmapData類已經考慮到了這,並試圖修復它。我上面描述的位圖是一個自底向上的位圖。從文檔BitmapData.Stride

跨距是像素的單行(掃描線)的寬度,向上舍入到一個四字節的邊界。 如果步幅爲正,則位圖是自頂向下的。如果步幅是負數,則位圖是自下而上的。

它的目的是與Scan0屬性被用來訪問以一致的方式無論是自上而下還是自下而上的位圖。