Mirror of
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add patterns

+524 -14
+1 -1
ReleaseImageGenerator.API/Program.cs
··· 22 22 23 23 app.MapGet("/image-generator", (string? text, int? width, int? height, SupportedFonts? font) => 24 24 { 25 - var options = new ImageGeneratorOptions(text ?? "1.0", width ?? 1920, height ?? 1080, font ?? SupportedFonts.JETBRAINS_BOLD); 25 + var options = new ImageGeneratorOptions(text ?? "1.0", width ?? 1920, height ?? 1080, font ?? SupportedFonts.READEX_BOLD); 26 26 var imageGenerator = new ImageGenerator(options); 27 27 return Results.File(imageGenerator.GenerateImage().ToArray(), "image/jpeg"); 28 28 })
+29 -13
ReleaseImageGenerator.Domain/Implementations/ImageGenerator.cs
··· 23 23 { 24 24 using var surface = SKSurface.Create(new SKImageInfo(Width, Height)); 25 25 var canvas = surface.Canvas; 26 - 26 + 27 27 var random = new Random(); 28 28 29 29 // Generate a harmonious color palette around the primary color ··· 33 33 var colorPalette = ColorGenerator.GetRandomPalette(primaryColor, 8).Select(UnicolourToSKColor).ToArray(); 34 34 35 35 var backgroundRotationIsClockwise = random.Next(2) == 0; 36 - 36 + 37 37 // Fill with a smooth gradient background based on the palette 38 38 var paint = new SKPaint 39 39 { 40 40 Shader = SKShader.CreateLinearGradient( 41 - new SKPoint(0, random.Next(backgroundRotationIsClockwise ? -1000 : Height, backgroundRotationIsClockwise ? 0 : Height + 1000)), 42 - new SKPoint(Width, random.Next(backgroundRotationIsClockwise ? Height : -1000, backgroundRotationIsClockwise ? Height + 1000: 0)), 41 + new SKPoint(0, 42 + random.Next(backgroundRotationIsClockwise ? -1000 : Height, 43 + backgroundRotationIsClockwise ? 0 : Height + 1000)), 44 + new SKPoint(Width, 45 + random.Next(backgroundRotationIsClockwise ? Height : -1000, 46 + backgroundRotationIsClockwise ? Height + 1000 : 0)), 43 47 colorPalette, 44 48 SKShaderTileMode.Clamp 45 49 ) 46 50 }; 47 - 48 - var colorPalette2 = ColorGenerator.GetRandomPalette(primaryColor, 6, 60, 0.3D).Select(UnicolourToSKColor).ToArray(); 51 + 52 + var colorPalette2 = ColorGenerator.GetRandomPalette(primaryColor, 6, 60, 0.3D).Select(UnicolourToSKColor) 53 + .ToArray(); 49 54 var paint2 = new SKPaint 50 55 { 51 56 Shader = SKShader.CreateLinearGradient( 52 - new SKPoint(0, random.Next(!backgroundRotationIsClockwise ? -1000 : Height, !backgroundRotationIsClockwise ? 0 : Height + 1000)), 53 - new SKPoint(Width, random.Next(!backgroundRotationIsClockwise ? Height : -1000, !backgroundRotationIsClockwise ? Height + 1000: 0)), 57 + new SKPoint(0, 58 + random.Next(!backgroundRotationIsClockwise ? -1000 : Height, 59 + !backgroundRotationIsClockwise ? 0 : Height + 1000)), 60 + new SKPoint(Width, 61 + random.Next(!backgroundRotationIsClockwise ? Height : -1000, 62 + !backgroundRotationIsClockwise ? Height + 1000 : 0)), 54 63 colorPalette2, 55 64 SKShaderTileMode.Clamp 56 65 ) 57 66 }; 58 - 59 - var colorPalette3 = ColorGenerator.GetRandomPalette(primaryColor, 3, 50, 0.2D).Select(UnicolourToSKColor).ToArray(); 67 + 68 + var colorPalette3 = ColorGenerator.GetRandomPalette(primaryColor, 3, 50, 0.2D).Select(UnicolourToSKColor) 69 + .ToArray(); 60 70 var paint3 = new SKPaint 61 71 { 62 72 Shader = SKShader.CreateRadialGradient( ··· 72 82 73 83 // Add some noise 74 84 AddNoise(canvas, Width, Height); 85 + 86 + // Add some pattern 87 + PatternGenerator.AddBackgroundPatterns(canvas, Width, Height, random, primaryColor.Oklch.L); 75 88 76 89 // Load JetBrains Mono font 77 90 var typeface = Font switch ··· 84 97 SupportedFonts.JETBRAINS_LIGHT => SKTypeface.FromFile("./fonts/JetbrainsMono-Light.ttf"), 85 98 SupportedFonts.SOURCE_CODE_BOLD => SKTypeface.FromFile("./fonts/SourceCodePro-Bold.ttf"), 86 99 SupportedFonts.SOURCE_CODE_MEDIUM => SKTypeface.FromFile("./fonts/SourceCodePro-Medium.ttf"), 87 - SupportedFonts.SOURCE_CODE_LIGHT => SKTypeface.FromFile("./fonts/SourceCodePro-Light.ttf"), 100 + SupportedFonts.SOURCE_CODE_LIGHT => SKTypeface.FromFile("./fonts/SourceCodePro-Light.ttf"), 88 101 _ => SKTypeface.Default 89 102 }; 90 103 var fontsize = GetMaxFontSize(Width - Width / 3, typeface, Text, 1f, Width > Height ? Height / 3 : Width / 3); ··· 166 179 canvas.DrawPoint(random.Next(width), random.Next(height), noisePaint); 167 180 } 168 181 } 169 - 170 - public float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f) 182 + 183 + public float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, 184 + float maxFont = 100f) 171 185 { 172 186 var max = maxFont; // The upper bound. We know the font size is below this value 173 187 var min = 0f; // The lower bound, We know the font size is equal to or above this value ··· 202 216 } 203 217 } 204 218 } 219 + 220 + 205 221 }
+494
ReleaseImageGenerator.Domain/PatternGenerator.cs
··· 1 + using SkiaSharp; 2 + 3 + namespace ReleaseImageGenerator.Domain; 4 + 5 + public static class PatternGenerator 6 + { 7 + public static void AddBackgroundPatterns(SKCanvas canvas, int width, int height, Random random, double lightness) 8 + { 9 + // Choose a pattern type randomly 10 + int patternType = random.Next(10); 11 + 12 + switch (patternType) 13 + { 14 + case 0: 15 + // Subtle grid pattern 16 + DrawGridPattern(canvas, width, height, random, lightness); 17 + break; 18 + case 1: 19 + // Dotted pattern 20 + DrawDottedPattern(canvas, width, height, random, lightness); 21 + break; 22 + case 2: 23 + // Wavy lines pattern 24 + DrawWavyPattern(canvas, width, height, random, lightness); 25 + break; 26 + case 3: 27 + // Geometric shapes 28 + DrawGeometricPattern(canvas, width, height, random, lightness); 29 + break; 30 + case 4: 31 + // Hexagonal grid 32 + DrawHexagonalPattern(canvas, width, height, random, lightness); 33 + break; 34 + case 5: 35 + // Concentric circles 36 + DrawConcentricPattern(canvas, width, height, random, lightness); 37 + break; 38 + case 6: 39 + // Circuit board pattern 40 + DrawCircuitPattern(canvas, width, height, random, lightness); 41 + break; 42 + case 7: 43 + // Maze pattern 44 + DrawMazePattern(canvas, width, height, random, lightness); 45 + break; 46 + case 8: 47 + // Steps pattern 48 + DrawStepsPattern(canvas, width, height, random, lightness); 49 + break; 50 + } 51 + } 52 + 53 + private static void DrawGridPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 54 + { 55 + // Subtle grid lines with very low opacity 56 + var paint = new SKPaint 57 + { 58 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 59 + IsStroke = true, 60 + StrokeWidth = 1 61 + }; 62 + 63 + int spacing = random.Next(30, 60); 64 + 65 + // Draw vertical lines 66 + for (int x = 0; x < width; x += spacing) 67 + { 68 + canvas.DrawLine(x, 0, x, height, paint); 69 + } 70 + 71 + // Draw horizontal lines 72 + for (int y = 0; y < height; y += spacing) 73 + { 74 + canvas.DrawLine(0, y, width, y, paint); 75 + } 76 + } 77 + 78 + private static void DrawDottedPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 79 + { 80 + // Create tiny dots with low opacity 81 + var paint = new SKPaint 82 + { 83 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 84 + IsAntialias = true 85 + }; 86 + 87 + int spacing = random.Next(15, 45); 88 + float dotSize = random.Next(1, 3); 89 + 90 + for (int x = 0; x < width; x += spacing) 91 + { 92 + for (int y = 0; y < height; y += spacing) 93 + { 94 + // Add slight randomness to position 95 + float offsetX = (float)(random.NextDouble() * 5 - 2.5); 96 + float offsetY = (float)(random.NextDouble() * 5 - 2.5); 97 + 98 + canvas.DrawCircle(x + offsetX, y + offsetY, dotSize, paint); 99 + } 100 + } 101 + } 102 + 103 + private static void DrawWavyPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 104 + { 105 + var paint = new SKPaint 106 + { 107 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 108 + IsAntialias = true, 109 + IsStroke = true, 110 + StrokeWidth = 1 111 + }; 112 + 113 + int spacing = random.Next(40, 80); 114 + int amplitude = random.Next(10, 30); 115 + int frequency = random.Next(3, 6); 116 + 117 + // Draw horizontal wavy lines 118 + for (int y = 0; y < height; y += spacing) 119 + { 120 + var path = new SKPath(); 121 + path.MoveTo(0, y); 122 + 123 + for (int x = 0; x < width; x += 2) 124 + { 125 + float yOffset = (float)(Math.Sin(x * frequency / (double)width * Math.PI * 2) * amplitude); 126 + path.LineTo(x, y + yOffset); 127 + } 128 + 129 + canvas.DrawPath(path, paint); 130 + } 131 + 132 + // Draw vertical wavy lines with different frequency 133 + frequency = random.Next(2, 5); 134 + for (int x = 0; x < width; x += spacing) 135 + { 136 + var path = new SKPath(); 137 + path.MoveTo(x, 0); 138 + 139 + for (int y = 0; y < height; y += 2) 140 + { 141 + float xOffset = (float)(Math.Sin(y * frequency / (double)height * Math.PI * 2) * amplitude); 142 + path.LineTo(x + xOffset, y); 143 + } 144 + 145 + canvas.DrawPath(path, paint); 146 + } 147 + } 148 + 149 + private static void DrawGeometricPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 150 + { 151 + var paint = new SKPaint 152 + { 153 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 154 + IsAntialias = true, 155 + IsStroke = true, 156 + StrokeWidth = 1 157 + }; 158 + 159 + int spacing = random.Next(70, 140); 160 + 161 + for (int x = 0; x < width; x += spacing) 162 + { 163 + for (int y = 0; y < height; y += spacing) 164 + { 165 + int shapeType = random.Next(3); 166 + int size = random.Next(20, 40); 167 + 168 + // Add slight randomness to position 169 + float offsetX = (float)(random.NextDouble() * 34 - 17); 170 + float offsetY = (float)(random.NextDouble() * 34 - 17); 171 + 172 + switch (shapeType) 173 + { 174 + case 0: // Squares 175 + canvas.DrawRect(x - size / 2 + offsetX, y - size / 2 + offsetY, size, size, paint); 176 + break; 177 + case 1: // Circles 178 + canvas.DrawCircle(x + offsetX, y + offsetY, size / 2, paint); 179 + break; 180 + case 2: // Triangles 181 + var path = new SKPath(); 182 + path.MoveTo(x + offsetX, y - size / 2 + offsetY); 183 + path.LineTo(x - size / 2 + offsetX, y + size / 2 + offsetY); 184 + path.LineTo(x + size / 2 + offsetX, y + size / 2 + offsetY); 185 + path.Close(); 186 + canvas.DrawPath(path, paint); 187 + break; 188 + } 189 + } 190 + } 191 + } 192 + 193 + private static void DrawHexagonalPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 194 + { 195 + var paint = new SKPaint 196 + { 197 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 198 + IsAntialias = true, 199 + IsStroke = true, 200 + StrokeWidth = 1 201 + }; 202 + 203 + // Size of hexagons 204 + int size = random.Next(40, 80); 205 + float h = (float)(size * Math.Sqrt(3) / 2); // Height of equilateral triangle 206 + 207 + // Offset rows to create hexagonal tiling 208 + for (int row = -1; row < height / h + 1; row++) 209 + { 210 + for (int col = -1; col < width / size + 1; col++) 211 + { 212 + float centerX = col * size + (row % 2 == 0 ? 0 : size / 2); 213 + float centerY = row * h; 214 + 215 + // Draw a hexagon 216 + var path = new SKPath(); 217 + for (int i = 0; i < 6; i++) 218 + { 219 + float angle = (float)(Math.PI / 3 * i); 220 + float x = centerX + (float)(size / 2 * Math.Cos(angle)); 221 + float y = centerY + (float)(size / 2 * Math.Sin(angle)); 222 + 223 + if (i == 0) 224 + path.MoveTo(x, y); 225 + else 226 + path.LineTo(x, y); 227 + } 228 + 229 + path.Close(); 230 + canvas.DrawPath(path, paint); 231 + } 232 + } 233 + } 234 + 235 + private static void DrawConcentricPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 236 + { 237 + var paint = new SKPaint 238 + { 239 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 240 + IsAntialias = true, 241 + IsStroke = true, 242 + StrokeWidth = 1 243 + }; 244 + 245 + // Number of center points for concentric circles 246 + int numCenters = random.Next(1, 3); 247 + int maxRadius = Math.Max(width, height) / 2; 248 + 249 + for (int i = 0; i < numCenters; i++) 250 + { 251 + // Random center point 252 + float centerX = random.Next(width); 253 + float centerY = random.Next(height); 254 + 255 + // Draw several concentric circles from each center 256 + int numCircles = random.Next(3, 8); 257 + float radiusStep = maxRadius / numCircles; 258 + 259 + for (int j = 1; j <= numCircles; j++) 260 + { 261 + float radius = j * radiusStep; 262 + canvas.DrawCircle(centerX, centerY, radius, paint); 263 + } 264 + } 265 + } 266 + 267 + private static void DrawCrosshatchPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 268 + { 269 + var paint = new SKPaint 270 + { 271 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 272 + IsAntialias = true, 273 + IsStroke = true, 274 + StrokeWidth = 1, 275 + PathEffect = SKPathEffect.CreateDash(new float[] { 2, 4 }, 0) 276 + }; 277 + 278 + int spacing = random.Next(30, 70); 279 + float angle1 = (float)(random.Next(30, 60) * Math.PI / 180); // Convert to radians 280 + float angle2 = angle1 + (float)(Math.PI / 2); // Perpendicular 281 + 282 + // Additional randomness to have varied line density 283 + int lineDensity = random.Next(1, 3); 284 + 285 + // Draw lines at angle1 286 + for (int i = -height; i < width + height; i += spacing / lineDensity) 287 + { 288 + float x1 = i; 289 + float y1 = 0; 290 + float x2 = i + height * (float)Math.Cos(angle1); 291 + float y2 = height * (float)Math.Sin(angle1); 292 + 293 + canvas.DrawLine(x1, y1, x2, y2, paint); 294 + } 295 + 296 + // Draw lines at angle2 297 + for (int i = -height; i < width + height; i += spacing / lineDensity) 298 + { 299 + float x1 = i; 300 + float y1 = 0; 301 + float x2 = i + height * (float)Math.Cos(angle2); 302 + float y2 = height * (float)Math.Sin(angle2); 303 + 304 + canvas.DrawLine(x1, y1, x2, y2, paint); 305 + } 306 + } 307 + 308 + private static void DrawCircuitPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 309 + { 310 + var paint = new SKPaint 311 + { 312 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), 313 + IsAntialias = true, 314 + IsStroke = true, 315 + StrokeWidth = 1 316 + }; 317 + 318 + int gridSize = random.Next(80, 160); 319 + int lineChance = 70; // Percentage chance of drawing a line 320 + 321 + // Create a grid of nodes 322 + for (int x = gridSize; x < width; x += gridSize) 323 + { 324 + for (int y = gridSize; y < height; y += gridSize) 325 + { 326 + // Sometimes draw a small node 327 + if (random.Next(100) < 60) 328 + { 329 + float nodeSize = random.Next(2, 5); 330 + canvas.DrawCircle(x, y, nodeSize, paint); 331 + 332 + // Draw horizontal line to the right 333 + if (x + gridSize < width && random.Next(100) < lineChance) 334 + { 335 + // Occasionally draw straight line 336 + if (random.Next(100) < 80) 337 + { 338 + canvas.DrawLine(x, y, x + gridSize, y, paint); 339 + } 340 + else 341 + { 342 + // Draw L-shaped line 343 + int midX = x + gridSize / 2; 344 + int midY = y + (random.Next(2) == 0 ? gridSize / 2 : -gridSize / 2); 345 + 346 + var path = new SKPath(); 347 + path.MoveTo(x, y); 348 + path.LineTo(midX, y); 349 + path.LineTo(midX, midY); 350 + path.LineTo(x + gridSize, midY); 351 + path.LineTo(x + gridSize, y); 352 + canvas.DrawPath(path, paint); 353 + } 354 + } 355 + 356 + // Draw vertical line down 357 + if (y + gridSize < height && random.Next(100) < lineChance) 358 + { 359 + // Occasionally draw straight line 360 + if (random.Next(100) < 80) 361 + { 362 + canvas.DrawLine(x, y, x, y + gridSize, paint); 363 + } 364 + else 365 + { 366 + // Draw L-shaped line 367 + int midY = y + gridSize / 2; 368 + int midX = x + (random.Next(2) == 0 ? gridSize / 2 : -gridSize / 2); 369 + 370 + var path = new SKPath(); 371 + path.MoveTo(x, y); 372 + path.LineTo(x, midY); 373 + path.LineTo(midX, midY); 374 + path.LineTo(midX, y + gridSize); 375 + path.LineTo(x, y + gridSize); 376 + canvas.DrawPath(path, paint); 377 + } 378 + } 379 + } 380 + } 381 + } 382 + } 383 + 384 + private static void DrawMazePattern(SKCanvas canvas, int width, int height, Random random, double lightness) 385 + { 386 + var paint = new SKPaint 387 + { 388 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), // Slightly more visible 389 + IsAntialias = true, 390 + IsStroke = true, 391 + StrokeWidth = 2 // Thicker lines 392 + }; 393 + 394 + // Create a chess/steps pattern - decide on grid size 395 + int gridSize = random.Next(30, 60); 396 + 397 + // Determine how many grid cells we need in each dimension 398 + int horizontalCells = width / gridSize + 1; 399 + int verticalCells = height / gridSize + 1; 400 + 401 + // Create the step pattern 402 + for (int i = 0; i < horizontalCells + verticalCells - 1; i++) 403 + { 404 + // For each diagonal, we draw the step pattern 405 + for (int x = 0; x <= i; x++) 406 + { 407 + int y = i - x; 408 + 409 + // Only draw if within the grid bounds 410 + if (x < horizontalCells && y < verticalCells) 411 + { 412 + // Calculate the actual coordinates 413 + int x1 = x * gridSize; 414 + int y1 = y * gridSize; 415 + int x2 = (x + 1) * gridSize; 416 + int y2 = (y + 1) * gridSize; 417 + 418 + // Only draw if this is a "white" square in our chessboard pattern 419 + if ((x + y) % 2 == 0) 420 + { 421 + bool goUp = random.Next(2) == 0; 422 + 423 + if (goUp) 424 + { 425 + canvas.DrawLine(x1, y1, x2, y1, paint); // Horizontal line 426 + canvas.DrawLine(x2, y1, x2, y2, paint); // Vertical down 427 + } 428 + else 429 + { 430 + canvas.DrawLine(x1, y2, x2, y2, paint); // Horizontal line 431 + canvas.DrawLine(x2, y2, x2, y1, paint); // Vertical up 432 + } 433 + } 434 + } 435 + } 436 + } 437 + } 438 + 439 + private static void DrawStepsPattern(SKCanvas canvas, int width, int height, Random random, double lightness) 440 + { 441 + var paint = new SKPaint 442 + { 443 + Color = SKColors.White.WithAlpha((byte)(random.Next(3) + lightness * 10 + 8)), // Slightly more visible 444 + IsAntialias = true, 445 + IsStroke = true, 446 + StrokeWidth = 2 // Thicker lines 447 + }; 448 + 449 + // Create a chess/steps pattern - decide on grid size 450 + int gridSize = random.Next(30, 60); 451 + 452 + // Determine how many grid cells we need in each dimension 453 + int horizontalCells = width / gridSize + 1; 454 + int verticalCells = height / gridSize + 1; 455 + 456 + // Determine whether to go up or down 457 + bool goUp = random.Next(2) == 0; 458 + 459 + // Create the step pattern 460 + for (int i = 0; i < horizontalCells + verticalCells - 1; i++) 461 + { 462 + // For each diagonal, we draw the step pattern 463 + for (int x = 0; x <= i; x++) 464 + { 465 + int y = i - x; 466 + 467 + // Only draw if within the grid bounds 468 + if (x < horizontalCells && y < verticalCells) 469 + { 470 + // Calculate the actual coordinates 471 + int x1 = x * gridSize; 472 + int y1 = y * gridSize; 473 + int x2 = (x + 1) * gridSize; 474 + int y2 = (y + 1) * gridSize; 475 + 476 + // Only draw if this is a "white" square in our chessboard pattern 477 + if ((x + y) % 2 == 0) 478 + { 479 + if (goUp) 480 + { 481 + canvas.DrawLine(x1, y1, x2, y1, paint); // Horizontal line 482 + canvas.DrawLine(x2, y1, x2, y2, paint); // Vertical down 483 + } 484 + else 485 + { 486 + canvas.DrawLine(x1, y2, x2, y2, paint); // Horizontal line 487 + canvas.DrawLine(x2, y2, x2, y1, paint); // Vertical up 488 + } 489 + } 490 + } 491 + } 492 + } 493 + } 494 + }