You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
3.1 KiB
89 lines
3.1 KiB
1 year ago
|
using System;
|
||
|
using System.Linq;
|
||
|
using System.Text;
|
||
|
using System.Text.Json;
|
||
|
using Force.Crc32;
|
||
|
using StabilityMatrix.Avalonia.Models;
|
||
|
using StabilityMatrix.Avalonia.Models.Inference;
|
||
|
|
||
|
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(
|
||
|
byte[] inputImage,
|
||
|
GenerationParameters generationParameters,
|
||
|
InferenceProjectDocument projectDocument
|
||
|
)
|
||
|
{
|
||
|
var imageWidthBytes = inputImage[0x10..0x14];
|
||
|
var imageHeightBytes = inputImage[0x14..0x18];
|
||
|
var imageWidth = BitConverter.ToInt32(imageWidthBytes.Reverse().ToArray());
|
||
|
var imageHeight = BitConverter.ToInt32(imageHeightBytes.Reverse().ToArray());
|
||
|
|
||
|
var idatIndex = SearchBytes(inputImage, Idat);
|
||
|
var iendIndex = SearchBytes(inputImage, Iend);
|
||
|
|
||
|
var textEndIndex = idatIndex - 4; // go back 4 cuz we don't want the length
|
||
|
var existingData = inputImage[..textEndIndex];
|
||
|
|
||
|
var smprojJson = JsonSerializer.Serialize(projectDocument);
|
||
|
var smprojChunk = GetTextChunk("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: {imageWidth}x{imageHeight}, "
|
||
|
+ $"Model hash: {generationParameters.ModelHash}, Model: {generationParameters.ModelName}";
|
||
|
var paramsChunk = GetTextChunk("parameters", paramsData);
|
||
|
|
||
|
// Go back 4 from the idat index because we need the length of the data
|
||
|
idatIndex -= 4;
|
||
|
|
||
|
// Go forward 8 from the iend index because we need the crc
|
||
|
iendIndex += 8;
|
||
|
var actualImageData = inputImage[idatIndex..iendIndex];
|
||
|
|
||
|
var finalImage = existingData
|
||
|
.Concat(smprojChunk)
|
||
|
.Concat(paramsChunk)
|
||
|
.Concat(actualImageData);
|
||
|
|
||
|
return finalImage.ToArray();
|
||
|
}
|
||
|
|
||
|
private static byte[] GetTextChunk(string key, string value)
|
||
|
{
|
||
|
var textData = $"{key}\0{value}";
|
||
|
var textDataLength = BitConverter.GetBytes(textData.Length).Reverse();
|
||
|
var textDataBytes = Text.Concat(Encoding.UTF8.GetBytes(textData)).ToArray();
|
||
|
var crc = BitConverter.GetBytes(Crc32Algorithm.Compute(textDataBytes));
|
||
|
|
||
|
return textDataLength.Concat(textDataBytes).Concat(crc).ToArray();
|
||
|
}
|
||
|
|
||
|
private static int SearchBytes(byte[] haystack, byte[] needle)
|
||
|
{
|
||
|
var limit = haystack.Length - needle.Length;
|
||
|
for (var i = 0; i <= limit; i++)
|
||
|
{
|
||
|
var k = 0;
|
||
|
for (; k < needle.Length; k++)
|
||
|
{
|
||
|
if (needle[k] != haystack[i + k])
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (k == needle.Length)
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
}
|