-
-
Notifications
You must be signed in to change notification settings - Fork 551
Expand file tree
/
Copy pathPngDataHelper.cs
More file actions
135 lines (114 loc) · 5.37 KB
/
PngDataHelper.cs
File metadata and controls
135 lines (114 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
using System.Text;
using System.Text.Json;
using Force.Crc32;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Core.Models;
namespace StabilityMatrix.Avalonia.Helpers;
public static class PngDataHelper
{
private static readonly byte[] Idat = { 0x49, 0x44, 0x41, 0x54 };
private static readonly byte[] Text = { 0x74, 0x45, 0x58, 0x74 };
private static readonly byte[] Iend = { 0x49, 0x45, 0x4E, 0x44 };
public static byte[] AddMetadata(
Stream inputStream,
GenerationParameters generationParameters,
InferenceProjectDocument projectDocument
)
{
using var ms = new MemoryStream();
inputStream.CopyTo(ms);
return AddMetadata(ms.ToArray(), generationParameters, projectDocument);
}
public static byte[] AddMetadata(
byte[] inputImage,
GenerationParameters generationParameters,
InferenceProjectDocument projectDocument
)
{
using var memoryStream = new MemoryStream();
var position = 8; // Skip the PNG signature
memoryStream.Write(inputImage, 0, position);
var metadataInserted = false;
while (position < inputImage.Length)
{
var chunkLength = BitConverter.ToInt32(
inputImage[position..(position + 4)].AsEnumerable().Reverse().ToArray(),
0
);
var chunkType = Encoding.ASCII.GetString(inputImage[(position + 4)..(position + 8)]);
switch (chunkType)
{
case "IHDR":
{
var imageWidthBytes = inputImage[(position + 8)..(position + 12)];
var imageHeightBytes = inputImage[(position + 12)..(position + 16)];
var imageWidth = BitConverter.ToInt32(imageWidthBytes.AsEnumerable().Reverse().ToArray());
var imageHeight = BitConverter.ToInt32(
imageHeightBytes.AsEnumerable().Reverse().ToArray()
);
generationParameters.Width = imageWidth;
generationParameters.Height = imageHeight;
break;
}
case "IDAT" when !metadataInserted:
{
var smprojJson = JsonSerializer.Serialize(projectDocument);
var smprojChunk = BuildTextChunk("smproj", smprojJson);
var paramsData =
$"{generationParameters.PositivePrompt}\nNegative prompt: {generationParameters.NegativePrompt}\n"
+ $"Steps: {generationParameters.Steps}, Sampler: {generationParameters.Sampler}, "
+ $"CFG scale: {generationParameters.CfgScale}, Seed: {generationParameters.Seed}, "
+ $"Size: {generationParameters.Width}x{generationParameters.Height}, "
+ $"Model hash: {generationParameters.ModelHash}, Model: {generationParameters.ModelName}";
var paramsChunk = BuildTextChunk("parameters", paramsData);
var paramsJson = JsonSerializer.Serialize(generationParameters);
var paramsJsonChunk = BuildTextChunk("parameters-json", paramsJson);
memoryStream.Write(paramsChunk, 0, paramsChunk.Length);
memoryStream.Write(paramsJsonChunk, 0, paramsJsonChunk.Length);
memoryStream.Write(smprojChunk, 0, smprojChunk.Length);
metadataInserted = true; // Ensure we only insert the metadata once
break;
}
}
// Write the current chunk to the output stream
memoryStream.Write(inputImage, position, chunkLength + 12); // Write the length, type, data, and CRC
position += chunkLength + 12;
}
return memoryStream.ToArray();
}
public static byte[] RemoveMetadata(byte[] inputImage)
{
using var memoryStream = new MemoryStream();
var position = 8; // Skip the PNG signature
memoryStream.Write(inputImage, 0, position);
while (position < inputImage.Length)
{
var chunkLength = BitConverter.ToInt32(
inputImage[position..(position + 4)].AsEnumerable().Reverse().ToArray(),
0
);
var chunkType = Encoding.ASCII.GetString(inputImage[(position + 4)..(position + 8)]);
// If the chunk is not a text chunk, write it to the output
if (chunkType != "tEXt" && chunkType != "zTXt" && chunkType != "iTXt")
{
memoryStream.Write(inputImage, position, chunkLength + 12); // Write the length, type, data, and CRC
}
// Move to the next chunk
position += chunkLength + 12;
}
return memoryStream.ToArray();
}
private static byte[] BuildTextChunk(string key, string value)
{
var textData = $"{key}\0{value}";
var dataBytes = Encoding.UTF8.GetBytes(textData);
var textDataLength = BitConverter.GetBytes(dataBytes.Length).AsEnumerable().Reverse().ToArray();
var textDataBytes = Text.Concat(dataBytes).ToArray();
var crc = BitConverter
.GetBytes(Crc32Algorithm.Compute(textDataBytes))
.AsEnumerable()
.Reverse()
.ToArray();
return textDataLength.Concat(textDataBytes).Concat(crc).ToArray();
}
}