Question

Je veux utiliser des images partiellement transparentes dans les opérations de glisser / déposer. Tout cela est mis en place et fonctionne très bien, mais la transformation réelle de la transparence a un effet secondaire bizarre. Pour une raison quelconque, les pixels semblent être mélangés sur un fond noir.

L'image suivante décrit le problème:

problème de transparence

Figure a) est l'image d'origine.

Figure b) est ce qui est produit après la fusion alpha a été effectuée. Évidemment, cela est beaucoup plus sombre que le filtre alpha destiné 50% prévu.

Figure c) est l'effet recherché, l'image a) avec 50% de transparence (ajouté à la composition avec un programme de dessin).

Le code j'utilise pour produire l'image de trasparent est le suivant:

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;

Quelqu'un sait-il pourquoi le mélange alpha ne fonctionne pas?

Mise à jour I:

Pour plus de clarté, le code ne fonctionne si je suis alphablending au-dessus d'une surface tirée. Le problème est que je veux créer une image complètement semi-transparente d'une image existante et l'utiliser comme un curseur dynamique lors des opérations de glisser / déposer de. Même sauter ce qui précède et la peinture seulement un rectangle rempli de couleur 88ffffff donne une couleur gris foncé. Quelque chose de louche qui se passe avec l'icône.

Mise à jour II:

Depuis que je l'ai reseached beaucoup et je crois que cela a quelque chose à voir avec la création du curseur, je vais inclure ce code ci-dessous aussi. Si je GetPixel échantillon le bitmap juste avant l'appel CreateIconIndirect, les quatre valeurs de couleur semblent être intactes. Ainsi, je sens les coupables pourraient être le hbmColor ou les membres hbmMask de la structure ICONINFO.

Voici la structure ICONINFO:

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. 

}

Et voici le code qui crée en fait le curseur:

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

Les deux fonctions externes sont définies comme suit:

    [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);
Était-ce utile?

La solution

+ GDI a un certain nombre de problèmes liés à la fusion alpha lorsque faisant l'interopérabilité avec GDI (et Win32). Dans ce cas, l'appel à bmp.GetHbitmap () mélangera votre image avec un fond noir. article sur CodeProject donne plus de détails sur le problème, et une solution qui a été utilisée pour ajouter des images à une liste d'images.

Vous devriez être en mesure d'utiliser un code similaire pour obtenir le HBITMAP à utiliser pour le masque:

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

Autres conseils

Il y a quelque temps, je lis ce problème découle d'une exigence pour les canaux alpha prémultipliés dans les bitmaps. Je ne sais pas si cela avait un problème avec les curseurs Windows ou GDI, et pour la vie de moi, je ne peux pas trouver de la documentation à ce sujet. Ainsi, alors que cette explication peut ou peut ne pas être correct, le code suivant ne fait en effet ce que vous voulez, en utilisant un canal alpha prémultipliés dans le bitmap du curseur.

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

Les déclarations de PInvoke se trouvent dans la classe externe, ici:

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

Quelques notes sur le code:

  1. Pour utiliser la méthode dangereuse, UnsafeCopy (), vous devez compiler avec l'indicateur / risque.
  2. Les méthodes de copie bitmap sont laids, en particulier la méthode sûre qui utilise des appels Marshal.ReadByte () / Marshal.WriteByte (). Il doit y avoir un moyen plus rapide pour copier des données bitmap tout en insérant octets alpha.
  3. Je suppose que l'image bitmap source est apte à être verrouillé dans une 24 bits par format de pixel. Cela ne devrait pas être un problème, cependant.

essayez de réduire la valeur de bleu à 0,7 ou 0,6 et voir si cela est plus proche de ce que vous voulez.

Voici un bon site qui explique ColorMatrix :

Quand je lance votre code pour modifier une image dans un picturebox avec une image de grille de fond, je reçois l'effet que vous désiriez sans changer votre code. Peut-être que votre image est en cours d'élaboration sur le dessus de quelque chose qui a une couleur foncée ...

Pardonnez-moi si ma suggestion est trop simpliste (je suis encore nouveau pour C #) mais je trouve cela sur le site MSDN et peut-être cette pourrait vous orienter dans la bonne direction?

/ mat

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top