/// <summary> /// Writes integers in different sizes based on /// digit characters in the <paramref name="format"/> string. /// <para> /// Zero skips a value, while other characters are ignored. /// Maximum size of one integer is 8 bytes. /// </para> /// </summary> public static void WriteFormat( this ImageBinWriter s, ReadOnlySpan <char> format, ReadOnlySpan <long> values) { if (s == null) { throw new ArgumentNullException(nameof(s)); } Span <byte> buffer = stackalloc byte[sizeof(long)]; int valueIndex = 0; for (int i = 0; i < format.Length; i++) { int digit = CharUnicodeInfo.GetDecimalDigitValue(format[i]); if (digit == -1 || digit > buffer.Length) { continue; } if (digit > 0) { long x = values[valueIndex]; for (int j = 0; j < digit; j++) { int shift = 8 * j; buffer[j] = (byte)((x >> shift) & 0xff); } s.Write(buffer.Slice(0, digit)); } valueIndex++; } }
public static void Write <TImage>(ImageBinWriter state, TImage image) where TImage : IPixelRowProvider { if (state == null) { throw new ArgumentNullException(nameof(state)); } if (image == null) { throw new ArgumentNullException(nameof(image)); } throw new NotImplementedException(); }
public static void Write <TPixelRowProvider>(ImageBinWriter state, TPixelRowProvider image) where TPixelRowProvider : IPixelRowProvider { if (state == null) { throw new ArgumentNullException(nameof(state)); } image.ThrowIfCancelled(); int width = image.Width; int height = image.Height; if (height <= 0 || width <= 0) { throw new ArgumentException("Invalid image dimensions.", nameof(state)); } CultureInfo cult = CultureInfo.InvariantCulture; byte[] Head1 = Encoding.UTF8.GetBytes(string.Format(cult, "EXPOSURE=1.0\n\n-Y {0} +X {1}\n", height.ToString(cult), width.ToString(cult))); state.Write(Head0.Span); state.Write(Head1); // TODO: pool buffers byte[]? scratch = null; if (width >= 8 && width < 32768) { scratch = new byte[width * 4]; } float[] rowBuffer = new float[width]; for (int row = 0; row < height; row++) { image.ThrowIfCancelled(); image.GetFloatRow(row, rowBuffer); WriteHdrScanline(state, image, rowBuffer, scratch); } }
public static void WriteBits( ImageBinWriter s, ref BitBuffer32 bitBuf, ushort bs0, ushort bs1) { Debug.Assert(s != null); bitBuf.Count += bs1; bitBuf.Value |= bs0 << (24 - bitBuf.Count); while (bitBuf.Count >= 8) { byte c = (byte)((bitBuf.Value >> 16) & 0xff); s.WriteByte(c); if (c == 0xff) { s.WriteByte(0); } bitBuf.Value <<= 8; bitBuf.Count -= 8; } }
/// <summary> /// Used for writing raw data with headers. /// </summary> public static void OutFile <TImage>( this ImageBinWriter state, TImage image, bool flipRgb, int verticalDirection, bool expandMono, int alphaDirection, int scanlinePad, ReadOnlySpan <char> format, ReadOnlySpan <long> values) where TImage : IPixelRowProvider { if (state == null) { throw new ArgumentNullException(nameof(state)); } if (image == null) { throw new ArgumentNullException(nameof(image)); } if (image.Width <= 0 || image.Height <= 0) { throw new ArgumentException("Invalid image dimensions.", nameof(state)); } WriteFormat(state, format, values); WritePixels(state, image, flipRgb, verticalDirection, alphaDirection, scanlinePad, expandMono); }
private static void WriteHdrScanline <TPixelRowProvider>( ImageBinWriter state, TPixelRowProvider image, float[] data, byte[]?buffer) where TPixelRowProvider : IPixelRowProvider { int width = image.Width; int n = image.Components; Span <byte> scanlineHeader = stackalloc byte[4] { 2, 2, (byte)((width & 0xff00) >> 8), (byte)(width & 0x00ff), }; Span <byte> rgbe = stackalloc byte[4]; Span <float> linear = stackalloc float[3]; bool hasColor = n == 4 || n == 3; const int ofsR = 0; int ofsG = hasColor ? 1 : 0; int ofsB = hasColor ? 2 : 0; if (width >= 8 && width < 32768) { Debug.Assert(buffer != null); Span <float> src = data.AsSpan(0, width * n); for (int x = 0; x < src.Length; x += n) { linear[2] = src[x + ofsB]; linear[1] = src[x + ofsG]; linear[0] = src[x + ofsR]; LinearToRgbe(linear, rgbe); buffer[x + width * 3] = rgbe[3]; buffer[x + width * 2] = rgbe[2]; buffer[x + width * 1] = rgbe[1]; buffer[x + width * 0] = rgbe[0]; } state.Write(scanlineHeader); for (int c = 0; c < 4; c++) { int o = width * c; int x = 0; while (x < width) { int r = x; while ((r + 2) < width) { if (buffer[o + r] == buffer[o + r + 2] && buffer[o + r] == buffer[o + r + 1]) { break; } r++; } if (r + 2 >= width) { r = width; } while (x < r) { int len = r - x; if (len > 128) { len = 128; } WriteDumpData(state, buffer.AsSpan(o + x, len)); x += len; } if (r + 2 < width) { while ((r < width) && (buffer[o + r] == buffer[o + x])) { r++; } while (x < r) { int len = r - x; if (len > 127) { len = 127; } WriteRunData(state, len, buffer[o + x]); x += len; } } } } } else { Span <float> src = data.AsSpan(0, width * n); for (int x = 0; x < src.Length; x += n) { linear[2] = src[x + ofsB]; linear[1] = src[x + ofsG]; linear[0] = src[x + ofsR]; LinearToRgbe(linear, rgbe); state.Write(rgbe); } } }
private static void WriteDumpData(ImageBinWriter s, ReadOnlySpan <byte> data) { s.WriteByte((byte)((data.Length) & 0xff)); // lengthbyte s.Write(data); }
private static void WriteRunData(ImageBinWriter s, int length, byte databyte) { s.WriteByte((byte)((length + 128) & 0xff)); // lengthbyte s.WriteByte(databyte); }
public static void WritePixels <TImage>( this ImageBinWriter state, TImage image, bool flipRgb, int verticalDirection, int alphaDirection, int scanlinePad, bool expandMono) where TImage : IPixelRowProvider { if (state == null) { throw new ArgumentNullException(nameof(state)); } if (image == null) { throw new ArgumentNullException(nameof(image)); } if (scanlinePad < 0 || scanlinePad > 4) { throw new ArgumentOutOfRangeException(nameof(scanlinePad)); } if (image.Width <= 0 || image.Height <= 0) { return; } int row; int rowEnd; if (verticalDirection < 0) { rowEnd = -1; row = image.Height - 1; } else { rowEnd = image.Height; row = 0; } int width = image.Width; int comp = image.Components; int stride = width * comp; int scanlineMax = stride + scanlinePad; Span <byte> scanline = scanlineMax <= 4096 ? stackalloc byte[scanlineMax] : new byte[scanlineMax]; for (; row != rowEnd; row += verticalDirection) { image.GetByteRow(row, scanline); int offset = 0; for (int x = 0; x < width; x++) { offset += WritePixel( flipRgb, alphaDirection, expandMono, scanline.Slice(x * comp, comp), scanline.Slice(offset, comp)); } if (offset != stride) { Span <byte> padSlice = scanline.Slice(offset, scanlinePad); padSlice.Clear(); // clear possible garbage from last row offset += scanlinePad; } state.Write(scanline.Slice(0, offset)); } }
public static void Write <TImage>(ImageBinWriter state, TImage image) where TImage : IPixelRowProvider { // we only support RGB and RGBA, no palette indexing // TODO: support for palette indexing if (state == null) { throw new ArgumentNullException(nameof(state)); } if (image == null) { throw new ArgumentNullException(nameof(image)); } image.ThrowIfCancelled(); int bytesPerPixel = image.Components == 4 ? 4 : 3; int bitDepth = bytesPerPixel * 8; int pad = (-image.Width * bytesPerPixel) & bytesPerPixel; int compressionHeaders = image.Components == 4 ? 17 : 0; int extraPad = compressionHeaders * sizeof(int); // extra bytes for compression headers int dataSize = (image.Width * bytesPerPixel + pad) * image.Height; int dibHeaderLen = 40 + extraPad; int fileSize = 14 + dibHeaderLen + dataSize; int dataOffset = 14 + dibHeaderLen; int compression = image.Components == 4 ? 3 : 0; // 3 == bitfields | 0 == no compression Span <long> header = stackalloc long[] { // BMP header: 'B', 'M', fileSize, 0, 0, dataOffset, // DIB header: dibHeaderLen, image.Width, image.Height, 1, bitDepth, compression, dataSize, 0, 0, 0, 0, // Data needed for 32bit bitmaps: 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000, // RGBA masks 0x206E6957, // little-endian (value equal to string "Win ") 0, 0, 0, 0, 0, 0, 0, 0, 0, // colorspace endpoints (unused) 0, 0, 0 // RGB gamma (unused) }; int alphaDir = image.Components == 4 ? 1 : 0; string format = "11 4 22 44 44 22 444444 " + // base header "4444 4 444444444 444"; // compression header int headerCount = 17 + compressionHeaders; ImageWriteHelpers.OutFile( state, image, true, -1, true, alphaDir, pad, format, header.Slice(0, headerCount)); } }
public static void Write <TImage>(ImageBinWriter state, TImage image, bool useRLE) where TImage : IPixelRowProvider { if (state == null) { throw new ArgumentNullException(nameof(state)); } if (image == null) { throw new ArgumentNullException(nameof(image)); } int width = image.Width; int height = image.Height; int comp = image.Components; if ((height < 0) || (width < 0)) { throw new ArgumentException("Invalid image dimensions.", nameof(state)); } image.ThrowIfCancelled(); int hasAlpha = (comp == 2 || comp == 4) ? 1 : 0; int colorbytes = hasAlpha != 0 ? comp - 1 : comp; int format = colorbytes < 2 ? 3 : 2; if (!useRLE) { Span <long> headerValues = stackalloc long[] { 0, 0, format, 0, 0, 0, 0, 0, width, height, (colorbytes + hasAlpha) * 8, hasAlpha * 8 }; ImageWriteHelpers.OutFile( state, image, true, -1, false, hasAlpha, 0, "111 221 2222 11", headerValues); } else { Span <long> headerValues = stackalloc long[] { 0, 0, format + 8, 0, 0, 0, 0, 0, width, height, (colorbytes + hasAlpha) * 8, hasAlpha * 8 }; ImageWriteHelpers.WriteFormat(state, "111 221 2222 11", headerValues); Span <byte> rowScratch = new byte[width * comp].AsSpan(); Span <byte> outBuffer = stackalloc byte[4]; for (int y = height; y-- > 0;) { image.GetByteRow(y, rowScratch); int len; for (int x = 0; x < width; x += len) { Span <byte> begin = rowScratch[(x * comp)..];
public static void WriteBits( ImageBinWriter s, ref BitBuffer32 bitBuf, PointU16 point) { WriteBits(s, ref bitBuf, point.X, point.Y); }
public static void WriteCore <TImage>( ImageBinWriter state, TImage image, bool useFloatPixels, int quality, bool allowSubsample, bool forceSubsample) where TImage : IPixelRowProvider { if (state == null) { throw new ArgumentNullException(nameof(state)); } if (image == null) { throw new ArgumentNullException(nameof(image)); } int width = image.Width; int height = image.Height; int comp = image.Components; if (width == 0 || (height == 0)) { throw new ArgumentException("Invalid dimensions.", nameof(image)); } if ((comp < 1) || (comp > 4)) { throw new ArgumentException("Invalid component count.", nameof(image)); } image.ThrowIfCancelled(); Span <float> fdtbl_Y = stackalloc float[64]; Span <float> fdtbl_UV = stackalloc float[64]; Span <byte> YTable = stackalloc byte[64]; Span <byte> UVTable = stackalloc byte[64]; quality = quality != 0 ? quality : 90; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; bool subsample = forceSubsample || (allowSubsample && quality <= 90); ReadOnlySpan <byte> zigZag = ZigZag; for (int i = 0; i < 64; i++) { int yti = (YQT[i] * quality + 50) / 100; YTable[zigZag[i]] = (byte)(yti < 1 ? 1 : yti > 255 ? 255 : yti); int uvti = (UVQT[i] * quality + 50) / 100; UVTable[zigZag[i]] = (byte)(uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); } for (int row = 0, k = 0; row < 8; row++) { for (int col = 0; col < 8; col++, k++) { fdtbl_Y[k] = 1 / (YTable[zigZag[k]] * aasf[row] * aasf[col]); fdtbl_UV[k] = 1 / (UVTable[zigZag[k]] * aasf[row] * aasf[col]); } } { Span <byte> head1 = stackalloc byte[24] { 0xFF, 0xC0, 0, 0x11, 8, (byte)(height >> 8), (byte)((height) & 0xff), (byte)(width >> 8), (byte)((width) & 0xff), 3, 1, (byte)(subsample ? 0x22 : 0x11), 0, 2, 0x11, 1, 3, 0x11, 1, 0xFF, 0xC4, 0x01, 0xA2, 0 }; state.Write(Head0); state.Write(YTable); state.WriteByte(1); state.Write(UVTable); state.Write(head1); state.Write(DefaultDcLuminanceNrcodes[1..DefaultDcChrominanceNrcodes.Length]);
public static int ProcessDU( ImageBinWriter s, ref BitBuffer32 bitBuf, Span <float> CDU, int duStride, Span <float> fdtbl, int DC, PointU16[] HTDC, PointU16[] HTAC) { Debug.Assert(HTDC != null); Debug.Assert(HTAC != null); Span <int> DU = stackalloc int[64]; CalculateCDU(CDU, duStride, fdtbl, DU); int diff = DU[0] - DC; if (diff == 0) { WriteBits(s, ref bitBuf, HTDC[0]); } else { BitBuffer16 bitBuf16 = CalcBitBuffer16(diff); WriteBits(s, ref bitBuf, HTDC[bitBuf16.Count]); WriteBits(s, ref bitBuf, bitBuf16.Value, bitBuf16.Count); } int end0pos = 63; for (; (end0pos > 0) && (DU[end0pos] == 0); end0pos--) { } if (end0pos == 0) { WriteBits(s, ref bitBuf, HTAC[0x00]); return(DU[0]); } for (int i = 1; i <= end0pos; i++) { int startpos = i; for (; (DU[i] == 0) && (i <= end0pos); i++) { } int nrzeroes = i - startpos; if (nrzeroes >= 16) { int lng = nrzeroes >> 4; for (int nrmarker = 1; nrmarker <= lng; nrmarker++) { WriteBits(s, ref bitBuf, HTAC[0xF0]); } nrzeroes &= 15; } BitBuffer16 bitBuf16 = CalcBitBuffer16(DU[i]); WriteBits(s, ref bitBuf, HTAC[(nrzeroes << 4) + bitBuf16.Count]); WriteBits(s, ref bitBuf, bitBuf16.Value, bitBuf16.Count); } if (end0pos != 63) { WriteBits(s, ref bitBuf, HTAC[0x00]); } return(DU[0]); }