locked
Overlay image with png 24 RRS feed

  • Question

  • I'm trying to create an overlay over a jpg image with a semi transparent png-24 but the output isn't what I want ;)
    The out.png sure is semi transparent but the "base image" is completely overwritten so i guess that WritePixels isn't the correct method to use, can someone please point me in the right direction.

                const string IMAGE_BASE_PATH = "c:\\in.jpg";
                const string IMAGE_OVERLAY_PATH = "c:\\overlay.png";
                const string IMAGE_OUTPUT_PATH = "c:\\out.png";
    
                BitmapImage baseImage;
                BitmapImage overlayImage;
    
                // read base image from disk
                using (FileStream fs = new FileStream(IMAGE_BASE_PATH, FileMode.Open))
                {
                    baseImage = new BitmapImage();
                    baseImage.BeginInit();
                    baseImage.CacheOption = BitmapCacheOption.OnLoad;
                    baseImage.StreamSource = fs;
                    baseImage.EndInit();
                }
    
                // read overlay image from disk
                using (FileStream fs = new FileStream(IMAGE_OVERLAY_PATH, FileMode.Open))
                {
                    overlayImage = new BitmapImage();
                    overlayImage.BeginInit();
                    overlayImage.CacheOption = BitmapCacheOption.OnLoad;
                    overlayImage.StreamSource = fs;
                    overlayImage.EndInit();
                }
    
                // setup variables for base image
                int widthBase = baseImage.PixelWidth;
                int heightBase = baseImage.PixelHeight;
                int bytesPerPixelBase = baseImage.Format.BitsPerPixel;
                int strideBase = widthBase * bytesPerPixelBase;
                byte[] bytesBase = new byte[heightBase * widthBase * bytesPerPixelBase];
    
                // setup variables for overlay image
                int widthOverlay = overlayImage.PixelWidth;
                int heightOverlay = overlayImage.PixelHeight;
                int bytesPerPixelOverlay = overlayImage.Format.BitsPerPixel;
                int strideOverlay = widthOverlay * bytesPerPixelOverlay;
                byte[] bytesOverlay = new byte[heightOverlay * widthOverlay * bytesPerPixelOverlay];
    
                // bope copy pixels from base and overlay image
                baseImage.CopyPixels(bytesBase, strideBase, 0);
                overlayImage.CopyPixels(bytesOverlay, strideOverlay, 0);
    
                // create new writable bitmap with the same
                // properties as the overlay image
                WriteableBitmap wb = new WriteableBitmap(widthBase, heightBase, 72, 72, System.Windows.Media.PixelFormats.Bgra32, null);
                
                // write base image
                wb.WritePixels(new Int32Rect(0, 0, widthBase, heightBase), bytesBase, strideBase, 0);
    
                // write overlay image
                wb.WritePixels(new Int32Rect(0, 0, widthOverlay, heightOverlay), bytesOverlay, strideOverlay, 0);
    
                // get encoder from output filename
                BitmapEncoder encoder = GetBitmapEncoder(GetMimeType(IMAGE_OUTPUT_PATH), 100);
                
                // create frame from the writable bitmap and add to encoder
                encoder.Frames.Add(BitmapFrame.Create(wb));
    
                // write the new file back to disk
                using (FileStream fs = new FileStream(IMAGE_OUTPUT_PATH, FileMode.Create))
                {
                    encoder.Save(fs);
                }
    Thursday, September 10, 2009 7:12 PM

Answers

  • Your WriteableBitmap code doesn't work because you aren't blending the two together. You're overwriting every pixel with the overlay image so your result is, not surprisingly, the overlay image. You'd need to loop through each pixel of both and do proper alpha blending.

    Your second attempt is probably the easiest and fastest way to do this. You could RenderTargetBitmap with a pixel shader to do the blend, but all you want is a standard blend so why not let us do it for you :)
    • Proposed as answer by zer0mus Saturday, September 12, 2009 12:27 AM
    • Marked as answer by Hua Chen Thursday, September 17, 2009 9:25 AM
    Saturday, September 12, 2009 12:26 AM

All replies

  • Have you thought of using Pixel Shaders for this?
    They were born for this kind of work.
    Bigsby, Lisboa, Portugal - O que for, quando for, é que será o que é... http://bigsby.eu
    Thursday, September 10, 2009 8:09 PM
  • No i haven't... ;) Below is a working, but probably not the best, solution... Now, dig into Pixel Shaders!

                BitmapImage baseBitmap = new BitmapImage(new Uri(IMAGE_BASE_PATH));
    
                Image baseImage = new Image() { Source = baseBitmap };
                Image overlayImage = new Image() { Source = new BitmapImage(new Uri(IMAGE_OVERLAY_PATH)) };
    
                Grid grid = new Grid();
                grid.Children.Add(baseImage);
                grid.Children.Add(overlayImage);
    
                grid.Arrange(new Rect(0, 0, baseBitmap.Width, baseBitmap.Height));
    
                RenderTargetBitmap renderer = new RenderTargetBitmap(baseBitmap.PixelWidth,
                    baseBitmap.PixelHeight,
                    baseBitmap.DpiX,
                    baseBitmap.DpiY,
                    PixelFormats.Pbgra32);
    
                renderer.Render(grid);
    
                BitmapEncoder encoder = GetBitmapEncoder(GetMimeType(IMAGE_OUTPUT_PATH), 70);
                encoder.Frames.Add(BitmapFrame.Create(renderer));
    
                using (FileStream fs = new FileStream(IMAGE_OUTPUT_PATH, FileMode.Create))
                {
                    encoder.Save(fs);
                }

    • Edited by olovnilzen Thursday, September 10, 2009 9:23 PM Typo
    Thursday, September 10, 2009 9:23 PM
  • Your WriteableBitmap code doesn't work because you aren't blending the two together. You're overwriting every pixel with the overlay image so your result is, not surprisingly, the overlay image. You'd need to loop through each pixel of both and do proper alpha blending.

    Your second attempt is probably the easiest and fastest way to do this. You could RenderTargetBitmap with a pixel shader to do the blend, but all you want is a standard blend so why not let us do it for you :)
    • Proposed as answer by zer0mus Saturday, September 12, 2009 12:27 AM
    • Marked as answer by Hua Chen Thursday, September 17, 2009 9:25 AM
    Saturday, September 12, 2009 12:26 AM