module internal ImageServices open System.IO open System.Drawing open System.Drawing.Imaging open DeathSocket open ColourServices open ImagePrep open SkiaSharp (* Note on the Use of Repeated Code =========================================================================== In this file, you will find code which seems duplicated. The areas in question are mostly the graphics/drawing parts of the file. Before you consider refactoring this code, please note the memory footprint of this library. By using "use", the objects created are diposed of properly. This, also, means it is difficult to pass/return them from one function to the next. Therefore, the decision made was to accept the duplicated code in exchange for a smaller memory footprint. Several (large) undisposed images lurking in an end users RAM can grind their computer to a halt. On top of that, they have no means to make alterations to the code. *) (* SkiaSharp Functions ======================================================================== *) // Does not work when using SkiaSharp NuGet v. 1.68 let drawSkiaGrid (spec: SkiaSpec) = use fileStream = File.Open (spec.originalPath, FileMode.Open) use skStream = new SKManagedStream (fileStream) use bitmap = SKBitmap.Decode (skStream) use shader = SKShader.CreateBitmap (bitmap, SKShaderTileMode.Mirror, SKShaderTileMode.Mirror) let imageInfo = new SKImageInfo (bitmap.Width, bitmap.Height) use surface = SKSurface.Create (imageInfo) use canvas = surface.Canvas canvas.Clear (SKColors.White) use imageFill = new SKPaint () imageFill.Style <- SKPaintStyle.Fill imageFill.Shader <- shader canvas.DrawPaint (imageFill) use stroke = new SKPaint () stroke.Style <- SKPaintStyle.Stroke stroke.StrokeWidth <- spec.penWidth stroke.Color <- spec.skColour let horizontalLines = createSKHorizontalLines (bitmap.Width) (bitmap.Height) (spec.rows) let verticalLines = createSKVerticalLines (bitmap.Width) (bitmap.Height) (spec.columns) for hLine in horizontalLines do canvas.DrawLine (hLine.[0], hLine.[1], stroke) for vLine in verticalLines do canvas.DrawLine (vLine.[0], vLine.[1], stroke) use snapshot = surface.Snapshot () use data = match Path.GetExtension (spec.savePath) with | ".jpg" | ".JPG" -> snapshot.Encode (SKEncodedImageFormat.Jpeg, 100) | ".png"| ".PNG" -> snapshot.Encode (SKEncodedImageFormat.Png, 100) | _ -> invalidArg "savePath" "The file type must be a .jpg or .png file." use saveStream = File.OpenWrite (spec.savePath) data.SaveTo (saveStream) // Does not work when using SkiaSharp NuGet v. 1.68 let drawSkiaRGBGrid spec = use fileStream = File.Open (spec.originalPath, FileMode.Open) use skStream = new SKManagedStream (fileStream) use bitmap = SKBitmap.Decode (skStream) use shader = SKShader.CreateBitmap (bitmap, SKShaderTileMode.Mirror, SKShaderTileMode.Mirror) let imageInfo = new SKImageInfo (bitmap.Width, bitmap.Height) use surface = SKSurface.Create (imageInfo) use canvas = surface.Canvas canvas.Clear (SKColors.White) use imageFill = new SKPaint () imageFill.Style <- SKPaintStyle.Fill imageFill.Shader <- shader canvas.DrawPaint (imageFill) use stroke = new SKPaint () stroke.Style <- SKPaintStyle.Stroke stroke.StrokeWidth <- spec.penWidth stroke.Color <- new SKColor ((byte spec.red), (byte spec.green), (byte spec.blue)) let horizontalLines = createSKHorizontalLines (bitmap.Width) (bitmap.Height) (spec.rows) let verticalLines = createSKVerticalLines (bitmap.Width) (bitmap.Height) (spec.columns) for hLine in horizontalLines do canvas.DrawLine (hLine.[0], hLine.[1], stroke) for vLine in verticalLines do canvas.DrawLine (vLine.[0], vLine.[1], stroke) use snapshot = surface.Snapshot () use data = match Path.GetExtension (spec.savePath) with | ".jpg" | ".JPG" -> snapshot.Encode (SKEncodedImageFormat.Jpeg, 100) | ".png"| ".PNG" -> snapshot.Encode (SKEncodedImageFormat.Png, 100) | _ -> invalidArg "savePath" "The file type must be a .jpg or .png file." use saveStream = File.OpenWrite (spec.savePath) data.SaveTo (saveStream) // Does not work when using SkiaSharp NuGet v. 1.68 let createSkiaBuffer (spec: SkiaBufferSpec) = use fileStream = File.Open (spec.filePath, FileMode.Open) use skStream = new SKManagedStream (fileStream) use bitmap = SKBitmap.Decode (skStream) use shader = SKShader.CreateBitmap (bitmap, SKShaderTileMode.Mirror, SKShaderTileMode.Mirror) let imageInfo = new SKImageInfo (bitmap.Width, bitmap.Height) use surface = SKSurface.Create (imageInfo) use canvas = surface.Canvas canvas.Clear (SKColors.White) use imageFill = new SKPaint () imageFill.Style <- SKPaintStyle.Fill imageFill.Shader <- shader canvas.DrawPaint (imageFill) use stroke = new SKPaint () stroke.Style <- SKPaintStyle.Stroke stroke.StrokeWidth <- spec.penWidth stroke.Color <- spec.skColour let horizontalLines = createSKHorizontalLines (bitmap.Width) (bitmap.Height) (spec.rows) let verticalLines = createSKVerticalLines (bitmap.Width) (bitmap.Height) (spec.columns) for hLine in horizontalLines do canvas.DrawLine (hLine.[0], hLine.[1], stroke) for vLine in verticalLines do canvas.DrawLine (vLine.[0], vLine.[1], stroke) use snapshot = surface.Snapshot () let data = match Path.GetExtension (spec.filePath) with | ".jpg" | ".JPG" -> snapshot.Encode (SKEncodedImageFormat.Jpeg, 100) | ".png"| ".PNG" -> snapshot.Encode (SKEncodedImageFormat.Png, 100) | _ -> invalidArg "filePath" "The file type must be a .jpg or .png file." // (data) Buffer returned -- does not dispose automatically. data // Does not work when using SkiaSharp NuGet v. 1.68 let createSkiaRGBBuffer (spec: SkiaRGBBufferSpec) = use fileStream = File.Open (spec.filePath, FileMode.Open) use skStream = new SKManagedStream (fileStream) use bitmap = SKBitmap.Decode (skStream) use shader = SKShader.CreateBitmap (bitmap, SKShaderTileMode.Mirror, SKShaderTileMode.Mirror) let imageInfo = new SKImageInfo (bitmap.Width, bitmap.Height) use surface = SKSurface.Create (imageInfo) use canvas = surface.Canvas canvas.Clear (SKColors.White) use imageFill = new SKPaint () imageFill.Style <- SKPaintStyle.Fill imageFill.Shader <- shader canvas.DrawPaint (imageFill) use stroke = new SKPaint () stroke.Style <- SKPaintStyle.Stroke stroke.StrokeWidth <- spec.penWidth stroke.Color <- new SKColor ((byte spec.red), (byte spec.green), (byte spec.blue)) let horizontalLines = createSKHorizontalLines (bitmap.Width) (bitmap.Height) (spec.rows) let verticalLines = createSKVerticalLines (bitmap.Width) (bitmap.Height) (spec.columns) for hLine in horizontalLines do canvas.DrawLine (hLine.[0], hLine.[1], stroke) for vLine in verticalLines do canvas.DrawLine (vLine.[0], vLine.[1], stroke) use snapshot = surface.Snapshot () let data = match Path.GetExtension (spec.filePath) with | ".jpg" | ".JPG" -> snapshot.Encode (SKEncodedImageFormat.Jpeg, 100) | ".png"| ".PNG" -> snapshot.Encode (SKEncodedImageFormat.Png, 100) | _ -> invalidArg "filePath" "The file type must be a .jpg or .png file." // (data) Buffer returned -- does not dispose automatically. data (* System.Drawing Functions ======================================================================== *) (* Note on Use of Temp. File in Functions which Add A Grid Overlay =========================================================================== The temp. file is used in the functions below are there to convert images with indexed pixels. Instead of listing them all (future additions), just assume any function with a "*Spec" type as a parameter will use this "temp" file. *) let drawBrushSpecGrid (spec: BrushSpec) = use original = Bitmap.FromFile spec.originalPath use temp = new Bitmap(original) use clone = temp.Clone(new Rectangle(0, 0, temp.Width, temp.Height), PixelFormat.Format32bppArgb) use graphics = Graphics.FromImage(clone) use pen = new Pen (spec.colour, width = spec.penWidth) graphics.DrawImage(original,new Rectangle(0, 0, clone.Width, clone.Height)) let horizontalLines = createHorizontalLines (clone.Size.Width) (clone.Size.Height) (spec.rows) let verticalLines = createVerticalLines (clone.Size.Width) (clone.Size.Height) (spec.columns) for line in horizontalLines do graphics.DrawLines (pen, line) for line in verticalLines do graphics.DrawLines (pen, line) clone.Save (spec.savePath) let drawRGBAGrid (spec: RGBASpec) = use original = Bitmap.FromFile spec.originalPath use temp = new Bitmap (original) use clone = temp.Clone (new Rectangle(0, 0, temp.Width, temp.Height), PixelFormat.Format32bppArgb) use graphics = Graphics.FromImage(clone) use pen = new Pen ((makeBrushFromRGBASpec spec), width = spec.penWidth) graphics.DrawImage (original,new Rectangle(0, 0, clone.Width, clone.Height)) let horizontalLines = createHorizontalLines (clone.Size.Width) (clone.Size.Height) (spec.rows) let verticalLines = createVerticalLines (clone.Size.Width) (clone.Size.Height) (spec.columns) for line in horizontalLines do graphics.DrawLines (pen, line) for line in verticalLines do graphics.DrawLines (pen, line) clone.Save (spec.savePath)