예제 #1
0
        /// <summary>
        /// 拷贝该非托管图片
        /// </summary>
        /// <param name="destImage">将非托管图片拷贝至的目标图片位置</param>
        /// <remarks><para>The method copies current unmanaged image to the specified image.
        /// Size and pixel format of the destination image must be exactly the same.</para></remarks>
        /// <exception cref="InvalidImagePropertiesException">Destination image has different size or pixel format.</exception>
        public void Copy(UnmanagedImage destImage)
        {
            if (
                (width != destImage.width) || (height != destImage.height) ||
                (pixelFormat != destImage.pixelFormat))
            {
                throw new InvalidImagePropertiesException("Destination image has different size or pixel format.");
            }

            if (stride == destImage.stride)
            {
                // copy entire image
                UnmanagedMemoryHelper.CopyUnmanagedMemory(destImage.imageData, imageData, stride * height);
            }
            else
            {
                unsafe
                {
                    int dstStride  = destImage.stride;
                    int copyLength = (stride < dstStride) ? stride : dstStride;

                    byte *src = (byte *)imageData.ToPointer();
                    byte *dst = (byte *)destImage.imageData.ToPointer();

                    // copy line by line
                    for (int i = 0; i < height; i++)
                    {
                        UnmanagedMemoryHelper.CopyUnmanagedMemory(dst, src, copyLength);

                        dst += dstStride;
                        src += stride;
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// 从指定的托管内存图片转换成非托管内存图片
        /// </summary>
        /// <param name="imageData">锁定的托管内存图片</param>
        /// <returns>Returns new unmanaged image, which is a copy of source managed image.</returns>
        /// <remarks><para>The method creates an exact copy of specified managed image, but allocated
        /// in unmanaged memory. This means that managed image may be unlocked right after call to this
        /// method.</para></remarks>
        /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format of source image.</exception>
        public static UnmanagedImage FromManagedImage(BitmapData imageData)
        {
            PixelFormat pixelFormat = imageData.PixelFormat;

            // check source pixel format
            if (
                (pixelFormat != PixelFormat.Format8bppIndexed) &&
                (pixelFormat != PixelFormat.Format16bppGrayScale) &&
                (pixelFormat != PixelFormat.Format24bppRgb) &&
                (pixelFormat != PixelFormat.Format32bppRgb) &&
                (pixelFormat != PixelFormat.Format32bppArgb) &&
                (pixelFormat != PixelFormat.Format32bppPArgb) &&
                (pixelFormat != PixelFormat.Format48bppRgb) &&
                (pixelFormat != PixelFormat.Format64bppArgb) &&
                (pixelFormat != PixelFormat.Format64bppPArgb))
            {
                throw new UnsupportedImageFormatException("Unsupported pixel format of the source image.");
            }

            // allocate memory for the image
            IntPtr dstImageData = System.Runtime.InteropServices.Marshal.AllocHGlobal(imageData.Stride * imageData.Height);

            UnmanagedImage image = new UnmanagedImage(dstImageData, imageData.Width, imageData.Height, imageData.Stride, pixelFormat);

            UnmanagedMemoryHelper.CopyUnmanagedMemory(dstImageData, imageData.Scan0, imageData.Stride * imageData.Height);
            image.mustBeDisposed = true;

            return(image);
        }
예제 #3
0
        /// <summary>
        /// Apply filter to an unmanaged image or its part.
        /// </summary>
        ///
        /// <param name="image">Unmanaged image to apply filter to.</param>
        /// <param name="rect">Image rectangle for processing by the filter.</param>
        ///
        /// <remarks>The method applies the filter directly to the provided source image.</remarks>
        ///
        /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format of the source image.</exception>
        ///
        public void ApplyInPlace(UnmanagedImage image, Rectangle rect)
        {
            // check pixel format of the source image
            CheckSourceFormat(image.PixelFormat);

            // validate rectangle
            rect.Intersect(new Rectangle(0, 0, image.Width, image.Height));

            // process the filter if rectangle is not empty
            if ((rect.Width | rect.Height) != 0)
            {
                // create a copy of the source image
                int size = image.Stride * image.Height;

                IntPtr imageCopy = MemoryManager.Alloc(size);
                UnmanagedMemoryHelper.CopyUnmanagedMemory(imageCopy, image.ImageData, size);

                // process the filter
                ProcessFilter(
                    new UnmanagedImage(imageCopy, image.Width, image.Height, image.Stride, image.PixelFormat),
                    image, rect);

                MemoryManager.Free(imageCopy);
            }
        }
예제 #4
0
        /// <summary>
        /// 将非托管图片转换成托管图片
        /// </summary>
        /// <param name="makeCopy">是否拷贝一份新的非托管内存</param>
        /// <returns>Returns managed copy of the unmanaged image.</returns>
        /// <remarks><para>If the <paramref name="makeCopy"/> is set to <see langword="true"/>, then the method
        /// creates a managed copy of the unmanaged image, so the managed image stays valid even when the unmanaged
        /// image gets disposed. However, setting this parameter to <see langword="false"/> creates a managed image which is
        /// just a wrapper around the unmanaged image. So if unmanaged image is disposed, the
        /// managed image becomes no longer valid and accessing it will generate an exception.</para></remarks>
        public Bitmap ToManagedImage(bool makeCopy)
        {
            Bitmap dstImage = null;

            if (!makeCopy)
            {
                dstImage = new Bitmap(width, height, stride, pixelFormat, imageData);
                if (pixelFormat == PixelFormat.Format8bppIndexed)
                {
                    Gimela.Media.Imaging.ImageHelper.SetGrayscalePalette(dstImage);
                }
            }
            else
            {
                // create new image of required format
                dstImage = (pixelFormat == PixelFormat.Format8bppIndexed) ?
                           Gimela.Media.Imaging.ImageHelper.CreateGrayscaleImage(width, height) :
                           new Bitmap(width, height, pixelFormat);

                // lock destination bitmap data
                BitmapData dstData = dstImage.LockBits(
                    new Rectangle(0, 0, width, height),
                    ImageLockMode.ReadWrite, pixelFormat);

                int dstStride = dstData.Stride;
                int lineSize  = Math.Min(stride, dstStride);

                unsafe
                {
                    byte *dst = (byte *)dstData.Scan0.ToPointer();
                    byte *src = (byte *)imageData.ToPointer();

                    if (stride != dstStride)
                    {
                        // copy image
                        for (int y = 0; y < height; y++)
                        {
                            UnmanagedMemoryHelper.CopyUnmanagedMemory(dst, src, lineSize);
                            dst += dstStride;
                            src += stride;
                        }
                    }
                    else
                    {
                        UnmanagedMemoryHelper.CopyUnmanagedMemory(dst, src, stride * height);
                    }
                }

                // unlock destination images
                dstImage.UnlockBits(dstData);
            }

            return(dstImage);
        }
예제 #5
0
        private void InitFromRawPointer(IntPtr rawObjectPtr, bool addRef)
        {
            if (!UnmanagedMemoryHelper.ValidateComObject(rawObjectPtr))
            {
                throw new ArgumentException("Expected COM object, but validation failed.");
            }

            _typeLibPointer = ComPointer <ITypeLibInternal> .GetObject(rawObjectPtr, addRef);

            InitCommon();
        }
예제 #6
0
        /// <summary>
        /// 克隆该非托管图片
        /// </summary>
        /// <returns>Returns clone of the unmanaged image.</returns>
        /// <remarks><para>The method does complete cloning of the object.</para></remarks>
        public UnmanagedImage Clone()
        {
            // allocate memory for the image
            IntPtr newImageData = System.Runtime.InteropServices.Marshal.AllocHGlobal(stride * height);

            UnmanagedImage newImage = new UnmanagedImage(newImageData, width, height, stride, pixelFormat);

            newImage.mustBeDisposed = true;

            UnmanagedMemoryHelper.CopyUnmanagedMemory(newImageData, imageData, stride * height);

            return(newImage);
        }
예제 #7
0
        public void Parse(string fileName)
        {
            // FbxObject (root)
            //               |`-- "Objects"
            //               |`-- ...    |`-- "Geometry"
            //               |`-- ...    |`-- ...     |`-- "Vertices" { [0]: double[], ... }
            //                `-- ...    |`-- ...     |`-- "PolygonVertexIndex" { [0]: int[], ... }
            //                            `-- ...     |`-- "LayerElementNormal"
            //                                        |`-- ...               |`-- "Normals" { [0]: double[], ... }
            //                                         `-- ...               |`-- ...
            //                                                                `-- ...

            var Objects            = "Objects".ToASCII();
            var Geometry           = "Geometry".ToASCII();
            var Vertices           = "Vertices".ToASCII();
            var PolygonVertexIndex = "PolygonVertexIndex".ToASCII();
            var LayerElementNormal = "LayerElementNormal".ToASCII();
            var Normals            = "Normals".ToASCII();

            UnmanagedMemoryHelper.Initialize();
            using (var stream = File.OpenRead(fileName))
                using (var fbx = FbxParser.Parse(stream)) {
                    Assert.True(fbx.Nodes.Count > 0);

                    var positions = fbx.Find(Objects)
                                    .Find(Geometry)
                                    .Find(Vertices)
                                    .Properties[0].AsDoubleArray();

                    var indices = fbx.Find(Objects)
                                  .Find(Geometry)
                                  .Find(PolygonVertexIndex)
                                  .Properties[0].AsInt32Array();

                    var normals = fbx.Find(Objects)
                                  .Find(Geometry)
                                  .Find(LayerElementNormal)
                                  .Find(Normals)
                                  .Properties[0].AsDoubleArray();

                    Assert.True(positions.Length > 0);
                    Assert.True(indices.Length > 0);
                    Assert.True(normals.Length > 0);
                }
            UnmanagedMemoryHelper.AssertResourceReleased();
        }
예제 #8
0
        /// <summary>
        /// Clone image.
        /// </summary>
        ///
        /// <param name="sourceData">Source image data.</param>
        ///
        /// <returns>Clones image from source image data. The message does not clone pallete in the
        /// case if the source image has indexed pixel format.</returns>
        ///
        public static Bitmap Clone(BitmapData sourceData)
        {
            // get source image size
            int width  = sourceData.Width;
            int height = sourceData.Height;

            // create new image
            Bitmap destination = new Bitmap(width, height, sourceData.PixelFormat);

            // lock destination bitmap data
            BitmapData destinationData = destination.LockBits(
                new Rectangle(0, 0, width, height),
                ImageLockMode.ReadWrite, destination.PixelFormat);

            UnmanagedMemoryHelper.CopyUnmanagedMemory(destinationData.Scan0, sourceData.Scan0, height * sourceData.Stride);

            // unlock destination image
            destination.UnlockBits(destinationData);

            return(destination);
        }
예제 #9
0
        // Create motion zones' image
        private unsafe void CreateMotionZonesFrame()
        {
            lock (sync)
            {
                // free previous motion zones frame
                if (zonesFrame != null)
                {
                    zonesFrame.Dispose();
                    zonesFrame = null;
                }

                // create motion zones frame only in the case if the algorithm has processed at least one frame
                if ((motionZones != null) && (motionZones.Length != 0) && (videoWidth != 0))
                {
                    zonesFrame = UnmanagedImage.Create(videoWidth, videoHeight, PixelFormat.Format8bppIndexed);

                    Rectangle imageRect = new Rectangle(0, 0, videoWidth, videoHeight);

                    // draw all motion zones on motion frame
                    foreach (Rectangle rect in motionZones)
                    {
                        rect.Intersect(imageRect);

                        // rectangle's dimenstion
                        int rectWidth  = rect.Width;
                        int rectHeight = rect.Height;

                        // start pointer
                        int   stride = zonesFrame.Stride;
                        byte *ptr    = (byte *)zonesFrame.ImageData.ToPointer() + rect.Y * stride + rect.X;

                        for (int y = 0; y < rectHeight; y++)
                        {
                            UnmanagedMemoryHelper.SetUnmanagedMemory(ptr, 255, rectWidth);
                            ptr += stride;
                        }
                    }
                }
            }
        }
예제 #10
0
        private void InitFromRawPointer(IntPtr rawObjectPtr, bool addRef)
        {
            if (!UnmanagedMemoryHelper.ValidateComObject(rawObjectPtr))
            {
                throw new ArgumentException("Expected COM object, but validation failed.");
            }

            // We have to restrict interface requests to VBE hosted ITypeInfos due to a bug in their implementation.
            // See TypeInfoWrapper class XML doc for details.

            // VBE provides two implementations of ITypeInfo for each component.  Both versions have different quirks and limitations.
            // We use both versions to try to expose a more complete/accurate version of ITypeInfo.
            _typeInfoPointer =
                ComPointer <ITypeInfoInternal> .GetObjectViaAggregation(rawObjectPtr, addRef, queryType : false);

            _typeInfoAlternatePointer =
                ComPointer <ITypeInfoInternal> .GetObjectViaAggregation(rawObjectPtr, addRef, queryType : true);

            // safely test whether the provided ITypeInfo is hosted by the VBE, and thus exposes the VBE extensions
            HasVBEExtensions = ComHelper.DoesComObjPtrSupportInterface <IVBEComponent>(rawObjectPtr);

            InitCommon();
            DetectUserFormClass();
        }
예제 #11
0
        /// <summary>
        /// 为新的图片创建非托管内存
        /// </summary>
        /// <param name="width">Image width.</param>
        /// <param name="height">Image height.</param>
        /// <param name="pixelFormat">Image pixel format.</param>
        /// <returns>Return image allocated in unmanaged memory.</returns>
        /// <remarks><para>Allocate new image with specified attributes in unmanaged memory.</para>
        /// <para><note>The method supports only
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format8bppIndexed</see>,
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format16bppGrayScale</see>,
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format24bppRgb</see>,
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format32bppRgb</see>,
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format32bppArgb</see>,
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format32bppPArgb</see>,
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format48bppRgb</see>,
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format64bppArgb</see> and
        /// <see cref="System.Drawing.Imaging.PixelFormat">Format64bppPArgb</see> pixel formats.
        /// In the case if <see cref="System.Drawing.Imaging.PixelFormat">Format8bppIndexed</see>
        /// format is specified, pallete is not not created for the image (supposed that it is
        /// 8 bpp grayscale image).
        /// </note></para>
        /// </remarks>
        /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format was specified.</exception>
        /// <exception cref="InvalidImagePropertiesException">Invalid image size was specified.</exception>
        public static UnmanagedImage Create(int width, int height, PixelFormat pixelFormat)
        {
            int bytesPerPixel = 0;

            // calculate bytes per pixel
            switch (pixelFormat)
            {
            case PixelFormat.Format8bppIndexed:
                bytesPerPixel = 1;
                break;

            case PixelFormat.Format16bppGrayScale:
                bytesPerPixel = 2;
                break;

            case PixelFormat.Format24bppRgb:
                bytesPerPixel = 3;
                break;

            case PixelFormat.Format32bppRgb:
            case PixelFormat.Format32bppArgb:
            case PixelFormat.Format32bppPArgb:
                bytesPerPixel = 4;
                break;

            case PixelFormat.Format48bppRgb:
                bytesPerPixel = 6;
                break;

            case PixelFormat.Format64bppArgb:
            case PixelFormat.Format64bppPArgb:
                bytesPerPixel = 8;
                break;

            default:
                throw new UnsupportedImageFormatException("Can not create image with specified pixel format.");
            }

            // check image size
            if ((width <= 0) || (height <= 0))
            {
                throw new InvalidImagePropertiesException("Invalid image size specified.");
            }

            // calculate stride
            int stride = width * bytesPerPixel;

            if (stride % 4 != 0)
            {
                stride += (4 - (stride % 4));
            }

            // allocate memory for the image
            IntPtr imageData = System.Runtime.InteropServices.Marshal.AllocHGlobal(stride * height);

            UnmanagedMemoryHelper.SetUnmanagedMemory(imageData, 0, stride * height);

            UnmanagedImage image = new UnmanagedImage(imageData, width, height, stride, pixelFormat);

            image.mustBeDisposed = true;

            return(image);
        }
        /// <summary>
        /// Process new video frame.
        /// </summary>
        ///
        /// <param name="videoFrame">Video frame to process (detect motion in).</param>
        ///
        /// <remarks><para>Processes new frame from video source and detects motion in it.</para>
        ///
        /// <para>Check <see cref="MotionLevel"/> property to get information about amount of motion
        /// (changes) in the processed frame.</para>
        /// </remarks>
        ///
        public unsafe void ProcessFrame(UnmanagedImage videoFrame)
        {
            lock (sync)
            {
                // check background frame
                if (backgroundFrame == null)
                {
                    // save image dimension
                    width  = videoFrame.Width;
                    height = videoFrame.Height;

                    // alocate memory for background frame
                    backgroundFrame = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
                    frameSize       = backgroundFrame.Stride * height;

                    // convert source frame to grayscale
                    Grayscale.CommonAlgorithms.BT709.Apply(videoFrame, backgroundFrame);

                    return;
                }

                // check image dimension
                if ((videoFrame.Width != width) || (videoFrame.Height != height))
                {
                    return;
                }

                // check motion frame
                if (motionFrame == null)
                {
                    motionFrame = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);

                    // temporary buffer
                    if (suppressNoise)
                    {
                        tempFrame = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
                    }
                }

                // convert current image to grayscale
                Grayscale.CommonAlgorithms.BT709.Apply(videoFrame, motionFrame);

                // pointers to background and current frames
                byte *backFrame;
                byte *currFrame;
                int   diff;

                backFrame = (byte *)backgroundFrame.ImageData.ToPointer();
                currFrame = (byte *)motionFrame.ImageData.ToPointer();

                // 1 - get difference between frames
                // 2 - threshold the difference
                for (int i = 0; i < frameSize; i++, backFrame++, currFrame++)
                {
                    // difference
                    diff = (int)*currFrame - (int)*backFrame;
                    // treshold
                    *currFrame = ((diff >= differenceThreshold) || (diff <= differenceThresholdNeg)) ? (byte)255 : (byte)0;
                }

                if (suppressNoise)
                {
                    // suppress noise and calculate motion amount
                    UnmanagedMemoryHelper.CopyUnmanagedMemory(tempFrame.ImageData, motionFrame.ImageData, frameSize);
                    erosionFilter.Apply(tempFrame, motionFrame);

                    if (keepObjectEdges)
                    {
                        UnmanagedMemoryHelper.CopyUnmanagedMemory(tempFrame.ImageData, motionFrame.ImageData, frameSize);
                        dilatationFilter.Apply(tempFrame, motionFrame);
                    }
                }

                // calculate amount of motion pixels
                pixelsChanged = 0;
                byte *motion = (byte *)motionFrame.ImageData.ToPointer();

                for (int i = 0; i < frameSize; i++, motion++)
                {
                    pixelsChanged += (*motion & 1);
                }
            }
        }
예제 #13
0
        /// <summary>
        /// Process new video frame.
        /// </summary>
        ///
        /// <param name="videoFrame">Video frame to process (detect motion in).</param>
        ///
        /// <remarks><para>Processes new frame from video source and detects motion in it.</para>
        ///
        /// <para>Check <see cref="MotionLevel"/> property to get information about amount of motion
        /// (changes) in the processed frame.</para>
        /// </remarks>
        ///
        public unsafe void ProcessFrame(UnmanagedImage videoFrame)
        {
            lock (sync)
            {
                // check background frame
                if (backgroundFrame == null)
                {
                    lastTimeMeasurment = DateTime.Now;

                    // save image dimension
                    width  = videoFrame.Width;
                    height = videoFrame.Height;

                    // alocate memory for previous and current frames
                    backgroundFrame = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
                    motionFrame     = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);

                    frameSize = motionFrame.Stride * height;

                    // temporary buffer
                    if (suppressNoise)
                    {
                        tempFrame = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
                    }

                    // convert source frame to grayscale
                    Grayscale.CommonAlgorithms.BT709.Apply(videoFrame, backgroundFrame);

                    return;
                }

                // check image dimension
                if ((videoFrame.Width != width) || (videoFrame.Height != height))
                {
                    return;
                }

                // convert current image to grayscale
                Grayscale.CommonAlgorithms.BT709.Apply(videoFrame, motionFrame);

                // pointers to background and current frames
                byte *backFrame;
                byte *currFrame;
                int   diff;

                // update background frame
                if (millisecondsPerBackgroundUpdate == 0)
                {
                    // update background frame using frame counter as a base
                    if (++framesCounter == framesPerBackgroundUpdate)
                    {
                        framesCounter = 0;

                        backFrame = (byte *)backgroundFrame.ImageData.ToPointer();
                        currFrame = (byte *)motionFrame.ImageData.ToPointer();

                        for (int i = 0; i < frameSize; i++, backFrame++, currFrame++)
                        {
                            diff = *currFrame - *backFrame;
                            if (diff > 0)
                            {
                                (*backFrame)++;
                            }
                            else if (diff < 0)
                            {
                                (*backFrame)--;
                            }
                        }
                    }
                }
                else
                {
                    // update background frame using timer as a base

                    // get current time and calculate difference
                    DateTime currentTime = DateTime.Now;
                    TimeSpan timeDff     = currentTime - lastTimeMeasurment;
                    // save current time as the last measurment
                    lastTimeMeasurment = currentTime;

                    int millisonds = (int)timeDff.TotalMilliseconds + millisecondsLeftUnprocessed;

                    // save remainder so it could be taken into account in the future
                    millisecondsLeftUnprocessed = millisonds % millisecondsPerBackgroundUpdate;
                    // get amount for background update
                    int updateAmount = (int)(millisonds / millisecondsPerBackgroundUpdate);

                    backFrame = (byte *)backgroundFrame.ImageData.ToPointer();
                    currFrame = (byte *)motionFrame.ImageData.ToPointer();

                    for (int i = 0; i < frameSize; i++, backFrame++, currFrame++)
                    {
                        diff = *currFrame - *backFrame;
                        if (diff > 0)
                        {
                            (*backFrame) += (byte)((diff < updateAmount) ? diff : updateAmount);
                        }
                        else if (diff < 0)
                        {
                            (*backFrame) += (byte)((-diff < updateAmount) ? diff : -updateAmount);
                        }
                    }
                }

                backFrame = (byte *)backgroundFrame.ImageData.ToPointer();
                currFrame = (byte *)motionFrame.ImageData.ToPointer();

                // 1 - get difference between frames
                // 2 - threshold the difference
                for (int i = 0; i < frameSize; i++, backFrame++, currFrame++)
                {
                    // difference
                    diff = (int)*currFrame - (int)*backFrame;
                    // treshold
                    *currFrame = ((diff >= differenceThreshold) || (diff <= differenceThresholdNeg)) ? (byte)255 : (byte)0;
                }

                if (suppressNoise)
                {
                    // suppress noise and calculate motion amount
                    UnmanagedMemoryHelper.CopyUnmanagedMemory(tempFrame.ImageData, motionFrame.ImageData, frameSize);
                    erosionFilter.Apply(tempFrame, motionFrame);

                    if (keepObjectEdges)
                    {
                        UnmanagedMemoryHelper.CopyUnmanagedMemory(tempFrame.ImageData, motionFrame.ImageData, frameSize);
                        dilatationFilter.Apply(tempFrame, motionFrame);
                    }
                }

                // calculate amount of motion pixels
                pixelsChanged = 0;
                byte *motion = (byte *)motionFrame.ImageData.ToPointer();

                for (int i = 0; i < frameSize; i++, motion++)
                {
                    pixelsChanged += (*motion & 1);
                }
            }
        }
예제 #14
0
        /// <summary>
        /// Fill rectangle on the specified image.
        /// </summary>
        ///
        /// <param name="image">Source image to draw on.</param>
        /// <param name="rectangle">Rectangle's coordinates to fill.</param>
        /// <param name="color">Rectangle's color.</param>
        ///
        /// <exception cref="UnsupportedImageFormatException">The source image has incorrect pixel format.</exception>
        ///
        public static unsafe void FillRectangle(UnmanagedImage image, Rectangle rectangle, Color color)
        {
            CheckPixelFormat(image.PixelFormat);

            int pixelSize = System.Drawing.Image.GetPixelFormatSize(image.PixelFormat) / 8;

            // image dimension
            int imageWidth  = image.Width;
            int imageHeight = image.Height;
            int stride      = image.Stride;

            // rectangle dimension and position
            int rectX1 = rectangle.X;
            int rectY1 = rectangle.Y;
            int rectX2 = rectangle.X + rectangle.Width - 1;
            int rectY2 = rectangle.Y + rectangle.Height - 1;

            // check if rectangle is in the image
            if ((rectX1 >= imageWidth) || (rectY1 >= imageHeight) || (rectX2 < 0) || (rectY2 < 0))
            {
                // nothing to draw
                return;
            }

            int startX = Math.Max(0, rectX1);
            int stopX  = Math.Min(imageWidth - 1, rectX2);
            int startY = Math.Max(0, rectY1);
            int stopY  = Math.Min(imageHeight - 1, rectY2);

            // do the job
            byte *ptr = (byte *)image.ImageData.ToPointer() + startY * stride + startX * pixelSize;

            if (image.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                // grayscale image
                byte gray = (byte)(0.2125 * color.R + 0.7154 * color.G + 0.0721 * color.B);

                int fillWidth = stopX - startX + 1;

                for (int y = startY; y <= stopY; y++)
                {
                    UnmanagedMemoryHelper.SetUnmanagedMemory(ptr, gray, fillWidth);
                    ptr += stride;
                }
            }
            else
            {
                // color image
                byte red   = color.R;
                byte green = color.G;
                byte blue  = color.B;

                int offset = stride - (stopX - startX + 1) * pixelSize;

                for (int y = startY; y <= stopY; y++)
                {
                    for (int x = startX; x <= stopX; x++, ptr += pixelSize)
                    {
                        ptr[RGB.R] = red;
                        ptr[RGB.G] = green;
                        ptr[RGB.B] = blue;
                    }
                    ptr += offset;
                }
            }
        }