public static unsafe byte[] DecompressImage(int width, int height, byte[] blocks, SquishFlags flags, Action <int, int> progressFn) { // // fix any bad flags // flags = fixFlags(flags); // // initialise the block input // byte[] dest = new byte[width * height * 4]; int bytesPerBlock = (flags & SquishFlags.Dxt1) != 0 ? 8 : 16; int progress = 0; progressFn?.Invoke(0, height); // // loop over blocks // fixed(byte *pBlocks = blocks) { byte *source = pBlocks; fixed(byte *pDest = dest) { byte *rgba = pDest; Parallel.ForEach(SteppedEnumerable.SteppedRange(0, height, 4), y => { Parallel.ForEach(SteppedEnumerable.SteppedRange(0, width, 4), x => { // // decompress the block // int blockNum = (width + 3) / 4 * (y / 4) + x / 4; byte *sourceBlock = source + bytesPerBlock * blockNum; byte[] targetRgba = new byte[4 * 16]; // // write the decompressed pixels to the correct image locations // fixed(byte *pTargetRgba = targetRgba) { decompress(pTargetRgba, sourceBlock, flags); byte *sourcePixel = pTargetRgba; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // // get the target location // int sx = x + px; int sy = y + py; if (sx < width && sy < height) { byte *targetPixel = rgba + 4 * (width * sy + sx); // // copy the rgba value // for (int i = 0; i < 4; ++i) { *targetPixel++ = *sourcePixel++; } } else { // // skip this pixel as it is outside the image // sourcePixel += 4; } } } } Interlocked.Add(ref progress, 4); progressFn?.Invoke(progress, height); }); }); } } progressFn?.Invoke(height, height); return(dest); }
public static unsafe byte[] CompressImage(byte[] rgba, int width, int height, SquishFlags flags, Action <int, int> progressFn) { // // fix any bad flags // flags = fixFlags(flags); // // initialise the block output // int blockCount = (width + 3) / 4 * ((height + 3) / 4); int blockSize = (flags & SquishFlags.Dxt1) != 0 ? 8 : 16; byte[] blocks = new byte[blockCount * blockSize]; int progress = 0; progressFn?.Invoke(0, height); // // loop over blocks // fixed(byte *pBlocks = blocks) { byte *targetBlock = pBlocks; fixed(byte *pRgba = rgba) { byte *source = pRgba; Parallel.ForEach(SteppedEnumerable.SteppedRange(0, height, 4), y => { Parallel.ForEach(SteppedEnumerable.SteppedRange(0, width, 4), x => { // // build the 4x4 block of pixels // byte[] sourceRgba = new byte[16 * 4]; int mask = 0; fixed(byte *pSourceRgba = sourceRgba) { byte *targetPixel = pSourceRgba; for (int py = 0; py < 4; ++py) { for (int px = 0; px < 4; ++px) { // // get the source pixel in the image // int sx = x + px; int sy = y + py; // // enable if we're in the image // if (sx < width && sy < height) { // // copy the rgba value // byte *sourcePixel = source + 4 * (width * sy + sx); for (int i = 0; i < 4; ++i) { *targetPixel++ = *sourcePixel++; } // // enable this pixel // mask |= 1 << (4 * py + px); } else { // // skip this pixel as its outside the image // targetPixel += 4; } } } // // compress it into the output // int blockNum = (width + 3) / 4 * (y / 4) + x / 4; byte *outputBlock = targetBlock + blockSize * blockNum; compressMasked(pSourceRgba, mask, outputBlock, flags); } }); Interlocked.Add(ref progress, 4); progressFn?.Invoke(progress, height); }); } } progressFn?.Invoke(height, height); return(blocks); }