@@ -56,6 +56,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
5656 /// </summary>
5757 private const int ColorPaletteSize4Bit = 64 ;
5858
59+ /// <summary>
60+ /// The color palette for an 1 bit image will have 2 entry's with 4 bytes for each entry.
61+ /// </summary>
62+ private const int ColorPaletteSize1Bit = 8 ;
63+
5964 /// <summary>
6065 /// Used for allocating memory during processing operations.
6166 /// </summary>
@@ -79,7 +84,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
7984 private readonly bool writeV4Header ;
8085
8186 /// <summary>
82- /// The quantizer for reducing the color count for 8-Bit images.
87+ /// The quantizer for reducing the color count for 8-Bit, 4-Bit and 1-Bit images.
8388 /// </summary>
8489 private readonly IQuantizer quantizer ;
8590
@@ -93,7 +98,7 @@ public BmpEncoderCore(IBmpEncoderOptions options, MemoryAllocator memoryAllocato
9398 this . memoryAllocator = memoryAllocator ;
9499 this . bitsPerPixel = options . BitsPerPixel ;
95100 this . writeV4Header = options . SupportTransparency ;
96- this . quantizer = options . Quantizer ?? KnownQuantizers . Octree ;
101+ this . quantizer = options . Quantizer ?? KnownQuantizers . Wu ;
97102 }
98103
99104 /// <summary>
@@ -180,6 +185,10 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
180185 {
181186 colorPaletteSize = ColorPaletteSize4Bit ;
182187 }
188+ else if ( this . bitsPerPixel == BmpBitsPerPixel . Pixel1 )
189+ {
190+ colorPaletteSize = ColorPaletteSize1Bit ;
191+ }
183192
184193 var fileHeader = new BmpFileHeader (
185194 type : BmpConstants . TypeMarkers . Bitmap ,
@@ -241,6 +250,10 @@ private void WriteImage<TPixel>(Stream stream, ImageFrame<TPixel> image)
241250 case BmpBitsPerPixel . Pixel4 :
242251 this . Write4BitColor ( stream , image ) ;
243252 break ;
253+
254+ case BmpBitsPerPixel . Pixel1 :
255+ this . Write1BitColor ( stream , image ) ;
256+ break ;
244257 }
245258 }
246259
@@ -325,7 +338,7 @@ private void Write16Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
325338 }
326339
327340 /// <summary>
328- /// Writes an 8 Bit image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
341+ /// Writes an 8 bit image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
329342 /// </summary>
330343 /// <typeparam name="TPixel">The type of the pixel.</typeparam>
331344 /// <param name="stream">The <see cref="Stream"/> to write to.</param>
@@ -349,7 +362,7 @@ private void Write8Bit<TPixel>(Stream stream, ImageFrame<TPixel> image)
349362 }
350363
351364 /// <summary>
352- /// Writes an 8 Bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
365+ /// Writes an 8 bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
353366 /// </summary>
354367 /// <typeparam name="TPixel">The type of the pixel.</typeparam>
355368 /// <param name="stream">The <see cref="Stream"/> to write to.</param>
@@ -377,7 +390,7 @@ private void Write8BitColor<TPixel>(Stream stream, ImageFrame<TPixel> image, Spa
377390 }
378391
379392 /// <summary>
380- /// Writes an 8 Bit gray image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
393+ /// Writes an 8 bit gray image with a color palette. The color palette has 256 entry's with 4 bytes for each entry.
381394 /// </summary>
382395 /// <typeparam name="TPixel">The type of the pixel.</typeparam>
383396 /// <param name="stream">The <see cref="Stream"/> to write to.</param>
@@ -415,7 +428,7 @@ private void Write8BitGray<TPixel>(Stream stream, ImageFrame<TPixel> image, Span
415428 }
416429
417430 /// <summary>
418- /// Writes an 4 Bit color image with a color palette. The color palette has 16 entry's with 4 bytes for each entry.
431+ /// Writes an 4 bit color image with a color palette. The color palette has 16 entry's with 4 bytes for each entry.
419432 /// </summary>
420433 /// <typeparam name="TPixel">The type of the pixel.</typeparam>
421434 /// <param name="stream">The <see cref="Stream"/> to write to.</param>
@@ -458,6 +471,52 @@ private void Write4BitColor<TPixel>(Stream stream, ImageFrame<TPixel> image)
458471 }
459472 }
460473
474+ /// <summary>
475+ /// Writes a 1 bit image with a color palette. The color palette has 2 entry's with 4 bytes for each entry.
476+ /// </summary>
477+ /// <typeparam name="TPixel">The type of the pixel.</typeparam>
478+ /// <param name="stream">The <see cref="Stream"/> to write to.</param>
479+ /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param>
480+ private void Write1BitColor < TPixel > ( Stream stream , ImageFrame < TPixel > image )
481+ where TPixel : unmanaged, IPixel < TPixel >
482+ {
483+ using IQuantizer < TPixel > frameQuantizer = this . quantizer . CreatePixelSpecificQuantizer < TPixel > ( this . configuration , new QuantizerOptions ( )
484+ {
485+ MaxColors = 2
486+ } ) ;
487+ using IndexedImageFrame < TPixel > quantized = frameQuantizer . BuildPaletteAndQuantizeFrame ( image , image . Bounds ( ) ) ;
488+ using IMemoryOwner < byte > colorPaletteBuffer = this . memoryAllocator . AllocateManagedByteBuffer ( ColorPaletteSize1Bit , AllocationOptions . Clean ) ;
489+
490+ Span < byte > colorPalette = colorPaletteBuffer . GetSpan ( ) ;
491+ ReadOnlySpan < TPixel > quantizedColorPalette = quantized . Palette . Span ;
492+ this . WriteColorPalette ( stream , quantizedColorPalette , colorPalette ) ;
493+
494+ ReadOnlySpan < byte > quantizedPixelRow = quantized . GetPixelRowSpan ( 0 ) ;
495+ int rowPadding = quantizedPixelRow . Length % 8 != 0 ? this . padding - 1 : this . padding ;
496+ for ( int y = image . Height - 1 ; y >= 0 ; y -- )
497+ {
498+ quantizedPixelRow = quantized . GetPixelRowSpan ( y ) ;
499+
500+ int endIdx = quantizedPixelRow . Length % 8 == 0 ? quantizedPixelRow . Length : quantizedPixelRow . Length - 8 ;
501+ for ( int i = 0 ; i < endIdx ; i += 8 )
502+ {
503+ Write1BitPalette ( stream , i , i + 8 , quantizedPixelRow ) ;
504+ }
505+
506+ if ( quantizedPixelRow . Length % 8 != 0 )
507+ {
508+ int startIdx = quantizedPixelRow . Length - 7 ;
509+ endIdx = quantizedPixelRow . Length ;
510+ Write1BitPalette ( stream , startIdx , endIdx , quantizedPixelRow ) ;
511+ }
512+
513+ for ( int i = 0 ; i < rowPadding ; i ++ )
514+ {
515+ stream . WriteByte ( 0 ) ;
516+ }
517+ }
518+ }
519+
461520 /// <summary>
462521 /// Writes the color palette to the stream. The color palette has 4 bytes for each entry.
463522 /// </summary>
@@ -478,5 +537,25 @@ private void WriteColorPalette<TPixel>(Stream stream, ReadOnlySpan<TPixel> quant
478537
479538 stream . Write ( colorPalette ) ;
480539 }
540+
541+ /// <summary>
542+ /// Writes a 1-bit palette.
543+ /// </summary>
544+ /// <param name="stream">The stream to write the palette to.</param>
545+ /// <param name="startIdx">The start index.</param>
546+ /// <param name="endIdx">The end index.</param>
547+ /// <param name="quantizedPixelRow">A quantized pixel row.</param>
548+ private static void Write1BitPalette ( Stream stream , int startIdx , int endIdx , ReadOnlySpan < byte > quantizedPixelRow )
549+ {
550+ int shift = 7 ;
551+ byte indices = 0 ;
552+ for ( int j = startIdx ; j < endIdx ; j ++ )
553+ {
554+ indices = ( byte ) ( indices | ( ( byte ) ( quantizedPixelRow [ j ] & 1 ) << shift ) ) ;
555+ shift -- ;
556+ }
557+
558+ stream . WriteByte ( indices ) ;
559+ }
481560 }
482561}
0 commit comments