├── .gitignore ├── Assets ├── JPEGEncoder.cs ├── JPEGEncoder.cs.meta ├── StreamingAssets.meta ├── StreamingAssets │ ├── log.txt │ └── log.txt.meta ├── UpLoaderDemo.cs ├── UpLoaderDemo.cs.meta ├── demo.unity └── demo.unity.meta ├── PHP └── upload.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Bb]uilds/ 6 | Assets/AssetStoreTools* 7 | 8 | # Visual Studio cache directory 9 | .vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | *.opendb 26 | 27 | # Unity3D generated meta files 28 | *.pidb.meta 29 | *.pdb.meta 30 | 31 | # Unity3D Generated File On Crash Reports 32 | sysinfo.txt 33 | 34 | # Builds 35 | *.apk 36 | *.unitypackage 37 | -------------------------------------------------------------------------------- /Assets/JPEGEncoder.cs: -------------------------------------------------------------------------------- 1 | #if !UNITY_FLASH 2 | using UnityEngine; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | //Copy from http://mcain.bluehoststaff.com/gamedesignftp/backup_Corey/Mega-Fiers/Scripts/MegaGrab/JPGEncoder.cs 7 | public class ByteArrayTool 8 | { 9 | private MemoryStream stream; 10 | private BinaryWriter writer; 11 | 12 | public ByteArrayTool() 13 | { 14 | stream = new MemoryStream(); 15 | writer = new BinaryWriter(stream); 16 | } 17 | 18 | /** 19 | * Function from AS3--add a byte to our stream 20 | */ 21 | public void writeByte(byte value) 22 | { 23 | writer.Write(value); 24 | } 25 | 26 | /** 27 | * Spit back all bytes--to either pass via WWW or save to disk 28 | */ 29 | public byte[] GetAllBytes() 30 | { 31 | byte[] buffer = new byte[stream.Length]; 32 | stream.Position = 0; 33 | stream.Read(buffer, 0, buffer.Length); 34 | 35 | return buffer; 36 | } 37 | } 38 | 39 | /** 40 | * This should really be a struct--if you care, declare it in C# 41 | */ 42 | class BitString 43 | { 44 | public int len = 0; 45 | public int val = 0; 46 | } 47 | 48 | /** 49 | * Another flash class--emulating the stuff the encoder uses 50 | */ 51 | public class BitmapData 52 | { 53 | public int height; 54 | public int width; 55 | 56 | private Color[] pixels; 57 | 58 | /** 59 | * Pull all of our pixels off the texture (Unity stuff isn't thread safe, and this is faster) 60 | */ 61 | public BitmapData(Color[] _pixels, int _width, int _height) 62 | { 63 | height = _height; 64 | width = _width; 65 | 66 | pixels = _pixels; 67 | } 68 | 69 | public BitmapData(Texture2D texture) 70 | { 71 | height = texture.height; 72 | width = texture.width; 73 | 74 | pixels = texture.GetPixels(); 75 | } 76 | 77 | /** 78 | * Mimic the flash function 79 | */ 80 | public Color getPixelColor(int x, int y) 81 | { 82 | if (x >= width) 83 | x = width - 1; 84 | 85 | if (y >= height) 86 | y = height - 1; 87 | 88 | if (x < 0) 89 | x = 0; 90 | 91 | if (y < 0) 92 | y = 0; 93 | 94 | return pixels[y * width + x]; 95 | } 96 | } 97 | 98 | /** 99 | * Class that converts BitmapData into a valid JPEG 100 | */ 101 | public class JPGEncoder 102 | { 103 | 104 | // Static table initialization 105 | 106 | public int[] ZigZag = new int[64] { 107 | 0, 1, 5, 6,14,15,27,28, 108 | 2, 4, 7,13,16,26,29,42, 109 | 3, 8,12,17,25,30,41,43, 110 | 9,11,18,24,31,40,44,53, 111 | 10,19,23,32,39,45,52,54, 112 | 20,22,33,38,46,51,55,60, 113 | 21,34,37,47,50,56,59,61, 114 | 35,36,48,49,57,58,62,63 115 | }; 116 | 117 | private int[] YTable = new int[64]; 118 | private int[] UVTable = new int[64]; 119 | private float[] fdtbl_Y = new float[64]; 120 | private float[] fdtbl_UV = new float[64]; 121 | 122 | private void initQuantTables(int sf) 123 | { 124 | int i; 125 | float t; 126 | int[] YQT = new int[64] { 127 | 16, 11, 10, 16, 24, 40, 51, 61, 128 | 12, 12, 14, 19, 26, 58, 60, 55, 129 | 14, 13, 16, 24, 40, 57, 69, 56, 130 | 14, 17, 22, 29, 51, 87, 80, 62, 131 | 18, 22, 37, 56, 68,109,103, 77, 132 | 24, 35, 55, 64, 81,104,113, 92, 133 | 49, 64, 78, 87,103,121,120,101, 134 | 72, 92, 95, 98,112,100,103, 99 135 | }; 136 | 137 | for (i = 0; i < 64; i++) 138 | { 139 | t = Mathf.Floor((YQT[i] * sf + 50.0f) / 100.0f); 140 | if (t < 1.0f) 141 | { 142 | t = 1.0f; 143 | } 144 | else if (t > 255.0f) 145 | { 146 | t = 255.0f; 147 | } 148 | YTable[ZigZag[i]] = (int)t; 149 | } 150 | 151 | int[] UVQT = new int[64] { 152 | 17, 18, 24, 47, 99, 99, 99, 99, 153 | 18, 21, 26, 66, 99, 99, 99, 99, 154 | 24, 26, 56, 99, 99, 99, 99, 99, 155 | 47, 66, 99, 99, 99, 99, 99, 99, 156 | 99, 99, 99, 99, 99, 99, 99, 99, 157 | 99, 99, 99, 99, 99, 99, 99, 99, 158 | 99, 99, 99, 99, 99, 99, 99, 99, 159 | 99, 99, 99, 99, 99, 99, 99, 99 160 | }; 161 | 162 | for (i = 0; i < 64; i++) 163 | { 164 | t = Mathf.Floor((UVQT[i] * sf + 50.0f) / 100.0f); 165 | if (t < 1.0f) 166 | { 167 | t = 1.0f; 168 | } 169 | else if (t > 255.0f) 170 | { 171 | t = 255.0f; 172 | } 173 | UVTable[ZigZag[i]] = (int)t; 174 | } 175 | 176 | float[] aasf = new float[8] { 177 | 1.0f, 1.387039845f, 1.306562965f, 1.175875602f, 178 | 1.0f, 0.785694958f, 0.541196100f, 0.275899379f 179 | }; 180 | 181 | i = 0; 182 | for (int row = 0; row < 8; row++) 183 | { 184 | for (int col = 0; col < 8; col++) 185 | { 186 | fdtbl_Y[i] = (1.0f / (YTable[ZigZag[i]] * aasf[row] * aasf[col] * 8.0f)); 187 | fdtbl_UV[i] = (1.0f / (UVTable[ZigZag[i]] * aasf[row] * aasf[col] * 8.0f)); 188 | i++; 189 | } 190 | } 191 | } 192 | 193 | private BitString[] YDC_HT; 194 | private BitString[] UVDC_HT; 195 | private BitString[] YAC_HT; 196 | private BitString[] UVAC_HT; 197 | 198 | private BitString[] computeHuffmanTbl(int[] nrcodes, int[] std_table) 199 | { 200 | int codevalue = 0; 201 | int pos_in_table = 0; 202 | BitString[] HT = new BitString[16 * 16]; 203 | for (int k = 1; k <= 16; k++) 204 | { 205 | for (int j = 1; j <= nrcodes[k]; j++) 206 | { 207 | HT[std_table[pos_in_table]] = new BitString(); 208 | HT[std_table[pos_in_table]].val = codevalue; 209 | HT[std_table[pos_in_table]].len = k; 210 | pos_in_table++; 211 | codevalue++; 212 | } 213 | codevalue *= 2; 214 | } 215 | return HT; 216 | } 217 | 218 | 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 }; 219 | private int[] std_dc_luminance_values = new int[12] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; 220 | 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, 0x7d }; 221 | private int[] std_ac_luminance_values = new int[162] { 222 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 223 | 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 224 | 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 225 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, 226 | 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 227 | 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 228 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 229 | 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 230 | 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 231 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 232 | 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 233 | 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 234 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 235 | 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, 236 | 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 237 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, 238 | 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 239 | 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 240 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 241 | 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 242 | 0xf9,0xfa 243 | }; 244 | 245 | 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 }; 246 | private int[] std_dc_chrominance_values = new int[12] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; 247 | 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, 0x77 }; 248 | private int[] std_ac_chrominance_values = new int[162] { 249 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 250 | 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 251 | 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 252 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, 253 | 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 254 | 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 255 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 256 | 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, 257 | 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 258 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, 259 | 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 260 | 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 261 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 262 | 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, 263 | 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 264 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, 265 | 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 266 | 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 267 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 268 | 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 269 | 0xf9,0xfa 270 | }; 271 | 272 | private void initHuffmanTbl() 273 | { 274 | YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes, std_dc_luminance_values); 275 | UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes, std_dc_chrominance_values); 276 | YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes, std_ac_luminance_values); 277 | UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes, std_ac_chrominance_values); 278 | } 279 | 280 | 281 | private BitString[] bitcode = new BitString[65535]; 282 | private int[] category = new int[65535]; 283 | 284 | private void initCategoryfloat() 285 | { 286 | int nrlower = 1; 287 | int nrupper = 2; 288 | int nr; 289 | BitString bs; 290 | 291 | for (int cat = 1; cat <= 15; cat++) 292 | { 293 | //Positive numbers 294 | for (nr = nrlower; nr < nrupper; nr++) 295 | { 296 | category[32767 + nr] = cat; 297 | 298 | bs = new BitString(); 299 | bs.len = cat; 300 | bs.val = nr; 301 | bitcode[32767 + nr] = bs; 302 | } 303 | //Negative numbers 304 | for (nr = -(nrupper - 1); nr <= -nrlower; nr++) 305 | { 306 | category[32767 + nr] = cat; 307 | 308 | bs = new BitString(); 309 | bs.len = cat; 310 | bs.val = nrupper - 1 + nr; 311 | bitcode[32767 + nr] = bs; 312 | } 313 | nrlower <<= 1; 314 | nrupper <<= 1; 315 | } 316 | } 317 | 318 | // IO functions 319 | private int bytenew = 0; 320 | private int bytepos = 7; 321 | public ByteArrayTool byteout = new ByteArrayTool(); 322 | 323 | /** 324 | * Get the result 325 | */ 326 | public byte[] GetBytes() 327 | { 328 | if (!isDone) 329 | { 330 | Debug.LogError("JPEGEncoder not complete, cannot get bytes!"); 331 | return new byte[1]; 332 | } 333 | 334 | return byteout.GetAllBytes(); 335 | } 336 | 337 | private void writeBits(BitString bs) 338 | { 339 | int value = bs.val; 340 | int posval = bs.len - 1; 341 | while (posval >= 0) 342 | { 343 | if (((uint)value & System.Convert.ToUInt32(1 << posval)) != 0) 344 | { 345 | bytenew |= (int)(System.Convert.ToUInt32(1 << bytepos)); 346 | } 347 | posval--; 348 | bytepos--; 349 | if (bytepos < 0) 350 | { 351 | if (bytenew == 0xFF) 352 | { 353 | writeByte(0xFF); 354 | writeByte(0); 355 | } 356 | else 357 | { 358 | writeByte((byte)bytenew); 359 | } 360 | bytepos = 7; 361 | bytenew = 0; 362 | } 363 | } 364 | } 365 | 366 | private void writeByte(byte value) 367 | { 368 | byteout.writeByte(value); 369 | } 370 | 371 | private void writeWord(int value) 372 | { 373 | writeByte((byte)((value >> 8) & 0xFF)); 374 | writeByte((byte)((value) & 0xFF)); 375 | } 376 | 377 | // DCT & quantization core 378 | 379 | private float[] fDCTQuant(float[] data, float[] fdtbl) 380 | { 381 | float tmp0; float tmp1; float tmp2; float tmp3; float tmp4; float tmp5; float tmp6; float tmp7; 382 | float tmp10; float tmp11; float tmp12; float tmp13; 383 | 384 | float z1; float z2; float z3; float z4; float z5; float z11; float z13; 385 | 386 | int i; 387 | 388 | /* Pass 1: process rows. */ 389 | int dataOff = 0; 390 | for (i = 0; i < 8; i++) 391 | { 392 | tmp0 = data[dataOff + 0] + data[dataOff + 7]; 393 | tmp7 = data[dataOff + 0] - data[dataOff + 7]; 394 | tmp1 = data[dataOff + 1] + data[dataOff + 6]; 395 | tmp6 = data[dataOff + 1] - data[dataOff + 6]; 396 | tmp2 = data[dataOff + 2] + data[dataOff + 5]; 397 | tmp5 = data[dataOff + 2] - data[dataOff + 5]; 398 | tmp3 = data[dataOff + 3] + data[dataOff + 4]; 399 | tmp4 = data[dataOff + 3] - data[dataOff + 4]; 400 | 401 | /* Even part */ 402 | tmp10 = tmp0 + tmp3; /* phase 2 */ 403 | tmp13 = tmp0 - tmp3; 404 | tmp11 = tmp1 + tmp2; 405 | tmp12 = tmp1 - tmp2; 406 | 407 | data[dataOff + 0] = tmp10 + tmp11; /* phase 3 */ 408 | data[dataOff + 4] = tmp10 - tmp11; 409 | 410 | z1 = (tmp12 + tmp13) * 0.707106781f; /* c4 */ 411 | data[dataOff + 2] = tmp13 + z1; /* phase 5 */ 412 | data[dataOff + 6] = tmp13 - z1; 413 | 414 | /* Odd part */ 415 | tmp10 = tmp4 + tmp5; /* phase 2 */ 416 | tmp11 = tmp5 + tmp6; 417 | tmp12 = tmp6 + tmp7; 418 | 419 | /* The rotator is modified from fig 4-8 to avoid extra negations. */ 420 | z5 = (tmp10 - tmp12) * 0.382683433f; /* c6 */ 421 | z2 = 0.541196100f * tmp10 + z5; /* c2-c6 */ 422 | z4 = 1.306562965f * tmp12 + z5; /* c2+c6 */ 423 | z3 = tmp11 * 0.707106781f; /* c4 */ 424 | 425 | z11 = tmp7 + z3; /* phase 5 */ 426 | z13 = tmp7 - z3; 427 | 428 | data[dataOff + 5] = z13 + z2; /* phase 6 */ 429 | data[dataOff + 3] = z13 - z2; 430 | data[dataOff + 1] = z11 + z4; 431 | data[dataOff + 7] = z11 - z4; 432 | 433 | dataOff += 8; /* advance pointer to next row */ 434 | } 435 | 436 | /* Pass 2: process columns. */ 437 | dataOff = 0; 438 | for (i = 0; i < 8; i++) 439 | { 440 | tmp0 = data[dataOff + 0] + data[dataOff + 56]; 441 | tmp7 = data[dataOff + 0] - data[dataOff + 56]; 442 | tmp1 = data[dataOff + 8] + data[dataOff + 48]; 443 | tmp6 = data[dataOff + 8] - data[dataOff + 48]; 444 | tmp2 = data[dataOff + 16] + data[dataOff + 40]; 445 | tmp5 = data[dataOff + 16] - data[dataOff + 40]; 446 | tmp3 = data[dataOff + 24] + data[dataOff + 32]; 447 | tmp4 = data[dataOff + 24] - data[dataOff + 32]; 448 | 449 | /* Even part */ 450 | tmp10 = tmp0 + tmp3; /* phase 2 */ 451 | tmp13 = tmp0 - tmp3; 452 | tmp11 = tmp1 + tmp2; 453 | tmp12 = tmp1 - tmp2; 454 | 455 | data[dataOff + 0] = tmp10 + tmp11; /* phase 3 */ 456 | data[dataOff + 32] = tmp10 - tmp11; 457 | 458 | z1 = (tmp12 + tmp13) * 0.707106781f; /* c4 */ 459 | data[dataOff + 16] = tmp13 + z1; /* phase 5 */ 460 | data[dataOff + 48] = tmp13 - z1; 461 | 462 | /* Odd part */ 463 | tmp10 = tmp4 + tmp5; /* phase 2 */ 464 | tmp11 = tmp5 + tmp6; 465 | tmp12 = tmp6 + tmp7; 466 | 467 | /* The rotator is modified from fig 4-8 to avoid extra negations. */ 468 | z5 = (tmp10 - tmp12) * 0.382683433f; /* c6 */ 469 | z2 = 0.541196100f * tmp10 + z5; /* c2-c6 */ 470 | z4 = 1.306562965f * tmp12 + z5; /* c2+c6 */ 471 | z3 = tmp11 * 0.707106781f; /* c4 */ 472 | 473 | z11 = tmp7 + z3; /* phase 5 */ 474 | z13 = tmp7 - z3; 475 | 476 | data[dataOff + 40] = z13 + z2; /* phase 6 */ 477 | data[dataOff + 24] = z13 - z2; 478 | data[dataOff + 8] = z11 + z4; 479 | data[dataOff + 56] = z11 - z4; 480 | 481 | dataOff++; /* advance pointer to next column */ 482 | } 483 | 484 | // Quantize/descale the coefficients 485 | for (i = 0; i < 64; i++) 486 | { 487 | // Apply the quantization and scaling factor & Round to nearest integer 488 | data[i] = Mathf.Round((data[i] * fdtbl[i])); 489 | } 490 | return data; 491 | } 492 | 493 | // Chunk writing 494 | 495 | private void writeAPP0() 496 | { 497 | writeWord(0xFFE0); // marker 498 | writeWord(16); // length 499 | writeByte(0x4A); // J 500 | writeByte(0x46); // F 501 | writeByte(0x49); // I 502 | writeByte(0x46); // F 503 | writeByte(0); // = "JFIF",'\0' 504 | writeByte(1); // versionhi 505 | writeByte(1); // versionlo 506 | writeByte(0); // xyunits 507 | writeWord(1); // xdensity 508 | writeWord(1); // ydensity 509 | writeByte(0); // thumbnwidth 510 | writeByte(0); // thumbnheight 511 | } 512 | 513 | private void writeSOF0(int width, int height) 514 | { 515 | writeWord(0xFFC0); // marker 516 | writeWord(17); // length, truecolor YUV JPG 517 | writeByte(8); // precision 518 | writeWord(height); 519 | writeWord(width); 520 | writeByte(3); // nrofcomponents 521 | writeByte(1); // IdY 522 | writeByte(0x11); // HVY 523 | writeByte(0); // QTY 524 | writeByte(2); // IdU 525 | writeByte(0x11); // HVU 526 | writeByte(1); // QTU 527 | writeByte(3); // IdV 528 | writeByte(0x11); // HVV 529 | writeByte(1); // QTV 530 | } 531 | 532 | private void writeDQT() 533 | { 534 | writeWord(0xFFDB); // marker 535 | writeWord(132); // length 536 | writeByte(0); 537 | int i; 538 | for (i = 0; i < 64; i++) 539 | { 540 | writeByte((byte)(YTable[i])); 541 | } 542 | writeByte(1); 543 | for (i = 0; i < 64; i++) 544 | { 545 | writeByte((byte)(UVTable[i])); 546 | } 547 | } 548 | 549 | private void writeDHT() 550 | { 551 | writeWord(0xFFC4); // marker 552 | writeWord(0x01A2); // length 553 | int i; 554 | 555 | writeByte(0); // HTYDCinfo 556 | for (i = 0; i < 16; i++) 557 | { 558 | writeByte((byte)(std_dc_luminance_nrcodes[i + 1])); 559 | } 560 | for (i = 0; i <= 11; i++) 561 | { 562 | writeByte((byte)(std_dc_luminance_values[i])); 563 | } 564 | 565 | writeByte(0x10); // HTYACinfo 566 | for (i = 0; i < 16; i++) 567 | { 568 | writeByte((byte)(std_ac_luminance_nrcodes[i + 1])); 569 | } 570 | for (i = 0; i <= 161; i++) 571 | { 572 | writeByte((byte)(std_ac_luminance_values[i])); 573 | } 574 | 575 | writeByte(1); // HTUDCinfo 576 | for (i = 0; i < 16; i++) 577 | { 578 | writeByte((byte)(std_dc_chrominance_nrcodes[i + 1])); 579 | } 580 | for (i = 0; i <= 11; i++) 581 | { 582 | writeByte((byte)(std_dc_chrominance_values[i])); 583 | } 584 | 585 | writeByte(0x11); // HTUACinfo 586 | for (i = 0; i < 16; i++) 587 | { 588 | writeByte((byte)(std_ac_chrominance_nrcodes[i + 1])); 589 | } 590 | for (i = 0; i <= 161; i++) 591 | { 592 | writeByte((byte)(std_ac_chrominance_values[i])); 593 | } 594 | } 595 | 596 | private void writeSOS() 597 | { 598 | writeWord(0xFFDA); // marker 599 | writeWord(12); // length 600 | writeByte(3); // nrofcomponents 601 | writeByte(1); // IdY 602 | writeByte(0); // HTY 603 | writeByte(2); // IdU 604 | writeByte(0x11); // HTU 605 | writeByte(3); // IdV 606 | writeByte(0x11); // HTV 607 | writeByte(0); // Ss 608 | writeByte(0x3f); // Se 609 | writeByte(0); // Bf 610 | } 611 | 612 | // Core processing 613 | private int[] DU = new int[64]; 614 | 615 | private float processDU(float[] CDU, float[] fdtbl, float DC, BitString[] HTDC, BitString[] HTAC) 616 | { 617 | BitString EOB = HTAC[0x00]; 618 | BitString M16zeroes = HTAC[0xF0]; 619 | int i; 620 | 621 | float[] DU_DCT = fDCTQuant(CDU, fdtbl); 622 | 623 | //ZigZag reorder 624 | for (i = 0; i < 64; i++) 625 | { 626 | DU[ZigZag[i]] = (int)(DU_DCT[i]); 627 | } 628 | int Diff = (int)(DU[0] - DC); 629 | DC = DU[0]; 630 | 631 | //Encode DC 632 | if (Diff == 0) 633 | { 634 | writeBits(HTDC[0]); // Diff might be 0 635 | } 636 | else 637 | { 638 | writeBits(HTDC[category[32767 + Diff]]); 639 | writeBits(bitcode[32767 + Diff]); 640 | } 641 | //Encode ACs 642 | int end0pos = 63; 643 | for (; (end0pos > 0) && (DU[end0pos] == 0); end0pos--) 644 | { 645 | }; 646 | //end0pos = first element in reverse order !=0 647 | if (end0pos == 0) 648 | { 649 | writeBits(EOB); 650 | return DC; 651 | } 652 | i = 1; 653 | while (i <= end0pos) 654 | { 655 | int startpos = i; 656 | for (; (DU[i] == 0) && (i <= end0pos); i++) 657 | { 658 | } 659 | int nrzeroes = i - startpos; 660 | if (nrzeroes >= 16) 661 | { 662 | for (int nrmarker = 1; nrmarker <= nrzeroes / 16; nrmarker++) 663 | { 664 | writeBits(M16zeroes); 665 | } 666 | nrzeroes = (nrzeroes & 0xF); 667 | } 668 | writeBits(HTAC[nrzeroes * 16 + category[32767 + DU[i]]]); 669 | writeBits(bitcode[32767 + DU[i]]); 670 | i++; 671 | } 672 | if (end0pos != 63) 673 | { 674 | writeBits(EOB); 675 | } 676 | return DC; 677 | } 678 | 679 | private float[] YDU = new float[64]; 680 | private float[] UDU = new float[64]; 681 | private float[] VDU = new float[64]; 682 | 683 | private void RGB2YUV(BitmapData img, int xpos, int ypos) 684 | { 685 | int pos = 0; 686 | for (int y = 0; y < 8; y++) 687 | { 688 | for (int x = 0; x < 8; x++) 689 | { 690 | Color C = img.getPixelColor(xpos + x, img.height - (ypos + y)); 691 | float R = C.r * 255.0f; 692 | float G = C.g * 255.0f; 693 | float B = C.b * 255.0f; 694 | YDU[pos] = (((0.29900f) * R + (0.58700f) * G + (0.11400f) * B)) - 128.0f; 695 | UDU[pos] = (((-0.16874f) * R + (-0.33126f) * G + (0.50000f) * B)); 696 | VDU[pos] = (((0.50000f) * R + (-0.41869f) * G + (-0.08131f) * B)); 697 | pos++; 698 | } 699 | } 700 | } 701 | 702 | /** 703 | * Constructor for JPEGEncoder class 704 | * 705 | * @param quality The quality level between 1 and 100 that detrmines the 706 | * level of compression used in the generated JPEG 707 | * @langversion ActionScript 3.0 708 | * @playerversion Flash 9.0 709 | * @tiptext 710 | */ 711 | 712 | // public flag--other scripts must watch this to know when they can safely get data out 713 | public bool isDone = false; 714 | private BitmapData image; 715 | private int sf = 0; 716 | 717 | public JPGEncoder(Color[] pixels, int width, int height, float quality) 718 | { 719 | // save out texture data to our own data structure 720 | image = new BitmapData(pixels, width, height); 721 | 722 | if (quality <= 0.0f) 723 | quality = 1.0f; 724 | 725 | if (quality > 100.0f) 726 | quality = 100.0f; 727 | 728 | if (quality < 50.0f) 729 | sf = (int)(5000.0f / quality); 730 | else 731 | sf = (int)(200.0f - quality * 2.0f); 732 | } 733 | 734 | public JPGEncoder(Texture2D texture, float quality) 735 | { 736 | // save out texture data to our own data structure 737 | image = new BitmapData(texture); 738 | 739 | if (quality <= 0.0f) 740 | quality = 1.0f; 741 | 742 | if (quality > 100.0f) 743 | quality = 100.0f; 744 | 745 | if (quality < 50.0f) 746 | sf = (int)(5000.0f / quality); 747 | else 748 | sf = (int)(200.0f - quality * 2.0f); 749 | } 750 | 751 | 752 | 753 | /** 754 | * Handle our initialization and encoding 755 | */ 756 | public void doEncoding() 757 | { 758 | isDone = false; 759 | 760 | // Create tables -- technically we could only do this once for multiple encodes 761 | initHuffmanTbl(); 762 | initCategoryfloat(); 763 | initQuantTables(sf); 764 | 765 | // Do actual encoding 766 | encode(); 767 | 768 | // signal that our data is ok to use now 769 | isDone = true; 770 | 771 | // tell the thread to stop--not sure if this is actually needed 772 | image = null; 773 | } 774 | 775 | /** 776 | * Created a JPEG image from the specified BitmapData 777 | * 778 | * @param image The BitmapData that will be converted into the JPEG format. 779 | * @return a ByteArrayTool representing the JPEG encoded image data. 780 | * @langversion ActionScript 3.0 781 | * @playerversion Flash 9.0 782 | * @tiptext 783 | */ 784 | private void encode() 785 | { 786 | // Initialize bit writer 787 | byteout = new ByteArrayTool(); 788 | bytenew = 0; 789 | bytepos = 7; 790 | 791 | // Add JPEG headers 792 | writeWord(0xFFD8); // SOI 793 | writeAPP0(); 794 | writeDQT(); 795 | writeSOF0(image.width, image.height); 796 | writeDHT(); 797 | writeSOS(); 798 | 799 | // Encode 8x8 macroblocks 800 | float DCY = 0.0f; 801 | float DCU = 0.0f; 802 | float DCV = 0.0f; 803 | bytenew = 0; 804 | bytepos = 7; 805 | for (int ypos = 0; ypos < image.height; ypos += 8) 806 | { 807 | for (int xpos = 0; xpos < image.width; xpos += 8) 808 | { 809 | RGB2YUV(image, xpos, ypos); 810 | DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); 811 | DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 812 | DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 813 | 814 | // let other threads do stuff too 815 | //Thread.Sleep(0); 816 | } 817 | } 818 | 819 | // Do the bit alignment of the EOI marker 820 | if (bytepos >= 0) 821 | { 822 | BitString fillbits = new BitString(); 823 | fillbits.len = bytepos + 1; 824 | fillbits.val = (1 << (bytepos + 1)) - 1; 825 | writeBits(fillbits); 826 | } 827 | 828 | writeWord(0xFFD9); //EOI 829 | //return byteout; 830 | isDone = true; 831 | } 832 | } 833 | 834 | /* 835 | * Ported to UnityScript by Matthew Wegner, Flashbang Studios 836 | * 837 | * Original code is from as3corelib, found here: 838 | * http://code.google.com/p/as3corelib/source/browse/trunk/src/com/adobe/images/JPGEncoder.as 839 | * 840 | * Original copyright notice is below: 841 | */ 842 | 843 | /* 844 | * Ported to C# by Tony McBride 845 | * 846 | * C# version isnt threaded so just call like this: 847 | * JPGEncoder NewEncoder = new JPGEncoder( MyTexture , 75.0f ); 848 | * NewEncoder.doEncoding(); 849 | * byte[] TexData = NewEncoder.GetBytes(); 850 | */ 851 | #endif 852 | 853 | -------------------------------------------------------------------------------- /Assets/JPEGEncoder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 85d2f3d5db3c6df4fa21e4a6bd212968 3 | timeCreated: 1534926757 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/StreamingAssets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3863f1e23e99fb74b9db29a22809d118 3 | folderAsset: yes 4 | timeCreated: 1534925481 5 | licenseType: Free 6 | DefaultImporter: 7 | externalObjects: {} 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/StreamingAssets/log.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/U3DC/Unity_UpLoader/b1097461944cd142d42cee96002f3914166288ee/Assets/StreamingAssets/log.txt -------------------------------------------------------------------------------- /Assets/StreamingAssets/log.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ef7157620d65b464e919d48721dc393b 3 | timeCreated: 1534925499 4 | licenseType: Free 5 | DefaultImporter: 6 | externalObjects: {} 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/UpLoaderDemo.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.IO; 4 | using System.Runtime.Serialization.Formatters.Binary; 5 | 6 | namespace U3DC.Extents 7 | { 8 | public class UpLoaderDemo : MonoBehaviour 9 | { 10 | private Camera CutFrameCamer; 11 | private Rect _canvas; 12 | private readonly string m_URL = "http://www.u3dc.com/appupload/upload.php"; 13 | 14 | private void Awake() 15 | { 16 | CutFrameCamer = Camera.main; 17 | } 18 | 19 | private string GetInfo() 20 | { 21 | 22 | var systemInfo = "\tTitle:Current System Back Info:\nDeviceModel:" + SystemInfo.deviceModel + "\nDeviceName:" + SystemInfo.deviceName + "\nDeviceType:" + SystemInfo.deviceType + 23 | "\nDeviceUniqueIdentifier:" + SystemInfo.deviceUniqueIdentifier + "\nGraphicsDeviceID:" + SystemInfo.graphicsDeviceID + 24 | "\nGraphicsDeviceName:" + SystemInfo.graphicsDeviceName + "\nGraphicsDeviceVendor:" + SystemInfo.graphicsDeviceVendor + 25 | "\nGraphicsDeviceVendorID:" + SystemInfo.graphicsDeviceVendorID + "\nGraphicsDeviceVersion:" + SystemInfo.graphicsDeviceVersion + 26 | "\nGraphicsMemorySize(M):" + SystemInfo.graphicsMemorySize + 27 | "\nGraphicsShaderLevel:" + SystemInfo.graphicsShaderLevel + "\nMaxTextureSize:" + SystemInfo.maxTextureSize + 28 | "\nnpotSupport:" + SystemInfo.npotSupport + "\nOperatingSystem:" + SystemInfo.operatingSystem + 29 | "\nProcessorCount:" + SystemInfo.processorCount + "\nProcessorType:" + SystemInfo.processorType + 30 | "\nsupportedRenderTargetCount:" + SystemInfo.supportedRenderTargetCount + "\nsupports3DTextures:" + SystemInfo.supports3DTextures + 31 | "\nsupportsAccelerometer:" + SystemInfo.supportsAccelerometer + "\nsupportsComputeShaders:" + SystemInfo.supportsComputeShaders + 32 | "\nsupportsGyroscope:" + SystemInfo.supportsGyroscope + "\nsupportsImageEffects:" + SystemInfo.supportsImageEffects + 33 | "\nsupportsInstancing:" + SystemInfo.supportsInstancing + "\nsupportsLocationService:" + SystemInfo.supportsLocationService + 34 | "\nsupportsRenderToCubemap:" + SystemInfo.supportsRenderToCubemap + 35 | "\nsupportsShadows:" + SystemInfo.supportsShadows + "\nsupportsSparseTextures:" + SystemInfo.supportsSparseTextures + 36 | "\nsupportsVibration:" + SystemInfo.supportsVibration + "\nSystemMemorySize:" + SystemInfo.systemMemorySize; 37 | return systemInfo; 38 | 39 | } 40 | 41 | 42 | 43 | public void UpLoadLogInfo() 44 | { 45 | SaveStringToBinary(GetInfo()); 46 | StartCoroutine(UploadLogFile(m_URL)); 47 | } 48 | 49 | public void UpLoadSreenShot() 50 | { 51 | _canvas.Set(0, 0, Screen.width, Screen.height); //设置画布大小等于当前屏幕的宽和高。 52 | CaptureScreen(CutFrameCamer, _canvas); 53 | 54 | } 55 | 56 | 57 | /// 58 | /// 截图上传到服务器 59 | /// 60 | /// 61 | /// 62 | /// 63 | public void CaptureScreen(Camera c, Rect r) 64 | { 65 | //捕抓摄像机图像并转换成字符数组 66 | var rt = new RenderTexture((int)r.width, (int)r.height, 0); 67 | c.targetTexture = rt; 68 | c.Render(); 69 | 70 | RenderTexture.active = rt; 71 | var screenShot = new Texture2D((int)r.width, (int)r.height, TextureFormat.RGB24, false); 72 | screenShot.ReadPixels(r, 0, 0); 73 | screenShot.Apply(); 74 | 75 | c.targetTexture = null; 76 | RenderTexture.active = null; 77 | GameObject.Destroy(rt); 78 | 79 | StartCoroutine(UploadTexture(screenShot, m_URL)); 80 | 81 | } 82 | 83 | private IEnumerator UploadTexture(Texture2D screenShot, string Url) 84 | { 85 | //var encoder = new JPGEncoder(screenShot, 20);//质量1~100 86 | //encoder.doEncoding(); 87 | //while (!encoder.isDone) 88 | // yield return null; 89 | //var bytes = encoder.GetBytes(); 90 | 91 | var bytes = screenShot.EncodeToJPG(); 92 | var form = new WWWForm(); 93 | // form.AddBinaryData("file", bytes); //把图片流上传 94 | form.AddBinaryData("file", bytes, "screenShot.jpg", "image/jpeg"); 95 | var www = new WWW(Url, form); 96 | yield return www; 97 | if (www.error == null) 98 | Debug.Log("upload done :" + www.text); 99 | else 100 | Debug.Log("Error during upload: " + www.error); 101 | StartCoroutine(PostData(www)); //启动子线程 102 | Destroy(screenShot); //销毁 103 | } 104 | 105 | /// 106 | /// 上传log到服务器 107 | /// 108 | /// 109 | private void SaveStringToBinary(string str) 110 | { 111 | //序列化过程(将Save对象转换为字节流) 112 | //创建Save对象并保存当前游戏状态 113 | //创建一个二进制格式化程序 114 | var bf = new BinaryFormatter(); 115 | //创建一个文件流 116 | var fileStream = File.Create(Application.streamingAssetsPath + "/log.txt"); 117 | //用二进制格式化程序的序列化方法来序列化Save对象,参数:创建的文件流和需要序列化的对象 118 | bf.Serialize(fileStream, str); 119 | //关闭流 120 | fileStream.Close(); 121 | 122 | //如果文件存在,则显示保存成功 123 | if (File.Exists(Application.streamingAssetsPath + "/log.txt")) 124 | { 125 | Debug.Log("保存成功~"); 126 | } 127 | } 128 | 129 | IEnumerator UploadLogFile(string url) 130 | { 131 | var localFile = new WWW(Application.streamingAssetsPath + "/log.txt"); 132 | yield return localFile; 133 | if (localFile.error == null) 134 | Debug.Log("Loaded file successfully"); 135 | else 136 | { 137 | Debug.Log("Open file error: " + localFile.error); 138 | yield break; // stop the coroutine here 139 | } 140 | var postForm = new WWWForm(); 141 | // version 1 上传一个二进制文件 .dat 142 | postForm.AddBinaryData("file", localFile.bytes); 143 | // version 2 144 | //上传一个原格式的文件 源文件后缀 145 | //postForm.AddBinaryData("file", localFile.bytes,"log.txt", "text/plain"); 146 | var upload = new WWW(url, postForm); 147 | yield return upload; 148 | if (upload.error == null) 149 | Debug.Log("upload done :" + upload.text); 150 | else 151 | Debug.Log("Error during upload: " + upload.error); 152 | } 153 | 154 | IEnumerator PostData(WWW www) 155 | { 156 | yield return www; 157 | Debug.Log(www.text); //输出服务器返回结果。 158 | } 159 | 160 | 161 | } 162 | } -------------------------------------------------------------------------------- /Assets/UpLoaderDemo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e086337a30a074596b9c2444a10fc048 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/demo.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/U3DC/Unity_UpLoader/b1097461944cd142d42cee96002f3914166288ee/Assets/demo.unity -------------------------------------------------------------------------------- /Assets/demo.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38fada7f556a1314ebf5a50a77f49278 3 | timeCreated: 1534925859 4 | licenseType: Free 5 | DefaultImporter: 6 | externalObjects: {} 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /PHP/upload.php: -------------------------------------------------------------------------------- 1 | 0) 18 | { 19 | echo "错误:: " . $_FILES["file"]["error"] . "
"; 20 | } 21 | else 22 | { 23 | echo "上传文件名: " . $_FILES["file"]["name"] . "
"; 24 | echo "文件类型: " . $_FILES["file"]["type"] . "
"; 25 | echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB
"; 26 | echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "
"; 27 | 28 | 29 | //移动上传文件到uploads目录 30 | if(file_exists($newname )) 31 | {echo "
您上传文件的已经在本地保存过";} 32 | else 33 | { 34 | //文件以时间戳 重命名 35 | date_default_timezone_set('PRC');//中国时区 36 | $D=date("YmdHis");//时间戳 37 | $filetype=substr(strrchr($_FILES['file']['name'],"."),1);//获取文件后缀名 38 | $newname=$D.".".$filetype; //文件新名字 39 | //echo "
新文件名:".$newname; 40 | 41 | move_uploaded_file($_FILES['file']['tmp_name'], "upload/".$newname);//移动文件到指定文件夹uploads; 42 | echo "
上传文件本地保存路径:"."./upload/".$newname; 43 | 44 | } 45 | 46 | /* // 判断当期目录下的 upload 目录是否存在该文件 47 | // 如果没有 upload 目录,你需要创建它,upload 目录权限为 777 48 | if (file_exists("upload/" . $_FILES["file"]["name"])) 49 | { 50 | echo $_FILES["file"]["name"] . " 文件已经存在。 "; 51 | } 52 | else 53 | { 54 | // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下 55 | move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); 56 | echo "文件存储在: " . "upload/" . $_FILES["file"]["name"]; 57 | } */ 58 | } 59 | /* } 60 | else 61 | { 62 | echo "非法的文件格式"; 63 | } */ 64 | ?> -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity_UpLoader 2 | 3 | 4 | **En:** 5 | 6 | A solution for UpLoad data(TXT,ScreenShot) to server,Contact with PHP. 7 | 8 | 9 | How to use it: 10 | 11 | 1. put the 'upload.php' page on your webside or server. 12 | 2. change the demo url for your url. 13 | 3. SaveStringToBinary(string str),use this funtion for convert string to a file. 14 | 4. CaptureScreen(),use this funtion for take a screenShot. 15 | 5. UploadLogFile(),use this funtion for upload file. 16 | 6. UploadTexture(),use this funtion for upload screenShot. 17 | 7. JPGEncoder is a class for compress pic 18 | 8. enjoy it! 19 | 20 | **CH:** 21 | 22 | 23 | 数据上传解决方案,比如上传log信息,上传屏幕截图,PHP后端交互存储文件 24 | 25 | 如何使用: 26 | 27 | 1. 上传upload.php页面到服务端。 28 | 2. 在demo中更改url即可直接体验到上传效果。 29 | 3. 使用SaveStringToBinary函数来将string字符串转为二进制文件。 30 | 4. CaptureScreen函数用来截图。 31 | 5. UploadLogFile用来上传日志。 32 | 6. UploadTexture用来上传贴图。 33 | 7. JPGEncoder是一个用于压缩图片的类 34 | 8. 动手试一下呗~ 35 | 36 | 37 | 38 | 39 | 40 | --------------------------------------------------------------------------------