Пример #1
0
        private static byte[] Parse(int scanlineLength, int scanlineCount, byte[] pureData, int bytesPerPixel, PngFileHeader header, ColorReader reader, int bytesPerPixelOutput)
        {
            var width  = (int)header.Size.X;
            var height = (int)header.Size.Y;
            var pixels = new byte[width * height * bytesPerPixelOutput];
            int length = scanlineLength - 1;
            var data   = new Span <byte>(pureData);

            // Analyze if the scanlines can be read in parallel.
            byte filterMode = data[0];

            for (var i = 0; i < scanlineCount; i++)
            {
                byte f = data[scanlineLength * i];
                if (f == filterMode)
                {
                    continue;
                }
                filterMode = byte.MaxValue;
                break;
            }

            // Multiple filters or a dependency filter are in affect.
            if (filterMode == byte.MaxValue || filterMode != 0 && filterMode != 1)
            {
                if (scanlineCount >= 1500)
                {
                    Engine.Log.Trace("Loaded a big PNG with scanlines which require filtering. If you re-export it without filters, it will load faster.", MessageSource.ImagePng);
                }

                PerfProfiler.ProfilerEventStart("PNG Parse Sequential", "Loading");
                var readOffset       = 0;
                var previousScanline = Span <byte> .Empty;
                for (var i = 0; i < scanlineCount; i++)
                {
                    // Early out for invalid data.
                    if (data.Length - readOffset < scanlineLength)
                    {
                        break;
                    }

                    Span <byte> scanline = data.Slice(readOffset + 1, length);
                    int         filter   = data[readOffset];
                    ApplyFilter(scanline, previousScanline, filter, bytesPerPixel);

                    reader(width, ConvertBitArray(scanline, header), pixels, i);
                    previousScanline = scanline;
                    readOffset      += scanlineLength;
                }

                PerfProfiler.ProfilerEventEnd("PNG Parse Sequential", "Loading");
                return(pixels);
            }

            // Single line filter
            if (filterMode == 1)
            {
                PerfProfiler.ProfilerEventStart("PNG Parse Threaded", "Loading");
                ParallelWork.FastLoops(scanlineCount, (start, end) =>
                {
                    int readOffset = start * scanlineLength;
                    for (int i = start; i < end; i++)
                    {
                        // Early out for invalid data.
                        if (pureData.Length - readOffset < scanlineLength)
                        {
                            break;
                        }
                        Span <byte> scanline = new Span <byte>(pureData).Slice(readOffset + 1, length);
                        for (int j = bytesPerPixel; j < scanline.Length; j++)
                        {
                            scanline[j] = (byte)(scanline[j] + scanline[j - bytesPerPixel]);
                        }

                        reader(width, ConvertBitArray(scanline, header), pixels, i);
                        readOffset += scanlineLength;
                    }
                }).Wait();
                PerfProfiler.ProfilerEventEnd("PNG Parse Threaded", "Loading");
            }

            // No filter!
            // ReSharper disable once InvertIf
            if (filterMode == 0)
            {
                PerfProfiler.ProfilerEventStart("PNG Parse Threaded", "Loading");
                ParallelWork.FastLoops(scanlineCount, (start, end) =>
                {
                    int readOffset = start * scanlineLength;
                    for (int i = start; i < end; i++)
                    {
                        // Early out for invalid data.
                        if (pureData.Length - readOffset < scanlineLength)
                        {
                            break;
                        }
                        Span <byte> row = ConvertBitArray(new Span <byte>(pureData).Slice(readOffset + 1, length), header);
                        reader(width, row, pixels, i);
                        readOffset += scanlineLength;
                    }
                }).Wait();
                PerfProfiler.ProfilerEventEnd("PNG Parse Threaded", "Loading");
            }

            return(pixels);
        }
Пример #2
0
        private static void Parse(byte[] data, PngFileHeader fileHeader, int bytesPerPixel, IColorReader reader, byte[] pixels)
        {
            // Find the scan line length.
            int scanlineLength = GetScanlineLength(fileHeader.Width, fileHeader) + 1;
            int scanLineCount  = data.Length / scanlineLength;

            // Run through all scanlines.
            var cannotParallel = new List <int>();

            ParallelWork.FastLoops(scanLineCount, (start, end) =>
            {
                int readOffset = start * scanlineLength;
                for (int i = start; i < end; i++)
                {
                    // Early out for invalid data.
                    if (data.Length - readOffset < scanlineLength)
                    {
                        break;
                    }

                    // Get the current scanline.
                    var rowData = new Span <byte>(data, readOffset + 1, scanlineLength - 1);
                    int filter  = data[readOffset];
                    readOffset += scanlineLength;

                    // Check if it has a filter.
                    // PNG filters require the previous row.
                    // We can't do those in parallel.
                    if (filter != 0)
                    {
                        lock (cannotParallel)
                        {
                            cannotParallel.Add(i);
                        }

                        continue;
                    }

                    reader.ReadScanline(rowData, pixels, fileHeader, i);
                }
            }).Wait();

            if (cannotParallel.Count == 0)
            {
                return;
            }

            PerfProfiler.ProfilerEventStart("PNG Parse Sequential", "Loading");

            // Run scanlines which couldn't be parallel processed.
            if (scanLineCount >= 2000)
            {
                Engine.Log.Trace("Loaded a big PNG with scanlines which require filtering. If you re-export it without that, it will load faster.", MessageSource.ImagePng);
            }
            cannotParallel.Sort();
            for (var i = 0; i < cannotParallel.Count; i++)
            {
                int idx = cannotParallel[i];

                int         rowStart    = idx * scanlineLength;
                Span <byte> prevRowData = idx == 0 ? null : new Span <byte>(data, (idx - 1) * scanlineLength + 1, scanlineLength - 1);
                var         rowData     = new Span <byte>(data, rowStart + 1, scanlineLength - 1);

                // Apply filter to the whole row.
                int filter = data[rowStart];
                for (var column = 0; column < rowData.Length; column++)
                {
                    rowData[column] = ApplyFilter(rowData, prevRowData, filter, column, bytesPerPixel);
                }

                reader.ReadScanline(rowData, pixels, fileHeader, idx);
            }

            PerfProfiler.ProfilerEventEnd("PNG Parse Sequential", "Loading");
        }
Пример #3
0
        public static Glyph[] ParseGlyf(ByteReader reader, int[] locaOffsets)
        {
            var glyphs = new Glyph[locaOffsets.Length - 1];

            // Go through all glyphs and resolve them, leaving composite glyphs for later.
            var compositeGlyphParse = new List <CompositeGlyphRequest>();

            ParallelWork.FastLoops(glyphs.Length, (start, end) =>
            {
                for (int i = start; i < end; i++)
                {
                    var current = new Glyph();
                    glyphs[i]   = current;

                    if (i > locaOffsets.Length)
                    {
                        continue;
                    }
                    int glyphOffset = locaOffsets[i];
                    int nextOffset  = locaOffsets[i + 1];

                    // No data for glyph.
                    if (glyphOffset == nextOffset || glyphOffset >= reader.Data.Length)
                    {
                        current.Vertices = new GlyphVertex[0];
                        continue;
                    }

                    ByteReader glyphData = reader.Branch(glyphOffset, true);
                    int numberOfContours = glyphData.ReadShortBE();
                    current.XMin         = glyphData.ReadShortBE();
                    current.YMin         = glyphData.ReadShortBE();
                    current.XMax         = glyphData.ReadShortBE();
                    current.YMax         = glyphData.ReadShortBE();
                    // Non-composite
                    if (numberOfContours > 0)
                    {
                        ResolveTtfGlyph(numberOfContours, glyphData, current);
                    }
                    // Composite
                    else if (numberOfContours == -1)
                    {
                        lock (compositeGlyphParse)
                        {
                            compositeGlyphParse.Add(new CompositeGlyphRequest(glyphData, current));
                        }
                    }

                    // 0 is an invalid value.
                }
            }).Wait();

            // ReSharper disable once ImplicitlyCapturedClosure
            ParallelWork.FastLoops(compositeGlyphParse.Count, (start, end) =>
            {
                for (int i = start; i < end; i++)
                {
                    CompositeGlyphRequest request = compositeGlyphParse[i];
                    ResolveCompositeTtfGlyph(request.Reader, request.Glyph, glyphs);
                }
            });

            Debug.Assert(glyphs.All(x => x != null));

            return(glyphs);
        }