Beispiel #1
0
        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
            });
        }
    }
Beispiel #2
0
        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
            });
        }
Beispiel #3
0
        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})");
        }
Beispiel #4
0
        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).");
        }
Beispiel #5
0
        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"));
            }
        }
Beispiel #7
0
 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));
     }
 }
Beispiel #8
0
        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}");
        }
Beispiel #9
0
        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}");
        }
Beispiel #11
0
        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);
        }
Beispiel #12
0
        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
            });
        }
Beispiel #13
0
        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
            });
        }
Beispiel #14
0
        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})");
        }
Beispiel #15
0
        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);
            }
        }
Beispiel #16
0
        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);
        }
Beispiel #17
0
        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
            });
        }
Beispiel #18
0
 public string GetFileDestination(DpiPath dpi)
 => GetFileDestination(Info, dpi, IntermediateOutputPath);
Beispiel #19
0
 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);
        }