/// <summary> /// A Webp lossless image can go through four different types of transformation before being entropy encoded. /// This will reverse the transformations, if any are present. /// </summary> /// <param name="decoder">The decoder holding the transformation infos.</param> /// <param name="pixelData">The pixel data to apply the transformation.</param> /// <param name="memoryAllocator">The memory allocator is needed to allocate memory during the predictor transform.</param> public static void ApplyInverseTransforms(Vp8LDecoder decoder, Span <uint> pixelData, MemoryAllocator memoryAllocator) { List <Vp8LTransform> transforms = decoder.Transforms; for (int i = transforms.Count - 1; i >= 0; i--) { Vp8LTransform transform = transforms[i]; Vp8LTransformType transformType = transform.TransformType; switch (transformType) { case Vp8LTransformType.PredictorTransform: using (IMemoryOwner <uint> output = memoryAllocator.Allocate <uint>(pixelData.Length, AllocationOptions.Clean)) { LosslessUtils.PredictorInverseTransform(transform, pixelData, output.GetSpan()); } break; case Vp8LTransformType.SubtractGreen: LosslessUtils.AddGreenToBlueAndRed(pixelData); break; case Vp8LTransformType.CrossColorTransform: LosslessUtils.ColorSpaceInverseTransform(transform, pixelData); break; case Vp8LTransformType.ColorIndexingTransform: LosslessUtils.ColorIndexInverseTransform(transform, pixelData); break; } } }
/// <summary> /// Reads the transformations, if any are present. /// </summary> /// <param name="xSize">The width of the image.</param> /// <param name="ySize">The height of the image.</param> /// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param> private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder) { var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2); var transform = new Vp8LTransform(transformType, xSize, ySize); // Each transform is allowed to be used only once. foreach (Vp8LTransform decoderTransform in decoder.Transforms) { if (decoderTransform.TransformType == transform.TransformType) { WebpThrowHelper.ThrowImageFormatException("Each transform can only be present once"); } } switch (transformType) { case Vp8LTransformType.SubtractGreen: // There is no data associated with this transform. break; case Vp8LTransformType.ColorIndexingTransform: // The transform data contains color table size and the entries in the color table. // 8 bit value for color table size. uint numColors = this.bitReader.ReadValue(8) + 1; int bits = numColors > 16 ? 0 : numColors > 4 ? 1 : numColors > 2 ? 2 : 3; transform.Bits = bits; using (IMemoryOwner <uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false)) { int finalNumColors = 1 << (8 >> transform.Bits); IMemoryOwner <uint> newColorMap = this.memoryAllocator.Allocate <uint>(finalNumColors, AllocationOptions.Clean); LosslessUtils.ExpandColorMap((int)numColors, colorMap.GetSpan(), newColorMap.GetSpan()); transform.Data = newColorMap; } break; case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.CrossColorTransform: { // The first 3 bits of prediction data define the block width and height in number of bits. transform.Bits = (int)this.bitReader.ReadValue(3) + 2; int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits); int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits); IMemoryOwner <uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false); transform.Data = transformData; break; } } decoder.Transforms.Add(transform); }