Exemple #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 <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);
            }
        }
        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);
 }
Exemple #5
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));
            }
        }
Exemple #6
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]);