Browse Source

Merge branch 'Core' of https://github.com/JimBobSquarePants/ImageProcessor into Core

Former-commit-id: 84515130e5aee9a6c6738e67c5457479c159dccb
Former-commit-id: d70c60799b0ecb9a6791e3a8691da127e16013bb
Former-commit-id: 5273fd493554ac2641df94b18f880a523213bd9f
pull/1/head
James Jackson-South 10 years ago
parent
commit
9eacf8e370
  1. 9
      README.md
  2. 28
      src/ImageProcessorCore/Common/Helpers/ImageMaths.cs
  3. 67
      src/ImageProcessorCore/Numerics/Point.cs
  4. 37
      src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs
  5. 3
      src/ImageProcessorCore/Samplers/Resize.cs
  6. 106
      src/ImageProcessorCore/Samplers/Rotate.cs
  7. 26
      src/ImageProcessorCore/Samplers/RotateFlip.cs
  8. 13
      src/ImageProcessorCore/Samplers/Skew.cs
  9. 31
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
  10. 19
      tests/TestWebsites/MVC/Properties/launchSettings.json

9
README.md

@ -72,7 +72,7 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Size
- [x] Point
- [x] Ellipse
- Resampling algorithms. (Optional gamma correction, Performance improvements?)
- Resampling algorithms. (Optional gamma correction, resize modes, Performance improvements?)
- [x] Box
- [x] Bicubic
- [x] Lanczos3
@ -86,13 +86,18 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Spline
- [x] Triangle
- [x] Welch
- Padding
- [x] Pad
- [x] ResizeMode.Pad
- [x] ResizeMode.BoxPad
- Cropping
- [x] Rectangular Crop
- [ ] Elliptical Crop
- [x] Entropy Crop
- [x] ResizeMode.Crop
- Rotation/Skew
- [x] Flip (90, 270, FlipType etc)
- [x] Rotate by angle and center point.
- [x] Rotate by angle and center point (Expandable canvas).
- [x] Skew by x/y angles and center point.
- ColorMatrix operations (Uses Matrix4x4)
- [x] BlackWhite

28
src/ImageProcessorCore/Common/Helpers/ImageMaths.cs

@ -112,15 +112,13 @@ namespace ImageProcessorCore
/// <summary>
/// Returns the given degrees converted to radians.
/// </summary>
/// <param name="angleInDegrees">
/// The angle in degrees.
/// </param>
/// <param name="degrees">The angle in degrees.</param>
/// <returns>
/// The <see cref="double"/> representing the degree as radians.
/// The <see cref="float"/> representing the degree as radians.
/// </returns>
public static double DegreesToRadians(double angleInDegrees)
public static float DegreesToRadians(float degrees)
{
return angleInDegrees * (PI / 180);
return degrees * (PI / 180);
}
/// <summary>
@ -140,6 +138,24 @@ namespace ImageProcessorCore
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
// http://gamedev.stackexchange.com/questions/22840/create-a-rectangle-struct-to-be-rotated-and-have-a-intersects-function
public static Rectangle GetBoundingRotatedRectangle(Rectangle rectangle, float degrees, Point center)
{
float radians = DegreesToRadians(degrees);
Matrix3x2 matrix = Matrix3x2.CreateRotation(radians, center.ToVector2());
Vector2 leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix);
Vector2 rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix);
Vector2 leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix);
Vector2 rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix);
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop), Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop), Vector2.Max(leftBottom, rightBottom));
// TODO: minY is wrong - negative
return new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
}
/// <summary>
/// Calculates the new size after rotation.
/// </summary>

67
src/ImageProcessorCore/Numerics/Point.cs

@ -159,26 +159,26 @@ namespace ImageProcessorCore
}
/// <summary>
/// Rotates a point around a given a rotation matrix.
/// Creates a rotation matrix for the given point and angle.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="rotation">Rotation matrix used</param>
/// <returns></returns>
public static Point Rotate( Point point, Matrix3x2 rotation )
/// <param name="origin">The origin point to rotate around</param>
/// <param name="degrees">Rotation in degrees</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
public static Matrix3x2 CreateRotation(Point origin, float degrees)
{
return new Point( Vector2.Transform( point.backingVector, rotation ) );
float radians = (float)ImageMaths.DegreesToRadians(degrees);
return Matrix3x2.CreateRotation(radians, origin.backingVector);
}
/// <summary>
/// Creates a rotation matrix for
/// Rotates a point around a given a rotation matrix.
/// </summary>
/// <param name="origion">The origin point to rotate around</param>
/// <param name="degrees">Rotation in degrees</param>
/// <returns></returns>
public static Matrix3x2 CreateRotatation( Point origin, float degrees )
/// <param name="point">The point to rotate</param>
/// <param name="rotation">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
public static Point Rotate(Point point, Matrix3x2 rotation)
{
float radians = (float)ImageMaths.DegreesToRadians( degrees );
return Matrix3x2.CreateRotation( radians, origin.backingVector );
return new Point(Vector2.Transform(point.backingVector, rotation));
}
/// <summary>
@ -187,26 +187,48 @@ namespace ImageProcessorCore
/// <param name="point">The point to rotate</param>
/// <param name="origin">The center point to rotate around.</param>
/// <param name="degrees">The angle in degrees.</param>
/// <returns></returns>
/// <returns>The rotated <see cref="Point"/></returns>
public static Point Rotate(Point point, Point origin, float degrees)
{
float radians = (float)ImageMaths.DegreesToRadians(degrees);
return new Point(Vector2.Transform(point.backingVector, Matrix3x2.CreateRotation(radians, origin.backingVector)));
return new Point(Vector2.Transform(point.backingVector, CreateRotation(origin, degrees)));
}
/// <summary>
/// Skews a point around a given origin by the specified angles in degrees.
/// Creates a skew matrix for the given point and angle.
/// </summary>
/// <param name="origin">The origin point to rotate around</param>
/// <param name="degreesX">The x-angle in degrees.</param>
/// <param name="degreesY">The y-angle in degrees.</param>
/// <returns>The rotation <see cref="Matrix3x2"/></returns>
public static Matrix3x2 CreateSkew(Point origin, float degreesX, float degreesY)
{
float radiansX = (float)ImageMaths.DegreesToRadians(degreesX);
float radiansY = (float)ImageMaths.DegreesToRadians(degreesY);
return Matrix3x2.CreateSkew(radiansX, radiansY, origin.backingVector);
}
/// <summary>
/// Skews a point using a given a skew matrix.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="skew">Rotation matrix used</param>
/// <returns>The rotated <see cref="Point"/></returns>
public static Point Skew(Point point, Matrix3x2 skew)
{
return new Point(Vector2.Transform(point.backingVector, skew));
}
/// <summary>
/// Skews a point around a given origin by the specified angles in degrees.
/// </summary>
/// <param name="point">The point to skew.</param>
/// <param name="origin">The center point to rotate around.</param>
/// <param name="degreesX">The x-angle in degrees.</param>
/// <param name="degreesY">The y-angle in degrees.</param>
/// <returns></returns>
/// <returns>The skewed <see cref="Point"/></returns>
public static Point Skew(Point point, Point origin, float degreesX, float degreesY)
{
float radiansX = (float)ImageMaths.DegreesToRadians(degreesX);
float radiansY = (float)ImageMaths.DegreesToRadians(degreesY);
return new Point(Vector2.Transform(point.backingVector, Matrix3x2.CreateSkew(degreesX, degreesY, origin.backingVector)));
return new Point(Vector2.Transform(point.backingVector, CreateSkew(origin, degreesX, degreesY)));
}
/// <inheritdoc/>
@ -223,8 +245,7 @@ namespace ImageProcessorCore
return "Point [ Empty ]";
}
return
$"Point [ X={this.X}, Y={this.Y} ]";
return $"Point [ X={this.X}, Y={this.Y} ]";
}
/// <inheritdoc/>

37
src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs

@ -42,7 +42,7 @@ namespace ImageProcessorCore.Samplers
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
if (sourceRectangle.Width < width || sourceRectangle.Height < height)
{
// If the source rectangle is smaller than the target perform a
@ -85,6 +85,26 @@ namespace ImageProcessorCore.Samplers
}
}
/// <summary>
/// Evenly pads an image to fit the new dimensions.
/// </summary>
/// <param name="source">The source image to pad.</param>
/// <param name="width">The new width.</param>
/// <param name="height">The new height.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Pad(this Image source, int width, int height, ProgressEventHandler progressHandler = null)
{
ResizeOptions options = new ResizeOptions
{
Size = new Size(width, height),
Mode = ResizeMode.BoxPad,
Sampler = new NearestNeighborResampler()
};
return Resize(source, options, progressHandler);
}
/// <summary>
/// Resizes an image in accordance with the given <see cref="ResizeOptions"/>.
/// </summary>
@ -95,7 +115,7 @@ namespace ImageProcessorCore.Samplers
/// <remarks>Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image</remarks>
public static Image Resize(this Image source, ResizeOptions options, ProgressEventHandler progressHandler = null)
{
// Ensure size is populated acros both dimensions.
// Ensure size is populated across both dimensions.
if (options.Size.Width == 0 && options.Size.Height > 0)
{
options.Size = new Size(source.Width * options.Size.Height / source.Height, options.Size.Height);
@ -203,7 +223,7 @@ namespace ImageProcessorCore.Samplers
}
/// <summary>
/// Rotates an image by the given angle in degrees.
/// Rotates an image by the given angle in degrees, expanding the image to fit the rotated result.
/// </summary>
/// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
@ -211,7 +231,7 @@ namespace ImageProcessorCore.Samplers
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
return Rotate(source, degrees, Rectangle.Center(source.Bounds), progressHandler);
return Rotate(source, degrees, Point.Empty, true, progressHandler);
}
/// <summary>
@ -219,12 +239,13 @@ namespace ImageProcessorCore.Samplers
/// </summary>
/// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="center">The center point at which to skew the image.</param>
/// <param name="center">The center point at which to rotate the image.</param>
/// <param name="expand">Whether to expand the image to fit the rotated result.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, Point center, ProgressEventHandler progressHandler = null)
public static Image Rotate(this Image source, float degrees, Point center, bool expand, ProgressEventHandler progressHandler = null)
{
Rotate processor = new Rotate { Angle = degrees, Center = center };
Rotate processor = new Rotate { Angle = degrees, Center = center, Expand = expand };
processor.OnProgress += progressHandler;
try
@ -270,7 +291,7 @@ namespace ImageProcessorCore.Samplers
/// <returns>The <see cref="Image"/></returns>
public static Image Skew(this Image source, float degreesX, float degreesY, ProgressEventHandler progressHandler = null)
{
return Skew(source, degreesX, degreesY, Rectangle.Center(source.Bounds), progressHandler);
return Skew(source, degreesX, degreesY, Point.Empty, progressHandler);
}
/// <summary>

3
src/ImageProcessorCore/Samplers/Resize.cs

@ -184,6 +184,9 @@ namespace ImageProcessorCore.Samplers
{
target.ClonePixels(target.Width, target.Height, source.Pixels);
}
// Clean up
this.firstPass?.Dispose();
}
}
}

106
src/ImageProcessorCore/Samplers/Rotate.cs

@ -3,10 +3,9 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Numerics;
namespace ImageProcessorCore.Samplers
{
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
@ -15,12 +14,20 @@ namespace ImageProcessorCore.Samplers
public class Rotate : ImageSampler
{
/// <summary>
/// The angle of rotation.
/// The image used for storing the first pass pixels.
/// </summary>
private Image firstPass;
/// <summary>
/// The angle of rotation in degrees.
/// </summary>
private float angle;
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// Gets or sets the angle of rotation.
/// Gets or sets the angle of rotation in degrees.
/// </summary>
public float Angle
{
@ -50,47 +57,84 @@ namespace ImageProcessorCore.Samplers
/// </summary>
public Point Center { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the rotated image.
/// </summary>
public bool Expand { get; set; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
{
int targetY = targetRectangle.Y;
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
float negativeAngle = -this.angle;
Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center;
// If we are expanding we need to pad the bounds of the source rectangle.
// We can use the resizer in nearest neighbor mode to do this fairly quickly.
if (this.Expand)
{
// First find out how the target rectangle should be.
Rectangle rectangle = ImageMaths.GetBoundingRotatedRectangle(source.Width, source.Height, -this.angle);
Rectangle rectangle2 = ImageMaths.GetBoundingRotatedRectangle(sourceRectangle, -this.angle, this.Center);
ResizeOptions options = new ResizeOptions
{
Size = new Size(rectangle.Width, rectangle.Height),
Mode = ResizeMode.BoxPad,
Sampler = new NearestNeighborResampler()
};
// Scaling factors
float widthFactor = source.Width / (float)target.Width;
float heightFactor = source.Height / (float)target.Height;
// Get the padded bounds and resize the image.
Rectangle bounds = ResizeHelper.CalculateTargetLocationAndBounds(source, options);
this.firstPass = new Image(rectangle.Width, rectangle.Height);
target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]);
new Resize(new NearestNeighborResampler()).Apply(this.firstPass, source, rectangle.Width, rectangle.Height, bounds, sourceRectangle);
}
else
{
// Just clone the pixels across.
this.firstPass = new Image(source.Width, source.Height);
this.firstPass.ClonePixels(source.Width, source.Height, source.Pixels);
}
}
Matrix3x2 rotation = Point.CreateRotatation( centre, negativeAngle );
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int targetY = this.firstPass.Bounds.Y;
int targetHeight = this.firstPass.Height;
int startX = this.firstPass.Bounds.X;
int endX = this.firstPass.Bounds.Right;
float negativeAngle = -this.angle;
Point centre = this.Center == Point.Empty ? Rectangle.Center(this.firstPass.Bounds) : this.Center;
Matrix3x2 rotation = Point.CreateRotation(centre, negativeAngle);
// Since we are not working in parallel we use full height and width of the first pass image.
Parallel.For(
startY,
endY,
0,
targetHeight,
y =>
{
if (y >= targetY && y < targetBottom)
// Y coordinates of source points
int originY = y - targetY;
for (int x = startX; x < endX; x++)
{
// Y coordinates of source points
int originY = (int)((y - targetY) * heightFactor);
// X coordinates of source points
int originX = x - startX;
for (int x = startX; x < endX; x++)
// Rotate at the centre point
Point rotated = Point.Rotate(new Point(originX, originY), rotation);
if (this.firstPass.Bounds.Contains(rotated.X, rotated.Y))
{
// X coordinates of source points
int originX = (int)((x - startX) * widthFactor);
// Rotate at the centre point
Point rotated = Point.Rotate(new Point(originX, originY), rotation);
if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
target[x, y] = source[rotated.X, rotated.Y];
}
target[x, y] = this.firstPass[rotated.X, rotated.Y];
}
this.OnRowProcessed();
}
this.OnRowProcessed();
});
}
/// <inheritdoc/>
protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
{
// Cleanup.
this.firstPass.Dispose();
}
}
}

26
src/ImageProcessorCore/Samplers/RotateFlip.cs

@ -2,7 +2,6 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Samplers
{
using System;
@ -43,13 +42,13 @@ namespace ImageProcessorCore.Samplers
switch (this.RotateType)
{
case RotateType.Rotate90:
Rotate90(target, source);
this.Rotate90(target, source);
break;
case RotateType.Rotate180:
Rotate180(target, source);
this.Rotate180(target, source);
break;
case RotateType.Rotate270:
Rotate270(target, source);
this.Rotate270(target, source);
break;
default:
target.ClonePixels(target.Width, target.Height, source.Pixels);
@ -60,10 +59,10 @@ namespace ImageProcessorCore.Samplers
{
// No default needed as we have already set the pixels.
case FlipType.Vertical:
FlipX(target);
this.FlipX(target);
break;
case FlipType.Horizontal:
FlipY(target);
this.FlipY(target);
break;
}
}
@ -79,7 +78,7 @@ namespace ImageProcessorCore.Samplers
int height = source.Height;
Image temp = new Image(height, width);
Parallel.For(0, height,
Parallel.For(0, height,
y =>
{
for (int x = 0; x < width; x++)
@ -90,6 +89,7 @@ namespace ImageProcessorCore.Samplers
newY = width - newY - 1;
temp[newX, newY] = source[x, y];
}
this.OnRowProcessed();
});
@ -106,7 +106,7 @@ namespace ImageProcessorCore.Samplers
int width = source.Width;
int height = source.Height;
Parallel.For(0, height,
Parallel.For(0, height,
y =>
{
for (int x = 0; x < width; x++)
@ -115,6 +115,7 @@ namespace ImageProcessorCore.Samplers
int newY = height - y - 1;
target[newX, newY] = source[x, y];
}
this.OnRowProcessed();
});
}
@ -130,7 +131,7 @@ namespace ImageProcessorCore.Samplers
int height = source.Height;
Image temp = new Image(height, width);
Parallel.For(0, height,
Parallel.For(0, height,
y =>
{
for (int x = 0; x < width; x++)
@ -138,6 +139,7 @@ namespace ImageProcessorCore.Samplers
int newX = height - y - 1;
temp[newX, x] = source[x, y];
}
this.OnRowProcessed();
});
@ -157,7 +159,7 @@ namespace ImageProcessorCore.Samplers
ImageBase temp = new Image(width, height);
temp.ClonePixels(width, height, target.Pixels);
Parallel.For(0, halfHeight,
Parallel.For(0, halfHeight,
y =>
{
for (int x = 0; x < width; x++)
@ -166,6 +168,7 @@ namespace ImageProcessorCore.Samplers
target[x, y] = temp[x, newY];
target[x, newY] = temp[x, y];
}
this.OnRowProcessed();
});
}
@ -183,7 +186,7 @@ namespace ImageProcessorCore.Samplers
ImageBase temp = new Image(width, height);
temp.ClonePixels(width, height, target.Pixels);
Parallel.For(0, height,
Parallel.For(0, height,
y =>
{
for (int x = 0; x < halfWidth; x++)
@ -192,6 +195,7 @@ namespace ImageProcessorCore.Samplers
target[x, y] = temp[newX, y];
target[newX, y] = temp[x, y];
}
this.OnRowProcessed();
});
}

13
src/ImageProcessorCore/Samplers/Skew.cs

@ -5,6 +5,7 @@
namespace ImageProcessorCore.Samplers
{
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
@ -89,10 +90,7 @@ namespace ImageProcessorCore.Samplers
float negativeAngleX = -this.angleX;
float negativeAngleY = -this.angleY;
Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center;
// Scaling factors
float widthFactor = source.Width / (float)target.Width;
float heightFactor = source.Height / (float)target.Height;
Matrix3x2 skew = Point.CreateSkew(centre, negativeAngleX, negativeAngleY);
Parallel.For(
startY,
@ -102,20 +100,21 @@ namespace ImageProcessorCore.Samplers
if (y >= targetY && y < targetBottom)
{
// Y coordinates of source points
int originY = (int)((y - targetY) * heightFactor);
int originY = y - targetY;
for (int x = startX; x < endX; x++)
{
// X coordinates of source points
int originX = (int)((x - startX) * widthFactor);
int originX = x - startX;
// Skew at the centre point
Point skewed = Point.Skew(new Point(originX, originY), centre, negativeAngleX, negativeAngleY);
Point skewed = Point.Skew(new Point(originX, originY), skew);
if (sourceRectangle.Contains(skewed.X, skewed.Y))
{
target[x, y] = source[skewed.X, skewed.Y];
}
}
this.OnRowProcessed();
}
});

31
tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

@ -76,6 +76,34 @@
}
}
[Fact]
public void ImageShouldPad()
{
if (!Directory.Exists("TestOutput/Pad"))
{
Directory.CreateDirectory("TestOutput/Pad");
}
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
string filename = Path.GetFileName(file);
using (Image image = new Image(stream))
using (FileStream output = File.OpenWrite($"TestOutput/Pad/{filename}"))
{
image.Pad(image.Width + 50, image.Height + 50, this.ProgressUpdate)
.Save(output);
}
Trace.WriteLine($"{watch.ElapsedMilliseconds}ms");
}
}
}
[Theory]
[MemberData("ReSamplers")]
public void ImageShouldResize(string name, IResampler sampler)
@ -424,6 +452,7 @@
Directory.CreateDirectory("TestOutput/Skew");
}
// Matches live example http://www.w3schools.com/css/tryit.asp?filename=trycss3_transform_skew
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
@ -435,7 +464,7 @@
using (Image image = new Image(stream))
using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}"))
{
image.Skew(45, 45, this.ProgressUpdate)
image.Skew(20, 10, this.ProgressUpdate)
.Save(output);
}

19
tests/TestWebsites/MVC/Properties/launchSettings.json

@ -0,0 +1,19 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:55993/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Loading…
Cancel
Save