Skip to content

Commit

Permalink
Started working on a custom image parser library since System.Drawing…
Browse files Browse the repository at this point in the history
… seems to be Windows only
  • Loading branch information
jmeaster30 committed Sep 25, 2022
1 parent 8964dc9 commit 4171c8b
Show file tree
Hide file tree
Showing 14 changed files with 278 additions and 60 deletions.
13 changes: 13 additions & 0 deletions BlastIMG/BlastIMG.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>

</Project>
41 changes: 41 additions & 0 deletions BlastIMG/BlastImage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace BlastIMG;

public enum FileFormat
{
BMP,
PNG,
JPEG,
QOI
}

public class Pixel
{
public int X { get; }
public int Y { get; }
public byte R { get; }
public byte G { get; }
public byte B { get; }
public byte A { get; }
}

public class Image
{
public FileFormat Format { get; }
public int Width { get; }
public int Height { get; }
public Pixel[,] Pixels { get; }

public Pixel GetPixel(int x, int y)
{
return Pixels[x, y];
}

public static Image Load(string imagePath)
{
if (string.IsNullOrWhiteSpace(imagePath))
throw new ArgumentException("Image path cannot be null or whitespace.", nameof(imagePath));


return new Image();
}
}
117 changes: 117 additions & 0 deletions BlastIMG/ImageLoaders/BmpLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System.Text;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using JsonConverter = Newtonsoft.Json.JsonConverter;

namespace BlastIMG.ImageLoaders;

enum CompressionMethod
{
BI_RGB, // 0
BI_RLE8, // 1
BI_RLE4, // 2
BI_BITFIELDS, // 3
BI_JPEG, // 4
BI_PNG, // 5
BI_ALPHABITFIELDS, // 6
BI_CMYK, // 11
BI_CMYKRLE8, // 12
BI_CMYKRLE4, // 13
}

class BitmapFileHeader
{
public string MagicNumber { get; set; }
public int FileSize { get; set; }
// there are 4 bytes that are set by the program that creates the image but we probably don't need it
public int PixelArrayOffset { get; set; }
}

class BitmapInfoHeader
{
public int HeaderSize { get; set; }
public int BitmapWidth { get; set; }
public int BitmapHeight { get; set; }
public short ColorPlanes { get; set; } // must be 1
public short BitsPerPixel { get; set; }
public CompressionMethod CompressionMethod { get; set; }
public int RawImageSize { get; set; } // can be set to 0
public int HorizontalResolution { get; set; } // ppm
public int VerticalResolution { get; set; } // ppm
public int ColorPalette { get; set; } // 0 for all
public int ImportantColors { get; set; }
}

public static class BmpLoader
{
public static Image Load(string filepath)
{
using var imageFile = File.OpenRead(filepath);

var fileHeader = imageFile.ReadBytes(0, 14);
var parsedFileHeader = new BitmapFileHeader
{
MagicNumber = Encoding.Default.GetString(fileHeader[..2]),
FileSize = BitConverter.ToInt32(fileHeader[2..6]),
PixelArrayOffset = BitConverter.ToInt32(fileHeader[10..14])
};

var headerSize = BitConverter.ToInt32(imageFile.ReadBytes(14, 4));
var dibHeader = imageFile.ReadBytes(14, headerSize);
// TODO pull more of the header data here if the header isn't the default header
var parsedDibHeader = new BitmapInfoHeader
{
HeaderSize = headerSize,
BitmapWidth = BitConverter.ToInt32(dibHeader[4..8]),
BitmapHeight = BitConverter.ToInt32(dibHeader[8..12]),
ColorPlanes = BitConverter.ToInt16(dibHeader[12..14]),
BitsPerPixel = BitConverter.ToInt16(dibHeader[14..16]),
CompressionMethod = BitConverter.ToInt32(dibHeader[16..20]).GetCompressionMethod(),
RawImageSize = BitConverter.ToInt32(dibHeader[20..24]),
HorizontalResolution = BitConverter.ToInt32(dibHeader[24..28]),
VerticalResolution = BitConverter.ToInt32(dibHeader[28..32]),
ColorPalette = BitConverter.ToInt32(dibHeader[32..36]),
ImportantColors = BitConverter.ToInt32(dibHeader[36..40]),
};

Console.Out.WriteLine($"{parsedFileHeader.MagicNumber}");
Console.Out.WriteLine($"{parsedFileHeader.FileSize}");
Console.Out.WriteLine($"{parsedFileHeader.PixelArrayOffset}");
Console.Out.WriteLine($"Width: {parsedDibHeader.BitmapWidth}");
Console.Out.WriteLine($"Height: {parsedDibHeader.BitmapHeight}");
Console.Out.WriteLine(JsonConvert.SerializeObject(parsedDibHeader, Formatting.Indented, new JsonConverter[] {new StringEnumConverter()}));

return new Image(); // DUMMY FOR NOW
}
}

static class MyExtensions {
public static CompressionMethod GetCompressionMethod(this int method)
{
return method switch
{
0 => CompressionMethod.BI_RGB,
1 => CompressionMethod.BI_RLE8,
2 => CompressionMethod.BI_RLE4,
3 => CompressionMethod.BI_BITFIELDS,
4 => CompressionMethod.BI_JPEG,
5 => CompressionMethod.BI_PNG,
6 => CompressionMethod.BI_ALPHABITFIELDS,
11 => CompressionMethod.BI_CMYK,
12 => CompressionMethod.BI_CMYKRLE8,
13 => CompressionMethod.BI_CMYKRLE4,
_ => throw new ArgumentException("Unknown Bitmap Compression Method", nameof(method))
};
}

public static byte[] ReadBytes(this Stream stream, int fileOffset, int count)
{
var buffer = new byte[count];
stream.Seek(fileOffset, SeekOrigin.Begin);
var bytesRead = stream.Read(buffer, 0, count);
if (bytesRead != count)
Console.Error.WriteLine("Didn't read the correct amount of bytes");
return buffer;
}
}
14 changes: 14 additions & 0 deletions BlastPDF.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShowCase", "ShowCase\ShowCa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlastPDF.Test", "BlastPDF.Test\BlastPDF.Test.csproj", "{331127D4-4996-4D21-A808-09CA2B2CC975}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlastIMG", "BlastIMG\BlastIMG.csproj", "{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -58,5 +60,17 @@ Global
{331127D4-4996-4D21-A808-09CA2B2CC975}.Release|x64.Build.0 = Release|Any CPU
{331127D4-4996-4D21-A808-09CA2B2CC975}.Release|x86.ActiveCfg = Release|Any CPU
{331127D4-4996-4D21-A808-09CA2B2CC975}.Release|x86.Build.0 = Release|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Debug|x64.ActiveCfg = Debug|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Debug|x64.Build.0 = Debug|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Debug|x86.ActiveCfg = Debug|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Debug|x86.Build.0 = Debug|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Release|Any CPU.Build.0 = Release|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Release|x64.ActiveCfg = Release|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Release|x64.Build.0 = Release|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Release|x86.ActiveCfg = Release|Any CPU
{DA68CCF5-1B74-4539-8827-1BDC385BA4BE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
4 changes: 4 additions & 0 deletions BlastPDF/BlastPDF.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BlastIMG\BlastIMG.csproj" />
</ItemGroup>

</Project>
17 changes: 17 additions & 0 deletions ShowCase/ImageParsingExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using BlastIMG;
using BlastIMG.ImageLoaders;

namespace ShowCase;

public class ImageParsingExample
{
public static void Run(string imagePath)
{
var loaded = BmpLoader.Load(imagePath);

Console.WriteLine($"Loaded: {imagePath}");
Console.WriteLine($"Width: {loaded.Width}");
Console.WriteLine($"Height: {loaded.Height}");
}
}
69 changes: 69 additions & 0 deletions ShowCase/PdfBuilderExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.IO;
using BlastPDF.Builder;
using BlastPDF.Builder.Graphics;
using BlastPDF.Builder.Graphics.Drawing;
using BlastPDF.Exporter.Basic;

namespace ShowCase;

public class PdfBuilderExample
{
public static void Run(string outputPdfName)
{
if (File.Exists(outputPdfName)) {
File.Delete(outputPdfName);
}

var graphicsGroup = PdfGraphicsGroup.Create();

var triangleWidth = 5.0M;
for(int i = 0; i < 96; i++) {
graphicsGroup.Add(
PdfGraphicsObject.Create()
.Translate(500, 500)
.Rotate(i * (decimal)Math.PI / 96)
.LineWidth(2)
.SetStrokeRGB(i / 96.0M, 0.0M, 1.0M - (i / 96.0M))
.DrawTriangle(0, 0, triangleWidth)
.ResetState()
);
triangleWidth *= 1.070M;
}

using FileStream fs = File.Create(outputPdfName);
PdfDocument.Create()
.AddPage(PdfPage.Create()
.DotsPerInch(100)
.Width(10)
.Height(10)
.AddResource("GoodImage", PdfImage.FromFile("cat.png", PdfColorSpace.DeviceRGB))
.AddGraphics(PdfGraphicsObject.Create()
.SetCMYK(0.0M, 0.0M, 1.0M, 0.0M)
.Rect(0, 0, 1000, 1000)
.Paint(PaintModeEnum.CloseFillStroke))
.AddGraphics(graphicsGroup)
.AddGraphics(PdfGraphicsObject.Create()
.Translate(250, 702)
.Resource("GoodImage")))
.Save(fs);
}
}

public static class MyExtension {
public static PdfGraphicsObject DrawTriangle(this PdfGraphicsObject path, decimal x, decimal y, decimal length)
{
var radius = length / (2 * (decimal)Math.Cos(Math.PI / 6));
var apothem = length * (decimal)Math.Tan(Math.PI / 6) / 2;

var top = (x, y + radius);
var left = (x - (length / 2), y - apothem);
var right = (x + (length / 2), y - apothem);

return path
.Move(top.Item1, top.Item2)
.Line(left.Item1, left.Item2)
.Line(right.Item1, right.Item2)
.Paint(PaintModeEnum.CloseStroke);
}
}
63 changes: 3 additions & 60 deletions ShowCase/Program.cs
Original file line number Diff line number Diff line change
@@ -1,67 +1,10 @@
using BlastPDF.Exporter.Basic;
using BlastPDF.Builder;
using BlastPDF.Builder.Graphics;
using BlastPDF.Builder.Graphics.Drawing;
using System;
using System.IO;


namespace ShowCase;

public class Program {
public static void Main(string[] args) {
string path = "test.pdf";

if (File.Exists(path)) {
File.Delete(path);
}

var graphicsGroup = PdfGraphicsGroup.Create();

var triangleWidth = 5.0M;
for(int i = 0; i < 96; i++) {
graphicsGroup.Add(
PdfGraphicsObject.Create()
.Translate(500, 500)
.Rotate(i * (decimal)Math.PI / 96)
.LineWidth(2)
.SetStrokeRGB(i / 96.0M, 0.0M, 1.0M - (i / 96.0M))
.DrawTriangle(0, 0, triangleWidth)
.ResetState()
);
triangleWidth *= 1.070M;
}

using FileStream fs = File.Create(path);
PdfDocument.Create()
.AddPage(PdfPage.Create()
.DotsPerInch(100)
.Width(10)
.Height(10)
.AddGraphics(PdfGraphicsObject.Create()
.SetCMYK(0.0M, 0.0M, 1.0M, 0.0M)
.Rect(0, 0, 1000, 1000)
.Paint(PaintModeEnum.CloseFillStroke))
.AddGraphics(graphicsGroup))
.Save(fs);
//PdfBuilderExample.Run("test.pdf");
ImageParsingExample.Run("../../../images/w3c_home_256.bmp");
}


}

public static class MyExtension {
public static PdfGraphicsObject DrawTriangle(this PdfGraphicsObject path, decimal x, decimal y, decimal length)
{
var radius = length / (2 * (decimal)Math.Cos(Math.PI / 6));
var apothem = length * (decimal)Math.Tan(Math.PI / 6) / 2;

var top = (x, y + radius);
var left = (x - (length / 2), y - apothem);
var right = (x + (length / 2), y - apothem);

return path
.Move(top.Item1, top.Item2)
.Line(left.Item1, left.Item2)
.Line(right.Item1, right.Item2)
.Paint(PaintModeEnum.CloseStroke);
}
}
Binary file added ShowCase/cat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ShowCase/images/cat.bmp
Binary file not shown.
Binary file added ShowCase/images/w3c_home.bmp
Binary file not shown.
Binary file added ShowCase/images/w3c_home_2.bmp
Binary file not shown.
Binary file added ShowCase/images/w3c_home_256.bmp
Binary file not shown.
Binary file added ShowCase/images/w3c_home_gray.bmp
Binary file not shown.

0 comments on commit 4171c8b

Please sign in to comment.