qcap/tests: Add media tests for the SmartTee filter.
[wine/multimedia.git] / dlls / msrle32 / msrle32.c
blobbd02ae6595de1836722290f17a8b04a859be7686
1 /*
2 * Copyright 2002-2003 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 /* TODO:
20 * - some improvements possible
21 * - implement DecompressSetPalette? -- do we need it for anything?
24 #include <assert.h>
26 #include "msrle_private.h"
28 #include "winnls.h"
29 #include "winuser.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(msrle32);
35 static HINSTANCE MSRLE32_hModule = 0;
37 #define compare_fourcc(fcc1, fcc2) (((fcc1)^(fcc2))&~0x20202020)
39 static inline WORD ColorCmp(WORD clr1, WORD clr2)
41 UINT a = clr1 - clr2;
42 return a * a;
44 static inline WORD Intensity(RGBQUAD clr)
46 return (30 * clr.rgbRed + 59 * clr.rgbGreen + 11 * clr.rgbBlue)/4;
49 #define GetRawPixel(lpbi,lp,x) \
50 ((lpbi)->biBitCount == 1 ? ((lp)[(x)/8] >> (8 - (x)%8)) & 1 : \
51 ((lpbi)->biBitCount == 4 ? ((lp)[(x)/2] >> (4 * (1 - (x)%2))) & 15 : lp[x]))
53 /*****************************************************************************/
55 /* utility functions */
56 static BOOL isSupportedDIB(LPCBITMAPINFOHEADER lpbi);
57 static BOOL isSupportedMRLE(LPCBITMAPINFOHEADER lpbi);
58 static BYTE MSRLE32_GetNearestPaletteIndex(UINT count, const RGBQUAD *clrs, RGBQUAD clr);
60 /* compression functions */
61 static void computeInternalFrame(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn, const BYTE *lpIn);
62 static LONG MSRLE32_GetMaxCompressedSize(LPCBITMAPINFOHEADER lpbi);
63 static LRESULT MSRLE32_CompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
64 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
65 LPBYTE lpOut, BOOL isKey);
66 static LRESULT MSRLE32_CompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
67 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
68 LPBYTE lpOut, BOOL isKey);
70 /* decompression functions */
71 static LRESULT MSRLE32_DecompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
72 const BYTE *lpIn, LPBYTE lpOut);
73 static LRESULT MSRLE32_DecompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
74 const BYTE *lpIn, LPBYTE lpOut);
76 /* API functions */
77 static LRESULT CompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
78 LPBITMAPINFOHEADER lpbiOut);
79 static LRESULT CompressGetSize(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
80 LPCBITMAPINFOHEADER lpbiOut);
81 static LRESULT CompressQuery(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
82 LPCBITMAPINFOHEADER lpbiOut);
83 static LRESULT CompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
84 LPCBITMAPINFOHEADER lpbiOut);
85 static LRESULT Compress(CodecInfo *pi, ICCOMPRESS* lpic, DWORD dwSize);
86 static LRESULT CompressEnd(CodecInfo *pi);
88 static LRESULT DecompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
89 LPBITMAPINFOHEADER lpbiOut);
90 static LRESULT DecompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
91 LPCBITMAPINFOHEADER lpbiOut);
92 static LRESULT DecompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
93 LPCBITMAPINFOHEADER lpbiOut);
94 static LRESULT Decompress(CodecInfo *pi, ICDECOMPRESS *pic, DWORD dwSize);
95 static LRESULT DecompressEnd(CodecInfo *pi);
96 static LRESULT DecompressGetPalette(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
97 LPBITMAPINFOHEADER lpbiOut);
99 /*****************************************************************************/
101 static BOOL isSupportedMRLE(LPCBITMAPINFOHEADER lpbi)
103 /* pre-conditions */
104 assert(lpbi != NULL);
106 if (lpbi->biSize < sizeof(BITMAPINFOHEADER) ||
107 lpbi->biPlanes != 1)
108 return FALSE;
110 if (lpbi->biCompression == BI_RLE4) {
111 if (lpbi->biBitCount != 4 ||
112 (lpbi->biWidth % 2) != 0)
113 return FALSE;
114 } else if (lpbi->biCompression == BI_RLE8) {
115 if (lpbi->biBitCount != 8)
116 return FALSE;
117 } else
118 return FALSE;
120 return TRUE;
123 static BOOL isSupportedDIB(LPCBITMAPINFOHEADER lpbi)
125 /* pre-conditions */
126 assert(lpbi != NULL);
128 /* check structure version/planes/compression */
129 if (lpbi->biSize < sizeof(BITMAPINFOHEADER) ||
130 lpbi->biPlanes != 1)
131 return FALSE;
132 if (lpbi->biCompression != BI_RGB &&
133 lpbi->biCompression != BI_BITFIELDS)
134 return FALSE;
136 /* check bit-depth */
137 if (lpbi->biBitCount != 1 &&
138 lpbi->biBitCount != 4 &&
139 lpbi->biBitCount != 8 &&
140 lpbi->biBitCount != 15 &&
141 lpbi->biBitCount != 16 &&
142 lpbi->biBitCount != 24 &&
143 lpbi->biBitCount != 32)
144 return FALSE;
146 /* check for size(s) */
147 if (!lpbi->biWidth || !lpbi->biHeight)
148 return FALSE; /* image with zero size, makes no sense so error ! */
149 if (DIBWIDTHBYTES(*lpbi) * (DWORD)lpbi->biHeight >= (1UL << 31) - 1)
150 return FALSE; /* image too big ! */
152 /* check for nonexistent colortable for hi- and true-color DIB's */
153 if (lpbi->biBitCount >= 15 && lpbi->biClrUsed > 0)
154 return FALSE;
156 return TRUE;
159 static BYTE MSRLE32_GetNearestPaletteIndex(UINT count, const RGBQUAD *clrs, RGBQUAD clr)
161 INT diff = 0x00FFFFFF;
162 UINT i;
163 UINT idx = 0;
165 /* pre-conditions */
166 assert(clrs != NULL);
168 for (i = 0; i < count; i++) {
169 int r = ((int)clrs[i].rgbRed - (int)clr.rgbRed);
170 int g = ((int)clrs[i].rgbGreen - (int)clr.rgbGreen);
171 int b = ((int)clrs[i].rgbBlue - (int)clr.rgbBlue);
173 r = r*r + g*g + b*b;
175 if (r < diff) {
176 idx = i;
177 diff = r;
178 if (diff == 0)
179 break;
183 return idx;
186 /*****************************************************************************/
188 void computeInternalFrame(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn, const BYTE *lpIn)
190 WORD wIntensityTbl[256];
191 DWORD lInLine, lOutLine;
192 LPWORD lpOut;
193 UINT i;
194 LONG y;
196 /* pre-conditions */
197 assert(pi != NULL && lpbiIn != NULL && lpIn != NULL);
198 assert(pi->pCurFrame != NULL);
200 lInLine = DIBWIDTHBYTES(*lpbiIn);
201 lOutLine = WIDTHBYTES((WORD)lpbiIn->biWidth * 8u * sizeof(WORD)) / 2u;
202 lpOut = pi->pCurFrame;
204 assert(lpbiIn->biClrUsed != 0);
207 const RGBQUAD *lp =
208 (const RGBQUAD *)((const BYTE*)lpbiIn + lpbiIn->biSize);
210 for (i = 0; i < lpbiIn->biClrUsed; i++)
211 wIntensityTbl[i] = Intensity(lp[i]);
214 for (y = 0; y < lpbiIn->biHeight; y++) {
215 LONG x;
217 switch (lpbiIn->biBitCount) {
218 case 1:
219 for (x = 0; x < lpbiIn->biWidth / 8; x++) {
220 for (i = 0; i < 7; i++)
221 lpOut[8 * x + i] = wIntensityTbl[(lpIn[x] >> (7 - i)) & 1];
223 break;
224 case 4:
225 for (x = 0; x < lpbiIn->biWidth / 2; x++) {
226 lpOut[2 * x + 0] = wIntensityTbl[(lpIn[x] >> 4)];
227 lpOut[2 * x + 1] = wIntensityTbl[(lpIn[x] & 0x0F)];
229 break;
230 case 8:
231 for (x = 0; x < lpbiIn->biWidth; x++)
232 lpOut[x] = wIntensityTbl[lpIn[x]];
233 break;
236 lpIn += lInLine;
237 lpOut += lOutLine;
241 static LONG MSRLE32_GetMaxCompressedSize(LPCBITMAPINFOHEADER lpbi)
243 LONG a, b, size;
245 /* pre-condition */
246 assert(lpbi != NULL);
248 a = lpbi->biWidth / 255;
249 b = lpbi->biWidth % 255;
250 if (lpbi->biBitCount <= 4) {
251 a /= 2;
252 b /= 2;
255 size = (2 + a * (2 + ((a + 2) & ~2)) + b * (2 + ((b + 2) & ~2)));
256 return size * lpbi->biHeight + 2;
259 /* lpP => current pos in previous frame
260 * lpA => previous pos in current frame
261 * lpB => current pos in current frame
263 static INT countDiffRLE4(const WORD *lpP, const WORD *lpA, const WORD *lpB, INT pos, LONG lDist, LONG width)
265 INT count;
266 WORD clr1, clr2;
268 /* pre-conditions */
269 assert(lpA && lpB && lDist >= 0 && width > 0);
271 if (pos >= width)
272 return 0;
273 if (pos+1 == width)
274 return 1;
276 clr1 = lpB[pos++];
277 clr2 = lpB[pos];
279 count = 2;
280 while (pos + 1 < width) {
281 WORD clr3, clr4;
283 clr3 = lpB[++pos];
284 if (pos + 1 >= width)
285 return count + 1;
287 clr4 = lpB[++pos];
288 if (ColorCmp(clr1, clr3) <= lDist &&
289 ColorCmp(clr2, clr4) <= lDist) {
290 /* diff at end? -- look-ahead for at least ?? more encodable pixels */
291 if (pos + 2 < width && ColorCmp(clr1,lpB[pos+1]) <= lDist &&
292 ColorCmp(clr2,lpB[pos+2]) <= lDist) {
293 if (pos + 4 < width && ColorCmp(lpB[pos+1],lpB[pos+3]) <= lDist &&
294 ColorCmp(lpB[pos+2],lpB[pos+4]) <= lDist)
295 return count - 3; /* followed by at least 4 encodable pixels */
296 return count - 2;
298 } else if (lpP != NULL && ColorCmp(lpP[pos], lpB[pos]) <= lDist) {
299 /* 'compare' with previous frame for end of diff */
300 INT count2 = 0;
302 /* FIXME */
304 if (count2 >= 8)
305 return count;
307 pos -= count2;
310 count += 2;
311 clr1 = clr3;
312 clr2 = clr4;
315 return count;
318 /* lpP => current pos in previous frame
319 * lpA => previous pos in current frame
320 * lpB => current pos in current frame
322 static INT countDiffRLE8(const WORD *lpP, const WORD *lpA, const WORD *lpB, INT pos, LONG lDist, LONG width)
324 INT count;
326 for (count = 0; pos < width; pos++, count++) {
327 if (ColorCmp(lpA[pos], lpB[pos]) <= lDist) {
328 /* diff at end? -- look-ahead for some more encodable pixel */
329 if (pos + 1 < width && ColorCmp(lpB[pos], lpB[pos+1]) <= lDist)
330 return count - 1;
331 if (pos + 2 < width && ColorCmp(lpB[pos+1], lpB[pos+2]) <= lDist)
332 return count - 1;
333 } else if (lpP != NULL && ColorCmp(lpP[pos], lpB[pos]) <= lDist) {
334 /* 'compare' with previous frame for end of diff */
335 INT count2 = 0;
337 for (count2 = 0, pos++; pos < width && count2 <= 5; pos++, count2++) {
338 if (ColorCmp(lpP[pos], lpB[pos]) > lDist)
339 break;
341 if (count2 > 4)
342 return count;
344 pos -= count2;
348 return count;
351 static INT MSRLE32_CompressRLE4Line(const CodecInfo *pi, const WORD *lpP,
352 const WORD *lpC, LPCBITMAPINFOHEADER lpbi,
353 const BYTE *lpIn, LONG lDist,
354 INT x, LPBYTE *ppOut,
355 DWORD *lpSizeImage)
357 LPBYTE lpOut = *ppOut;
358 INT count, pos;
359 WORD clr1, clr2;
361 /* try to encode as many pixel as possible */
362 count = 1;
363 pos = x;
364 clr1 = lpC[pos++];
365 if (pos < lpbi->biWidth) {
366 clr2 = lpC[pos];
367 for (++count; pos + 1 < lpbi->biWidth; ) {
368 ++pos;
369 if (ColorCmp(clr1, lpC[pos]) > lDist)
370 break;
371 count++;
372 if (pos + 1 >= lpbi->biWidth)
373 break;
374 ++pos;
375 if (ColorCmp(clr2, lpC[pos]) > lDist)
376 break;
377 count++;
381 if (count < 4) {
382 /* add some pixel for absoluting if possible */
383 count += countDiffRLE4(lpP, lpC - 1, lpC, pos-1, lDist, lpbi->biWidth);
385 assert(count > 0);
387 /* check for near end of line */
388 if (x + count > lpbi->biWidth)
389 count = lpbi->biWidth - x;
391 /* absolute pixel(s) in groups of at least 3 and at most 254 pixels */
392 while (count > 2) {
393 INT i;
394 INT size = min(count, 254);
395 int bytes = ((size + 1) & (~1)) / 2;
396 int extra_byte = bytes & 0x01;
398 *lpSizeImage += 2 + bytes + extra_byte;
399 assert(((*lpSizeImage) % 2) == 0);
400 count -= size;
401 *lpOut++ = 0;
402 *lpOut++ = size;
403 for (i = 0; i < size; i += 2) {
404 clr1 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
405 x++;
406 if (i + 1 < size) {
407 clr2 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
408 x++;
409 } else
410 clr2 = 0;
412 *lpOut++ = (clr1 << 4) | clr2;
414 if (extra_byte)
415 *lpOut++ = 0;
418 if (count > 0) {
419 /* too little for absoluting so we must encode them */
420 assert(count <= 2);
422 *lpSizeImage += 2;
423 clr1 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
424 x++;
425 if (count == 2) {
426 clr2 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
427 x++;
428 } else
429 clr2 = 0;
430 *lpOut++ = count;
431 *lpOut++ = (clr1 << 4) | clr2;
433 } else {
434 /* encode count pixel(s) */
435 clr1 = ((pi->palette_map[GetRawPixel(lpbi,lpIn,x)] << 4) |
436 pi->palette_map[GetRawPixel(lpbi,lpIn,x + 1)]);
438 x += count;
439 while (count > 0) {
440 INT size = min(count, 254);
442 *lpSizeImage += 2;
443 count -= size;
444 *lpOut++ = size;
445 *lpOut++ = clr1;
449 *ppOut = lpOut;
451 return x;
454 static INT MSRLE32_CompressRLE8Line(const CodecInfo *pi, const WORD *lpP,
455 const WORD *lpC, LPCBITMAPINFOHEADER lpbi,
456 const BYTE *lpIn, INT x, LPBYTE *ppOut,
457 DWORD *lpSizeImage)
459 LPBYTE lpOut = *ppOut;
460 INT count, pos;
461 WORD clr;
463 assert(lpbi->biBitCount <= 8);
464 assert(lpbi->biCompression == BI_RGB);
466 /* try to encode as much as possible */
467 pos = x;
468 clr = lpC[pos++];
469 for (count = 1; pos < lpbi->biWidth; count++) {
470 if (ColorCmp(clr, lpC[pos++]) > 0)
471 break;
474 if (count < 2) {
475 /* add some more pixels for absoluting if possible */
476 count += countDiffRLE8(lpP, lpC - 1, lpC, pos-1, 0, lpbi->biWidth);
478 assert(count > 0);
480 /* check for over end of line */
481 if (x + count > lpbi->biWidth)
482 count = lpbi->biWidth - x;
484 /* absolute pixel(s) in groups of at least 3 and at most 255 pixels */
485 while (count > 2) {
486 INT i;
487 INT size = min(count, 255);
488 int extra_byte = size % 2;
490 *lpSizeImage += 2 + size + extra_byte;
491 count -= size;
492 *lpOut++ = 0;
493 *lpOut++ = size;
494 for (i = 0; i < size; i++) {
495 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
496 x++;
498 if (extra_byte)
499 *lpOut++ = 0;
501 if (count > 0) {
502 /* too little for absoluting so we must encode them even if it's expensive! */
503 assert(count <= 2);
505 *lpSizeImage += 2 * count;
506 *lpOut++ = 1;
507 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
508 x++;
510 if (count == 2) {
511 *lpOut++ = 1;
512 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
513 x++;
516 } else {
517 /* encode count pixel(s) */
518 clr = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
520 /* optimize end of line */
521 if (x + count + 1 == lpbi->biWidth)
522 count++;
524 x += count;
525 while (count > 0) {
526 INT size = min(count, 255);
528 *lpSizeImage += 2;
529 count -= size;
530 *lpOut++ = size;
531 *lpOut++ = clr;
535 *ppOut = lpOut;
537 return x;
540 LRESULT MSRLE32_CompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
541 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
542 LPBYTE lpOut, BOOL isKey)
544 LPWORD lpC;
545 LONG lLine, lInLine;
546 LPBYTE lpOutStart = lpOut;
548 /* pre-conditions */
549 assert(pi != NULL && lpbiOut != NULL);
550 assert(lpIn != NULL && lpOut != NULL);
551 assert(pi->pCurFrame != NULL);
553 lpC = pi->pCurFrame;
554 lInLine = DIBWIDTHBYTES(*lpbiIn);
555 lLine = WIDTHBYTES(lpbiOut->biWidth * 16) / 2;
557 lpbiOut->biSizeImage = 0;
558 if (isKey) {
559 /* keyframe -- convert internal frame to output format */
560 INT x, y;
562 for (y = 0; y < lpbiOut->biHeight; y++) {
563 x = 0;
565 do {
566 x = MSRLE32_CompressRLE4Line(pi, NULL, lpC, lpbiIn, lpIn, 0, x,
567 &lpOut, &lpbiOut->biSizeImage);
568 } while (x < lpbiOut->biWidth);
570 lpC += lLine;
571 lpIn += lInLine;
573 /* add EOL -- end of line */
574 lpbiOut->biSizeImage += 2;
575 *(LPWORD)lpOut = 0;
576 lpOut += sizeof(WORD);
577 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
579 } else {
580 /* delta-frame -- compute delta between last and this internal frame */
581 LPWORD lpP;
582 INT x, y;
583 INT jumpx, jumpy;
585 assert(pi->pPrevFrame != NULL);
587 lpP = pi->pPrevFrame;
588 jumpy = 0;
589 jumpx = -1;
591 for (y = 0; y < lpbiOut->biHeight; y++) {
592 x = 0;
594 do {
595 INT count, pos;
597 if (jumpx == -1)
598 jumpx = x;
599 for (count = 0, pos = x; pos < lpbiOut->biWidth; pos++, count++) {
600 if (ColorCmp(lpP[pos], lpC[pos]) > 0)
601 break;
604 if (pos == lpbiOut->biWidth && count > 8) {
605 /* (count > 8) secures that we will save space */
606 jumpy++;
607 break;
608 } else if (jumpy || jumpx != pos) {
609 /* time to jump */
610 assert(jumpx != -1);
612 if (pos < jumpx) {
613 /* can only jump in positive direction -- jump until EOL, EOL */
614 INT w = lpbiOut->biWidth - jumpx;
616 assert(jumpy > 0);
617 assert(w >= 4);
619 jumpx = 0;
620 jumpy--;
621 /* if (w % 255 == 2) then equal costs
622 * else if (w % 255 < 4 && we could encode all) then 2 bytes too expensive
623 * else it will be cheaper
625 while (w > 0) {
626 lpbiOut->biSizeImage += 4;
627 *lpOut++ = 0;
628 *lpOut++ = 2;
629 *lpOut = min(w, 255);
630 w -= *lpOut++;
631 *lpOut++ = 0;
633 /* add EOL -- end of line */
634 lpbiOut->biSizeImage += 2;
635 *((LPWORD)lpOut) = 0;
636 lpOut += sizeof(WORD);
639 /* FIXME: if (jumpy == 0 && could encode all) then jump too expensive */
641 /* write out real jump(s) */
642 while (jumpy || pos != jumpx) {
643 lpbiOut->biSizeImage += 4;
644 *lpOut++ = 0;
645 *lpOut++ = 2;
646 *lpOut = min(pos - jumpx, 255);
647 x += *lpOut;
648 jumpx += *lpOut++;
649 *lpOut = min(jumpy, 255);
650 jumpy -= *lpOut++;
653 jumpy = 0;
656 jumpx = -1;
658 if (x < lpbiOut->biWidth) {
659 /* skipped the 'same' things corresponding to previous frame */
660 x = MSRLE32_CompressRLE4Line(pi, lpP, lpC, lpbiIn, lpIn, 0, x,
661 &lpOut, &lpbiOut->biSizeImage);
663 } while (x < lpbiOut->biWidth);
665 lpP += lLine;
666 lpC += lLine;
667 lpIn += lInLine;
669 if (jumpy == 0) {
670 assert(jumpx == -1);
672 /* add EOL -- end of line */
673 lpbiOut->biSizeImage += 2;
674 *((LPWORD)lpOut) = 0;
675 lpOut += sizeof(WORD);
676 assert(lpOut == lpOutStart + lpbiOut->biSizeImage);
680 /* add EOL -- will be changed to EOI */
681 lpbiOut->biSizeImage += 2;
682 *((LPWORD)lpOut) = 0;
683 lpOut += sizeof(WORD);
686 /* change EOL to EOI -- end of image */
687 lpOut[-1] = 1;
688 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
690 return ICERR_OK;
693 LRESULT MSRLE32_CompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
694 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
695 LPBYTE lpOut, BOOL isKey)
697 LPWORD lpC;
698 LONG lInLine, lLine;
699 LPBYTE lpOutStart = lpOut;
701 assert(pi != NULL && lpbiOut != NULL);
702 assert(lpIn != NULL && lpOut != NULL);
703 assert(pi->pCurFrame != NULL);
705 lpC = pi->pCurFrame;
706 lInLine = DIBWIDTHBYTES(*lpbiIn);
707 lLine = WIDTHBYTES(lpbiOut->biWidth * 16) / 2;
709 lpbiOut->biSizeImage = 0;
710 if (isKey) {
711 /* keyframe -- convert internal frame to output format */
712 INT x, y;
714 for (y = 0; y < lpbiOut->biHeight; y++) {
715 x = 0;
717 do {
718 x = MSRLE32_CompressRLE8Line(pi, NULL, lpC, lpbiIn, lpIn, x,
719 &lpOut, &lpbiOut->biSizeImage);
720 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
721 } while (x < lpbiOut->biWidth);
723 lpC += lLine;
724 lpIn += lInLine;
726 /* add EOL -- end of line */
727 lpbiOut->biSizeImage += 2;
728 *((LPWORD)lpOut) = 0;
729 lpOut += sizeof(WORD);
730 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
732 } else {
733 /* delta-frame -- compute delta between last and this internal frame */
734 LPWORD lpP;
735 INT x, y;
736 INT jumpx, jumpy;
738 assert(pi->pPrevFrame != NULL);
740 lpP = pi->pPrevFrame;
741 jumpx = -1;
742 jumpy = 0;
744 for (y = 0; y < lpbiOut->biHeight; y++) {
745 x = 0;
747 do {
748 INT count, pos;
750 if (jumpx == -1)
751 jumpx = x;
752 for (count = 0, pos = x; pos < lpbiOut->biWidth; pos++, count++) {
753 if (ColorCmp(lpP[pos], lpC[pos]) > 0)
754 break;
757 if (pos == lpbiOut->biWidth && count > 4) {
758 /* (count > 4) secures that we will save space */
759 jumpy++;
760 break;
761 } else if (jumpy || jumpx != pos) {
762 /* time to jump */
763 assert(jumpx != -1);
765 if (pos < jumpx) {
766 /* can only jump in positive direction -- do an EOL then jump */
767 assert(jumpy > 0);
769 jumpx = 0;
770 jumpy--;
772 /* add EOL -- end of line */
773 lpbiOut->biSizeImage += 2;
774 *((LPWORD)lpOut) = 0;
775 lpOut += sizeof(WORD);
776 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
779 /* FIXME: if (jumpy == 0 && could encode all) then jump too expensive */
781 /* write out real jump(s) */
782 while (jumpy || pos != jumpx) {
783 lpbiOut->biSizeImage += 4;
784 *lpOut++ = 0;
785 *lpOut++ = 2;
786 *lpOut = min(pos - jumpx, 255);
787 jumpx += *lpOut++;
788 *lpOut = min(jumpy, 255);
789 jumpy -= *lpOut++;
791 x = pos;
793 jumpy = 0;
796 jumpx = -1;
798 if (x < lpbiOut->biWidth) {
799 /* skip the 'same' things corresponding to previous frame */
800 x = MSRLE32_CompressRLE8Line(pi, lpP, lpC, lpbiIn, lpIn, x,
801 &lpOut, &lpbiOut->biSizeImage);
802 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
804 } while (x < lpbiOut->biWidth);
806 lpP += lLine;
807 lpC += lLine;
808 lpIn += lInLine;
810 if (jumpy == 0) {
811 /* add EOL -- end of line */
812 lpbiOut->biSizeImage += 2;
813 *((LPWORD)lpOut) = 0;
814 lpOut += sizeof(WORD);
815 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
819 /* add EOL */
820 lpbiOut->biSizeImage += 2;
821 *((LPWORD)lpOut) = 0;
822 lpOut += sizeof(WORD);
825 /* add EOI -- end of image */
826 lpbiOut->biSizeImage += 2;
827 *lpOut++ = 0;
828 *lpOut++ = 1;
829 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
831 return ICERR_OK;
834 /*****************************************************************************/
836 static LRESULT MSRLE32_DecompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
837 const BYTE *lpIn, LPBYTE lpOut)
839 int bytes_per_pixel;
840 int line_size;
841 int pixel_ptr = 0;
842 int i;
843 BOOL bEndFlag = FALSE;
845 assert(pi != NULL);
846 assert(lpbi != NULL && lpbi->biCompression == BI_RGB);
847 assert(lpIn != NULL && lpOut != NULL);
849 bytes_per_pixel = (lpbi->biBitCount + 1) / 8;
850 line_size = DIBWIDTHBYTES(*lpbi);
852 do {
853 BYTE code0, code1;
855 code0 = *lpIn++;
856 code1 = *lpIn++;
858 if (code0 == 0) {
859 int extra_byte;
861 switch (code1) {
862 case 0: /* EOL - end of line */
863 pixel_ptr = 0;
864 lpOut += line_size;
865 break;
866 case 1: /* EOI - end of image */
867 bEndFlag = TRUE;
868 break;
869 case 2: /* skip */
870 pixel_ptr += *lpIn++ * bytes_per_pixel;
871 lpOut += *lpIn++ * line_size;
872 if (pixel_ptr >= lpbi->biWidth * bytes_per_pixel) {
873 pixel_ptr = 0;
874 lpOut += line_size;
876 break;
877 default: /* absolute mode */
878 extra_byte = (((code1 + 1) & (~1)) / 2) & 0x01;
880 if (pixel_ptr/bytes_per_pixel + code1 > lpbi->biWidth)
881 return ICERR_ERROR;
883 code0 = code1;
884 for (i = 0; i < code0 / 2; i++) {
885 if (bytes_per_pixel == 1) {
886 code1 = lpIn[i];
887 lpOut[pixel_ptr++] = pi->palette_map[(code1 >> 4)];
888 if (2 * i + 1 <= code0)
889 lpOut[pixel_ptr++] = pi->palette_map[(code1 & 0x0F)];
890 } else if (bytes_per_pixel == 2) {
891 code1 = lpIn[i] >> 4;
892 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
893 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
895 if (2 * i + 1 <= code0) {
896 code1 = lpIn[i] & 0x0F;
897 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
898 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
900 } else {
901 code1 = lpIn[i] >> 4;
902 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
903 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
904 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
905 pixel_ptr += bytes_per_pixel;
907 if (2 * i + 1 <= code0) {
908 code1 = lpIn[i] & 0x0F;
909 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
910 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
911 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
912 pixel_ptr += bytes_per_pixel;
916 if (code0 & 0x01) {
917 if (bytes_per_pixel == 1) {
918 code1 = lpIn[i];
919 lpOut[pixel_ptr++] = pi->palette_map[(code1 >> 4)];
920 } else if (bytes_per_pixel == 2) {
921 code1 = lpIn[i] >> 4;
922 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
923 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
924 } else {
925 code1 = lpIn[i] >> 4;
926 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
927 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
928 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
929 pixel_ptr += bytes_per_pixel;
931 lpIn++;
933 lpIn += code0 / 2;
935 /* if the RLE code is odd, skip a byte in the stream */
936 if (extra_byte)
937 lpIn++;
939 } else {
940 /* coded mode */
941 if (pixel_ptr/bytes_per_pixel + code0 > lpbi->biWidth)
942 return ICERR_ERROR;
944 if (bytes_per_pixel == 1) {
945 BYTE c1 = pi->palette_map[(code1 >> 4)];
946 BYTE c2 = pi->palette_map[(code1 & 0x0F)];
948 for (i = 0; i < code0; i++) {
949 if ((i & 1) == 0)
950 lpOut[pixel_ptr++] = c1;
951 else
952 lpOut[pixel_ptr++] = c2;
954 } else if (bytes_per_pixel == 2) {
955 BYTE hi1 = pi->palette_map[(code1 >> 4) * 2 + 0];
956 BYTE lo1 = pi->palette_map[(code1 >> 4) * 2 + 1];
958 BYTE hi2 = pi->palette_map[(code1 & 0x0F) * 2 + 0];
959 BYTE lo2 = pi->palette_map[(code1 & 0x0F) * 2 + 1];
961 for (i = 0; i < code0; i++) {
962 if ((i & 1) == 0) {
963 lpOut[pixel_ptr++] = hi1;
964 lpOut[pixel_ptr++] = lo1;
965 } else {
966 lpOut[pixel_ptr++] = hi2;
967 lpOut[pixel_ptr++] = lo2;
970 } else {
971 BYTE b1 = pi->palette_map[(code1 >> 4) * 4 + 0];
972 BYTE g1 = pi->palette_map[(code1 >> 4) * 4 + 1];
973 BYTE r1 = pi->palette_map[(code1 >> 4) * 4 + 2];
975 BYTE b2 = pi->palette_map[(code1 & 0x0F) * 4 + 0];
976 BYTE g2 = pi->palette_map[(code1 & 0x0F) * 4 + 1];
977 BYTE r2 = pi->palette_map[(code1 & 0x0F) * 4 + 2];
979 for (i = 0; i < code0; i++) {
980 if ((i & 1) == 0) {
981 lpOut[pixel_ptr + 0] = b1;
982 lpOut[pixel_ptr + 1] = g1;
983 lpOut[pixel_ptr + 2] = r1;
984 } else {
985 lpOut[pixel_ptr + 0] = b2;
986 lpOut[pixel_ptr + 1] = g2;
987 lpOut[pixel_ptr + 2] = r2;
989 pixel_ptr += bytes_per_pixel;
993 } while (! bEndFlag);
995 return ICERR_OK;
998 static LRESULT MSRLE32_DecompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
999 const BYTE *lpIn, LPBYTE lpOut)
1001 int bytes_per_pixel;
1002 int line_size;
1003 int pixel_ptr = 0;
1004 BOOL bEndFlag = FALSE;
1006 assert(pi != NULL);
1007 assert(lpbi != NULL && lpbi->biCompression == BI_RGB);
1008 assert(lpIn != NULL && lpOut != NULL);
1010 bytes_per_pixel = (lpbi->biBitCount + 1) / 8;
1011 line_size = DIBWIDTHBYTES(*lpbi);
1013 do {
1014 BYTE code0, code1;
1016 code0 = *lpIn++;
1017 code1 = *lpIn++;
1019 if (code0 == 0) {
1020 int extra_byte;
1022 switch (code1) {
1023 case 0: /* EOL - end of line */
1024 pixel_ptr = 0;
1025 lpOut += line_size;
1026 break;
1027 case 1: /* EOI - end of image */
1028 bEndFlag = TRUE;
1029 break;
1030 case 2: /* skip */
1031 pixel_ptr += *lpIn++ * bytes_per_pixel;
1032 lpOut += *lpIn++ * line_size;
1033 if (pixel_ptr >= lpbi->biWidth * bytes_per_pixel) {
1034 pixel_ptr = 0;
1035 lpOut += line_size;
1037 break;
1038 default: /* absolute mode */
1039 if (pixel_ptr/bytes_per_pixel + code1 > lpbi->biWidth) {
1040 WARN("aborted absolute: (%d=%d/%d+%d) > %d\n",pixel_ptr/bytes_per_pixel + code1,pixel_ptr,bytes_per_pixel,code1,lpbi->biWidth);
1041 return ICERR_ERROR;
1043 extra_byte = code1 & 0x01;
1045 code0 = code1;
1046 while (code0--) {
1047 code1 = *lpIn++;
1048 if (bytes_per_pixel == 1) {
1049 lpOut[pixel_ptr] = pi->palette_map[code1];
1050 } else if (bytes_per_pixel == 2) {
1051 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 2 + 0];
1052 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 2 + 1];
1053 } else {
1054 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
1055 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
1056 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
1058 pixel_ptr += bytes_per_pixel;
1061 /* if the RLE code is odd, skip a byte in the stream */
1062 if (extra_byte)
1063 lpIn++;
1065 } else {
1066 /* coded mode */
1067 if (pixel_ptr/bytes_per_pixel + code0 > lpbi->biWidth) {
1068 WARN("aborted coded: (%d=%d/%d+%d) > %d\n",pixel_ptr/bytes_per_pixel + code1,pixel_ptr,bytes_per_pixel,code1,lpbi->biWidth);
1069 return ICERR_ERROR;
1072 if (bytes_per_pixel == 1) {
1073 code1 = pi->palette_map[code1];
1074 while (code0--)
1075 lpOut[pixel_ptr++] = code1;
1076 } else if (bytes_per_pixel == 2) {
1077 BYTE hi = pi->palette_map[code1 * 2 + 0];
1078 BYTE lo = pi->palette_map[code1 * 2 + 1];
1080 while (code0--) {
1081 lpOut[pixel_ptr + 0] = hi;
1082 lpOut[pixel_ptr + 1] = lo;
1083 pixel_ptr += bytes_per_pixel;
1085 } else {
1086 BYTE r = pi->palette_map[code1 * 4 + 2];
1087 BYTE g = pi->palette_map[code1 * 4 + 1];
1088 BYTE b = pi->palette_map[code1 * 4 + 0];
1090 while (code0--) {
1091 lpOut[pixel_ptr + 0] = b;
1092 lpOut[pixel_ptr + 1] = g;
1093 lpOut[pixel_ptr + 2] = r;
1094 pixel_ptr += bytes_per_pixel;
1098 } while (! bEndFlag);
1100 return ICERR_OK;
1103 /*****************************************************************************/
1105 static CodecInfo* Open(LPICOPEN icinfo)
1107 CodecInfo* pi = NULL;
1109 if (icinfo == NULL) {
1110 TRACE("(NULL)\n");
1111 return (LPVOID)0xFFFF0000;
1114 if (compare_fourcc(icinfo->fccType, ICTYPE_VIDEO)) return NULL;
1116 TRACE("(%p = {%u,0x%08X(%4.4s),0x%08X(%4.4s),0x%X,0x%X,...})\n", icinfo,
1117 icinfo->dwSize, icinfo->fccType, (char*)&icinfo->fccType,
1118 icinfo->fccHandler, (char*)&icinfo->fccHandler,
1119 icinfo->dwVersion,icinfo->dwFlags);
1121 switch (icinfo->fccHandler) {
1122 case FOURCC_RLE:
1123 case FOURCC_RLE4:
1124 case FOURCC_RLE8:
1125 case FOURCC_MRLE:
1126 break;
1127 case mmioFOURCC('m','r','l','e'):
1128 icinfo->fccHandler = FOURCC_MRLE;
1129 break;
1130 default:
1131 WARN("unknown FOURCC = 0x%08X(%4.4s) !\n",
1132 icinfo->fccHandler,(char*)&icinfo->fccHandler);
1133 return NULL;
1136 pi = LocalAlloc(LPTR, sizeof(CodecInfo));
1138 if (pi != NULL) {
1139 pi->fccHandler = icinfo->fccHandler;
1141 pi->bCompress = FALSE;
1142 pi->nPrevFrame = -1;
1143 pi->pPrevFrame = pi->pCurFrame = NULL;
1145 pi->bDecompress = FALSE;
1146 pi->palette_map = NULL;
1149 icinfo->dwError = (pi != NULL ? ICERR_OK : ICERR_MEMORY);
1151 return pi;
1154 static LRESULT Close(CodecInfo *pi)
1156 TRACE("(%p)\n", pi);
1158 /* pre-condition */
1159 assert(pi != NULL);
1161 if (pi->pPrevFrame != NULL || pi->pCurFrame != NULL)
1162 CompressEnd(pi);
1164 LocalFree(pi);
1165 return 1;
1168 static LRESULT GetInfo(const CodecInfo *pi, ICINFO *icinfo, DWORD dwSize)
1170 /* pre-condition */
1171 assert(pi != NULL);
1173 /* check parameters */
1174 if (icinfo == NULL)
1175 return sizeof(ICINFO);
1176 if (dwSize < sizeof(ICINFO))
1177 return 0;
1179 icinfo->dwSize = sizeof(ICINFO);
1180 icinfo->fccType = ICTYPE_VIDEO;
1181 icinfo->fccHandler = (pi != NULL ? pi->fccHandler : FOURCC_MRLE);
1182 icinfo->dwFlags = VIDCF_QUALITY | VIDCF_TEMPORAL | VIDCF_CRUNCH | VIDCF_FASTTEMPORALC;
1183 icinfo->dwVersion = ICVERSION;
1184 icinfo->dwVersionICM = ICVERSION;
1186 LoadStringW(MSRLE32_hModule, IDS_NAME, icinfo->szName, sizeof(icinfo->szName)/sizeof(WCHAR));
1187 LoadStringW(MSRLE32_hModule, IDS_DESCRIPTION, icinfo->szDescription, sizeof(icinfo->szDescription)/sizeof(WCHAR));
1189 return sizeof(ICINFO);
1192 static LRESULT Configure(const CodecInfo *pi, HWND hWnd)
1194 /* pre-condition */
1195 assert(pi != NULL);
1197 /* FIXME */
1198 return ICERR_OK;
1201 static LRESULT About(CodecInfo *pi, HWND hWnd)
1203 WCHAR szTitle[20];
1204 WCHAR szAbout[128];
1206 /* pre-condition */
1207 assert(MSRLE32_hModule != 0);
1209 LoadStringW(MSRLE32_hModule, IDS_NAME, szTitle, sizeof(szTitle)/sizeof(szTitle[0]));
1210 LoadStringW(MSRLE32_hModule, IDS_ABOUT, szAbout, sizeof(szAbout)/sizeof(szAbout[0]));
1212 MessageBoxW(hWnd, szAbout, szTitle, MB_OK|MB_ICONINFORMATION);
1214 return ICERR_OK;
1217 static LRESULT CompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1218 LPBITMAPINFOHEADER lpbiOut)
1220 LRESULT size;
1222 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1224 /* pre-condition */
1225 assert(pi != NULL);
1227 /* check parameters -- need at least input format */
1228 if (lpbiIn == NULL) {
1229 if (lpbiOut != NULL)
1230 return ICERR_BADPARAM;
1231 return 0;
1234 /* handle unsupported input format */
1235 if (CompressQuery(pi, lpbiIn, NULL) != ICERR_OK)
1236 return (lpbiOut == NULL ? ICERR_BADFORMAT : 0);
1238 assert(0 < lpbiIn->biBitCount && lpbiIn->biBitCount <= 8);
1240 switch (pi->fccHandler) {
1241 case FOURCC_RLE4:
1242 size = 1 << 4;
1243 break;
1244 case FOURCC_RLE8:
1245 size = 1 << 8;
1246 break;
1247 case FOURCC_RLE:
1248 case FOURCC_MRLE:
1249 size = (lpbiIn->biBitCount <= 4 ? 1 << 4 : 1 << 8);
1250 break;
1251 default:
1252 return ICERR_ERROR;
1255 if (lpbiIn->biClrUsed != 0)
1256 size = lpbiIn->biClrUsed;
1258 size = sizeof(BITMAPINFOHEADER) + size * sizeof(RGBQUAD);
1260 if (lpbiOut != NULL) {
1261 lpbiOut->biSize = sizeof(BITMAPINFOHEADER);
1262 lpbiOut->biWidth = lpbiIn->biWidth;
1263 lpbiOut->biHeight = lpbiIn->biHeight;
1264 lpbiOut->biPlanes = 1;
1265 if (pi->fccHandler == FOURCC_RLE4 ||
1266 lpbiIn->biBitCount <= 4) {
1267 lpbiOut->biCompression = BI_RLE4;
1268 lpbiOut->biBitCount = 4;
1269 } else {
1270 lpbiOut->biCompression = BI_RLE8;
1271 lpbiOut->biBitCount = 8;
1273 lpbiOut->biSizeImage = MSRLE32_GetMaxCompressedSize(lpbiOut);
1274 lpbiOut->biXPelsPerMeter = lpbiIn->biXPelsPerMeter;
1275 lpbiOut->biYPelsPerMeter = lpbiIn->biYPelsPerMeter;
1276 if (lpbiIn->biClrUsed == 0)
1277 size = 1<<lpbiIn->biBitCount;
1278 else
1279 size = lpbiIn->biClrUsed;
1280 lpbiOut->biClrUsed = min(size, 1 << lpbiOut->biBitCount);
1281 lpbiOut->biClrImportant = 0;
1283 memcpy((LPBYTE)lpbiOut + lpbiOut->biSize,
1284 (const BYTE*)lpbiIn + lpbiIn->biSize, lpbiOut->biClrUsed * sizeof(RGBQUAD));
1286 return ICERR_OK;
1287 } else
1288 return size;
1291 static LRESULT CompressGetSize(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1292 LPCBITMAPINFOHEADER lpbiOut)
1294 /* pre-condition */
1295 assert(pi != NULL);
1297 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1299 /* check parameter -- need at least one format */
1300 if (lpbiIn == NULL && lpbiOut == NULL)
1301 return 0;
1302 /* check if the given format is supported */
1303 if (CompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1304 return 0;
1306 /* the worst case is coding the complete image in absolute mode. */
1307 if (lpbiIn)
1308 return MSRLE32_GetMaxCompressedSize(lpbiIn);
1309 else
1310 return MSRLE32_GetMaxCompressedSize(lpbiOut);
1313 static LRESULT CompressQuery(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1314 LPCBITMAPINFOHEADER lpbiOut)
1316 /* pre-condition */
1317 assert(pi != NULL);
1319 /* need at least one format */
1320 if (lpbiIn == NULL && lpbiOut == NULL)
1321 return ICERR_BADPARAM;
1323 /* check input format if given */
1324 if (lpbiIn != NULL) {
1325 if (!isSupportedDIB(lpbiIn))
1326 return ICERR_BADFORMAT;
1328 /* for 4-bit need an even width */
1329 if (lpbiIn->biBitCount <= 4 && (lpbiIn->biWidth % 2))
1330 return ICERR_BADFORMAT;
1332 if (pi->fccHandler == FOURCC_RLE4 && lpbiIn->biBitCount > 4)
1333 return ICERR_UNSUPPORTED;
1334 else if (lpbiIn->biBitCount > 8)
1335 return ICERR_UNSUPPORTED;
1338 /* check output format if given */
1339 if (lpbiOut != NULL) {
1340 if (!isSupportedMRLE(lpbiOut))
1341 return ICERR_BADFORMAT;
1343 if (lpbiIn != NULL) {
1344 if (lpbiIn->biWidth != lpbiOut->biWidth)
1345 return ICERR_UNSUPPORTED;
1346 if (lpbiIn->biHeight != lpbiOut->biHeight)
1347 return ICERR_UNSUPPORTED;
1348 if (lpbiIn->biBitCount > lpbiOut->biBitCount)
1349 return ICERR_UNSUPPORTED;
1353 return ICERR_OK;
1356 static LRESULT CompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1357 LPCBITMAPINFOHEADER lpbiOut)
1359 const RGBQUAD *rgbIn;
1360 const RGBQUAD *rgbOut;
1361 UINT i;
1362 size_t size;
1364 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1366 /* pre-condition */
1367 assert(pi != NULL);
1369 /* check parameters -- need both formats */
1370 if (lpbiIn == NULL || lpbiOut == NULL)
1371 return ICERR_BADPARAM;
1372 /* And both must be supported */
1373 if (CompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1374 return ICERR_BADFORMAT;
1376 /* FIXME: cannot compress and decompress at same time! */
1377 if (pi->bDecompress) {
1378 FIXME("cannot compress and decompress at same time!\n");
1379 return ICERR_ERROR;
1382 if (pi->bCompress)
1383 CompressEnd(pi);
1385 size = WIDTHBYTES(lpbiOut->biWidth * 16) / 2 * lpbiOut->biHeight;
1386 pi->pPrevFrame = GlobalLock(GlobalAlloc(GPTR, size * sizeof(WORD)));
1387 if (pi->pPrevFrame == NULL)
1388 return ICERR_MEMORY;
1389 pi->pCurFrame = GlobalLock(GlobalAlloc(GPTR, size * sizeof(WORD)));
1390 if (pi->pCurFrame == NULL) {
1391 CompressEnd(pi);
1392 return ICERR_MEMORY;
1394 pi->nPrevFrame = -1;
1395 pi->bCompress = TRUE;
1397 rgbIn = (const RGBQUAD*)((const BYTE*)lpbiIn + lpbiIn->biSize);
1398 rgbOut = (const RGBQUAD*)((const BYTE*)lpbiOut + lpbiOut->biSize);
1400 switch (lpbiOut->biBitCount) {
1401 case 4:
1402 case 8:
1403 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed);
1404 if (pi->palette_map == NULL) {
1405 CompressEnd(pi);
1406 return ICERR_MEMORY;
1409 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1410 pi->palette_map[i] = MSRLE32_GetNearestPaletteIndex(lpbiOut->biClrUsed, rgbOut, rgbIn[i]);
1412 break;
1415 return ICERR_OK;
1418 static LRESULT Compress(CodecInfo *pi, ICCOMPRESS* lpic, DWORD dwSize)
1420 BOOL is_key;
1421 int i;
1423 TRACE("(%p,%p,%u)\n",pi,lpic,dwSize);
1425 /* pre-condition */
1426 assert(pi != NULL);
1428 /* check parameters */
1429 if (lpic == NULL || dwSize < sizeof(ICCOMPRESS))
1430 return ICERR_BADPARAM;
1431 if (!lpic->lpbiOutput || !lpic->lpOutput ||
1432 !lpic->lpbiInput || !lpic->lpInput)
1433 return ICERR_BADPARAM;
1435 TRACE("lpic={0x%X,%p,%p,%p,%p,%p,%p,%d,%u,%u,%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);
1437 if (! pi->bCompress) {
1438 LRESULT hr = CompressBegin(pi, lpic->lpbiInput, lpic->lpbiOutput);
1439 if (hr != ICERR_OK)
1440 return hr;
1441 } else if (CompressQuery(pi, lpic->lpbiInput, lpic->lpbiOutput) != ICERR_OK)
1442 return ICERR_BADFORMAT;
1444 if (lpic->lFrameNum >= pi->nPrevFrame + 1) {
1445 /* we continue in the sequence so we need to initialize
1446 * our internal framedata */
1448 computeInternalFrame(pi, lpic->lpbiInput, lpic->lpInput);
1449 } else if (lpic->lFrameNum == pi->nPrevFrame) {
1450 /* Oops, compress same frame again ? Okay, as you wish.
1451 * No need to recompute internal framedata, because we only swapped buffers */
1452 LPWORD pTmp = pi->pPrevFrame;
1454 pi->pPrevFrame = pi->pCurFrame;
1455 pi->pCurFrame = pTmp;
1456 } else if ((lpic->dwFlags & ICCOMPRESS_KEYFRAME) == 0) {
1457 LPWORD pTmp;
1459 WARN(": prev=%d cur=%d gone back? -- untested\n",pi->nPrevFrame,lpic->lFrameNum);
1460 if (lpic->lpbiPrev == NULL || lpic->lpPrev == NULL)
1461 return ICERR_GOTOKEYFRAME; /* Need a keyframe if you go back */
1462 if (CompressQuery(pi, lpic->lpbiPrev, lpic->lpbiOutput) != ICERR_OK)
1463 return ICERR_BADFORMAT;
1465 WARN(": prev=%d cur=%d compute swapped -- untested\n",pi->nPrevFrame,lpic->lFrameNum);
1466 computeInternalFrame(pi, lpic->lpbiPrev, lpic->lpPrev);
1468 /* swap buffers for current and previous frame */
1469 /* Don't free and alloc new -- costs too much time and they are of equal size ! */
1470 pTmp = pi->pPrevFrame;
1471 pi->pPrevFrame = pi->pCurFrame;
1472 pi->pCurFrame = pTmp;
1473 pi->nPrevFrame = lpic->lFrameNum;
1476 is_key = (lpic->dwFlags & ICCOMPRESS_KEYFRAME) != 0;
1478 for (i = 0; i < 3; i++) {
1479 lpic->lpbiOutput->biSizeImage = 0;
1481 if (lpic->lpbiOutput->biBitCount == 4)
1482 MSRLE32_CompressRLE4(pi, lpic->lpbiInput, lpic->lpInput, lpic->lpbiOutput, lpic->lpOutput, is_key);
1483 else
1484 MSRLE32_CompressRLE8(pi, lpic->lpbiInput, lpic->lpInput, lpic->lpbiOutput, lpic->lpOutput, is_key);
1486 if (lpic->dwFrameSize == 0 ||
1487 lpic->lpbiOutput->biSizeImage < lpic->dwFrameSize)
1488 break;
1490 if ((*lpic->lpdwFlags & ICCOMPRESS_KEYFRAME) == 0) {
1491 if (lpic->lpbiOutput->biBitCount == 4)
1492 MSRLE32_CompressRLE4(pi, lpic->lpbiInput, lpic->lpInput,
1493 lpic->lpbiOutput, lpic->lpOutput, TRUE);
1494 else
1495 MSRLE32_CompressRLE8(pi, lpic->lpbiInput, lpic->lpInput,
1496 lpic->lpbiOutput, lpic->lpOutput, TRUE);
1498 if (lpic->dwFrameSize == 0 ||
1499 lpic->lpbiOutput->biSizeImage < lpic->dwFrameSize) {
1500 WARN("switched to keyframe, was small enough!\n");
1501 is_key = TRUE;
1502 *lpic->lpckid = MAKEAVICKID(cktypeDIBbits,
1503 StreamFromFOURCC(*lpic->lpckid));
1504 break;
1508 if (lpic->dwQuality < 1000)
1509 break;
1511 lpic->dwQuality -= 1000; /* reduce quality by 10% */
1514 { /* swap buffer for current and previous frame */
1515 /* Don't free and alloc new -- costs too much time and they are of equal size ! */
1516 LPWORD pTmp = pi->pPrevFrame;
1518 pi->pPrevFrame = pi->pCurFrame;
1519 pi->pCurFrame = pTmp;
1520 pi->nPrevFrame = lpic->lFrameNum;
1523 /* FIXME: What is AVIIF_TWOCC? */
1524 *lpic->lpdwFlags |= AVIIF_TWOCC | (is_key ? AVIIF_KEYFRAME : 0);
1525 return ICERR_OK;
1528 static LRESULT CompressEnd(CodecInfo *pi)
1530 TRACE("(%p)\n",pi);
1532 if (pi != NULL) {
1533 if (pi->pPrevFrame != NULL)
1535 GlobalUnlock(GlobalHandle(pi->pPrevFrame));
1536 GlobalFree(GlobalHandle(pi->pPrevFrame));
1538 if (pi->pCurFrame != NULL)
1540 GlobalUnlock(GlobalHandle(pi->pCurFrame));
1541 GlobalFree(GlobalHandle(pi->pCurFrame));
1543 pi->pPrevFrame = NULL;
1544 pi->pCurFrame = NULL;
1545 pi->nPrevFrame = -1;
1546 pi->bCompress = FALSE;
1549 return ICERR_OK;
1552 static LRESULT DecompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1553 LPBITMAPINFOHEADER lpbiOut)
1555 DWORD size;
1557 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1559 /* pre-condition */
1560 assert(pi != NULL);
1562 if (lpbiIn == NULL)
1563 return (lpbiOut != NULL ? ICERR_BADPARAM : 0);
1565 if (DecompressQuery(pi, lpbiIn, NULL) != ICERR_OK)
1566 return (lpbiOut != NULL ? ICERR_BADFORMAT : 0);
1568 size = lpbiIn->biSize;
1570 if (lpbiIn->biBitCount <= 8)
1571 size += lpbiIn->biClrUsed * sizeof(RGBQUAD);
1573 if (lpbiOut != NULL) {
1574 memcpy(lpbiOut, lpbiIn, size);
1575 lpbiOut->biCompression = BI_RGB;
1576 lpbiOut->biSizeImage = DIBWIDTHBYTES(*lpbiOut) * lpbiOut->biHeight;
1578 return ICERR_OK;
1579 } else
1580 return size;
1583 static LRESULT DecompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1584 LPCBITMAPINFOHEADER lpbiOut)
1586 LRESULT hr = ICERR_OK;
1588 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1590 /* pre-condition */
1591 assert(pi != NULL);
1593 /* need at least one format */
1594 if (lpbiIn == NULL && lpbiOut == NULL)
1595 return ICERR_BADPARAM;
1597 /* check input format if given */
1598 if (lpbiIn != NULL) {
1599 if (!isSupportedMRLE(lpbiIn))
1600 return ICERR_BADFORMAT;
1603 /* check output format if given */
1604 if (lpbiOut != NULL) {
1605 if (!isSupportedDIB(lpbiOut))
1606 hr = ICERR_BADFORMAT;
1608 if (lpbiIn != NULL) {
1609 if (lpbiIn->biWidth != lpbiOut->biWidth)
1610 hr = ICERR_UNSUPPORTED;
1611 if (lpbiIn->biHeight != lpbiOut->biHeight)
1612 hr = ICERR_UNSUPPORTED;
1613 if (lpbiIn->biBitCount > lpbiOut->biBitCount)
1614 hr = ICERR_UNSUPPORTED;
1618 return hr;
1621 static LRESULT DecompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1622 LPCBITMAPINFOHEADER lpbiOut)
1624 const RGBQUAD *rgbIn;
1625 const RGBQUAD *rgbOut;
1626 UINT i;
1628 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1630 /* pre-condition */
1631 assert(pi != NULL);
1633 /* check parameters */
1634 if (lpbiIn == NULL || lpbiOut == NULL)
1635 return ICERR_BADPARAM;
1636 if (DecompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1637 return ICERR_BADFORMAT;
1639 /* FIXME: cannot compress and decompress at a time! */
1640 if (pi->bCompress) {
1641 FIXME("cannot compress and decompress at same time!\n");
1642 return ICERR_ERROR;
1645 if (pi->bDecompress)
1646 DecompressEnd(pi);
1648 rgbIn = (const RGBQUAD*)((const BYTE*)lpbiIn + lpbiIn->biSize);
1649 rgbOut = (const RGBQUAD*)((const BYTE*)lpbiOut + lpbiOut->biSize);
1651 switch (lpbiOut->biBitCount) {
1652 case 4:
1653 case 8:
1654 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed);
1655 if (pi->palette_map == NULL)
1656 return ICERR_MEMORY;
1658 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1659 pi->palette_map[i] = MSRLE32_GetNearestPaletteIndex(lpbiOut->biClrUsed, rgbOut, rgbIn[i]);
1661 break;
1662 case 15:
1663 case 16:
1664 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed * 2);
1665 if (pi->palette_map == NULL)
1666 return ICERR_MEMORY;
1668 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1669 WORD color;
1671 if (lpbiOut->biBitCount == 15)
1672 color = ((rgbIn[i].rgbRed >> 3) << 10)
1673 | ((rgbIn[i].rgbGreen >> 3) << 5) | (rgbIn[i].rgbBlue >> 3);
1674 else
1675 color = ((rgbIn[i].rgbRed >> 3) << 11)
1676 | ((rgbIn[i].rgbGreen >> 3) << 5) | (rgbIn[i].rgbBlue >> 3);
1678 pi->palette_map[i * 2 + 1] = color >> 8;
1679 pi->palette_map[i * 2 + 0] = color & 0xFF;
1681 break;
1682 case 24:
1683 case 32:
1684 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed * sizeof(RGBQUAD));
1685 if (pi->palette_map == NULL)
1686 return ICERR_MEMORY;
1687 memcpy(pi->palette_map, rgbIn, lpbiIn->biClrUsed * sizeof(RGBQUAD));
1688 break;
1691 pi->bDecompress = TRUE;
1693 return ICERR_OK;
1696 static LRESULT Decompress(CodecInfo *pi, ICDECOMPRESS *pic, DWORD dwSize)
1698 TRACE("(%p,%p,%u)\n",pi,pic,dwSize);
1700 /* pre-condition */
1701 assert(pi != NULL);
1703 /* check parameters */
1704 if (pic == NULL)
1705 return ICERR_BADPARAM;
1706 if (pic->lpbiInput == NULL || pic->lpInput == NULL ||
1707 pic->lpbiOutput == NULL || pic->lpOutput == NULL)
1708 return ICERR_BADPARAM;
1710 /* check formats */
1711 if (! pi->bDecompress) {
1712 LRESULT hr = DecompressBegin(pi, pic->lpbiInput, pic->lpbiOutput);
1713 if (hr != ICERR_OK)
1714 return hr;
1715 } else if (DecompressQuery(pi, pic->lpbiInput, pic->lpbiOutput) != ICERR_OK)
1716 return ICERR_BADFORMAT;
1718 assert(pic->lpbiInput->biWidth == pic->lpbiOutput->biWidth);
1719 assert(pic->lpbiInput->biHeight == pic->lpbiOutput->biHeight);
1721 pic->lpbiOutput->biSizeImage = DIBWIDTHBYTES(*pic->lpbiOutput) * pic->lpbiOutput->biHeight;
1722 if (pic->lpbiInput->biBitCount == 4)
1723 return MSRLE32_DecompressRLE4(pi, pic->lpbiOutput, pic->lpInput, pic->lpOutput);
1724 else
1725 return MSRLE32_DecompressRLE8(pi, pic->lpbiOutput, pic->lpInput, pic->lpOutput);
1728 static LRESULT DecompressEnd(CodecInfo *pi)
1730 TRACE("(%p)\n",pi);
1732 /* pre-condition */
1733 assert(pi != NULL);
1735 pi->bDecompress = FALSE;
1737 if (pi->palette_map != NULL) {
1738 LocalFree(pi->palette_map);
1739 pi->palette_map = NULL;
1742 return ICERR_OK;
1745 static LRESULT DecompressGetPalette(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1746 LPBITMAPINFOHEADER lpbiOut)
1748 int size;
1750 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1752 /* pre-condition */
1753 assert(pi != NULL);
1755 /* check parameters */
1756 if (lpbiIn == NULL || lpbiOut == NULL)
1757 return ICERR_BADPARAM;
1759 if (DecompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1760 return ICERR_BADFORMAT;
1762 if (lpbiOut->biBitCount > 8)
1763 return ICERR_ERROR;
1765 if (lpbiIn->biBitCount <= 8) {
1766 if (lpbiIn->biClrUsed > 0)
1767 size = lpbiIn->biClrUsed;
1768 else
1769 size = (1 << lpbiIn->biBitCount);
1771 lpbiOut->biClrUsed = size;
1773 memcpy((LPBYTE)lpbiOut + lpbiOut->biSize, (const BYTE*)lpbiIn + lpbiIn->biSize, size * sizeof(RGBQUAD));
1774 } /* else could never occur ! */
1776 return ICERR_OK;
1779 /* DriverProc - entry point for an installable driver */
1780 LRESULT CALLBACK MSRLE32_DriverProc(DWORD_PTR dwDrvID, HDRVR hDrv, UINT uMsg,
1781 LPARAM lParam1, LPARAM lParam2)
1783 CodecInfo *pi = (CodecInfo*)dwDrvID;
1785 TRACE("(%lx,%p,0x%04X,0x%08lX,0x%08lX)\n", dwDrvID, hDrv, uMsg, lParam1, lParam2);
1787 switch (uMsg) {
1788 /* standard driver messages */
1789 case DRV_LOAD:
1790 return DRVCNF_OK;
1791 case DRV_OPEN:
1792 return (LRESULT)Open((ICOPEN*)lParam2);
1793 case DRV_CLOSE:
1794 if (dwDrvID != 0xFFFF0000 && (LPVOID)dwDrvID != NULL)
1795 Close(pi);
1796 return DRVCNF_OK;
1797 case DRV_ENABLE:
1798 case DRV_DISABLE:
1799 return DRVCNF_OK;
1800 case DRV_FREE:
1801 return DRVCNF_OK;
1802 case DRV_QUERYCONFIGURE:
1803 return DRVCNF_CANCEL; /* FIXME */
1804 case DRV_CONFIGURE:
1805 return DRVCNF_OK; /* FIXME */
1806 case DRV_INSTALL:
1807 case DRV_REMOVE:
1808 return DRVCNF_OK;
1810 /* installable compression manager messages */
1811 case ICM_CONFIGURE:
1812 FIXME("ICM_CONFIGURE (%ld)\n",lParam1);
1813 if (lParam1 == -1)
1814 return ICERR_UNSUPPORTED; /* FIXME */
1815 else
1816 return Configure(pi, (HWND)lParam1);
1817 case ICM_ABOUT:
1818 if (lParam1 == -1)
1819 return ICERR_OK;
1820 else
1821 return About(pi, (HWND)lParam1);
1822 case ICM_GETSTATE:
1823 case ICM_SETSTATE:
1824 return 0; /* no state */
1825 case ICM_GETINFO:
1826 return GetInfo(pi, (ICINFO*)lParam1, (DWORD)lParam2);
1827 case ICM_GETDEFAULTQUALITY:
1828 if ((LPVOID)lParam1 != NULL) {
1829 *((LPDWORD)lParam1) = MSRLE32_DEFAULTQUALITY;
1830 return ICERR_OK;
1832 break;
1833 case ICM_COMPRESS_GET_FORMAT:
1834 return CompressGetFormat(pi, (LPCBITMAPINFOHEADER)lParam1,
1835 (LPBITMAPINFOHEADER)lParam2);
1836 case ICM_COMPRESS_GET_SIZE:
1837 return CompressGetSize(pi, (LPCBITMAPINFOHEADER)lParam1,
1838 (LPCBITMAPINFOHEADER)lParam2);
1839 case ICM_COMPRESS_QUERY:
1840 return CompressQuery(pi, (LPCBITMAPINFOHEADER)lParam1,
1841 (LPCBITMAPINFOHEADER)lParam2);
1842 case ICM_COMPRESS_BEGIN:
1843 return CompressBegin(pi, (LPCBITMAPINFOHEADER)lParam1,
1844 (LPCBITMAPINFOHEADER)lParam2);
1845 case ICM_COMPRESS:
1846 return Compress(pi, (ICCOMPRESS*)lParam1, (DWORD)lParam2);
1847 case ICM_COMPRESS_END:
1848 return CompressEnd(pi);
1849 case ICM_DECOMPRESS_GET_FORMAT:
1850 return DecompressGetFormat(pi, (LPCBITMAPINFOHEADER)lParam1,
1851 (LPBITMAPINFOHEADER)lParam2);
1852 case ICM_DECOMPRESS_QUERY:
1853 return DecompressQuery(pi, (LPCBITMAPINFOHEADER)lParam1,
1854 (LPCBITMAPINFOHEADER)lParam2);
1855 case ICM_DECOMPRESS_BEGIN:
1856 return DecompressBegin(pi, (LPCBITMAPINFOHEADER)lParam1,
1857 (LPCBITMAPINFOHEADER)lParam2);
1858 case ICM_DECOMPRESS:
1859 return Decompress(pi, (ICDECOMPRESS*)lParam1, (DWORD)lParam2);
1860 case ICM_DECOMPRESS_END:
1861 return DecompressEnd(pi);
1862 case ICM_DECOMPRESS_SET_PALETTE:
1863 FIXME("(...) -> SetPalette(%p,%p,%p): stub!\n", pi, (LPVOID)lParam1, (LPVOID)lParam2);
1864 return ICERR_UNSUPPORTED;
1865 case ICM_DECOMPRESS_GET_PALETTE:
1866 return DecompressGetPalette(pi, (LPBITMAPINFOHEADER)lParam1,
1867 (LPBITMAPINFOHEADER)lParam2);
1868 case ICM_GETDEFAULTKEYFRAMERATE:
1869 if ((LPVOID)lParam1 != NULL)
1870 *(LPDWORD)lParam1 = 15;
1871 return ICERR_OK;
1872 default:
1873 if (uMsg < DRV_USER)
1874 return DefDriverProc(dwDrvID, hDrv, uMsg, lParam1, lParam2);
1875 else
1876 FIXME("Unknown message uMsg=0x%08X lParam1=0x%08lX lParam2=0x%08lX\n",uMsg,lParam1,lParam2);
1879 return ICERR_UNSUPPORTED;
1882 /* DllMain - library initialization code */
1883 BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
1885 TRACE("(%p,%d,%p)\n",hModule,dwReason,lpReserved);
1887 switch (dwReason) {
1888 case DLL_PROCESS_ATTACH:
1889 DisableThreadLibraryCalls(hModule);
1890 MSRLE32_hModule = hModule;
1891 break;
1894 return TRUE;