CGBitmapContext を使用してピクセルを操作しているときに、本当に奇妙なことに遭遇しました。基本的に、PNG のアルファ値を変更しています。ImageView のスーパービューが画像を含む ImageView である場合を除いて、PNG ピクセルのアルファ値を正常にゼロにできる例があります。これはなぜですか? それがバグなのか、コンテキスト内の何かが正しく設定されていないのかはわかりません。
以下は例です。アルファ値を正常にゼロにしますが、スーパービューの UIImageView に画像を指定すると、正しく機能しません。ゼロのアルファを持つ必要があるピクセルがわずかに表示されます。しかし、スーパービューの UIImageView から画像を削除すると、アルファ値が正しくゼロになり、その背後にある白い背景が完全に表示されます。いくつかの異なる CGBlendMode を使用してみましたが、うまくいかないようです。
この奇妙なイベントを示すサンプル プロジェクトへのドロップボックス リンクを次に示します: https://www.dropbox.com/s/e93hzxl5ru5wnss/TestAlpha.zip
そして、ここにコードのコピー/貼り付けがあります:
// Create a Background ImageView
UIImageView Background = new UIImageView(this.View.Bounds);
// Comment out the below line to see the Pixels alpha values change correctly, UnComment the below line to see the pixel's alpha values change incorrectly. Why is this?
// When Commented out, The pixels whose alpha value I set to zero become transparent and I can see the white background through the image.
// When Commented in, I SHOULD see the "GrayPattern.png" Image through the transparent pixels. I can kind of see it, but for some reason I still see the dirt pixels who's alpha is set to zero! Is this a bug? Is the CGBitmapContext set up incorrectly?
Background.Image = UIImage.FromFile("GrayPattern.png");
this.View.AddSubview(Background);
UIImageView MyImageView = new UIImageView(UIScreen.MainScreen.Bounds);
UIImage sourceImage = UIImage.FromFile("Dirt.png");
MyImageView.Image = sourceImage;
Background.AddSubview(MyImageView);
CGImage image = MyImageView.Image.CGImage;
Console.WriteLine(image.AlphaInfo);
int width = image.Width;
int height = image.Height;
CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB();
int bytesPerRow = image.BytesPerRow;
int bytesPerPixel = bytesPerRow / width;
int bitmapByteCount = bytesPerRow * height;
int bitsPerComponent = image.BitsPerComponent;
CGImageAlphaInfo alphaInfo = CGImageAlphaInfo.PremultipliedLast;
// Allocate memory because the BitmapData is unmanaged
IntPtr BitmapData = Marshal.AllocHGlobal(bitmapByteCount);
CGBitmapContext context = new CGBitmapContext(BitmapData, width, height, bitsPerComponent, bytesPerRow, colorSpace, alphaInfo);
context.SetBlendMode(CGBlendMode.Copy);
context.DrawImage(new RectangleF(0, 0, width, height), image);
UIImageView MyImageView_brush = new UIImageView(new RectangleF(0, 0, 100, 100));
UIImage sourceImage_brush = UIImage.FromFile("BB_Brush_Rect_MoreFeather.png");
MyImageView_brush.Image = sourceImage_brush;
MyImageView_brush.Hidden = true;
Background.AddSubview(MyImageView_brush);
CGImage image_brush = sourceImage_brush.CGImage;
Console.WriteLine(image_brush.AlphaInfo);
int width_brush = image_brush.Width;
int height_brush = image_brush.Height;
CGColorSpace colorSpace_brush = CGColorSpace.CreateDeviceRGB();
int bytesPerRow_brush = image_brush.BytesPerRow;
int bytesPerPixel_brush = bytesPerRow_brush / width_brush;
int bitmapByteCount_brush = bytesPerRow_brush * height_brush;
int bitsPerComponent_brush = image_brush.BitsPerComponent;
CGImageAlphaInfo alphaInfo_brush = CGImageAlphaInfo.PremultipliedLast;
// Allocate memory because the BitmapData is unmanaged
IntPtr BitmapData_brush = Marshal.AllocHGlobal(bitmapByteCount_brush);
CGBitmapContext context_brush = new CGBitmapContext(BitmapData_brush, width_brush, height_brush, bitsPerComponent_brush, bytesPerRow_brush, colorSpace_brush, alphaInfo_brush);
context_brush.SetBlendMode(CGBlendMode.Copy);
context_brush.DrawImage(new RectangleF(0, 0, width_brush, height_brush), image_brush);
for ( int x = 0; x < width_brush; x++ )
{
for ( int y = 0; y < height_brush; y++ )
{
int byteIndex_brush = (bytesPerRow_brush * y) + x * bytesPerPixel_brush;
byte alpha_brush = GetByte(byteIndex_brush+3, BitmapData_brush);
// Console.WriteLine("alpha_brush = " + alpha_brush);
byte setValue = (byte)(255 - alpha_brush);
// Console.WriteLine("setValue = " + setValue);
int byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
SetByte(byteIndex+3, BitmapData, setValue);
}
}
// Set the MyImageView Image equal to the context image, but I still see all top image, I dont' see any bottom image showing through.
MyImageView.Image = UIImage.FromImage (context.ToImage());
context.Dispose();
context_brush.Dispose();
// Free memory used by the BitmapData now that we're finished
Marshal.FreeHGlobal(BitmapData);
Marshal.FreeHGlobal(BitmapData_brush);
public unsafe byte GetByte(int offset, IntPtr buffer)
{
byte* bufferAsBytes = (byte*) buffer;
return bufferAsBytes[offset];
}
public unsafe void SetByte(int offset, IntPtr buffer, byte setValue)
{
byte* bufferAsBytes = (byte*) buffer;
bufferAsBytes[offset] = setValue;
}
更新 回避策を見つけました (ただし、そのパフォーマンスは私が行っていることに対してはかなり受け入れられません) 回避策は、上記のコードから結果の画像を取得し、それを NSData に変換してから、NSData を UIIMage に変換することです。 . 魔法のように画像は正しく見えます! この回避策; ただし、私の画像でこれを行うには文字通り 2 秒かかり、それは私のアプリには長すぎるため、私がやっていることには受け入れられません. 数百ミリ秒は楽しいでしょう. 、しかし、私には 2 秒は長すぎます.これがバグである場合は、解決されることを願っています!
回避策 (パフォーマンスの低下) のコードを次に示します。このコードを上記のコードの最後に追加するだけです (または、私の Dropbox プロジェクトをダウンロードし、このコードを最後に追加して実行します)。
NSData test = new NSData();
test = MyImageView.Image.AsPNG();
UIImage workAroundImage = UIImage.LoadFromData(test);
MyImageView.Image = workAroundImage;
前後の写真がこちら。Before は回避策なし、After は回避策あり。
グレーが完全に表示されないことに注意してください。アルファ値が完全にゼロになっていないかのように、明るい茶色で覆われています。
回避策を使用すると、灰色が完全かつ明確に表示され、その上に薄い茶色がまったく表示されないことに注意してください。私がした唯一のことは、画像をNSDataに変換してから、再びUIImageに戻すことでした! 非常に奇妙な...