Browse Source

update comments and add 'skia stream' functionality

master
Craig Oates 5 years ago
parent
commit
a3d9da4457
  1. 80
      DeathSocket/Domain.fs
  2. 46
      DeathSocket/GridPainter.fs
  3. 110
      DeathSocket/ImageServices.fs

80
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

46
DeathSocket/GridPainter.fs

@ -15,10 +15,13 @@ namespace DeathSocket
open Validation
open ImageServices
open SkiaSharp
/// <summary>
/// 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.
/// </summary>
/// <param name="spec">
/// 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
// ========================================================================
// ====================================================================
/// <summary>
/// Determines the (Pen) points needed to draw the appropriate number
@ -162,7 +166,45 @@ namespace DeathSocket
makeBrushFromRGBASpec spec
// SkiaSharp Functions
// ========================================================================
// ====================================================================
// NOT TESTED
/// <summary>
/// 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.
/// </summary>
/// <param name="spec">
/// The specification used to generate the new SKData buffer. The
/// ImageSpec is a discriminated union, consisting of a SkiaBuffer and
/// a SkiaRGBBuffer spec.
/// </param>
/// <remarks>
/// 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.
/// </remarks>
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
}
/// <summary>
/// Determines the (SKPoints) points needed to draw the appropriate

110
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))

Loading…
Cancel
Save