Set alpha of some pixels to zero

2019-08-06 00:13发布

问题:

Let's say I have 2 images, one on top of the other. I want to set a portion of the top image's pixel's alpha values to zero so I get this effect illustrated below:

The brown is the top image, and the blue water is the bottom image. Ultimately what I'm going to do is have the alpha channel of the top image's pixels change to zero as the user touches the iPad screen so they can essentially draw with their fingertip and have the blue water image appear.

I have a function that can examine the image counting it's pixels whose alpha is equal to zero. I also have a function that Sets the alpha of a pixel to zero but I think it doesn't work. I can read the alpha value, and set the alpha value. Then I can re-read the alpha value to confirm that it was set. But when I set the new image I can't see a difference!

Here is a dropbox link to my example project:

https://www.dropbox.com/s/e93hzxl5ru5wnss/TestAlpha.zip

And below is the source:

        Water = new UIImageView(this.Bounds);
        Water_OriginalImage = UIImage.FromFile("Media/Images/Backgrounds/WaterTexture.png");
        Water.Image = Water_OriginalImage;
        this.AddSubview(Water);

        Dirt = new UIImageView(this.Bounds);
        Dirt_ModifiableImage = UIImage.FromFile("Media/Images/Backgrounds/DirtBackground.png");
        Dirt.Image = null;
        this.AddSubview(Dirt);


        CGImage image = Dirt_ModifiableImage.CGImage;
        int width = image.Width;
        int height = image.Height;
        CGColorSpace colorSpace = image.ColorSpace;
        int bytesPerRow = image.BytesPerRow;
        int bitmapByteCount = bytesPerRow * height;
        int bitsPerComponent = image.BitsPerComponent;
        CGImageAlphaInfo alphaInfo = image.AlphaInfo;

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


        // Console output from this function call says "alpha count = 0"
        CountZeroedAlphas(BitmapData, Dirt_ModifiableImage);

        RemoveSomeAlpha(BitmapData, Dirt_ModifiableImage);

        // Console output from this function call says "alpha count = 2000".  So it seems that I have set the alpha of 2000 pixels to zero...
        CountZeroedAlphas(BitmapData, Dirt_ModifiableImage);


        // Set the Dirt Image equal to the context image, but I still see all top image, I dont' see any bottom image showing through.  
        Dirt.Image = UIImage.FromImage (context.ToImage());


        // Free memory used by the BitmapData now that we're finished
        Marshal.FreeHGlobal(BitmapData);




    public void CountZeroedAlphas( IntPtr bitmapData, UIImage Image )
    {
        int widthIndex = 0;
        int heightIndex = 0;
        int count = 0;

        while ( widthIndex < Image.Size.Width )
        {
            while ( heightIndex < Image.Size.Height )
            {
                PointF point = new PointF(widthIndex, heightIndex);
                var startByte = (int) ((point.Y * Image.Size.Width + point.X) * 4);
                byte alpha = GetByte(startByte, bitmapData);

                if ( alpha == 0 )
                    count++;

                heightIndex++;
            }


            widthIndex++;
        }

        Console.WriteLine("alpha count = " + count);

    }


    public void RemoveSomeAlpha( IntPtr bitmapData, UIImage Image )
    {
        int widthIndex = 0;
        int heightIndex = 0;

        while ( widthIndex < Image.Size.Width )
        {
            while ( heightIndex < Image.Size.Height )
            {
                PointF point = new PointF(widthIndex, heightIndex);
                var startByte = (int) ((point.Y * Image.Size.Width + point.X) * 4);
                ZeroByte(startByte, bitmapData);

                heightIndex++;
            }

            widthIndex++;
        }

    }


    public unsafe byte GetByte(int offset, IntPtr buffer)
    {
        byte* bufferAsBytes = (byte*) buffer;
        return bufferAsBytes[offset];
    }


    public unsafe void ZeroByte(int offset, IntPtr buffer)
    {
        byte* bufferAsBytes = (byte*) buffer;
        bufferAsBytes[offset] = 0;
    }

回答1:

Got it working! I'm not sure what the problem was exactly (assuming I was accessing the wrong bytes on accident or something), but I have the working code.

This was extremely useful:

        // Create layers of matter on the battlefield.  Example:  Grass, dirt, water
        Water = new UIImageView(UIScreen.MainScreen.Bounds);
        Water_OriginalImage = UIImage.FromFile("WaterTexture.png");
        Water.Image = Water_OriginalImage;
        this.View.AddSubview(Water);

        Dirt = new UIImageView(UIScreen.MainScreen.Bounds);
        Dirt_ModifiableImage = UIImage.FromFile("DirtBackground.png");
        Dirt.Image = null;
        this.View.AddSubview(Dirt);



        CGImage image = Dirt_ModifiableImage.CGImage;
        int width = image.Width;
        int height = image.Height;
        CGColorSpace colorSpace = image.ColorSpace;
        int bytesPerRow = image.BytesPerRow;
        // int bytesPerPixel = 4;
        int bytesPerPixel = bytesPerRow / width;
        int bitmapByteCount = bytesPerRow * height;
        int bitsPerComponent = image.BitsPerComponent;
        CGImageAlphaInfo alphaInfo = image.AlphaInfo;

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

        int tempX = 0;

        while ( tempX < Dirt_ModifiableImage.Size.Width )
        {
            int tempY = 0;
            while ( tempY < Dirt_ModifiableImage.Size.Height )
            {
                int byteIndex = (bytesPerRow * tempY) + tempX * bytesPerPixel;


                ZeroByte(byteIndex+3, BitmapData);

                byte red = GetByte(byteIndex, BitmapData);
                byte green = GetByte(byteIndex+1, BitmapData);
                byte blue = GetByte(byteIndex+2, BitmapData);
                byte alpha = GetByte(byteIndex+3, BitmapData);


                //Console.WriteLine("red = " + red + " green = " + green + " blue = " + blue + " alpha = " + alpha);

                tempY++;
            }
            tempX++;
        }


            NSData newImageData = NSData.FromBytes(BitmapData, (uint)(bitmapByteCount));
            Dirt.Image = UIImage.LoadFromData(newImageData);