/// <summary>Merges two colors for DXT decompression.</summary> /// <param name="result">The storage to be used for the result.</param> /// <param name="color1">A color.</param> /// <param name="color2">A color.</param> internal static unsafe void DxtMergeHalves(ArgbColor* result, ArgbColor* color1, ArgbColor* color2) { result->B = (byte)((color1->B + color2->B) >> 1); result->G = (byte)((color1->G + color2->G) >> 1); result->R = (byte)((color1->R + color2->R) >> 1); result->A = 255; }
/// <summary>Copies an <see cref="ArgbColor"/> into another, with forced opaque alpha.</summary> /// <param name="destination">The destination color.</param> /// <param name="source">The source color.</param> internal static unsafe void CopyWithAlpha(ArgbColor* destination, ArgbColor* source, byte alpha) { destination->B = source->B; destination->G = source->G; destination->R = source->R; destination->A = alpha; }
/// <summary>Copies an <see cref="ArgbColor"/> into another, with forced opaque alpha.</summary> /// <param name="destination">The destination color.</param> /// <param name="source">The source color.</param> internal static unsafe void CopyOpaque(ArgbColor* destination, ArgbColor* source) { destination->B = source->B; destination->G = source->G; destination->R = source->R; destination->A = 255; }
/// <summary>Reads a 256 color palette from a stream</summary> /// <param name="reader">The BinaryReader used for reading data in the stream</param> /// <param name="alphaOperation">The operation to apply on each palette entry's alpha component</param> /// <returns>An array of bytes containing the palette entries</returns> private ArgbColor[] ReadPalette(BinaryReader reader, AlphaOperation alphaOperation) { var palette = new ArgbColor[256]; for (int i = 0; i < palette.Length; i++) { byte b = reader.ReadByte(); byte g = reader.ReadByte(); byte r = reader.ReadByte(); byte a = reader.ReadByte(); palette[i] = new ArgbColor(r, g, b, alphaOperation != AlphaOperation.None ? alphaOperation == AlphaOperation.SetAlpha ? (byte)255 : (byte)~a : a); } return(palette); }
public PaletteSurface(ArgbColor[] palette, int width, int height, bool opaque = true, byte separateAlphaBitCount = 0, bool alphaPremultiplied = false, bool sharePalette = false) : base(width, height, GetAlphaBitCount(opaque, separateAlphaBitCount), alphaPremultiplied) { if (palette == null) throw new ArgumentNullException("palette"); if (palette.Length != 256) throw new ArgumentException(); int length = Width * Height; if (this.separateAlpha = separateAlphaBitCount != 0) { int divisor = 8 / separateAlphaBitCount; int remainder = length % divisor; length = length / divisor; if (remainder != 0) length++; } this.data = new byte[length]; this.palette = sharePalette ? palette : palette.Clone() as ArgbColor[]; }
private unsafe void CopyToArgbAlpha8(SurfaceData surfaceData) { fixed(byte *dataPointer = data) fixed(ArgbColor * palettePointer = palette) { byte *destinationRowPointer = (byte *)surfaceData.DataPointer; byte *sourceColorPointer = dataPointer; byte *sourceAlphaPointer = dataPointer + Width * Height; for (int i = Height; i-- != 0; destinationRowPointer += surfaceData.Stride) { ArgbColor *destinationPointer = (ArgbColor *)destinationRowPointer; for (int j = Width; j-- != 0;) { ArgbColor.CopyWithAlpha(destinationPointer++, palettePointer + *sourceColorPointer++, *sourceAlphaPointer++); } } } }
private unsafe void CopyToArgbOpaque(SurfaceData surfaceData) { int rowLength = Width; fixed(byte *dataPointer = data) fixed(ArgbColor * palettePointer = palette) { byte *destinationRowPointer = (byte *)surfaceData.DataPointer; byte *sourcePointer = dataPointer; for (int i = Height; i-- != 0; destinationRowPointer += surfaceData.Stride) { ArgbColor *destinationPointer = (ArgbColor *)destinationRowPointer; for (int j = Width; j-- != 0;) { ArgbColor.CopyOpaque(destinationPointer++, palettePointer + *sourcePointer++); } } } }
protected unsafe override void CopyToArgbInternal(SurfaceData surfaceData) { var colors = stackalloc ArgbColor[4]; fixed(byte *dataPointer = data) { var destinationRowPointer = (byte *)surfaceData.DataPointer; var sourcePointer = dataPointer; int rowBlockStride = surfaceData.Stride << 2; for (int i = Height; i > 0; i -= 4, destinationRowPointer += rowBlockStride) { var destinationPointer = destinationRowPointer; for (int j = Width; j > 0; j -= 4) { var alphaPointer = (ushort *)sourcePointer; // Save the alpha block pointer for later sourcePointer += 8; // Get to the color block ushort color0 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); ushort color1 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); colors[0] = new ArgbColor(color0); colors[1] = new ArgbColor(color1); ArgbColor.DxtMergeThirds(colors + 2, colors + 1, colors); ArgbColor.DxtMergeThirds(colors + 3, colors, colors + 1); // Handle the case where the surface's width is not a multiple of 4. int inverseBlockWidth = j > 4 ? 0 : 4 - j; var blockRowDestinationPointer = destinationPointer; for (int k = 4; k-- != 0; blockRowDestinationPointer += surfaceData.Stride) { byte rowData = *sourcePointer++; if (i + k < 4) { continue; // Handle the case where the surface's height is not a multiple of 4. } var blockDestinationPointer = (ArgbColor *)blockRowDestinationPointer; // The small loop here has been unrolled, which should be well worth it: // - No loop variable is needed. // - No useless shift and incrementation for the last step. // - Only one conditional jump. // - The loop will be executed very often, as the blocks are very small. switch (inverseBlockWidth) { case 0: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], (byte)(*alphaPointer << 4)); rowData >>= 2; goto case 1; case 1: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], (byte)(*alphaPointer & 0xF0)); rowData >>= 2; goto case 2; case 2: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], (byte)((*alphaPointer >> 4) & 0xF0)); rowData >>= 2; goto case 3; case 3: ArgbColor.CopyWithAlpha(blockDestinationPointer, &colors[rowData & 3], (byte)((*alphaPointer++ >> 8) & 0xF0)); break; } } destinationPointer += 4 * sizeof(uint); // Skip the 4 processed pixels } } } }
protected unsafe override void CopyToArgbInternal(SurfaceData surfaceData) { var colors = stackalloc ArgbColor[4]; var alpha = stackalloc byte[8]; fixed (byte* dataPointer = data) { var destinationRowPointer = (byte*)surfaceData.DataPointer; var sourcePointer = dataPointer; int rowBlockStride = surfaceData.Stride << 2; for (int i = Height; i > 0; i -= 4, destinationRowPointer += rowBlockStride) { var destinationPointer = destinationRowPointer; for (int j = Width; j > 0; j -= 4) { alpha[0] = *sourcePointer++; alpha[1] = *sourcePointer++; // The divisions here have been optimized with multiply/shift // Maybe there is way for some optimization in the multiplications, (using shifts and additions/substractions where applicable) // but for now I just hope the .NET JIT knows how to do those optimizations when they are possible… if (alpha[0] <= alpha[1]) { alpha[2] = (byte)((4 * alpha[0] + alpha[1]) * 1639 >> 13); alpha[3] = (byte)((3 * alpha[0] + 2 * alpha[1]) * 1639 >> 13); alpha[4] = (byte)((2 * alpha[0] + 3 * alpha[1]) * 1639 >> 13); alpha[5] = (byte)((alpha[0] + 4 * alpha[1]) * 1639 >> 13); alpha[6] = 0; alpha[7] = 255; } else { alpha[2] = (byte)((6 * alpha[0] + alpha[1]) * 2341 >> 14); alpha[3] = (byte)((5 * alpha[0] + 2 * alpha[1]) * 2341 >> 14); alpha[4] = (byte)((4 * alpha[0] + 3 * alpha[1]) * 2341 >> 14); alpha[5] = (byte)((3 * alpha[0] + 4 * alpha[1]) * 2341 >> 14); alpha[6] = (byte)((2 * alpha[0] + 5 * alpha[1]) * 2341 >> 14); alpha[7] = (byte)((alpha[0] + 6 * alpha[1]) * 2341 >> 14); } // Store the block's alpha data in a 64 bit integer. This will probably be a bit slower on 32-bit CPUs, but who cares… :p ulong blockAlphaData = (ulong)(*sourcePointer++ | (uint)*sourcePointer++ << 8 | (uint)*sourcePointer++ << 16 | (uint)*sourcePointer++ << 24) | (ulong)(*sourcePointer++ | (uint)*sourcePointer++ << 8) << 32; ushort color0 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); ushort color1 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); colors[0] = new ArgbColor(color0); colors[1] = new ArgbColor(color1); ArgbColor.DxtMergeThirds(colors + 2, colors + 1, colors); ArgbColor.DxtMergeThirds(colors + 3, colors, colors + 1); // Handle the case where the surface's width is not a multiple of 4. int inverseBlockWidth = j > 4 ? 0 : 4 - j; var blockRowDestinationPointer = destinationPointer; for (int k = 4; k-- != 0; blockRowDestinationPointer += surfaceData.Stride) { byte rowData = *sourcePointer++; if (i + k < 4) continue; // Handle the case where the surface's height is not a multiple of 4. var blockDestinationPointer = (ArgbColor*)blockRowDestinationPointer; if (inverseBlockWidth != 0) blockAlphaData >>= 3 * inverseBlockWidth; // The small loop here has been unrolled, which shoudl be well worth it: // - No loop variable is needed. // - No useless shift and incrementation for the last step. // - Only one conditional jump. // - The loop will be executed very often, as the blocks are very small. switch (inverseBlockWidth) { case 0: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], alpha[blockAlphaData & 7]); rowData >>= 2; blockAlphaData >>= 3; goto case 1; case 1: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], alpha[blockAlphaData & 7]); rowData >>= 2; blockAlphaData >>= 3; goto case 2; case 2: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], alpha[blockAlphaData & 7]); rowData >>= 2; blockAlphaData >>= 3; goto case 3; case 3: ArgbColor.CopyWithAlpha(blockDestinationPointer, &colors[rowData & 3], alpha[blockAlphaData & 7]); blockAlphaData >>= 3; break; } } destinationPointer += 4 * sizeof(uint); // Skip the 4 processed pixels } } } }
protected unsafe override void CopyToArgbInternal(SurfaceData surfaceData) { var colors = stackalloc ArgbColor[4]; fixed (byte* dataPointer = data) { var destinationRowPointer = (byte*)surfaceData.DataPointer; var sourcePointer = dataPointer; int rowBlockStride = surfaceData.Stride << 2; for (int i = Height; i > 0; i -= 4, destinationRowPointer += rowBlockStride) { var destinationPointer = destinationRowPointer; for (int j = Width; j > 0; j -= 4) { var alphaPointer = (ushort*)sourcePointer; // Save the alpha block pointer for later sourcePointer += 8; // Get to the color block ushort color0 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); ushort color1 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); colors[0] = new ArgbColor(color0); colors[1] = new ArgbColor(color1); ArgbColor.DxtMergeThirds(colors + 2, colors + 1, colors); ArgbColor.DxtMergeThirds(colors + 3, colors, colors + 1); // Handle the case where the surface's width is not a multiple of 4. int inverseBlockWidth = j > 4 ? 0 : 4 - j; var blockRowDestinationPointer = destinationPointer; for (int k = 4; k-- != 0; blockRowDestinationPointer += surfaceData.Stride) { byte rowData = *sourcePointer++; if (i + k < 4) continue; // Handle the case where the surface's height is not a multiple of 4. var blockDestinationPointer = (ArgbColor*)blockRowDestinationPointer; // The small loop here has been unrolled, which should be well worth it: // - No loop variable is needed. // - No useless shift and incrementation for the last step. // - Only one conditional jump. // - The loop will be executed very often, as the blocks are very small. switch (inverseBlockWidth) { case 0: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], (byte)(*alphaPointer << 4)); rowData >>= 2; goto case 1; case 1: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], (byte)(*alphaPointer & 0xF0)); rowData >>= 2; goto case 2; case 2: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], (byte)((*alphaPointer >> 4) & 0xF0)); rowData >>= 2; goto case 3; case 3: ArgbColor.CopyWithAlpha(blockDestinationPointer, &colors[rowData & 3], (byte)((*alphaPointer++ >> 8) & 0xF0)); break; } } destinationPointer += 4 * sizeof(uint); // Skip the 4 processed pixels } } } }
protected unsafe override void CopyToArgbInternal(SurfaceData surfaceData) { var colors = stackalloc ArgbColor[4]; var alpha = stackalloc byte[8]; fixed(byte *dataPointer = data) { var destinationRowPointer = (byte *)surfaceData.DataPointer; var sourcePointer = dataPointer; int rowBlockStride = surfaceData.Stride << 2; for (int i = Height; i > 0; i -= 4, destinationRowPointer += rowBlockStride) { var destinationPointer = destinationRowPointer; for (int j = Width; j > 0; j -= 4) { alpha[0] = *sourcePointer++; alpha[1] = *sourcePointer++; // The divisions here have been optimized with multiply/shift // Maybe there is way for some optimization in the multiplications, (using shifts and additions/substractions where applicable) // but for now I just hope the .NET JIT knows how to do those optimizations when they are possible… if (alpha[0] <= alpha[1]) { alpha[2] = (byte)((4 * alpha[0] + alpha[1]) * 1639 >> 13); alpha[3] = (byte)((3 * alpha[0] + 2 * alpha[1]) * 1639 >> 13); alpha[4] = (byte)((2 * alpha[0] + 3 * alpha[1]) * 1639 >> 13); alpha[5] = (byte)((alpha[0] + 4 * alpha[1]) * 1639 >> 13); alpha[6] = 0; alpha[7] = 255; } else { alpha[2] = (byte)((6 * alpha[0] + alpha[1]) * 2341 >> 14); alpha[3] = (byte)((5 * alpha[0] + 2 * alpha[1]) * 2341 >> 14); alpha[4] = (byte)((4 * alpha[0] + 3 * alpha[1]) * 2341 >> 14); alpha[5] = (byte)((3 * alpha[0] + 4 * alpha[1]) * 2341 >> 14); alpha[6] = (byte)((2 * alpha[0] + 5 * alpha[1]) * 2341 >> 14); alpha[7] = (byte)((alpha[0] + 6 * alpha[1]) * 2341 >> 14); } // Store the block's alpha data in a 64 bit integer. This will probably be a bit slower on 32-bit CPUs, but who cares… :p ulong blockAlphaData = (ulong)(*sourcePointer++ | (uint)*sourcePointer++ << 8 | (uint)*sourcePointer++ << 16 | (uint)*sourcePointer++ << 24) | (ulong)(*sourcePointer++ | (uint)*sourcePointer++ << 8) << 32; ushort color0 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); ushort color1 = (ushort)(*sourcePointer++ | *sourcePointer++ << 8); colors[0] = new ArgbColor(color0); colors[1] = new ArgbColor(color1); ArgbColor.DxtMergeThirds(colors + 2, colors + 1, colors); ArgbColor.DxtMergeThirds(colors + 3, colors, colors + 1); // Handle the case where the surface's width is not a multiple of 4. int inverseBlockWidth = j > 4 ? 0 : 4 - j; var blockRowDestinationPointer = destinationPointer; for (int k = 4; k-- != 0; blockRowDestinationPointer += surfaceData.Stride) { byte rowData = *sourcePointer++; if (i + k < 4) { continue; // Handle the case where the surface's height is not a multiple of 4. } var blockDestinationPointer = (ArgbColor *)blockRowDestinationPointer; if (inverseBlockWidth != 0) { blockAlphaData >>= 3 * inverseBlockWidth; } // The small loop here has been unrolled, which shoudl be well worth it: // - No loop variable is needed. // - No useless shift and incrementation for the last step. // - Only one conditional jump. // - The loop will be executed very often, as the blocks are very small. switch (inverseBlockWidth) { case 0: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], alpha[blockAlphaData & 7]); rowData >>= 2; blockAlphaData >>= 3; goto case 1; case 1: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], alpha[blockAlphaData & 7]); rowData >>= 2; blockAlphaData >>= 3; goto case 2; case 2: ArgbColor.CopyWithAlpha(blockDestinationPointer++, &colors[rowData & 3], alpha[blockAlphaData & 7]); rowData >>= 2; blockAlphaData >>= 3; goto case 3; case 3: ArgbColor.CopyWithAlpha(blockDestinationPointer, &colors[rowData & 3], alpha[blockAlphaData & 7]); blockAlphaData >>= 3; break; } } destinationPointer += 4 * sizeof(uint); // Skip the 4 processed pixels } } } }
/// <summary>Merges two colors for DXT decompression.</summary> /// <param name="result">The storage to be used for the result.</param> /// <param name="minColor">The color whose weight will be 1/3.</param> /// <param name="maxColor">The color whose weight will be 2/3.</param> internal static unsafe void DxtMergeThirds(ArgbColor* result, ArgbColor* minColor, ArgbColor* maxColor) { // Formula used here: // x / 3 = x * 683 >> 11 (for 0 ≤ x ≤ 3 * 255) // Need to verify that this is indeed faster, but it'll do the work for now. result->B = (byte)((minColor->B + maxColor->B + maxColor->B) * 683 >> 11); result->G = (byte)((minColor->G + maxColor->G + maxColor->G) * 683 >> 11); result->R = (byte)((minColor->R + maxColor->R + maxColor->R) * 683 >> 11); result->A = 255; }