diff --git a/DeathSocket/Domain.fs b/DeathSocket/Domain.fs
index 1044c5b..834f72c 100644
--- a/DeathSocket/Domain.fs
+++ b/DeathSocket/Domain.fs
@@ -8,9 +8,9 @@
open SkiaSharp
open System.Threading
- /// The specification used to note the orignial file's location and the
- /// changes the user would like to make to it. (including the save
- /// location of the modified version). Use this spec if you are using
+ /// This specification is used to note the orignial file's location and
+ /// the changes the user would like to make to it. (including the save
+ /// location of the modified version). Use this spec. if you are using
/// System.Drawing.
type BrushSpec =
{ /// The original path of the image which the grid is being added to.
@@ -27,11 +27,11 @@
///The number of columns the grid will have.
columns: int }
- /// The specification used to note the orignial file's location and the
- /// changes the user would like to make to it (including the save
+ /// This specification is used to note the orignial file's location and
+ /// the changes the user would like to make to it (including the save
/// location of the modified version). This specification includes
- /// individual RGBA values to describe the grid's colour. Use this spec.
- /// if you are using System.Drawing.
+ /// individual RGBA values to describe the grid's colour. Use this
+ /// spec. if you are using System.Drawing.
type RGBASpec =
{ /// The original path of the image which the grid is being added to.
originalPath: string
@@ -53,10 +53,10 @@
///The number of columns the grid will have.
columns: int }
- /// The specification used to note the orignial file's location and the
- /// changes the user would like to make to it (including the save
+ /// This specification is used to note the orignial file's location and
+ /// the changes the user would like to make to it (including the save
/// location of the modified version). Use this spec. if you are using
- /// the Skia-Sharp library.
+ /// the SkiaSharp library.
type SkiaSpec =
{ /// The original path of the image which the grid is being added to.
originalPath: string
@@ -71,11 +71,53 @@
///The number of columns the grid will have.
columns: int }
- /// The specification used to note the orignial file's location and the
- /// changes the user would like to make to it (including the save
+ /// This specification is used to create a gridded image as an SKData
+ /// buffer. This spec. allows you to use one of the pre-formed
+ /// SKColors. This makes it quicker and easier to develop your code
+ /// when compared to SkiaRGBStream. That requires setting individual
+ /// RGB values. You this spec. if you are using the SkiaSharp library.
+ type SkiaBufferSpec =
+ { /// The location of the image the grid will be applied to
+ filePath: string
+ /// The (SkiaSharp) colour used to draw the grid.
+ skColour: SKColor
+ /// The thickness of the line on the grid.
+ penWidth: float32
+ /// The number of rows the grid will have.
+ rows: int
+ ///The number of columns the grid will have.
+ columns: int }
+
+ /// This specification is used to create a gridded image as a SKData
+ /// buffer. This spec. allows you to specify the colour of the grid
+ /// using individual RGB values. This gives you greater control of your
+ /// colour choice than a SkiaStreamSpec, which requires a pre-formed
+ /// SKColor value. you this spec. if you are using the SkiaSharp
+ /// library.
+ type SkiaRGBBufferSpec =
+ { /// The location of the image the grid will be applied to
+ filePath: string
+ /// The amount of red (RGB) the grid's line will have.
+ /// (0 = no red, 255 = red turned up to eleven).
+ red: float32
+ /// The amount of green (RGB) the grid's line will have.
+ /// (0 = no green, 255 = green turned up to eleven).
+ green: float32
+ /// The amount of blue (RGB) the grid's line will have.
+ /// (0 = no blue, 255 = blue turned up to eleven).
+ blue: float32
+ /// The thickness of the line on the grid.
+ penWidth: float32
+ /// The number of rows the grid will have.
+ rows: int
+ ///The number of columns the grid will have.
+ columns: int }
+
+ /// This specification is used to note the orignial file's location and
+ /// the changes the user would like to make to it (including the save
/// location of the modified version). This specification includes
/// individual RGB values to describe the grid's colour. Use this spec.
- /// if you are using the Skia-Sharp library.
+ /// if you are using the SkiaSharp library.
type SkiaRGBSpec =
{ /// The original path of the image which the grid is being added to.
originalPath: string
@@ -100,12 +142,20 @@
/// A Discriminated Union representing the various specification types
/// Death Socket can use to apply a grid to an image.
type ImageSpec =
+ /// Spec. which uses System.Drawing and its built-in (colour) Brush.
| Brush of BrushSpec
+ /// Spec. which uses System.Drawing and individual RGBA values.
| RGBA of RGBASpec
+ /// Spec. which uses SkiaSharp and its built-in SKColors.
| Skia of SkiaSpec
+ /// Spec. which uses SkiaSharp and individual RGB values.
| SkiaRGB of SkiaRGBSpec
+ /// Spec. which uses SkiaSharp and its built-in SKColors.
+ | SkiaBuffer of SkiaBufferSpec
+ /// Spec. which uses SkiaSharp and individual RGB values.
+ | SkiaRGBBuffer of SkiaRGBBufferSpec
- /// A Discriminated Union representing a type of image. The type refers
+ /// A Discriminated Union denoting a type of image. The type refers
/// to the graphics library used to read the image file.
/// The graphics library determines how the image is read and
/// manipulated in memory and the functions you can perform on it.
@@ -114,5 +164,7 @@
/// the recommended choice. If you using Xamarin, SkiaSharp is the
/// recommended choice.
type ImageType =
+ /// Denotes an image created using SkiaSharp
| SkiaSharp of string
+ /// Denotes an image created using System.Drawing
| SystemDrawing of string
\ No newline at end of file
diff --git a/DeathSocket/GridPainter.fs b/DeathSocket/GridPainter.fs
index 7240dd5..2e8126e 100644
--- a/DeathSocket/GridPainter.fs
+++ b/DeathSocket/GridPainter.fs
@@ -15,10 +15,13 @@ namespace DeathSocket
open Validation
open ImageServices
+ open SkiaSharp
///
/// Uses the information included in spec to create a gridded image.
/// It then asynchronously saves it. Uses .jpg or .png formats only.
+ /// Please note: Any buffer-based spec's (E.G. SkiaBufferSpec) will be
+ /// ignored by this function.
///
///
/// The specification used to generate the new gridded image. The
@@ -46,6 +49,7 @@ namespace DeathSocket
| SkiaRGB sR ->
validateIO sR.originalPath sR.savePath |> ignore
drawSkiaRGBGrid sR
+ | _ -> printfn "[DEATH SOCKET INFO] Inappropriate ImageSpec used here. Please use none buffer-based spec's when using this function."
with
| :? FileNotFoundException as ex ->
printfn "File could not be found at %s" ex.Message
@@ -104,7 +108,7 @@ namespace DeathSocket
reraise ()
// System.Drawing Functions
- // ========================================================================
+ // ====================================================================
///
/// Determines the (Pen) points needed to draw the appropriate number
@@ -162,7 +166,45 @@ namespace DeathSocket
makeBrushFromRGBASpec spec
// SkiaSharp Functions
- // ========================================================================
+ // ====================================================================
+
+ // NOT TESTED
+
+ ///
+ /// Uses the information included in spec to create a SKData buffer.
+ /// The buffer will contain a gridded image which you can then use in
+ /// any way you see fit. Accepted image formats are limited to .jpg
+ /// and .png. Please note: Any non buffer-based spec's (E.G. SkiaSpec)
+ /// will be ignored by this function.
+ ///
+ ///
+ /// The specification used to generate the new SKData buffer. The
+ /// ImageSpec is a discriminated union, consisting of a SkiaBuffer and
+ /// a SkiaRGBBuffer spec.
+ ///
+ ///
+ /// Make sure the image, which is used to create the SKData buffer,
+ /// is not in use or needed by another program/process.
+ /// This is because it is locked whilst in this function.
+ ///
+ let createSKDataAsync (spec: ImageSpec) =
+ async {
+ try
+ match spec with
+ | SkiaBuffer s ->
+ validateFilePath s.filePath |> ignore
+ return createSkiaBuffer s
+ | SkiaRGBBuffer sR ->
+ validateFilePath sR.filePath |> ignore
+ return createSkiaRGBBuffer sR
+ | _ ->
+ printfn "[DEATH SOCKET INFO] Inappropriate ImageSpec used here. Please use buffer-based spec's when using this function. Returning an empty SKData object."
+ return SKData.Empty
+ with
+ | :? FileNotFoundException as ex ->
+ printfn "%s" ex.Message |> ignore
+ return SKData.Empty
+ }
///
/// Determines the (SKPoints) points needed to draw the appropriate
diff --git a/DeathSocket/ImageServices.fs b/DeathSocket/ImageServices.fs
index d75ed25..26ca891 100644
--- a/DeathSocket/ImageServices.fs
+++ b/DeathSocket/ImageServices.fs
@@ -8,13 +8,25 @@
open SkiaSharp
open System
+ (* Why 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 grid their computer to a halt. On top of
+ that, they have no means to make alterations to the code. *)
+
let setLineThickness pDimension aDimension lineWidth =
if (pDimension <= 0.0 || aDimension <= 0.0 || lineWidth <= 0.0) then
raise (new DivideByZeroException ("[ERROR] The images height and width must be greater than 0."))
else lineWidth / (pDimension / aDimension)
- // SkiaSharp Functions
- // ========================================================================
+ (* SkiaSharp Functions
+ ======================================================================== *)
let createSKHorizontalLines width height rows =
let interval = float32 (height / rows)
@@ -113,8 +125,87 @@
use saveStream = File.OpenWrite (spec.savePath)
data.SaveTo (saveStream)
- // System.Drawing Functions
- // ========================================================================
+ // 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
+ ======================================================================== *)
let createHorizontalLines width height rows =
let interval = height / rows
@@ -135,13 +226,15 @@
(* 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, just assume any function
- with a "*Spec" type as a parameter will use this "temp" file. *)
+ 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 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))
@@ -156,7 +249,8 @@
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 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))