예제 #1
0
        /// <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);
            }
        }
예제 #4
0
        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;
            }
        }
예제 #5
0
        /// <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);
 }
예제 #9
0
        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));
            }
        }
예제 #10
0
        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)..];
예제 #12
0
 public static void WriteBits(
     ImageBinWriter s, ref BitBuffer32 bitBuf, PointU16 point)
 {
     WriteBits(s, ref bitBuf, point.X, point.Y);
 }
예제 #13
0
        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]);
예제 #14
0
        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]);
        }