From 7b9d67fe7b9fa618f34cbcb1ef06a28c27f6d38e Mon Sep 17 00:00:00 2001 From: Dustin Date: Sat, 17 May 2025 22:44:47 -0600 Subject: [PATCH 01/11] Converted to use skia raw --- CommonImageActions.Core/ImageProcessor.cs | 160 +++++++++++++++------- CommonImageActions.Pdf/PdfProcessor.cs | 2 +- 2 files changed, 109 insertions(+), 53 deletions(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index e3882b2..6079608 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -1,5 +1,4 @@ using Microsoft.Maui.Graphics; -using Microsoft.Maui.Graphics.Skia; using SkiaSharp; using System; using System.Collections.Generic; @@ -91,25 +90,25 @@ await Task.Run(() => if (isVirtual) { - Color virtualImageColor = null; + SKColor virtualImageColor; //set the text color if (!string.IsNullOrEmpty(actions.ImageColor)) { //try regular - if (Color.TryParse(actions.ImageColor, out var newColor)) + if (SKColor.TryParse(actions.ImageColor, out var newColor)) { virtualImageColor = newColor; } //try hex - else if (Color.TryParse($"#{actions.ImageColor}", out var newColorFromHex)) + else if (SKColor.TryParse($"#{actions.ImageColor}", out var newColorFromHex)) { virtualImageColor = newColorFromHex; } //fall back to white if they both fail else { - virtualImageColor = Colors.Black; + virtualImageColor = SKColors.Black; } } else if (actions.ChooseImageColorFromTextValue.HasValue @@ -123,24 +122,24 @@ await Task.Run(() => { backgroundColor = $"#{backgroundColor}"; } - if (Color.TryParse(backgroundColor, out var newColor)) + if (SKColor.TryParse(backgroundColor, out var newColor)) { virtualImageColor = newColor; } else { - virtualImageColor = Colors.Black; + virtualImageColor = SKColors.Black; } } else { - virtualImageColor = Colors.Black; + virtualImageColor = SKColors.Black; } var newBitmap = new SKBitmap(100, 100); using var canvas = new SKCanvas(newBitmap); - canvas.Clear(virtualImageColor.AsSKColor()); - using var newImage = new SkiaImage(newBitmap); + canvas.Clear(virtualImageColor); + using var newImage = SKImage.FromBitmap(newBitmap); encodedImage = EncodeSkiaImage(newImage, actions); } else @@ -148,7 +147,7 @@ await Task.Run(() => using var stream = new MemoryStream(imageData); using var codec = SKCodec.Create(stream); using var originalBitmap = SKBitmap.Decode(codec); - using var newImage = new SkiaImage(originalBitmap); + using var newImage = SKImage.FromBitmap(originalBitmap); encodedImage = EncodeSkiaImage(newImage, actions, codec); } @@ -162,7 +161,7 @@ await Task.Run(() => return encodedImage.ToArray(); } - public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActions, SKCodec codec = null) + public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions, SKCodec codec = null) { //make sure image was loaded successfully if (newImage == null) @@ -215,8 +214,10 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio } // Create a new bitmap with the new dimensions - var skBmp = new SkiaBitmapExportContext(imageActions.Width.Value, imageActions.Height.Value, 1.0f); - var canvas = skBmp.Canvas; + var skBmp = new SKBitmap(imageActions.Width.Value, imageActions.Height.Value); + var recorder = new SKPictureRecorder(); + var rect = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); + var canvas = recorder.BeginRecording(rect); //if no shape specified, but a corner radius is then set shape to rounded rectangle if (imageActions.Shape.HasValue == false && imageActions.CornerRadius.HasValue) @@ -239,28 +240,31 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio var centerX = imageActions.Width.Value / 2; var centerY = imageActions.Height.Value / 2; - var a = new PathF(); - a.AppendCircle(centerX, centerY, radius); + var a = new SKPath(); + a.AddCircle(centerX, centerY, radius); canvas.ClipPath(a); } else if (imageActions.Shape == ImageShape.Ellipse) { - var a = new PathF(); + var a = new SKPath(); var centerX = imageActions.Width.Value / 2; var centerY = imageActions.Height.Value / 2; - a.AppendEllipse(0, 0, imageActions.Width.Value, imageActions.Height.Value); + var r = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); + a.AddOval(r); canvas.ClipPath(a); } else if (imageActions.Shape == ImageShape.RoundedRectangle) { - var a = new PathF(); + var a = new SKPath(); + var r = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); if (imageActions.CornerRadius.HasValue) { - a.AppendRoundedRectangle(0, 0, imageActions.Width.Value, imageActions.Height.Value, imageActions.CornerRadius.Value); + + a.AddRoundRect(r, imageActions.CornerRadius.Value, imageActions.CornerRadius.Value); } else { - a.AppendRoundedRectangle(0, 0, imageActions.Width.Value, imageActions.Height.Value, CornerRadius); + a.AddRoundRect(r, CornerRadius, CornerRadius); } canvas.ClipPath(a); } @@ -279,33 +283,33 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio break; case SKEncodedOrigin.TopRight: - canvas.Rotate(180, imageActions.Width.Value / 2, imageActions.Height.Value / 2); + canvas.RotateDegrees(180, imageActions.Width.Value / 2, imageActions.Height.Value / 2); break; case SKEncodedOrigin.BottomRight: - canvas.Rotate(180, imageActions.Width.Value / 2, imageActions.Height.Value / 2); + canvas.RotateDegrees(180, imageActions.Width.Value / 2, imageActions.Height.Value / 2); break; case SKEncodedOrigin.BottomLeft: break; case SKEncodedOrigin.LeftTop: - canvas.Rotate(90, imageActions.Width.Value / 2, imageActions.Height.Value / 2); + canvas.RotateDegrees(90, imageActions.Width.Value / 2, imageActions.Height.Value / 2); isOddRotation = true; break; case SKEncodedOrigin.RightTop: - canvas.Rotate(90, imageActions.Width.Value / 2, imageActions.Height.Value / 2); + canvas.RotateDegrees(90, imageActions.Width.Value / 2, imageActions.Height.Value / 2); isOddRotation = true; break; case SKEncodedOrigin.RightBottom: - canvas.Rotate(270, imageActions.Width.Value / 2, imageActions.Height.Value / 2); + canvas.RotateDegrees(270, imageActions.Width.Value / 2, imageActions.Height.Value / 2); isOddRotation = true; break; case SKEncodedOrigin.LeftBottom: - canvas.Rotate(270, imageActions.Width.Value / 2, imageActions.Height.Value / 2); + canvas.RotateDegrees(270, imageActions.Width.Value / 2, imageActions.Height.Value / 2); isOddRotation = true; break; } @@ -330,11 +334,13 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio case ImageMode.Max: if (isOddRotation) { - canvas.DrawImage(newImage, rotationOffsetX, rotationOffsetY, imageActions.Height.Value, imageActions.Width.Value); + var drawRect = new SKRect(rotationOffsetX, rotationOffsetY, imageActions.Height.Value, imageActions.Width.Value); + canvas.DrawImage(newImage, drawRect); } else { - canvas.DrawImage(newImage, 0, 0, imageActions.Width.Value, imageActions.Height.Value); + var drawRect = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); + canvas.DrawImage(newImage, drawRect); } break; @@ -349,7 +355,9 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio var fitScaledHeight = (int)(newImage.Height * fitScale); var fitOffsetX = (imageActions.Width.Value - fitScaledWidth) / 2; var fitOffsetY = (imageActions.Height.Value - fitScaledHeight) / 2; - canvas.DrawImage(newImage, fitOffsetX, fitOffsetY, fitScaledWidth, fitScaledHeight); + var drawRect2 = new SKRect(fitOffsetX, fitOffsetY, fitScaledWidth, fitScaledHeight); + + canvas.DrawImage(newImage, drawRect2); break; //zoom in and fill canvas while maintaing aspect ratio @@ -363,7 +371,8 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio var scaledHeight = (int)(newImage.Height * scale); var offsetX = (imageActions.Width.Value - scaledWidth) / 2; var offsetY = (imageActions.Height.Value - scaledHeight) / 2; - canvas.DrawImage(newImage, offsetX, offsetY, scaledWidth, scaledHeight); + var drawRect3 = new SKRect(offsetX, offsetY, scaledWidth, scaledHeight); + canvas.DrawImage(newImage, drawRect3); break; } @@ -377,55 +386,63 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio textToPrint = GetInitials(imageActions.Text); } - var myFont = new Font("Arial", weight: 800); + var myTypeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold); var myFontSize = (int)(imageActions.Height.Value * 0.85); - canvas.Font = myFont; + + // Set up paint for text + using var paint = new SKPaint + { + Typeface = myTypeface, + IsAntialias = true, + TextSize = myFontSize, + Color = SKColors.Black, // Default text color + TextAlign = SKTextAlign.Center + }; //calculate string size where height is image height to get scale of text - var textSize = canvas.GetStringSize(textToPrint, myFont, myFontSize); + var textSize = paint.MeasureText(textToPrint); //specify the max width that is wanted var maxWidth = imageActions.Width.Value * 0.75; // it needs to fit in the image, so if it is too narrow then we need to shrink down the font - if (textSize.Width > maxWidth) + if (textSize > maxWidth) { - myFontSize = (int)((maxWidth / textSize.Width) * myFontSize); + myFontSize = (int)((maxWidth / textSize) * myFontSize); } - //calculate the text size again with the new font size - var point = new Point( - x: (skBmp.Width - textSize.Width) / 2, - y: (skBmp.Height - textSize.Height) / 2); - var myTextRectangle = new Rect(point, textSize); - canvas.FontSize = myFontSize; + // Calculate maximum font size to fit text within the image + paint.TextSize = myFontSize; //set the text color if (!string.IsNullOrEmpty(imageActions.TextColor)) { //try regular - if (Color.TryParse(imageActions.TextColor, out var newColor)) + if (SKColor.TryParse(imageActions.TextColor, out var newColor)) { - canvas.FontColor = newColor; + paint.Color = newColor; } //try hex - else if (Color.TryParse($"#{imageActions.TextColor}", out var newColorFromHex)) + else if (SKColor.TryParse($"#{imageActions.TextColor}", out var newColorFromHex)) { - canvas.FontColor = newColorFromHex; + paint.Color = newColorFromHex; } //fall back to white if they both fail else { - canvas.FontColor = Colors.White; + paint.Color = SKColors.White; } } else { - canvas.FontColor = Colors.White; + paint.Color = SKColors.White; } - canvas.DrawString(textToPrint, myTextRectangle, HorizontalAlignment.Center, VerticalAlignment.Center, TextFlow.OverflowBounds); + // Calculate text position + var x = imageActions.Width.Value / 2f; + var y = (imageActions.Height.Value / 2f) - ((paint.FontMetrics.Ascent + paint.FontMetrics.Descent) / 2); + canvas.DrawText(textToPrint, x, y, paint); } //set export format @@ -444,24 +461,63 @@ public static SKData EncodeSkiaImage(SkiaImage newImage, ImageActions imageActio //set encoding quality SKData encodedImage = null; + var picture = recorder.EndRecording(); + var ouputSize = new SKSizeI(imageActions.Width.Value, imageActions.Height.Value); + var outputImage = SKImage.FromPicture(picture, ouputSize); switch (exportImageType) { default: - encodedImage = skBmp.SKImage.Encode(exportImageType, 100); + encodedImage = outputImage.Encode(exportImageType, 100); break; case SKEncodedImageFormat.Jpeg: - encodedImage = skBmp.SKImage.Encode(SKEncodedImageFormat.Jpeg, JpegQuality); + encodedImage = outputImage.Encode(SKEncodedImageFormat.Jpeg, JpegQuality); break; case SKEncodedImageFormat.Gif: - encodedImage = skBmp.SKImage.Encode(SKEncodedImageFormat.Gif, GifQuality); + encodedImage = outputImage.Encode(SKEncodedImageFormat.Gif, GifQuality); break; } return encodedImage; } + public static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f) + { + var max = maxFont; // The upper bound. We know the font size is below this value + var min = 0f; // The lower bound, We know the font size is equal to or above this value + var last = -1f; // The last calculated value. + float value; + while (true) + { + value = min + ((max - min) / 2); // Find the half way point between Max and Min + using (SKFont ft = new SKFont(typeface, value)) + using (SKPaint paint = new SKPaint(ft)) + { + if (paint.MeasureText(text) > sectorSize) // Measure the string size at this font size + { + // The text size is too large + // therefore the max possible size is below value + last = value; + max = value; + } + else + { + // The text fits within the area + // therefore the min size is above or equal to value + min = value; + + // Check if this value is within our degree of certainty + if (Math.Abs(last - value) <= degreeOfCertainty) + return last; // Value is within certainty range, we found the best font size! + + //This font difference is not within our degree of certainty + last = value; + } + } + } + } + public static UInt64 CalculateHash(string read) { UInt64 hashedValue = 3074457345618258791ul; diff --git a/CommonImageActions.Pdf/PdfProcessor.cs b/CommonImageActions.Pdf/PdfProcessor.cs index e5acad4..6788490 100644 --- a/CommonImageActions.Pdf/PdfProcessor.cs +++ b/CommonImageActions.Pdf/PdfProcessor.cs @@ -151,7 +151,7 @@ await Task.Run(() => //convert into skia format using var originalBitmap = SKBitmap.Decode(bmpData); - using var newImage = new SkiaImage(originalBitmap); + using var newImage = SKImage.FromBitmap(originalBitmap); //process skia image into encoded image returnValue = ImageProcessor.EncodeSkiaImage(newImage, actions).ToArray(); From 4aac96963d82db38172984fae8b41163a37bbcaa Mon Sep 17 00:00:00 2001 From: Dustin Date: Sat, 17 May 2025 22:56:36 -0600 Subject: [PATCH 02/11] Fixed blurryness issue with filter quality paint property --- CommonImageActions.Core/ImageProcessor.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index 6079608..1935e89 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -324,6 +324,12 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions rotationOffsetX = rotationOffsetY * -1; } + var imagePaint = new SKPaint + { + IsAntialias = true, + FilterQuality = SKFilterQuality.High + }; + //write to the canvas switch (imageActions.Mode) { @@ -335,12 +341,12 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions if (isOddRotation) { var drawRect = new SKRect(rotationOffsetX, rotationOffsetY, imageActions.Height.Value, imageActions.Width.Value); - canvas.DrawImage(newImage, drawRect); + canvas.DrawImage(newImage, drawRect, paint:imagePaint); } else { var drawRect = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); - canvas.DrawImage(newImage, drawRect); + canvas.DrawImage(newImage, drawRect, paint: imagePaint); } break; @@ -357,7 +363,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions var fitOffsetY = (imageActions.Height.Value - fitScaledHeight) / 2; var drawRect2 = new SKRect(fitOffsetX, fitOffsetY, fitScaledWidth, fitScaledHeight); - canvas.DrawImage(newImage, drawRect2); + canvas.DrawImage(newImage, drawRect2, paint: imagePaint); break; //zoom in and fill canvas while maintaing aspect ratio @@ -372,7 +378,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions var offsetX = (imageActions.Width.Value - scaledWidth) / 2; var offsetY = (imageActions.Height.Value - scaledHeight) / 2; var drawRect3 = new SKRect(offsetX, offsetY, scaledWidth, scaledHeight); - canvas.DrawImage(newImage, drawRect3); + canvas.DrawImage(newImage, drawRect3, paint: imagePaint); break; } From 0f27ca25bf3e59e67ca6ee361e5a8a4ad13c14eb Mon Sep 17 00:00:00 2001 From: Dustin Date: Sat, 17 May 2025 23:03:20 -0600 Subject: [PATCH 03/11] Removed test that tested internal components --- .../ImageProcessorTests.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/CommonImageActions.Core.Tests/ImageProcessorTests.cs b/CommonImageActions.Core.Tests/ImageProcessorTests.cs index 849f7a7..d634678 100644 --- a/CommonImageActions.Core.Tests/ImageProcessorTests.cs +++ b/CommonImageActions.Core.Tests/ImageProcessorTests.cs @@ -115,22 +115,6 @@ public async Task ProcessVirtualImageAsync_ShouldReturnProcessedImage() Assert.NotEmpty(result); } - [Fact] - public void EncodeSkiaImage_ShouldReturnEncodedImage() - { - // Arrange - var bitmap = new SKBitmap(100, 100); - using var canvas = new SKCanvas(bitmap); - using var newImage = new SkiaImage(bitmap); - var actions = new ImageActions(); - - // Act - var result = ImageProcessor.EncodeSkiaImage(newImage, actions); - - // Assert - Assert.NotNull(result); - } - [Fact] public void GetInitials_ShouldReturnInitials() { From 60a2c037a972743a8bf73f8f2247c06c4fcc876e Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 18 May 2025 10:22:12 -0600 Subject: [PATCH 04/11] Removed unused function --- CommonImageActions.Core/ImageProcessor.cs | 36 ----------------------- 1 file changed, 36 deletions(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index 1935e89..d15f9ab 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -488,42 +488,6 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions return encodedImage; } - public static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f) - { - var max = maxFont; // The upper bound. We know the font size is below this value - var min = 0f; // The lower bound, We know the font size is equal to or above this value - var last = -1f; // The last calculated value. - float value; - while (true) - { - value = min + ((max - min) / 2); // Find the half way point between Max and Min - using (SKFont ft = new SKFont(typeface, value)) - using (SKPaint paint = new SKPaint(ft)) - { - if (paint.MeasureText(text) > sectorSize) // Measure the string size at this font size - { - // The text size is too large - // therefore the max possible size is below value - last = value; - max = value; - } - else - { - // The text fits within the area - // therefore the min size is above or equal to value - min = value; - - // Check if this value is within our degree of certainty - if (Math.Abs(last - value) <= degreeOfCertainty) - return last; // Value is within certainty range, we found the best font size! - - //This font difference is not within our degree of certainty - last = value; - } - } - } - } - public static UInt64 CalculateHash(string read) { UInt64 hashedValue = 3074457345618258791ul; From cfdd3adc4fdce8aa9bbf0ff55b19630b6f6896da Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 18 May 2025 10:24:19 -0600 Subject: [PATCH 05/11] Thickened text to match old style --- CommonImageActions.Core/ImageProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index d15f9ab..77eba6b 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -392,7 +392,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions textToPrint = GetInitials(imageActions.Text); } - var myTypeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold); + var myTypeface = SKTypeface.FromFamilyName("Arial", SKFontStyleWeight.Black, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); var myFontSize = (int)(imageActions.Height.Value * 0.85); // Set up paint for text From d85332eadcd538100ed51fa0dbe5d62cd7aab995 Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 18 May 2025 10:34:32 -0600 Subject: [PATCH 06/11] Removed the ability to support named colors --- CommonImageActions.Core/ImageProcessor.cs | 14 ++------------ .../wwwroot/index.html | 16 +++------------- README.md | 4 ++-- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index 77eba6b..267e8a8 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -95,13 +95,8 @@ await Task.Run(() => //set the text color if (!string.IsNullOrEmpty(actions.ImageColor)) { - //try regular - if (SKColor.TryParse(actions.ImageColor, out var newColor)) - { - virtualImageColor = newColor; - } //try hex - else if (SKColor.TryParse($"#{actions.ImageColor}", out var newColorFromHex)) + if (SKColor.TryParse($"#{actions.ImageColor}", out var newColorFromHex)) { virtualImageColor = newColorFromHex; } @@ -423,13 +418,8 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions //set the text color if (!string.IsNullOrEmpty(imageActions.TextColor)) { - //try regular - if (SKColor.TryParse(imageActions.TextColor, out var newColor)) - { - paint.Color = newColor; - } //try hex - else if (SKColor.TryParse($"#{imageActions.TextColor}", out var newColorFromHex)) + if (SKColor.TryParse($"#{imageActions.TextColor}", out var newColorFromHex)) { paint.Color = newColorFromHex; } diff --git a/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html b/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html index b9b729b..4dd2819 100644 --- a/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html +++ b/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html @@ -132,16 +132,6 @@
Initials
-
-
-

- -

-
Color Named
- /test/thumbsUp.jpg?w=100&h=100&f=png&t=DustinG&in=true&tc=blue -
-
-

@@ -155,10 +145,10 @@

Color Hex

- +

-
Background Color (Manual)
- /test/thumbsUp.jpg?w=100&h=100&f=png&t=DustinG&in=true&tc=00ff00&c=red +
Background Color (Manual Hex)
+ /test/thumbsUp.jpg?w=100&h=100&f=png&t=DustinG&in=true&tc=00ff00&c=0000ff
diff --git a/README.md b/README.md index 107ac49..c2803ce 100644 --- a/README.md +++ b/README.md @@ -160,8 +160,8 @@ app.UseCommonImageActions( | corner, cr | Integer | The corner radius when shape is RoundedRectangle. Default is 10. | | text, t | String | The text to display on the image | | initials, in | Boolean | When true will only display initials of text. For example DustinG is displayed as DG. | -| color, c | String (ffccff or blue) | Set a color for the image | -| textColor, tc | String (ffccff or blue) | Set the color of the text | +| color, c | String ffccff | Set a color for the image | +| textColor, tc | String ffccff | Set the color of the text | | colorFromText, ft | Boolean | When true a color will be generated based on a hash of the text. The list of colors can be updated in `ImageProcessor.BackgroundColours`. | | format, f | Bmp, Gif, Ico, Jpeg, Png, Wbmp, Webp, Pkm, Ktx, Astc, Dng, Heif, Avif | What format to export the resulting image as. Default is png. | | password, pw | String | (pdf only) password to open pdf | From 10510ba6c9b736434529d39fe94abd2d5740ac93 Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 18 May 2025 10:36:26 -0600 Subject: [PATCH 07/11] Fixed typo in example --- CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html b/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html index 4dd2819..62434a5 100644 --- a/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html +++ b/CommonImageActions.SampleAspnetCoreProject/wwwroot/index.html @@ -27,7 +27,7 @@
Stretch (Default)

Max
- /test/thumbsUp.jpg?s=circle&w=100&h=100&m=zoom&f=png + /test/thumbsUp.jpg?s=circle&w=100&h=100&m=Max&f=png
@@ -37,7 +37,7 @@
Max

Fit
- /test/thumbsUp.jpg?s=ellipse&w=100&h=100&m=zoom&f=png + /test/thumbsUp.jpg?s=ellipse&w=100&h=100&m=Fit&f=png From d37e39e79cc23c05da750eb633901e6db9242a24 Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 18 May 2025 11:00:48 -0600 Subject: [PATCH 08/11] Fixed a major calculation error with the conversion --- CommonImageActions.Core/ImageProcessor.cs | 25 ++++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index 267e8a8..e9c80e1 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -211,7 +211,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions // Create a new bitmap with the new dimensions var skBmp = new SKBitmap(imageActions.Width.Value, imageActions.Height.Value); var recorder = new SKPictureRecorder(); - var rect = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); + var rect = GetSKRectByWidthAndHeight(0, 0, imageActions.Width.Value, imageActions.Height.Value); var canvas = recorder.BeginRecording(rect); //if no shape specified, but a corner radius is then set shape to rounded rectangle @@ -244,14 +244,14 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions var a = new SKPath(); var centerX = imageActions.Width.Value / 2; var centerY = imageActions.Height.Value / 2; - var r = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); + var r = GetSKRectByWidthAndHeight(0, 0, imageActions.Width.Value, imageActions.Height.Value); a.AddOval(r); canvas.ClipPath(a); } else if (imageActions.Shape == ImageShape.RoundedRectangle) { var a = new SKPath(); - var r = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); + var r = GetSKRectByWidthAndHeight(0, 0, imageActions.Width.Value, imageActions.Height.Value); if (imageActions.CornerRadius.HasValue) { @@ -335,12 +335,12 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions case ImageMode.Max: if (isOddRotation) { - var drawRect = new SKRect(rotationOffsetX, rotationOffsetY, imageActions.Height.Value, imageActions.Width.Value); + var drawRect = GetSKRectByWidthAndHeight(rotationOffsetX, rotationOffsetY, imageActions.Height.Value, imageActions.Width.Value); canvas.DrawImage(newImage, drawRect, paint:imagePaint); } else { - var drawRect = new SKRect(0, 0, imageActions.Width.Value, imageActions.Height.Value); + var drawRect = GetSKRectByWidthAndHeight(0, 0, imageActions.Width.Value, imageActions.Height.Value); canvas.DrawImage(newImage, drawRect, paint: imagePaint); } break; @@ -354,11 +354,11 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions } var fitScaledWidth = (int)(newImage.Width * fitScale); var fitScaledHeight = (int)(newImage.Height * fitScale); - var fitOffsetX = (imageActions.Width.Value - fitScaledWidth) / 2; - var fitOffsetY = (imageActions.Height.Value - fitScaledHeight) / 2; - var drawRect2 = new SKRect(fitOffsetX, fitOffsetY, fitScaledWidth, fitScaledHeight); + var fitOffsetX = (imageActions.Width.Value - fitScaledWidth) / 2f; + var fitOffsetY = (imageActions.Height.Value - fitScaledHeight) / 2f; + var drawRect2 = GetSKRectByWidthAndHeight(fitOffsetX, fitOffsetY, fitScaledWidth, fitScaledHeight); - canvas.DrawImage(newImage, drawRect2, paint: imagePaint); + canvas.DrawImage(newImage, drawRect2); break; //zoom in and fill canvas while maintaing aspect ratio @@ -372,7 +372,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions var scaledHeight = (int)(newImage.Height * scale); var offsetX = (imageActions.Width.Value - scaledWidth) / 2; var offsetY = (imageActions.Height.Value - scaledHeight) / 2; - var drawRect3 = new SKRect(offsetX, offsetY, scaledWidth, scaledHeight); + var drawRect3 = GetSKRectByWidthAndHeight(offsetX, offsetY, scaledWidth, scaledHeight); canvas.DrawImage(newImage, drawRect3, paint: imagePaint); break; } @@ -478,6 +478,11 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions return encodedImage; } + public static SKRect GetSKRectByWidthAndHeight(float left, float top, float width, float height) + { + return new SKRect(left, top, width + left, top + height); + } + public static UInt64 CalculateHash(string read) { UInt64 hashedValue = 3074457345618258791ul; From da4d8dad386506ea13b08b2ca9fcd39268ab29df Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 18 May 2025 18:02:49 -0600 Subject: [PATCH 09/11] Added ignore for local item --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6ef84b8..d10c047 100644 --- a/.gitignore +++ b/.gitignore @@ -365,3 +365,4 @@ FodyWeavers.xsd /CommonImageActions/wwwroot/cache /CommonImageActions/wwwroot/test/thumbsUp.jpg /CommonImageActions.SampleAspnetCoreProject/wwwroot/cache +/CommonImageActions.SampleAspnetCoreProject/wwwroot/test/ProjectsIcon.png From bc001c02388389aadf5e173c4b77749707ff9d19 Mon Sep 17 00:00:00 2001 From: Dustin Date: Sat, 24 May 2025 08:56:51 -0600 Subject: [PATCH 10/11] Added antialising to borders when masking --- CommonImageActions.Core/ImageProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index e9c80e1..9ad201f 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -237,7 +237,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions var a = new SKPath(); a.AddCircle(centerX, centerY, radius); - canvas.ClipPath(a); + canvas.ClipPath(a, antialias:true); } else if (imageActions.Shape == ImageShape.Ellipse) { @@ -246,7 +246,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions var centerY = imageActions.Height.Value / 2; var r = GetSKRectByWidthAndHeight(0, 0, imageActions.Width.Value, imageActions.Height.Value); a.AddOval(r); - canvas.ClipPath(a); + canvas.ClipPath(a, antialias:true); } else if (imageActions.Shape == ImageShape.RoundedRectangle) { @@ -261,7 +261,7 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions { a.AddRoundRect(r, CornerRadius, CornerRadius); } - canvas.ClipPath(a); + canvas.ClipPath(a, antialias:true); } } From 9ef17536e30815eb10252c3f8451826851c3037a Mon Sep 17 00:00:00 2001 From: Dustin Date: Sat, 24 May 2025 08:57:54 -0600 Subject: [PATCH 11/11] Removed antialising on image scale --- CommonImageActions.Core/ImageProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/CommonImageActions.Core/ImageProcessor.cs b/CommonImageActions.Core/ImageProcessor.cs index 9ad201f..f9d9f51 100644 --- a/CommonImageActions.Core/ImageProcessor.cs +++ b/CommonImageActions.Core/ImageProcessor.cs @@ -321,7 +321,6 @@ public static SKData EncodeSkiaImage(SKImage newImage, ImageActions imageActions var imagePaint = new SKPaint { - IsAntialias = true, FilterQuality = SKFilterQuality.High };