Strted adding support for validating images on packing watchface, added support for alpha-channel on extracting images

fonts_experiment
Valeriy Mironov 2017-11-25 12:10:44 +02:00
parent 9c2b7b8781
commit 68274901de
39 changed files with 202 additions and 142 deletions

View File

@ -1,19 +1,21 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using NLog;
namespace Resources
{
public class ImageReader
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly BinaryReader _reader;
private ushort _bitsPerPixel;
private ushort _height;
private List<Color> _palette;
private Color[] _palette;
private ushort _paletteColors;
private ushort _rowLengthInBytes;
private ushort _transp;
private ushort _transparency;
private ushort _width;
public ImageReader(Stream stream)
@ -34,25 +36,35 @@ namespace Resources
private void ReadHeader()
{
Logger.Trace("Reading image header...");
_width = _reader.ReadUInt16();
_height = _reader.ReadUInt16();
_rowLengthInBytes = _reader.ReadUInt16();
_bitsPerPixel = _reader.ReadUInt16();
_paletteColors = _reader.ReadUInt16();
_transp = _reader.ReadUInt16();
_transparency = _reader.ReadUInt16();
Logger.Trace("Image header was read:");
Logger.Trace("Width: {0}, Height: {1}, RowLength: {2}", _width, _height, _rowLengthInBytes);
Logger.Trace("BPP: {0}, PaletteColors: {1}, Transaparency: {2}",
_bitsPerPixel, _paletteColors, _transparency
);
}
private void ReadPalette()
{
_palette = new List<Color>(_paletteColors);
Logger.Trace("Reading palette...");
_palette = new Color[_paletteColors];
for (var i = 0; i < _paletteColors; i++)
{
var r = _reader.ReadByte();
var g = _reader.ReadByte();
var b = _reader.ReadByte();
var a = _reader.ReadByte();
Logger.Trace("Palette item {0}: R {1:x2}, G {2:x2}, B {3:x2}, A {4:x2}", i, r, g, b, a);
_palette.Add(Color.FromArgb(a, r, g, b));
var color = _transparency > 0 && i == 0 ? Color.Transparent : Color.FromArgb(0xff, r, g, b);
_palette[i] = color;
}
}

View File

@ -20,9 +20,9 @@ namespace Resources
{
for (var i = 0; i < _images.Length; i++)
{
var fileName = Path.Combine(outputDirectory, $"{i}.bmp");
var fileName = Path.Combine(outputDirectory, $"{i}.png");
Logger.Debug("Exporting {0}...", fileName);
_images[i].Save(fileName, ImageFormat.Bmp);
_images[i].Save(fileName, ImageFormat.Png);
}
}
}

View File

@ -6,19 +6,19 @@ namespace WatchFace.Parser.Elements
{
public class Activity
{
[RawParameter(Id = 3)]
public Number Calories { get; set; }
[RawParameter(Id = 4)]
public Number Pulse { get; set; }
[RawParameter(Id = 1)]
[ParameterId(1)]
public Number Steps { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public Number StepsGoal { get; set; }
[RawParameter(Id = 5)]
[ParameterId(3)]
public Number Calories { get; set; }
[ParameterId(4)]
public Number Pulse { get; set; }
[ParameterId(5)]
public FormattedNumber Distance { get; set; }
}
}

View File

@ -5,13 +5,15 @@ namespace WatchFace.Parser.Elements.ActivityElements
{
public class FormattedNumber
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Number Number { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
[ParameterImageIndex]
public long SuffixImageIndex { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
[ParameterImagesCount]
public long DecimalPointImageIndex { get; set; }
}
}

View File

@ -1,17 +1,17 @@
using WatchFace.Parser.Elements.BasicElements;
using WatchFace.Parser.Elements.AnalogDialFaceElements;
using WatchFace.Parser.Utils;
namespace WatchFace.Parser.Elements
{
public class AnalogDialFace
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public ClockHand Hours { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public ClockHand Minutes { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public ClockHand Seconds { get; set; }
}
}

View File

@ -1,26 +1,27 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using WatchFace.Parser.Elements.BasicElements;
using WatchFace.Parser.JsonConverters;
using WatchFace.Parser.Utils;
namespace WatchFace.Parser.Elements.BasicElements
namespace WatchFace.Parser.Elements.AnalogDialFaceElements
{
public class ClockHand
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public long Unknown1 { get; set; }
[JsonConverter(typeof(HexStringJsonConverter))]
[RawParameter(Id = 2)]
[ParameterId(2)]
public long Color { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public Coordinates Center { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public List<Coordinates> Shape { get; set; }
[RawParameter(Id = 5)]
[ParameterId(5)]
public Image CenterImage { get; set; }
}
}

View File

@ -5,7 +5,7 @@ namespace WatchFace.Parser.Elements
{
public class Background
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Image Image { get; set; }
}
}

View File

@ -6,29 +6,29 @@ namespace WatchFace.Parser.Elements.BasicElements
{
public class CircleScale
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public long CenterX { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public long CenterY { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public long RadiusX { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public long RadiusY { get; set; }
[RawParameter(Id = 5)]
[ParameterId(5)]
public long StartAngle { get; set; }
[RawParameter(Id = 6)]
[ParameterId(6)]
public long EndAngle { get; set; }
[RawParameter(Id = 7)]
[ParameterId(7)]
public long Width { get; set; }
[JsonConverter(typeof(HexStringJsonConverter))]
[RawParameter(Id = 8)]
[ParameterId(8)]
public long Color { get; set; }
}
}

View File

@ -4,10 +4,10 @@ namespace WatchFace.Parser.Elements.BasicElements
{
public class Coordinates
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public long X { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public long Y { get; set; }
}
}

View File

@ -4,13 +4,14 @@ namespace WatchFace.Parser.Elements.BasicElements
{
public class Image
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public long X { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public long Y { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
[ParameterImageIndex]
public long ImageIndex { get; set; }
}
}

View File

@ -4,16 +4,18 @@ namespace WatchFace.Parser.Elements.BasicElements
{
public class ImageSet
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public long X { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public long Y { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
[ParameterImageIndex]
public long ImageIndex { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
[ParameterImagesCount]
public long ImagesCount { get; set; }
}
}

View File

@ -4,28 +4,30 @@ namespace WatchFace.Parser.Elements.BasicElements
{
public class Number
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public long TopLeftX { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public long TopLeftY { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public long BottomRightX { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public long BottomRightY { get; set; }
[RawParameter(Id = 5)]
[ParameterId(5)]
public long Alignment { get; set; }
[RawParameter(Id = 6)]
[ParameterId(6)]
public long Unknown6 { get; set; }
[RawParameter(Id = 7)]
[ParameterId(7)]
[ParameterImageIndex]
public long ImageIndex { get; set; }
[RawParameter(Id = 8)]
[ParameterId(8)]
[ParameterImagesCount]
public long ImagesCount { get; set; }
}
}

View File

@ -5,10 +5,12 @@ namespace WatchFace.Parser.Elements.BasicElements
{
public class Scale
{
[RawParameter(Id = 1)]
[ParameterId(1)]
[ParameterImageIndex]
public long StartImageIndex { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
[ParameterImagesCount]
public List<Coordinates> Segments { get; set; }
}
}

View File

@ -5,13 +5,13 @@ namespace WatchFace.Parser.Elements
{
public class Battery
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Number Text { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public ImageSet Icon { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public Scale Scale { get; set; }
}
}

View File

@ -6,10 +6,10 @@ namespace WatchFace.Parser.Elements
{
public class Date
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public MonthAndDay MonthAndDay { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public ImageSet WeekDay { get; set; }
}
}

View File

@ -4,16 +4,16 @@ namespace WatchFace.Parser.Elements.DateElements
{
public class MonthAndDay
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public SeparateMonthAndDay Separate { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public OneLineMonthAndDay OneLine { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public long Unknown3 { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public long Unknown4 { get; set; }
}
}

View File

@ -5,10 +5,11 @@ namespace WatchFace.Parser.Elements.DateElements
{
public class OneLineMonthAndDay
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Number Number { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
[ParameterImageIndex]
public long DelimiterImageIndex { get; set; }
}
}

View File

@ -5,10 +5,12 @@ namespace WatchFace.Parser.Elements.DateElements
{
public class SeparateMonthAndDay
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Number Month { get; set; }
[RawParameter(Id = 3)]
// TODO: Looks like here should be Id 2 also
[ParameterId(3)]
public Number Day { get; set; }
}
}

View File

@ -5,16 +5,16 @@ namespace WatchFace.Parser.Elements
{
public class Status
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Switch Bluetooth { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public Flag Alarm { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public Flag Lock { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public Flag DoNotDisturb { get; set; }
}
}

View File

@ -5,10 +5,11 @@ namespace WatchFace.Parser.Elements.StatusElements
{
public class Flag
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Coordinates Coordinates { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
[ParameterImageIndex]
public long ImageIndex { get; set; }
}
}

View File

@ -5,13 +5,15 @@ namespace WatchFace.Parser.Elements.StatusElements
{
public class Switch
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Coordinates Coordinates { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
[ParameterImageIndex]
public long ImageIndexOn { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
[ParameterImageIndex]
public long ImageIndexOff { get; set; }
}
}

View File

@ -5,13 +5,13 @@ namespace WatchFace.Parser.Elements
{
public class StepsProgress
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Image GoalImage { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public Scale Linear { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public CircleScale Circle { get; set; }
}
}

View File

@ -5,16 +5,16 @@ namespace WatchFace.Parser.Elements
{
public class Time
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public TwoDigits Hours { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public TwoDigits Minutes { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public TwoDigits Seconds { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public AmPm AmPm { get; set; }
}
}

View File

@ -4,16 +4,18 @@ namespace WatchFace.Parser.Elements.TimeElements
{
public class AmPm
{
[RawParameter(Id = 4)]
public long ImageIndexPm { get; set; }
[RawParameter(Id = 3)]
public long ImageIndexAm { get; set; }
[RawParameter(Id = 1)]
[ParameterId(1)]
public long X { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public long Y { get; set; }
[ParameterId(3)]
[ParameterImageIndex]
public long ImageIndexAm { get; set; }
[ParameterId(4)]
[ParameterImageIndex]
public long ImageIndexPm { get; set; }
}
}

View File

@ -5,10 +5,10 @@ namespace WatchFace.Parser.Elements.TimeElements
{
public class TwoDigits
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public ImageSet Tens { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public ImageSet Ones { get; set; }
}
}

View File

@ -5,13 +5,13 @@ namespace WatchFace.Parser.Elements
{
public class Weather
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public WeatherIcon Icon { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public Temperature Temperature { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public AirPollution AirPollution { get; set; }
}
}

View File

@ -5,7 +5,9 @@ namespace WatchFace.Parser.Elements.WeatherElements
{
public class AirPollution
{
[RawParameter(Id = 2)]
// TODO: Looks like here should be Id 1 also
[ParameterId(2)]
public ImageSet Icon { get; set; }
}
}

View File

@ -5,19 +5,22 @@ namespace WatchFace.Parser.Elements.WeatherElements
{
public class OneLineTemperature
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Number Number { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
[ParameterImageIndex]
public long MinusSignImageIndex { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
[ParameterImageIndex]
public long DelimiterImageIndex { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public long AppendDegreesForBoth { get; set; }
[RawParameter(Id = 5)]
[ParameterId(5)]
[ParameterImageIndex]
public long DegreesImageIndex { get; set; }
}
}

View File

@ -5,16 +5,16 @@ namespace WatchFace.Parser.Elements.WeatherElements
{
public class SeparateTemperature
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public TemperatureNumber Day { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public TemperatureNumber Night { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public Coordinates Unknown3 { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public Coordinates Unknown4 { get; set; }
}
}

View File

@ -4,10 +4,10 @@ namespace WatchFace.Parser.Elements.WeatherElements
{
public class Temperature
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public TemperatureNumber Current { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public TodayTemperature Today { get; set; }
}
}

View File

@ -5,13 +5,15 @@ namespace WatchFace.Parser.Elements.WeatherElements
{
public class TemperatureNumber
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Number Number { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
[ParameterImageIndex]
public long MinusImageIndex { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
[ParameterImageIndex]
public long DegreesImageIndex { get; set; }
}
}

View File

@ -4,10 +4,10 @@ namespace WatchFace.Parser.Elements.WeatherElements
{
public class TodayTemperature
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public SeparateTemperature Separate { get; set; }
[RawParameter(Id = 2)]
[ParameterId(2)]
public OneLineTemperature OneLine { get; set; }
}
}

View File

@ -5,13 +5,15 @@ namespace WatchFace.Parser.Elements.WeatherElements
{
public class WeatherIcon
{
[RawParameter(Id = 1)]
[ParameterId(1)]
public Coordinates Coordinates { get; set; }
[RawParameter(Id = 3)]
// TODO: Looks like here should be Id 2 also
[ParameterId(3)]
public Coordinates Unknown3 { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public Coordinates Unknown4 { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace WatchFace.Parser.Utils
{
[AttributeUsage(AttributeTargets.Property)]
public class ParameterIdAttribute : Attribute
{
public ParameterIdAttribute(byte id)
{
Id = id;
}
public byte Id { get; }
}
}

View File

@ -3,8 +3,5 @@
namespace WatchFace.Parser.Utils
{
[AttributeUsage(AttributeTargets.Property)]
public class RawParameterAttribute : Attribute
{
public byte Id { get; set; }
}
public class ParameterImageIndexAttribute : Attribute { }
}

View File

@ -0,0 +1,7 @@
using System;
namespace WatchFace.Parser.Utils
{
[AttributeUsage(AttributeTargets.Property)]
public class ParameterImagesCountAttribute : Attribute { }
}

View File

@ -128,18 +128,18 @@ namespace WatchFace.Parser.Utils
var properties = new Dictionary<byte, PropertyInfo>();
foreach (var propertyInfo in typeInfo.DeclaredProperties)
{
var rawParameterAttribute =
(RawParameterAttribute) propertyInfo.GetCustomAttribute(typeof(RawParameterAttribute));
if (rawParameterAttribute == null)
var parameterIdAttribute =
(ParameterIdAttribute) propertyInfo.GetCustomAttribute(typeof(ParameterIdAttribute));
if (parameterIdAttribute == null)
throw new ArgumentException(
$"Class {typeInfo.Name} doesn't have RawParameterAttribute on property {propertyInfo.Name}"
$"Class {typeInfo.Name} doesn't have ParameterIdAttribute on property {propertyInfo.Name}"
);
if (properties.ContainsKey(rawParameterAttribute.Id))
if (properties.ContainsKey(parameterIdAttribute.Id))
throw new ArgumentException(
$"Class {typeInfo.Name} already has RawParameterAttribute with Id {rawParameterAttribute.Id}"
$"Class {typeInfo.Name} already has ParameterIdAttribute with Id {parameterIdAttribute.Id}"
);
properties[rawParameterAttribute.Id] = propertyInfo;
properties[parameterIdAttribute.Id] = propertyInfo;
}
return properties.OrderBy(kv => kv.Key).ToDictionary(kv => kv.Key, kv => kv.Value);
}

View File

@ -50,7 +50,7 @@
<Compile Include="Elements\AnalogDialFace.cs" />
<Compile Include="Elements\Background.cs" />
<Compile Include="Elements\BasicElements\CircleScale.cs" />
<Compile Include="Elements\BasicElements\ClockHand.cs" />
<Compile Include="Elements\AnalogDialFaceElements\ClockHand.cs" />
<Compile Include="Elements\BasicElements\Coordinates.cs" />
<Compile Include="Elements\BasicElements\Image.cs" />
<Compile Include="Elements\BasicElements\ImageSet.cs" />
@ -83,8 +83,10 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Reader.cs" />
<Compile Include="Utils\BitWriter.cs" />
<Compile Include="Utils\ParameterImagesCountAttribute.cs" />
<Compile Include="Utils\ParametersConverter.cs" />
<Compile Include="Utils\RawParameterAttribute.cs" />
<Compile Include="Utils\ParameterImageIndexAttribute.cs" />
<Compile Include="Utils\ParameterIdAttribute.cs" />
<Compile Include="WatchFace.cs" />
<Compile Include="Writer.cs" />
</ItemGroup>

View File

@ -5,31 +5,31 @@ namespace WatchFace.Parser
{
public class WatchFace
{
[RawParameter(Id = 2)]
[ParameterId(2)]
public Background Background { get; set; }
[RawParameter(Id = 3)]
[ParameterId(3)]
public Time Time { get; set; }
[RawParameter(Id = 4)]
[ParameterId(4)]
public Activity Activity { get; set; }
[RawParameter(Id = 5)]
[ParameterId(5)]
public Date Date { get; set; }
[RawParameter(Id = 6)]
[ParameterId(6)]
public Weather Weather { get; set; }
[RawParameter(Id = 7)]
[ParameterId(7)]
public StepsProgress StepsProgress { get; set; }
[RawParameter(Id = 8)]
[ParameterId(8)]
public Status Status { get; set; }
[RawParameter(Id = 9)]
[ParameterId(9)]
public Battery Battery { get; set; }
[RawParameter(Id = 10)]
[ParameterId(10)]
public AnalogDialFace AnalogDialFace { get; set; }
}
}