-
-
Notifications
You must be signed in to change notification settings - Fork 851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds support for encoding 8-bit bitmaps #906
Adds support for encoding 8-bit bitmaps #906
Conversation
Codecov Report
@@ Coverage Diff @@
## master #906 +/- ##
==========================================
- Coverage 89.55% 89.46% -0.09%
==========================================
Files 1031 1031
Lines 46115 46137 +22
Branches 3261 3264 +3
==========================================
- Hits 41297 41278 -19
- Misses 4108 4111 +3
- Partials 710 748 +38
Continue to review full report at Codecov.
|
Codecov Report
@@ Coverage Diff @@
## master #906 +/- ##
==========================================
+ Coverage 89.6% 89.61% +0.01%
==========================================
Files 1060 1060
Lines 46542 46586 +44
Branches 3268 3272 +4
==========================================
+ Hits 41703 41750 +47
+ Misses 4128 4125 -3
Partials 711 711
Continue to review full report at Codecov.
|
@JimBobSquarePants: i have some trouble with unit tests failing for net472 and net462. I cannot not reproduce it locally.
As far as i can tell, this matches the appveyor configuration which is failing. Do you have an idea how i can reproduce this issue? The tests work fine with net462 and net472 when i run them locally with:
|
@brianpopow I think hardware-specific floating point inaccuracies are trolling us again. Which |
@antonfirsov |
@brianpopow for quantization, inconsistencies across platforms are acceptable. You just need to pass a tolerant comparer in |
@@ -1022,7 +1022,8 @@ private void ReadInfoHeader() | |||
this.bmpMetadata.InfoHeaderType = infoHeaderType; | |||
|
|||
// We can only encode at these bit rates so far. | |||
if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel16) | |||
if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel8) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there other options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, 4 bit and 1 bit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor change requests and questions, otherwise looks very good!
where TPixel : struct, IPixel<TPixel> | ||
{ | ||
#if NETCOREAPP2_1 | ||
Span<byte> colorPalette = stackalloc byte[ColorPaletteSize8Bit]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1024 byte is too much for stackallock, we can remove this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the limit? I can never find an explicit instruction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've read something in Joe Duffy's original material suggesting 128 bytes, based on their benchmarks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok. I use 257 bytes in the HuffmanTable
but I'm gonna leave that as-is.
#endif | ||
|
||
var quantizer = new OctreeQuantizer(dither: true, maxColors: 256); | ||
QuantizedFrame<TPixel> quantized = quantizer.CreateFrameQuantizer<TPixel>(this.configuration).QuantizeFrame(image); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FrameQuantizer<T>
and QuantizedFrame<T>
are IDisposable
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also:
IQuantizer
should be configuratble by the user as an Encoder parameter, like we do it in PngEncoder
!
var color = default(Rgba32); | ||
foreach (TPixel quantizedColor in quantized.Palette) | ||
{ | ||
quantizedColor.ToRgba32(ref color); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be a stupid idea and lack of understanding, but:
What if we convert to Rgba32 first, and quantize in Rgba32 space?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, in Octree we're working in the Rgba32
space then converting to/from so I see where you are coming from. However, by converting before quantization we end up allocating a new buffer to house the image. That's probably a lot more costly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally forgot that it will lead to another allocation, so let's keep it for now.
(An idea is starting to formulate in my mind, but of course it would break IQuantizer
API again 😄.)
#if NETCOREAPP2_1 | ||
Span<byte> colorPalette = stackalloc byte[ColorPaletteSize8Bit]; | ||
#else | ||
byte[] colorPalette = new byte[ColorPaletteSize8Bit]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to use MemoryAllocator.AllocateManagedByteBuffer()
for this size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't actually need the #ifdef
anymore. It's supported in latest c# versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean replacing GC array allocation.
where TPixel : struct, IPixel<TPixel> => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); | ||
|
||
[Theory] | ||
[WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] | ||
public void Encode_8BitGray_WithV3Header_Works<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't say enough times, how much I ❤️ this kind of extensive and explanatory test coverage! This is an important enabler for us to do optimizations and refactors.
For #907 stuff I'm literally spending days to add missing tests, so I can change the code safely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
I'm merging, to resolve conflicts with #908.
@brianpopow I've been thinking on the quantizer inaccuracies. Do we really need execute the quantization when when |
@antonfirsov no you are right, when its Gray8, we do not actually need the quantization. I will change that. |
…coding Adds support for encoding 8-bit bitmaps
Prerequisites
Description
This PR add the support for encoding 8-Bit Bitmaps.