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); }
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"); }
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); }