2017-04-08 7 views
0

AusgangBild wird nicht richtig gepolstert

enter image description here

Ich denke, der folgende Code wird nicht das richtige Ergebnis zu geben.

Was ist falsch mit dem folgenden Code?

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

Relevant Source Code:

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

Antwort

2

Das Layout eines Windows-Bitmap ist anders als man erwarten könnte. Die untere Zeile des Bildes ist die erste Zeile im Speicher und wird von dort rückwärts fortgesetzt. Es kann auch anders herum angeordnet werden, wenn die Höhe negativ ist, aber diese werden nicht oft angetroffen.

Die Berechnung eines Offsets in die Bitmap scheint dies zu berücksichtigen, daher muss Ihr Problem subtiler sein.

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

Das Problem ist, dass die BitmapData Klasse nimmt dies bereits berücksichtigt und versucht, es für Sie zu beheben. Die Bitmap, die ich oben beschrieben habe, ist eine Bottom-Up-Bitmap. Aus der Dokumentation für BitmapData.Stride:

Der Schritt die Breite einer einzelnen Reihe von Pixeln (a-Scan-Linie), aufgerundet auf eine Vier-Byte-Grenze. Wenn der Schritt positiv ist, ist die Bitmap von oben nach unten. Wenn der Schritt negativ ist, ist die Bitmap von unten nach oben.

Es ist beabsichtigt, mit der Scan0 Eigenschaft verwendet werden, um die Bitmap in einer konsistenten Art und Weise zuzugreifen, ob es von oben nach unten oder von unten nach oben ist.

Verwandte Themen