mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: bb6daf831ba9b3efeeef392cf7b7eb53c7358030 Former-commit-id: 568e90c5570902dd0a2467960d91cf69d8036629 Former-commit-id: 24eaa11c52350199f263d023c763e9abb600bedeaf/merge-core
21 changed files with 488 additions and 395 deletions
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:a83d4e0e313a749cb054c61b3c6f9dcd63546e2880e2221f23e39865ba3bed4b |
|||
size 59460 |
|||
@ -0,0 +1,58 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="PixelOperations.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Performs per-pixel operations.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Imaging.Helpers |
|||
{ |
|||
using System; |
|||
using System.Drawing; |
|||
using ImageProcessor.Common.Extensions; |
|||
|
|||
/// <summary>
|
|||
/// Performs per-pixel operations.
|
|||
/// </summary>
|
|||
public static class PixelOperations |
|||
{ |
|||
/// <summary>
|
|||
/// Returns the given color adjusted by the given gamma value.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The <see cref="Color"/> to adjust.
|
|||
/// </param>
|
|||
/// <param name="value">
|
|||
/// The gamma value - Between .1 and 5.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The adjusted <see cref="Color"/>.
|
|||
/// </returns>
|
|||
/// <exception cref="ArgumentOutOfRangeException">
|
|||
/// Thrown if the given gamma value is out with the acceptable range.
|
|||
/// </exception>
|
|||
public static Color Gamma(Color color, float value) |
|||
{ |
|||
if (value > 5 || value < .1) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("value", "Value should be between .1 and 5."); |
|||
} |
|||
|
|||
byte[] ramp = new byte[256]; |
|||
for (int x = 0; x < 256; ++x) |
|||
{ |
|||
byte val = ((255.0 * Math.Pow(x / 255.0, value)) + 0.5).ToByte(); |
|||
ramp[x] = val; |
|||
} |
|||
|
|||
byte r = ramp[color.R]; |
|||
byte g = ramp[color.G]; |
|||
byte b = ramp[color.B]; |
|||
|
|||
return Color.FromArgb(color.A, r, g, b); |
|||
} |
|||
} |
|||
} |
|||
@ -1,262 +0,0 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="Halftone.cs" company="James South">
|
|||
// Copyright (c) James South.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Processors |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Drawing; |
|||
using System.Drawing.Drawing2D; |
|||
using System.Threading.Tasks; |
|||
|
|||
using ImageProcessor.Common.Exceptions; |
|||
using ImageProcessor.Common.Extensions; |
|||
using ImageProcessor.Imaging; |
|||
using ImageProcessor.Imaging.Colors; |
|||
using ImageProcessor.Imaging.Helpers; |
|||
|
|||
/// <summary>
|
|||
/// The halftone.
|
|||
/// </summary>
|
|||
class Halftone : IGraphicsProcessor |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Halftone"/> class.
|
|||
/// </summary>
|
|||
public Halftone() |
|||
{ |
|||
this.Settings = new Dictionary<string, string>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the dynamic parameter.
|
|||
/// </summary>
|
|||
public dynamic DynamicParameter |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets any additional settings required by the processor.
|
|||
/// </summary>
|
|||
public Dictionary<string, string> Settings |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
/// <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; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue