Added packing all types of images

amazfit_cor
Valeriy Mironov 2018-04-14 16:42:37 +03:00
parent d7d90aeb16
commit 1d73ef6c20
4 changed files with 174 additions and 48 deletions

View File

@ -0,0 +1,10 @@
namespace Resources.Image
{
enum ImageType
{
Paletted = 0x64,
Bpp24 = 0x1b,
Bpp16 = 0x08,
Bpp32 = 0x10
}
}

View File

@ -12,8 +12,11 @@ namespace Resources.Image
public class Writer public class Writer
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly byte[] Signature = {(byte) 'B', (byte) 'M', (byte) 'd', 0};
private readonly List<Color> _palette; private readonly List<Color> _palette;
private bool hasTransparentColor;
private bool hasSemiTransparentColors;
private bool hasPresiceColors;
private ImageType imageType;
private readonly BinaryWriter _writer; private readonly BinaryWriter _writer;
@ -31,29 +34,83 @@ namespace Resources.Image
_palette = new List<Color>(); _palette = new List<Color>();
} }
private byte[] Signature => new byte[] { (byte)'B', (byte)'M', (byte)imageType, 0 };
public void Write(Bitmap image) public void Write(Bitmap image)
{ {
_image = image; _image = image;
_width = (ushort) image.Width; _width = (ushort)image.Width;
_height = (ushort) image.Height; _height = (ushort)image.Height;
ExtractPalette(); ExtractPalette();
if (_bitsPerPixel == 3) _bitsPerPixel = 4; if (hasSemiTransparentColors)
if (_bitsPerPixel == 0) _bitsPerPixel = 1; {
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) if (imageType == ImageType.Paletted) {
throw new ArgumentException( _paletteColors = (ushort)_palette.Count;
$"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 (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); _writer.Write(Signature);
WriteHeader(); 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() private void ExtractPalette()
@ -68,48 +125,35 @@ namespace Resources.Image
var color = context.GetPixel(x, y); var color = context.GetPixel(x, y);
if (_palette.Contains(color)) continue; 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", Logger.Trace("Found fully transaparent color");
_palette.Count, color.R, color.G, color.B
);
_palette.Insert(0, color); _palette.Insert(0, color);
_transparency = 1; hasTransparentColor = true;
} }
else 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); _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;
}
} }
} }
_bitsPerPixel = (ushort)Math.Ceiling(Math.Log(_palette.Count, 2));
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));
} }
private void WriteHeader() 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<Color, byte>(); var paletteHash = new Dictionary<Color, byte>();
byte i = 0; 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);
}
}
}
} }
} }

View File

@ -50,6 +50,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Extractor.cs" /> <Compile Include="Extractor.cs" />
<Compile Include="ImageLoader.cs" /> <Compile Include="ImageLoader.cs" />
<Compile Include="Image\ImageType.cs" />
<Compile Include="Models\FileDescriptor.cs" /> <Compile Include="Models\FileDescriptor.cs" />
<Compile Include="Models\Header.cs" /> <Compile Include="Models\Header.cs" />
<Compile Include="Utils\BitReader.cs" /> <Compile Include="Utils\BitReader.cs" />

View File

@ -9,7 +9,6 @@ namespace WatchFace.Parser.Elements.BatteryElements
public Number Number { get; set; } public Number Number { get; set; }
[ParameterId(2)] [ParameterId(2)]
[ParameterImageIndex]
public CircleScale CircleScale { get; set; } public CircleScale CircleScale { get; set; }
[ParameterId(3)] [ParameterId(3)]