mirror of https://github.com/SixLabors/ImageSharp
197 changed files with 5778 additions and 2552 deletions
@ -0,0 +1,36 @@ |
|||
using System.Diagnostics; |
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes |
|||
{ |
|||
/// <summary>
|
|||
/// A struct that defines a single color stop.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
[DebuggerDisplay("ColorStop({Ratio} -> {Color}")] |
|||
public struct ColorStop<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ColorStop{TPixel}" /> struct.
|
|||
/// </summary>
|
|||
/// <param name="ratio">Where should it be? 0 is at the start, 1 at the end of the Gradient.</param>
|
|||
/// <param name="color">What color should be used at that point?</param>
|
|||
public ColorStop(float ratio, TPixel color) |
|||
{ |
|||
this.Ratio = ratio; |
|||
this.Color = color; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the point along the defined gradient axis.
|
|||
/// </summary>
|
|||
public float Ratio { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the color to be used.
|
|||
/// </summary>
|
|||
public TPixel Color { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,167 @@ |
|||
using System; |
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes |
|||
{ |
|||
/// <summary>
|
|||
/// Gradient Brush with elliptic shape.
|
|||
/// The ellipse is defined by a center point,
|
|||
/// a point on the longest extension of the ellipse and
|
|||
/// the ratio between longest and shortest extension.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format that is used.</typeparam>
|
|||
public sealed class EllipticGradientBrush<TPixel> : GradientBrushBase<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly Point center; |
|||
|
|||
private readonly Point referenceAxisEnd; |
|||
|
|||
private readonly float axisRatio; |
|||
|
|||
/// <inheritdoc cref="GradientBrushBase{TPixel}" />
|
|||
/// <param name="center">The center of the elliptical gradient and 0 for the color stops.</param>
|
|||
/// <param name="referenceAxisEnd">The end point of the reference axis of the ellipse.</param>
|
|||
/// <param name="axisRatio">
|
|||
/// The ratio of the axis widths.
|
|||
/// The second axis' is perpendicular to the reference axis and
|
|||
/// it's length is the reference axis' length multiplied by this factor.
|
|||
/// </param>
|
|||
/// <param name="repetitionMode">Defines how the colors of the gradients are repeated.</param>
|
|||
/// <param name="colorStops">the color stops as defined in base class.</param>
|
|||
public EllipticGradientBrush( |
|||
Point center, |
|||
Point referenceAxisEnd, |
|||
float axisRatio, |
|||
GradientRepetitionMode repetitionMode, |
|||
params ColorStop<TPixel>[] colorStops) |
|||
: base(repetitionMode, colorStops) |
|||
{ |
|||
this.center = center; |
|||
this.referenceAxisEnd = referenceAxisEnd; |
|||
this.axisRatio = axisRatio; |
|||
} |
|||
|
|||
/// <inheritdoc cref="CreateApplicator" />
|
|||
public override BrushApplicator<TPixel> CreateApplicator( |
|||
ImageFrame<TPixel> source, |
|||
RectangleF region, |
|||
GraphicsOptions options) => |
|||
new RadialGradientBrushApplicator( |
|||
source, |
|||
options, |
|||
this.center, |
|||
this.referenceAxisEnd, |
|||
this.axisRatio, |
|||
this.ColorStops, |
|||
this.RepetitionMode); |
|||
|
|||
/// <inheritdoc />
|
|||
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase |
|||
{ |
|||
private readonly Point center; |
|||
|
|||
private readonly Point referenceAxisEnd; |
|||
|
|||
private readonly float axisRatio; |
|||
|
|||
private readonly double rotation; |
|||
|
|||
private readonly float referenceRadius; |
|||
|
|||
private readonly float secondRadius; |
|||
|
|||
private readonly float cosRotation; |
|||
|
|||
private readonly float sinRotation; |
|||
|
|||
private readonly float secondRadiusSquared; |
|||
|
|||
private readonly float referenceRadiusSquared; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
|
|||
/// </summary>
|
|||
/// <param name="target">The target image</param>
|
|||
/// <param name="options">The options</param>
|
|||
/// <param name="center">Center of the ellipse</param>
|
|||
/// <param name="referenceAxisEnd">Point on one angular points of the ellipse.</param>
|
|||
/// <param name="axisRatio">
|
|||
/// Ratio of the axis length's. Used to determine the length of the second axis,
|
|||
/// the first is defined by <see cref="center"/> and <see cref="referenceAxisEnd"/>.</param>
|
|||
/// <param name="colorStops">Definition of colors</param>
|
|||
/// <param name="repetitionMode">Defines how the gradient colors are repeated.</param>
|
|||
public RadialGradientBrushApplicator( |
|||
ImageFrame<TPixel> target, |
|||
GraphicsOptions options, |
|||
Point center, |
|||
Point referenceAxisEnd, |
|||
float axisRatio, |
|||
ColorStop<TPixel>[] colorStops, |
|||
GradientRepetitionMode repetitionMode) |
|||
: base(target, options, colorStops, repetitionMode) |
|||
{ |
|||
this.center = center; |
|||
this.referenceAxisEnd = referenceAxisEnd; |
|||
this.axisRatio = axisRatio; |
|||
this.rotation = this.AngleBetween( |
|||
this.center, |
|||
new PointF(this.center.X + 1, this.center.Y), |
|||
this.referenceAxisEnd); |
|||
this.referenceRadius = this.DistanceBetween(this.center, this.referenceAxisEnd); |
|||
this.secondRadius = this.referenceRadius * this.axisRatio; |
|||
|
|||
this.referenceRadiusSquared = this.referenceRadius * this.referenceRadius; |
|||
this.secondRadiusSquared = this.secondRadius * this.secondRadius; |
|||
|
|||
this.sinRotation = (float)Math.Sin(this.rotation); |
|||
this.cosRotation = (float)Math.Cos(this.rotation); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override void Dispose() |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override float PositionOnGradient(int xt, int yt) |
|||
{ |
|||
float x0 = xt - this.center.X; |
|||
float y0 = yt - this.center.Y; |
|||
|
|||
float x = (x0 * this.cosRotation) - (y0 * this.sinRotation); |
|||
float y = (x0 * this.sinRotation) + (y0 * this.cosRotation); |
|||
|
|||
float xSquared = x * x; |
|||
float ySquared = y * y; |
|||
|
|||
var inBoundaryChecker = (xSquared / this.referenceRadiusSquared) |
|||
+ (ySquared / this.secondRadiusSquared); |
|||
|
|||
return inBoundaryChecker; |
|||
} |
|||
|
|||
private float AngleBetween(PointF junction, PointF a, PointF b) |
|||
{ |
|||
var vA = a - junction; |
|||
var vB = b - junction; |
|||
return (float)(Math.Atan2(vB.Y, vB.X) |
|||
- Math.Atan2(vA.Y, vA.X)); |
|||
} |
|||
|
|||
private float DistanceBetween( |
|||
PointF p1, |
|||
PointF p2) |
|||
{ |
|||
float dX = p1.X - p2.X; |
|||
float dXsquared = dX * dX; |
|||
|
|||
float dY = p1.Y - p2.Y; |
|||
float dYsquared = dY * dY; |
|||
return (float)Math.Sqrt(dXsquared + dYsquared); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,174 @@ |
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.PixelFormats.PixelBlenders; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes |
|||
{ |
|||
/// <summary>
|
|||
/// Base class for Gradient brushes
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format</typeparam>
|
|||
public abstract class GradientBrushBase<TPixel> : IBrush<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <inheritdoc cref="IBrush{TPixel}"/>
|
|||
/// <param name="repetitionMode">Defines how the colors are repeated beyond the interval [0..1]</param>
|
|||
/// <param name="colorStops">The gradient colors.</param>
|
|||
protected GradientBrushBase( |
|||
GradientRepetitionMode repetitionMode, |
|||
params ColorStop<TPixel>[] colorStops) |
|||
{ |
|||
this.RepetitionMode = repetitionMode; |
|||
this.ColorStops = colorStops; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets how the colors are repeated beyond the interval [0..1].
|
|||
/// </summary>
|
|||
protected GradientRepetitionMode RepetitionMode { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the list of color stops for this gradient.
|
|||
/// </summary>
|
|||
protected ColorStop<TPixel>[] ColorStops { get; } |
|||
|
|||
/// <inheritdoc cref="IBrush{TPixel}" />
|
|||
public abstract BrushApplicator<TPixel> CreateApplicator( |
|||
ImageFrame<TPixel> source, |
|||
RectangleF region, |
|||
GraphicsOptions options); |
|||
|
|||
/// <summary>
|
|||
/// Base class for gradient brush applicators
|
|||
/// </summary>
|
|||
protected abstract class GradientBrushApplicatorBase : BrushApplicator<TPixel> |
|||
{ |
|||
private readonly ColorStop<TPixel>[] colorStops; |
|||
|
|||
private readonly GradientRepetitionMode repetitionMode; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GradientBrushApplicatorBase"/> class.
|
|||
/// </summary>
|
|||
/// <param name="target">The target.</param>
|
|||
/// <param name="options">The options.</param>
|
|||
/// <param name="colorStops">An array of color stops sorted by their position.</param>
|
|||
/// <param name="repetitionMode">Defines if and how the gradient should be repeated.</param>
|
|||
protected GradientBrushApplicatorBase( |
|||
ImageFrame<TPixel> target, |
|||
GraphicsOptions options, |
|||
ColorStop<TPixel>[] colorStops, |
|||
GradientRepetitionMode repetitionMode) |
|||
: base(target, options) |
|||
{ |
|||
this.colorStops = colorStops; // TODO: requires colorStops to be sorted by position - should that be checked?
|
|||
this.repetitionMode = repetitionMode; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Base implementation of the indexer for gradients
|
|||
/// (follows the facade pattern, using abstract methods)
|
|||
/// </summary>
|
|||
/// <param name="x">X coordinate of the Pixel.</param>
|
|||
/// <param name="y">Y coordinate of the Pixel.</param>
|
|||
internal override TPixel this[int x, int y] |
|||
{ |
|||
get |
|||
{ |
|||
float positionOnCompleteGradient = this.PositionOnGradient(x, y); |
|||
|
|||
switch (this.repetitionMode) |
|||
{ |
|||
case GradientRepetitionMode.None: |
|||
// do nothing. The following could be done, but is not necessary:
|
|||
// onLocalGradient = Math.Min(0, Math.Max(1, onLocalGradient));
|
|||
break; |
|||
case GradientRepetitionMode.Repeat: |
|||
positionOnCompleteGradient = positionOnCompleteGradient % 1; |
|||
break; |
|||
case GradientRepetitionMode.Reflect: |
|||
positionOnCompleteGradient = positionOnCompleteGradient % 2; |
|||
if (positionOnCompleteGradient > 1) |
|||
{ |
|||
positionOnCompleteGradient = 2 - positionOnCompleteGradient; |
|||
} |
|||
|
|||
break; |
|||
case GradientRepetitionMode.DontFill: |
|||
if (positionOnCompleteGradient > 1 || positionOnCompleteGradient < 0) |
|||
{ |
|||
return NamedColors<TPixel>.Transparent; |
|||
} |
|||
|
|||
break; |
|||
default: |
|||
throw new ArgumentOutOfRangeException(); |
|||
} |
|||
|
|||
var (from, to) = this.GetGradientSegment(positionOnCompleteGradient); |
|||
|
|||
if (from.Color.Equals(to.Color)) |
|||
{ |
|||
return from.Color; |
|||
} |
|||
else |
|||
{ |
|||
var fromAsVector = from.Color.ToVector4(); |
|||
var toAsVector = to.Color.ToVector4(); |
|||
float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / to.Ratio; |
|||
|
|||
// TODO: this should be changeble for different gradienting functions
|
|||
Vector4 result = PorterDuffFunctions.Normal( |
|||
fromAsVector, |
|||
toAsVector, |
|||
onLocalGradient); |
|||
|
|||
TPixel resultColor = default; |
|||
resultColor.PackFromVector4(result); |
|||
return resultColor; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// calculates the position on the gradient for a given pixel.
|
|||
/// This method is abstract as it's content depends on the shape of the gradient.
|
|||
/// </summary>
|
|||
/// <param name="x">The x coordinate of the pixel</param>
|
|||
/// <param name="y">The y coordinate of the pixel</param>
|
|||
/// <returns>
|
|||
/// The position the given pixel has on the gradient.
|
|||
/// The position is not bound to the [0..1] interval.
|
|||
/// Values outside of that interval may be treated differently,
|
|||
/// e.g. for the <see cref="GradientRepetitionMode" /> enum.
|
|||
/// </returns>
|
|||
protected abstract float PositionOnGradient(int x, int y); |
|||
|
|||
private (ColorStop<TPixel> from, ColorStop<TPixel> to) GetGradientSegment( |
|||
float positionOnCompleteGradient) |
|||
{ |
|||
var localGradientFrom = this.colorStops[0]; |
|||
ColorStop<TPixel> localGradientTo = default; |
|||
|
|||
// TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient)
|
|||
foreach (var colorStop in this.colorStops) |
|||
{ |
|||
localGradientTo = colorStop; |
|||
|
|||
if (colorStop.Ratio > positionOnCompleteGradient) |
|||
{ |
|||
// we're done here, so break it!
|
|||
break; |
|||
} |
|||
|
|||
localGradientFrom = localGradientTo; |
|||
} |
|||
|
|||
return (localGradientFrom, localGradientTo); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes |
|||
{ |
|||
/// <summary>
|
|||
/// Modes to repeat a gradient.
|
|||
/// </summary>
|
|||
public enum GradientRepetitionMode |
|||
{ |
|||
/// <summary>
|
|||
/// don't repeat, keep the color of start and end beyond those points stable.
|
|||
/// </summary>
|
|||
None, |
|||
|
|||
/// <summary>
|
|||
/// Repeat the gradient.
|
|||
/// If it's a black-white gradient, with Repeat it will be Black->{gray}->White|Black->{gray}->White|...
|
|||
/// </summary>
|
|||
Repeat, |
|||
|
|||
/// <summary>
|
|||
/// Reflect the gradient.
|
|||
/// Similar to <see cref="Repeat"/>, but each other repetition uses inverse order of <see cref="ColorStop{TPixel}"/>s.
|
|||
/// Used on a Black-White gradient, Reflect leads to Black->{gray}->White->{gray}->White...
|
|||
/// </summary>
|
|||
Reflect, |
|||
|
|||
/// <summary>
|
|||
/// With DontFill a gradient does not touch any pixel beyond it's borders.
|
|||
/// For the <see cref="LinearGradientBrush{TPixel}" /> this is beyond the orthogonal through start and end,
|
|||
/// TODO For the cref="PolygonalGradientBrush" it's outside the polygon,
|
|||
/// For <see cref="RadialGradientBrush{TPixel}" /> and <see cref="EllipticGradientBrush{TPixel}" /> it's beyond 1.0.
|
|||
/// </summary>
|
|||
DontFill |
|||
} |
|||
} |
|||
@ -0,0 +1,152 @@ |
|||
using System; |
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes |
|||
{ |
|||
/// <summary>
|
|||
/// Provides an implementation of a brush for painting linear gradients within areas.
|
|||
/// Supported right now:
|
|||
/// - a set of colors in relative distances to each other.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format</typeparam>
|
|||
public sealed class LinearGradientBrush<TPixel> : GradientBrushBase<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly Point p1; |
|||
|
|||
private readonly Point p2; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="LinearGradientBrush{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="p1">Start point</param>
|
|||
/// <param name="p2">End point</param>
|
|||
/// <param name="repetitionMode">defines how colors are repeated.</param>
|
|||
/// <param name="colorStops"><inheritdoc /></param>
|
|||
public LinearGradientBrush( |
|||
Point p1, |
|||
Point p2, |
|||
GradientRepetitionMode repetitionMode, |
|||
params ColorStop<TPixel>[] colorStops) |
|||
: base(repetitionMode, colorStops) |
|||
{ |
|||
this.p1 = p1; |
|||
this.p2 = p2; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public override BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options) |
|||
=> new LinearGradientBrushApplicator(source, this.p1, this.p2, this.ColorStops, this.RepetitionMode, options); |
|||
|
|||
/// <summary>
|
|||
/// The linear gradient brush applicator.
|
|||
/// </summary>
|
|||
private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase |
|||
{ |
|||
private readonly Point start; |
|||
|
|||
private readonly Point end; |
|||
|
|||
/// <summary>
|
|||
/// the vector along the gradient, x component
|
|||
/// </summary>
|
|||
private readonly float alongX; |
|||
|
|||
/// <summary>
|
|||
/// the vector along the gradient, y component
|
|||
/// </summary>
|
|||
private readonly float alongY; |
|||
|
|||
/// <summary>
|
|||
/// the vector perpendicular to the gradient, y component
|
|||
/// </summary>
|
|||
private readonly float acrossY; |
|||
|
|||
/// <summary>
|
|||
/// the vector perpendicular to the gradient, x component
|
|||
/// </summary>
|
|||
private readonly float acrossX; |
|||
|
|||
/// <summary>
|
|||
/// the result of <see cref="alongX"/>^2 + <see cref="alongY"/>^2
|
|||
/// </summary>
|
|||
private readonly float alongsSquared; |
|||
|
|||
/// <summary>
|
|||
/// the length of the defined gradient (between source and end)
|
|||
/// </summary>
|
|||
private readonly float length; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="LinearGradientBrushApplicator" /> class.
|
|||
/// </summary>
|
|||
/// <param name="source">The source</param>
|
|||
/// <param name="start">start point of the gradient</param>
|
|||
/// <param name="end">end point of the gradient</param>
|
|||
/// <param name="colorStops">tuple list of colors and their respective position between 0 and 1 on the line</param>
|
|||
/// <param name="repetitionMode">defines how the gradient colors are repeated.</param>
|
|||
/// <param name="options">the graphics options</param>
|
|||
public LinearGradientBrushApplicator( |
|||
ImageFrame<TPixel> source, |
|||
Point start, |
|||
Point end, |
|||
ColorStop<TPixel>[] colorStops, |
|||
GradientRepetitionMode repetitionMode, |
|||
GraphicsOptions options) |
|||
: base(source, options, colorStops, repetitionMode) |
|||
{ |
|||
this.start = start; |
|||
this.end = end; |
|||
|
|||
// the along vector:
|
|||
this.alongX = this.end.X - this.start.X; |
|||
this.alongY = this.end.Y - this.start.Y; |
|||
|
|||
// the cross vector:
|
|||
this.acrossX = this.alongY; |
|||
this.acrossY = -this.alongX; |
|||
|
|||
// some helpers:
|
|||
this.alongsSquared = (this.alongX * this.alongX) + (this.alongY * this.alongY); |
|||
this.length = (float)Math.Sqrt(this.alongsSquared); |
|||
} |
|||
|
|||
protected override float PositionOnGradient(int x, int y) |
|||
{ |
|||
if (this.acrossX == 0) |
|||
{ |
|||
return (x - this.start.X) / (float)(this.end.X - this.start.X); |
|||
} |
|||
else if (this.acrossY == 0) |
|||
{ |
|||
return (y - this.start.Y) / (float)(this.end.Y - this.start.Y); |
|||
} |
|||
else |
|||
{ |
|||
float deltaX = x - this.start.X; |
|||
float deltaY = y - this.start.Y; |
|||
float k = ((this.alongY * deltaX) - (this.alongX * deltaY)) / this.alongsSquared; |
|||
|
|||
// point on the line:
|
|||
float x4 = x - (k * this.alongY); |
|||
float y4 = y + (k * this.alongX); |
|||
|
|||
// get distance from (x4,y4) to start
|
|||
float distance = (float)Math.Sqrt( |
|||
Math.Pow(x4 - this.start.X, 2) |
|||
+ Math.Pow(y4 - this.start.Y, 2)); |
|||
|
|||
// get and return ratio
|
|||
float ratio = distance / this.length; |
|||
return ratio; |
|||
} |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
using System; |
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes |
|||
{ |
|||
/// <summary>
|
|||
/// A Circular Gradient Brush, defined by center point and radius.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
public sealed class RadialGradientBrush<TPixel> : GradientBrushBase<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly Point center; |
|||
|
|||
private readonly float radius; |
|||
|
|||
/// <inheritdoc cref="GradientBrushBase{TPixel}" />
|
|||
/// <param name="center">The center of the circular gradient and 0 for the color stops.</param>
|
|||
/// <param name="radius">The radius of the circular gradient and 1 for the color stops.</param>
|
|||
/// <param name="repetitionMode">Defines how the colors in the gradient are repeated.</param>
|
|||
/// <param name="colorStops">the color stops as defined in base class.</param>
|
|||
public RadialGradientBrush( |
|||
Point center, |
|||
float radius, |
|||
GradientRepetitionMode repetitionMode, |
|||
params ColorStop<TPixel>[] colorStops) |
|||
: base(repetitionMode, colorStops) |
|||
{ |
|||
this.center = center; |
|||
this.radius = radius; |
|||
} |
|||
|
|||
/// <inheritdoc cref="CreateApplicator" />
|
|||
public override BrushApplicator<TPixel> CreateApplicator( |
|||
ImageFrame<TPixel> source, |
|||
RectangleF region, |
|||
GraphicsOptions options) => |
|||
new RadialGradientBrushApplicator( |
|||
source, |
|||
options, |
|||
this.center, |
|||
this.radius, |
|||
this.ColorStops, |
|||
this.RepetitionMode); |
|||
|
|||
/// <inheritdoc />
|
|||
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase |
|||
{ |
|||
private readonly Point center; |
|||
|
|||
private readonly float radius; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
|
|||
/// </summary>
|
|||
/// <param name="target">The target image</param>
|
|||
/// <param name="options">The options.</param>
|
|||
/// <param name="center">Center point of the gradient.</param>
|
|||
/// <param name="radius">Radius of the gradient.</param>
|
|||
/// <param name="colorStops">Definition of colors.</param>
|
|||
/// <param name="repetitionMode">How the colors are repeated beyond the first gradient.</param>
|
|||
public RadialGradientBrushApplicator( |
|||
ImageFrame<TPixel> target, |
|||
GraphicsOptions options, |
|||
Point center, |
|||
float radius, |
|||
ColorStop<TPixel>[] colorStops, |
|||
GradientRepetitionMode repetitionMode) |
|||
: base(target, options, colorStops, repetitionMode) |
|||
{ |
|||
this.center = center; |
|||
this.radius = radius; |
|||
} |
|||
|
|||
/// <inheritdoc cref="Dispose" />
|
|||
public override void Dispose() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// As this is a circular gradient, the position on the gradient is based on
|
|||
/// the distance of the point to the center.
|
|||
/// </summary>
|
|||
/// <param name="x">The X coordinate of the target pixel.</param>
|
|||
/// <param name="y">The Y coordinate of the target pixel.</param>
|
|||
/// <returns>the position on the color gradient.</returns>
|
|||
protected override float PositionOnGradient(int x, int y) |
|||
{ |
|||
float distance = (float)Math.Sqrt(Math.Pow(this.center.X - x, 2) + Math.Pow(this.center.Y - y, 2)); |
|||
return distance / this.radius; |
|||
} |
|||
|
|||
internal override void Apply(Span<float> scanline, int x, int y) |
|||
{ |
|||
// TODO: each row is symmetric across center, so we can calculate half of it and mirror it to improve performance.
|
|||
base.Apply(scanline, x, y); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,55 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="byte"/> struct buffers.
|
|||
/// </summary>
|
|||
internal static class ByteExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Returns a reference to the given position of the array unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
|
|||
/// </summary>
|
|||
/// <param name="bytes">The byte array.</param>
|
|||
/// <param name="offset">The offset in bytes.</param>
|
|||
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ref Rgb24 GetRgb24(this byte[] bytes, int offset) |
|||
{ |
|||
DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset)); |
|||
|
|||
return ref Unsafe.As<byte, Rgb24>(ref bytes[offset]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a reference to the given position of the span unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
|
|||
/// </summary>
|
|||
/// <param name="bytes">The byte span.</param>
|
|||
/// <param name="offset">The offset in bytes.</param>
|
|||
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ref Rgb24 GetRgb24(this Span<byte> bytes, int offset) |
|||
{ |
|||
DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset)); |
|||
|
|||
return ref Unsafe.As<byte, Rgb24>(ref bytes[offset]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a reference to the given position of the buffer pointed by `baseRef` unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
|
|||
/// </summary>
|
|||
/// <param name="baseRef">A reference to the beginning of the buffer</param>
|
|||
/// <param name="offset">The offset in bytes.</param>
|
|||
/// <returns>The <see cref="ImageSharp.PixelFormats.Rgb24"/> reference at the given offset.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ref Rgb24 GetRgb24(ref byte baseRef, int offset) |
|||
{ |
|||
return ref Unsafe.As<byte, Rgb24>(ref Unsafe.Add(ref baseRef, offset)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +1,10 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
/// <summary>
|
|||
/// Common interface to represent raw Jpeg components.
|
|||
@ -1,13 +1,16 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
/// <inheritdoc />
|
|||
/// <summary>
|
|||
/// Represents decompressed, unprocessed jpeg data with spectral space <see cref="T:SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.IJpegComponent" />-s.
|
|||
/// Represents decompressed, unprocessed jpeg data with spectral space <see cref="IJpegComponent" />-s.
|
|||
/// </summary>
|
|||
internal interface IRawJpegData : IDisposable |
|||
{ |
|||
@ -1,4 +1,7 @@ |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies the colorspace of a Jpeg image
|
|||
@ -1,8 +1,12 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates postprocessing data for one component for <see cref="JpegImagePostProcessor"/>.
|
|||
@ -1,18 +1,16 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Poor man's stackalloc: Contains a value-type <see cref="float"/> buffer sized for 4 <see cref="Common.Block8x8F"/> instances.
|
|||
/// Poor man's stackalloc: Contains a value-type <see cref="float"/> buffer sized for 4 <see cref="Block8x8F"/> instances.
|
|||
/// Useful for decoder/encoder operations allocating a block for each Jpeg component.
|
|||
/// </summary>
|
|||
internal unsafe struct BlockQuad |
|||
{ |
|||
/// <summary>
|
|||
/// The value-type <see cref="float"/> buffer sized for 4 <see cref="Common.Block8x8F"/> instances.
|
|||
/// The value-type <see cref="float"/> buffer sized for 4 <see cref="Block8x8F"/> instances.
|
|||
/// </summary>
|
|||
public fixed float Data[4 * Block8x8F.Size]; |
|||
} |
|||
@ -1,7 +1,7 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the Huffman tables
|
|||
@ -1,7 +1,7 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// A compiled look-up table representation of a huffmanSpec.
|
|||
@ -1,7 +1,7 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// The Huffman encoding specifications.
|
|||
@ -1,7 +1,7 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the quantization tables
|
|||
@ -1,10 +1,10 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Common; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
|
|||
@ -1,11 +1,8 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
// <auto-generated />
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal unsafe partial struct GenericBlock8x8<T> |
|||
{ |
|||
@ -1,10 +1,11 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
/// <summary>
|
|||
/// Holds the Jpeg UnZig array in a value/stack type.
|
|||
@ -1,189 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort |
|||
{ |
|||
/// <summary>
|
|||
/// Defines jpeg constants defined in the specification.
|
|||
/// </summary>
|
|||
internal static class OrigJpegConstants |
|||
{ |
|||
/// <summary>
|
|||
/// The maximum allowable length in each dimension of a jpeg image.
|
|||
/// </summary>
|
|||
public const ushort MaxLength = 65535; |
|||
|
|||
/// <summary>
|
|||
/// The list of mimetypes that equate to a jpeg.
|
|||
/// </summary>
|
|||
public static readonly IEnumerable<string> MimeTypes = new[] { "image/jpeg", "image/pjpeg" }; |
|||
|
|||
/// <summary>
|
|||
/// The list of file extensions that equate to a jpeg.
|
|||
/// </summary>
|
|||
public static readonly IEnumerable<string> FileExtensions = new[] { "jpg", "jpeg", "jfif" }; |
|||
|
|||
/// <summary>
|
|||
/// Describes common Jpeg markers
|
|||
/// </summary>
|
|||
internal static class Markers |
|||
{ |
|||
/// <summary>
|
|||
/// Marker prefix. Next byte is a marker.
|
|||
/// </summary>
|
|||
public const byte XFF = 0xff; |
|||
|
|||
/// <summary>
|
|||
/// Same as <see cref="XFF"/> but of type <see cref="int"/>
|
|||
/// </summary>
|
|||
public const int XFFInt = XFF; |
|||
|
|||
/// <summary>
|
|||
/// Start of Image
|
|||
/// </summary>
|
|||
public const byte SOI = 0xd8; |
|||
|
|||
/// <summary>
|
|||
/// Start of Frame (baseline DCT)
|
|||
/// <remarks>
|
|||
/// Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components,
|
|||
/// and component subsampling (e.g., 4:2:0).
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte SOF0 = 0xc0; |
|||
|
|||
/// <summary>
|
|||
/// Start Of Frame (Extended Sequential DCT)
|
|||
/// <remarks>
|
|||
/// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
|
|||
/// and component subsampling (e.g., 4:2:0).
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte SOF1 = 0xc1; |
|||
|
|||
/// <summary>
|
|||
/// Start Of Frame (progressive DCT)
|
|||
/// <remarks>
|
|||
/// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
|
|||
/// and component subsampling (e.g., 4:2:0).
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte SOF2 = 0xc2; |
|||
|
|||
/// <summary>
|
|||
/// Define Huffman Table(s)
|
|||
/// <remarks>
|
|||
/// Specifies one or more Huffman tables.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte DHT = 0xc4; |
|||
|
|||
/// <summary>
|
|||
/// Define Quantization Table(s)
|
|||
/// <remarks>
|
|||
/// Specifies one or more quantization tables.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte DQT = 0xdb; |
|||
|
|||
/// <summary>
|
|||
/// Define Restart Interval
|
|||
/// <remarks>
|
|||
/// Specifies the interval between RSTn markers, in macroblocks. This marker is followed by two bytes
|
|||
/// indicating the fixed size so it can be treated like any other variable size segment.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte DRI = 0xdd; |
|||
|
|||
/// <summary>
|
|||
/// Define First Restart
|
|||
/// <remarks>
|
|||
/// Inserted every r macroblocks, where r is the restart interval set by a DRI marker.
|
|||
/// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte RST0 = 0xd0; |
|||
|
|||
/// <summary>
|
|||
/// Define Eigth Restart
|
|||
/// <remarks>
|
|||
/// Inserted every r macroblocks, where r is the restart interval set by a DRI marker.
|
|||
/// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte RST7 = 0xd7; |
|||
|
|||
/// <summary>
|
|||
/// Start of Scan
|
|||
/// <remarks>
|
|||
/// Begins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan.
|
|||
/// Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it
|
|||
/// will contain, and is immediately followed by entropy-coded data.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte SOS = 0xda; |
|||
|
|||
/// <summary>
|
|||
/// Comment
|
|||
/// <remarks>
|
|||
/// Contains a text comment.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
public const byte COM = 0xfe; |
|||
|
|||
/// <summary>
|
|||
/// End of Image
|
|||
/// </summary>
|
|||
public const byte EOI = 0xd9; |
|||
|
|||
/// <summary>
|
|||
/// Application specific marker for marking the jpeg format.
|
|||
/// <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html"/>
|
|||
/// </summary>
|
|||
public const byte APP0 = 0xe0; |
|||
|
|||
/// <summary>
|
|||
/// Application specific marker for marking where to store metadata.
|
|||
/// </summary>
|
|||
public const byte APP1 = 0xe1; |
|||
|
|||
/// <summary>
|
|||
/// Application specific marker for marking where to store ICC profile information.
|
|||
/// </summary>
|
|||
public const byte APP2 = 0xe2; |
|||
|
|||
/// <summary>
|
|||
/// Application specific marker used by Adobe for storing encoding information for DCT filters.
|
|||
/// </summary>
|
|||
public const byte APP14 = 0xee; |
|||
|
|||
/// <summary>
|
|||
/// Application specific marker used by GraphicConverter to store JPEG quality.
|
|||
/// </summary>
|
|||
public const byte APP15 = 0xef; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Describes Adobe specific markers <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe"/>
|
|||
/// </summary>
|
|||
internal static class Adobe |
|||
{ |
|||
/// <summary>
|
|||
/// The color transform is unknown.(RGB or CMYK)
|
|||
/// </summary>
|
|||
public const int ColorTransformUnknown = 0; |
|||
|
|||
/// <summary>
|
|||
/// The color transform is YCbCr (luminance, red chroma, blue chroma)
|
|||
/// </summary>
|
|||
public const int ColorTransformYCbCr = 1; |
|||
|
|||
/// <summary>
|
|||
/// The color transform is YCCK (luminance, red chroma, blue chroma, keyline)
|
|||
/// </summary>
|
|||
public const int ColorTransformYcck = 2; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,238 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
// TODO: This could be useful elsewhere.
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
|||
{ |
|||
/// <summary>
|
|||
/// A stream reader that add a secondary level buffer in addition to native stream buffered reading
|
|||
/// to reduce the overhead of small incremental reads.
|
|||
/// </summary>
|
|||
internal class DoubleBufferedStreamReader : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// The length, in bytes, of the buffering chunk
|
|||
/// </summary>
|
|||
public const int ChunkLength = 4096; |
|||
|
|||
private const int ChunkLengthMinusOne = ChunkLength - 1; |
|||
|
|||
private readonly Stream stream; |
|||
|
|||
private readonly IManagedByteBuffer managedBuffer; |
|||
|
|||
private readonly byte[] bufferChunk; |
|||
|
|||
private readonly int length; |
|||
|
|||
private int bytesRead; |
|||
|
|||
private int position; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DoubleBufferedStreamReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
|
|||
/// <param name="stream">The input stream.</param>
|
|||
public DoubleBufferedStreamReader(MemoryManager memoryManager, Stream stream) |
|||
{ |
|||
this.stream = stream; |
|||
this.length = (int)stream.Length; |
|||
this.managedBuffer = memoryManager.AllocateCleanManagedByteBuffer(ChunkLength); |
|||
this.bufferChunk = this.managedBuffer.Array; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the length, in bytes, of the stream
|
|||
/// </summary>
|
|||
public long Length => this.length; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the current position within the stream
|
|||
/// </summary>
|
|||
public long Position |
|||
{ |
|||
get => this.position; |
|||
|
|||
set |
|||
{ |
|||
// Reset everything. It's easier than tracking.
|
|||
this.position = (int)value; |
|||
this.stream.Seek(this.position, SeekOrigin.Begin); |
|||
this.bytesRead = ChunkLength; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a byte from the stream and advances the position within the stream by one
|
|||
/// byte, or returns -1 if at the end of the stream.
|
|||
/// </summary>
|
|||
/// <returns>The unsigned byte cast to an <see cref="int"/>, or -1 if at the end of the stream.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public int ReadByte() |
|||
{ |
|||
if (this.position >= this.length) |
|||
{ |
|||
return -1; |
|||
} |
|||
|
|||
if (this.position == 0 || this.bytesRead > ChunkLengthMinusOne) |
|||
{ |
|||
return this.ReadByteSlow(); |
|||
} |
|||
|
|||
this.position++; |
|||
return this.bufferChunk[this.bytesRead++]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Skips the number of bytes in the stream
|
|||
/// </summary>
|
|||
/// <param name="count">The number of bytes to skip</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void Skip(int count) |
|||
{ |
|||
this.Position += count; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a sequence of bytes from the current stream and advances the position within the stream
|
|||
/// by the number of bytes read.
|
|||
/// </summary>
|
|||
/// <param name="buffer">
|
|||
/// An array of bytes. When this method returns, the buffer contains the specified
|
|||
/// byte array with the values between offset and (offset + count - 1) replaced by
|
|||
/// the bytes read from the current source.
|
|||
/// </param>
|
|||
/// <param name="offset">
|
|||
/// The zero-based byte offset in buffer at which to begin storing the data read
|
|||
/// from the current stream.
|
|||
/// </param>
|
|||
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
|
|||
/// <returns>
|
|||
/// The total number of bytes read into the buffer. This can be less than the number
|
|||
/// of bytes requested if that many bytes are not currently available, or zero (0)
|
|||
/// if the end of the stream has been reached.
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public int Read(byte[] buffer, int offset, int count) |
|||
{ |
|||
if (buffer.Length > ChunkLength) |
|||
{ |
|||
return this.ReadToBufferSlow(buffer, offset, count); |
|||
} |
|||
|
|||
if (this.position == 0 || count + this.bytesRead > ChunkLength) |
|||
{ |
|||
return this.ReadToChunkSlow(buffer, offset, count); |
|||
} |
|||
|
|||
int n = this.GetCount(count); |
|||
this.CopyBytes(buffer, offset, n); |
|||
|
|||
this.position += n; |
|||
this.bytesRead += n; |
|||
|
|||
return n; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Dispose() |
|||
{ |
|||
this.managedBuffer?.Dispose(); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
private int ReadByteSlow() |
|||
{ |
|||
if (this.position != this.stream.Position) |
|||
{ |
|||
this.stream.Seek(this.position, SeekOrigin.Begin); |
|||
} |
|||
|
|||
this.stream.Read(this.bufferChunk, 0, ChunkLength); |
|||
this.bytesRead = 0; |
|||
|
|||
this.position++; |
|||
return this.bufferChunk[this.bytesRead++]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
private int ReadToChunkSlow(byte[] buffer, int offset, int count) |
|||
{ |
|||
// Refill our buffer then copy.
|
|||
if (this.position != this.stream.Position) |
|||
{ |
|||
this.stream.Seek(this.position, SeekOrigin.Begin); |
|||
} |
|||
|
|||
this.stream.Read(this.bufferChunk, 0, ChunkLength); |
|||
this.bytesRead = 0; |
|||
|
|||
int n = this.GetCount(count); |
|||
this.CopyBytes(buffer, offset, n); |
|||
|
|||
this.position += n; |
|||
this.bytesRead += n; |
|||
|
|||
return n; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
private int ReadToBufferSlow(byte[] buffer, int offset, int count) |
|||
{ |
|||
// Read to target but don't copy to our chunk.
|
|||
if (this.position != this.stream.Position) |
|||
{ |
|||
this.stream.Seek(this.position, SeekOrigin.Begin); |
|||
} |
|||
|
|||
int n = this.stream.Read(buffer, offset, count); |
|||
this.Position += n; |
|||
return n; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private int GetCount(int count) |
|||
{ |
|||
int n = this.length - this.position; |
|||
if (n > count) |
|||
{ |
|||
n = count; |
|||
} |
|||
|
|||
if (n < 0) |
|||
{ |
|||
n = 0; |
|||
} |
|||
|
|||
return n; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private void CopyBytes(byte[] buffer, int offset, int count) |
|||
{ |
|||
if (count < 9) |
|||
{ |
|||
int byteCount = count; |
|||
int read = this.bytesRead; |
|||
byte[] chunk = this.bufferChunk; |
|||
|
|||
while (--byteCount > -1) |
|||
{ |
|||
buffer[offset + byteCount] = chunk[read + byteCount]; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
Buffer.BlockCopy(this.bufferChunk, this.bytesRead, buffer, offset, count); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats |
|||
{ |
|||
/// <summary>
|
|||
/// Provides enumeration of available PNG filter methods.
|
|||
/// </summary>
|
|||
public enum PngFilterMethod |
|||
{ |
|||
/// <summary>
|
|||
/// With the None filter, the scanline is transmitted unmodified.
|
|||
/// </summary>
|
|||
None, |
|||
|
|||
/// <summary>
|
|||
/// The Sub filter transmits the difference between each byte and the value of the corresponding
|
|||
/// byte of the prior pixel.
|
|||
/// </summary>
|
|||
Sub, |
|||
|
|||
/// <summary>
|
|||
/// The Up filter is just like the <see cref="Sub"/> filter except that the pixel immediately above the current pixel,
|
|||
/// rather than just to its left, is used as the predictor.
|
|||
/// </summary>
|
|||
Up, |
|||
|
|||
/// <summary>
|
|||
/// The Average filter uses the average of the two neighboring pixels (left and above) to predict the value of a pixel.
|
|||
/// </summary>
|
|||
Average, |
|||
|
|||
/// <summary>
|
|||
/// The Paeth filter computes a simple linear function of the three neighboring pixels (left, above, upper left),
|
|||
/// then chooses as predictor the neighboring pixel closest to the computed value.
|
|||
/// </summary>
|
|||
Paeth, |
|||
|
|||
/// <summary>
|
|||
/// Computes the output scanline using all five filters, and selects the filter that gives the smallest sum of
|
|||
/// absolute values of outputs.
|
|||
/// This method usually outperforms any single fixed filter choice.
|
|||
/// </summary>
|
|||
Adaptive, |
|||
} |
|||
} |
|||
@ -1,115 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates an imaged collection of frames.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
public interface IImageFrameCollection<TPixel> : IEnumerable<ImageFrame<TPixel>> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the count.
|
|||
/// </summary>
|
|||
int Count { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the root frame.
|
|||
/// </summary>
|
|||
ImageFrame<TPixel> RootFrame { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="ImageFrame{TPixel}"/> at the specified index.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The <see cref="ImageFrame{TPixel}"/>.
|
|||
/// </value>
|
|||
/// <param name="index">The index.</param>
|
|||
/// <returns>The <see cref="ImageFrame{TPixel}"/> at the specified index.</returns>
|
|||
ImageFrame<TPixel> this[int index] { get; } |
|||
|
|||
/// <summary>
|
|||
/// Clones the the frame at <paramref name="index"/> and generates a new images with all the same metadata from the orgional but with only the single frame on it.
|
|||
/// </summary>
|
|||
/// <param name="index"> The zero-based index at which item should be removed.</param>
|
|||
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
|||
/// <returns>The new <see cref="Image{TPixel}"/> with only the one frame on it.</returns>
|
|||
Image<TPixel> CloneFrame(int index); |
|||
|
|||
/// <summary>
|
|||
/// Removed the frame at <paramref name="index"/> and generates a new images with all the same metadata from the orgional but with only the single frame on it.
|
|||
/// </summary>
|
|||
/// <param name="index"> The zero-based index at which item should be removed.</param>
|
|||
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
|||
/// <returns>The new <see cref="Image{TPixel}"/> with only the one frame on it.</returns>
|
|||
Image<TPixel> ExportFrame(int index); |
|||
|
|||
/// <summary>
|
|||
/// Remove the frame at <paramref name="index"/> and frees all freeable resources associated with it.
|
|||
/// </summary>
|
|||
/// <param name="index"> The zero-based index at which item should be removed.</param>
|
|||
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
|||
void RemoveFrame(int index); |
|||
|
|||
/// <summary>
|
|||
/// Creates a new <seealso cref="ImageFrame{TPixel}"/> and appends it appends it to the end of the collection.
|
|||
/// </summary>
|
|||
/// <returns>The new <see cref="ImageFrame{TPixel}"/>.</returns>
|
|||
ImageFrame<TPixel> CreateFrame(); |
|||
|
|||
/// <summary>
|
|||
/// Clones the <paramref name="source"/> frame and appends the clone to the end of the collection.
|
|||
/// </summary>
|
|||
/// <param name="source">The raw pixel data to generate <seealso cref="ImageFrame{TPixel}"/> from.</param>
|
|||
/// <returns>The cloned <see cref="ImageFrame{TPixel}"/>.</returns>
|
|||
ImageFrame<TPixel> AddFrame(ImageFrame<TPixel> source); |
|||
|
|||
/// <summary>
|
|||
/// Creates a new frame from the pixel data at the same dimensions at the current image and inserts the new frame
|
|||
/// into the <seealso cref="Image{TPixel}"/> at the end of the collection.
|
|||
/// </summary>
|
|||
/// <param name="source">The raw pixel data to generate <seealso cref="ImageFrame{TPixel}"/> from.</param>
|
|||
/// <returns>The new <see cref="ImageFrame{TPixel}"/>.</returns>
|
|||
ImageFrame<TPixel> AddFrame(TPixel[] source); |
|||
|
|||
/// <summary>
|
|||
/// Clones and inserts the <paramref name="source"/> into the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>.
|
|||
/// </summary>
|
|||
/// <param name="index"> The zero-based index at which item should be inserted.</param>
|
|||
/// <param name="source">The <seealso cref="ImageFrame{TPixel}"/> to clone and insert into the <seealso cref="Image{TPixel}"/>.</param>
|
|||
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception>
|
|||
/// <returns>The cloned <see cref="ImageFrame{TPixel}"/>.</returns>
|
|||
ImageFrame<TPixel> InsertFrame(int index, ImageFrame<TPixel> source); |
|||
|
|||
/// <summary>
|
|||
/// Moves a <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index to the other index.
|
|||
/// </summary>
|
|||
/// <param name="sourceIndex">The zero-based index of the item to move.</param>
|
|||
/// <param name="destinationIndex">The zero-based index of the new index that should be inserted at.</param>
|
|||
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
|||
void MoveFrame(int sourceIndex, int destinationIndex); |
|||
|
|||
/// <summary>
|
|||
/// Determines the index of a specific <paramref name="frame"/> in the <seealso cref="Image{TPixel}"/>.
|
|||
/// </summary>
|
|||
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="Image{TPixel}"/>.</param>
|
|||
/// <returns>The index of item if found in the list; otherwise, -1.</returns>
|
|||
int IndexOf(ImageFrame<TPixel> frame); |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>.
|
|||
/// </summary>
|
|||
/// <param name="frame">The frame.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c> if the <seealso cref="Image{TPixel}"/> the specified frame; otherwise, <c>false</c>.
|
|||
/// </returns>
|
|||
bool Contains(ImageFrame<TPixel> frame); |
|||
} |
|||
} |
|||
@ -1,48 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Memory |
|||
{ |
|||
/// <summary>
|
|||
/// Utility methods for <see cref="Span{T}"/>
|
|||
/// </summary>
|
|||
internal static class SpanHelper |
|||
{ |
|||
/// <summary>
|
|||
/// Copy all elements of 'source' into 'destination'.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The element type.</typeparam>
|
|||
/// <param name="source">The <see cref="Span{T}"/> to copy elements from.</param>
|
|||
/// <param name="destination">The destination <see cref="Span{T}"/>.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Copy<T>(ReadOnlySpan<T> source, Span<T> destination) |
|||
where T : struct |
|||
{ |
|||
source.Slice(0, Math.Min(source.Length, destination.Length)).CopyTo(destination); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of `count` elements in bytes.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The element type.</typeparam>
|
|||
/// <param name="count">The count of the elements</param>
|
|||
/// <returns>The size in bytes as int</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int SizeOf<T>(int count) |
|||
where T : struct => Unsafe.SizeOf<T>() * count; |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of `count` elements in bytes as UInt32
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The element type.</typeparam>
|
|||
/// <param name="count">The count of the elements</param>
|
|||
/// <returns>The size in bytes as UInt32</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static uint USizeOf<T>(int count) |
|||
where T : struct |
|||
=> (uint)SizeOf<T>(count); |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue