Этап 9
This commit is contained in:
38
Minint.Core/Services/Impl/ImageEffectsService.cs
Normal file
38
Minint.Core/Services/Impl/ImageEffectsService.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Minint.Core.Models;
|
||||
|
||||
namespace Minint.Core.Services.Impl;
|
||||
|
||||
public sealed class ImageEffectsService : IImageEffectsService
|
||||
{
|
||||
public void ApplyContrast(MinintDocument doc, double factor)
|
||||
{
|
||||
for (int i = 1; i < doc.Palette.Count; i++)
|
||||
{
|
||||
var c = doc.Palette[i];
|
||||
doc.Palette[i] = new RgbaColor(
|
||||
ContrastByte(c.R, factor),
|
||||
ContrastByte(c.G, factor),
|
||||
ContrastByte(c.B, factor),
|
||||
c.A);
|
||||
}
|
||||
doc.InvalidatePaletteCache();
|
||||
}
|
||||
|
||||
public void ApplyGrayscale(MinintDocument doc)
|
||||
{
|
||||
for (int i = 1; i < doc.Palette.Count; i++)
|
||||
{
|
||||
var c = doc.Palette[i];
|
||||
byte gray = (byte)Math.Clamp((int)(0.299 * c.R + 0.587 * c.G + 0.114 * c.B + 0.5), 0, 255);
|
||||
doc.Palette[i] = new RgbaColor(gray, gray, gray, c.A);
|
||||
}
|
||||
doc.InvalidatePaletteCache();
|
||||
}
|
||||
|
||||
private static byte ContrastByte(byte value, double factor)
|
||||
{
|
||||
double v = ((value / 255.0) - 0.5) * factor + 0.5;
|
||||
return (byte)Math.Clamp((int)(v * 255 + 0.5), 0, 255);
|
||||
}
|
||||
}
|
||||
113
Minint.Core/Services/Impl/PatternGenerator.cs
Normal file
113
Minint.Core/Services/Impl/PatternGenerator.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using Minint.Core.Models;
|
||||
|
||||
namespace Minint.Core.Services.Impl;
|
||||
|
||||
public sealed class PatternGenerator : IPatternGenerator
|
||||
{
|
||||
public MinintDocument Generate(PatternType type, int width, int height, RgbaColor[] colors, int param1, int param2 = 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(width, 1);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(height, 1);
|
||||
if (colors.Length < 2)
|
||||
throw new ArgumentException("At least two colors are required.", nameof(colors));
|
||||
|
||||
var doc = new MinintDocument($"Pattern ({type})");
|
||||
var layer = new MinintLayer("Pattern", width * height);
|
||||
doc.Layers.Add(layer);
|
||||
|
||||
int[] colorIndices = new int[colors.Length];
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
colorIndices[i] = doc.EnsureColorCached(colors[i]);
|
||||
|
||||
int cellSize = Math.Max(param1, 1);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PatternType.Checkerboard:
|
||||
FillCheckerboard(layer.Pixels, width, height, colorIndices, cellSize);
|
||||
break;
|
||||
case PatternType.HorizontalGradient:
|
||||
FillGradient(layer.Pixels, width, height, colors[0], colors[1], doc, horizontal: true);
|
||||
break;
|
||||
case PatternType.VerticalGradient:
|
||||
FillGradient(layer.Pixels, width, height, colors[0], colors[1], doc, horizontal: false);
|
||||
break;
|
||||
case PatternType.HorizontalStripes:
|
||||
FillStripes(layer.Pixels, width, height, colorIndices, cellSize, horizontal: true);
|
||||
break;
|
||||
case PatternType.VerticalStripes:
|
||||
FillStripes(layer.Pixels, width, height, colorIndices, cellSize, horizontal: false);
|
||||
break;
|
||||
case PatternType.ConcentricCircles:
|
||||
FillCircles(layer.Pixels, width, height, colorIndices, cellSize);
|
||||
break;
|
||||
case PatternType.Tile:
|
||||
FillTile(layer.Pixels, width, height, colorIndices, cellSize, Math.Max(param2, 1));
|
||||
break;
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static void FillCheckerboard(int[] pixels, int w, int h, int[] ci, int cell)
|
||||
{
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++)
|
||||
pixels[y * w + x] = ci[((x / cell) + (y / cell)) % 2 == 0 ? 0 : 1];
|
||||
}
|
||||
|
||||
private static void FillGradient(int[] pixels, int w, int h, RgbaColor c0, RgbaColor c1,
|
||||
MinintDocument doc, bool horizontal)
|
||||
{
|
||||
int steps = horizontal ? w : h;
|
||||
for (int s = 0; s < steps; s++)
|
||||
{
|
||||
double t = steps > 1 ? (double)s / (steps - 1) : 0;
|
||||
var c = new RgbaColor(
|
||||
Lerp(c0.R, c1.R, t), Lerp(c0.G, c1.G, t),
|
||||
Lerp(c0.B, c1.B, t), Lerp(c0.A, c1.A, t));
|
||||
int idx = doc.EnsureColorCached(c);
|
||||
if (horizontal)
|
||||
for (int y = 0; y < h; y++) pixels[y * w + s] = idx;
|
||||
else
|
||||
for (int x = 0; x < w; x++) pixels[s * w + x] = idx;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillStripes(int[] pixels, int w, int h, int[] ci, int stripe, bool horizontal)
|
||||
{
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
int coord = horizontal ? y : x;
|
||||
pixels[y * w + x] = ci[(coord / stripe) % ci.Length];
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillCircles(int[] pixels, int w, int h, int[] ci, int ringWidth)
|
||||
{
|
||||
double cx = (w - 1) / 2.0, cy = (h - 1) / 2.0;
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
double dist = Math.Sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
|
||||
int ring = (int)(dist / ringWidth);
|
||||
pixels[y * w + x] = ci[ring % ci.Length];
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillTile(int[] pixels, int w, int h, int[] ci, int tileW, int tileH)
|
||||
{
|
||||
for (int y = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
int tx = (x / tileW) % ci.Length;
|
||||
int ty = (y / tileH) % ci.Length;
|
||||
pixels[y * w + x] = ci[(tx + ty) % ci.Length];
|
||||
}
|
||||
}
|
||||
|
||||
private static byte Lerp(byte a, byte b, double t)
|
||||
=> (byte)Math.Clamp((int)(a + (b - a) * t + 0.5), 0, 255);
|
||||
}
|
||||
Reference in New Issue
Block a user