public ResizedImageInfo Resize(DpiPath dpi, string inputsFile) { var destination = GetFileDestination(dpi); if (Info.IsVector) { destination = Path.ChangeExtension(destination, ".png"); } if (IsUpToDate(Info.Filename, destination, inputsFile, Logger)) { return new ResizedImageInfo { Filename = destination, Dpi = dpi } } ; if (tools == null) { tools = SkiaSharpTools.Create(Info.IsVector, Info.Filename, Info.BaseSize, Info.TintColor, Logger); } tools.Resize(dpi, destination); return(new ResizedImageInfo { Filename = destination, Dpi = dpi }); } }
public ResizedImageInfo Resize(DpiPath dpi, string destination, Func <Stream>?getStream = null) { var sw = new Stopwatch(); sw.Start(); // 1. if an explicit size was given by the type of image, use that // 2. if an explicit size was given in the csproj, use that // 3. try determine the best size based on the background then foreground var(canvasSize, unscaledCanvasSize) = SkiaSharpTools.GetCanvasSize( dpi, Info.BaseSize, backgroundTools ?? foregroundTools); using (var tempBitmap = new SKBitmap(canvasSize.Width, canvasSize.Height)) { Draw(tempBitmap, dpi, unscaledCanvasSize); Save(tempBitmap, destination, getStream); } sw.Stop(); Logger?.Log($"Save app icon took {sw.ElapsedMilliseconds}ms ({destination})"); return(new ResizedImageInfo { Dpi = dpi, Filename = destination }); }
public void Resize(DpiPath dpi, string destination) { var originalSize = GetOriginalSize(); var(scaledSize, scale) = GetScaledSize(originalSize, dpi.Scale); var sw = new Stopwatch(); sw.Start(); // Allocate using (var tempBitmap = new SKBitmap(scaledSize.Width, scaledSize.Height)) { // Draw (copy) using (var canvas = new SKCanvas(tempBitmap)) { canvas.Clear(SKColors.Transparent); canvas.Save(); canvas.Scale(scale, scale); DrawUnscaled(canvas, scale); } // Save (encode) using var stream = File.Create(destination); tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100); } sw.Stop(); Logger?.Log($"Save Image took {sw.ElapsedMilliseconds}ms ({destination})"); }
public static (SKSizeI Scaled, SKSize Unscaled) GetCanvasSize(DpiPath dpi, SKSize?baseSize = null, SkiaSharpTools baseTools = null) { // if an explicit size was given by the type of image, use that if (dpi.Size is SKSize size) { var scale = (float)dpi.Scale; var scaled = new SKSizeI( (int)(size.Width * scale), (int)(size.Height * scale)); return(scaled, size); } // if an explicit size was given in the csproj, use that if (baseSize is SKSize bs) { var scale = (float)dpi.Scale; var scaled = new SKSizeI( (int)(bs.Width * scale), (int)(bs.Height * scale)); return(scaled, bs); } // try determine the best size based on the loaded image if (baseTools is not null) { var baseOriginalSize = baseTools.GetOriginalSize(); var(baseScaledSize, _) = baseTools.GetScaledSize(baseOriginalSize, dpi.Scale); return(baseScaledSize, baseOriginalSize); } throw new InvalidOperationException("The canvas size cannot be calculated if there is no size to start from (DPI size, BaseSize or image size)."); }
public ResizedImageInfo CopyFile(DpiPath dpi, string inputsFile) { var destination = GetFileDestination(dpi); if (Info.IsVector) { destination = Path.ChangeExtension(destination, ".png"); } if (IsUpToDate(Info.Filename, destination, inputsFile, Logger)) { return new ResizedImageInfo { Filename = destination, Dpi = dpi } } ; if (Info.IsVector) { Rasterize(dpi, destination); } else { File.Copy(Info.Filename, destination, true); } return(new ResizedImageInfo { Filename = destination, Dpi = dpi }); }
void ProcessAppIcon(SharedImageInfo img, ConcurrentBag <ResizedImageInfo> resizedImages) { var appIconName = img.OutputName; // Generate the actual bitmap app icons themselves var appIconDpis = DpiPath.GetAppIconDpis(PlatformType, appIconName); Log.LogMessage(MessageImportance.Low, $"App Icon"); // Apple and Android have special additional files to generate for app icons if (PlatformType == "android") { Log.LogMessage(MessageImportance.Low, $"Android Adaptive Icon Generator"); appIconName = appIconName.ToLowerInvariant(); var adaptiveIconGen = new AndroidAdaptiveIconGenerator(img, appIconName, IntermediateOutputPath, this); var iconsGenerated = adaptiveIconGen.Generate(); foreach (var iconGenerated in iconsGenerated) { resizedImages.Add(iconGenerated); } } else if (PlatformType == "ios") { Log.LogMessage(MessageImportance.Low, $"iOS Icon Assets Generator"); var appleAssetGen = new AppleIconAssetsGenerator(img, appIconName, IntermediateOutputPath, appIconDpis, this); var assetsGenerated = appleAssetGen.Generate(); foreach (var assetGenerated in assetsGenerated) { resizedImages.Add(assetGenerated); } } Log.LogMessage(MessageImportance.Low, $"Generating App Icon Bitmaps for DPIs"); var appTool = new SkiaSharpAppIconTools(img, this); Log.LogMessage(MessageImportance.Low, $"App Icon: Intermediate Path " + IntermediateOutputPath); foreach (var dpi in appIconDpis) { Log.LogMessage(MessageImportance.Low, $"App Icon: " + dpi); var destination = Resizer.GetFileDestination(img, dpi, IntermediateOutputPath) .Replace("{name}", appIconName); Log.LogMessage(MessageImportance.Low, $"App Icon Destination: " + destination); appTool.Resize(dpi, Path.ChangeExtension(destination, ".png")); } }
public (SKSizeI, float) GetScaledSize(SKSize originalSize, DpiPath dpi) { if (dpi.Size.HasValue) { return(GetScaledSize(originalSize, dpi.Scale, dpi.Size.Value)); } else { return(GetScaledSize(originalSize, dpi.Scale)); } }
void ProcessImageCopy(ResizeImageInfo img, DpiPath originalScaleDpi, ConcurrentBag <ResizedImageInfo> resizedImages) { var resizer = new Resizer(img, IntermediateOutputPath, this); LogDebugMessage($"Copying {img.Filename}"); var r = resizer.CopyFile(originalScaleDpi, InputsFile); resizedImages.Add(r); LogDebugMessage($"Copied {img.Filename}"); }
void ProcessImageCopy(ResizeImageInfo img, DpiPath originalScaleDpi, ConcurrentBag <ResizedImageInfo> resizedImages) { var resizer = new Resizer(img, IntermediateOutputPath, this); LogDebugMessage($"Copying {img.Filename}"); var r = resizer.CopyFile(originalScaleDpi, InputsFile, PlatformType.Equals("android", StringComparison.OrdinalIgnoreCase)); resizedImages.Add(r); LogDebugMessage($"Copied {img.Filename}"); }
void ProcessImageCopy(SharedImageInfo img, DpiPath originalScaleDpi, ConcurrentBag <ResizedImageInfo> resizedImages) { var resizer = new Resizer(img, IntermediateOutputPath, this); Log.LogMessage(MessageImportance.Low, $"Copying {img.Filename}"); var r = resizer.CopyFile(originalScaleDpi, InputsFile, PlatformType.ToLower().Equals("android")); resizedImages.Add(r); Log.LogMessage(MessageImportance.Low, $"Copied {img.Filename}"); }
public static string GetFileDestination(ResizeImageInfo info, DpiPath dpi, string intermediateOutputPath) { var fullIntermediateOutputPath = new DirectoryInfo(intermediateOutputPath); var destination = Path.Combine(fullIntermediateOutputPath.FullName, dpi.Path, info.OutputName + dpi.FileSuffix + info.OutputExtension); var fileInfo = new FileInfo(destination); if (!fileInfo.Directory.Exists) { fileInfo.Directory.Create(); } return(destination); }
public ResizedImageInfo Generate() { string destinationFolder = IntermediateOutputPath; string fileName = Path.GetFileNameWithoutExtension(Info.OutputName); string destination = Path.Combine(destinationFolder, $"{fileName}.ico"); Directory.CreateDirectory(destinationFolder); Logger.Log($"Generating ICO: {destination}"); var tools = new SkiaSharpAppIconTools(Info, Logger); var dpi = new DpiPath(fileName, 1.0m, size: new SKSize(64, 64)); MemoryStream memoryStream = new MemoryStream(); tools.Resize(dpi, destination, () => memoryStream); memoryStream.Position = 0; int numberOfImages = 1; using BinaryWriter writer = new BinaryWriter(File.Create(destination)); writer.Write((short)0x0); // Reserved. Must always be 0. writer.Write((short)0x1); // Specifies image type: 1 for icon (.ICO) image writer.Write((short)numberOfImages); // Specifies number of images in the file. writer.Write((byte)dpi.Size.Value.Width); writer.Write((byte)dpi.Size.Value.Height); writer.Write((byte)0x0); // Specifies number of colors in the color palette writer.Write((byte)0x0); // Reserved. Should be 0 writer.Write((short)0x1); // Specifies color planes. Should be 0 or 1 writer.Write((short)0x8); // Specifies bits per pixel. writer.Write((int)memoryStream.Length); // Specifies the size of the image's data in bytes int offset = 6 + (16 * numberOfImages); // + length of previous images writer.Write(offset); // Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file // write png data for each image memoryStream.CopyTo(writer.BaseStream); writer.Flush(); return(new ResizedImageInfo { Dpi = dpi, Filename = destination }); }
public ResizedImageInfo CopyFile(DpiPath dpi, string inputsFile, bool isAndroid = false) { var destination = GetFileDestination(dpi); var androidVector = false; if (isAndroid && Info.IsVector && !Info.Resize) { // Update destination to be .xml file destination = Path.ChangeExtension(destination, ".xml"); androidVector = true; } if (IsUpToDate(Info.Filename, destination, inputsFile, Logger)) { return new ResizedImageInfo { Filename = destination, Dpi = dpi } } ; if (androidVector) { Logger.Log("Converting SVG to Android Drawable Vector: " + Info.Filename); // Transform into an android vector drawable var convertErr = Svg2VectorDrawable.Svg2Vector.Convert(Info.Filename, destination); if (!string.IsNullOrEmpty(convertErr)) { throw new Svg2AndroidDrawableConversionException(convertErr, Info.Filename); } } else { // Otherwise just copy it straight File.Copy(Info.Filename, destination, true); } return(new ResizedImageInfo { Filename = destination, Dpi = dpi }); }
public void Resize(DpiPath dpi, string destination, double additionalScale = 1.0, bool dpiSizeIsAbsolute = false) { var sw = new Stopwatch(); sw.Start(); var originalSize = GetOriginalSize(); var absoluteSize = dpiSizeIsAbsolute ? dpi.Size : null; var(scaledSize, scale) = GetScaledSize(originalSize, dpi, absoluteSize); var(canvasSize, _) = GetCanvasSize(dpi, null, this); using (var tempBitmap = new SKBitmap(canvasSize.Width, canvasSize.Height)) { Draw(tempBitmap, additionalScale, originalSize, scale, scaledSize); Save(destination, tempBitmap); } sw.Stop(); Logger?.Log($"Save Image took {sw.ElapsedMilliseconds}ms ({destination})"); }
void Draw(SKBitmap tempBitmap, DpiPath dpi, SKSize unscaledCanvasSize) { var canvasSize = tempBitmap.Info.Size; using var canvas = new SKCanvas(tempBitmap); canvas.Clear(Info.Color ?? SKColors.Transparent); // draw background if (backgroundTools is not null) { canvas.Save(); var backgroundOriginalSize = backgroundTools.GetOriginalSize(); var(bgScaledSize, bgScale) = backgroundTools.GetScaledSize(backgroundOriginalSize, dpi, unscaledCanvasSize); // center the background canvas.Translate( (canvasSize.Width - bgScaledSize.Width) / 2, (canvasSize.Height - bgScaledSize.Height) / 2); // scale the background to the desired size canvas.Scale(bgScale, bgScale); // draw backgroundTools.DrawUnscaled(canvas, bgScale); canvas.Restore(); } // draw foreground if (foregroundTools is not null) { var foregroundOriginalSize = foregroundTools.GetOriginalSize(); var(fgScaledSize, fgScale) = foregroundTools.GetScaledSize(foregroundOriginalSize, dpi, unscaledCanvasSize); // center the foreground canvas.Translate( (canvasSize.Width - fgScaledSize.Width) / 2, (canvasSize.Height - fgScaledSize.Height) / 2); // scale the background to the desired size canvas.Scale(fgScale, fgScale); // add any foreground scale on top if (Info.ForegroundScale != 1.0) { var userFgScale = (float)Info.ForegroundScale; // add the user scale to the main scale fgScale *= userFgScale; // work out the center as if the canvas was exactly the same size as the foreground var fgCenterX = foregroundOriginalSize.Width / 2; var fgCenterY = foregroundOriginalSize.Height / 2; // scale to the user scale, centering canvas.Scale(userFgScale, userFgScale, fgCenterX, fgCenterY); } // draw foregroundTools.DrawUnscaled(canvas, fgScale); } }
public override System.Threading.Tasks.Task ExecuteAsync() { var images = ResizeImageInfo.Parse(Images); var dpis = DpiPath.GetDpis(PlatformType); if (dpis == null || dpis.Length <= 0) { return(System.Threading.Tasks.Task.CompletedTask); } var originalScaleDpi = DpiPath.GetOriginal(PlatformType); var resizedImages = new ConcurrentBag <ResizedImageInfo>(); this.ParallelForEach(images, img => { try { var opStopwatch = new Stopwatch(); opStopwatch.Start(); string op; if (img.IsAppIcon) { // App icons are special ProcessAppIcon(img, resizedImages); op = "App Icon"; } else { // By default we resize, but let's make sure if (img.Resize) { ProcessImageResize(img, dpis, resizedImages); op = "Resize"; } else { // Otherwise just copy the thing over to the 1.0 scale ProcessImageCopy(img, originalScaleDpi, resizedImages); op = "Copy"; } } opStopwatch.Stop(); LogDebugMessage($"{op} took {opStopwatch.ElapsedMilliseconds}ms"); } catch (Exception ex) { LogWarning("MAUI0000", ex.ToString()); throw; } }); if (PlatformType == "tizen") { var tizenResourceXmlGenerator = new TizenResourceXmlGenerator(IntermediateOutputPath, Logger); tizenResourceXmlGenerator.Generate(); } var copiedResources = new List <TaskItem>(); foreach (var img in resizedImages) { var attr = new Dictionary <string, string>(); string itemSpec = Path.GetFullPath(img.Filename); // Fix the item spec to be relative for mac if (bool.TryParse(IsMacEnabled, out bool isMac) && isMac) { itemSpec = img.Filename; } // Add DPI info to the itemspec so we can use it in the targets attr.Add("_ResizetizerDpiPath", img.Dpi.Path); attr.Add("_ResizetizerDpiScale", img.Dpi.Scale.ToString("0.0", CultureInfo.InvariantCulture)); copiedResources.Add(new TaskItem(itemSpec, attr)); } CopiedResources = copiedResources.ToArray(); return(System.Threading.Tasks.Task.CompletedTask); }
public ResizedImageInfo Resize(DpiPath dpi, string destination, Func <Stream> getStream = null) { var sw = new Stopwatch(); sw.Start(); var(bgScaledSize, bgScale) = backgroundTools.GetScaledSize(backgroundOriginalSize, dpi); // Make the canvas size match the desired size var canvasSize = bgScaledSize; if (dpi.Size is SKSize size && size.Width != size.Height) { var scale = (float)dpi.Scale; canvasSize = new SKSizeI((int)(size.Width * scale), (int)(size.Height * scale)); } // Allocate using (var tempBitmap = new SKBitmap(canvasSize.Width, canvasSize.Height)) { // Draw (copy) using (var canvas = new SKCanvas(tempBitmap)) { canvas.Clear(Info.Color ?? SKColors.Transparent); canvas.Save(); canvas.Translate( (canvasSize.Width - bgScaledSize.Width) / 2, (canvasSize.Height - bgScaledSize.Height) / 2); canvas.Scale(bgScale, bgScale); backgroundTools.DrawUnscaled(canvas, bgScale); canvas.Restore(); if (hasForeground) { var userFgScale = (float)Info.ForegroundScale; // get the ratio to make the foreground fill the background var fitRatio = bgScaledSize.Width / foregroundOriginalSize.Width; // calculate the scale for the foreground to fit the background exactly var(fgScaledSize, fgScale) = foregroundTools.GetScaledSize(foregroundOriginalSize, (decimal)fitRatio); //Logger.Log("\tdpi.Size: " + dpi.Size); //Logger.Log("\tdpi.Scale: " + dpi.Scale); //Logger.Log("\tbgScaledSize: " + bgScaledSize); //Logger.Log("\tbgScale: " + bgScale); //Logger.Log("\tforegroundOriginalSize: " + foregroundOriginalSize); //Logger.Log("\tfgScaledSize: " + fgScaledSize); //Logger.Log("\tfgScale: " + fgScale); //Logger.Log("\tuserFgScale: " + userFgScale); // now work out the center as if the canvas was exactly the same size as the foreground var fgScaledSizeCenterX = foregroundOriginalSize.Width / 2; var fgScaledSizeCenterY = foregroundOriginalSize.Height / 2; //Logger.Log("\tfgScaledSizeCenterX: " + fgScaledSizeCenterX); //Logger.Log("\tfgScaledSizeCenterY: " + fgScaledSizeCenterY); // center the foreground canvas.Translate( (canvasSize.Width - fgScaledSize.Width) / 2, (canvasSize.Height - fgScaledSize.Height) / 2); // scale so the forground is the same size as the background canvas.Scale(fgScale, fgScale); // scale to the user scale, centering canvas.Scale(userFgScale, userFgScale, fgScaledSizeCenterX, fgScaledSizeCenterY); foregroundTools.DrawUnscaled(canvas, fgScale * userFgScale); } } // Save (encode) if (getStream is not null) { var stream = getStream(); tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100); } else { using var wrapper = File.Create(destination); tempBitmap.Encode(wrapper, SKEncodedImageFormat.Png, 100); } } sw.Stop(); Logger?.Log($"Save app icon took {sw.ElapsedMilliseconds}ms ({destination})"); return(new ResizedImageInfo { Dpi = dpi, Filename = destination }); }
public string GetFileDestination(DpiPath dpi) => GetFileDestination(Info, dpi, IntermediateOutputPath);
public (SKSizeI, float) GetScaledSize(SKSize originalSize, DpiPath dpi, SKSize?absoluteSize = null) =>
System.Threading.Tasks.Task DoExecute() { Svg.SvgDocument.SkipGdiPlusCapabilityCheck = true; var images = ParseImageTaskItems(SharedImages); var dpis = DpiPath.GetDpis(PlatformType); if (dpis == null || dpis.Length <= 0) { return(System.Threading.Tasks.Task.CompletedTask); } var originalScaleDpi = DpiPath.GetOriginal(PlatformType); var resizedImages = new ConcurrentBag <ResizedImageInfo>(); System.Threading.Tasks.Parallel.ForEach(images, img => { try { var opStopwatch = new Stopwatch(); opStopwatch.Start(); string op; if (img.IsAppIcon) { // App icons are special ProcessAppIcon(img, resizedImages); op = "App Icon"; } else { // By default we resize, but let's make sure if (img.Resize) { ProcessImageResize(img, dpis, resizedImages); op = "Resize"; } else { // Otherwise just copy the thing over to the 1.0 scale ProcessImageCopy(img, originalScaleDpi, resizedImages); op = "Copy"; } } opStopwatch.Stop(); Log.LogMessage(MessageImportance.Low, $"{op} took {opStopwatch.ElapsedMilliseconds}ms"); } catch (Exception ex) { Log.LogErrorFromException(ex); throw; } }); var copiedResources = new List <TaskItem>(); foreach (var img in resizedImages) { var attr = new Dictionary <string, string>(); string itemSpec = Path.GetFullPath(img.Filename); // Fix the item spec to be relative for mac if (bool.TryParse(IsMacEnabled, out bool isMac) && isMac) { itemSpec = img.Filename; } // Add DPI info to the itemspec so we can use it in the targets attr.Add("_ResizetizerDpiPath", img.Dpi.Path); attr.Add("_ResizetizerDpiScale", img.Dpi.Scale.ToString()); copiedResources.Add(new TaskItem(itemSpec, attr)); } CopiedResources = copiedResources.ToArray(); return(System.Threading.Tasks.Task.CompletedTask); }