Frage

Ich mag teilweise transparente Bilder in Drag / Drop-Operationen verwenden. Das ist alles eingerichtet und funktioniert gut, aber die eigentliche Transformation zur Transparenz hat eine seltsame Nebenwirkung. Aus irgendeinem Grund scheinen die Pixel vor einem schwarzen Hintergrund gemischt werden.

Das folgende Bild beschreibt das Problem:

Transparenz Problem

Abbildung a) ist das ursprüngliche Bitmap.

Abbildung b) ist das, was produziert wird, nachdem Alpha-Blending durchgeführt wurde. Offensichtlich ist dies viel dunkler als die vorgesehenen 50% Alpha-Filter vorgesehen.

Abbildung c) ist der gewünschte Effekt, Bild a) mit 50% Transparenz (hinzugefügt, um die Zusammensetzung mit einem Zeichenprogramm).

Der Code Ich benutze das trasparent Bild zu erzeugen, ist die folgende:

Bitmap bmpNew = new Bitmap(bmpOriginal.Width, bmpOriginal.Height);
Graphics g = Graphics.FromImage(bmpNew);

// Making the bitmap 50% transparent:
float[][] ptsArray ={ 
    new float[] {1, 0, 0, 0, 0},        // Red
    new float[] {0, 1, 0, 0, 0},        // Green
    new float[] {0, 0, 1, 0, 0},        // Blue
    new float[] {0, 0, 0, 0.5f, 0},     // Alpha
    new float[] {0, 0, 0, 0, 1}         // Brightness
};
ColorMatrix clrMatrix = new ColorMatrix(ptsArray);
ImageAttributes imgAttributes = new ImageAttributes();
imgAttributes.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(bmpOriginal, new Rectangle(0, 0, bmpOriginal.Width, bmpOriginal.Height), 0, 0, bmpOriginal.Width, bmpOriginal.Height, GraphicsUnit.Pixel, imgAttributes);
Cursors.Default.Draw(g, new Rectangle(bmpOriginal.Width / 2 - 8, bmpOriginal.Height / 2 - 8, 32, 32));
g.Dispose();
imgAttributes.Dispose();
return bmpNew;

Weiß jemand, warum das Alpha-Blending nicht funktioniert?

Update I:

Aus Gründen der Klarheit wird der Code funktioniert, wenn ich auf einer gezogenen Oberfläche Alphablending. Das Problem ist, dass ich ein völlig halbtransparente Bild aus einem vorhandenen Bild erstellen möge, und verwenden Sie dies als dynamische Cursor während Drag / Drop-Operationen. Auch die oben Überspringen und nur ein gefülltes Rechteck von Farbe malt 88ffffff eine dunkelgraue Farbe ergibt. Etwas faul ist los mit dem Symbol.

Update II:

Da ich eine ganze Menge reseached habe und glaube, dass dies hat etwas mit der Cursor Schöpfung zu tun, ich werde schließen, dass Code unten zu. Wenn ich die Bitmap GetPixel-Probe scheinen kurz vor dem CreateIconIndirect Aufruf, die vier Farbwerte intakt. So habe ich ein Gefühl, die Schuldigen könnte die hbmColor oder die hbmMask Mitglieder der ICONINFO Struktur sein.

Hier ist die ICONINFO Struktur:

public struct IconInfo {    // http://msdn.microsoft.com/en-us/library/ms648052(VS.85).aspx
    public bool fIcon;      // Icon or cursor. True = Icon, False = Cursor
    public int xHotspot;
    public int yHotspot;
    public IntPtr hbmMask;  // Specifies the icon bitmask bitmap. If this structure defines a black and white icon, 
                            // this bitmask is formatted so that the upper half is the icon AND bitmask and the lower 
                            // half is the icon XOR bitmask. Under this condition, the height should be an even multiple of two. 
                            // If this structure defines a color icon, this mask only defines the AND bitmask of the icon.
    public IntPtr hbmColor; // Handle to the icon color bitmap. This member can be optional if this structure defines a black 
                            // and white icon. The AND bitmask of hbmMask is applied with the SRCAND flag to the destination; 
                            // subsequently, the color bitmap is applied (using XOR) to the destination by using the SRCINVERT flag. 

}

Und hier ist der Code, der tatsächlich den Cursor erstellt:

    public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot) {
        IconInfo iconInfo = new IconInfo();
        GetIconInfo(bmp.GetHicon(), ref iconInfo);
        iconInfo.hbmColor = (IntPtr)0;
        iconInfo.hbmMask = bmp.GetHbitmap();
        iconInfo.xHotspot = xHotSpot;
        iconInfo.yHotspot = yHotSpot;
        iconInfo.fIcon = false;

        return new Cursor(CreateIconIndirect(ref iconInfo));
    }

Die beiden externen Funktionen sind wie folgt definiert:

    [DllImport("user32.dll", EntryPoint = "CreateIconIndirect")]
    public static extern IntPtr CreateIconIndirect(ref IconInfo icon);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
War es hilfreich?

Lösung

GDI + hat eine Reihe von Problemen auf Alpha-Blending Zusammenhang, wenn Interop mit GDI zu tun (und Win32). In diesem Fall mischt der Anruf an bmp.GetHbitmap () Ihr Bild mit einem schwarzen Hintergrund. Ein Artikel über Codeproject weitere Einzelheiten über das Problem gibt, und eine Lösung, die verwendet wurde, für Bilder, die auf eine Bildliste hinzuzufügen.

Sie sollten fähig sein, einen ähnlichen Code zu verwenden, um die HBITMAP zu bekommen für die Maske zu verwenden:

[DllImport("kernel32.dll")]
public static extern bool RtlMoveMemory(IntPtr dest, IntPtr source, int dwcount);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateDIBSection(IntPtr hdc, [In, MarshalAs(UnmanagedType.LPStruct)]BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);

public static IntPtr GetBlendedHBitmap(Bitmap bitmap)
{
    BITMAPINFO bitmapInfo = new BITMAPINFO();
    bitmapInfo.biSize = 40;
    bitmapInfo.biBitCount = 32;
    bitmapInfo.biPlanes = 1;

    bitmapInfo.biWidth = bitmap.Width;
    bitmapInfo.biHeight = -bitmap.Height;

    IntPtr pixelData;
    IntPtr hBitmap = CreateDIBSection(
        IntPtr.Zero, bitmapInfo, 0, out pixelData, IntPtr.Zero, 0);

    Rectangle bounds = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    BitmapData bitmapData = bitmap.LockBits(
        bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb );
    RtlMoveMemory(
        pixelData, bitmapData.Scan0, bitmap.Height * bitmapData.Stride);

    bitmap.UnlockBits(bitmapData);
    return hBitmap;
}

Andere Tipps

Vor einiger Zeit las ich dieses Problem aus einer Anforderung ergibt sich für Pre-multiplizierten Alpha-Kanäle in dem Bitmaps. Ich bin mir nicht sicher, ob dies ein Problem mit Windows-Cursor oder GDI, und für das Leben von mir, ich kann nicht Dokumentation bezüglich diesem finden. So, während diese Erklärung kann oder nicht richtig sein kann, wird der folgende Code in der Tat tun, was Sie wollen, einen Pre-Alpha-Kanal multiplizierten im Cursor-Bitmap verwendet wird.

public class CustomCursor
{
  // alphaLevel is a value between 0 and 255. For 50% transparency, use 128.
  public Cursor CreateCursorFromBitmap(Bitmap bitmap, byte alphaLevel, Point hotSpot)
  {
    Bitmap cursorBitmap = null;
    External.ICONINFO iconInfo = new External.ICONINFO();
    Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

    try
    {
      // Here, the premultiplied alpha channel is specified
      cursorBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppPArgb);

      // I'm assuming the source bitmap can be locked in a 24 bits per pixel format
      BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
      BitmapData cursorBitmapData = cursorBitmap.LockBits(rectangle, ImageLockMode.WriteOnly, cursorBitmap.PixelFormat);

      // Use either SafeCopy() or UnsafeCopy() to set the bitmap contents
      SafeCopy(bitmapData, cursorBitmapData, alphaLevel);
      //UnsafeCopy(bitmapData, cursorBitmapData, alphaLevel);

      cursorBitmap.UnlockBits(cursorBitmapData);
      bitmap.UnlockBits(bitmapData);

      if (!External.GetIconInfo(cursorBitmap.GetHicon(), out iconInfo))
        throw new Exception("GetIconInfo() failed.");

      iconInfo.xHotspot = hotSpot.X;
      iconInfo.yHotspot = hotSpot.Y;
      iconInfo.IsIcon = false;

      IntPtr cursorPtr = External.CreateIconIndirect(ref iconInfo);
      if (cursorPtr == IntPtr.Zero)
        throw new Exception("CreateIconIndirect() failed.");

      return (new Cursor(cursorPtr));
    }
    finally
    {
      if (cursorBitmap != null)
        cursorBitmap.Dispose();
      if (iconInfo.ColorBitmap != IntPtr.Zero)
        External.DeleteObject(iconInfo.ColorBitmap);
      if (iconInfo.MaskBitmap != IntPtr.Zero)
        External.DeleteObject(iconInfo.MaskBitmap);
    }
  }

  private void SafeCopy(BitmapData srcData, BitmapData dstData, byte alphaLevel)
  {
    for (int y = 0; y < srcData.Height; y++)
      for (int x = 0; x < srcData.Width; x++)
      {
        byte b = Marshal.ReadByte(srcData.Scan0, y * srcData.Stride + x * 3);
        byte g = Marshal.ReadByte(srcData.Scan0, y * srcData.Stride + x * 3 + 1);
        byte r = Marshal.ReadByte(srcData.Scan0, y * srcData.Stride + x * 3 + 2);

        Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4, b);
        Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4 + 1, g);
        Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4 + 2, r);
        Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4 + 3, alphaLevel);
      }
  }

  private unsafe void UnsafeCopy(BitmapData srcData, BitmapData dstData, byte alphaLevel)
  {
    for (int y = 0; y < srcData.Height; y++)
    {
      byte* srcRow = (byte*)srcData.Scan0 + (y * srcData.Stride);
      byte* dstRow = (byte*)dstData.Scan0 + (y * dstData.Stride);

      for (int x = 0; x < srcData.Width; x++)
      {
        dstRow[x * 4] = srcRow[x * 3];
        dstRow[x * 4 + 1] = srcRow[x * 3 + 1];
        dstRow[x * 4 + 2] = srcRow[x * 3 + 2];
        dstRow[x * 4 + 3] = alphaLevel;
      }
    }
  }
}

Die pinvoke Erklärungen werden in der externen Klasse gefunden, hier:

public class External
{
  [StructLayout(LayoutKind.Sequential)]
  public struct ICONINFO
  {
    public bool IsIcon;
    public int xHotspot;
    public int yHotspot;
    public IntPtr MaskBitmap;
    public IntPtr ColorBitmap;
  };

  [DllImport("user32.dll")]
  public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);

  [DllImport("user32.dll")]
  public static extern IntPtr CreateIconIndirect([In] ref ICONINFO piconinfo);

  [DllImport("gdi32.dll")]
  public static extern bool DeleteObject(IntPtr hObject);

  [DllImport("gdi32.dll")]
  public static extern IntPtr CreateBitmap(int nWidth, int nHeight, uint cPlanes, uint cBitsPerPel, IntPtr lpvBits);
}

Ein paar Anmerkungen zu dem Code:

  1. die unsichere Methode zu verwenden, UnsafeCopy (), können Sie mit der / unsicheren Flagge kompilieren müssen.
  2. Die Bitmap Kopiermethoden hässlich sind, vor allem die sichere Methode, die verwendet Marshal.ReadByte () / Marshal.WriteByte () aufruft. Es muss einen schnelleren Weg, um Bitmap-Daten zu kopieren, während auch Alpha-Bytes eingefügt wird.
  3. Ich davon aus, dass der Quell-Bitmap der Lage ist, in einem 24 Bit pro Pixel-Format gesperrt werden. Dies sollte kein Problem sein, aber.

versuchen Blau den Wert auf 0,7 oder 0,6 zu senken und sehen, ob das näher an, was Sie wollen.

Hier ist eine gute Website, die Colormatrix erklärt:

Wenn ich Ihren Code ausführen ein Bild in einem picturebox mit einem Hintergrundgitterbild zu ändern, erhalte ich die Wirkung, die Sie ohne Änderung der Codes gewünscht. Vielleicht ist Ihr Bild wird über den oberen Teil etwas gezogen, die eine dunkle Farbe hat ...

Verzeih mir, wenn mein Vorschlag zu simpel ist (ich zu C # noch neu bin), aber ich fand dies auf der MSDN-Website und vielleicht diese könnten Sie in die richtige Richtung?

/ matt

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top