سؤال

أريد استخدام صور شفافة جزئيا في عمليات السحب / الانخفاض. كل هذا ينشئ ويعمل بشكل جيد، ولكن التحول الفعلي للشفافية له تأثير جانبي غريب. لسبب ما، يبدو أن بكسل المخلوطة ضد خلفية سوداء.

تصف الصورة التالية المشكلة:

Transparency problem

الشكل أ) هو الصورة النقطية الأصلية.

الشكل ب) هو ما يتم إنتاجه بعد انتهاء مزج ألفا. من الواضح أن هذا أغمق أكثر بكثير من مرشح ألفا بنسبة 50٪ المقصود.

الشكل ج) هو التأثير المرغوب فيه، صورة أ) مع شفافية 50٪ (تضاف إلى التركيب مع برنامج رسم).

الرمز الذي أستخدمه لإنتاج صورة TrasParent هو ما يلي:

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;

هل يعرف أحد لماذا مزج ألفا لا يعمل؟

تحديث الأول:

بالنسبة للوضوح، يعمل الكود إذا كنت أبجد على رأس سطح مرسوم. المشكلة هي أنني أرغب في إنشاء صورة شبه نصفية تماما من صورة موجودة واستخدام هذا كمؤشر ديناميكي أثناء عمليات السحب / الانخفاض. حتى تخطي ما سبق ويحمل فقط مستطيل مليء اللون 88FFFFFF غلة لون رمادي داكن. شيء مريب يحدث مع الرمز.

التحديث الثاني:

نظرا لأنني قمت بخصم الكثير وأعتقد أن هذا لديه علاقة مع إنشاء المؤشر، فأنا ستشمل هذا الرمز أدناه أيضا. إذا قمت بتوفير عينة من الصورة النقطية مباشرة قبل مكالمة CreateIconInindirect، يبدو أن القيم الملونة الأربعة سليمة. وبالتالي، لدي شعور قد تكون الجناة هو HBMColor أو أعضاء HBMMASK في هيكل Iconinfo.

إليك هيكل 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. 

}

وهنا هو الرمز الذي يقوم بالفعل بإنشاء المؤشر:

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

يتم تعريف الوظائف الخارجيةتين على النحو التالي:

    [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);
هل كانت مفيدة؟

المحلول

GDI + لديه عدد من المشكلات المتعلقة بمزج ألفا عند القيام ب Interop مع GDI (و Win32). في هذه الحالة، سيقوم دعوة BMP.gethbitmap () بمخدم صورتك بخلفية سوداء. أ مقال عن الشفرة يعطي المزيد من التفاصيل حول المشكلة، وحل تم استخدامه لإضافة الصور إلى قائمة الصور.

يجب أن تكون قادرا على استخدام رمز مماثل للحصول على HBITMAP لاستخدامه للقناع:

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

نصائح أخرى

منذ فترة، قرأت هذه المشكلة تنشأ عن شرط قنوات ألفا مضروبة مسبقا في الصور النقطية. لست متأكدا مما إذا كانت هذه مشكلة مع مؤشرات Windows أو GDI، وحياتي، لا يمكنني العثور على وثائق فيما يتعلق بهذا. لذلك، في حين أن هذا التفسير قد يكون صحيحا أو لا يكون صحيحا، فإن التعليمات البرمجية التالية تفعل بالفعل ما تريد، باستخدام قناة ألفا مضروبة مسبقا في النقطات النقطية المؤشر.

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

تم العثور على تصريحات Pinvoke في الفصل الخارجي، والتي تظهر هنا:

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

بعض الملاحظات على الرمز:

  1. لاستخدام طريقة غير آمنة، غير مخالفة ()، يجب عليك تجميعها مع العلم / غير الآمن.
  2. أساليب النسخ النقطية قبيحة، خاصة الطريقة الآمنة، والتي تستخدم مكالمات marshal.readbyte () / marshal.writebyte (). يجب أن يكون هناك طريقة أسرع لنسخ بيانات الصورة النقطية أثناء إدراج بايت ألفا أيضا.
  3. أفترض أن الصورة النقطية المصدر قادرة على إغلاقها في 25 بت لكل تنسيق بكسل. هذا لا ينبغي أن يكون مشكلة، رغم ذلك.

حاول خفض قيمة الأزرق إلى .7 أو 0.6 ومعرفة ما إذا كان هذا أقرب إلى ما تريد.

إليك موقع جيد يفسر colormatrix.:

عندما أقوم بتشغيل التعليمات البرمجية الخاصة بك لتعديل صورة في صورة صورة مع صورة شبكة خلفية، أحصل على التأثير الذي تريده دون تغيير التعليمات البرمجية الخاصة بك. ربما يتم استخلص صورتك على الجزء العلوي من شيء له لون مظلم ...

سامحني إذا كان اقتراحي بسيط للغاية (ما زلت جديدا في C #) لكنني وجدت هذا على موقع MSDN وربما هذه قد تشير إليك في الاتجاه الصحيح؟

/غير لامع

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top