/// <summary> /// Convert raw spectral DCT data to color data and copy it to the color buffer <see cref="ColorBuffer"/>. /// </summary> public void CopyBlocksToColorBuffer(int spectralStep) { Buffer2D <Block8x8> spectralBuffer = this.component.SpectralBlocks; float maximumValue = this.frame.MaxColorChannelValue; int destAreaStride = this.ColorBuffer.Width; int yBlockStart = spectralStep * this.blockRowsPerStep; Size subSamplingDivisors = this.component.SubSamplingDivisors; Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.component.QuantizationTableIndex]; Block8x8F workspaceBlock = default; for (int y = 0; y < this.blockRowsPerStep; y++) { int yBuffer = y * this.blockAreaSize.Height; Span <float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); Span <Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) { // Integer to float workspaceBlock.LoadFrom(ref blockRow[xBlock]); // Dequantize workspaceBlock.MultiplyInPlace(ref dequantTable); // Convert from spectral to color FastFloatingPointDCT.TransformIDCT(ref workspaceBlock); // To conform better to libjpeg we actually NEED TO loose precision here. // This is because they store blocks as Int16 between all the operations. // To be "more accurate", we need to emulate this by rounding! workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue); // Write to color buffer acording to sampling factors int xColorBufferStart = xBlock * this.blockAreaSize.Width; workspaceBlock.ScaledCopyTo( ref colorBufferRow[xColorBufferStart], destAreaStride, subSamplingDivisors.Width, subSamplingDivisors.Height); } } }