Example #1
0
            public void ThenShouldReturnPointWithX2AndY2GivenCenterPoint25AndNegative25AndPointToRotateXAndY(
                int pointToRotateX,
                int pointToRotateY,
                int expectedX,
                int expectedY)
            {
                // Arrange
                var pointToRotate = new Point(pointToRotateX, pointToRotateY);

                // Act
                var rotatePoint = ImageMaths.RotatePoint(pointToRotate, 90, new Point(25, -25));

                // Assert
                Assert.That(rotatePoint, Is.EqualTo(new Point(expectedX, expectedY)));
            }
Example #2
0
        /// <summary>
        /// The process image.
        /// </summary>
        /// <param name="factory">
        /// The factory.
        /// </param>
        /// <returns>
        /// The <see cref="Image"/>.
        /// </returns>
        /// <exception cref="ImageProcessingException">
        /// </exception>
        public Image ProcessImage(ImageFactory factory)
        {
            Bitmap cyan     = null;
            Bitmap magenta  = null;
            Bitmap yellow   = null;
            Bitmap keyline  = null;
            Bitmap newImage = null;
            Image  image    = factory.Image;

            try
            {
                int width  = image.Width;
                int height = image.Height;

                // Angles taken from Wikipedia page.
                float cyanAngle    = 15f;
                float magentaAngle = 75f;
                float yellowAngle  = 0f;
                float keylineAngle = 45f;

                int   diameter   = 4;
                float multiplier = 4 * (float)Math.Sqrt(2);

                // Cyan color sampled from Wikipedia page.
                Brush cyanBrush    = new SolidBrush(Color.FromArgb(0, 153, 239));
                Brush magentaBrush = Brushes.Magenta;
                Brush yellowBrush  = Brushes.Yellow;
                Brush keylineBrush;

                // Create our images.
                cyan     = new Bitmap(width, height);
                magenta  = new Bitmap(width, height);
                yellow   = new Bitmap(width, height);
                keyline  = new Bitmap(width, height);
                newImage = new Bitmap(width, height);

                // Ensure the correct resolution is set.
                cyan.SetResolution(image.HorizontalResolution, image.VerticalResolution);
                magenta.SetResolution(image.HorizontalResolution, image.VerticalResolution);
                yellow.SetResolution(image.HorizontalResolution, image.VerticalResolution);
                keyline.SetResolution(image.HorizontalResolution, image.VerticalResolution);
                newImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

                // Check bounds against this.
                Rectangle rectangle = new Rectangle(0, 0, width, height);

                using (Graphics graphicsCyan = Graphics.FromImage(cyan))
                    using (Graphics graphicsMagenta = Graphics.FromImage(magenta))
                        using (Graphics graphicsYellow = Graphics.FromImage(yellow))
                            using (Graphics graphicsKeyline = Graphics.FromImage(keyline))
                            {
                                // Ensure cleared out.
                                graphicsCyan.Clear(Color.Transparent);
                                graphicsMagenta.Clear(Color.Transparent);
                                graphicsYellow.Clear(Color.Transparent);
                                graphicsKeyline.Clear(Color.Transparent);

                                // This is too slow. The graphics object can't be called within a parallel
                                // loop so we have to do it old school. :(
                                using (FastBitmap sourceBitmap = new FastBitmap(image))
                                {
                                    for (int y = -height * 2; y < height * 2; y += diameter)
                                    {
                                        for (int x = -width * 2; x < width * 2; x += diameter)
                                        {
                                            Color     color;
                                            CmykColor cmykColor;
                                            float     brushWidth;

                                            // Cyan
                                            Point rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), cyanAngle);
                                            int   angledX      = rotatedPoint.X;
                                            int   angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = diameter * (cmykColor.C / 255f) * multiplier;
                                                graphicsCyan.FillEllipse(cyanBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Magenta
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), magentaAngle);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = diameter * (cmykColor.M / 255f) * multiplier;
                                                graphicsMagenta.FillEllipse(magentaBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Yellow
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), yellowAngle);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = diameter * (cmykColor.Y / 255f) * multiplier;
                                                graphicsYellow.FillEllipse(yellowBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Keyline
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), keylineAngle);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = diameter * (cmykColor.K / 255f) * multiplier;

                                                // Just using blck is too dark.
                                                keylineBrush = new SolidBrush(CmykColor.FromCmykColor(0, 0, 0, cmykColor.K));
                                                graphicsKeyline.FillEllipse(keylineBrush, angledX, angledY, brushWidth, brushWidth);
                                            }
                                        }
                                    }
                                }

                                // Set our white background.
                                using (Graphics graphics = Graphics.FromImage(newImage))
                                {
                                    graphics.Clear(Color.White);
                                }

                                // Blend the colors now to mimic adaptive blending.
                                using (FastBitmap cyanBitmap = new FastBitmap(cyan))
                                    using (FastBitmap magentaBitmap = new FastBitmap(magenta))
                                        using (FastBitmap yellowBitmap = new FastBitmap(yellow))
                                            using (FastBitmap keylineBitmap = new FastBitmap(keyline))
                                                using (FastBitmap destinationBitmap = new FastBitmap(newImage))
                                                {
                                                    Parallel.For(
                                                        0,
                                                        height,
                                                        y =>
                                                    {
                                                        for (int x = 0; x < width; x++)
                                                        {
                                                            // ReSharper disable AccessToDisposedClosure
                                                            Color cyanPixel    = cyanBitmap.GetPixel(x, y);
                                                            Color magentaPixel = magentaBitmap.GetPixel(x, y);
                                                            Color yellowPixel  = yellowBitmap.GetPixel(x, y);
                                                            Color keylinePixel = keylineBitmap.GetPixel(x, y);

                                                            CmykColor blended = cyanPixel.AddAsCmykColor(magentaPixel, yellowPixel, keylinePixel);
                                                            destinationBitmap.SetPixel(x, y, blended);
                                                            // ReSharper restore AccessToDisposedClosure
                                                        }
                                                    });
                                                }
                            }

                cyan.Dispose();
                magenta.Dispose();
                yellow.Dispose();
                keyline.Dispose();
                image.Dispose();
                image = newImage;
            }
            catch (Exception ex)
            {
                if (cyan != null)
                {
                    cyan.Dispose();
                }

                if (magenta != null)
                {
                    magenta.Dispose();
                }

                if (yellow != null)
                {
                    yellow.Dispose();
                }

                if (keyline != null)
                {
                    keyline.Dispose();
                }

                if (newImage != null)
                {
                    newImage.Dispose();
                }

                throw new ImageProcessingException("Error processing image with " + this.GetType().Name, ex);
            }

            return(image);
        }
Example #3
0
        /// <summary>
        /// Applies the halftone filter.
        /// </summary>
        /// <param name="source">
        /// The <see cref="Bitmap"/> to apply the filter to.
        /// </param>
        /// <returns>
        /// The <see cref="Bitmap"/> with the filter applied.
        /// </returns>
        public Bitmap ApplyFilter(Bitmap source)
        {
            // TODO: Make this class implement an interface?
            Bitmap padded   = null;
            Bitmap cyan     = null;
            Bitmap magenta  = null;
            Bitmap yellow   = null;
            Bitmap keyline  = null;
            Bitmap newImage = null;

            try
            {
                int sourceWidth  = source.Width;
                int sourceHeight = source.Height;
                int width        = source.Width + this.distance;
                int height       = source.Height + this.distance;

                // Draw a slightly larger image, flipping the top/left pixels to prevent
                // jagged edge of output.
                padded = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
                padded.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                using (Graphics graphicsPadded = Graphics.FromImage(padded))
                {
                    graphicsPadded.Clear(Color.White);
                    Rectangle destinationRectangle = new Rectangle(0, 0, sourceWidth + this.distance, source.Height + this.distance);
                    using (TextureBrush tb = new TextureBrush(source))
                    {
                        tb.WrapMode = WrapMode.TileFlipXY;
                        tb.TranslateTransform(this.distance, this.distance);
                        graphicsPadded.FillRectangle(tb, destinationRectangle);
                    }
                }

                // Calculate min and max widths/heights.
                Rectangle rotatedBounds = this.GetBoundingRectangle(width, height);
                int       minY          = -(rotatedBounds.Height + height);
                int       maxY          = rotatedBounds.Height + height;
                int       minX          = -(rotatedBounds.Width + width);
                int       maxX          = rotatedBounds.Width + width;
                Point     center        = Point.Empty;

                // Yellow oversaturates the output.
                int   offset            = this.distance;
                float yellowMultiplier  = this.distance * 1.587f;
                float magentaMultiplier = this.distance * 2.176f;
                float multiplier        = this.distance * 2.2f;
                float max        = this.distance * (float)Math.Sqrt(2);
                float magentaMax = this.distance * (float)Math.Sqrt(1.4545);

                // Bump up the keyline max so that black looks black.
                float keylineMax = max * (float)Math.Sqrt(2);

                // Color sampled process colours from Wikipedia pages.
                // Keyline brush is declared separately.
                Brush cyanBrush    = new SolidBrush(Color.FromArgb(0, 183, 235));
                Brush magentaBrush = new SolidBrush(Color.FromArgb(255, 0, 144));
                Brush yellowBrush  = new SolidBrush(Color.FromArgb(255, 239, 0));

                // Create our images.
                cyan     = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
                magenta  = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
                yellow   = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
                keyline  = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
                newImage = new Bitmap(sourceWidth, sourceHeight, PixelFormat.Format32bppPArgb);

                // Ensure the correct resolution is set.
                cyan.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                magenta.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                yellow.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                keyline.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                newImage.SetResolution(source.HorizontalResolution, source.VerticalResolution);

                // Check bounds against this.
                Rectangle rectangle = new Rectangle(0, 0, width, height);

                using (Graphics graphicsCyan = Graphics.FromImage(cyan))
                    using (Graphics graphicsMagenta = Graphics.FromImage(magenta))
                        using (Graphics graphicsYellow = Graphics.FromImage(yellow))
                            using (Graphics graphicsKeyline = Graphics.FromImage(keyline))
                            {
                                // Set the quality properties.
                                graphicsCyan.PixelOffsetMode    = PixelOffsetMode.Half;
                                graphicsMagenta.PixelOffsetMode = PixelOffsetMode.Half;
                                graphicsYellow.PixelOffsetMode  = PixelOffsetMode.Half;
                                graphicsKeyline.PixelOffsetMode = PixelOffsetMode.Half;

                                graphicsCyan.SmoothingMode    = SmoothingMode.AntiAlias;
                                graphicsMagenta.SmoothingMode = SmoothingMode.AntiAlias;
                                graphicsYellow.SmoothingMode  = SmoothingMode.AntiAlias;
                                graphicsKeyline.SmoothingMode = SmoothingMode.AntiAlias;

                                graphicsCyan.CompositingQuality    = CompositingQuality.HighQuality;
                                graphicsMagenta.CompositingQuality = CompositingQuality.HighQuality;
                                graphicsYellow.CompositingQuality  = CompositingQuality.HighQuality;
                                graphicsKeyline.CompositingQuality = CompositingQuality.HighQuality;

                                // Set up the canvas.
                                graphicsCyan.Clear(Color.White);
                                graphicsMagenta.Clear(Color.White);
                                graphicsYellow.Clear(Color.White);
                                graphicsKeyline.Clear(Color.White);

                                // This is too slow. The graphics object can't be called within a parallel
                                // loop so we have to do it old school. :(
                                using (FastBitmap sourceBitmap = new FastBitmap(padded))
                                {
                                    for (int y = minY; y < maxY; y += offset)
                                    {
                                        for (int x = minX; x < maxX; x += offset)
                                        {
                                            Color     color;
                                            CmykColor cmykColor;
                                            float     brushWidth;

                                            // Cyan
                                            Point rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.cyanAngle, center);
                                            int   angledX      = rotatedPoint.X;
                                            int   angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = Math.Min((cmykColor.C / 100f) * multiplier, max);
                                                graphicsCyan.FillEllipse(cyanBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Magenta
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.magentaAngle, center);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = Math.Min((cmykColor.M / 100f) * magentaMultiplier, magentaMax);
                                                graphicsMagenta.FillEllipse(magentaBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Yellow
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.yellowAngle, center);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = Math.Min((cmykColor.Y / 100f) * yellowMultiplier, max);
                                                graphicsYellow.FillEllipse(yellowBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Keyline
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.keylineAngle, center);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = Math.Min((cmykColor.K / 100f) * multiplier, keylineMax);

                                                // Just using black is too dark.
                                                Brush keylineBrush = new SolidBrush(CmykColor.FromCmykColor(0, 0, 0, cmykColor.K));
                                                graphicsKeyline.FillEllipse(keylineBrush, angledX, angledY, brushWidth, brushWidth);
                                            }
                                        }
                                    }
                                }

                                // Set our white background.
                                using (Graphics graphics = Graphics.FromImage(newImage))
                                {
                                    graphics.Clear(Color.White);
                                }

                                // Blend the colors now to mimic adaptive blending.
                                using (FastBitmap cyanBitmap = new FastBitmap(cyan))
                                    using (FastBitmap magentaBitmap = new FastBitmap(magenta))
                                        using (FastBitmap yellowBitmap = new FastBitmap(yellow))
                                            using (FastBitmap keylineBitmap = new FastBitmap(keyline))
                                                using (FastBitmap destinationBitmap = new FastBitmap(newImage))
                                                {
                                                    Parallel.For(
                                                        offset,
                                                        height,
                                                        y =>
                                                    {
                                                        for (int x = offset; x < width; x++)
                                                        {
                                                            // ReSharper disable AccessToDisposedClosure
                                                            Color cyanPixel    = cyanBitmap.GetPixel(x, y);
                                                            Color magentaPixel = magentaBitmap.GetPixel(x, y);
                                                            Color yellowPixel  = yellowBitmap.GetPixel(x, y);
                                                            Color keylinePixel = keylineBitmap.GetPixel(x, y);

                                                            // Negate the offset.
                                                            int xBack = x - offset;
                                                            int yBack = y - offset;

                                                            CmykColor blended = cyanPixel.AddAsCmykColor(magentaPixel, yellowPixel, keylinePixel);
                                                            if (rectangle.Contains(new Point(xBack, yBack)))
                                                            {
                                                                destinationBitmap.SetPixel(xBack, yBack, blended);
                                                            }
                                                            // ReSharper restore AccessToDisposedClosure
                                                        }
                                                    });
                                                }
                            }

                padded.Dispose();
                cyan.Dispose();
                magenta.Dispose();
                yellow.Dispose();
                keyline.Dispose();
                source.Dispose();
                source = newImage;
            }
            catch
            {
                if (padded != null)
                {
                    padded.Dispose();
                }

                if (cyan != null)
                {
                    cyan.Dispose();
                }

                if (magenta != null)
                {
                    magenta.Dispose();
                }

                if (yellow != null)
                {
                    yellow.Dispose();
                }

                if (keyline != null)
                {
                    keyline.Dispose();
                }

                if (newImage != null)
                {
                    newImage.Dispose();
                }
            }

            return(source);
        }
Example #4
0
        /// <summary>
        /// Applies the halftone filter.
        /// </summary>
        /// <param name="source">
        /// The <see cref="Bitmap"/> to apply the filter to.
        /// </param>
        /// <returns>
        /// The <see cref="Bitmap"/> with the filter applied.
        /// </returns>
        public Bitmap ApplyFilter(Bitmap source)
        {
            // TODO: Make this class implement an interface?
            Bitmap cyan     = null;
            Bitmap magenta  = null;
            Bitmap yellow   = null;
            Bitmap keyline  = null;
            Bitmap newImage = null;

            try
            {
                int width  = source.Width;
                int height = source.Height;

                // Calculate min and max widths/heights.
                Rectangle rotatedBounds = this.GetBoundingRectangle(width, height);
                int       minY          = -(rotatedBounds.Height + height);
                int       maxY          = rotatedBounds.Height + height;
                int       minX          = -(rotatedBounds.Width + width);
                int       maxX          = rotatedBounds.Width + width;
                Point     center        = Point.Empty;

                // Yellow oversaturates the output.
                const float YellowMultiplier = 4;
                float       multiplier       = YellowMultiplier * (float)Math.Sqrt(2);

                float max = this.distance;

                // Bump up the keyline max so that black looks black.
                float keylineMax = max + ((float)Math.Sqrt(2) * 1.44f);

                // Color sampled process colours from Wikipedia pages.
                // Keyline brush is declared separately.
                Brush cyanBrush    = new SolidBrush(Color.FromArgb(0, 183, 235));
                Brush magentaBrush = new SolidBrush(Color.FromArgb(255, 0, 144));
                Brush yellowBrush  = new SolidBrush(Color.FromArgb(255, 239, 0));

                // Create our images.
                cyan     = new Bitmap(width, height);
                magenta  = new Bitmap(width, height);
                yellow   = new Bitmap(width, height);
                keyline  = new Bitmap(width, height);
                newImage = new Bitmap(width, height);

                // Ensure the correct resolution is set.
                cyan.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                magenta.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                yellow.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                keyline.SetResolution(source.HorizontalResolution, source.VerticalResolution);
                newImage.SetResolution(source.HorizontalResolution, source.VerticalResolution);

                // Check bounds against this.
                Rectangle rectangle = new Rectangle(0, 0, width, height);

                using (Graphics graphicsCyan = Graphics.FromImage(cyan))
                    using (Graphics graphicsMagenta = Graphics.FromImage(magenta))
                        using (Graphics graphicsYellow = Graphics.FromImage(yellow))
                            using (Graphics graphicsKeyline = Graphics.FromImage(keyline))
                            {
                                // Set the quality properties.
                                graphicsCyan.PixelOffsetMode    = PixelOffsetMode.HighQuality;
                                graphicsMagenta.PixelOffsetMode = PixelOffsetMode.HighQuality;
                                graphicsYellow.PixelOffsetMode  = PixelOffsetMode.HighQuality;
                                graphicsKeyline.PixelOffsetMode = PixelOffsetMode.HighQuality;

                                // Set up the canvas.
                                graphicsCyan.Clear(Color.Transparent);
                                graphicsMagenta.Clear(Color.Transparent);
                                graphicsYellow.Clear(Color.Transparent);
                                graphicsKeyline.Clear(Color.Transparent);

                                int d = this.distance;

                                // This is too slow. The graphics object can't be called within a parallel
                                // loop so we have to do it old school. :(
                                using (FastBitmap sourceBitmap = new FastBitmap(source))
                                {
                                    for (int y = minY; y < maxY; y += d)
                                    {
                                        for (int x = minX; x < maxX; x += d)
                                        {
                                            Color     color;
                                            CmykColor cmykColor;
                                            float     brushWidth;
                                            int       offsetX = x - (d >> 1);
                                            int       offsetY = y - (d >> 1);

                                            // Cyan
                                            Point rotatedPoint = ImageMaths.RotatePoint(new Point(offsetX, offsetY), this.cyanAngle, center);
                                            int   angledX      = rotatedPoint.X;
                                            int   angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = ImageMaths.Clamp(d * (cmykColor.C / 255f) * multiplier, 0, max);
                                                graphicsCyan.FillEllipse(cyanBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Magenta
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(offsetX, offsetY), this.magentaAngle, center);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = ImageMaths.Clamp(d * (cmykColor.M / 255f) * multiplier, 0, max);
                                                graphicsMagenta.FillEllipse(magentaBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Yellow
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(offsetX, offsetY), this.yellowAngle, center);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = ImageMaths.Clamp(d * (cmykColor.Y / 255f) * YellowMultiplier, 0, max);
                                                graphicsYellow.FillEllipse(yellowBrush, angledX, angledY, brushWidth, brushWidth);
                                            }

                                            // Keyline
                                            rotatedPoint = ImageMaths.RotatePoint(new Point(offsetX, offsetY), this.keylineAngle, center);
                                            angledX      = rotatedPoint.X;
                                            angledY      = rotatedPoint.Y;
                                            if (rectangle.Contains(new Point(angledX, angledY)))
                                            {
                                                color      = sourceBitmap.GetPixel(angledX, angledY);
                                                cmykColor  = color;
                                                brushWidth = ImageMaths.Clamp(d * (cmykColor.K / 255f) * multiplier, 0, keylineMax);

                                                // Just using black is too dark.
                                                Brush keylineBrush = new SolidBrush(CmykColor.FromCmykColor(0, 0, 0, cmykColor.K));
                                                graphicsKeyline.FillEllipse(keylineBrush, angledX, angledY, brushWidth, brushWidth);
                                            }
                                        }
                                    }
                                }

                                // Set our white background.
                                using (Graphics graphics = Graphics.FromImage(newImage))
                                {
                                    graphics.Clear(Color.White);
                                }

                                // Blend the colors now to mimic adaptive blending.
                                using (FastBitmap cyanBitmap = new FastBitmap(cyan))
                                    using (FastBitmap magentaBitmap = new FastBitmap(magenta))
                                        using (FastBitmap yellowBitmap = new FastBitmap(yellow))
                                            using (FastBitmap keylineBitmap = new FastBitmap(keyline))
                                                using (FastBitmap destinationBitmap = new FastBitmap(newImage))
                                                {
                                                    Parallel.For(
                                                        0,
                                                        height,
                                                        y =>
                                                    {
                                                        for (int x = 0; x < width; x++)
                                                        {
                                                            // ReSharper disable AccessToDisposedClosure
                                                            Color cyanPixel    = cyanBitmap.GetPixel(x, y);
                                                            Color magentaPixel = magentaBitmap.GetPixel(x, y);
                                                            Color yellowPixel  = yellowBitmap.GetPixel(x, y);
                                                            Color keylinePixel = keylineBitmap.GetPixel(x, y);

                                                            CmykColor blended = cyanPixel.AddAsCmykColor(magentaPixel, yellowPixel, keylinePixel);
                                                            if (rectangle.Contains(new Point(x, y)))
                                                            {
                                                                destinationBitmap.SetPixel(x, y, blended);
                                                            }
                                                            // ReSharper restore AccessToDisposedClosure
                                                        }
                                                    });
                                                }
                            }

                cyan.Dispose();
                magenta.Dispose();
                yellow.Dispose();
                keyline.Dispose();
                source.Dispose();
                source = newImage;
            }
            catch
            {
                if (cyan != null)
                {
                    cyan.Dispose();
                }

                if (magenta != null)
                {
                    magenta.Dispose();
                }

                if (yellow != null)
                {
                    yellow.Dispose();
                }

                if (keyline != null)
                {
                    keyline.Dispose();
                }

                if (newImage != null)
                {
                    newImage.Dispose();
                }
            }

            return(source);
        }