Added support for time elements drawing order, fixed displaying time when DrawingOrder isn't present, added drawing preview for state when current temperature isn't available

fonts_experiment
Valeriy Mironov 2017-12-25 12:23:48 +02:00
parent c28196305f
commit 1957abecbc
24 changed files with 203 additions and 141 deletions

View File

@ -46,7 +46,6 @@ namespace Resources
}
FloydSteinbergDitherer.Process(clone);
clone.Save("tmp.png");
return clone;
}
}

View File

@ -11,7 +11,7 @@ namespace WatchFace.Parser.Elements.AnalogDialFaceElements
[ParameterId(1)]
public bool OnlyBorder { get; set; }
[JsonConverter(typeof(HexStringJsonConverter))]
[JsonConverter(typeof(ColorJsonConverter))]
[ParameterId(2)]
public long Color { get; set; }

View File

@ -27,7 +27,7 @@ namespace WatchFace.Parser.Elements.BasicElements
[ParameterId(7)]
public long Width { get; set; }
[JsonConverter(typeof(HexStringJsonConverter))]
[JsonConverter(typeof(ColorJsonConverter))]
[ParameterId(8)]
public long Color { get; set; }
}

View File

@ -1,5 +1,7 @@
using WatchFace.Parser.Attributes;
using Newtonsoft.Json;
using WatchFace.Parser.Attributes;
using WatchFace.Parser.Elements.TimeElements;
using WatchFace.Parser.JsonConverters;
namespace WatchFace.Parser.Elements
{
@ -16,5 +18,9 @@ namespace WatchFace.Parser.Elements
[ParameterId(4)]
public AmPm AmPm { get; set; }
[JsonConverter(typeof(DrawingOrderJsonConverter))]
[ParameterId(5)]
public long DrawingOrder { get; set; }
}
}

View File

@ -1,4 +1,6 @@
using WatchFace.Parser.Attributes;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using WatchFace.Parser.Attributes;
using WatchFace.Parser.Elements.BasicElements;
namespace WatchFace.Parser.Elements.WeatherElements
@ -12,9 +14,23 @@ namespace WatchFace.Parser.Elements.WeatherElements
public TemperatureNumber Night { get; set; }
[ParameterId(3)]
public Coordinates Unknown3 { get; set; }
public Coordinates DayAlt { get; set; }
[ParameterId(4)]
public Coordinates Unknown4 { get; set; }
public Coordinates NightAlt { get; set; }
// For compatibility with "Unknown3" JSON attribute
[JsonProperty("Unknown3")]
private Coordinates Unknown3
{
set => DayAlt = value;
}
// For compatibility with "Unknown4" JSON attribute
[JsonProperty("Unknown4")]
private Coordinates Unknown4
{
set => NightAlt = value;
}
}
}

View File

@ -1,4 +1,6 @@
using WatchFace.Parser.Attributes;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using WatchFace.Parser.Attributes;
using WatchFace.Parser.Elements.BasicElements;
namespace WatchFace.Parser.Elements.WeatherElements
@ -11,9 +13,16 @@ namespace WatchFace.Parser.Elements.WeatherElements
// TODO: Looks like here should be Id 2 also
[ParameterId(3)]
public Coordinates Unknown3 { get; set; }
public Coordinates CoordinatesAlt { get; set; }
[ParameterId(4)]
public Coordinates Unknown4 { get; set; }
// For compatibility with "Unknown3" JSON attribute
[JsonProperty("Unknown3")]
private Coordinates Unknown3
{
set => CoordinatesAlt = value;
}
}
}

View File

@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace WatchFace.Parser.JsonConverters
{
public class HexStringJsonConverter : JsonConverter
public class ColorJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{

View File

@ -0,0 +1,27 @@
using System;
using Newtonsoft.Json;
namespace WatchFace.Parser.JsonConverters
{
public class DrawingOrderJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue($"{value:X}");
}
public override bool CanConvert(Type objectType)
{
return typeof(uint) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var str = (string) reader.Value;
if (str == null)
throw new JsonSerializationException();
return Convert.ToInt64(str, 16);
}
}
}

View File

@ -0,0 +1,10 @@
namespace WatchFace.Parser.Models
{
public enum DrawingOrderPosition
{
HourTens = 1,
HourOnes = 2,
MinuteTens = 3,
MinuteOnes = 4
}
}

View File

@ -22,6 +22,11 @@ namespace WatchFace.Parser.Models.Elements
return new Rectangle((int) X, (int) Y, (int) (BottomRightX - X), (int) (BottomRightY - Y));
}
public Rectangle GetAltBox(CoordinatesElement altCoordinates)
{
return new Rectangle((int) altCoordinates.X, (int) altCoordinates.Y, (int) (BottomRightX - X), (int) (BottomRightY - Y));
}
public void Draw(Graphics drawer, Bitmap[] images, int number, int minimumDigits = 1)
{
DrawerHelper.DrawImages(drawer, GetImagesForNumber(images, number, minimumDigits), (int) Spacing, Alignment, GetBox());

View File

@ -1,18 +0,0 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class HoursElement : TwoDigitsElement, IDrawable
{
public HoursElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
var timeElement = (TimeElement) _parent;
var hours = timeElement.AmPm == null ? state.Time.Hour : state.Time.Hour % 12;
Draw(drawer, resources, hours);
}
}
}

View File

@ -1,16 +0,0 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class MinutesElement : TwoDigitsElement, IDrawable
{
public MinutesElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Time.Minute);
}
}
}

View File

@ -1,16 +0,0 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class SecondsElement : TwoDigitsElement, IDrawable
{
public SecondsElement(Parameter parameter, Element parent, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
Draw(drawer, resources, state.Time.Second);
}
}
}

View File

@ -1,31 +1,70 @@
namespace WatchFace.Parser.Models.Elements
using System.Drawing;
using NLog;
using WatchFace.Parser.Utils;
namespace WatchFace.Parser.Models.Elements
{
public class TimeElement : ContainerElement
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public TimeElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public HoursElement Hours { get; set; }
public MinutesElement Minutes { get; set; }
public SecondsElement Seconds { get; set; }
public TwoDigitsElement Hours { get; set; }
public TwoDigitsElement Minutes { get; set; }
public TwoDigitsElement Seconds { get; set; }
public AmPmElement AmPm { get; set; }
public long DrawingOrder { get; set; }
public override void Draw(Graphics drawer, Bitmap[] images, WatchState state)
{
var hours = AmPm == null ? state.Time.Hour : state.Time.Hour % 12;
var drawingOrder = DrawingOrder == 0 ? 0x1234 : DrawingOrder;
foreach (var position in DrawingOrderIterator.Iterate(drawingOrder))
switch (position)
{
case DrawingOrderPosition.HourTens:
Hours?.Tens?.Draw(drawer, images, hours % 100 / 10);
break;
case DrawingOrderPosition.HourOnes:
Hours?.Ones?.Draw(drawer, images, hours % 10);
break;
case DrawingOrderPosition.MinuteTens:
Minutes?.Tens?.Draw(drawer, images, state.Time.Minute % 100 / 10);
break;
case DrawingOrderPosition.MinuteOnes:
Minutes?.Ones?.Draw(drawer, images, state.Time.Minute % 10);
break;
default:
Logger.Warn("Not supported element {0} in DrawingOrder value", position);
break;
}
Seconds?.Draw(drawer, images, state.Time.Second);
AmPm?.Draw(drawer, images, state);
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Hours = new HoursElement(parameter, this, nameof(Hours));
Hours = new TwoDigitsElement(parameter, this, nameof(Hours));
return Hours;
case 2:
Minutes = new MinutesElement(parameter, this, nameof(Minutes));
Minutes = new TwoDigitsElement(parameter, this, nameof(Minutes));
return Minutes;
case 3:
Seconds = new SecondsElement(parameter, this, nameof(Seconds));
Seconds = new TwoDigitsElement(parameter, this, nameof(Seconds));
return Seconds;
case 4:
AmPm = new AmPmElement(parameter, this, nameof(AmPm));
return AmPm;
case 5:
DrawingOrder = parameter.Value;
return new ValueElement(parameter, this, nameof(DrawingOrder));
default:
return base.CreateChildForParameter(parameter);
}

View File

@ -14,10 +14,11 @@ namespace WatchFace.Parser.Models.Elements
public long MinusImageIndex { get; set; }
public long? DegreesImageIndex { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, int temperature)
public void Draw(Graphics drawer, Bitmap[] resources, int temperature, CoordinatesElement altCoordinates = null)
{
var drawingBox = altCoordinates == null ? Number.GetBox() : Number.GetAltBox(altCoordinates);
var images = GetImagesForTemperature(resources, temperature);
DrawerHelper.DrawImages(drawer, images, (int) Number.Spacing, Number.Alignment, Number.GetBox());
DrawerHelper.DrawImages(drawer, images, (int) Number.Spacing, Number.Alignment, drawingBox);
}
public List<Bitmap> GetImagesForTemperature(Bitmap[] resources, int temperature)

View File

@ -1,31 +1,51 @@
namespace WatchFace.Parser.Models.Elements
using System.Drawing;
namespace WatchFace.Parser.Models.Elements
{
public class SeparateTemperatureElement : ContainerElement
{
public SeparateTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public TodayDayTemperatureElement Day { get; set; }
public TodayNightTemperatureElement Night { get; set; }
public CoordinatesElement Unknown3 { get; set; }
public CoordinatesElement Unknown4 { get; set; }
public TemperatureNumberElement Day { get; set; }
public TemperatureNumberElement Night { get; set; }
public CoordinatesElement DayAlt { get; set; }
public CoordinatesElement NightAlt { get; set; }
public override void Draw(Graphics drawer, Bitmap[] images, WatchState state)
{
if (state.CurrentTemperature != null)
{
if (state.DayTemperature != null)
Day?.Draw(drawer, images, state.DayTemperature.Value);
if (state.NightTemperature != null)
Night?.Draw(drawer, images, state.NightTemperature.Value);
}
else
{
if (state.DayTemperature != null)
Day?.Draw(drawer, images, state.DayTemperature.Value, DayAlt);
if (state.NightTemperature != null)
Night?.Draw(drawer, images, state.NightTemperature.Value, NightAlt);
}
}
protected override Element CreateChildForParameter(Parameter parameter)
{
switch (parameter.Id)
{
case 1:
Day = new TodayDayTemperatureElement(parameter, this);
Day = new TemperatureNumberElement(parameter, this);
return Day;
case 2:
Night = new TodayNightTemperatureElement(parameter, this);
Night = new TemperatureNumberElement(parameter, this);
return Night;
case 3:
Unknown3 = new CoordinatesElement(parameter, this);
return Unknown3;
DayAlt = new CoordinatesElement(parameter, this);
return DayAlt;
case 4:
Unknown4 = new CoordinatesElement(parameter, this);
return Unknown4;
NightAlt = new CoordinatesElement(parameter, this);
return NightAlt;
default:
return base.CreateChildForParameter(parameter);
}

View File

@ -1,17 +0,0 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class TodayDayTemperatureElement : TemperatureNumberElement, IDrawable
{
public TodayDayTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.DayTemperature != null)
Draw(drawer, resources, state.DayTemperature.Value);
}
}
}

View File

@ -1,17 +0,0 @@
using System.Drawing;
using WatchFace.Parser.Interfaces;
namespace WatchFace.Parser.Models.Elements
{
public class TodayNightTemperatureElement : TemperatureNumberElement, IDrawable
{
public TodayNightTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
base(parameter, parent, name) { }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.NightTemperature != null)
Draw(drawer, resources, state.NightTemperature.Value);
}
}
}

View File

@ -10,17 +10,15 @@ namespace WatchFace.Parser.Models.Elements
base(parameter, parent, name) { }
public CoordinatesElement Current { get; set; }
public CoordinatesElement Today { get; set; }
public CoordinatesElement Tomorrow { get; set; }
public CoordinatesElement CurrentAlt { get; set; }
public CoordinatesElement Unknown4 { get; set; }
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
{
if (state.CurrentWeather != WeatherCondition.Unknown && Current != null)
if (state.CurrentTemperature != null)
drawer.DrawImage(LoadWeatherImage(state.CurrentWeather), Current.X, Current.Y);
else if (state.TodayWeather != WeatherCondition.Unknown && Today != null)
drawer.DrawImage(LoadWeatherImage(state.TodayWeather), Today.X, Today.Y);
else if (state.TomorrowWeather != WeatherCondition.Unknown && Tomorrow != null)
drawer.DrawImage(LoadWeatherImage(state.TomorrowWeather), Tomorrow.X, Tomorrow.Y);
else
drawer.DrawImage(LoadWeatherImage(state.CurrentWeather), CurrentAlt.X, CurrentAlt.Y);
}
private static Bitmap LoadWeatherImage(WeatherCondition weather)
@ -38,11 +36,11 @@ namespace WatchFace.Parser.Models.Elements
Current = new CoordinatesElement(parameter, this);
return Current;
case 3:
Today = new CoordinatesElement(parameter, this);
return Today;
CurrentAlt = new CoordinatesElement(parameter, this);
return CurrentAlt;
case 4:
Tomorrow = new CoordinatesElement(parameter, this);
return Tomorrow;
Unknown4 = new CoordinatesElement(parameter, this);
return Unknown4;
default:
return base.CreateChildForParameter(parameter);
}

View File

@ -18,8 +18,6 @@ namespace WatchFace.Parser.Models
public int? TomorrowDayTemperature { get; set; }
public int? TomorrowNightTemperature { get; set; }
public WeatherCondition CurrentWeather { get; set; } = WeatherCondition.Cloudy;
public WeatherCondition TodayWeather { get; set; } = WeatherCondition.Unknown;
public WeatherCondition TomorrowWeather { get; set; } = WeatherCondition.Unknown;
public AirCondition Air { get; set; } = AirCondition.Excellent;
public int BatteryLevel { get; set; } = 67;

View File

@ -35,7 +35,13 @@ namespace WatchFace.Parser
watchState.DayTemperature += 2;
watchState.NightTemperature += 4;
watchState.CurrentTemperature += 3;
if (num < 3)
watchState.CurrentTemperature = null;
else if (num == 3)
watchState.CurrentTemperature = -10;
else
watchState.CurrentTemperature += 6;
watchState.Time = new DateTime(time.Year, num, num * 2 + 5, i * 2, i * 6, i);
using (var image = CreateFrame(previewWatchFace, images, watchState))

View File

@ -0,0 +1,19 @@
using System.Collections;
using WatchFace.Parser.Models;
namespace WatchFace.Parser.Utils
{
public class DrawingOrderIterator
{
public static IEnumerable Iterate(long drawingOrder)
{
var order = drawingOrder;
while (order != 0)
{
var position = (DrawingOrderPosition) ((order & 0xf000) >> 12);
yield return position;
order = (order << 4) & 0xffff;
}
}
}
}

View File

@ -82,7 +82,9 @@
<Compile Include="Elements\WeatherElements\WeatherIcon.cs" />
<Compile Include="Helpers\DrawerHelper.cs" />
<Compile Include="Interfaces\IDrawable.cs" />
<Compile Include="JsonConverters\HexStringJsonConverter.cs" />
<Compile Include="JsonConverters\DrawingOrderJsonConverter.cs" />
<Compile Include="JsonConverters\ColorJsonConverter.cs" />
<Compile Include="Models\DrawingOrderPosition.cs" />
<Compile Include="Models\Elements\Activity\CaloriesElement.cs" />
<Compile Include="Models\Elements\Activity\StepsElement.cs" />
<Compile Include="Models\Elements\Activity\StepsGoalElement.cs" />
@ -115,14 +117,9 @@
<Compile Include="Models\Elements\Date\MonthAndDayElement.cs" />
<Compile Include="Models\Elements\Date\WeekDayElement.cs" />
<Compile Include="Models\Elements\TimeElement.cs" />
<Compile Include="Models\Elements\Time\SecondsElement.cs" />
<Compile Include="Models\Elements\Time\MinutesElement.cs" />
<Compile Include="Models\Elements\Time\HoursElement.cs" />
<Compile Include="Models\Elements\Common\TwoDigitsElement.cs" />
<Compile Include="Models\Elements\WatchFace.cs" />
<Compile Include="Models\Elements\Weather\Temperature\Today\OnelineTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\Today\TodayNightTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\Today\TodayDayTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\Temperature\CurrentTemperatureElement.cs" />
<Compile Include="Models\Elements\Weather\AirPollution\AirPollutionImageElement.cs" />
<Compile Include="Models\Elements\Weather\AirPollutionElement.cs" />
@ -155,6 +152,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Reader.cs" />
<Compile Include="Attributes\ParameterImagesCountAttribute.cs" />
<Compile Include="Utils\DrawingOrderIterator.cs" />
<Compile Include="Utils\ElementsHelper.cs" />
<Compile Include="Utils\ImagesLoader.cs" />
<Compile Include="Utils\ParametersConverter.cs" />
@ -173,12 +171,8 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="WeatherIcons\247.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="WeatherIcons\279.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="WeatherIcons\247.png" />
<EmbeddedResource Include="WeatherIcons\279.png" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -3,7 +3,6 @@ using System.Drawing;
using System.IO;
using NLog;
using WatchFace.Parser.Models;
using WatchFace.Parser.Utils;
namespace WatchFace.Parser
{