I'm currently developing a cloth editor for a friend's tailor shop using skiasharp. I wanted to use the ClipPath to prevent users from placing images or text outside of the bounds of the cloth that is being edited or some other effects. The problem is that I haven't been able to consistently align the path with the cloth design across different devices with different aspect ratios. Here are some images to show better the problem and below the code I'm currently using:
Samsung Galaxy J2 Pro - 540 x 960 pixels, 16:9 ratio (~220 ppi density)
While it is not perfect, the result is very acceptable.
Samsung Galaxy A20s - 720 x 1560 pixels, 19.5:9 ratio (~264 ppi density)
But in this case the clip path is completely not aligned with the bitmaps of the shirt
Here is the code for the Paint Surfce event:
void SKCanvasView_PaintSurface_Post(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
canvas.SetMatrix(matrix);
if (AreBitmapsLoaded) //Make sure that bitmaps that are being loaded in a different thread are ready.
{
if (IsManipulating) //This draws a copy of the bitmap if it can be manipulated so it can be seen even if outside the clip path
{
foreach (TouchManipulationBitmap manipulationBitmap in Bitmaps)
{
if (manipulationBitmap.Interactable && current == manipulationBitmap)
{
manipulationBitmap.Paint(canvas, info.Width, info.Height, painto);
break;
}
}
}
//Cliping canvas to path
if (!CalculateClippingOnce)
{
TestPath.GetTightBounds(out bounds);
TranslateCanvasX = info.Width / 2f;
TranslateCanvasY = info.Height / 2f;
ratio = bounds.Width >= bounds.Height
? info.Height / bounds.Height
: info.Width / bounds.Width;
CalculateClippingOnce = true;
}
canvas.Translate(TranslateCanvasX, TranslateCanvasY);
canvas.Scale(0.85f * ratio);
canvas.Translate(-bounds.MidX, -bounds.MidY);
canvas.ClipPath(TestPath, SKClipOperation.Intersect, true);
canvas.ResetMatrix();
Bitmaps = Bitmaps.OrderBy(b => b.ZOrder).ToList(); //A questionable way of having a Z order.
foreach (TouchManipulationBitmap manipulationBitmap in Bitmaps)
{
manipulationBitmap.Paint(canvas, info.Width, info.Height);
}
if (current == null)
{
current = Bitmaps[0];
}
}
if (TakeAndSaveSnapshot)
{
TakeAndSaveSnapshot = false;
SKPixmap pixmap = surface.PeekPixels();
Render = new SKBitmap();
Render.InstallPixels(pixmap);
Task.Run(async () =>
{
await ProcessDesign();
});
}
}
I think it will be useful to share the code for the Paint function in the TouchManipulationBitmap class:
public void Paint(SKCanvas canvas, int infoWidth, int infoHeight, SKPaint paint = null)
{
canvas.Save();
SKMatrix matrix = Matrix;
canvas.Concat(ref matrix);
if (ShouldColor) // The shadow details in the shirt should not be colored for example.
{
if (TargetColor == null)
{
TargetColor = SKColors.White;
}
paint = new SKPaint();
var tableRed = new byte[256];
var tableGreen = new byte[256];
var tableBlue = new byte[256];
for (int i = 0; i < 256; i++)
{
tableRed[i] = TargetColor.Red;
tableGreen[i] = TargetColor.Green;
tableBlue[i] = TargetColor.Blue;
}
paint.ColorFilter = SKColorFilter.CreateTable(null, tableRed, tableGreen, tableBlue);
paint.FilterQuality = SKFilterQuality.Low;
}
if (!IsText) // if the bitmap does not represent text.
{
if (!CalculateDrawOnce)
{
float scale = Math.Min((float)infoWidth / bitmap.Width, (float)infoHeight / bitmap.Height);
float x = (infoWidth - scale * bitmap.Width) / 2;
float y = (infoHeight - scale * bitmap.Height) / 2;
rect = new SKRect(x, y, x + scale * bitmap.Width, y + scale * bitmap.Height);
Scale = scale;
X = x;
Y = y;
CalculateDrawOnce = true;
}
canvas.DrawBitmap(bitmap, rect, paint);
}
else
{
float x = (infoWidth - bitmap.Width) / 2;
float y = (infoHeight - bitmap.Height) / 2;
X = x;
Y = y;
canvas.DrawBitmap(bitmap, x, y, paint);
}
canvas.Restore();
}
Some other useful Information:
-> The shirt is a mix of bitmaps that are being draw one on top of the other and they align perfectly just as I designed in Figma.
-> The TestPath fits perfectly the contour of the bitmaps when made in Figma, but won't align once I try inside the app.
-> The reason there are foreach loops it's because the user can upload their own images to make a design, or add text or change the cloth type (let's say a short) so the bitmaps to be draw are not fixed. I feel it is probably not the best approach and while it is not the main focus of my question, if you have any feedback on this I would love to listen to it.
This is my first attempt at coding anything with Skiasharp so my code is a mix of tutorials I found around and changes I made to fulfill the needs of the app, so any guidance on how to solve my problem will be nice, Thanks!