Implemented the MS RLE video codec.
[wine/wine64.git] / dlls / msvideo / msrle32 / msrle32.c
blob039f7cf7676631a9b10b12081b304fa380dd4e3e
1 /*
2 * Copyright 2002 Michael Günnewig
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /* TODO:
20 * - compression of RLE4 is buggy -- see FIXME's
21 * - many improvements possible
22 * - implement DecompressSetPalette? -- does we need it for anything?
25 #include <assert.h>
27 #include "msrle_private.h"
29 #include "winnls.h"
30 #include "winuser.h"
31 #include "windowsx.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(msrle32);
37 static HINSTANCE MSRLE32_hModule = 0;
39 #define ABS(a) ((a) < 0 ? -(a) : (a))
40 #define SQR(a) ((a) * (a))
42 #define QUALITY_to_DIST(q) (ICQUALITY_HIGH - q)
43 inline WORD ColorCmp(WORD clr1, WORD clr2)
45 register UINT a = (clr1-clr2);
46 return SQR(a);
48 inline WORD Intensity(RGBQUAD clr)
50 return (30 * clr.rgbRed + 59 * clr.rgbGreen + 11 * clr.rgbBlue)/4;
53 #define GetRawPixel(lpbi,lp,x) \
54 ((lpbi)->biBitCount == 1 ? ((lp)[(x)/8] >> (8 - (x)%8)) & 1 : \
55 ((lpbi)->biBitCount == 4 ? ((lp)[(x)/2] >> (4 * (1 - (x)%2))) & 15 : lp[x]))
57 /*****************************************************************************/
59 /* utility functions */
60 static BOOL isSupportedDIB(LPCBITMAPINFOHEADER lpbi);
61 static BOOL isSupportedMRLE(LPCBITMAPINFOHEADER lpbi);
62 static void LoadWideString(UINT id, LPWSTR str, INT len);
63 static BYTE MSRLE32_GetNearestPaletteIndex(UINT count, const RGBQUAD *clrs, RGBQUAD clr);
65 /* compression functions */
66 static void computeInternalFrame(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn, LPBYTE lpIn);
67 static LONG MSRLE32_GetMaxCompressedSize(LPCBITMAPINFOHEADER lpbi);
68 static LRESULT MSRLE32_CompressRLE4(CodecInfo *pi, LPBITMAPINFOHEADER lpbiIn, LPBYTE lpIn, LPBITMAPINFOHEADER lpbiOut, LPBYTE lpOut, BOOL isKey);
69 static LRESULT MSRLE32_CompressRLE8(CodecInfo *pi, LPBITMAPINFOHEADER lpbiIn, LPBYTE lpIn, LPBITMAPINFOHEADER lpbiOut, LPBYTE lpOut, BOOL isKey);
71 /* decompression functions */
72 static LRESULT MSRLE32_DecompressRLE4(CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
73 LPBYTE lpIn, LPBYTE lpOut);
74 static LRESULT MSRLE32_DecompressRLE8(CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
75 LPBYTE lpIn, LPBYTE lpOut);
77 /* API functions */
78 static LRESULT CompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
79 LPBITMAPINFOHEADER lpbiOut);
80 static LRESULT CompressGetSize(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
81 LPCBITMAPINFOHEADER lpbiOut);
82 static LRESULT CompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
83 LPCBITMAPINFOHEADER lpbiOut);
84 static LRESULT CompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
85 LPCBITMAPINFOHEADER lpbiOut);
86 static LRESULT Compress(CodecInfo *pi, ICCOMPRESS* lpic, DWORD dwSize);
87 static LRESULT CompressEnd(CodecInfo *pi);
89 static LRESULT DecompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
90 LPBITMAPINFOHEADER lpbiOut);
91 static LRESULT DecompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
92 LPCBITMAPINFOHEADER lpbiOut);
93 static LRESULT DecompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
94 LPCBITMAPINFOHEADER lpbiOut);
95 static LRESULT Decompress(CodecInfo *pi, ICDECOMPRESS *pic, DWORD dwSize);
96 static LRESULT DecompressEnd(CodecInfo *pi);
97 static LRESULT DecompressGetPalette(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
98 LPBITMAPINFOHEADER lpbiOut);
100 /*****************************************************************************/
102 static void LoadWideString(UINT id, LPWSTR str, INT len)
104 char szTemp[80];
106 LoadStringA(MSRLE32_hModule, id, szTemp, sizeof(szTemp));
107 MultiByteToWideChar(CP_ACP, 0, szTemp, -1, str, len);
110 static BOOL isSupportedMRLE(LPCBITMAPINFOHEADER lpbi)
112 /* pre-conditions */
113 assert(lpbi != NULL);
115 if (lpbi->biSize < sizeof(BITMAPINFOHEADER) || \
116 lpbi->biPlanes != 1)
117 return FALSE;
119 if (lpbi->biCompression == BI_RLE4) {
120 if (lpbi->biBitCount != 4 || \
121 (lpbi->biWidth % 2) != 0)
122 return FALSE;
123 } else if (lpbi->biCompression == BI_RLE8) {
124 if (lpbi->biBitCount != 8)
125 return FALSE;
126 } else
127 return FALSE;
129 return TRUE;
132 static BOOL isSupportedDIB(LPCBITMAPINFOHEADER lpbi)
134 /* pre-conditions */
135 assert(lpbi != NULL);
137 /* check structure version/planes/compression */
138 if (lpbi->biSize < sizeof(BITMAPINFOHEADER) ||
139 lpbi->biPlanes != 1)
140 return FALSE;
141 if (lpbi->biCompression != BI_RGB &&
142 lpbi->biCompression != BI_BITFIELDS)
143 return FALSE;
145 /* check bit-depth */
146 if (lpbi->biBitCount != 1 &&
147 lpbi->biBitCount != 4 &&
148 lpbi->biBitCount != 8 &&
149 lpbi->biBitCount != 15 &&
150 lpbi->biBitCount != 16 &&
151 lpbi->biBitCount != 24 &&
152 lpbi->biBitCount != 32)
153 return FALSE;
155 /* check for size(s) */
156 if (!lpbi->biWidth || !lpbi->biHeight)
157 return FALSE; /* image with zero size, makes no sense so error ! */
158 if (DIBWIDTHBYTES(*lpbi) * lpbi->biHeight >= (1UL << 31) - 1)
159 return FALSE; /* image too big ! */
161 /* check for non existing colortable for hi- and true-color DIB's */
162 if (lpbi->biBitCount >= 15 && lpbi->biClrUsed > 0)
163 return FALSE;
165 return TRUE;
168 static BYTE MSRLE32_GetNearestPaletteIndex(UINT count, const RGBQUAD *clrs, RGBQUAD clr)
170 INT diff = 0x00FFFFFF;
171 UINT i;
172 UINT index = 0;
174 /* pre-conditions */
175 assert(clrs != NULL);
177 for (i = 0; i < count; i++) {
178 int r = ((int)clrs[i].rgbRed - (int)clr.rgbRed);
179 int g = ((int)clrs[i].rgbGreen - (int)clr.rgbGreen);
180 int b = ((int)clrs[i].rgbBlue - (int)clr.rgbBlue);
182 r = r*r + g*g + b*b;
184 if (r < diff) {
185 index = i;
186 diff = r;
187 if (diff == 0)
188 break;
192 return index;
195 /*****************************************************************************/
197 void computeInternalFrame(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn, LPBYTE lpIn)
199 WORD wIntensityTbl[256];
200 LONG lInLine, lOutLine;
201 LPWORD lpOut;
202 int i, y;
204 /* pre-conditions */
205 assert(pi != NULL && lpbiIn != NULL && lpIn != NULL);
206 assert(pi->pCurFrame != NULL);
208 lInLine = DIBWIDTHBYTES(*lpbiIn);
209 lOutLine = WIDTHBYTES(lpbiIn->biWidth * 8*sizeof(WORD)) / 2;
210 lpOut = pi->pCurFrame;
212 assert(lpbiIn->biClrUsed != 0);
215 const RGBQUAD *lp = (LPRGBQUAD)((LPBYTE)lpbiIn + lpbiIn->biSize);
217 for (i = 0; i < lpbiIn->biClrUsed; i++)
218 wIntensityTbl[i] = Intensity(lp[i]);
221 for (y = 0; y < lpbiIn->biHeight; y++) {
222 int x;
224 switch (lpbiIn->biBitCount) {
225 case 1:
226 for (x = 0; x < lpbiIn->biWidth; x += 8) {
227 for (i = 0; i < 7; i++)
228 lpOut[x + i] = wIntensityTbl[(lpIn[x] >> (7 - i)) & 1];
230 break;
231 case 4:
232 for (x = 0; x < lpbiIn->biWidth; x += 2) {
233 lpOut[x + 0] = wIntensityTbl[(lpIn[x] >> 4)];
234 lpOut[x + 1] = wIntensityTbl[(lpIn[x] & 0x0F)];
236 break;
237 case 8:
238 for (x = 0; x < lpbiIn->biWidth; x++)
239 lpOut[x] = wIntensityTbl[lpIn[x]];
240 break;
243 lpIn += lInLine;
244 lpOut += lOutLine;
248 static LONG MSRLE32_GetMaxCompressedSize(LPCBITMAPINFOHEADER lpbi)
250 LONG a, b, size;
252 /* pre-condition */
253 assert(lpbi != NULL);
255 a = lpbi->biWidth / 255;
256 b = lpbi->biWidth % 255;
257 if (lpbi->biBitCount <= 4) {
258 a /= 2;
259 b /= 2;
262 size = (2 + a * (2 + ((a + 2) & ~2)) + b * (2 + ((b + 2) & ~2)));
263 return size * lpbi->biHeight;
266 /* lpP => current pos in previous frame
267 * lpA => previous pos in current frame
268 * lpB => current pos in current frame
270 static INT countDiffRLE4(LPWORD lpP, LPWORD lpA, LPWORD lpB, INT pos, LONG lDist, LONG width)
272 INT count;
273 WORD clr1, clr2;
275 /* pre-conditions */
276 assert(lpA && lpB && lDist >= 0 && width > 0);
278 if (pos >= width)
279 return 0;
280 if (pos+1 == width)
281 return 1;
283 clr1 = lpB[pos++];
284 clr2 = lpB[pos];
286 count = 2;
287 while (pos + 1 < width) {
288 WORD clr3, clr4;
290 clr3 = lpB[++pos];
291 if (pos + 1 >= width)
292 return count + 1;
294 clr4 = lpB[++pos];
295 if (ColorCmp(clr1, clr3) <= lDist &&
296 ColorCmp(clr2, clr4) <= lDist) {
297 /* diff at end? -- look-ahead for atleast ?? more encodable pixel */
299 /* FIXME */
301 return count;
302 } else if (lpP && ColorCmp(lpP[pos], lpB[pos]) <= lDist) {
303 /* 'compare' with previous frame for end of diff */
304 INT count2 = 0;
306 /* FIXME */
308 if (count2 >= 4)
309 return count;
311 pos -= count2;
314 count += 2;
315 clr1 = clr3;
316 clr2 = clr4;
319 return count;
322 /* lpP => current pos in previous frame
323 * lpA => previous pos in current frame
324 * lpB => current pos in current frame
326 static INT countDiffRLE8(LPWORD lpP, LPWORD lpA, LPWORD lpB, INT pos, LONG lDist, LONG width)
328 INT count;
330 for (count = 0; pos < width; pos++, count++) {
331 if (ColorCmp(lpA[pos], lpB[pos]) <= lDist) {
332 /* diff at end? -- look-ahead for atleast one more encodable pixel */
333 if (pos + 1 < width && ColorCmp(lpA[pos], lpB[pos+1]) <= lDist)
334 return count;
335 } else if (lpP != NULL && ColorCmp(lpP[pos], lpB[pos]) <= lDist) {
336 /* 'compare' with previous frame for end of diff */
337 INT count2 = 0;
339 for (count2 = 0, pos++; pos < width && count2 <= 5; pos++, count2++) {
340 if (ColorCmp(lpP[pos], lpB[pos]) > lDist)
341 break;
343 if (count2 > 4)
344 return count;
346 pos -= count2;
350 return count;
353 static INT MSRLE32_CompressRLE4Line(CodecInfo *pi, LPWORD lpP, LPWORD lpC, LPCBITMAPINFOHEADER lpbi, BYTE *lpIn, LONG lDist, INT x, LPBYTE *ppOut, DWORD *lpSizeImage)
355 LPBYTE lpOut = *ppOut;
356 INT count, pos;
357 WORD clr1, clr2;
359 /* try to encode as many pixel as possible */
360 count = 1;
361 pos = x;
362 clr1 = lpC[pos++];
363 if (pos < lpbi->biWidth) {
364 clr2 = lpC[pos];
365 for (++count; pos + 1 < lpbi->biWidth; ) {
366 ++pos;
367 if (ColorCmp(clr1, lpC[pos]) > lDist)
368 break;
369 count++;
370 if (pos + 1 >= lpbi->biWidth)
371 break;
372 ++pos;
373 if (ColorCmp(clr2, lpC[pos]) > lDist)
374 break;
375 count++;
379 if (count < 4) {
380 /* add some pixel for absoluting if possible */
381 count += countDiffRLE4(lpP, lpC - 1, lpC, pos-1, lDist, lpbi->biWidth);
383 /* check for near end of line */
384 if (x + count > lpbi->biWidth)
385 count = lpbi->biWidth - x;
387 /* absolute pixel(s) in groups of atleast 3 and maximal 254 pixel */
388 while (count > 2) {
389 INT i;
390 INT size = min(count, 254);
391 BOOL extra_byte = size % 2;
393 *lpSizeImage += 2 + size + extra_byte;
394 count -= size;
395 *lpOut++ = 0;
396 *lpOut++ = size;
397 for (i = 0; i < size; i += 2) {
398 clr1 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
399 x++;
400 if (i + 1 < size) {
401 clr2 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
402 x++;
403 } else
404 clr2 = 0;
406 *lpOut++ = (clr1 << 4) | clr2;
408 if (extra_byte)
409 *lpOut++ = 0;
412 if (count > 0) {
413 /* too less for absoluting so we must encode them */
414 assert(count <= 2);
416 *lpSizeImage += 2;
417 clr1 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
418 x++;
419 if (count == 2) {
420 clr2 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
421 x++;
422 } else
423 clr2 = 0;
424 *lpOut++ = count;
425 *lpOut++ = (clr1 << 4) | clr2;
427 } else {
428 /* encode count pixel(s) */
429 clr1 = ((pi->palette_map[GetRawPixel(lpbi,lpIn,x)] << 4) |
430 pi->palette_map[GetRawPixel(lpbi,lpIn,x + 1)]);
432 x += count;
433 while (count > 0) {
434 INT size = min(count, 254);
436 *lpSizeImage += 2;
437 count -= size;
438 *lpOut++ = size;
439 *lpOut++ = clr1;
443 *ppOut = lpOut;
445 return x;
448 static INT MSRLE32_CompressRLE8Line(CodecInfo *pi, LPWORD lpP, LPWORD lpC, LPCBITMAPINFOHEADER lpbi, BYTE *lpIn, LONG lDist, INT x, LPBYTE *ppOut, DWORD *lpSizeImage)
450 LPBYTE lpOut = *ppOut;
451 INT count, pos;
452 WORD clr;
454 assert(lpbi->biBitCount <= 8);
455 assert(lpbi->biCompression == BI_RGB);
457 /* try to encode as much as possible */
458 pos = x;
459 clr = lpC[pos++];
460 for (count = 1; pos < lpbi->biWidth; count++) {
461 if (ColorCmp(clr, lpC[pos++]) > lDist)
462 break;
465 if (count < 2) {
466 /* add some more pixels for absoluting if possible */
467 count += countDiffRLE8(lpP, lpC - 1, lpC, pos-1, lDist, lpbi->biWidth);
469 /* check for over end of line */
470 if (x + count > lpbi->biWidth)
471 count = lpbi->biWidth - x;
473 /* absolute pixel(s) in groups of atleast 3 and maximal 255 pixels */
474 while (count > 2) {
475 INT i;
476 INT size = min(count, 255);
477 BOOL extra_byte = size % 2;
479 *lpSizeImage += 2 + size + extra_byte;
480 count -= size;
481 *lpOut++ = 0;
482 *lpOut++ = size;
483 for (i = 0; i < size; i++) {
484 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
485 x++;
487 if (extra_byte)
488 *lpOut++ = 0;
490 if (count > 0) {
491 /* too less for absoluting so we must encode them even if it's expensive! */
492 assert(count <= 2);
494 *lpSizeImage += 2 * count;
495 *lpOut++ = 1;
496 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
497 x++;
499 if (count == 2) {
500 *lpOut++ = 1;
501 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
502 x++;
505 } else {
506 /* encode count pixel(s) */
507 clr = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
509 /* optimize end of line */
510 if (x + count + 1 == lpbi->biWidth)
511 count++;
513 x += count;
514 while (count > 0) {
515 INT size = min(count, 255);
517 *lpSizeImage += 2;
518 count -= size;
519 *lpOut++ = size;
520 *lpOut++ = clr;
524 *ppOut = lpOut;
526 return x;
529 LRESULT MSRLE32_CompressRLE4(CodecInfo *pi, LPBITMAPINFOHEADER lpbiIn, LPBYTE lpIn, LPBITMAPINFOHEADER lpbiOut, LPBYTE lpOut, BOOL isKey)
531 LPWORD lpC;
532 LONG lLine, lInLine, lDist;
534 /* pre-conditions */
535 assert(pi != NULL && lpbiOut != NULL);
536 assert(lpIn != NULL && lpOut != NULL);
537 assert(pi->pCurFrame != NULL);
539 lpC = pi->pCurFrame;
540 lDist = QUALITY_to_DIST(pi->dwQuality);
541 lInLine = DIBWIDTHBYTES(*lpbiIn);
542 lLine = WIDTHBYTES(lpbiOut->biWidth * 16) / 2;
544 lpbiOut->biSizeImage = 0;
545 if (isKey) {
546 /* keyframe -- convert internal frame to output format */
547 INT x, y;
549 for (y = 0; y < lpbiOut->biHeight; y++) {
550 x = 0;
552 do {
553 x = MSRLE32_CompressRLE4Line(pi, NULL, lpC, lpbiIn, lpIn, lDist, x,
554 &lpOut, &lpbiOut->biSizeImage);
555 } while (x < lpbiOut->biWidth);
557 lpC += lLine;
558 lpIn += lInLine;
560 /* add EOL -- end of line */
561 lpbiOut->biSizeImage += 2;
562 *((LPWORD)lpOut)++ = 0;
564 } else {
565 /* delta-frame -- compute delta between last and this internal frame */
566 LPWORD lpP;
567 INT x, y;
568 INT jumpx, jumpy;
570 assert(pi->pPrevFrame != NULL);
572 lpP = pi->pPrevFrame;
573 jumpy = 0;
574 jumpx = -1;
576 for (y = 0; y < lpbiOut->biHeight; y++) {
577 x = 0;
579 do {
580 INT count, pos;
582 if (jumpx == -1)
583 jumpx = x;
584 for (count = 0, pos = x; pos < lpbiOut->biWidth; pos++, count++) {
585 if (ColorCmp(lpP[pos], lpC[pos]) > lDist)
586 break;
589 if (pos == lpbiOut->biWidth && count > 8) {
590 /* (count > 8) secures that we will save space */
591 jumpy++;
592 break;
593 } else if (jumpy || jumpx != pos) {
594 /* time to jump */
595 assert(jumpx != -1);
597 if (pos < jumpx) {
598 /* can only jump in positive direction -- jump until EOL, EOL */
599 INT w = lpbiOut->biWidth - jumpx;
601 assert(jumpy > 0);
602 assert(w >= 4);
604 jumpx = 0;
605 jumpy--;
606 /* if (w % 255 == 2) then equal costs
607 * else if (w % 255 < 4 && we could encode all) then 2 bytes too expensive
608 * else it will be cheaper
610 while (w > 0) {
611 lpbiOut->biSizeImage += 4;
612 *lpOut++ = 0;
613 *lpOut++ = 2;
614 *lpOut = min(w, 255);
615 w -= *lpOut++;
616 *lpOut++ = 0;
618 /* add EOL -- end of line */
619 lpbiOut->biSizeImage += 2;
620 *((LPWORD)lpOut)++ = 0;
623 /* FIXME: if (jumpy == 0 && could encode all) then jump too expensive */
625 /* write out real jump(s) */
626 while (jumpy || pos != jumpx) {
627 lpbiOut->biSizeImage += 4;
628 *lpOut++ = 0;
629 *lpOut++ = 2;
630 *lpOut = min(pos - jumpx, 255);
631 x += *lpOut;
632 jumpx += *lpOut++;
633 *lpOut = min(jumpy, 255);
634 jumpy -= *lpOut++;
637 jumpy = 0;
640 jumpx = -1;
642 if (x < lpbiOut->biWidth) {
643 /* skipped the 'same' things corresponding to previous frame */
644 x = MSRLE32_CompressRLE4Line(pi, lpP, lpC, lpbiIn, lpIn, lDist, x,
645 &lpOut, &lpbiOut->biSizeImage);
647 } while (x < lpbiOut->biWidth);
649 lpP += lLine;
650 lpC += lLine;
651 lpIn += lInLine;
653 if (jumpy == 0) {
654 assert(jumpx == -1);
656 /* add EOL -- end of line */
657 lpbiOut->biSizeImage += 2;
658 *((LPWORD)lpOut)++ = 0;
663 /* add EOI -- end of image */
664 lpbiOut->biSizeImage += 2;
665 *lpOut++ = 0;
666 *lpOut++ = 1;
668 return ICERR_OK;
671 LRESULT MSRLE32_CompressRLE8(CodecInfo *pi, LPBITMAPINFOHEADER lpbiIn, LPBYTE lpIn, LPBITMAPINFOHEADER lpbiOut, LPBYTE lpOut, BOOL isKey)
673 LPWORD lpC;
674 LONG lDist, lInLine, lLine;
676 assert(pi != NULL && lpbiOut != NULL);
677 assert(lpIn != NULL && lpOut != NULL);
678 assert(pi->pCurFrame != NULL);
680 lpC = pi->pCurFrame;
681 lDist = QUALITY_to_DIST(pi->dwQuality);
682 lInLine = DIBWIDTHBYTES(*lpbiIn);
683 lLine = WIDTHBYTES(lpbiOut->biWidth * 16) / 2;
685 lpbiOut->biSizeImage = 0;
686 if (isKey) {
687 /* keyframe -- convert internal frame to output format */
688 INT x, y;
690 for (y = 0; y < lpbiOut->biHeight; y++) {
691 x = 0;
693 do {
694 x = MSRLE32_CompressRLE8Line(pi, NULL, lpC, lpbiIn, lpIn, lDist, x,
695 &lpOut, &lpbiOut->biSizeImage);
696 } while (x < lpbiOut->biWidth);
698 lpC += lLine;
699 lpIn += lInLine;
701 /* add EOL -- end of line */
702 lpbiOut->biSizeImage += 2;
703 *((LPWORD)lpOut)++ = 0;
705 } else {
706 /* delta-frame -- compute delta between last and this internal frame */
707 LPWORD lpP;
708 INT x, y;
709 INT jumpx, jumpy;
711 assert(pi->pPrevFrame != NULL);
713 lpP = pi->pPrevFrame;
714 jumpx = -1;
715 jumpy = 0;
717 for (y = 0; y < lpbiOut->biHeight; y++) {
718 x = 0;
720 do {
721 INT count, pos;
723 if (jumpx == -1)
724 jumpx = x;
725 for (count = 0, pos = x; pos < lpbiOut->biWidth; pos++, count++) {
726 if (ColorCmp(lpP[pos], lpC[pos]) > lDist)
727 break;
730 if (pos == lpbiOut->biWidth && count > 4) {
731 /* (count > 4) secures that we will save space */
732 jumpy++;
733 break;
734 } else if (jumpy || jumpx != pos) {
735 /* time to jump */
736 assert(jumpx != -1);
738 if (pos < jumpx) {
739 /* can only jump in positive direction -- do a EOL then jump */
740 assert(jumpy > 0);
742 jumpx = 0;
743 jumpy--;
745 /* add EOL -- end of line */
746 lpbiOut->biSizeImage += 2;
747 *((LPWORD)lpOut)++ = 0;
750 /* FIXME: if (jumpy == 0 && could encode all) then jump too expensive */
752 /* write out real jump(s) */
753 while (jumpy || pos != jumpx) {
754 lpbiOut->biSizeImage += 4;
755 *lpOut++ = 0;
756 *lpOut++ = 2;
757 *lpOut = min(pos - jumpx, 255);
758 jumpx += *lpOut++;
759 *lpOut = min(jumpy, 255);
760 jumpy -= *lpOut++;
762 x = pos;
764 jumpy = 0;
767 jumpx = -1;
769 if (x < lpbiOut->biWidth) {
770 /* skip the 'same' things corresponding to previous frame */
771 x = MSRLE32_CompressRLE8Line(pi, lpP, lpC, lpbiIn, lpIn, lDist, x,
772 &lpOut, &lpbiOut->biSizeImage);
774 } while (x < lpbiOut->biWidth);
776 lpP += lLine;
777 lpC += lLine;
778 lpIn += lInLine;
780 if (jumpy == 0) {
781 /* add EOL -- end of line */
782 lpbiOut->biSizeImage += 2;
783 *lpOut++ = 0;
784 *lpOut++ = 0;
789 /* add EOI -- end of image */
790 lpbiOut->biSizeImage += 2;
791 *lpOut++ = 0;
792 *lpOut++ = 1;
794 return ICERR_OK;
797 /*****************************************************************************/
799 static LRESULT MSRLE32_DecompressRLE4(CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
800 LPBYTE lpIn, LPBYTE lpOut)
802 int bytes_per_pixel;
803 int line_size;
804 int pixel_ptr = 0;
805 int i;
806 BOOL bEndFlag = FALSE;
808 assert(pi != NULL);
809 assert(lpbi != NULL && lpbi->biCompression == BI_RGB);
810 assert(lpIn != NULL && lpOut != NULL);
812 bytes_per_pixel = (lpbi->biBitCount + 1) / 8;
813 line_size = DIBWIDTHBYTES(*lpbi);
815 do {
816 BYTE code0, code1;
818 code0 = *lpIn++;
819 code1 = *lpIn++;
821 if (code0 == 0) {
822 int extra_byte;
824 switch (code1) {
825 case 0: /* EOL - end of line */
826 pixel_ptr = 0;
827 lpOut += line_size;
828 break;
829 case 1: /* EOI - end of image */
830 bEndFlag = TRUE;
831 break;
832 case 2: /* skip */
833 pixel_ptr += *lpIn++ * bytes_per_pixel;
834 lpOut += *lpIn++ * line_size;
835 if (pixel_ptr >= lpbi->biWidth * bytes_per_pixel) {
836 pixel_ptr = 0;
837 lpOut += line_size;
839 break;
840 default: /* absolute mode */
841 extra_byte = (code1 / 2) & 0x01;
843 if (pixel_ptr/bytes_per_pixel + code1 > lpbi->biWidth)
844 return ICERR_ERROR;
846 code0 = code1;
847 for (i = 0; i < code0 / 2; i++) {
848 if (bytes_per_pixel == 1) {
849 code1 = lpIn[i];
850 lpOut[pixel_ptr++] = pi->palette_map[(code1 >> 4)];
851 if (2 * i <= code0)
852 lpOut[pixel_ptr++] = pi->palette_map[(code1 & 0x0F)];
853 } else if (bytes_per_pixel == 2) {
854 code1 = lpIn[i] >> 4;
855 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
856 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
858 if (2 * i <= code0) {
859 code1 = lpIn[i] & 0x0F;
860 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
861 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
863 } else {
864 code1 = lpIn[i] >> 4;
865 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
866 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
867 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
868 pixel_ptr += bytes_per_pixel;
870 if (2 * i <= code0) {
871 code1 = lpIn[i] & 0x0F;
872 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
873 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
874 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
875 pixel_ptr += bytes_per_pixel;
879 lpIn += code0 / 2;
881 /* if the RLE code is odd, skip a byte in the stream */
882 if (extra_byte)
883 lpIn++;
885 } else {
886 /* coded mode */
887 if (pixel_ptr/bytes_per_pixel + code0 > lpbi->biWidth)
888 return ICERR_ERROR;
890 if (bytes_per_pixel == 1) {
891 BYTE c1 = pi->palette_map[(code1 >> 4)];
892 BYTE c2 = pi->palette_map[(code1 & 0x0F)];
894 for (i = 0; i < code0; i++) {
895 if ((i & 1) == 0)
896 lpOut[pixel_ptr++] = c1;
897 else
898 lpOut[pixel_ptr++] = c2;
900 } else if (bytes_per_pixel == 2) {
901 BYTE hi1 = pi->palette_map[(code1 >> 4) * 2 + 0];
902 BYTE lo1 = pi->palette_map[(code1 >> 4) * 2 + 1];
904 BYTE hi2 = pi->palette_map[(code1 & 0x0F) * 2 + 0];
905 BYTE lo2 = pi->palette_map[(code1 & 0x0F) * 2 + 1];
907 for (i = 0; i < code0; i++) {
908 if ((i & 1) == 0) {
909 lpOut[pixel_ptr++] = hi1;
910 lpOut[pixel_ptr++] = lo1;
911 } else {
912 lpOut[pixel_ptr++] = hi2;
913 lpOut[pixel_ptr++] = lo2;
916 } else {
917 BYTE b1 = pi->palette_map[(code1 >> 4) * 4 + 0];
918 BYTE g1 = pi->palette_map[(code1 >> 4) * 4 + 1];
919 BYTE r1 = pi->palette_map[(code1 >> 4) * 4 + 2];
921 BYTE b2 = pi->palette_map[(code1 & 0x0F) * 4 + 0];
922 BYTE g2 = pi->palette_map[(code1 & 0x0F) * 4 + 1];
923 BYTE r2 = pi->palette_map[(code1 & 0x0F) * 4 + 2];
925 for (i = 0; i < code0; i++) {
926 if ((i & 1) == 0) {
927 lpOut[pixel_ptr + 0] = b1;
928 lpOut[pixel_ptr + 1] = g1;
929 lpOut[pixel_ptr + 2] = r1;
930 } else {
931 lpOut[pixel_ptr + 0] = b2;
932 lpOut[pixel_ptr + 1] = g2;
933 lpOut[pixel_ptr + 2] = r2;
935 pixel_ptr += bytes_per_pixel;
939 } while (! bEndFlag);
941 return ICERR_OK;
944 static LRESULT MSRLE32_DecompressRLE8(CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
945 LPBYTE lpIn, LPBYTE lpOut)
947 int bytes_per_pixel;
948 int line_size;
949 int pixel_ptr = 0;
950 BOOL bEndFlag = FALSE;
952 assert(pi != NULL);
953 assert(lpbi != NULL && lpbi->biCompression == BI_RGB);
954 assert(lpIn != NULL && lpOut != NULL);
956 bytes_per_pixel = (lpbi->biBitCount + 1) / 8;
957 line_size = DIBWIDTHBYTES(*lpbi);
959 do {
960 BYTE code0, code1;
962 code0 = *lpIn++;
963 code1 = *lpIn++;
965 if (code0 == 0) {
966 int extra_byte;
968 switch (code1) {
969 case 0: /* EOL - end of line */
970 pixel_ptr = 0;
971 lpOut += line_size;
972 break;
973 case 1: /* EOI - end of image */
974 bEndFlag = TRUE;
975 break;
976 case 2: /* skip */
977 pixel_ptr += *lpIn++ * bytes_per_pixel;
978 lpOut += *lpIn++ * line_size;
979 if (pixel_ptr >= lpbi->biWidth * bytes_per_pixel) {
980 pixel_ptr = 0;
981 lpOut += line_size;
983 break;
984 default: /* absolute mode */
985 if (pixel_ptr/bytes_per_pixel + code1 > lpbi->biWidth) {
986 WARN("aborted absolute: (%d=%d/%d+%d) > %ld\n",pixel_ptr/bytes_per_pixel + code1,pixel_ptr,bytes_per_pixel,code1,lpbi->biWidth);
987 return ICERR_ERROR;
989 extra_byte = code1 & 0x01;
991 code0 = code1;
992 while (code0--) {
993 code1 = *lpIn++;
994 if (bytes_per_pixel == 1) {
995 lpOut[pixel_ptr] = pi->palette_map[code1];
996 } else if (bytes_per_pixel == 2) {
997 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 2 + 0];
998 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 2 + 1];
999 } else {
1000 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
1001 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
1002 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
1004 pixel_ptr += bytes_per_pixel;
1007 /* if the RLE code is odd, skip a byte in the stream */
1008 if (extra_byte)
1009 lpIn++;
1011 } else {
1012 /* coded mode */
1013 if (pixel_ptr/bytes_per_pixel + code0 > lpbi->biWidth) {
1014 WARN("aborted coded: (%d=%d/%d+%d) > %ld\n",pixel_ptr/bytes_per_pixel + code1,pixel_ptr,bytes_per_pixel,code1,lpbi->biWidth);
1015 return ICERR_ERROR;
1018 if (bytes_per_pixel == 1) {
1019 code1 = pi->palette_map[code1];
1020 while (code0--)
1021 lpOut[pixel_ptr++] = code1;
1022 } else if (bytes_per_pixel == 2) {
1023 BYTE hi = pi->palette_map[code1 * 2 + 0];
1024 BYTE lo = pi->palette_map[code1 * 2 + 1];
1026 while (code0--) {
1027 lpOut[pixel_ptr + 0] = hi;
1028 lpOut[pixel_ptr + 1] = lo;
1029 pixel_ptr += bytes_per_pixel;
1031 } else {
1032 BYTE r = pi->palette_map[code1 * 4 + 2];
1033 BYTE g = pi->palette_map[code1 * 4 + 1];
1034 BYTE b = pi->palette_map[code1 * 4 + 0];
1036 while (code0--) {
1037 lpOut[pixel_ptr + 0] = b;
1038 lpOut[pixel_ptr + 1] = g;
1039 lpOut[pixel_ptr + 2] = r;
1040 pixel_ptr += bytes_per_pixel;
1044 } while (! bEndFlag);
1046 return ICERR_OK;
1049 /*****************************************************************************/
1051 static CodecInfo* Open(LPICOPEN icinfo)
1053 CodecInfo* pi = NULL;
1055 if (icinfo == NULL) {
1056 TRACE("(NULL)\n");
1057 return (LPVOID)0xFFFF0000;
1060 TRACE("(%p = {%lu,0x%08lX(%4.4s),0x%08lX(%4.4s),0x%lX,0x%lX,...})\n", icinfo,
1061 icinfo->dwSize, icinfo->fccType, (char*)&icinfo->fccType,
1062 icinfo->fccHandler, (char*)&icinfo->fccHandler,
1063 icinfo->dwVersion,icinfo->dwFlags);
1065 if (icinfo->fccType != ICTYPE_VIDEO)
1066 return NULL;
1068 switch (icinfo->fccHandler) {
1069 case FOURCC_RLE:
1070 case FOURCC_RLE4:
1071 case FOURCC_RLE8:
1072 case FOURCC_MRLE:
1073 break;
1074 case mmioFOURCC('m','r','l','e'):
1075 icinfo->fccHandler = FOURCC_MRLE;
1076 break;
1077 default:
1078 WARN("unknown FOURCC = 0x%08lX(%4.4s) !\n",
1079 icinfo->fccHandler,(char*)&icinfo->fccHandler);
1080 return NULL;
1083 pi = (CodecInfo*)LocalAlloc(LPTR, sizeof(CodecInfo));
1085 if (pi != NULL) {
1086 pi->fccHandler = icinfo->fccHandler;
1088 pi->bCompress = FALSE;
1089 pi->dwQuality = MSRLE32_DEFAULTQUALITY;
1090 pi->nPrevFrame = -1;
1091 pi->pPrevFrame = pi->pCurFrame = NULL;
1093 pi->bDecompress = FALSE;
1094 pi->palette_map = NULL;
1097 icinfo->dwError = (pi != NULL ? ICERR_OK : ICERR_MEMORY);
1099 return pi;
1102 static LRESULT Close(CodecInfo *pi)
1104 TRACE("(%p)\n", pi);
1106 /* pre-condition */
1107 assert(pi != NULL);
1109 if (pi->pPrevFrame != NULL || pi->pCurFrame != NULL)
1110 CompressEnd(pi);
1112 LocalFree((HLOCAL)pi);
1113 return 1;
1116 static LRESULT GetInfo(CodecInfo *pi, ICINFO *icinfo, DWORD dwSize)
1118 /* pre-condition */
1119 assert(pi != NULL);
1121 /* check parameters */
1122 if (icinfo == NULL)
1123 return sizeof(ICINFO);
1124 if (dwSize < sizeof(ICINFO))
1125 return 0;
1127 icinfo->dwSize = sizeof(ICINFO);
1128 icinfo->fccType = streamtypeVIDEO;
1129 icinfo->fccHandler = (pi != NULL ? pi->fccHandler : FOURCC_MRLE);
1130 icinfo->dwFlags = VIDCF_QUALITY | VIDCF_TEMPORAL | VIDCF_CRUNCH | VIDCF_FASTTEMPORALC;
1131 icinfo->dwVersion = MSRLE32_VERSION;
1132 icinfo->dwVersionICM = 0x01040000; /* Version 1.4 build 0 */
1134 LoadWideString(IDS_NAME, icinfo->szName, sizeof(icinfo->szName));
1135 LoadWideString(IDS_DESCRIPTION, icinfo->szDescription, sizeof(icinfo->szDescription));
1137 return sizeof(ICINFO);
1140 static LRESULT SetQuality(CodecInfo *pi, LONG lQuality)
1142 /* pre-condition */
1143 assert(pi != NULL);
1145 if (lQuality == -1)
1146 lQuality = MSRLE32_DEFAULTQUALITY;
1147 else if (ICQUALITY_LOW > lQuality || lQuality > ICQUALITY_HIGH)
1148 return ICERR_BADPARAM;
1150 pi->dwQuality = (DWORD)lQuality;
1152 return ICERR_OK;
1155 static LRESULT Configure(CodecInfo *pi, HWND hWnd)
1157 /* pre-condition */
1158 assert(pi != NULL);
1160 /* FIXME */
1161 return ICERR_OK;
1164 static LRESULT About(CodecInfo *pi, HWND hWnd)
1166 CHAR szTitle[20];
1167 CHAR szAbout[128];
1169 /* pre-condition */
1170 assert(MSRLE32_hModule != 0);
1172 LoadStringA(MSRLE32_hModule, IDS_NAME, szTitle, sizeof(szTitle));
1173 LoadStringA(MSRLE32_hModule, IDS_ABOUT, szAbout, sizeof(szAbout));
1175 MessageBoxA(hWnd, szAbout, szTitle, MB_OK|MB_ICONINFORMATION);
1177 return ICERR_OK;
1180 static LRESULT CompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1181 LPBITMAPINFOHEADER lpbiOut)
1183 LRESULT size;
1185 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1187 /* pre-condition */
1188 assert(pi != NULL);
1190 /* check parameters -- need atleast input format */
1191 if (lpbiIn == NULL) {
1192 if (lpbiOut != NULL)
1193 return ICERR_BADPARAM;
1194 return 0;
1197 /* handle unsupported input format */
1198 if (CompressQuery(pi, lpbiIn, NULL) != ICERR_OK)
1199 return (lpbiOut == NULL ? ICERR_BADFORMAT : 0);
1201 assert(0 < lpbiIn->biBitCount && lpbiIn->biBitCount <= 8);
1203 switch (pi->fccHandler) {
1204 case FOURCC_RLE4:
1205 size = 1 << 4;
1206 break;
1207 case FOURCC_RLE8:
1208 size = 1 << 8;
1209 break;
1210 case FOURCC_RLE:
1211 case FOURCC_MRLE:
1212 size = (lpbiIn->biBitCount <= 4 ? 1 << 4 : 1 << 8);
1213 break;
1214 default:
1215 return ICERR_ERROR;
1218 if (lpbiIn->biClrUsed != 0)
1219 size = lpbiIn->biClrUsed;
1221 size = sizeof(BITMAPINFOHEADER) + size * sizeof(RGBQUAD);
1223 if (lpbiOut != NULL) {
1224 lpbiOut->biSize = sizeof(BITMAPINFOHEADER);
1225 lpbiOut->biWidth = lpbiIn->biWidth;
1226 lpbiOut->biHeight = lpbiIn->biHeight;
1227 lpbiOut->biPlanes = 1;
1228 if (pi->fccHandler == FOURCC_RLE4 ||
1229 lpbiIn->biBitCount <= 4) {
1230 lpbiOut->biCompression = BI_RLE4;
1231 lpbiOut->biBitCount = 4;
1232 } else {
1233 lpbiOut->biCompression = BI_RLE8;
1234 lpbiOut->biBitCount = 8;
1236 lpbiOut->biSizeImage = MSRLE32_GetMaxCompressedSize(lpbiOut);
1237 lpbiOut->biXPelsPerMeter = lpbiIn->biXPelsPerMeter;
1238 lpbiOut->biYPelsPerMeter = lpbiIn->biYPelsPerMeter;
1239 if (lpbiIn->biClrUsed == 0)
1240 size = 1<<lpbiIn->biBitCount;
1241 else
1242 size = lpbiIn->biClrUsed;
1243 lpbiOut->biClrUsed = min(size, 1u << lpbiOut->biBitCount);
1244 lpbiOut->biClrImportant = 0;
1246 memcpy((LPBYTE)lpbiOut + lpbiOut->biSize,
1247 (LPBYTE)lpbiIn + lpbiIn->biSize, lpbiOut->biClrUsed * sizeof(RGBQUAD));
1249 return ICERR_OK;
1250 } else
1251 return size;
1254 static LRESULT CompressGetSize(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1255 LPCBITMAPINFOHEADER lpbiOut)
1257 /* pre-condition */
1258 assert(pi != NULL);
1260 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1262 /* check parameter -- need atleast one format */
1263 if (lpbiIn == NULL && lpbiOut == NULL)
1264 return 0;
1265 /* check if the given format is supported */
1266 if (CompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1267 return 0;
1269 /* the worst case is coding the complete image in absolute mode. */
1270 if (lpbiIn)
1271 return MSRLE32_GetMaxCompressedSize(lpbiIn);
1272 else
1273 return MSRLE32_GetMaxCompressedSize(lpbiOut);
1276 static LRESULT CompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1277 LPCBITMAPINFOHEADER lpbiOut)
1279 /* pre-condition */
1280 assert(pi != NULL);
1282 /* need atleast one format */
1283 if (lpbiIn == NULL && lpbiOut == NULL)
1284 return ICERR_BADPARAM;
1286 /* check input format if given */
1287 if (lpbiIn != NULL) {
1288 if (!isSupportedDIB(lpbiIn))
1289 return ICERR_BADFORMAT;
1291 /* for 4-bit need an even width */
1292 if (lpbiIn->biBitCount <= 4 && (lpbiIn->biWidth % 2))
1293 return ICERR_BADFORMAT;
1295 if (pi->fccHandler == FOURCC_RLE4 && lpbiIn->biBitCount > 4)
1296 return ICERR_UNSUPPORTED;
1297 else if (lpbiIn->biBitCount > 8)
1298 return ICERR_UNSUPPORTED;
1301 /* check output format if given */
1302 if (lpbiOut != NULL) {
1303 if (!isSupportedMRLE(lpbiOut))
1304 return ICERR_BADFORMAT;
1306 if (lpbiIn != NULL) {
1307 if (lpbiIn->biWidth != lpbiOut->biWidth)
1308 return ICERR_UNSUPPORTED;
1309 if (lpbiIn->biHeight != lpbiOut->biHeight)
1310 return ICERR_UNSUPPORTED;
1311 if (lpbiIn->biBitCount > lpbiOut->biBitCount)
1312 return ICERR_UNSUPPORTED;
1316 return ICERR_OK;
1319 static LRESULT CompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1320 LPCBITMAPINFOHEADER lpbiOut)
1322 RGBQUAD *rgbIn;
1323 RGBQUAD *rgbOut;
1324 int i;
1325 size_t size;
1327 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1329 /* pre-condition */
1330 assert(pi != NULL);
1332 /* check parameters -- need both formats */
1333 if (lpbiIn == NULL || lpbiOut == NULL)
1334 return ICERR_BADPARAM;
1335 /* And both must be supported */
1336 if (CompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1337 return ICERR_BADFORMAT;
1339 /* FIXME: cannot compress and decompress at a time! */
1340 if (pi->bDecompress) {
1341 FIXME("cannot compress and decompress at same time!\n");
1342 return ICERR_ERROR;
1345 if (pi->bCompress)
1346 CompressEnd(pi);
1348 size = WIDTHBYTES(lpbiOut->biWidth * 16) / 2 * lpbiOut->biHeight;
1349 pi->pPrevFrame = (LPWORD)GlobalAllocPtr(GPTR, size * sizeof(WORD));
1350 if (pi->pPrevFrame == NULL)
1351 return ICERR_MEMORY;
1352 pi->pCurFrame = (LPWORD)GlobalAllocPtr(GPTR, size * sizeof(WORD));
1353 if (pi->pCurFrame == NULL) {
1354 CompressEnd(pi);
1355 return ICERR_MEMORY;
1357 pi->nPrevFrame = -1;
1358 pi->bCompress = TRUE;
1360 rgbIn = (RGBQUAD*)((LPBYTE)lpbiIn + lpbiIn->biSize);
1361 rgbOut = (RGBQUAD*)((LPBYTE)lpbiOut + lpbiOut->biSize);
1363 switch (lpbiOut->biBitCount) {
1364 case 4:
1365 case 8:
1366 pi->palette_map = (LPBYTE)LocalAlloc(LPTR, lpbiIn->biClrUsed);
1367 if (pi->palette_map == NULL) {
1368 CompressEnd(pi);
1369 return ICERR_MEMORY;
1372 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1373 pi->palette_map[i] = MSRLE32_GetNearestPaletteIndex(lpbiOut->biClrUsed, rgbOut, rgbIn[i]);
1375 break;
1378 return ICERR_OK;
1381 static LRESULT Compress(CodecInfo *pi, ICCOMPRESS* lpic, DWORD dwSize)
1383 int i;
1385 TRACE("(%p,%p,%lu)\n",pi,lpic,dwSize);
1387 /* pre-condition */
1388 assert(pi != NULL);
1390 /* check parameters */
1391 if (lpic == NULL || dwSize < sizeof(ICCOMPRESS))
1392 return ICERR_BADPARAM;
1393 if (!lpic->lpbiOutput || !lpic->lpOutput ||
1394 !lpic->lpbiInput || !lpic->lpInput)
1395 return ICERR_BADPARAM;
1397 TRACE("lpic={0x%lX,%p,%p,%p,%p,%p,%p,%ld,%lu,%lu,%p,%p}\n",lpic->dwFlags,lpic->lpbiOutput,lpic->lpOutput,lpic->lpbiInput,lpic->lpInput,lpic->lpckid,lpic->lpdwFlags,lpic->lFrameNum,lpic->dwFrameSize,lpic->dwQuality,lpic->lpbiPrev,lpic->lpPrev);
1399 if (! pi->bCompress) {
1400 LRESULT hr = CompressBegin(pi, lpic->lpbiInput, lpic->lpbiOutput);
1401 if (hr != ICERR_OK)
1402 return hr;
1403 } else if (CompressQuery(pi, lpic->lpbiInput, lpic->lpbiOutput) != ICERR_OK)
1404 return ICERR_BADFORMAT;
1406 if (lpic->lFrameNum >= pi->nPrevFrame + 1) {
1407 /* we continue in the sequence so we need to initialize
1408 * our internal framedata */
1410 computeInternalFrame(pi, lpic->lpbiInput, lpic->lpInput);
1411 } else if (lpic->lFrameNum == pi->nPrevFrame) {
1412 /* Oops, compress same frame again ? Okay, as you wish.
1413 * No need to recompute internal framedata, because we only swapped buffers */
1414 LPWORD pTmp = pi->pPrevFrame;
1416 FIXME(": prev=%ld cur=%ld swap\n",pi->nPrevFrame,lpic->lFrameNum);
1417 pi->pPrevFrame = pi->pCurFrame;
1418 pi->pCurFrame = pTmp;
1419 } else if ((lpic->dwFlags & ICCOMPRESS_KEYFRAME) == 0) {
1420 LPWORD pTmp;
1422 WARN(": prev=%ld cur=%ld gone back? -- untested",pi->nPrevFrame,lpic->lFrameNum);
1423 if (lpic->lpbiPrev == NULL || lpic->lpPrev == NULL)
1424 return ICERR_GOTOKEYFRAME; /* Need a keyframe if you go back */
1425 if (CompressQuery(pi, lpic->lpbiPrev, lpic->lpbiOutput) != ICERR_OK)
1426 return ICERR_BADFORMAT;
1428 WARN(": prev=%ld cur=%ld compute swapped -- untested\n",pi->nPrevFrame,lpic->lFrameNum);
1429 computeInternalFrame(pi, lpic->lpbiPrev, lpic->lpPrev);
1431 /* swap buffers for current and previous frame */
1432 /* Don't free and alloc new -- costs to much time and they are of equal size ! */
1433 pTmp = pi->pPrevFrame;
1434 pi->pPrevFrame = pi->pCurFrame;
1435 pi->pCurFrame = pTmp;
1436 pi->nPrevFrame = lpic->lFrameNum;
1439 for (i = 0; i < 3; i++) {
1440 SetQuality(pi, lpic->dwQuality);
1442 lpic->lpbiOutput->biSizeImage = 0;
1444 if (lpic->lpbiOutput->biBitCount == 4)
1445 MSRLE32_CompressRLE4(pi, lpic->lpbiInput, (LPBYTE)lpic->lpInput,
1446 lpic->lpbiOutput, (LPBYTE)lpic->lpOutput, TRUE);
1447 /*MSRLE32_CompressRLE4(pi, lpic->lpbiInput, (LPBYTE)lpic->lpInput,
1448 lpic->lpbiOutput, (LPBYTE)lpic->lpOutput, (lpic->dwFlags & ICCOMPRESS_KEYFRAME) != 0);*/
1449 else
1450 MSRLE32_CompressRLE8(pi, lpic->lpbiInput, (LPBYTE)lpic->lpInput,
1451 lpic->lpbiOutput, (LPBYTE)lpic->lpOutput, (lpic->dwFlags & ICCOMPRESS_KEYFRAME) != 0);
1453 if (lpic->dwFrameSize == 0 ||
1454 lpic->lpbiOutput->biSizeImage < lpic->dwFrameSize)
1455 break;
1457 if ((*lpic->lpdwFlags & ICCOMPRESS_KEYFRAME) == 0) {
1458 if (lpic->lpbiOutput->biBitCount == 4)
1459 MSRLE32_CompressRLE4(pi, lpic->lpbiInput, (LPBYTE)lpic->lpInput,
1460 lpic->lpbiOutput, (LPBYTE)lpic->lpOutput, TRUE);
1461 else
1462 MSRLE32_CompressRLE8(pi, lpic->lpbiInput, (LPBYTE)lpic->lpInput,
1463 lpic->lpbiOutput, (LPBYTE)lpic->lpOutput, TRUE);
1465 if (lpic->dwFrameSize == 0 ||
1466 lpic->lpbiOutput->biSizeImage < lpic->dwFrameSize) {
1467 WARN("switched to keyframe, was small enough!\n");
1468 *lpic->lpdwFlags |= ICCOMPRESS_KEYFRAME;
1469 *lpic->lpckid = MAKEAVICKID(cktypeDIBbits,
1470 StreamFromFOURCC(*lpic->lpckid));
1471 break;
1475 if (lpic->dwQuality < 1000)
1476 break;
1478 lpic->dwQuality -= 1000; /* reduce quality by 10% */
1481 { /* swap buffer for current and previous frame */
1482 /* Don't free and alloc new -- costs to much time and they are of equal size ! */
1483 register LPWORD pTmp = pi->pPrevFrame;
1485 pi->pPrevFrame = pi->pCurFrame;
1486 pi->pCurFrame = pTmp;
1487 pi->nPrevFrame = lpic->lFrameNum;
1490 return ICERR_OK;
1493 static LRESULT CompressEnd(CodecInfo *pi)
1495 TRACE("(%p)\n",pi);
1497 if (pi != NULL) {
1498 if (pi->pPrevFrame != NULL)
1499 GlobalFreePtr(pi->pPrevFrame);
1500 if (pi->pCurFrame != NULL)
1501 GlobalFreePtr(pi->pCurFrame);
1502 pi->pPrevFrame = NULL;
1503 pi->pCurFrame = NULL;
1504 pi->nPrevFrame = -1;
1505 pi->bCompress = FALSE;
1508 return ICERR_OK;
1511 static LRESULT DecompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1512 LPBITMAPINFOHEADER lpbiOut)
1514 int size;
1516 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1518 /* pre-condition */
1519 assert(pi != NULL);
1521 if (lpbiIn == NULL)
1522 return (lpbiOut != NULL ? ICERR_BADPARAM : 0);
1524 if (DecompressQuery(pi, lpbiIn, NULL) != ICERR_OK)
1525 return (lpbiOut != NULL ? ICERR_BADFORMAT : 0);
1527 size = lpbiIn->biSize;
1529 if (lpbiOut != NULL) {
1530 memcpy(lpbiOut, lpbiIn, size);
1531 lpbiOut->biBitCount = 32;
1532 lpbiOut->biCompression = BI_RGB;
1533 lpbiOut->biSizeImage = DIBWIDTHBYTES(*lpbiOut) * lpbiOut->biHeight;
1534 lpbiOut->biClrImportant = 0;
1536 if (lpbiOut->biBitCount <= 8 && lpbiOut->biClrUsed == 0)
1537 lpbiOut->biClrUsed = 1 << lpbiOut->biBitCount;
1538 else
1539 lpbiOut->biClrUsed = 0;
1541 return size;
1542 } else
1543 return size;
1546 static LRESULT DecompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1547 LPCBITMAPINFOHEADER lpbiOut)
1549 LRESULT hr = ICERR_OK;
1551 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1553 /* pre-condition */
1554 assert(pi != NULL);
1556 /* need atleast one format */
1557 if (lpbiIn == NULL && lpbiOut == NULL)
1558 return ICERR_BADPARAM;
1560 /* check input format if given */
1561 if (lpbiIn != NULL) {
1562 if (!isSupportedMRLE(lpbiIn))
1563 return ICERR_BADFORMAT;
1566 /* check output format if given */
1567 if (lpbiOut != NULL) {
1568 if (!isSupportedDIB(lpbiOut))
1569 hr = ICERR_BADFORMAT;
1571 if (lpbiIn != NULL) {
1572 if (lpbiIn->biWidth != lpbiOut->biWidth)
1573 hr = ICERR_UNSUPPORTED;
1574 if (lpbiIn->biHeight != lpbiOut->biHeight)
1575 hr = ICERR_UNSUPPORTED;
1576 if (lpbiIn->biBitCount > lpbiOut->biBitCount)
1577 hr = ICERR_UNSUPPORTED;
1581 return hr;
1584 static LRESULT DecompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1585 LPCBITMAPINFOHEADER lpbiOut)
1587 RGBQUAD *rgbIn;
1588 RGBQUAD *rgbOut;
1589 int i;
1591 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1593 /* pre-condition */
1594 assert(pi != NULL);
1596 /* check parameters */
1597 if (lpbiIn == NULL || lpbiOut == NULL)
1598 return ICERR_BADPARAM;
1599 if (DecompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1600 return ICERR_BADFORMAT;
1602 /* FIXME: cannot compress and decompress at a time! */
1603 if (pi->bCompress) {
1604 FIXME("cannot compress and decompress at same time!\n");
1605 return ICERR_ERROR;
1608 if (pi->bDecompress)
1609 DecompressEnd(pi);
1611 rgbIn = (RGBQUAD*)((LPBYTE)lpbiIn + lpbiIn->biSize);
1612 rgbOut = (RGBQUAD*)((LPBYTE)lpbiOut + lpbiOut->biSize);
1614 switch (lpbiOut->biBitCount) {
1615 case 4:
1616 case 8:
1617 pi->palette_map = (LPBYTE)LocalAlloc(LPTR, lpbiIn->biClrUsed);
1618 if (pi->palette_map == NULL)
1619 return ICERR_MEMORY;
1621 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1622 pi->palette_map[i] = MSRLE32_GetNearestPaletteIndex(lpbiOut->biClrUsed, rgbOut, rgbIn[i]);
1624 break;
1625 case 15:
1626 case 16:
1627 pi->palette_map = (LPBYTE)LocalAlloc(LPTR, lpbiIn->biClrUsed * 2);
1628 if (pi->palette_map == NULL)
1629 return ICERR_MEMORY;
1631 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1632 WORD color;
1634 if (lpbiOut->biBitCount == 15)
1635 color = ((rgbIn[i].rgbRed >> 3) << 10)
1636 | ((rgbIn[i].rgbGreen >> 3) << 5) | (rgbIn[i].rgbBlue >> 3);
1637 else
1638 color = ((rgbIn[i].rgbRed >> 3) << 11)
1639 | ((rgbIn[i].rgbGreen >> 3) << 5) | (rgbIn[i].rgbBlue >> 3);
1641 pi->palette_map[i * 2 + 1] = color >> 8;
1642 pi->palette_map[i * 2 + 0] = color & 0xFF;
1644 break;
1645 case 24:
1646 case 32:
1647 pi->palette_map = (LPBYTE)LocalAlloc(LPTR, lpbiIn->biClrUsed * sizeof(RGBQUAD));
1648 if (pi->palette_map == NULL)
1649 return ICERR_MEMORY;
1650 memcpy(pi->palette_map, rgbIn, lpbiIn->biClrUsed * sizeof(RGBQUAD));
1651 break;
1654 pi->bDecompress = TRUE;
1656 return ICERR_OK;
1659 static LRESULT Decompress(CodecInfo *pi, ICDECOMPRESS *pic, DWORD dwSize)
1661 TRACE("(%p,%p,%lu)\n",pi,pic,dwSize);
1663 /* pre-condition */
1664 assert(pi != NULL);
1666 /* check parameters */
1667 if (pic == NULL)
1668 return ICERR_BADPARAM;
1669 if (pic->lpbiInput == NULL || pic->lpInput == NULL ||
1670 pic->lpbiOutput == NULL || pic->lpOutput == NULL)
1671 return ICERR_BADPARAM;
1673 /* check formats */
1674 if (! pi->bDecompress) {
1675 LRESULT hr = DecompressBegin(pi, pic->lpbiInput, pic->lpbiOutput);
1676 if (hr != ICERR_OK)
1677 return hr;
1678 } else if (DecompressQuery(pi, pic->lpbiInput, pic->lpbiOutput) != ICERR_OK)
1679 return ICERR_BADFORMAT;
1681 assert(pic->lpbiInput->biWidth == pic->lpbiOutput->biWidth);
1682 assert(pic->lpbiInput->biHeight == pic->lpbiOutput->biHeight);
1684 pic->lpbiOutput->biSizeImage = DIBWIDTHBYTES(*pic->lpbiOutput) * pic->lpbiOutput->biHeight;
1685 if (pic->lpbiInput->biBitCount == 4)
1686 return MSRLE32_DecompressRLE4(pi, pic->lpbiOutput, pic->lpInput, pic->lpOutput);
1687 else
1688 return MSRLE32_DecompressRLE8(pi, pic->lpbiOutput, pic->lpInput, pic->lpOutput);
1691 static LRESULT DecompressEnd(CodecInfo *pi)
1693 TRACE("(%p)\n",pi);
1695 /* pre-condition */
1696 assert(pi != NULL);
1698 pi->bDecompress = FALSE;
1700 if (pi->palette_map != NULL) {
1701 LocalFree((HLOCAL)pi->palette_map);
1702 pi->palette_map = NULL;
1705 return ICERR_OK;
1708 static LRESULT DecompressGetPalette(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1709 LPBITMAPINFOHEADER lpbiOut)
1711 int size;
1713 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1715 /* pre-condition */
1716 assert(pi != NULL);
1718 /* check parameters */
1719 if (lpbiIn == NULL || lpbiOut == NULL)
1720 return ICERR_BADPARAM;
1722 if (DecompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1723 return ICERR_BADFORMAT;
1725 if (lpbiOut->biBitCount > 8)
1726 return ICERR_ERROR;
1728 if (lpbiIn->biBitCount <= 8) {
1729 if (lpbiIn->biClrUsed > 0)
1730 size = lpbiIn->biClrUsed;
1731 else
1732 size = (1 << lpbiIn->biBitCount);
1734 lpbiOut->biClrUsed = size;
1736 memcpy((LPBYTE)lpbiOut + lpbiOut->biSize, (LPBYTE)lpbiIn + lpbiIn->biSize, size * sizeof(RGBQUAD));
1737 } /* else could never occur ! */
1739 return ICERR_OK;
1742 /* DriverProc - entry point for an installable driver */
1743 LRESULT CALLBACK MSRLE32_DriverProc(DWORD dwDrvID, HDRVR hDrv, UINT uMsg,
1744 LPARAM lParam1, LPARAM lParam2)
1746 CodecInfo *pi = (CodecInfo*)dwDrvID;
1748 TRACE("(%p,%p,0x%04X,0x%08lX,0x%08lX)\n", (LPVOID)dwDrvID, (LPVOID)hDrv,
1749 uMsg, lParam1, lParam2);
1751 switch (uMsg) {
1752 /* standard driver messages */
1753 case DRV_LOAD:
1754 return DRVCNF_OK;
1755 case DRV_OPEN:
1756 if (lParam2 == 0)
1757 return (LRESULT)0xFFFF0000;
1758 else
1759 return (LRESULT)Open((ICOPEN*)lParam2);
1760 case DRV_CLOSE:
1761 if (dwDrvID != 0xFFFF0000 && (LPVOID)dwDrvID != NULL)
1762 Close(pi);
1763 return DRVCNF_OK;
1764 case DRV_ENABLE:
1765 case DRV_DISABLE:
1766 return DRVCNF_OK;
1767 case DRV_FREE:
1768 return DRVCNF_OK;
1769 case DRV_QUERYCONFIGURE:
1770 return DRVCNF_CANCEL; /* FIXME */
1771 case DRV_CONFIGURE:
1772 return DRVCNF_OK; /* FIXME */
1773 case DRV_INSTALL:
1774 case DRV_REMOVE:
1775 return DRVCNF_OK;
1777 /* installable compression manager messages */
1778 case ICM_CONFIGURE:
1779 FIXME("ICM_CONFIGURE (%ld)\n",lParam1);
1780 if (lParam1 == -1)
1781 return ICERR_UNSUPPORTED; /* FIXME */
1782 else
1783 return Configure(pi, (HWND)lParam1);
1784 case ICM_ABOUT:
1785 if (lParam1 == -1)
1786 return ICERR_OK;
1787 else
1788 return About(pi, (HWND)lParam1);
1789 case ICM_GETSTATE:
1790 case ICM_SETSTATE:
1791 return 0; /* no state */
1792 case ICM_GETINFO:
1793 return GetInfo(pi, (ICINFO*)lParam1, (DWORD)lParam2);
1794 case ICM_GETDEFAULTQUALITY:
1795 if ((LPVOID)lParam1 != NULL) {
1796 *((LPDWORD)lParam1) = MSRLE32_DEFAULTQUALITY;
1797 return ICERR_OK;
1799 break;
1800 case ICM_GETQUALITY:
1801 if ((LPVOID)lParam1 != NULL) {
1802 *((LPDWORD)lParam1) = pi->dwQuality;
1803 return ICERR_OK;
1805 break;
1806 case ICM_SETQUALITY:
1807 return SetQuality(pi, *(LPLONG)lParam1);
1808 break;
1809 case ICM_COMPRESS_GET_FORMAT:
1810 return CompressGetFormat(pi, (LPCBITMAPINFOHEADER)lParam1,
1811 (LPBITMAPINFOHEADER)lParam2);
1812 case ICM_COMPRESS_GET_SIZE:
1813 return CompressGetSize(pi, (LPCBITMAPINFOHEADER)lParam1,
1814 (LPCBITMAPINFOHEADER)lParam2);
1815 case ICM_COMPRESS_QUERY:
1816 return CompressQuery(pi, (LPCBITMAPINFOHEADER)lParam1,
1817 (LPCBITMAPINFOHEADER)lParam2);
1818 case ICM_COMPRESS_BEGIN:
1819 return CompressBegin(pi, (LPCBITMAPINFOHEADER)lParam1,
1820 (LPCBITMAPINFOHEADER)lParam2);
1821 case ICM_COMPRESS:
1822 return Compress(pi, (ICCOMPRESS*)lParam1, (DWORD)lParam2);
1823 case ICM_COMPRESS_END:
1824 return CompressEnd(pi);
1825 case ICM_DECOMPRESS_GET_FORMAT:
1826 return DecompressGetFormat(pi, (LPCBITMAPINFOHEADER)lParam1,
1827 (LPBITMAPINFOHEADER)lParam2);
1828 case ICM_DECOMPRESS_QUERY:
1829 return DecompressQuery(pi, (LPCBITMAPINFOHEADER)lParam1,
1830 (LPCBITMAPINFOHEADER)lParam2);
1831 case ICM_DECOMPRESS_BEGIN:
1832 return DecompressBegin(pi, (LPCBITMAPINFOHEADER)lParam1,
1833 (LPCBITMAPINFOHEADER)lParam2);
1834 case ICM_DECOMPRESS:
1835 return Decompress(pi, (ICDECOMPRESS*)lParam1, (DWORD)lParam2);
1836 case ICM_DECOMPRESS_END:
1837 return DecompressEnd(pi);
1838 case ICM_DECOMPRESS_SET_PALETTE:
1839 FIXME("(...) -> SetPalette(%p,%p,%p): stub!\n", pi, (LPVOID)lParam1, (LPVOID)lParam2);
1840 return ICERR_UNSUPPORTED;
1841 case ICM_DECOMPRESS_GET_PALETTE:
1842 return DecompressGetPalette(pi, (LPBITMAPINFOHEADER)lParam1,
1843 (LPBITMAPINFOHEADER)lParam2);
1844 case ICM_GETDEFAULTKEYFRAMERATE:
1845 if ((LPVOID)lParam1 != NULL)
1846 *(LPDWORD)lParam1 = 15;
1847 return ICERR_OK;
1848 default:
1849 if (uMsg < DRV_USER)
1850 return DefDriverProc(dwDrvID, hDrv, uMsg, lParam1, lParam2);
1851 else
1852 FIXME("Unknown message uMsg=0x%08X lParam1=0x%08lX lParam2=0x%08lX\n",uMsg,lParam1,lParam2);
1855 return ICERR_UNSUPPORTED;
1858 /* DllMain - library initialization code */
1859 BOOL WINAPI MSRLE32_DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
1861 TRACE("(%p,%ld,%p)\n",(LPVOID)hModule,dwReason,lpReserved);
1863 switch (dwReason) {
1864 case DLL_PROCESS_ATTACH:
1865 if (MSRLE32_hModule == 0)
1866 MSRLE32_hModule = hModule;
1867 break;
1868 case DLL_THREAD_ATTACH:
1869 break;
1870 case DLL_THREAD_DETACH:
1871 break;
1872 case DLL_PROCESS_DETACH:
1873 break;
1876 return TRUE;