ImageProxy.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Print / Reach / AlphaFlattener / ImageProxy.cs / 1 / ImageProxy.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Printing
//  Copyright (c) Microsoft Corporation, 2004
//
//  File:       ImageProxy.cs 
//
//  History: 
//      [....]: 04/20/2004  Created 
//-----------------------------------------------------------------------------
 
using System;
using System.Collections;              // for ArrayList
using System.Collections.Generic;
using System.Diagnostics; 

using System.Windows;                  // for Rect                        WindowsBase.dll 
using System.Windows.Media;            // for Geometry, Brush, ImageSource. PresentationCore.dll 
using System.Windows.Media.Imaging;
using System.Windows.Controls;         // for Image 
using System.Windows.Shapes;           // for Glyphs

using System.Security;
using System.Security.Permissions; 
//using System.Drawing.Printing;
 
namespace Microsoft.Internal.AlphaFlattener 
{
    ///  
    /// Decode ImageSource into PARGB32 format, keep in managed memory to allow multiple blending,
    /// finally generate ImageSource when needed to interface with Avalon.
    /// Avalon ImageSource converts data to unmanaged memory.
    ///  
    internal class ImageProxy
    { 
        ///  
        /// Maximum ratio between pixel count of requested clip rectangle and actual image rectangle
        /// for clipping of image data to be performed. 
        /// 
        /// 
        /// The flattening process draws primitive intersection regions by blending brushes together,
        /// then clipping to that region. The problem is when blending image primitive with something: 
        /// the entire image is drawn regardless of the intersection region size. This can significantly
        /// increase spool file size. 
        /// 
        /// The solution is to detect such cases and clip image data down prior to blending and drawing
        /// the intersection. This ratio controls when this clipping occurs. 
        /// 
        private const double MaximumClipRatio = 0.9;

        ///  
        /// Minimum ratio between this image's size and brush size when blending before we magnify
        /// this image. Without magnification, the brush will lose detail due to being scaled down 
        /// to image's size. 
        /// 
        private const double MinimumBlendRatio = 0.5; 

        /// 
        /// Maximum size to use when magnify image if scale is less than MinimumBlendRatio, to avoid
        /// huge image 
        /// 
        private const int    MaximumOpacityMaskViewport = 1024; 
 
        protected int          _pixelWidth;
        protected int          _pixelHeight; 
        protected BitmapSource _image;

        /// 
        /// Critical: Data is from CriticalCopyPixels 
        /// 
        [SecurityCritical] 
        protected Byte[]       _pixels; 

        public ImageProxy(BitmapSource image) 
        {
            Debug.Assert(image != null);

            _pixelWidth  = image.PixelWidth; 
            _pixelHeight = image.PixelHeight;
            _image       = image; 
        //  _pixels      = null; 
        }
 
        public BitmapSource Image
        {
            get
            { 
                return _image;
            } 
        } 

        ///  
        ///     Critical: _pixels is SecurityCritical
        /// 
        public Byte[] Buffer
        { 
            [SecurityCritical]
            get 
            { 
                return _pixels;
            } 
        }

        public int PixelWidth
        { 
            get
            { 
                return _pixelWidth; 
            }
        } 

        public int PixelHeight
        {
            get 
            {
                return _pixelHeight; 
            } 
        }
 
        /// 
        /// Scales the image.
        /// 
        ///  
        /// 
        ///  
        /// Critical   : This code touches _pixels 
        /// TreatAsSafe: _pixels is reset to null, not leaked
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        public void Scale(double scaleX, double scaleY)
        {
            _image = new TransformedBitmap( 
                _image,
                new MatrixTransform(Matrix.CreateScaling(scaleX, scaleY)) 
                ); 

            _pixelWidth = _image.PixelWidth; 
            _pixelHeight = _image.PixelHeight;
            _pixels = null;
        }
 
        /// 
        /// Critical   : This code calls an internal PresentationCore function CriticalCopyPixels 
        /// TreatAsSafe: Pixel is copied to _pixels array, which is marked as SecurityCritical 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void Decode()
        {
            if (_pixels == null)
            { 
                _pixels = GetDecodedPixels(new Int32Rect(0, 0, _pixelWidth, _pixelHeight));
            } 
        } 

        ///  
        /// Critical   : This code calls an internal PresentationCore function CriticalCopyPixels, returns critical data
        /// 
        /// 
        /// Decodes a subimage, returning the decoded pixels. 
        /// 
        /// Bounds of subimage to decode 
        /// Returns critical pixels 
        [SecurityCritical]
        private byte[] GetDecodedPixels(Int32Rect bounds) 
        {
            Debug.Assert(
                (bounds.X >= 0) &&
                (bounds.Y >= 0) && 
                ((bounds.X + bounds.Width) <= _pixelWidth) &&
                ((bounds.Y + bounds.Height) <= _pixelHeight) 
                ); 

            int stride = bounds.Width * 4; 

            byte[] pixels = new Byte[stride * bounds.Height];

            FormatConvertedBitmap converter = new FormatConvertedBitmap(); 
            converter.BeginInit();
            converter.Source = _image; 
            converter.DestinationFormat = PixelFormats.Pbgra32; 
            converter.EndInit();
 
            converter.CriticalCopyPixels(bounds, pixels, stride, 0);

            return pixels;
        } 

        ///  
        /// Critical   : It touches critical data in _pixels 
        /// TreatAsSafe: _pixels is just modified in place, not leaked
        ///  
        /// 
        /// 
        /// Image destination rectangle
        /// Transformation from image to final destination 
        [SecurityCritical, SecurityTreatAsSafe]
        public void PushOpacity(double opacity, BrushProxy opacityMask, Rect rect, Matrix trans) 
        { 
            if (opacityMask != null)
            { 
                rect.Transform(trans);

                //
                // Blend this image on top of opacity mask. 
                //
 
                // Calculate scaling factor from opacity mask to this image. 
                TileBrush opacityBrush = opacityMask.Brush as TileBrush;
                Rect viewport; 

                if (opacityBrush != null)
                {
                    Debug.Assert(opacityBrush.ViewportUnits == BrushMappingMode.Absolute, "TileBrush must have absolute viewport by this point"); 

                    viewport = opacityBrush.Viewport; 
                } 
                else
                { 
                    // viewport covers entire image
                    viewport = rect;
                }
 
                // Fix for 1689025:
 
                double scaleX = _pixelWidth  / rect.Width; 
                double scaleY = _pixelHeight / rect.Height;
 
                // If current image is too small, magnify it to match opacity mask's size,
                // otherwise we lose the detail in opacity mask.
                if ((scaleX < MinimumBlendRatio || scaleY < MinimumBlendRatio) &&
                    (rect.Width  <= MaximumOpacityMaskViewport) && 
                    (rect.Height <= MaximumOpacityMaskViewport)) // Avoiding generate huge bitmap
                { 
                    Scale(rect.Width  / _pixelWidth, 
                          rect.Height / _pixelHeight);
                    scaleX = 1.0; 
                    scaleY = 1.0;
                }

                // Transform brush to image space. 
                Matrix transform = new Matrix();
                transform.Translate(-rect.Left, -rect.Top); 
                transform.Scale(scaleX, scaleY); 

                // Blend opacity mask into image. 
                BlendUnderBrush(false, opacityMask, transform);
            }

            int op = Utility.OpacityToByte(opacity); 

            if (op <= 0) 
            { 
                _image  = null;
                _pixels = null; 
                return;
            }
            else if (op >= 255)
            { 
                return;
            } 
 
            Decode();
 
            Byte[] map = new Byte[256];

            for (int i = 0; i < 256; i ++)
            { 
                map[i] = (Byte)(i * op / 255);
            } 
 
            int count = _pixelWidth * _pixelHeight * 4;
 
            for (int i = 0; i < count; i++)
            {
                _pixels[i] = map[_pixels[i]];
            } 
        }
 
        ///  
        /// Critical   : It touches critical data in _pixels
        /// TreatAsSafe: _pixels is just modified in place, not leaked 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        public void BlendUnderColor(Color color, double opacity, bool opacityOnly)
        { 
            Decode();
            Utility.BlendUnderColor(_pixels, _pixelWidth * _pixelHeight, color, opacity, opacityOnly); 
        } 

        ///  
        /// Critical   : It touches critical data in _pixels
        /// TreatAsSafe: _pixels is just modified in place, not leaked
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public void BlendOverColor(Color color, double opacity, bool opacityOnly)
        { 
            if (opacityOnly || !Utility.IsOpaque(opacity) || !IsOpaque()) 
            {
                // Always blend if image is opacity mask, so that a proper opacity mask image 
                // is formed, otherwise the original image pixels will be used.
                Decode();
                Utility.BlendOverColor(_pixels, _pixelWidth * _pixelHeight, color, opacity, opacityOnly);
            } 
        }
 
        ///  
        /// Render a brush on top of current image
        ///  
        /// 
        /// 
        /// 
        ///  
        ///     Critical: It calls into SecurityCritical function RasterizeBrush
        ///     TreatAsSafe: Output will be in _pixels, which is marked as SecurityCritical 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        public void BlendUnderBrush(bool opacityOnly, BrushProxy brush, Matrix trans) 
        {
            if (brush.Brush is SolidColorBrush)
            {
                SolidColorBrush sb = brush.Brush as SolidColorBrush; 

                BlendUnderColor(Utility.Scale(sb.Color, brush.Opacity), 1, opacityOnly); 
            } 
            else
            { 
                Byte[] brushPixels = RasterizeBrush(brush, trans);

                Decode();
 
                Utility.BlendPixels(_pixels, opacityOnly, brushPixels, brush.OpacityOnly, _pixelWidth * _pixelHeight, _pixels);
            } 
        } 

        ///  
        /// Rasterize a brush into a bitmap
        /// 
        /// Brush to rasterize
        ///  
        /// Pbgra32 pixel byte array
        ///  
        /// Critical: CreateBrushImage is SecurityCritical because it calls internal PresentationCore function CriticalCopyPixels 
        /// 
        [SecurityCritical] 
        private Byte[] RasterizeBrush(BrushProxy brush, Matrix trans)
        {
            return brush.CreateBrushImage(trans, _pixelWidth, _pixelHeight);
        } 

        ///  
        /// Render a brush under current image 
        /// 
        ///  
        /// 
        /// 
        /// 
        ///     Critical: It calls into SecurityCritical function RasterizeBrush 
        ///     TreatAsSafe: Output will be in _pixels, which is marked as SecurityCritical
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        public void BlendOverBrush(bool opacityOnly, BrushProxy brush, Matrix trans)
        { 
            if (IsOpaque())
            {
                Debug.Assert(!opacityOnly, "Opaque image OpacityMask should not be blended with brush");
                return; 
            }
 
            if (brush.Brush is SolidColorBrush) 
            {
                SolidColorBrush sb = brush.Brush as SolidColorBrush; 

                BlendOverColor(Utility.Scale(sb.Color, brush.Opacity), 1.0, opacityOnly);
            }
            else 
            {
                Byte[] brushPixels = RasterizeBrush(brush, trans); 
 
                Decode();
 
                Utility.BlendPixels(brushPixels, brush.OpacityOnly, _pixels, opacityOnly, _pixelWidth * _pixelHeight, _pixels);
            }
        }
 
        internal static int HasAlpha(BitmapSource bitmap)
        { 
            if (bitmap.Format.HasAlpha) 
            {
                return 1; 
            }

            if (bitmap.Format.Palettized)
            { 
                BitmapPalette palette = bitmap.Palette;
 
                if (palette != null) 
                {
                    IList palColor = palette.Colors; 

                    if (palColor != null)
                    {
                        foreach (Color c in palColor) 
                        {
                            if (! Utility.IsOpaque(c.ScA)) 
                            { 
                                return 2;
                            } 
                        }
                    }
                }
 
            }
 
            return 0; 
        }
 
        /// 
        /// Critical   : It touches critical data in _pixels
        /// TreatAsSafe: _pixels is read to return a Boolean value
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        public bool IsOpaque() 
        { 
            if (_image == null)
            { 
                return false;
            }

            if (_pixels == null) // Not decoded yet 
            {
                int hasAlpha = HasAlpha(_image); 
 
                if (hasAlpha == 2)
                { 
                    return false;
                }

                if (hasAlpha == 0) 
                {
                    return true; 
                } 
            }
 
            Decode();

            int count = _pixelWidth * _pixelHeight;
 
            for (int i = 0; i < count; i++)
            { 
                if (_pixels[i * 4 + 3] != 255) 
                {
                    return false; 
                }
            }

            return true; 
        }
 
        ///  
        /// Check if an image is totally transparent
        ///  
        /// 
        /// 
        /// Critical   : It touches critical data in _pixels
        /// TreatAsSafe: _pixels is exam to return a Boolean flag 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public bool IsTransparent() 
        {
            if (_image == null) 
            {
                return true;
            }
 
            Decode();
 
            int count = _pixelWidth * _pixelHeight * 4; 

            // _pixels is in PBGRA format, check all channels 

            for (int i = 0; i < count; i++)
            {
                if (_pixels[i] != 0) 
                {
                    return false; 
                } 
            }
 
            return true;
        }

        ///  
        /// Critical   : Critical information in _pixels is exposed
        /// TreatAsSafe: Image is wrapped in a BitmapSource, which needs CopyPixels or CriticalCopyPixels to get out 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        public BitmapSource GetImage() 
        {
            if (_pixels == null)
            {
                return _image; 
            }
            else if (_image != null) 
            { 
                return BitmapSource.Create(_pixelWidth, _pixelHeight, _image.DpiX, _image.DpiY, PixelFormats.Pbgra32, null, _pixels, _pixelWidth * 4);
            } 
            else
            {
                return null;
            } 
        }
 
        ///  
        /// Critical   : Critical information in _pixels is exposed
        /// TreatAsSafe: Image is wrapped in a BitmapSource, which needs CopyPixels or CriticalCopyPixels to get out 
        /// 
        /// 
        /// Creates a BitmapSource that has image clipped to the specified bounds.
        ///  
        /// Desired clipping bounds in image DPI
        /// Receives actual bounds to which image was clipped 
        ///  
        /// clipBounds may be one of following:
        /// - Empty: Entire image was clipped. 
        /// - Equal to original image size: No image clipping performed.
        /// - Other: Some clipping performed.
        ///
        /// Clipping is not always performed; see MaximumClipRatio. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public BitmapSource GetClippedImage(Rect bounds, out Rect clipBounds) 
        {
            BitmapSource result = null; // default to entire image clipped away 
            clipBounds = Rect.Empty;

            // scale bounds according to image DPI
            double dpiScaleX = _image.DpiX / 96.0; 
            double dpiScaleY = _image.DpiY / 96.0;
 
            if (Utility.IsZero(dpiScaleX)) 
                dpiScaleX = 1;
            if (Utility.IsZero(dpiScaleY)) 
                dpiScaleY = 1;

            bounds.Scale(dpiScaleX, dpiScaleY);
            bounds.Intersect(new Rect(0, 0, _pixelWidth, _pixelHeight)); 

            double currentPixelCount = _pixelWidth * _pixelHeight; 
            double clipPixelCount = bounds.Width * bounds.Height; 

            if (currentPixelCount > 0) 
            {
                if ((clipPixelCount / currentPixelCount) > MaximumClipRatio)
                {
                    // Desired clip bounds not small enough to necessitate clipping image data. 
                    result = GetImage();
                    clipBounds = new Rect(0, 0, _pixelWidth, _pixelHeight); 
                } 
                else
                { 
                    //
                    // Clipped rectangle significantly smaller than image size. Manually
                    // clip image down to bounds.
                    // 
                    // Fix bug 1494512: Round so that we try to get at least a pixel, otherwise
                    // bounds < 1 pixel (which'll display a solid color) may get clipped away. 
                    // 
                    int x0 = (int)Math.Max(Math.Floor(bounds.Left), 0);
                    int y0 = (int)Math.Max(Math.Floor(bounds.Top), 0); 
                    int x1 = (int)Math.Ceiling(bounds.Right);
                    int y1 = (int)Math.Ceiling(bounds.Bottom);

                    int width = x1 - x0; 
                    int height = y1 - y0;
 
                    if (width > 0 && height > 0) 
                    {
                        byte[] pixels; 

                        if (_pixels == null)
                        {
                            // not decoded yet, we perform clipping while decoding 
                            pixels = GetDecodedPixels(new Int32Rect(x0, y0, width, height));
                        } 
                        else 
                        {
                            // clip previously decoded pixels 
                            pixels = Utility.ClipPixels(_pixels, _pixelWidth, _pixelHeight, x0, y0, width, height);
                        }

                        result = BitmapSource.Create( 
                            width, height,
                            _image.DpiX, _image.DpiY, 
                            PixelFormats.Pbgra32, 
                            null,
                            pixels, 
                            width * 4
                            );

                        clipBounds = bounds; 
                    }
                } 
            } 

            // unscale according to image DPI 
            if (!clipBounds.IsEmpty)
            {
                clipBounds.Scale(1.0 / dpiScaleX, 1.0 / dpiScaleY);
            } 

            return result; 
        } 

        public ImageProxy Clone() 
        {
            return new ImageProxy(GetImage());
        }
    } // end of ImageProxy class 

} // end of namespace 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK