void DecodeARW(ImageBinaryReader input, long w, long h) { BitPumpMSB bits = new BitPumpMSB(input); int sum = 0; for (long x = w; (x--) != 0;) { for (int y = 0; y < h + 1; y += 2) { bits.Fill(); if (y == h) { y = 1; } int len = 4 - (int)bits.GetBits(2); if (len == 3 && bits.GetBit() != 0) { len = 0; } if (len == 4) { while (len < 17 && bits.GetBit() == 0) { len++; } } int diff = (int)bits.GetBits(len); if (len != 0 && (diff & (1 << (len - 1))) == 0) { diff -= (1 << len) - 1; } sum += diff; Debug.Assert((sum >> 12) == 0); if (y < h) { rawImage.fullSize.rawView[x + y * rawImage.fullSize.dim.width] = (ushort)sum; } } } }
/* This is probably the slowest decoder of them all. * I cannot see any way to effectively speed up the prediction * phase, which is by far the slowest part of this algorithm. * Also there is no way to multithread this code, since prediction * is based on the output of all previous pixel (bar the first four) */ private void DecodeCompressed(ImageBinaryReader s, uint width, uint height) { int nbits; long left0, nw0, left1, nw1; long sign, low, high; long[] acarry0 = new long[3], acarry1 = new long[3]; long pred, diff; //uint pitch = rawImage.pitch; /* Build a table to quickly look up "high" value */ byte[] bittable = new byte[4096]; for (int i = 0; i < 4096; i++) { int b = i; for (high = 0; high < 12; high++) { if (((b >> (11 - (int)high)) & 1) != 0) { break; } } bittable[i] = (byte)Math.Min(12, high); } left0 = nw0 = left1 = nw1 = 0; s.ReadBytes(7); BitPumpMSB bits = new BitPumpMSB(s); for (int y = 0; y < height; y++) { var pos = y * rawImage.fullSize.UncroppedDim.width; acarry0 = new long[3]; acarry1 = new long[3]; bool y_border = y < 2; bool border = true; for (int x = 0; x < width; x++) { bits.Fill(); int i = 0; if (acarry0[2] < 3) { i = 2; } for (nbits = 2 + i; acarry0[0] >> (nbits + i) != 0; nbits++) { ; } uint b = bits.PeekBits(15); sign = (b >> 14) * -1; low = (b >> 12) & 3; high = bittable[b & 4095]; // Skip bytes used above or read bits if (high == 12) { bits.SkipBits(15); high = bits.GetBits(16 - nbits) >> 1; } else { bits.SkipBits((int)high + 1 + 3); } acarry0[0] = (high << nbits) | bits.GetBits(nbits); diff = (acarry0[0] ^ sign) + acarry0[1]; acarry0[1] = (diff * 3 + acarry0[1]) >> 5; acarry0[2] = acarry0[0] > 16 ? 0 : acarry0[2] + 1; if (border) { if (y_border && x < 2) { pred = 0; } else if (y_border) { pred = left0; } else { pred = nw0 = rawImage.fullSize.rawView[pos - rawImage.fullSize.UncroppedDim.width + x]; } rawImage.fullSize.rawView[pos + x] = (ushort)(pred + ((diff << 2) | low)); // Set predictor left0 = rawImage.fullSize.rawView[pos + x]; } else { // Have local variables for values used several tiles // (having a "UInt16 *dst_up" that caches dest[-pitch+((int)x)] is actually slower, probably stack spill or aliasing) int up = rawImage.fullSize.rawView[pos - rawImage.fullSize.UncroppedDim.width + x]; long leftMinusNw = left0 - nw0; long upMinusNw = up - nw0; // Check if sign is different, and one is not zero if (leftMinusNw * upMinusNw < 0) { if (Other_abs(leftMinusNw) > 32 || Other_abs(upMinusNw) > 32) { pred = left0 + upMinusNw; } else { pred = (left0 + up) >> 1; } } else { pred = Other_abs(leftMinusNw) > Other_abs(upMinusNw) ? left0 : up; } rawImage.fullSize.rawView[pos + x] = (ushort)(pred + ((diff << 2) | low)); // Set predictors left0 = rawImage.fullSize.rawView[pos + x]; nw0 = up; } // ODD PIXELS x += 1; bits.Fill(); i = 0; if (acarry1[2] < 3) { i = 2; } for (nbits = 2 + i; acarry1[0] >> (nbits + i) != 0; nbits++) { ; } b = bits.PeekBits(15); sign = (b >> 14) * -1; low = (b >> 12) & 3; high = bittable[b & 4095]; // Skip bytes used above or read bits if (high == 12) { bits.SkipBits(15); high = bits.GetBits(16 - nbits) >> 1; } else { bits.SkipBits((int)high + 1 + 3); } acarry1[0] = (high << nbits) | bits.GetBits(nbits); diff = (acarry1[0] ^ sign) + acarry1[1]; acarry1[1] = (diff * 3 + acarry1[1]) >> 5; acarry1[2] = acarry1[0] > 16 ? 0 : acarry1[2] + 1; if (border) { if (y_border && x < 2) { pred = 0; } else if (y_border) { pred = left1; } else { pred = nw1 = rawImage.fullSize.rawView[pos - rawImage.fullSize.UncroppedDim.width + x]; } rawImage.fullSize.rawView[pos + x] = (ushort)(left1 = pred + ((diff << 2) | low)); } else { int up = rawImage.fullSize.rawView[pos - rawImage.fullSize.UncroppedDim.width + x]; long leftminusNw = left1 - nw1; long upminusNw = up - nw1; // Check if sign is different, and one is not zero if (leftminusNw * upminusNw < 0) { if (Other_abs(leftminusNw) > 32 || Other_abs(upminusNw) > 32) { pred = left1 + upminusNw; } else { pred = (left1 + up) >> 1; } } else { pred = Other_abs(leftminusNw) > Other_abs(upminusNw) ? left1 : up; } rawImage.fullSize.rawView[pos + x] = (ushort)(left1 = pred + ((diff << 2) | low)); nw1 = up; } border = y_border; } } }