Implemented basic WF packing, some packed WF still doesn't work correctly

fonts_experiment
Valeriy Mironov 2017-11-25 21:34:46 +02:00
parent 0869b979c7
commit e23093230e
10 changed files with 237 additions and 18 deletions

View File

@ -1,7 +1,7 @@
using System;
using System.IO;
namespace WatchFace.Parser.Utils
namespace Resources
{
public class BitWriter
{

View File

@ -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;

143
Resources/ImageWriter.cs Normal file
View File

@ -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);
}
}
}
}

View File

@ -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" />

View File

@ -13,7 +13,7 @@ namespace WatchFace.Parser.Elements.ActivityElements
public long SuffixImageIndex { get; set; }
[ParameterId(3)]
[ParameterImagesCount]
[ParameterImageIndex]
public long DecimalPointImageIndex { get; set; }
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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" />

View File

@ -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);
}
}
}
}

View File

@ -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);