···11+---
22+"release-image-generator": minor
33+---
44+55+**⚠️ Breaking change**: Move the image generation API endpoint from `/` to `/api/generateImage`. This means that you now need to add this path to your request:
66+77+```diff
88+-https://release-image-generator.trueberryless.org?text=1.0
99++https://release-image-generator.trueberryless.org/api/generateImage?text=1.0
1010+```
+5
.changeset/legal-moments-punch.md
···11+---
22+"release-image-generator": minor
33+---
44+55+Add new landing page which simplifies the generation of the correct URL query parameters. Try it out on https://release-image-generator.trueberryless.org
+5
.changeset/ninety-pillows-beam.md
···11+---
22+"release-image-generator": patch
33+---
44+55+Add new `hexagons` pattern and rename old `hexagons` pattern to `triangles` since it was not hexagons.
+5
.changeset/petite-baths-give.md
···11+---
22+"release-image-generator": patch
33+---
44+55+Migrate deployment to [Netlify](https://www.netlify.com/) to improve availability.
+5
.changeset/slow-streets-sneeze.md
···11+---
22+"release-image-generator": patch
33+---
44+55+Improve `circuitry` pattern by making sure the pattern extends over the edges of the image.
+5
.changeset/spotty-moments-walk.md
···11+---
22+"release-image-generator": patch
33+---
44+55+Add new `noiseLevel` with three options: `low`, `medium`, `high` to control how much randomness should be created over the image.
+5
.changeset/sweet-houses-stare.md
···11+---
22+"release-image-generator": minor
33+---
44+55+Migrate whole project from .NET to Astro (Typescript).
+5
.changeset/three-rings-decide.md
···11+---
22+"release-image-generator": patch
33+---
44+55+Add new `seed` option to deterministically generate colors, patterns, noise and background. Different options regarding `text`, `width`, `height`, `fontFamily`, `fontWeight` and `imageFormat` will still result in different image outcomes. Seeded images with same parameters will be cached.
···11-using System.Net.Mime;
22-using System.Reflection;
33-using ReleaseImageGenerator.Domain.Implementations;
44-using SkiaSharp;
55-using Wacton.Unicolour;
66-77-namespace ReleaseImageGenerator.Domain;
88-99-public static class TextGenerator
1010-{
1111- public static void GenerateText(SKCanvas canvas, string text, int width, int height, SupportedFontFamily fontFamily,
1212- SupportedFontWeight fontWeight, Unicolour primaryColor)
1313- {
1414- var typeface = LoadFont(fontFamily.ToString(), fontWeight.ToString());
1515- var fontsize = GetMaxFontSize(width - width / 3, typeface, text, 1f, width > height ? height / 3 : width / 3);
1616-1717- // Calculate text size and position
1818- var textPaint = new SKPaint
1919- {
2020- TextSize = fontsize,
2121- IsAntialias = true,
2222- Typeface = typeface,
2323- TextScaleX = 0.95f
2424- };
2525-2626- var textwidth = textPaint.MeasureText(text);
2727- var textBounds = new SKRect();
2828- textPaint.MeasureText(text, ref textBounds);
2929-3030- float textX = (width - textwidth) / 2;
3131- float textY = height / 2 + textBounds.Height / 2;
3232-3333- // Add light text shadow
3434- var textShadowPaint = new SKPaint
3535- {
3636- TextSize = fontsize,
3737- IsAntialias = true,
3838- Typeface = typeface,
3939- TextScaleX = 0.95f,
4040- Color = SKColors.Black.WithAlpha(80),
4141- MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, 5)
4242- };
4343- canvas.DrawText(text, textX + fontsize / 100, textY + fontsize / 100, textShadowPaint);
4444-4545- // Now set up the gradient with proper positioning
4646- textPaint.Shader = SKShader.CreateLinearGradient(
4747- new SKPoint(textX, textY + textBounds.Top),
4848- new SKPoint(textX + textwidth, textY + textBounds.Bottom),
4949- new[]
5050- {
5151- SKColors.White,
5252- ColorGenerator.UnicolourToSKColor(new Unicolour(ColourSpace.Oklch,
5353- (0.8 + Math.Max(0, (primaryColor.Oklch.L - 0.8) * 0.5), 0, 0), 1D))
5454- },
5555- SKShaderTileMode.Clamp
5656- );
5757-5858- // Draw glassmorphism background with smaller border and shadow
5959- var bgPaint = new SKPaint
6060- {
6161- Color = SKColors.White.WithAlpha(40),
6262- IsAntialias = true
6363- };
6464- var borderPaint = new SKPaint
6565- {
6666- Color = SKColors.White.WithAlpha(80),
6767- IsStroke = true,
6868- StrokeWidth = 4,
6969- IsAntialias = true
7070- };
7171-7272- var shadowPaint = new SKPaint
7373- {
7474- Color = SKColors.Black.WithAlpha(50),
7575- MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, 10)
7676- };
7777-7878- var padding = fontsize / 3;
7979- var rect = new SKRect(textX - padding, textY + textBounds.Top - padding, textX + textwidth + padding,
8080- textY + padding);
8181-8282- // Draw shadow in the direction of the light
8383- canvas.Translate(fontsize / 50, fontsize / 50);
8484- canvas.DrawRoundRect(rect, fontsize / 4, fontsize / 4, shadowPaint);
8585- canvas.Translate(-fontsize / 50, -fontsize / 50);
8686-8787- canvas.DrawRoundRect(rect, fontsize / 4, fontsize / 4, bgPaint);
8888- canvas.DrawRoundRect(rect, fontsize / 4, fontsize / 4, borderPaint);
8989-9090- // Draw the text
9191- canvas.DrawText(text, textX, textY, textPaint);
9292- }
9393-9494- private static SKTypeface LoadFont(string fontFamily, string fontWeight)
9595- {
9696- var fontName = $"{fontFamily}-{fontWeight}.ttf";
9797-9898- return SKTypeface.FromFile($"./fonts/{fontFamily}-{fontWeight}.ttf") ??
9999- SKTypeface.FromFile($"./fonts/readexpro-bold.ttf") ?? SKTypeface.Default;
100100- }
101101-102102- private static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text,
103103- float degreeOfCertainty = 1f,
104104- float maxFont = 100f)
105105- {
106106- var max = maxFont; // The upper bound. We know the font size is below this value
107107- var min = 0f; // The lower bound, We know the font size is equal to or above this value
108108- var last = -1f; // The last calculated value.
109109- float value;
110110- while (true)
111111- {
112112- value = min + ((max - min) / 2); // Find the half way point between Max and Min
113113- using (SKFont ft = new SKFont(typeface, value))
114114- using (SKPaint paint = new SKPaint(ft))
115115- {
116116- if (paint.MeasureText(text) > sectorSize) // Measure the string size at this font size
117117- {
118118- // The text size is too large
119119- // therefore the max possible size is below value
120120- last = value;
121121- max = value;
122122- }
123123- else
124124- {
125125- // The text fits within the area
126126- // therefore the min size is above or equal to value
127127- min = value;
128128-129129- // Check if this value is within our degree of certainty
130130- if (Math.Abs(last - value) <= degreeOfCertainty)
131131- return last; // Value is within certainty range, we found the best font size!
132132-133133- //This font difference is not within our degree of certainty
134134- last = value;
135135- }
136136- }
137137- }
138138- }
139139-}
-22
app/ReleaseImageGenerator.sln
···11-
22-Microsoft Visual Studio Solution File, Format Version 12.00
33-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReleaseImageGenerator.API", "ReleaseImageGenerator.API\ReleaseImageGenerator.API.csproj", "{6DAC702E-2CFB-41BA-813E-8352C60203AE}"
44-EndProject
55-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReleaseImageGenerator.Domain", "ReleaseImageGenerator.Domain\ReleaseImageGenerator.Domain.csproj", "{3042559D-EEF7-4E6D-99E0-6D604E8C31B9}"
66-EndProject
77-Global
88- GlobalSection(SolutionConfigurationPlatforms) = preSolution
99- Debug|Any CPU = Debug|Any CPU
1010- Release|Any CPU = Release|Any CPU
1111- EndGlobalSection
1212- GlobalSection(ProjectConfigurationPlatforms) = postSolution
1313- {6DAC702E-2CFB-41BA-813E-8352C60203AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1414- {6DAC702E-2CFB-41BA-813E-8352C60203AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
1515- {6DAC702E-2CFB-41BA-813E-8352C60203AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
1616- {6DAC702E-2CFB-41BA-813E-8352C60203AE}.Release|Any CPU.Build.0 = Release|Any CPU
1717- {3042559D-EEF7-4E6D-99E0-6D604E8C31B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1818- {3042559D-EEF7-4E6D-99E0-6D604E8C31B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
1919- {3042559D-EEF7-4E6D-99E0-6D604E8C31B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
2020- {3042559D-EEF7-4E6D-99E0-6D604E8C31B9}.Release|Any CPU.Build.0 = Release|Any CPU
2121- EndGlobalSection
2222-EndGlobal