using System; using UnityEngine; public class JPGEncoder { public int[] ZigZag = new int[64] { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; private int[] YTable = new int[64]; private int[] UVTable = new int[64]; private float[] fdtbl_Y = new float[64]; private float[] fdtbl_UV = new float[64]; private BitString[] YDC_HT; private BitString[] UVDC_HT; private BitString[] YAC_HT; private BitString[] UVAC_HT; private int[] std_dc_luminance_nrcodes = new int[17] { 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; private int[] std_dc_luminance_values = new int[12] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; private int[] std_ac_luminance_nrcodes = new int[17] { 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125 }; private int[] std_ac_luminance_values = new int[162] { 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250 }; private int[] std_dc_chrominance_nrcodes = new int[17] { 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; private int[] std_dc_chrominance_values = new int[12] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; private int[] std_ac_chrominance_nrcodes = new int[17] { 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119 }; private int[] std_ac_chrominance_values = new int[162] { 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250 }; private BitString[] bitcode = new BitString[65535]; private int[] category = new int[65535]; private int bytenew; private int bytepos = 7; public ByteArray byteout = new ByteArray(); private int[] DU = new int[64]; private float[] YDU = new float[64]; private float[] UDU = new float[64]; private float[] VDU = new float[64]; public bool isDone; private BitmapData image; private int sf; public JPGEncoder(Color[] pixels, int width, int height, float quality) { image = new BitmapData(pixels, width, height); if (quality <= 0f) { quality = 1f; } if (quality > 100f) { quality = 100f; } if (quality < 50f) { sf = (int)(5000f / quality); } else { sf = (int)(200f - quality * 2f); } } private void initQuantTables(int sf) { int[] array = new int[64] { 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 }; int i; for (i = 0; i < 64; i++) { float num = Mathf.Floor(((float)(array[i] * sf) + 50f) / 100f); if (num < 1f) { num = 1f; } else if (num > 255f) { num = 255f; } YTable[ZigZag[i]] = (int)num; } int[] array2 = new int[64] { 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }; for (i = 0; i < 64; i++) { float num = Mathf.Floor(((float)(array2[i] * sf) + 50f) / 100f); if (num < 1f) { num = 1f; } else if (num > 255f) { num = 255f; } UVTable[ZigZag[i]] = (int)num; } float[] array3 = new float[8] { 1f, 1.3870399f, 1.306563f, 1.1758755f, 1f, 0.78569496f, 0.5411961f, 0.27589938f }; i = 0; for (int j = 0; j < 8; j++) { for (int k = 0; k < 8; k++) { fdtbl_Y[i] = 1f / ((float)YTable[ZigZag[i]] * array3[j] * array3[k] * 8f); fdtbl_UV[i] = 1f / ((float)UVTable[ZigZag[i]] * array3[j] * array3[k] * 8f); i++; } } } private BitString[] computeHuffmanTbl(int[] nrcodes, int[] std_table) { int num = 0; int num2 = 0; BitString[] array = new BitString[256]; for (int i = 1; i <= 16; i++) { for (int j = 1; j <= nrcodes[i]; j++) { array[std_table[num2]] = new BitString(); array[std_table[num2]].val = num; array[std_table[num2]].len = i; num2++; num++; } num *= 2; } return array; } private void initHuffmanTbl() { YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes, std_dc_luminance_values); UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes, std_dc_chrominance_values); YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes, std_ac_luminance_values); UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes, std_ac_chrominance_values); } private void initCategoryfloat() { int num = 1; int num2 = 2; for (int i = 1; i <= 15; i++) { for (int j = num; j < num2; j++) { category[32767 + j] = i; BitString bitString = new BitString(); bitString.len = i; bitString.val = j; bitcode[32767 + j] = bitString; } for (int j = -(num2 - 1); j <= -num; j++) { category[32767 + j] = i; BitString bitString = new BitString(); bitString.len = i; bitString.val = num2 - 1 + j; bitcode[32767 + j] = bitString; } num <<= 1; num2 <<= 1; } } public byte[] GetBytes() { if (!isDone) { Debug.LogError("JPEGEncoder not complete, cannot get bytes!"); return new byte[1]; } return byteout.GetAllBytes(); } private void writeBits(BitString bs) { int val = bs.val; int num = bs.len - 1; while (num >= 0) { if (((uint)val & Convert.ToUInt32(1 << num)) != 0) { bytenew |= (int)Convert.ToUInt32(1 << bytepos); } num--; bytepos--; if (bytepos < 0) { if (bytenew == 255) { writeByte(byte.MaxValue); writeByte(0); } else { writeByte((byte)bytenew); } bytepos = 7; bytenew = 0; } } } private void writeByte(byte value) { byteout.writeByte(value); } private void writeWord(int value) { writeByte((byte)((value >> 8) & 0xFF)); writeByte((byte)(value & 0xFF)); } private float[] fDCTQuant(float[] data, float[] fdtbl) { int num = 0; for (int i = 0; i < 8; i++) { float num2 = data[num] + data[num + 7]; float num3 = data[num] - data[num + 7]; float num4 = data[num + 1] + data[num + 6]; float num5 = data[num + 1] - data[num + 6]; float num6 = data[num + 2] + data[num + 5]; float num7 = data[num + 2] - data[num + 5]; float num8 = data[num + 3] + data[num + 4]; float num9 = data[num + 3] - data[num + 4]; float num10 = num2 + num8; float num11 = num2 - num8; float num12 = num4 + num6; float num13 = num4 - num6; data[num] = num10 + num12; data[num + 4] = num10 - num12; float num14 = (num13 + num11) * 0.70710677f; data[num + 2] = num11 + num14; data[num + 6] = num11 - num14; num10 = num9 + num7; num12 = num7 + num5; num13 = num5 + num3; float num15 = (num10 - num13) * 0.38268343f; float num16 = 0.5411961f * num10 + num15; float num17 = 1.306563f * num13 + num15; float num18 = num12 * 0.70710677f; float num19 = num3 + num18; float num20 = num3 - num18; data[num + 5] = num20 + num16; data[num + 3] = num20 - num16; data[num + 1] = num19 + num17; data[num + 7] = num19 - num17; num += 8; } num = 0; for (int i = 0; i < 8; i++) { float num2 = data[num] + data[num + 56]; float num3 = data[num] - data[num + 56]; float num4 = data[num + 8] + data[num + 48]; float num5 = data[num + 8] - data[num + 48]; float num6 = data[num + 16] + data[num + 40]; float num7 = data[num + 16] - data[num + 40]; float num8 = data[num + 24] + data[num + 32]; float num9 = data[num + 24] - data[num + 32]; float num10 = num2 + num8; float num11 = num2 - num8; float num12 = num4 + num6; float num13 = num4 - num6; data[num] = num10 + num12; data[num + 32] = num10 - num12; float num14 = (num13 + num11) * 0.70710677f; data[num + 16] = num11 + num14; data[num + 48] = num11 - num14; num10 = num9 + num7; num12 = num7 + num5; num13 = num5 + num3; float num15 = (num10 - num13) * 0.38268343f; float num16 = 0.5411961f * num10 + num15; float num17 = 1.306563f * num13 + num15; float num18 = num12 * 0.70710677f; float num19 = num3 + num18; float num20 = num3 - num18; data[num + 40] = num20 + num16; data[num + 24] = num20 - num16; data[num + 8] = num19 + num17; data[num + 56] = num19 - num17; num++; } for (int i = 0; i < 64; i++) { data[i] = Mathf.Round(data[i] * fdtbl[i]); } return data; } private void writeAPP0() { writeWord(65504); writeWord(16); writeByte(74); writeByte(70); writeByte(73); writeByte(70); writeByte(0); writeByte(1); writeByte(1); writeByte(0); writeWord(1); writeWord(1); writeByte(0); writeByte(0); } private void writeSOF0(int width, int height) { writeWord(65472); writeWord(17); writeByte(8); writeWord(height); writeWord(width); writeByte(3); writeByte(1); writeByte(17); writeByte(0); writeByte(2); writeByte(17); writeByte(1); writeByte(3); writeByte(17); writeByte(1); } private void writeDQT() { writeWord(65499); writeWord(132); writeByte(0); for (int i = 0; i < 64; i++) { writeByte((byte)YTable[i]); } writeByte(1); for (int i = 0; i < 64; i++) { writeByte((byte)UVTable[i]); } } private void writeDHT() { writeWord(65476); writeWord(418); writeByte(0); for (int i = 0; i < 16; i++) { writeByte((byte)std_dc_luminance_nrcodes[i + 1]); } for (int i = 0; i <= 11; i++) { writeByte((byte)std_dc_luminance_values[i]); } writeByte(16); for (int i = 0; i < 16; i++) { writeByte((byte)std_ac_luminance_nrcodes[i + 1]); } for (int i = 0; i <= 161; i++) { writeByte((byte)std_ac_luminance_values[i]); } writeByte(1); for (int i = 0; i < 16; i++) { writeByte((byte)std_dc_chrominance_nrcodes[i + 1]); } for (int i = 0; i <= 11; i++) { writeByte((byte)std_dc_chrominance_values[i]); } writeByte(17); for (int i = 0; i < 16; i++) { writeByte((byte)std_ac_chrominance_nrcodes[i + 1]); } for (int i = 0; i <= 161; i++) { writeByte((byte)std_ac_chrominance_values[i]); } } private void writeSOS() { writeWord(65498); writeWord(12); writeByte(3); writeByte(1); writeByte(0); writeByte(2); writeByte(17); writeByte(3); writeByte(17); writeByte(0); writeByte(63); writeByte(0); } private float processDU(float[] CDU, float[] fdtbl, float DC, BitString[] HTDC, BitString[] HTAC) { BitString bs = HTAC[0]; BitString bs2 = HTAC[240]; float[] array = fDCTQuant(CDU, fdtbl); for (int i = 0; i < 64; i++) { DU[ZigZag[i]] = (int)array[i]; } int num = (int)((float)DU[0] - DC); DC = DU[0]; if (num == 0) { writeBits(HTDC[0]); } else { writeBits(HTDC[category[32767 + num]]); writeBits(bitcode[32767 + num]); } int num2 = 63; while (num2 > 0 && DU[num2] == 0) { num2--; } if (num2 == 0) { writeBits(bs); return DC; } for (int i = 1; i <= num2; i++) { int num3 = i; for (; DU[i] == 0 && i <= num2; i++) { } int num4 = i - num3; if (num4 >= 16) { for (int j = 1; j <= num4 / 16; j++) { writeBits(bs2); } num4 &= 0xF; } writeBits(HTAC[num4 * 16 + category[32767 + DU[i]]]); writeBits(bitcode[32767 + DU[i]]); } if (num2 != 63) { writeBits(bs); } return DC; } private void RGB2YUV(BitmapData img, int xpos, int ypos) { int num = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { Color pixelColor = img.getPixelColor(xpos + j, img.height - (ypos + i)); float num2 = pixelColor.r * 255f; float num3 = pixelColor.g * 255f; float num4 = pixelColor.b * 255f; YDU[num] = 0.299f * num2 + 0.587f * num3 + 0.114f * num4 - 128f; UDU[num] = -0.16874f * num2 + -0.33126f * num3 + 0.5f * num4; VDU[num] = 0.5f * num2 + -0.41869f * num3 + -0.08131f * num4; num++; } } } public void doEncoding() { isDone = false; initHuffmanTbl(); initCategoryfloat(); initQuantTables(sf); encode(); isDone = true; image = null; } private void encode() { byteout = new ByteArray(); bytenew = 0; bytepos = 7; writeWord(65496); writeAPP0(); writeDQT(); writeSOF0(image.width, image.height); writeDHT(); writeSOS(); float dC = 0f; float dC2 = 0f; float dC3 = 0f; bytenew = 0; bytepos = 7; for (int i = 0; i < image.height; i += 8) { for (int j = 0; j < image.width; j += 8) { RGB2YUV(image, j, i); dC = processDU(YDU, fdtbl_Y, dC, YDC_HT, YAC_HT); dC2 = processDU(UDU, fdtbl_UV, dC2, UVDC_HT, UVAC_HT); dC3 = processDU(VDU, fdtbl_UV, dC3, UVDC_HT, UVAC_HT); } } if (bytepos >= 0) { BitString bitString = new BitString(); bitString.len = bytepos + 1; bitString.val = (1 << bytepos + 1) - 1; writeBits(bitString); } writeWord(65497); isDone = true; } }