From 1d73ef6c2060d9e8a87f733f6fc276c5254306f7 Mon Sep 17 00:00:00 2001 From: Valeriy Mironov Date: Sat, 14 Apr 2018 16:42:37 +0300 Subject: [PATCH] Added packing all types of images --- Resources/Image/ImageType.cs | 10 + Resources/Image/Writer.cs | 210 ++++++++++++++---- Resources/Resources.csproj | 1 + .../Elements/BatteryElements/BatteryNumber.cs | 1 - 4 files changed, 174 insertions(+), 48 deletions(-) create mode 100644 Resources/Image/ImageType.cs diff --git a/Resources/Image/ImageType.cs b/Resources/Image/ImageType.cs new file mode 100644 index 0000000..c4c7778 --- /dev/null +++ b/Resources/Image/ImageType.cs @@ -0,0 +1,10 @@ +namespace Resources.Image +{ + enum ImageType + { + Paletted = 0x64, + Bpp24 = 0x1b, + Bpp16 = 0x08, + Bpp32 = 0x10 + } +} diff --git a/Resources/Image/Writer.cs b/Resources/Image/Writer.cs index 07028af..467bcec 100644 --- a/Resources/Image/Writer.cs +++ b/Resources/Image/Writer.cs @@ -12,8 +12,11 @@ namespace Resources.Image public class Writer { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private static readonly byte[] Signature = {(byte) 'B', (byte) 'M', (byte) 'd', 0}; private readonly List _palette; + private bool hasTransparentColor; + private bool hasSemiTransparentColors; + private bool hasPresiceColors; + private ImageType imageType; private readonly BinaryWriter _writer; @@ -31,29 +34,83 @@ namespace Resources.Image _palette = new List(); } + private byte[] Signature => new byte[] { (byte)'B', (byte)'M', (byte)imageType, 0 }; + public void Write(Bitmap image) { _image = image; - _width = (ushort) image.Width; - _height = (ushort) image.Height; + _width = (ushort)image.Width; + _height = (ushort)image.Height; ExtractPalette(); - if (_bitsPerPixel == 3) _bitsPerPixel = 4; - if (_bitsPerPixel == 0) _bitsPerPixel = 1; + if (hasSemiTransparentColors) + { + if (hasPresiceColors) + { + imageType = ImageType.Bpp32; + _bitsPerPixel = 32; + } + else + { + imageType = ImageType.Bpp24; + _bitsPerPixel = 24; + } + } + else if (hasTransparentColor) + { + if (_bitsPerPixel > 8) + { + imageType = ImageType.Bpp24; + _bitsPerPixel = 24; + } + else + { + imageType = ImageType.Paletted; + } + } else + { + if (_bitsPerPixel > 8) + { + imageType = ImageType.Bpp16; + _bitsPerPixel = 16; + } + else + { + imageType = ImageType.Paletted; + } + } - if (_bitsPerPixel > 4) - throw new ArgumentException( - $"The image has {_bitsPerPixel} bit/pixel and can't be packed for using on the watches. Looks like dithering works incorrectly on the image." - ); + if (imageType == ImageType.Paletted) { + _paletteColors = (ushort)_palette.Count; + if (hasTransparentColor) _transparency = 1; + if (_bitsPerPixel > 4 && _bitsPerPixel <8) _bitsPerPixel = 8; + if (_bitsPerPixel == 3) _bitsPerPixel = 4; + if (_bitsPerPixel == 0) _bitsPerPixel = 1; + } - _rowLengthInBytes = (ushort) Math.Ceiling(_width * _bitsPerPixel / 8.0); + _rowLengthInBytes = (ushort)Math.Ceiling(_width * _bitsPerPixel / 8.0); _writer.Write(Signature); WriteHeader(); - WritePalette(); - WriteImage(); + + switch (imageType) + { + case ImageType.Paletted: + WritePalette(); + WritePalettedImage(); + break; + case ImageType.Bpp16: + Write16BitImage(); + break; + case ImageType.Bpp24: + Write24BitImage(); + break; + case ImageType.Bpp32: + Write32BitImage(); + break; + } } private void ExtractPalette() @@ -68,48 +125,35 @@ namespace Resources.Image var color = context.GetPixel(x, y); if (_palette.Contains(color)) continue; - if (color.A < 0x80 && _transparency == 0) + Logger.Trace("Palette item {0}: R {1:X2}, G {2:X2}, B {3:X2}, A {4:X2}", + _palette.Count, color.R, color.G, color.B, color.A + ); + + if (!hasTransparentColor && color.A == 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 - ); + Logger.Trace("Found fully transaparent color"); _palette.Insert(0, color); - _transparency = 1; + hasTransparentColor = true; } 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); } + + if (!hasSemiTransparentColors && color.A > 0 && color.A < 0xff) + { + Logger.Trace("Found semi transaparent color"); + hasSemiTransparentColors = true; + } + + if (!hasPresiceColors && ((color.R & 0x7) > 0 || (color.G & 0x3) > 0 || (color.B & 0x3) > 0)) + { + Logger.Trace("Found presice color"); + hasPresiceColors = true; + } } } - - var startIndex = _transparency == 0 ? 0 : 1; - - for (var i = startIndex; i < _palette.Count - 1; i++) - { - var minColor = (uint) _palette[i].ToArgb(); - var minIndex = i; - for (var j = i + 1; j < _palette.Count; j++) - { - var color = (uint) _palette[j].ToArgb(); - if (color >= minColor) continue; - - minColor = color; - minIndex = j; - } - - if (minIndex == i) continue; - - var tmp = _palette[i]; - _palette[i] = _palette[minIndex]; - _palette[minIndex] = tmp; - } - - _paletteColors = (ushort) _palette.Count; - _bitsPerPixel = (ushort) Math.Ceiling(Math.Log(_paletteColors, 2)); + _bitsPerPixel = (ushort)Math.Ceiling(Math.Log(_palette.Count, 2)); } private void WriteHeader() @@ -140,9 +184,9 @@ namespace Resources.Image } } - private void WriteImage() + private void WritePalettedImage() { - Logger.Trace("Writing image..."); + Logger.Trace("Writing paletted image..."); var paletteHash = new Dictionary(); byte i = 0; @@ -178,5 +222,77 @@ namespace Resources.Image } } } + + private void Write16BitImage() + { + Logger.Trace("Writing 16-bit image..."); + using (var context = _image.CreateUnsafeContext()) + { + for (var y = 0; y < _height; y++) + { + var rowData = new byte[_rowLengthInBytes]; + var memoryStream = new MemoryStream(rowData); + for (var x = 0; x < _width; x++) + { + var color = context.GetPixel(x, y); + + byte first = (byte)((color.R >> 3) |((((color.G >> 2) & 0x7)) << 5)); + byte second = (byte)(color.B | ((color.G >> 5) & 0x7)); + + memoryStream.WriteByte(first); + memoryStream.WriteByte(second); + } + _writer.Write(rowData); + } + } + } + + private void Write24BitImage() + { + Logger.Trace("Writing 24-bit image..."); + using (var context = _image.CreateUnsafeContext()) + { + 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 = context.GetPixel(x, y); + + bitWriter.Write((byte)(0xff - color.A)); + bitWriter.WriteBits((ulong)color.B >> 3, 5); + bitWriter.WriteBits((ulong)color.G >> 2, 6); + bitWriter.WriteBits((ulong)color.R >> 3, 5); + } + bitWriter.Flush(); + _writer.Write(rowData); + } + } + } + + private void Write32BitImage() + { + Logger.Trace("Writing 32-bit image..."); + using (var context = _image.CreateUnsafeContext()) + { + for (var y = 0; y < _height; y++) + { + var rowData = new byte[_rowLengthInBytes]; + var memoryStream = new MemoryStream(rowData); + for (var x = 0; x < _width; x++) + { + var color = context.GetPixel(x, y); + + memoryStream.WriteByte(color.R); + memoryStream.WriteByte(color.G); + memoryStream.WriteByte(color.B); + memoryStream.WriteByte((byte)(0xff - color.A)); + } + _writer.Write(rowData); + } + } + } } } \ No newline at end of file diff --git a/Resources/Resources.csproj b/Resources/Resources.csproj index 79d0b28..bc6ff7d 100644 --- a/Resources/Resources.csproj +++ b/Resources/Resources.csproj @@ -50,6 +50,7 @@ + diff --git a/WatchFace.Parser/Elements/BatteryElements/BatteryNumber.cs b/WatchFace.Parser/Elements/BatteryElements/BatteryNumber.cs index ee3b377..e815536 100644 --- a/WatchFace.Parser/Elements/BatteryElements/BatteryNumber.cs +++ b/WatchFace.Parser/Elements/BatteryElements/BatteryNumber.cs @@ -9,7 +9,6 @@ namespace WatchFace.Parser.Elements.BatteryElements public Number Number { get; set; } [ParameterId(2)] - [ParameterImageIndex] public CircleScale CircleScale { get; set; } [ParameterId(3)]