Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1399 lines
44 KiB

  1. /*----------------------------------------------------------------------+
  2. | compress.c - Microsoft Video 1 Compressor - compress code |
  3. | |
  4. | |
  5. | Copyright (c) 1990-1994 Microsoft Corporation. |
  6. | Portions Copyright Media Vision Inc. |
  7. | All Rights Reserved. |
  8. | |
  9. | You have a non-exclusive, worldwide, royalty-free, and perpetual |
  10. | license to use this source code in developing hardware, software |
  11. | (limited to drivers and other software required for hardware |
  12. | functionality), and firmware for video display and/or processing |
  13. | boards. Microsoft makes no warranties, express or implied, with |
  14. | respect to the Video 1 codec, including without limitation warranties |
  15. | of merchantability or fitness for a particular purpose. Microsoft |
  16. | shall not be liable for any damages whatsoever, including without |
  17. | limitation consequential damages arising from your use of the Video 1 |
  18. | codec. |
  19. | |
  20. | |
  21. +----------------------------------------------------------------------*/
  22. #include <windows.h>
  23. #include <windowsx.h>
  24. #include <mmsystem.h> // for timeGetTime()
  25. #include <win32.h>
  26. #include "msvidc.h"
  27. #include <memory.h> // for _fmemcmp
  28. //#include <limits.h>
  29. //#include <aviffmt.h>
  30. DWORD numberOfBlocks;
  31. DWORD numberOfSolids;
  32. DWORD numberOfSolid4;
  33. DWORD numberOfSolid2;
  34. DWORD numberOfEdges;
  35. DWORD numberOfSkips;
  36. DWORD numberOfExtraSkips;
  37. DWORD numberOfSkipCodes;
  38. DWORD numberOfEvilRed;
  39. /*******************************************************************
  40. *******************************************************************/
  41. #define SWAP(x,y) ( (x)^=(y), (y)^=(x), (x)^=(y) )
  42. #define SWAPRGB(x,y)( SWAP((x).rgbRed, (y).rgbRed), \
  43. SWAP((x).rgbGreen, (y).rgbGreen),\
  44. SWAP((x).rgbBlue, (y).rgbBlue) )
  45. // Take an RGB quad value and figure out it's lumanence
  46. // Y = 0.3*R + 0.59*G + 0.11*B
  47. #define RgbToY(rgb) ((((WORD)((rgb).rgbRed) * 30) + \
  48. ((WORD)((rgb).rgbGreen)* 59) + \
  49. ((WORD)((rgb).rgbBlue) * 11))/100)
  50. #define RGB16(r,g,b) ((((WORD)(r) >> 3) << 10) | \
  51. (((WORD)(g) >> 3) << 5) | \
  52. (((WORD)(b) >> 3) << 0) )
  53. #define RGBQ16(rgb) RGB16((rgb).rgbRed,(rgb).rgbGreen,(rgb).rgbBlue)
  54. // this array is used to associate each of the 16 luminance values
  55. // with one of the sum values
  56. BYTE meanIndex[16] = { 0, 0, 1, 1,
  57. 0, 0, 1, 1,
  58. 2, 2, 3, 3,
  59. 2, 2, 3, 3 };
  60. /*****************************************************************************
  61. ****************************************************************************/
  62. //
  63. // map Quality into our threshold
  64. //
  65. // Quality goes from ICQUALITY_LOW-ICQUALITY_HIGH (bad to good)
  66. //
  67. // threshold = (Quality/ICQUALITY_HIGH)^THRESHOLD_POW * THRESHOLD_HIGH
  68. //
  69. DWORD FAR QualityToThreshold(DWORD dwQuality)
  70. {
  71. #define THRESHOLD_HIGH ((256*256l)/2)
  72. #define THRESHOLD_POW 4
  73. double dw1;
  74. dw1 = (double)(dwQuality) / ICQUALITY_HIGH;
  75. // unbelievably enough, pow() doesn't work on alpha or mips!
  76. // also I can't believe this will be less efficient than pow(x, 4)
  77. dw1 = (dw1 * dw1 * dw1 * dw1);
  78. return (DWORD) (dw1 * THRESHOLD_HIGH);
  79. //return (DWORD)(pow((double)(dwQuality)/ICQUALITY_HIGH,THRESHOLD_POW) * THRESHOLD_HIGH);
  80. }
  81. /*******************************************************************
  82. *******************************************************************/
  83. //
  84. // table to map a 5bit index (0-31) to a 8 bit value (0-255)
  85. //
  86. static BYTE aw5to8[32] = {(BYTE)-1};
  87. //
  88. // inverse table to map a RGB16 to a 8bit pixel
  89. //
  90. #define MAPRGB16(rgb16) lpITable[(rgb16)]
  91. #define MAPRGB(rgb) lpITable[RGBQ16(rgb)]
  92. /*******************************************************************
  93. *******************************************************************/
  94. DWORD FAR CompressFrameBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut,
  95. LPBYTE *lplpITable, RGBQUAD *prgbqOut)
  96. {
  97. int i;
  98. //
  99. // init the 5bit to 8bit conversion table.
  100. //
  101. if (aw5to8[0] != 0)
  102. for (i=0; i<32; i++)
  103. aw5to8[i] = (BYTE)(i * 255 / 31);
  104. //
  105. // copy the color table to local storage
  106. //
  107. if (lpbiIn->biBitCount == 8)
  108. {
  109. if (lpbiIn->biClrUsed == 0)
  110. lpbiIn->biClrUsed = 256;
  111. }
  112. //
  113. // if we are compressing to 8bit, then build a inverse table
  114. //
  115. if (lpbiOut->biBitCount == 8)
  116. {
  117. if (lpbiOut->biClrUsed == 0)
  118. lpbiOut->biClrUsed = 256;
  119. if (_fmemcmp((LPVOID)prgbqOut, (LPVOID)(lpbiOut+1),
  120. (int)lpbiOut->biClrUsed * sizeof(RGBQUAD)))
  121. {
  122. for (i=0; i<(int)lpbiOut->biClrUsed; i++)
  123. prgbqOut[i] = ((LPRGBQUAD)(lpbiOut+1))[i];
  124. if (*lplpITable)
  125. GlobalFreePtr(*lplpITable);
  126. *lplpITable = NULL;
  127. }
  128. if (*lplpITable == NULL)
  129. {
  130. // !!! Need a critical section around this code!
  131. DPF(("Building ITable.... (%d colors)", (int)lpbiOut->biClrUsed));
  132. *lplpITable = MakeITable(prgbqOut, (int)lpbiOut->biClrUsed);
  133. // !!! Critical section can end here....
  134. }
  135. if (*lplpITable == NULL)
  136. return (DWORD)ICERR_MEMORY;
  137. }
  138. return ICERR_OK;
  139. }
  140. /*******************************************************************
  141. *******************************************************************/
  142. DWORD FAR CompressFrameEnd(LPBYTE *lplpITable)
  143. {
  144. if (*lplpITable)
  145. GlobalFreePtr(*lplpITable);
  146. *lplpITable = NULL;
  147. return ICERR_OK;
  148. }
  149. /*******************************************************************
  150. *******************************************************************/
  151. void FAR CompressFrameFree(void)
  152. {
  153. }
  154. /*******************************************************************
  155. GetCell - get a 4x4 cell from a image.
  156. returns pointer to next cell.
  157. *******************************************************************/
  158. static LPVOID _FASTCALL
  159. GetCell(LPBITMAPINFOHEADER lpbi, LPVOID lpBits, PCELL pCell)
  160. {
  161. UINT WidthBytes;
  162. int bits;
  163. int i;
  164. int x;
  165. int y;
  166. BYTE b;
  167. HPBYTE pb;
  168. RGBQUAD FAR *prgbqIn;
  169. RGB555 rgb555;
  170. pb = lpBits;
  171. bits = (int)lpbi->biBitCount;
  172. WidthBytes = DIBWIDTHBYTES(*lpbi);
  173. WidthBytes-= (WIDTH_CBLOCK * bits/8);
  174. ((HPBYTE)lpBits) += (WIDTH_CBLOCK * bits/8); // "next" cell
  175. i = 0;
  176. switch (bits)
  177. {
  178. case 8:
  179. prgbqIn = (RGBQUAD FAR *) (lpbi + 1);
  180. for( y = 0; y < HEIGHT_CBLOCK; y++ )
  181. {
  182. for( x = 0; x < WIDTH_CBLOCK; x++ )
  183. {
  184. b = *pb++;
  185. pCell[i++] = prgbqIn[b];
  186. }
  187. pb += WidthBytes; // next row in this block
  188. }
  189. break;
  190. case 16:
  191. for( y = 0; y < HEIGHT_CBLOCK; y++ )
  192. {
  193. for( x = 0; x < WIDTH_CBLOCK; x++ )
  194. {
  195. rgb555 = *((HPRGB555)pb)++;
  196. pCell[i].rgbRed = aw5to8[(rgb555 >> 10) & 0x1F];
  197. pCell[i].rgbGreen = aw5to8[(rgb555 >> 5) & 0x1F];
  198. pCell[i].rgbBlue = aw5to8[(rgb555 >> 0) & 0x1F];
  199. i++;
  200. }
  201. pb += WidthBytes; // next row in this block
  202. }
  203. break;
  204. case 24:
  205. for( y = 0; y < HEIGHT_CBLOCK; y++ )
  206. {
  207. for( x = 0; x < WIDTH_CBLOCK; x++ )
  208. {
  209. pCell[i].rgbBlue = *pb++;
  210. pCell[i].rgbGreen = *pb++;
  211. pCell[i].rgbRed = *pb++;
  212. i++;
  213. }
  214. pb += WidthBytes; // next row in this block
  215. }
  216. break;
  217. case 32:
  218. for( y = 0; y < HEIGHT_CBLOCK; y++ )
  219. {
  220. for( x = 0; x < WIDTH_CBLOCK; x++ )
  221. {
  222. pCell[i].rgbBlue = *pb++;
  223. pCell[i].rgbGreen = *pb++;
  224. pCell[i].rgbRed = *pb++;
  225. pb++;
  226. i++;
  227. }
  228. pb += WidthBytes; // next row in this block
  229. }
  230. break;
  231. }
  232. //
  233. // return the pointer to the "next" cell
  234. //
  235. return lpBits;
  236. }
  237. /*******************************************************************
  238. CmpCell - compares two 4x4 cells and returns an error value.
  239. the error value is a sum of squares error.
  240. the error value ranges from
  241. 0 = exact
  242. 3*256^2 = way off
  243. *******************************************************************/
  244. static DWORD _FASTCALL
  245. CmpCell(PCELL cellA, PCELL cellB)
  246. {
  247. #if 0
  248. int i;
  249. long l;
  250. int dr,dg,db;
  251. for (l=0,i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
  252. {
  253. dr = (int)cellA[i].rgbRed - (int)cellB[i].rgbRed;
  254. dg = (int)cellA[i].rgbGreen - (int)cellB[i].rgbGreen;
  255. db = (int)cellA[i].rgbBlue - (int)cellB[i].rgbBlue;
  256. l += ((long)dr * dr) + ((long)dg * dg) + ((long)db * db);
  257. }
  258. return l / (HEIGHT_CBLOCK*WIDTH_CBLOCK);
  259. #else
  260. int i;
  261. DWORD dw;
  262. //
  263. //
  264. #define SUMSQ(a,b) \
  265. if (a > b) \
  266. dw += (UINT)(a-b) * (UINT)(a-b); \
  267. else \
  268. dw += (UINT)(b-a) * (UINT)(b-a);
  269. for (dw=0,i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
  270. {
  271. SUMSQ(cellA[i].rgbRed, cellB[i].rgbRed);
  272. SUMSQ(cellA[i].rgbGreen, cellB[i].rgbGreen);
  273. SUMSQ(cellA[i].rgbBlue, cellB[i].rgbBlue);
  274. }
  275. return dw / (HEIGHT_CBLOCK*WIDTH_CBLOCK);
  276. #endif
  277. }
  278. #if 0 // This routine is unused
  279. /*******************************************************************
  280. MapCell - map a CELL full of 24bit values down to thier nearest
  281. colors in the 8bit palette
  282. *******************************************************************/
  283. static void _FASTCALL
  284. MapCell(PCELL pCell)
  285. {
  286. int i;
  287. int n;
  288. for (i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
  289. {
  290. n = MAPRGB(pCell[i]); // map to nearest palette index
  291. pCell[i] = prgbqOut[n]; // ...and map back to a RGB
  292. }
  293. }
  294. #endif
  295. //////////////////////////////////////////////////////////////////////////
  296. //
  297. // take care of any outstanding skips
  298. //
  299. //////////////////////////////////////////////////////////////////////////
  300. #define FlushSkips() \
  301. \
  302. while (SkipCount > 0) \
  303. { \
  304. WORD w; \
  305. \
  306. w = min(SkipCount, SKIP_MAX); \
  307. SkipCount -= w; \
  308. w |= SKIP_MAGIC; \
  309. *dst++ = w; \
  310. numberOfSkipCodes++; \
  311. actualSize += 2; \
  312. }
  313. /*******************************************************************
  314. routine: CompressFrame16
  315. purp: compress a frame, outputing 16 bit compressed data.
  316. returns: number of bytes in compressed buffer
  317. *******************************************************************/
  318. DWORD FAR CompressFrame16(LPBITMAPINFOHEADER lpbi, // DIB header to compress
  319. LPVOID lpBits, // DIB bits to compress
  320. LPVOID lpData, // put compressed data here
  321. DWORD threshold, // edge threshold
  322. DWORD thresholdInter, // inter-frame threshold
  323. LPBITMAPINFOHEADER lpbiPrev, // previous frame
  324. LPVOID lpPrev, // previous frame
  325. LONG (CALLBACK *Status) (LPARAM lParam, UINT message, LONG l),
  326. LPARAM lParam,
  327. PCELLS pCells)
  328. {
  329. UINT bix;
  330. UINT biy;
  331. UINT WidthBytes;
  332. UINT WidthBytesPrev;
  333. WORD SkipCount;
  334. WORD luminance[16], luminanceMean[4];
  335. DWORD luminanceSum;
  336. WORD sumR,sumG,sumB;
  337. WORD sumR0[4],sumG0[4],sumB0[4];
  338. WORD sumR1[4],sumG1[4],sumB1[4];
  339. WORD meanR0[4],meanG0[4],meanB0[4];
  340. WORD meanR1[4],meanG1[4],meanB1[4];
  341. WORD zeros[4], ones[4];
  342. UINT x,y;
  343. WORD mask;
  344. HPBYTE srcPtr;
  345. HPBYTE prvPtr;
  346. DWORD actualSize;
  347. UINT i;
  348. UINT mi;
  349. HPWORD dst;
  350. RGBQUAD rgb;
  351. int iStatusEvery;
  352. #ifdef DEBUG
  353. DWORD time = timeGetTime();
  354. #endif
  355. WidthBytes = DIBWIDTHBYTES(*lpbi);
  356. if (lpbiPrev)
  357. WidthBytesPrev = DIBWIDTHBYTES(*lpbiPrev);
  358. bix = (int)lpbi->biWidth/WIDTH_CBLOCK;
  359. biy = (int)lpbi->biHeight/HEIGHT_CBLOCK;
  360. if (bix < 100)
  361. iStatusEvery = 4;
  362. else if (bix < 200)
  363. iStatusEvery = 2;
  364. else
  365. iStatusEvery = 1;
  366. actualSize = 0;
  367. numberOfSkipCodes = 0;
  368. numberOfSkips = 0;
  369. numberOfExtraSkips = 0;
  370. numberOfEdges = 0;
  371. numberOfBlocks = 0;
  372. numberOfSolids = 0;
  373. numberOfSolid4 = 0;
  374. numberOfSolid2 = 0;
  375. numberOfEvilRed = 0;
  376. dst = (HPWORD)lpData;
  377. SkipCount = 0;
  378. for( y = 0; y < biy; y++ )
  379. {
  380. if (Status && ((y % iStatusEvery) == 0)) {
  381. if (Status(lParam, ICSTATUS_STATUS, (y * 100) / biy) != 0)
  382. return (DWORD) -1;
  383. }
  384. srcPtr = lpBits;
  385. prvPtr = lpPrev;
  386. for( x = 0; x < bix; x++ )
  387. {
  388. //////////////////////////////////////////////////////////////////////////
  389. //
  390. // get the cell to compress from the image.
  391. //
  392. //////////////////////////////////////////////////////////////////////////
  393. srcPtr = GetCell(lpbi, srcPtr, pCells->cell);
  394. //////////////////////////////////////////////////////////////////////////
  395. //
  396. // see if it matches the cell in the previous frame.
  397. //
  398. //////////////////////////////////////////////////////////////////////////
  399. if (lpbiPrev)
  400. {
  401. prvPtr = GetCell(lpbiPrev, prvPtr, pCells->cellPrev);
  402. if (CmpCell(pCells->cell, pCells->cellPrev) <= thresholdInter)
  403. {
  404. skip_cell:
  405. numberOfSkips++;
  406. SkipCount++;
  407. continue;
  408. }
  409. }
  410. //////////////////////////////////////////////////////////////////////////
  411. // compute luminance of each pixel in the compression block
  412. // sum the total luminance in the block
  413. // find the pixels with the largest and smallest luminance
  414. //////////////////////////////////////////////////////////////////////////
  415. luminanceSum = 0;
  416. sumR = 0;
  417. sumG = 0;
  418. sumB = 0;
  419. for (i = 0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
  420. {
  421. sumR += pCells->cell[i].rgbRed;
  422. sumG += pCells->cell[i].rgbGreen;
  423. sumB += pCells->cell[i].rgbBlue;
  424. luminance[i] = RgbToY(pCells->cell[i]);
  425. luminanceSum += luminance[i];
  426. }
  427. //////////////////////////////////////////////////////////////////////////
  428. //
  429. // see if we make the cell a single color, and get away with it
  430. //
  431. //////////////////////////////////////////////////////////////////////////
  432. sumR /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
  433. sumG /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
  434. sumB /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
  435. rgb.rgbRed = (BYTE)sumR;
  436. rgb.rgbGreen = (BYTE)sumG;
  437. rgb.rgbBlue = (BYTE)sumB;
  438. for (i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
  439. pCells->cellT[i] = rgb;
  440. if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
  441. {
  442. if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
  443. {
  444. numberOfExtraSkips++;
  445. goto skip_cell;
  446. }
  447. FlushSkips();
  448. // single color!!
  449. solid_color:
  450. mask = RGB16(sumR, sumG, sumB) | 0x8000;
  451. if ((mask & ~SKIP_MASK) == SKIP_MAGIC)
  452. {
  453. numberOfEvilRed++;
  454. mask ^= SKIP_MAGIC;
  455. mask |= 0x8000;
  456. }
  457. *dst++ = mask;
  458. numberOfSolids++;
  459. actualSize += 2;
  460. continue;
  461. }
  462. //////////////////////////////////////////////////////////////////////////
  463. //
  464. // make a 4x4 block
  465. //
  466. //////////////////////////////////////////////////////////////////////////
  467. luminanceMean[0] = (WORD)(luminanceSum >> 4);
  468. // zero summing arrays
  469. zeros[0]=0;
  470. ones[0] =0;
  471. sumR0[0]=0;
  472. sumR1[0]=0;
  473. sumG0[0]=0;
  474. sumG1[0]=0;
  475. sumB0[0]=0;
  476. sumB1[0]=0;
  477. // define which of the two colors to choose
  478. // for each pixel by creating the mask
  479. mask = 0;
  480. for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
  481. {
  482. if( luminance[i] < luminanceMean[0] )
  483. {
  484. // mask &= ~(1 << i); // already clear
  485. zeros[0]++;
  486. sumR0[0] += pCells->cell[i].rgbRed;
  487. sumG0[0] += pCells->cell[i].rgbGreen;
  488. sumB0[0] += pCells->cell[i].rgbBlue;
  489. }
  490. else
  491. {
  492. mask |= (1 << i);
  493. ones[0]++;
  494. sumR1[0] += pCells->cell[i].rgbRed;
  495. sumG1[0] += pCells->cell[i].rgbGreen;
  496. sumB1[0] += pCells->cell[i].rgbBlue;
  497. }
  498. }
  499. // define the "one" color as the mean of each element
  500. if( ones[0] != 0 )
  501. {
  502. meanR1[0] = sumR1[0] / ones[0];
  503. meanG1[0] = sumG1[0] / ones[0];
  504. meanB1[0] = sumB1[0] / ones[0];
  505. }
  506. else
  507. {
  508. meanR1[0] = meanG1[0] = meanB1[0] = 0;
  509. }
  510. if( zeros[0] != 0 )
  511. {
  512. meanR0[0] = sumR0[0] / zeros[0];
  513. meanG0[0] = sumG0[0] / zeros[0];
  514. meanB0[0] = sumB0[0] / zeros[0];
  515. }
  516. else
  517. {
  518. meanR0[0] = meanG0[0] = meanB0[0] = 0;
  519. }
  520. //
  521. // build the block and make sure, it is within error.
  522. //
  523. for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
  524. {
  525. if( luminance[i] < luminanceMean[0] )
  526. {
  527. pCells->cellT[i].rgbRed = (BYTE)meanR0[0];
  528. pCells->cellT[i].rgbGreen = (BYTE)meanG0[0];
  529. pCells->cellT[i].rgbBlue = (BYTE)meanB0[0];
  530. }
  531. else
  532. {
  533. pCells->cellT[i].rgbRed = (BYTE)meanR1[0];
  534. pCells->cellT[i].rgbGreen = (BYTE)meanG1[0];
  535. pCells->cellT[i].rgbBlue = (BYTE)meanB1[0];
  536. }
  537. }
  538. if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
  539. {
  540. if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
  541. {
  542. numberOfExtraSkips++;
  543. goto skip_cell;
  544. }
  545. //
  546. // handle any outstanding skip codes
  547. //
  548. FlushSkips();
  549. //
  550. // we should never, ever generate a mask of all ones or
  551. // zeros!
  552. //
  553. if (mask == 0x0000)
  554. {
  555. DPF(("4x4 generated a zero mask!"));
  556. sumR = meanR0[0]; sumG = meanG0[0]; sumB = meanB0[0];
  557. goto solid_color;
  558. }
  559. if (mask == 0xFFFF)
  560. {
  561. DPF(("4x4 generated a FFFF mask!"));
  562. sumR = meanR1[0]; sumG = meanG1[0]; sumB = meanB1[0];
  563. goto solid_color;
  564. }
  565. //
  566. // remember the high bit of the mask is used to mark
  567. // a skip or solid color, so make sure the high bit
  568. // is zero.
  569. //
  570. if (mask & 0x8000)
  571. {
  572. *dst++ = ~mask;
  573. *dst++ = RGB16(meanR0[0],meanG0[0],meanB0[0]);
  574. *dst++ = RGB16(meanR1[0],meanG1[0],meanB1[0]);
  575. }
  576. else
  577. {
  578. *dst++ = mask;
  579. *dst++ = RGB16(meanR1[0],meanG1[0],meanB1[0]);
  580. *dst++ = RGB16(meanR0[0],meanG0[0],meanB0[0]);
  581. }
  582. actualSize += 6;
  583. numberOfBlocks++;
  584. continue;
  585. }
  586. //////////////////////////////////////////////////////////////////////////
  587. //
  588. // see if we make the cell four solid colorls, and get away with it
  589. //
  590. // C D E F
  591. // 8 9 A C
  592. // 4 5 6 7
  593. // 0 1 2 3
  594. //
  595. //////////////////////////////////////////////////////////////////////////
  596. #ifdef DEBUG
  597. for (i=0; i <= 10; i == 2 ? (i += 6) : (i += 2))
  598. {
  599. pCells->cellT[i].rgbRed = (BYTE)(((WORD)pCells->cell[i].rgbRed
  600. + pCells->cell[i+1].rgbRed
  601. + pCells->cell[i+4].rgbRed
  602. + pCells->cell[i+5].rgbRed ) / 4);
  603. pCells->cellT[i].rgbGreen = (BYTE)(((WORD)pCells->cell[i].rgbGreen
  604. + pCells->cell[i+1].rgbGreen
  605. + pCells->cell[i+4].rgbGreen
  606. + pCells->cell[i+5].rgbGreen ) / 4);
  607. pCells->cellT[i].rgbBlue = (BYTE)(((WORD)pCells->cell[i].rgbBlue
  608. + pCells->cell[i+1].rgbBlue
  609. + pCells->cell[i+4].rgbBlue
  610. + pCells->cell[i+5].rgbBlue ) / 4);
  611. pCells->cellT[i+1] = pCells->cellT[i+4]
  612. = pCells->cellT[i+5]
  613. = pCells->cellT[i];
  614. }
  615. if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
  616. {
  617. // four colors
  618. numberOfSolid4++;
  619. }
  620. #endif
  621. //////////////////////////////////////////////////////////////////////////
  622. //
  623. // make a 2x2 block
  624. //
  625. //////////////////////////////////////////////////////////////////////////
  626. FlushSkips();
  627. numberOfEdges++;
  628. luminanceMean[0] = (luminance[0] + luminance[1] + luminance[4] + luminance[5]) / 4;
  629. luminanceMean[1] = (luminance[2] + luminance[3] + luminance[6] + luminance[7]) / 4;
  630. luminanceMean[2] = (luminance[8] + luminance[9] + luminance[12] + luminance[13]) / 4;
  631. luminanceMean[3] = (luminance[10] + luminance[11] + luminance[14] + luminance[15]) / 4;
  632. // zero summing arrays
  633. zeros[0]=zeros[1]=zeros[2]=zeros[3]=0;
  634. ones[0]=ones[1]=ones[2]=ones[3]=0;
  635. sumR0[0]=sumR0[1]=sumR0[2]=sumR0[3]=0;
  636. sumR1[0]=sumR1[1]=sumR1[2]=sumR1[3]=0;
  637. sumG0[0]=sumG0[1]=sumG0[2]=sumG0[3]=0;
  638. sumG1[0]=sumG1[1]=sumG1[2]=sumG1[3]=0;
  639. sumB0[0]=sumB0[1]=sumB0[2]=sumB0[3]=0;
  640. sumB1[0]=sumB1[1]=sumB1[2]=sumB1[3]=0;
  641. // define which of the two colors to choose
  642. // for each pixel by creating the mask
  643. mask = 0;
  644. for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
  645. {
  646. mi = meanIndex[i];
  647. if( luminance[i] < luminanceMean[mi] )
  648. {
  649. // mask &= ~(1 << i); // already clear
  650. zeros[mi]++;
  651. sumR0[mi] += pCells->cell[i].rgbRed;
  652. sumG0[mi] += pCells->cell[i].rgbGreen;
  653. sumB0[mi] += pCells->cell[i].rgbBlue;
  654. }
  655. else
  656. {
  657. mask |= (1 << i);
  658. ones[mi]++;
  659. sumR1[mi] += pCells->cell[i].rgbRed;
  660. sumG1[mi] += pCells->cell[i].rgbGreen;
  661. sumB1[mi] += pCells->cell[i].rgbBlue;
  662. }
  663. }
  664. // store the mask
  665. if (mask & 0x8000)
  666. *dst++ = ~mask;
  667. else
  668. *dst++ = mask;
  669. actualSize += 2;
  670. // make the colors
  671. for( i=0; i < 4; i++ )
  672. {
  673. // define the "one" color as the mean of each element
  674. if( ones[i] != 0 )
  675. {
  676. meanR1[i] = sumR1[i] / ones[i];
  677. meanG1[i] = sumG1[i] / ones[i];
  678. meanB1[i] = sumB1[i] / ones[i];
  679. }
  680. else
  681. {
  682. meanR1[i] = meanG1[i] = meanB1[i] = 0;
  683. }
  684. if( zeros[i] != 0 )
  685. {
  686. meanR0[i] = sumR0[i] / zeros[i];
  687. meanG0[i] = sumG0[i] / zeros[i];
  688. meanB0[i] = sumB0[i] / zeros[i];
  689. }
  690. else
  691. {
  692. meanR0[i] = meanG0[i] = meanB0[i] = 0;
  693. }
  694. // convert to 555 and set bit 15 if this is an edge and
  695. // this is the first color
  696. if (mask & 0x8000)
  697. {
  698. *dst++ = RGB16(meanR0[i],meanG0[i],meanB0[i]) | (i==0 ? 0x8000 : 0);
  699. *dst++ = RGB16(meanR1[i],meanG1[i],meanB1[i]);
  700. }
  701. else
  702. {
  703. *dst++ = RGB16(meanR1[i],meanG1[i],meanB1[i]) | (i==0 ? 0x8000 : 0);
  704. *dst++ = RGB16(meanR0[i],meanG0[i],meanB0[i]);
  705. }
  706. actualSize += 4;
  707. }
  708. }
  709. //////////////////////////////////////////////////////////////////////////
  710. //
  711. // next scan.
  712. //
  713. //////////////////////////////////////////////////////////////////////////
  714. ((HPBYTE)lpBits) += WidthBytes * HEIGHT_CBLOCK;
  715. if (lpPrev)
  716. ((HPBYTE)lpPrev) += WidthBytesPrev * HEIGHT_CBLOCK;
  717. }
  718. //////////////////////////////////////////////////////////////////////////
  719. //
  720. // take care of any outstanding skips, !!! note we dont need this if we
  721. // assume a EOF!
  722. //
  723. //////////////////////////////////////////////////////////////////////////
  724. FlushSkips();
  725. //////////////////////////////////////////////////////////////////////////
  726. //
  727. // all done generate a EOF zero mask
  728. //
  729. //////////////////////////////////////////////////////////////////////////
  730. *dst++ = 0;
  731. actualSize += 2;
  732. //////////////////////////////////////////////////////////////////////////
  733. //////////////////////////////////////////////////////////////////////////
  734. DPF(("CompressFrame16:"));
  735. DPF((" time: %ld", timeGetTime() - time));
  736. DPF((" tol: %ld/%ld", threshold, thresholdInter));
  737. DPF((" Size: %ld", actualSize));
  738. DPF((" Skips: %ld (%ld)", numberOfSkips, numberOfSkipCodes));
  739. DPF((" Extra Skips: %ld", numberOfExtraSkips));
  740. DPF((" Solid: %ld", numberOfSolids));
  741. DPF((" 4x4: %ld", numberOfBlocks));
  742. DPF((" 2x2: %ld", numberOfEdges));
  743. DPF((" EvilRed: %ld", numberOfEvilRed));
  744. DPF((" 4Solid: %ld", numberOfSolid4));
  745. return( actualSize );
  746. }
  747. /*******************************************************************
  748. routine: CompressFrame8
  749. purp: compress a frame, outputing 8 bit compressed data.
  750. returns: number of bytes in compressed buffer
  751. !!! this is almost a 1:1 copy of the above routine help!
  752. *******************************************************************/
  753. DWORD FAR CompressFrame8(LPBITMAPINFOHEADER lpbi, // DIB header to compress
  754. LPVOID lpBits, // DIB bits to compress
  755. LPVOID lpData, // put compressed data here
  756. DWORD threshold, // edge threshold
  757. DWORD thresholdInter, // inter-frame threshold
  758. LPBITMAPINFOHEADER lpbiPrev, // previous frame
  759. LPVOID lpPrev, // previous frame
  760. LONG (CALLBACK *Status) (LPARAM lParam, UINT message, LONG l),
  761. LPARAM lParam,
  762. PCELLS pCells,
  763. LPBYTE lpITable,
  764. RGBQUAD * prgbqOut)
  765. {
  766. UINT bix;
  767. UINT biy;
  768. UINT WidthBytes;
  769. UINT WidthBytesPrev;
  770. WORD SkipCount;
  771. WORD luminance[16], luminanceMean[4];
  772. DWORD luminanceSum;
  773. WORD sumR,sumG,sumB;
  774. WORD sumR0[4],sumG0[4],sumB0[4];
  775. WORD sumR1[4],sumG1[4],sumB1[4];
  776. WORD meanR0[4],meanG0[4],meanB0[4];
  777. WORD meanR1[4],meanG1[4],meanB1[4];
  778. WORD zeros[4], ones[4];
  779. UINT x,y;
  780. WORD mask;
  781. HPBYTE srcPtr;
  782. HPBYTE prvPtr;
  783. DWORD actualSize;
  784. UINT i;
  785. WORD mi;
  786. HPWORD dst;
  787. RGBQUAD rgb,rgb0,rgb1;
  788. BYTE b, b0, b1;
  789. WORD w;
  790. int iStatusEvery;
  791. #ifdef DEBUG
  792. DWORD time = timeGetTime();
  793. #endif
  794. WidthBytes = DIBWIDTHBYTES(*lpbi);
  795. if (lpbiPrev)
  796. WidthBytesPrev = DIBWIDTHBYTES(*lpbiPrev);
  797. bix = (int)lpbi->biWidth/WIDTH_CBLOCK;
  798. biy = (int)lpbi->biHeight/HEIGHT_CBLOCK;
  799. if (bix < 100)
  800. iStatusEvery = 4;
  801. else if (bix < 200)
  802. iStatusEvery = 2;
  803. else
  804. iStatusEvery = 1;
  805. actualSize = 0;
  806. numberOfSkipCodes = 0;
  807. numberOfSkips = 0;
  808. numberOfExtraSkips = 0;
  809. numberOfEdges = 0;
  810. numberOfBlocks = 0;
  811. numberOfSolids = 0;
  812. numberOfSolid4 = 0;
  813. numberOfSolid2 = 0;
  814. numberOfEvilRed = 0;
  815. dst = (HPWORD)lpData;
  816. SkipCount = 0;
  817. if (lpITable == NULL)
  818. {
  819. DPF(("ICM_COMPRESS_BEGIN not recieved!"));
  820. return 0;
  821. }
  822. for( y = 0; y < biy; y++ )
  823. {
  824. srcPtr = lpBits;
  825. prvPtr = lpPrev;
  826. if (Status && ((y % iStatusEvery) == 0)) {
  827. if (Status(lParam, ICSTATUS_STATUS, (y * 100) / biy) != 0)
  828. return (DWORD) -1;
  829. }
  830. for( x = 0; x < bix; x++ )
  831. {
  832. //////////////////////////////////////////////////////////////////////////
  833. //
  834. // get the cell to compress from the image.
  835. //
  836. //////////////////////////////////////////////////////////////////////////
  837. srcPtr = GetCell(lpbi, srcPtr, pCells->cell);
  838. //////////////////////////////////////////////////////////////////////////
  839. //
  840. // see if it matches the cell in the previous frame.
  841. //
  842. //////////////////////////////////////////////////////////////////////////
  843. if (lpbiPrev)
  844. {
  845. prvPtr = GetCell(lpbiPrev, prvPtr, pCells->cellPrev);
  846. if (CmpCell(pCells->cell, pCells->cellPrev) <= thresholdInter)
  847. {
  848. skip_cell:
  849. numberOfSkips++;
  850. SkipCount++;
  851. continue;
  852. }
  853. }
  854. //////////////////////////////////////////////////////////////////////////
  855. // compute luminance of each pixel in the compression block
  856. // sum the total luminance in the block
  857. // find the pixels with the largest and smallest luminance
  858. //////////////////////////////////////////////////////////////////////////
  859. luminanceSum = 0;
  860. sumR = 0;
  861. sumG = 0;
  862. sumB = 0;
  863. for (i = 0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
  864. {
  865. sumR += pCells->cell[i].rgbRed;
  866. sumG += pCells->cell[i].rgbGreen;
  867. sumB += pCells->cell[i].rgbBlue;
  868. luminance[i] = RgbToY(pCells->cell[i]);
  869. luminanceSum += luminance[i];
  870. }
  871. //////////////////////////////////////////////////////////////////////////
  872. //
  873. // see if we make the cell a single color, and get away with it
  874. //
  875. //////////////////////////////////////////////////////////////////////////
  876. sumR /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
  877. sumG /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
  878. sumB /= HEIGHT_CBLOCK*WIDTH_CBLOCK;
  879. rgb.rgbRed = (BYTE)sumR;
  880. rgb.rgbGreen = (BYTE)sumG;
  881. rgb.rgbBlue = (BYTE)sumB;
  882. b = MAPRGB(rgb); // map color to 8bit
  883. rgb = prgbqOut[b];
  884. for (i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++)
  885. pCells->cellT[i] = rgb;
  886. if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
  887. {
  888. if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
  889. {
  890. numberOfExtraSkips++;
  891. goto skip_cell;
  892. }
  893. FlushSkips();
  894. solid_color:
  895. // single color!!
  896. mask = SOLID_MAGIC | b;
  897. *dst++ = mask;
  898. numberOfSolids++;
  899. actualSize += 2;
  900. continue;
  901. }
  902. //////////////////////////////////////////////////////////////////////////
  903. //
  904. // make a 4x4 block
  905. //
  906. //////////////////////////////////////////////////////////////////////////
  907. luminanceMean[0] = (WORD)(luminanceSum >> 4);
  908. // zero summing arrays
  909. zeros[0]=0;
  910. ones[0] =0;
  911. sumR0[0]=0;
  912. sumR1[0]=0;
  913. sumG0[0]=0;
  914. sumG1[0]=0;
  915. sumB0[0]=0;
  916. sumB1[0]=0;
  917. // define which of the two colors to choose
  918. // for each pixel by creating the mask
  919. mask = 0;
  920. for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
  921. {
  922. if( luminance[i] < luminanceMean[0] )
  923. {
  924. // mask &= ~(1 << i); // already clear
  925. zeros[0]++;
  926. sumR0[0] += pCells->cell[i].rgbRed;
  927. sumG0[0] += pCells->cell[i].rgbGreen;
  928. sumB0[0] += pCells->cell[i].rgbBlue;
  929. }
  930. else
  931. {
  932. mask |= (1 << i);
  933. ones[0]++;
  934. sumR1[0] += pCells->cell[i].rgbRed;
  935. sumG1[0] += pCells->cell[i].rgbGreen;
  936. sumB1[0] += pCells->cell[i].rgbBlue;
  937. }
  938. }
  939. // define the "one" color as the mean of each element
  940. if( ones[0] != 0 )
  941. {
  942. meanR1[0] = sumR1[0] / ones[0];
  943. meanG1[0] = sumG1[0] / ones[0];
  944. meanB1[0] = sumB1[0] / ones[0];
  945. }
  946. else
  947. {
  948. meanR1[0] = meanG1[0] = meanB1[0] = 0;
  949. }
  950. if( zeros[0] != 0 )
  951. {
  952. meanR0[0] = sumR0[0] / zeros[0];
  953. meanG0[0] = sumG0[0] / zeros[0];
  954. meanB0[0] = sumB0[0] / zeros[0];
  955. }
  956. else
  957. {
  958. meanR0[0] = meanG0[0] = meanB0[0] = 0;
  959. }
  960. //
  961. // map colors to 8-bit
  962. //
  963. rgb0.rgbRed = (BYTE)meanR0[0];
  964. rgb0.rgbGreen = (BYTE)meanG0[0];
  965. rgb0.rgbBlue = (BYTE)meanB0[0];
  966. b0 = MAPRGB(rgb0);
  967. rgb0 = prgbqOut[b0];
  968. rgb1.rgbRed = (BYTE)meanR1[0];
  969. rgb1.rgbGreen = (BYTE)meanG1[0];
  970. rgb1.rgbBlue = (BYTE)meanB1[0];
  971. b1 = MAPRGB(rgb1);
  972. rgb1 = prgbqOut[b1];
  973. //
  974. // build the block and make sure, it is within error.
  975. //
  976. for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
  977. {
  978. if( luminance[i] < luminanceMean[0] )
  979. pCells->cellT[i] = rgb0;
  980. else
  981. pCells->cellT[i] = rgb1;
  982. }
  983. if (CmpCell(pCells->cell, pCells->cellT) <= threshold)
  984. {
  985. if (lpbiPrev && CmpCell(pCells->cellT, pCells->cellPrev) <= thresholdInter)
  986. {
  987. numberOfExtraSkips++;
  988. goto skip_cell;
  989. }
  990. FlushSkips();
  991. // store the mask
  992. //
  993. // we should never, ever generate a mask of all ones or
  994. // zeros!
  995. //
  996. if (mask == 0x0000)
  997. {
  998. DPF(("4x4 generated a zero mask!"));
  999. b = b0;
  1000. goto solid_color;
  1001. }
  1002. if (mask == 0xFFFF)
  1003. {
  1004. DPF(("4x4 generated a FFFF mask!"));
  1005. b = b1;
  1006. goto solid_color;
  1007. }
  1008. if (b0 == b1)
  1009. {
  1010. DPF(("4x4 generated two colors the same!"));
  1011. b = b1;
  1012. goto solid_color;
  1013. }
  1014. //
  1015. // remember the high bit of the mask is used to mark
  1016. // a skip or solid color, so make sure the high bit
  1017. // is zero.
  1018. //
  1019. if (mask & 0x8000)
  1020. {
  1021. mask = ~mask;
  1022. SWAP(b0,b1);
  1023. }
  1024. *dst++ = mask;
  1025. *dst++ = (WORD)b1 | ((WORD)b0 << 8);
  1026. actualSize += 4;
  1027. numberOfBlocks++;
  1028. continue;
  1029. }
  1030. //////////////////////////////////////////////////////////////////////////
  1031. //
  1032. // make a 2x2 block
  1033. //
  1034. //////////////////////////////////////////////////////////////////////////
  1035. FlushSkips();
  1036. numberOfEdges++;
  1037. luminanceMean[0] = (luminance[0] + luminance[1] + luminance[4] + luminance[5]) / 4;
  1038. luminanceMean[1] = (luminance[2] + luminance[3] + luminance[6] + luminance[7]) / 4;
  1039. luminanceMean[2] = (luminance[8] + luminance[9] + luminance[12] + luminance[13]) / 4;
  1040. luminanceMean[3] = (luminance[10] + luminance[11] + luminance[14] + luminance[15]) / 4;
  1041. // zero summing arrays
  1042. zeros[0]=zeros[1]=zeros[2]=zeros[3]=0;
  1043. ones[0]=ones[1]=ones[2]=ones[3]=0;
  1044. sumR0[0]=sumR0[1]=sumR0[2]=sumR0[3]=0;
  1045. sumR1[0]=sumR1[1]=sumR1[2]=sumR1[3]=0;
  1046. sumG0[0]=sumG0[1]=sumG0[2]=sumG0[3]=0;
  1047. sumG1[0]=sumG1[1]=sumG1[2]=sumG1[3]=0;
  1048. sumB0[0]=sumB0[1]=sumB0[2]=sumB0[3]=0;
  1049. sumB1[0]=sumB1[1]=sumB1[2]=sumB1[3]=0;
  1050. // define which of the two colors to choose
  1051. // for each pixel by creating the mask
  1052. mask = 0;
  1053. for( i=0; i < HEIGHT_CBLOCK*WIDTH_CBLOCK; i++ )
  1054. {
  1055. mi = meanIndex[i];
  1056. if( luminance[i] < luminanceMean[mi] )
  1057. {
  1058. // mask &= ~(1 << i); // already clear
  1059. zeros[mi]++;
  1060. sumR0[mi] += pCells->cell[i].rgbRed;
  1061. sumG0[mi] += pCells->cell[i].rgbGreen;
  1062. sumB0[mi] += pCells->cell[i].rgbBlue;
  1063. }
  1064. else
  1065. {
  1066. mask |= (1 << i);
  1067. ones[mi]++;
  1068. sumR1[mi] += pCells->cell[i].rgbRed;
  1069. sumG1[mi] += pCells->cell[i].rgbGreen;
  1070. sumB1[mi] += pCells->cell[i].rgbBlue;
  1071. }
  1072. }
  1073. // store the mask
  1074. //
  1075. // in the 8-bit case the mask must have the following form:
  1076. //
  1077. // 1X1XXXXXXXXXXXXX
  1078. //
  1079. // these bits can be forces high by exchanging the colors for
  1080. // the top two cells.
  1081. //
  1082. w = mask;
  1083. if (!(mask & 0x8000))
  1084. w ^= 0xCC00;
  1085. if (!(mask & 0x2000))
  1086. w ^= 0x3300;
  1087. *dst++ = w;
  1088. actualSize += 2;
  1089. // make the colors
  1090. for( i=0; i < 4; i++ )
  1091. {
  1092. // define the "one" color as the mean of each element
  1093. if( ones[i] != 0 )
  1094. {
  1095. meanR1[i] = sumR1[i] / ones[i];
  1096. meanG1[i] = sumG1[i] / ones[i];
  1097. meanB1[i] = sumB1[i] / ones[i];
  1098. }
  1099. else
  1100. {
  1101. meanR1[i] = meanG1[i] = meanB1[i] = 0;
  1102. }
  1103. if( zeros[i] != 0 )
  1104. {
  1105. meanR0[i] = sumR0[i] / zeros[i];
  1106. meanG0[i] = sumG0[i] / zeros[i];
  1107. meanB0[i] = sumB0[i] / zeros[i];
  1108. }
  1109. else
  1110. {
  1111. meanR0[i] = meanG0[i] = meanB0[i] = 0;
  1112. }
  1113. // convert to the 8bit palette, and write out the colors
  1114. // make sure to exchange the colors if we hade to invert
  1115. // the mask to normalize it.
  1116. rgb0.rgbRed = (BYTE)meanR0[i];
  1117. rgb0.rgbGreen = (BYTE)meanG0[i];
  1118. rgb0.rgbBlue = (BYTE)meanB0[i];
  1119. b0 = MAPRGB(rgb0);
  1120. rgb1.rgbRed = (BYTE)meanR1[i];
  1121. rgb1.rgbGreen = (BYTE)meanG1[i];
  1122. rgb1.rgbBlue = (BYTE)meanB1[i];
  1123. b1 = MAPRGB(rgb1);
  1124. if (i==3 && !(mask & 0x8000))
  1125. SWAP(b0,b1);
  1126. if (i==2 && !(mask & 0x2000))
  1127. SWAP(b0,b1);
  1128. if (b0 == b0)
  1129. {
  1130. numberOfSolid2++;
  1131. }
  1132. *dst++ = (WORD)b1 | ((WORD)b0 << 8);
  1133. actualSize += 2;
  1134. }
  1135. }
  1136. //////////////////////////////////////////////////////////////////////////
  1137. //
  1138. // next scan.
  1139. //
  1140. //////////////////////////////////////////////////////////////////////////
  1141. ((HPBYTE)lpBits) += WidthBytes * HEIGHT_CBLOCK;
  1142. if (lpPrev)
  1143. ((HPBYTE)lpPrev) += WidthBytesPrev * HEIGHT_CBLOCK;
  1144. }
  1145. //////////////////////////////////////////////////////////////////////////
  1146. //
  1147. // take care of any outstanding skips, !!! note we dont need this if we
  1148. // assume a EOF!
  1149. //
  1150. //////////////////////////////////////////////////////////////////////////
  1151. FlushSkips();
  1152. //////////////////////////////////////////////////////////////////////////
  1153. //
  1154. // all done generate a EOF zero mask
  1155. //
  1156. //////////////////////////////////////////////////////////////////////////
  1157. *dst++ = 0;
  1158. actualSize += 2;
  1159. //////////////////////////////////////////////////////////////////////////
  1160. //////////////////////////////////////////////////////////////////////////
  1161. DPF(("CompressFrame8:"));
  1162. DPF((" time: %ld", timeGetTime() - time));
  1163. DPF((" tol: %ld/%ld", threshold, thresholdInter));
  1164. DPF((" Size: %ld", actualSize));
  1165. DPF((" Skips: %ld (%ld)", numberOfSkips, numberOfSkipCodes));
  1166. DPF((" Extra Skips: %ld", numberOfExtraSkips));
  1167. DPF((" Solid: %ld", numberOfSolids));
  1168. DPF((" 4x4: %ld", numberOfBlocks));
  1169. DPF((" 2x2: %ld", numberOfEdges));
  1170. return( actualSize );
  1171. }