|
| 1 | +// Copyright (c) Six Labors. |
| 2 | +// Licensed under the Apache License, Version 2.0. |
| 3 | + |
| 4 | +using System; |
| 5 | +using System.Buffers; |
| 6 | +using SixLabors.ImageSharp.IO; |
| 7 | +using SixLabors.ImageSharp.Memory; |
| 8 | +using SixLabors.ImageSharp.PixelFormats; |
| 9 | + |
| 10 | +namespace SixLabors.ImageSharp.Formats.Pbm |
| 11 | +{ |
| 12 | + /// <summary> |
| 13 | + /// Pixel decoding methods for the PBM binary encoding. |
| 14 | + /// </summary> |
| 15 | + internal class BinaryDecoder |
| 16 | + { |
| 17 | + private static L8 white = new(255); |
| 18 | + private static L8 black = new(0); |
| 19 | + |
| 20 | + /// <summary> |
| 21 | + /// Decode the specified pixels. |
| 22 | + /// </summary> |
| 23 | + /// <typeparam name="TPixel">The type of pixel to encode to.</typeparam> |
| 24 | + /// <param name="configuration">The configuration.</param> |
| 25 | + /// <param name="pixels">The pixel array to encode into.</param> |
| 26 | + /// <param name="stream">The stream to read the data from.</param> |
| 27 | + /// <param name="colorType">The ColorType to decode.</param> |
| 28 | + /// <param name="componentType">Data type of the pixles components.</param> |
| 29 | + /// <exception cref="InvalidImageContentException"> |
| 30 | + /// Thrown if an invalid combination of setting is requested. |
| 31 | + /// </exception> |
| 32 | + public static void Process<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream, PbmColorType colorType, PbmComponentType componentType) |
| 33 | + where TPixel : unmanaged, IPixel<TPixel> |
| 34 | + { |
| 35 | + if (colorType == PbmColorType.Grayscale) |
| 36 | + { |
| 37 | + if (componentType == PbmComponentType.Byte) |
| 38 | + { |
| 39 | + ProcessGrayscale(configuration, pixels, stream); |
| 40 | + } |
| 41 | + else |
| 42 | + { |
| 43 | + ProcessWideGrayscale(configuration, pixels, stream); |
| 44 | + } |
| 45 | + } |
| 46 | + else if (colorType == PbmColorType.Rgb) |
| 47 | + { |
| 48 | + if (componentType == PbmComponentType.Byte) |
| 49 | + { |
| 50 | + ProcessRgb(configuration, pixels, stream); |
| 51 | + } |
| 52 | + else |
| 53 | + { |
| 54 | + ProcessWideRgb(configuration, pixels, stream); |
| 55 | + } |
| 56 | + } |
| 57 | + else |
| 58 | + { |
| 59 | + ProcessBlackAndWhite(configuration, pixels, stream); |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + private static void ProcessGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
| 64 | + where TPixel : unmanaged, IPixel<TPixel> |
| 65 | + { |
| 66 | + const int bytesPerPixel = 1; |
| 67 | + int width = pixels.Width; |
| 68 | + int height = pixels.Height; |
| 69 | + MemoryAllocator allocator = configuration.MemoryAllocator; |
| 70 | + using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel); |
| 71 | + Span<byte> rowSpan = row.GetSpan(); |
| 72 | + |
| 73 | + for (int y = 0; y < height; y++) |
| 74 | + { |
| 75 | + stream.Read(rowSpan); |
| 76 | + Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
| 77 | + PixelOperations<TPixel>.Instance.FromL8Bytes( |
| 78 | + configuration, |
| 79 | + rowSpan, |
| 80 | + pixelSpan, |
| 81 | + width); |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + private static void ProcessWideGrayscale<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
| 86 | + where TPixel : unmanaged, IPixel<TPixel> |
| 87 | + { |
| 88 | + const int bytesPerPixel = 2; |
| 89 | + int width = pixels.Width; |
| 90 | + int height = pixels.Height; |
| 91 | + MemoryAllocator allocator = configuration.MemoryAllocator; |
| 92 | + using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel); |
| 93 | + Span<byte> rowSpan = row.GetSpan(); |
| 94 | + |
| 95 | + for (int y = 0; y < height; y++) |
| 96 | + { |
| 97 | + stream.Read(rowSpan); |
| 98 | + Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
| 99 | + PixelOperations<TPixel>.Instance.FromL16Bytes( |
| 100 | + configuration, |
| 101 | + rowSpan, |
| 102 | + pixelSpan, |
| 103 | + width); |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + private static void ProcessRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
| 108 | + where TPixel : unmanaged, IPixel<TPixel> |
| 109 | + { |
| 110 | + const int bytesPerPixel = 3; |
| 111 | + int width = pixels.Width; |
| 112 | + int height = pixels.Height; |
| 113 | + MemoryAllocator allocator = configuration.MemoryAllocator; |
| 114 | + using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel); |
| 115 | + Span<byte> rowSpan = row.GetSpan(); |
| 116 | + |
| 117 | + for (int y = 0; y < height; y++) |
| 118 | + { |
| 119 | + stream.Read(rowSpan); |
| 120 | + Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
| 121 | + PixelOperations<TPixel>.Instance.FromRgb24Bytes( |
| 122 | + configuration, |
| 123 | + rowSpan, |
| 124 | + pixelSpan, |
| 125 | + width); |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + private static void ProcessWideRgb<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
| 130 | + where TPixel : unmanaged, IPixel<TPixel> |
| 131 | + { |
| 132 | + const int bytesPerPixel = 6; |
| 133 | + int width = pixels.Width; |
| 134 | + int height = pixels.Height; |
| 135 | + MemoryAllocator allocator = configuration.MemoryAllocator; |
| 136 | + using IMemoryOwner<byte> row = allocator.Allocate<byte>(width * bytesPerPixel); |
| 137 | + Span<byte> rowSpan = row.GetSpan(); |
| 138 | + |
| 139 | + for (int y = 0; y < height; y++) |
| 140 | + { |
| 141 | + stream.Read(rowSpan); |
| 142 | + Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
| 143 | + PixelOperations<TPixel>.Instance.FromRgb48Bytes( |
| 144 | + configuration, |
| 145 | + rowSpan, |
| 146 | + pixelSpan, |
| 147 | + width); |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + private static void ProcessBlackAndWhite<TPixel>(Configuration configuration, Buffer2D<TPixel> pixels, BufferedReadStream stream) |
| 152 | + where TPixel : unmanaged, IPixel<TPixel> |
| 153 | + { |
| 154 | + int width = pixels.Width; |
| 155 | + int height = pixels.Height; |
| 156 | + int startBit = 0; |
| 157 | + MemoryAllocator allocator = configuration.MemoryAllocator; |
| 158 | + using IMemoryOwner<L8> row = allocator.Allocate<L8>(width); |
| 159 | + Span<L8> rowSpan = row.GetSpan(); |
| 160 | + |
| 161 | + for (int y = 0; y < height; y++) |
| 162 | + { |
| 163 | + for (int x = 0; x < width;) |
| 164 | + { |
| 165 | + int raw = stream.ReadByte(); |
| 166 | + int bit = startBit; |
| 167 | + startBit = 0; |
| 168 | + for (; bit < 8; bit++) |
| 169 | + { |
| 170 | + bool bitValue = (raw & (0x80 >> bit)) != 0; |
| 171 | + rowSpan[x] = bitValue ? black : white; |
| 172 | + x++; |
| 173 | + if (x == width) |
| 174 | + { |
| 175 | + startBit = (bit + 1) & 7; // Round off to below 8. |
| 176 | + if (startBit != 0) |
| 177 | + { |
| 178 | + stream.Seek(-1, System.IO.SeekOrigin.Current); |
| 179 | + } |
| 180 | + |
| 181 | + break; |
| 182 | + } |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); |
| 187 | + PixelOperations<TPixel>.Instance.FromL8( |
| 188 | + configuration, |
| 189 | + rowSpan, |
| 190 | + pixelSpan); |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | +} |
0 commit comments