private static void ReverseChromaSubSampling(int width, int height, int horizontalSubSampling, int verticalSubSampling, ReadOnlySpan <byte> source, Span <byte> destination) { // If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively, // then the source data will be padded. width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); int blockWidth = width / horizontalSubSampling; int blockHeight = height / verticalSubSampling; int cbCrOffsetInBlock = horizontalSubSampling * verticalSubSampling; int blockByteCount = cbCrOffsetInBlock + 2; for (int blockRow = blockHeight - 1; blockRow >= 0; blockRow--) { for (int blockCol = blockWidth - 1; blockCol >= 0; blockCol--) { int blockOffset = (blockRow * blockWidth) + blockCol; ReadOnlySpan <byte> blockData = source.Slice(blockOffset * blockByteCount, blockByteCount); byte cr = blockData[cbCrOffsetInBlock + 1]; byte cb = blockData[cbCrOffsetInBlock]; for (int row = verticalSubSampling - 1; row >= 0; row--) { for (int col = horizontalSubSampling - 1; col >= 0; col--) { int offset = 3 * ((((blockRow * verticalSubSampling) + row) * width) + (blockCol * horizontalSubSampling) + col); destination[offset + 2] = cr; destination[offset + 1] = cb; destination[offset] = blockData[(row * horizontalSubSampling) + col]; } } } } }
/// <inheritdoc/> public override void Decode(IMemoryOwner <byte>[] data, Buffer2D <TPixel> pixels, int left, int top, int width, int height) { Span <byte> yData = data[0].GetSpan(); Span <byte> cbData = data[1].GetSpan(); Span <byte> crData = data[2].GetSpan(); if (this.ycbcrSubSampling != null && !(this.ycbcrSubSampling[0] == 1 && this.ycbcrSubSampling[1] == 1)) { ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], cbData, crData); } var color = default(TPixel); int offset = 0; int widthPadding = 0; if (this.ycbcrSubSampling != null) { // Round to the next integer multiple of horizontalSubSampling. widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); } for (int y = top; y < top + height; y++) { Span <TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); for (int x = 0; x < pixelRow.Length; x++) { Rgba32 rgba = this.converter.ConvertToRgba32(yData[offset], cbData[offset], crData[offset]); color.FromRgba32(rgba); pixelRow[x] = color; offset++; } offset += widthPadding; } }
private static void ReverseChromaSubSampling(int width, int height, int horizontalSubSampling, int verticalSubSampling, Span <byte> planarCb, Span <byte> planarCr) { // If width and height are not multiples of ChromaSubsampleHoriz and ChromaSubsampleVert respectively, // then the source data will be padded. width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); for (int row = height - 1; row >= 0; row--) { for (int col = width - 1; col >= 0; col--) { int offset = (row * width) + col; int subSampleOffset = (row / verticalSubSampling * (width / horizontalSubSampling)) + (col / horizontalSubSampling); planarCb[offset] = planarCb[subSampleOffset]; planarCr[offset] = planarCr[subSampleOffset]; } } }
/// <inheritdoc/> public override void Decode(ReadOnlySpan <byte> data, Buffer2D <TPixel> pixels, int left, int top, int width, int height) { ReadOnlySpan <byte> ycbcrData = data; if (this.ycbcrSubSampling != null && !(this.ycbcrSubSampling[0] == 1 && this.ycbcrSubSampling[1] == 1)) { // 4 extra rows and columns for possible padding. int paddedWidth = width + 4; int paddedHeight = height + 4; int requiredBytes = paddedWidth * paddedHeight * 3; using IMemoryOwner <byte> tmpBuffer = this.memoryAllocator.Allocate <byte>(requiredBytes); Span <byte> tmpBufferSpan = tmpBuffer.GetSpan(); ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], data, tmpBufferSpan); ycbcrData = tmpBufferSpan; } var color = default(TPixel); int offset = 0; int widthPadding = 0; if (this.ycbcrSubSampling != null) { // Round to the next integer multiple of horizontalSubSampling. widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); } for (int y = top; y < top + height; y++) { Span <TPixel> pixelRow = pixels.GetRowSpan(y).Slice(left, width); for (int x = 0; x < pixelRow.Length; x++) { Rgba32 rgba = this.converter.ConvertToRgba32(ycbcrData[offset], ycbcrData[offset + 1], ycbcrData[offset + 2]); color.FromRgba32(rgba); pixelRow[x] = color; offset += 3; } offset += widthPadding * 3; } }