/// <summary> /// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor.</param> /// <param name="x">The x-position within the image.</param> /// <param name="y">The y-position within the image.</param> /// <param name="yBlock">The luminance block.</param> /// <param name="cbBlock">The red chroma block.</param> /// <param name="crBlock">The blue chroma block.</param> /// <param name="rgbBytes">Temporal <see cref="PixelArea{TColor}"/> provided by the caller</param> private static void ToYCbCr <TColor>( PixelAccessor <TColor> pixels, int x, int y, Block8x8F *yBlock, Block8x8F *cbBlock, Block8x8F *crBlock, PixelArea <TColor> rgbBytes) where TColor : struct, IPackedPixel, IEquatable <TColor> { float *yBlockRaw = (float *)yBlock; float *cbBlockRaw = (float *)cbBlock; float *crBlockRaw = (float *)crBlock; rgbBytes.Reset(); pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); byte *data = (byte *)rgbBytes.DataPointer; for (int j = 0; j < 8; j++) { int j8 = j * 8; for (int i = 0; i < 8; i++) { // Convert returned bytes into the YCbCr color space. Assume RGBA int r = data[0]; int g = data[1]; int b = data[2]; // Speed up the algorithm by removing floating point calculation // Scale by 65536, add .5F and truncate value. We use bit shifting to divide the result int y0 = 19595 * r; // (0.299F * 65536) + .5F int y1 = 38470 * g; // (0.587F * 65536) + .5F int y2 = 7471 * b; // (0.114F * 65536) + .5F int cb0 = -11057 * r; // (-0.168736F * 65536) + .5F int cb1 = 21710 * g; // (0.331264F * 65536) + .5F int cb2 = 32768 * b; // (0.5F * 65536) + .5F int cr0 = 32768 * r; // (0.5F * 65536) + .5F int cr1 = 27439 * g; // (0.418688F * 65536) + .5F int cr2 = 5329 * b; // (0.081312F * 65536) + .5F float yy = (y0 + y1 + y2) >> 16; float cb = 128 + ((cb0 - cb1 + cb2) >> 16); float cr = 128 + ((cr0 - cr1 - cr2) >> 16); int index = j8 + i; yBlockRaw[index] = yy; cbBlockRaw[index] = cb; crBlockRaw[index] = cr; data += 3; } } }
/// <summary> /// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor.</param> /// <param name="x">The x-position within the image.</param> /// <param name="y">The y-position within the image.</param> /// <param name="yBlock">The luminance block.</param> /// <param name="cbBlock">The red chroma block.</param> /// <param name="crBlock">The blue chroma block.</param> /// <param name="rgbBytes">Temporal <see cref="PixelArea{TColor}"/> provided by the caller</param> private static void ToYCbCr <TColor>( PixelAccessor <TColor> pixels, int x, int y, Block8x8F *yBlock, Block8x8F *cbBlock, Block8x8F *crBlock, PixelArea <TColor> rgbBytes) where TColor : struct, IPixel <TColor> { float *yBlockRaw = (float *)yBlock; float *cbBlockRaw = (float *)cbBlock; float *crBlockRaw = (float *)crBlock; rgbBytes.Reset(); pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); ref byte data0 = ref rgbBytes.Bytes[0];
/// <summary> /// Reads the frames colors, mapping indices to colors. /// </summary> /// <param name="indices">The indexed pixels.</param> /// <param name="colorTable">The color table containing the available colors.</param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; ImageFrame <TColor, TPacked> previousFrame = null; ImageFrame <TColor, TPacked> currentFrame = null; ImageBase <TColor, TPacked> image; if (this.nextFrame == null) { image = this.decodedImage; image.Quality = colorTable.Length / 3; // This initializes the image to become fully transparent because the alpha channel is zero. image.InitPixels(imageWidth, imageHeight); } else { if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { previousFrame = this.nextFrame; } currentFrame = this.nextFrame.Clone(); image = currentFrame; this.RestoreToBackground(image); this.decodedImage.Frames.Add(currentFrame); } if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) { image.FrameDelay = this.graphicsControlExtension.DelayTime; } int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line using (PixelAccessor <TColor, TPacked> pixelAccessor = image.Lock()) { using (PixelArea <TColor, TPacked> pixelRow = new PixelArea <TColor, TPacked>(imageWidth, ComponentOrder.XYZW)) { for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { // Check if this image is interlaced. int writeY; // the target y offset to write to if (descriptor.InterlaceFlag) { // If so then we read lines at predetermined offsets. // When an entire image height worth of offset lines has been read we consider this a pass. // With each pass the number of offset lines changes and the starting line changes. if (interlaceY >= descriptor.Height) { interlacePass++; switch (interlacePass) { case 1: interlaceY = 4; break; case 2: interlaceY = 2; interlaceIncrement = 4; break; case 3: interlaceY = 1; interlaceIncrement = 2; break; } } writeY = interlaceY + descriptor.Top; interlaceY += interlaceIncrement; } else { writeY = y; } pixelRow.Reset(); byte *pixelBase = pixelRow.PixelBase; for (int x = 0; x < descriptor.Width; x++) { int index = indices[i]; if (this.graphicsControlExtension == null || this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyIndex != index) { int indexOffset = index * 3; *(pixelBase + 0) = colorTable[indexOffset]; *(pixelBase + 1) = colorTable[indexOffset + 1]; *(pixelBase + 2) = colorTable[indexOffset + 2]; *(pixelBase + 3) = 255; } i++; pixelBase += 4; } pixelAccessor.CopyFrom(pixelRow, writeY, descriptor.Left); } } } if (previousFrame != null) { this.nextFrame = previousFrame; return; } if (currentFrame == null) { this.nextFrame = this.decodedImage.ToFrame(); } else { this.nextFrame = currentFrame.Clone(); } if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) { this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height); } }