public ArgbSurface(SurfaceData surfaceData, bool alphaPremultiplied = false) : base(surfaceData.Width, surfaceData.Height, 8, alphaPremultiplied) { int rowLength = sizeof(uint) * Width; int dataLength = rowLength * Height; if (surfaceData.Stride < rowLength) throw new ArgumentException(); data = new byte[dataLength]; unsafe { fixed (byte* dataPointer = data) { byte* destinationRowPointer = dataPointer; byte* sourceRowPointer = (byte*)surfaceData.DataPointer; for (int i = Height; i-- != 0; destinationRowPointer += rowLength, sourceRowPointer += surfaceData.Stride) { ArgbColor* destinationPointer = (ArgbColor*)destinationRowPointer; ArgbColor* sourcePointer = (ArgbColor*)sourceRowPointer; for (int j = Width; j-- != 0; ) *destinationPointer++ = *sourcePointer++; } } } }
/// <summary>Copies the contents of the surface to a buffer of same dimensions.</summary> /// <remarks> /// The destination buffer should use the ARGB format represented by <see cref="ArgbColor"/>. /// Some basic checks will be done to disallow invalid buffer informations. /// However, it is the responsibility of the caller to provide a valid destination buffer. /// </remarks> /// <param name="surfaceData">Information on the destination buffer.</param> /// <exception cref="ArgumentException">The dimensions of the buffer specified by <paramref name="surfaceData"/> do not match those of the surface.</exception> /// <exception cref="InvalidOperationException">The stride in <paramref name="surfaceData"/> does not match the width.</exception> public void CopyToArgb(SurfaceData surfaceData) { if (surfaceData.Width != width || surfaceData.Height != height) { throw new ArgumentException(); } if (surfaceData.Stride < sizeof(uint) * surfaceData.Width) { throw new InvalidOperationException(); } CopyToArgbInternal(surfaceData); }
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 CopyToArgbTransparent(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;) { *destinationPointer++ = palettePointer[*sourcePointer++]; } } } }
protected unsafe override void CopyToArgbInternal(SurfaceData surfaceData) { int rowLength = sizeof(uint) * Width; fixed(byte *dataPointer = data) { byte *destinationRowPointer = (byte *)surfaceData.DataPointer; byte *sourceRowPointer = dataPointer; for (int i = Height; i-- != 0; destinationRowPointer += surfaceData.Stride, sourceRowPointer += rowLength) { ArgbColor *destinationPointer = (ArgbColor *)destinationRowPointer; ArgbColor *sourcePointer = (ArgbColor *)sourceRowPointer; for (int j = Width; j-- != 0;) { *destinationPointer++ = *sourcePointer++; } } } }
protected unsafe override void CopyToArgbInternal(SurfaceData surfaceData) { switch (AlphaBitCount) { case 0: CopyToArgbOpaque(surfaceData); break; case 1: CopyToArgbAlpha1(surfaceData); break; case 4: CopyToArgbAlpha4(surfaceData); break; case 8: if (separateAlpha) { CopyToArgbAlpha8(surfaceData); } else { CopyToArgbTransparent(surfaceData); } break; default: throw new NotSupportedException(); // Should never happen… } }
protected abstract void CopyToArgbInternal(SurfaceData surfaceData);
protected override void CopyToArgbInternal(SurfaceData surfaceData) { throw new NotSupportedException(); }
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 override sealed void CopyToArgbInternal(SurfaceData surfaceData) { @this.CopyToArgbInternal(surfaceData); }
private unsafe void CopyToArgbAlpha1(SurfaceData surfaceData) { fixed (byte* dataPointer = data) fixed (ArgbColor* palettePointer = palette) { byte* destinationRowPointer = (byte*)surfaceData.DataPointer; byte* sourceColorPointer = dataPointer; byte* sourceAlphaPointer = dataPointer + Width * Height; byte alphaData = 0; byte alphaState = 0; for (int i = Height; i-- != 0; destinationRowPointer += surfaceData.Stride) { ArgbColor* destinationPointer = (ArgbColor*)destinationRowPointer; for (int j = Width; j-- != 0; alphaState--, alphaData >>= 1) { if (alphaState == 0) { alphaState = 8; alphaData = *sourceAlphaPointer++; } ArgbColor.CopyWithAlpha(destinationPointer++, palettePointer + *sourceColorPointer++, (alphaData & 1) != 0 ? (byte)255 : (byte)0); } } } }
/// <summary>Copies the contents of the surface to a buffer of same dimensions.</summary> /// <remarks> /// The destination buffer should use the ARGB format represented by <see cref="ArgbColor"/>. /// Some basic checks will be done to disallow invalid buffer informations. /// However, it is the responsibility of the caller to provide a valid destination buffer. /// </remarks> /// <param name="surfaceData">Information on the destination buffer.</param> /// <exception cref="ArgumentException">The dimensions of the buffer specified by <paramref name="surfaceData"/> do not match those of the surface.</exception> /// <exception cref="InvalidOperationException">The stride in <paramref name="surfaceData"/> does not match the width.</exception> public void CopyToArgb(SurfaceData surfaceData) { if (surfaceData.Width != width || surfaceData.Height != height) throw new ArgumentException(); if (surfaceData.Stride < sizeof(uint) * surfaceData.Width) throw new InvalidOperationException(); CopyToArgbInternal(surfaceData); }
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) { int rowLength = sizeof(uint) * Width; fixed (byte* dataPointer = data) { byte* destinationRowPointer = (byte*)surfaceData.DataPointer; byte* sourceRowPointer = dataPointer; for (int i = Height; i-- != 0; destinationRowPointer += surfaceData.Stride, sourceRowPointer += rowLength) { ArgbColor* destinationPointer = (ArgbColor*)destinationRowPointer; ArgbColor* sourcePointer = (ArgbColor*)sourceRowPointer; for (int j = Width; j-- != 0; ) *destinationPointer++ = *sourcePointer++; } } }
private unsafe void CopyToArgbTransparent(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; ) *destinationPointer++ = palettePointer[*sourcePointer++]; } } }
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 CopyToArgbAlpha4(SurfaceData surfaceData) { // Use a precalculated alpha table for very fast conversions. var alphaTable = stackalloc byte[16]; for (int i = 0; i < 16; i++) alphaTable[i] = (byte)(i * (255 * 2185) >> 15); // x / 15 = x * 2185 >> 15 (for 0 ≤ x ≤ 15 * 255) fixed (byte* dataPointer = data) fixed (ArgbColor* palettePointer = palette) { byte* destinationRowPointer = (byte*)surfaceData.DataPointer; byte* sourceColorPointer = dataPointer; byte* sourceAlphaPointer = dataPointer + Width * Height; byte alphaData = 0; bool alphaState = false; for (int i = Height; i-- != 0; destinationRowPointer += surfaceData.Stride) { ArgbColor* destinationPointer = (ArgbColor*)destinationRowPointer; for (int j = Width; j-- != 0; ) ArgbColor.CopyWithAlpha(destinationPointer++, palettePointer + *sourceColorPointer++, alphaTable[(alphaState = !alphaState) ? (alphaData = *sourceAlphaPointer++) & 0xF : alphaData >> 4]); } } }
protected sealed override void CopyToArgbInternal(SurfaceData surfaceData) { @this.CopyToArgbInternal(surfaceData); }
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) { switch (AlphaBitCount) { case 0: CopyToArgbOpaque(surfaceData); break; case 1: CopyToArgbAlpha1(surfaceData); break; case 4: CopyToArgbAlpha4(surfaceData); break; case 8: if (separateAlpha) CopyToArgbAlpha8(surfaceData); else CopyToArgbTransparent(surfaceData); break; default: throw new NotSupportedException(); // Should never happen… } }