Implemented basic WF packing, some packed WF still doesn't work correctly
parent
0869b979c7
commit
e23093230e
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace WatchFace.Parser.Utils
|
||||
namespace Resources
|
||||
{
|
||||
public class BitWriter
|
||||
{
|
|
@ -77,16 +77,15 @@ namespace Resources
|
|||
private Bitmap ReadImage()
|
||||
{
|
||||
var image = new Bitmap(_width, _height);
|
||||
|
||||
for (var i = 0; i < _height; i++)
|
||||
for (var y = 0; y < _height; y++)
|
||||
{
|
||||
var row = _reader.ReadBytes(_rowLengthInBytes);
|
||||
var bitReader = new BitReader(row);
|
||||
for (var j = 0; j < _width; j++)
|
||||
var rowBytes = _reader.ReadBytes(_rowLengthInBytes);
|
||||
var bitReader = new BitReader(rowBytes);
|
||||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var pixelColorIndex = bitReader.ReadBits(_bitsPerPixel);
|
||||
var color = _palette[(int) pixelColorIndex];
|
||||
image.SetPixel(j, i, color);
|
||||
image.SetPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
return image;
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
|
||||
namespace Resources
|
||||
{
|
||||
public class ImageWriter
|
||||
{
|
||||
private const int MaxSupportedColors = 9;
|
||||
private static readonly byte[] Signature = {(byte) 'B', (byte) 'M', (byte) 'd', 0};
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private readonly Bitmap _image;
|
||||
private readonly List<Color> _palette;
|
||||
|
||||
private readonly BinaryWriter _writer;
|
||||
|
||||
private ushort _bitsPerPixel;
|
||||
private ushort _height;
|
||||
private ushort _paletteColors;
|
||||
private ushort _rowLengthInBytes;
|
||||
private ushort _transparency;
|
||||
private ushort _width;
|
||||
|
||||
public ImageWriter(Stream stream, Bitmap image)
|
||||
{
|
||||
_writer = new BinaryWriter(stream);
|
||||
_image = image;
|
||||
_palette = new List<Color>();
|
||||
}
|
||||
|
||||
public void Write()
|
||||
{
|
||||
_writer.Write(Signature);
|
||||
|
||||
_width = (ushort) _image.Width;
|
||||
_height = (ushort) _image.Height;
|
||||
ExtractPalette();
|
||||
|
||||
_paletteColors = (ushort) _palette.Count;
|
||||
_bitsPerPixel = (ushort) Math.Ceiling(Math.Log(_paletteColors, 2));
|
||||
if (_bitsPerPixel == 3)
|
||||
_bitsPerPixel = 4;
|
||||
|
||||
if (_bitsPerPixel != 1 && _bitsPerPixel != 2 && _bitsPerPixel != 4)
|
||||
throw new ArgumentException($"{0} bits per pixel doesn't supported.");
|
||||
|
||||
_rowLengthInBytes = (ushort) Math.Ceiling(_width * _bitsPerPixel / 8.0);
|
||||
|
||||
if (_paletteColors > MaxSupportedColors)
|
||||
Logger.Warn($"Image cotains {0} colors but biggest known supported values is {1} colors",
|
||||
_paletteColors, MaxSupportedColors);
|
||||
|
||||
WriteHeader();
|
||||
WritePalette();
|
||||
WriteImage();
|
||||
}
|
||||
|
||||
private void ExtractPalette()
|
||||
{
|
||||
Logger.Trace("Extracting palette...");
|
||||
for (var y = 0; y < _height; y++)
|
||||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var color = _image.GetPixel(x, y);
|
||||
if (_palette.Contains(color)) continue;
|
||||
|
||||
if (color.A == 0 && _transparency == 0)
|
||||
{
|
||||
Logger.Trace("Palette item {0}: R {1:X2}, G {2:X2}, B {3:X2}, Transaparent color",
|
||||
_palette.Count, color.R, color.G, color.B
|
||||
);
|
||||
_palette.Insert(0, color);
|
||||
_transparency = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Trace("Palette item {0}: R {1:X2}, G {2:X2}, B {3:X2}",
|
||||
_palette.Count, color.R, color.G, color.B
|
||||
);
|
||||
_palette.Add(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteHeader()
|
||||
{
|
||||
Logger.Trace("Writing image header...");
|
||||
Logger.Trace("Width: {0}, Height: {1}, RowLength: {2}", _width, _height, _rowLengthInBytes);
|
||||
Logger.Trace("BPP: {0}, PaletteColors: {1}, Transaparency: {2}",
|
||||
_bitsPerPixel, _paletteColors, _transparency
|
||||
);
|
||||
|
||||
_writer.Write(_width);
|
||||
_writer.Write(_height);
|
||||
_writer.Write(_rowLengthInBytes);
|
||||
_writer.Write(_bitsPerPixel);
|
||||
_writer.Write(_paletteColors);
|
||||
_writer.Write(_transparency);
|
||||
}
|
||||
|
||||
private void WritePalette()
|
||||
{
|
||||
Logger.Trace("Writing palette...");
|
||||
foreach (var color in _palette)
|
||||
{
|
||||
_writer.Write(color.R);
|
||||
_writer.Write(color.G);
|
||||
_writer.Write(color.B);
|
||||
_writer.Write((byte) 0); // always 0 maybe padding
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteImage()
|
||||
{
|
||||
Logger.Trace("Writing image...");
|
||||
|
||||
var paletteHash = new Dictionary<Color, byte>();
|
||||
byte i = 0;
|
||||
foreach (var color in _palette)
|
||||
{
|
||||
paletteHash[color] = i;
|
||||
i++;
|
||||
}
|
||||
|
||||
for (var y = 0; y < _height; y++)
|
||||
{
|
||||
var rowData = new byte[_rowLengthInBytes];
|
||||
var memoryStream = new MemoryStream(rowData);
|
||||
var bitWriter = new BitWriter(memoryStream);
|
||||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var color = _image.GetPixel(x, y);
|
||||
var paletteIndex = paletteHash[color];
|
||||
bitWriter.WriteBits(paletteIndex, _bitsPerPixel);
|
||||
}
|
||||
bitWriter.Flush();
|
||||
_writer.Write(rowData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,6 +45,8 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BitReader.cs" />
|
||||
<Compile Include="BitWriter.cs" />
|
||||
<Compile Include="ImageWriter.cs" />
|
||||
<Compile Include="ResourcesExtractor.cs" />
|
||||
<Compile Include="ResourcesHeader.cs" />
|
||||
<Compile Include="ImageReader.cs" />
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace WatchFace.Parser.Elements.ActivityElements
|
|||
public long SuffixImageIndex { get; set; }
|
||||
|
||||
[ParameterId(3)]
|
||||
[ParameterImagesCount]
|
||||
[ParameterImageIndex]
|
||||
public long DecimalPointImageIndex { get; set; }
|
||||
}
|
||||
}
|
|
@ -18,10 +18,10 @@ namespace WatchFace.Parser.JsonConverters
|
|||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
var str = reader.ReadAsString();
|
||||
var str = (string)reader.Value;
|
||||
if (str == null || !str.StartsWith("0x"))
|
||||
throw new JsonSerializationException();
|
||||
return Convert.ToUInt32(str);
|
||||
return Convert.ToInt64(str.Substring(2), 16);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ namespace WatchFace.Parser.Utils
|
|||
$"Property {propertyInfo.Name} can't have both ParameterImageIndexAttribute and ParameterImagesCountAttribute"
|
||||
);
|
||||
|
||||
if (propertyType == typeof(long))
|
||||
if (propertyType == typeof(long) || (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)))
|
||||
{
|
||||
if (imageIndexAttribute != null)
|
||||
{
|
||||
|
@ -68,7 +68,10 @@ namespace WatchFace.Parser.Utils
|
|||
throw new ArgumentException(
|
||||
$"Property {propertyInfo.Name} can't be processed becuase ImageIndex isn't present or it is zero"
|
||||
);
|
||||
for (var i = lastImageIndexValue + 1; i < lastImageIndexValue + propertyValue; i++)
|
||||
|
||||
var imagesCount = propertyType.IsGenericType ? propertyValue.Count : propertyValue;
|
||||
|
||||
for (var i = lastImageIndexValue + 1; i < lastImageIndexValue + imagesCount; i++)
|
||||
LoadImage(i.Value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,6 @@
|
|||
<Compile Include="Models\ParameterFlags.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Reader.cs" />
|
||||
<Compile Include="Utils\BitWriter.cs" />
|
||||
<Compile Include="Attributes\ParameterImagesCountAttribute.cs" />
|
||||
<Compile Include="Utils\ImagesLoader.cs" />
|
||||
<Compile Include="Utils\ParametersConverter.cs" />
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using Resources;
|
||||
using WatchFace.Parser.Models;
|
||||
using WatchFace.Parser.Utils;
|
||||
|
||||
|
@ -23,16 +25,83 @@ namespace WatchFace.Parser
|
|||
public void Write(WatchFace watchFace)
|
||||
{
|
||||
var descriptor = ParametersConverter.Build(watchFace);
|
||||
var encodedParameters = new Dictionary<long, MemoryStream>(descriptor.Count);
|
||||
var encodedParameters = new Dictionary<byte, MemoryStream>(descriptor.Count);
|
||||
foreach (var parameter in descriptor)
|
||||
{
|
||||
var memoryStream = new MemoryStream();
|
||||
parameter.Write(memoryStream);
|
||||
foreach (var child in parameter.Children)
|
||||
child.Write(memoryStream);
|
||||
encodedParameters[parameter.Id] = memoryStream;
|
||||
}
|
||||
|
||||
var header = new Header {ParametersSize = 20};
|
||||
var parametersPositions = new List<Parameter>(descriptor.Count + 1);
|
||||
var offset = (long) 0;
|
||||
|
||||
foreach (var encodedParameter in encodedParameters)
|
||||
{
|
||||
var encodedParameterId = encodedParameter.Key;
|
||||
var encodedParameterLength = encodedParameter.Value.Length;
|
||||
parametersPositions.Add(new Parameter(encodedParameterId, new List<Parameter>
|
||||
{
|
||||
new Parameter(1, offset),
|
||||
new Parameter(2, encodedParameterLength)
|
||||
}));
|
||||
offset += encodedParameterLength;
|
||||
}
|
||||
parametersPositions.Insert(0, new Parameter(1, new List<Parameter>
|
||||
{
|
||||
new Parameter(1, offset),
|
||||
new Parameter(2, _images.Count)
|
||||
}));
|
||||
|
||||
var encodedParametersPositions = new MemoryStream();
|
||||
foreach (var parametersPosition in parametersPositions)
|
||||
parametersPosition.Write(encodedParametersPositions);
|
||||
|
||||
var header = new Header
|
||||
{
|
||||
ParametersSize = (uint) encodedParametersPositions.Length,
|
||||
Unknown = 0x37 // Value from Sydney (watchface with 0 doesn't work)
|
||||
};
|
||||
header.WriteTo(_stream);
|
||||
|
||||
encodedParametersPositions.Seek(0, SeekOrigin.Begin);
|
||||
encodedParametersPositions.WriteTo(_stream);
|
||||
|
||||
foreach (var encodedParameter in encodedParameters)
|
||||
{
|
||||
var stream = encodedParameter.Value;
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
stream.WriteTo(_stream);
|
||||
}
|
||||
WriteImages();
|
||||
}
|
||||
|
||||
public void WriteImages()
|
||||
{
|
||||
var offsetsTable = new byte[_images.Count * 4];
|
||||
var encodedImages = new MemoryStream[_images.Count];
|
||||
|
||||
var offset = (uint) 0;
|
||||
for (var i = 0; i < _images.Count; i++)
|
||||
{
|
||||
var offsetBytes = BitConverter.GetBytes(offset);
|
||||
offsetBytes.CopyTo(offsetsTable, i * 4);
|
||||
|
||||
var encodedImage = new MemoryStream();
|
||||
Logger.Debug("Writing image {0}...", i);
|
||||
new ImageWriter(encodedImage, _images[i]).Write();
|
||||
offset += (uint) encodedImage.Length;
|
||||
encodedImages[i] = encodedImage;
|
||||
}
|
||||
|
||||
_stream.Write(offsetsTable, 0, offsetsTable.Length);
|
||||
|
||||
foreach (var encodedImage in encodedImages)
|
||||
{
|
||||
encodedImage.Seek(0, SeekOrigin.Begin);
|
||||
encodedImage.CopyTo(_stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,7 +68,11 @@ namespace WatchFace
|
|||
private static void PackWatchFace(string inputFileName)
|
||||
{
|
||||
var watchFace = ReadConfig(inputFileName);
|
||||
var outputFileName = Path.ChangeExtension(inputFileName, "bin");
|
||||
if (watchFace == null) return;
|
||||
|
||||
var baseName = Path.GetFileNameWithoutExtension(inputFileName);
|
||||
var outputDirectory = Path.GetDirectoryName(inputFileName);
|
||||
var outputFileName = Path.Combine(outputDirectory, baseName + "_packed.bin");
|
||||
SetupLogger(Path.ChangeExtension(outputFileName, ".log"));
|
||||
var imagesDirectory = Path.GetDirectoryName(inputFileName);
|
||||
WriteWatchFace(outputFileName, imagesDirectory, watchFace);
|
||||
|
|
Loading…
Reference in New Issue