Browse Source

Merge pull request #5 from CraigOates/0.4

0.4
master
Craig Oates 6 years ago committed by GitHub
parent
commit
6d49856502
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      DeathSocket/ColourServices.fs
  2. 16
      DeathSocket/DeathSocket.fsproj
  3. 29
      DeathSocket/Domain.fs
  4. 62
      DeathSocket/GridPainter.fs
  5. 27
      DeathSocket/ImageServices.fs
  6. 4
      DeathSocket/ScratchPad.fsx
  7. 4
      DeathSocketCLI/AssemblyInfo.fs
  8. 4
      DeathSocketCLI/Commands.fs
  9. 2
      DeathSocketCLI/Validation.fs
  10. 4
      TestCentre/ConsoleTests.fs
  11. 93
      TestCentre/LibraryTests.fs
  12. 22
      TestCentre/Script.fsx

17
DeathSocket/ColourServices.fs

@ -0,0 +1,17 @@
module internal ColourServices
open System.Drawing
open DeathSocket.Domain
let makeBrushFromRGBASpec (spec: RGBASpec) =
let a = int spec.alpha
let r = int spec.red
let g = int spec.green
let b = int spec.blue
let colour = Color.FromArgb (a, r, g, b)
new SolidBrush (colour)
let makeBrushFromRGBA r g b a =
let colour = Color.FromArgb (a, r, g, b)
new SolidBrush (colour)

16
DeathSocket/DeathSocket.fsproj

@ -2,11 +2,27 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Version>0.4-alpha</Version>
<Authors>Craig Oates</Authors>
<Product>Death Socket</Product>
<Description>A .Net Standard library which you can plug into to project and draw gridded overlays onto you images. Please note, Death Socket uses System.Drawing brushes and not System.Media brushes. For futher information, visit the projects repository on GitHub. To get a sense of what Death Socket can do, there is a console program you can download seperately (on GitHub).</Description>
<PackageProjectUrl>https://github.com/CraigOates/Death-Socket/tree/master</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/CraigOates/Death-Socket/blob/master/LICENSE</PackageLicenseUrl>
<Company />
<Copyright>Craig Oates</Copyright>
<RepositoryUrl>https://github.com/CraigOates/Death-Socket/tree/master</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>deathsocket craig oates image overlay f#</PackageTags>
<PackageReleaseNotes>This release has renamed the ImageSpec to BrushSpec and added a new RGBASpec type. The RGBASpec allows the user to pass in individual RGBA values instead of creating a SolidBrush before adding it to the BrushSpec.
New functions have been added so you can now create a SolidBrush using individual RGBA values or an RGBASpec -- you do not need to do it yourself. Another "apply grid" function has been added , as well, which uses the new RGBASpec.</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
<Compile Include="Domain.fs" />
<Compile Include="Validation.fs" />
<Compile Include="ColourServices.fs" />
<Compile Include="ImageServices.fs" />
<Compile Include="GridPainter.fs" />
<None Include="ScratchPad.fsx" />

29
DeathSocket/Domain.fs

@ -6,10 +6,8 @@
open System.Drawing
/// <summary>
/// The specification used by Death Socket when adding a grid to an image.
/// </summary>
type ImageSpec =
/// The specification which uses System.Drawing brush to draw a grid.
type BrushSpec =
{ /// The original path of the image which the grid is being added to.
originalPath: string
/// The location of the new gridded image.
@ -21,4 +19,27 @@
/// The number of rows the grid will have.
rows: int
///The number of columns the grid will have.
columns: int }
/// The specification which uses includes individual RGBA values to
/// draw a grid.
type RGBASpec =
{ /// The original path of the image which the grid is being added to.
originalPath: string
/// The location of the new gridded image.
savePath: string
/// The opacity level of the grid.
/// 0 makes it completely see through and 1 makes it solid.
alpha: float
/// The amount of red the grid's line will have.
red: float
/// The amount of green the grid's line will have.
green: float
/// The amount of blue the grid's line will have.
blue: float
/// 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 }

62
DeathSocket/GridPainter.fs

@ -1,6 +1,7 @@
namespace DeathSocket
open System.IO
open ColourServices
/// Provides functions which help draw gridded overlays onto images.
/// Grid Painter, and all of Death Socket, uses the System.Drawing brushes/colours.
@ -11,10 +12,10 @@ namespace DeathSocket
open ImageServices
/// <summary>
/// Uses the information included in spec and creates a gridded image.
/// Uses the information included in spec to create a gridded image.
/// It then asynchronously saves it.
/// Please stick to .bmp, .jpg or .png files.
/// The others (image) file types have not been tested.
/// The other (image) file types have not been tested.
/// </summary>
/// <param name="spec">
/// The specification used to generate the new gridded image
@ -28,17 +29,70 @@ namespace DeathSocket
/// is not in use or needed by another program/process.
/// This is because it is locked whilst in this function.
/// </remarks>
let applyGridAsync spec =
let applyBrushSpecGridAsync (spec: BrushSpec) =
async {
try
validateFilePath spec.originalPath |> ignore
validatFileType spec.savePath |> ignore
drawGrid spec |> ignore
drawBrushSpecGrid spec |> ignore
with
| :? FileNotFoundException as ex ->
printfn "File could not be found at %s" ex.Message
}
/// <summary>
/// Uses the information included in spec to create a gridded image. It
/// then asynchronously saves it. Please stick to .bmp, .jpg or .png
/// files. The other (image) file types have not been tested.
/// </summary>
/// <param name="spec">
/// The specification used to generate the gridded image.
/// </param>
/// <exeption cref="System.IO.FileNotFoundException">
/// If the file the grid is being applied to cannot be found,
/// a FileNotFoundException will be thrown.
/// </exception>
/// <remarks
/// Make sure the image, which is having the overlay added to it,
/// is not in use or needed by another program/process.
/// This is because it is locked whilst in this function.
/// </remarks>
let applyRGBAGridAsync (spec: RGBASpec) =
async {
try
validateFilePath spec.originalPath |> ignore
validatFileType spec.savePath |> ignore
drawRGBAGrid spec
with
| :? FileNotFoundException as ex ->
printfn "File could not be found at %s" ex.Message
}
/// <summary>
/// Creates a Sytsem.Drawing SolidBrush from the individual RGBA values.
/// </summary>
/// <param name="r">The red value.</param>
/// <param name="g">The green value.</param>
/// <param name="b">The blue value.</param>
/// <param name="a">The alpha value.</param>
/// <remarks>
/// Death Socket uses System.Drawing and not System.Media for colours
/// and brushes.
/// </remarks>
let makeSolidBrushFromRGBA r g b a = makeBrushFromRGBA r g b a
/// <summary>
/// Creates a System.Drawing SolidBrush from a RGBASpec.
/// </summary>
/// <param name="spec">
///The specification which the brush is made from.
///</param>
/// <remarks>
/// Death Socket uses System.Drawing and not System.Media for colours
/// and brushes.
/// </remarks>
let makeSolidBrushFromRGBASpec spec = makeBrushFromRGBASpec spec
/// <summary>
/// Determines the (Pen) points needed to draw the appropriate number of horizontal lines (I.E. rows).
/// Each item in the array includes a start and end co-ordinate (point) for each line.

27
DeathSocket/ImageServices.fs

@ -3,6 +3,7 @@
open System.Drawing
open System.Drawing.Imaging
open DeathSocket
open ColourServices
let createHorizontalLines width height rows =
let interval = height / rows
@ -16,8 +17,13 @@
[| Point ((interval * point), 0)
Point ((interval * point), height)|]|]
let drawGrid spec =
// The temp. file is used as a way to convert images with indexed pixels.
(* 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 listig them all, 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)
@ -30,4 +36,19 @@
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)
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)

4
DeathSocket/ScratchPad.fsx

@ -30,7 +30,7 @@ let verticalLines = createVerticalLines 300 600 10
Death Socket assumes either JPEG or PNG files so use other files at your own
risk. Cannot guarantee they will work. Also, either in this spec. can be
changed to suit your needs. *)
let spec =
let spec :ImageSpec =
{ originalPath = desktop + "/test.jpg"
savePath = desktop + "/grid.png"
colour = Brushes.Chartreuse
@ -39,4 +39,4 @@ let spec =
columns = 10 }
// Run this when you have finished building the spec.
GridPainter.applyGrid spec |> Async.RunSynchronously
GridPainter.applyGridAsync spec |> Async.RunSynchronously

4
DeathSocketCLI/AssemblyInfo.fs

@ -38,8 +38,8 @@ open System.Runtime.InteropServices
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [<assembly: AssemblyVersion("1.0.*")>]
[<assembly: AssemblyVersion("0.3.0.0")>]
[<assembly: AssemblyFileVersion("0.3.0.0")>]
[<assembly: AssemblyVersion("0.4.0.0")>]
[<assembly: AssemblyFileVersion("0.4.0.0")>]
do
()

4
DeathSocketCLI/Commands.fs

@ -38,7 +38,7 @@
try
printfn "[INFO.] Adding default grid to image..."
buildDefaultSpec imgPath newPath
|> applyGridAsync
|> applyBrushSpecGridAsync
|> Async.Start
showEndOfCommandMessage
with
@ -57,7 +57,7 @@
try
printfn "[INFO.] Adding grid to image..."
buildSpec imgPath numRows numColumns pWidth colour newPath
|> applyGridAsync
|> applyBrushSpecGridAsync
|> Async.Start
showEndOfCommandMessage
with

2
DeathSocketCLI/Validation.fs

@ -32,6 +32,8 @@
(String.Concat("The colour specifed is invalid.\n",
"Please use the 'list-colours' command to see what you can use."))
(* This function creates a consistent line width amoung different image
sizes. Use this when the user has no means to specify a pen width. *)
let setPenWidth imgWidth imgHeight =
let width = float32 imgWidth
let height = float32 imgHeight

4
TestCentre/ConsoleTests.fs

@ -59,7 +59,7 @@
(setPenWidth (Random().Next()) (Random().Next())) > 0.0f
[<Property>]
let ``Can build the intended default image specification`` () =
let ``Can build the intended default BrushSpec`` () =
let defaultSpec =
{ originalPath = loadPath
savePath = savePath
@ -71,7 +71,7 @@
defaultSpec = spec
[<Property>]
let ``Can build an image specification as intended`` () =
let ``Can build a BrushSpec as intended`` () =
let colourString = randomColourString ()
let brush = parseColour colourString
let pWidth = float32 (Random().Next())

93
TestCentre/LibraryTests.fs

@ -20,13 +20,16 @@
open System.Drawing
open System.Reflection
open System.IO
open DeathSocket.Domain
let rand = Random ()
(* These are duplicates from ConsoleTests.fs (both of them). See point
about helpers. Tests for checking these locations can be found in
ConsoleTests.fs. *)
ConsoleTests.fs.
Also, these folders should not show up in Visual Studios
Solution Explorer -- unless you are viewing the solution in its folder
format. *)
let loadLocation = __SOURCE_DIRECTORY__ + "/LoadingTestArea"
let saveLocation = __SOURCE_DIRECTORY__ + "/SavingTestArea"
@ -46,6 +49,10 @@
Intended for horizontal and vertical line tests. *)
let newNum () = rand.Next(1, 1000)
let newPenWidth () = rand.Next (1, 10)
let newRGBANum () = rand.Next (255)
let imagesInLoadingTestArea =
Directory.GetFileSystemEntries (loadLocation, "*.png")
@ -67,6 +74,17 @@
files
|> Array.iter (fun f -> File.Delete(f))
let makeTestRGBASpec r g b a =
{ originalPath = "test path"
savePath = "test path"
alpha = float a
red = float r
green = float g
blue = float b
penWidth = float32 10
rows = newNum ()
columns = newNum () }
module PropertyTests =
open FsCheck.Xunit
@ -75,28 +93,68 @@
open DeathSocket.GridPainter
open TestingHelpers
open System.IO
open System
(* With regards to the "saving images" tests, you should end up with
one image left over in the SavingTestArea folder. Comment out the
"reset" function to see all the images produced by this test. This will
mean you will need to manually delete the images yourself if you do. *)
[<Property>]
let ``Can apply grid to image and save it`` () =
(* You should end up with one image left over in SavingTestArea.
Comment out the "reset" function to see all the images produced,
by this test. This will mean you will need to manually delete the
images yourself if you do. *)
let ``Can apply grid to image and save it using BrushSpec`` () =
resetSavingTestArea ()
let oPath = generateLoadPath ()
let sPath = generateSavePath oPath
let spec =
let (spec: BrushSpec) =
{ originalPath = oPath
savePath = sPath
colour = randomBrush () :?> Brush
penWidth = float32 1
penWidth = float32 (newPenWidth())
rows = 10
columns = 10 }
applyBrushSpecGridAsync spec
|> Async.RunSynchronously
(File.Exists sPath) = true
[<Property>]
let ``Can apply grid to image and save it using RGBASpec`` () =
resetSavingTestArea ()
let oPath = generateLoadPath ()
let sPath = generateSavePath oPath
let (spec: RGBASpec) =
{ originalPath = oPath
savePath = sPath
alpha = float (newRGBANum ())
red = float (newRGBANum ())
green = float (newRGBANum ())
blue = float (newRGBANum ())
penWidth = float32 (newPenWidth())
rows = 10
columns = 10 }
applyGridAsync spec
applyRGBAGridAsync spec
|> Async.RunSynchronously
(File.Exists sPath) = true
[<Property>]
let ``SolidBrush colour matches the individual RGBA values`` () =
let a = newRGBANum ()
let r = newRGBANum ()
let g = newRGBANum ()
let b = newRGBANum ()
let referenceColour = Color.FromArgb (a, r, g, b)
let brush = makeSolidBrushFromRGBA r g b a
brush.Color = referenceColour
[<Property>]
let ``SolidBrush colour matches the RGBASpec`` () =
let a = newRGBANum ()
let r = newRGBANum ()
let g = newRGBANum ()
let b = newRGBANum ()
let referenceColour = Color.FromArgb (a, r, g, b)
let referenceSpec = makeTestRGBASpec r g b a
let brush = makeSolidBrushFromRGBASpec referenceSpec
brush.Color = referenceColour
[<Property>]
let ``Can return a collection of points which represent a grids horizontal lines`` () =
let result = determineHorizontalLines (newNum()) (newNum()) (newNum())
@ -113,8 +171,9 @@
open Xunit
open DeathSocket
open System
open System.IO
(* This test is a precaution (a test for the tests if you will...).
(* This test is a pre-test test (a test for the tests if you will...).
It is here to make sure the property test has what it needs to run.
If the property test fails, here is a good place to start.
See script.fs (in Test Centre) for information on populating the
@ -125,6 +184,16 @@
let imagesAreThere = if length < 100 then false else true
Assert.True imagesAreThere
(* This test is a pre-test test. If the property tests fails, here is a
good place to start. The easiest way to get this test to pass is to
create a folder called "SavingTestArea" in this projects folder
(at the root). You can, also, see script.fs (in Test Centre) for
creating this folder via code. (See "LoadingTestArea contains..." note
for extra context. *)
[<Fact>]
let ``SavingTestArea folder can be found`` () =
Assert.True (Directory.Exists saveLocation)
[<Fact>]
let ``Divide By Zero Exception is thrown when 0 rows is used when determining horizontal lines`` () =
let result () = GridPainter.determineHorizontalLines 100 100 0

22
TestCentre/Script.fsx

@ -14,7 +14,7 @@ let loadLocation = __SOURCE_DIRECTORY__ + "/LoadingTestArea"
let saveLocation = __SOURCE_DIRECTORY__ + "/SavingTestArea"
let random = Random()
(*Resetting the Testing Area Folders Scripts
(* Resetting the Testing Area Folders Scripts
===============================================================================
The following scripts are for when you need to "manually" clear out the
LoadingTestArea and SavingTestArea folders. If you do not want to open up the
@ -93,4 +93,22 @@ let populateLoadingTestArea () =
// You should only need this once.
// Make sure you have passed the above into F# Interactive.
populateLoadingTestArea ()
populateLoadingTestArea ()
(* Creating the Saving Test Area Script
==============================================================================
This bit of code is for creating the SavingTestArea folder which Test Centre
will use when it runs its tests. You will normally only need to run this code
when you have just cloned this repository or you accidently deleted said
fodler. In other words, you should only need to use it once. *)
let createSavingTestArea () =
match Directory.Exists saveLocation with
| false ->
Directory.CreateDirectory saveLocation |> ignore
printfn "SavingTestArea created."
| _ -> printfn "SavingTestArea already exists."
(* Before calling this function, make sure the test checking this folder exists
is failing first. *)
createSavingTestArea ()
Loading…
Cancel
Save