wineconsole: Free registry key name (valgrind).
[wine.git] / dlls / msrle32 / msrle32.c
blobaba94a0072796686850d72eb5aec854c056c0658
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 #define ABS(a) ((a) < 0 ? -(a) : (a))
40 #define SQR(a) ((a) * (a))
42 static inline WORD ColorCmp(WORD clr1, WORD clr2)
44 UINT a = clr1 - clr2;
45 return SQR(a);
47 static inline WORD Intensity(RGBQUAD clr)
49 return (30 * clr.rgbRed + 59 * clr.rgbGreen + 11 * clr.rgbBlue)/4;
52 #define GetRawPixel(lpbi,lp,x) \
53 ((lpbi)->biBitCount == 1 ? ((lp)[(x)/8] >> (8 - (x)%8)) & 1 : \
54 ((lpbi)->biBitCount == 4 ? ((lp)[(x)/2] >> (4 * (1 - (x)%2))) & 15 : lp[x]))
56 /*****************************************************************************/
58 /* utility functions */
59 static BOOL isSupportedDIB(LPCBITMAPINFOHEADER lpbi);
60 static BOOL isSupportedMRLE(LPCBITMAPINFOHEADER lpbi);
61 static BYTE MSRLE32_GetNearestPaletteIndex(UINT count, const RGBQUAD *clrs, RGBQUAD clr);
63 /* compression functions */
64 static void computeInternalFrame(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn, const BYTE *lpIn);
65 static LONG MSRLE32_GetMaxCompressedSize(LPCBITMAPINFOHEADER lpbi);
66 static LRESULT MSRLE32_CompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
67 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
68 LPBYTE lpOut, BOOL isKey);
69 static LRESULT MSRLE32_CompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
70 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
71 LPBYTE lpOut, BOOL isKey);
73 /* decompression functions */
74 static LRESULT MSRLE32_DecompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
75 const BYTE *lpIn, LPBYTE lpOut);
76 static LRESULT MSRLE32_DecompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
77 const BYTE *lpIn, LPBYTE lpOut);
79 /* API functions */
80 static LRESULT CompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
81 LPBITMAPINFOHEADER lpbiOut);
82 static LRESULT CompressGetSize(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
83 LPCBITMAPINFOHEADER lpbiOut);
84 static LRESULT CompressQuery(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
85 LPCBITMAPINFOHEADER lpbiOut);
86 static LRESULT CompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
87 LPCBITMAPINFOHEADER lpbiOut);
88 static LRESULT Compress(CodecInfo *pi, ICCOMPRESS* lpic, DWORD dwSize);
89 static LRESULT CompressEnd(CodecInfo *pi);
91 static LRESULT DecompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
92 LPBITMAPINFOHEADER lpbiOut);
93 static LRESULT DecompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
94 LPCBITMAPINFOHEADER lpbiOut);
95 static LRESULT DecompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
96 LPCBITMAPINFOHEADER lpbiOut);
97 static LRESULT Decompress(CodecInfo *pi, ICDECOMPRESS *pic, DWORD dwSize);
98 static LRESULT DecompressEnd(CodecInfo *pi);
99 static LRESULT DecompressGetPalette(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
100 LPBITMAPINFOHEADER lpbiOut);
102 /*****************************************************************************/
104 static BOOL isSupportedMRLE(LPCBITMAPINFOHEADER lpbi)
106 /* pre-conditions */
107 assert(lpbi != NULL);
109 if (lpbi->biSize < sizeof(BITMAPINFOHEADER) ||
110 lpbi->biPlanes != 1)
111 return FALSE;
113 if (lpbi->biCompression == BI_RLE4) {
114 if (lpbi->biBitCount != 4 ||
115 (lpbi->biWidth % 2) != 0)
116 return FALSE;
117 } else if (lpbi->biCompression == BI_RLE8) {
118 if (lpbi->biBitCount != 8)
119 return FALSE;
120 } else
121 return FALSE;
123 return TRUE;
126 static BOOL isSupportedDIB(LPCBITMAPINFOHEADER lpbi)
128 /* pre-conditions */
129 assert(lpbi != NULL);
131 /* check structure version/planes/compression */
132 if (lpbi->biSize < sizeof(BITMAPINFOHEADER) ||
133 lpbi->biPlanes != 1)
134 return FALSE;
135 if (lpbi->biCompression != BI_RGB &&
136 lpbi->biCompression != BI_BITFIELDS)
137 return FALSE;
139 /* check bit-depth */
140 if (lpbi->biBitCount != 1 &&
141 lpbi->biBitCount != 4 &&
142 lpbi->biBitCount != 8 &&
143 lpbi->biBitCount != 15 &&
144 lpbi->biBitCount != 16 &&
145 lpbi->biBitCount != 24 &&
146 lpbi->biBitCount != 32)
147 return FALSE;
149 /* check for size(s) */
150 if (!lpbi->biWidth || !lpbi->biHeight)
151 return FALSE; /* image with zero size, makes no sense so error ! */
152 if (DIBWIDTHBYTES(*lpbi) * (DWORD)lpbi->biHeight >= (1UL << 31) - 1)
153 return FALSE; /* image too big ! */
155 /* check for nonexistent colortable for hi- and true-color DIB's */
156 if (lpbi->biBitCount >= 15 && lpbi->biClrUsed > 0)
157 return FALSE;
159 return TRUE;
162 static BYTE MSRLE32_GetNearestPaletteIndex(UINT count, const RGBQUAD *clrs, RGBQUAD clr)
164 INT diff = 0x00FFFFFF;
165 UINT i;
166 UINT idx = 0;
168 /* pre-conditions */
169 assert(clrs != NULL);
171 for (i = 0; i < count; i++) {
172 int r = ((int)clrs[i].rgbRed - (int)clr.rgbRed);
173 int g = ((int)clrs[i].rgbGreen - (int)clr.rgbGreen);
174 int b = ((int)clrs[i].rgbBlue - (int)clr.rgbBlue);
176 r = r*r + g*g + b*b;
178 if (r < diff) {
179 idx = i;
180 diff = r;
181 if (diff == 0)
182 break;
186 return idx;
189 /*****************************************************************************/
191 void computeInternalFrame(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn, const BYTE *lpIn)
193 WORD wIntensityTbl[256];
194 DWORD lInLine, lOutLine;
195 LPWORD lpOut;
196 UINT i;
197 LONG y;
199 /* pre-conditions */
200 assert(pi != NULL && lpbiIn != NULL && lpIn != NULL);
201 assert(pi->pCurFrame != NULL);
203 lInLine = DIBWIDTHBYTES(*lpbiIn);
204 lOutLine = WIDTHBYTES((WORD)lpbiIn->biWidth * 8u * sizeof(WORD)) / 2u;
205 lpOut = pi->pCurFrame;
207 assert(lpbiIn->biClrUsed != 0);
210 const RGBQUAD *lp =
211 (const RGBQUAD *)((const BYTE*)lpbiIn + lpbiIn->biSize);
213 for (i = 0; i < lpbiIn->biClrUsed; i++)
214 wIntensityTbl[i] = Intensity(lp[i]);
217 for (y = 0; y < lpbiIn->biHeight; y++) {
218 LONG x;
220 switch (lpbiIn->biBitCount) {
221 case 1:
222 for (x = 0; x < lpbiIn->biWidth / 8; x++) {
223 for (i = 0; i < 7; i++)
224 lpOut[8 * x + i] = wIntensityTbl[(lpIn[x] >> (7 - i)) & 1];
226 break;
227 case 4:
228 for (x = 0; x < lpbiIn->biWidth / 2; x++) {
229 lpOut[2 * x + 0] = wIntensityTbl[(lpIn[x] >> 4)];
230 lpOut[2 * x + 1] = wIntensityTbl[(lpIn[x] & 0x0F)];
232 break;
233 case 8:
234 for (x = 0; x < lpbiIn->biWidth; x++)
235 lpOut[x] = wIntensityTbl[lpIn[x]];
236 break;
239 lpIn += lInLine;
240 lpOut += lOutLine;
244 static LONG MSRLE32_GetMaxCompressedSize(LPCBITMAPINFOHEADER lpbi)
246 LONG a, b, size;
248 /* pre-condition */
249 assert(lpbi != NULL);
251 a = lpbi->biWidth / 255;
252 b = lpbi->biWidth % 255;
253 if (lpbi->biBitCount <= 4) {
254 a /= 2;
255 b /= 2;
258 size = (2 + a * (2 + ((a + 2) & ~2)) + b * (2 + ((b + 2) & ~2)));
259 return size * lpbi->biHeight + 2;
262 /* lpP => current pos in previous frame
263 * lpA => previous pos in current frame
264 * lpB => current pos in current frame
266 static INT countDiffRLE4(const WORD *lpP, const WORD *lpA, const WORD *lpB, INT pos, LONG lDist, LONG width)
268 INT count;
269 WORD clr1, clr2;
271 /* pre-conditions */
272 assert(lpA && lpB && lDist >= 0 && width > 0);
274 if (pos >= width)
275 return 0;
276 if (pos+1 == width)
277 return 1;
279 clr1 = lpB[pos++];
280 clr2 = lpB[pos];
282 count = 2;
283 while (pos + 1 < width) {
284 WORD clr3, clr4;
286 clr3 = lpB[++pos];
287 if (pos + 1 >= width)
288 return count + 1;
290 clr4 = lpB[++pos];
291 if (ColorCmp(clr1, clr3) <= lDist &&
292 ColorCmp(clr2, clr4) <= lDist) {
293 /* diff at end? -- look-ahead for at least ?? more encodable pixels */
294 if (pos + 2 < width && ColorCmp(clr1,lpB[pos+1]) <= lDist &&
295 ColorCmp(clr2,lpB[pos+2]) <= lDist) {
296 if (pos + 4 < width && ColorCmp(lpB[pos+1],lpB[pos+3]) <= lDist &&
297 ColorCmp(lpB[pos+2],lpB[pos+4]) <= lDist)
298 return count - 3; /* followed by at least 4 encodable pixels */
299 return count - 2;
301 } else if (lpP != NULL && ColorCmp(lpP[pos], lpB[pos]) <= lDist) {
302 /* 'compare' with previous frame for end of diff */
303 INT count2 = 0;
305 /* FIXME */
307 if (count2 >= 8)
308 return count;
310 pos -= count2;
313 count += 2;
314 clr1 = clr3;
315 clr2 = clr4;
318 return count;
321 /* lpP => current pos in previous frame
322 * lpA => previous pos in current frame
323 * lpB => current pos in current frame
325 static INT countDiffRLE8(const WORD *lpP, const WORD *lpA, const WORD *lpB, INT pos, LONG lDist, LONG width)
327 INT count;
329 for (count = 0; pos < width; pos++, count++) {
330 if (ColorCmp(lpA[pos], lpB[pos]) <= lDist) {
331 /* diff at end? -- look-ahead for some more encodable pixel */
332 if (pos + 1 < width && ColorCmp(lpB[pos], lpB[pos+1]) <= lDist)
333 return count - 1;
334 if (pos + 2 < width && ColorCmp(lpB[pos+1], lpB[pos+2]) <= lDist)
335 return count - 1;
336 } else if (lpP != NULL && ColorCmp(lpP[pos], lpB[pos]) <= lDist) {
337 /* 'compare' with previous frame for end of diff */
338 INT count2 = 0;
340 for (count2 = 0, pos++; pos < width && count2 <= 5; pos++, count2++) {
341 if (ColorCmp(lpP[pos], lpB[pos]) > lDist)
342 break;
344 if (count2 > 4)
345 return count;
347 pos -= count2;
351 return count;
354 static INT MSRLE32_CompressRLE4Line(const CodecInfo *pi, const WORD *lpP,
355 const WORD *lpC, LPCBITMAPINFOHEADER lpbi,
356 const BYTE *lpIn, LONG lDist,
357 INT x, LPBYTE *ppOut,
358 DWORD *lpSizeImage)
360 LPBYTE lpOut = *ppOut;
361 INT count, pos;
362 WORD clr1, clr2;
364 /* try to encode as many pixel as possible */
365 count = 1;
366 pos = x;
367 clr1 = lpC[pos++];
368 if (pos < lpbi->biWidth) {
369 clr2 = lpC[pos];
370 for (++count; pos + 1 < lpbi->biWidth; ) {
371 ++pos;
372 if (ColorCmp(clr1, lpC[pos]) > lDist)
373 break;
374 count++;
375 if (pos + 1 >= lpbi->biWidth)
376 break;
377 ++pos;
378 if (ColorCmp(clr2, lpC[pos]) > lDist)
379 break;
380 count++;
384 if (count < 4) {
385 /* add some pixel for absoluting if possible */
386 count += countDiffRLE4(lpP, lpC - 1, lpC, pos-1, lDist, lpbi->biWidth);
388 assert(count > 0);
390 /* check for near end of line */
391 if (x + count > lpbi->biWidth)
392 count = lpbi->biWidth - x;
394 /* absolute pixel(s) in groups of at least 3 and at most 254 pixels */
395 while (count > 2) {
396 INT i;
397 INT size = min(count, 254);
398 int bytes = ((size + 1) & (~1)) / 2;
399 int extra_byte = bytes & 0x01;
401 *lpSizeImage += 2 + bytes + extra_byte;
402 assert(((*lpSizeImage) % 2) == 0);
403 count -= size;
404 *lpOut++ = 0;
405 *lpOut++ = size;
406 for (i = 0; i < size; i += 2) {
407 clr1 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
408 x++;
409 if (i + 1 < size) {
410 clr2 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
411 x++;
412 } else
413 clr2 = 0;
415 *lpOut++ = (clr1 << 4) | clr2;
417 if (extra_byte)
418 *lpOut++ = 0;
421 if (count > 0) {
422 /* too little for absoluting so we must encode them */
423 assert(count <= 2);
425 *lpSizeImage += 2;
426 clr1 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
427 x++;
428 if (count == 2) {
429 clr2 = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
430 x++;
431 } else
432 clr2 = 0;
433 *lpOut++ = count;
434 *lpOut++ = (clr1 << 4) | clr2;
436 } else {
437 /* encode count pixel(s) */
438 clr1 = ((pi->palette_map[GetRawPixel(lpbi,lpIn,x)] << 4) |
439 pi->palette_map[GetRawPixel(lpbi,lpIn,x + 1)]);
441 x += count;
442 while (count > 0) {
443 INT size = min(count, 254);
445 *lpSizeImage += 2;
446 count -= size;
447 *lpOut++ = size;
448 *lpOut++ = clr1;
452 *ppOut = lpOut;
454 return x;
457 static INT MSRLE32_CompressRLE8Line(const CodecInfo *pi, const WORD *lpP,
458 const WORD *lpC, LPCBITMAPINFOHEADER lpbi,
459 const BYTE *lpIn, INT x, LPBYTE *ppOut,
460 DWORD *lpSizeImage)
462 LPBYTE lpOut = *ppOut;
463 INT count, pos;
464 WORD clr;
466 assert(lpbi->biBitCount <= 8);
467 assert(lpbi->biCompression == BI_RGB);
469 /* try to encode as much as possible */
470 pos = x;
471 clr = lpC[pos++];
472 for (count = 1; pos < lpbi->biWidth; count++) {
473 if (ColorCmp(clr, lpC[pos++]) > 0)
474 break;
477 if (count < 2) {
478 /* add some more pixels for absoluting if possible */
479 count += countDiffRLE8(lpP, lpC - 1, lpC, pos-1, 0, lpbi->biWidth);
481 assert(count > 0);
483 /* check for over end of line */
484 if (x + count > lpbi->biWidth)
485 count = lpbi->biWidth - x;
487 /* absolute pixel(s) in groups of at least 3 and at most 255 pixels */
488 while (count > 2) {
489 INT i;
490 INT size = min(count, 255);
491 int extra_byte = size % 2;
493 *lpSizeImage += 2 + size + extra_byte;
494 count -= size;
495 *lpOut++ = 0;
496 *lpOut++ = size;
497 for (i = 0; i < size; i++) {
498 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
499 x++;
501 if (extra_byte)
502 *lpOut++ = 0;
504 if (count > 0) {
505 /* too little for absoluting so we must encode them even if it's expensive! */
506 assert(count <= 2);
508 *lpSizeImage += 2 * count;
509 *lpOut++ = 1;
510 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
511 x++;
513 if (count == 2) {
514 *lpOut++ = 1;
515 *lpOut++ = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
516 x++;
519 } else {
520 /* encode count pixel(s) */
521 clr = pi->palette_map[GetRawPixel(lpbi,lpIn,x)];
523 /* optimize end of line */
524 if (x + count + 1 == lpbi->biWidth)
525 count++;
527 x += count;
528 while (count > 0) {
529 INT size = min(count, 255);
531 *lpSizeImage += 2;
532 count -= size;
533 *lpOut++ = size;
534 *lpOut++ = clr;
538 *ppOut = lpOut;
540 return x;
543 LRESULT MSRLE32_CompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
544 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
545 LPBYTE lpOut, BOOL isKey)
547 LPWORD lpC;
548 LONG lLine, lInLine;
549 LPBYTE lpOutStart = lpOut;
551 /* pre-conditions */
552 assert(pi != NULL && lpbiOut != NULL);
553 assert(lpIn != NULL && lpOut != NULL);
554 assert(pi->pCurFrame != NULL);
556 lpC = pi->pCurFrame;
557 lInLine = DIBWIDTHBYTES(*lpbiIn);
558 lLine = WIDTHBYTES(lpbiOut->biWidth * 16) / 2;
560 lpbiOut->biSizeImage = 0;
561 if (isKey) {
562 /* keyframe -- convert internal frame to output format */
563 INT x, y;
565 for (y = 0; y < lpbiOut->biHeight; y++) {
566 x = 0;
568 do {
569 x = MSRLE32_CompressRLE4Line(pi, NULL, lpC, lpbiIn, lpIn, 0, x,
570 &lpOut, &lpbiOut->biSizeImage);
571 } while (x < lpbiOut->biWidth);
573 lpC += lLine;
574 lpIn += lInLine;
576 /* add EOL -- end of line */
577 lpbiOut->biSizeImage += 2;
578 *(LPWORD)lpOut = 0;
579 lpOut += sizeof(WORD);
580 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
582 } else {
583 /* delta-frame -- compute delta between last and this internal frame */
584 LPWORD lpP;
585 INT x, y;
586 INT jumpx, jumpy;
588 assert(pi->pPrevFrame != NULL);
590 lpP = pi->pPrevFrame;
591 jumpy = 0;
592 jumpx = -1;
594 for (y = 0; y < lpbiOut->biHeight; y++) {
595 x = 0;
597 do {
598 INT count, pos;
600 if (jumpx == -1)
601 jumpx = x;
602 for (count = 0, pos = x; pos < lpbiOut->biWidth; pos++, count++) {
603 if (ColorCmp(lpP[pos], lpC[pos]) > 0)
604 break;
607 if (pos == lpbiOut->biWidth && count > 8) {
608 /* (count > 8) secures that we will save space */
609 jumpy++;
610 break;
611 } else if (jumpy || jumpx != pos) {
612 /* time to jump */
613 assert(jumpx != -1);
615 if (pos < jumpx) {
616 /* can only jump in positive direction -- jump until EOL, EOL */
617 INT w = lpbiOut->biWidth - jumpx;
619 assert(jumpy > 0);
620 assert(w >= 4);
622 jumpx = 0;
623 jumpy--;
624 /* if (w % 255 == 2) then equal costs
625 * else if (w % 255 < 4 && we could encode all) then 2 bytes too expensive
626 * else it will be cheaper
628 while (w > 0) {
629 lpbiOut->biSizeImage += 4;
630 *lpOut++ = 0;
631 *lpOut++ = 2;
632 *lpOut = min(w, 255);
633 w -= *lpOut++;
634 *lpOut++ = 0;
636 /* add EOL -- end of line */
637 lpbiOut->biSizeImage += 2;
638 *((LPWORD)lpOut) = 0;
639 lpOut += sizeof(WORD);
642 /* FIXME: if (jumpy == 0 && could encode all) then jump too expensive */
644 /* write out real jump(s) */
645 while (jumpy || pos != jumpx) {
646 lpbiOut->biSizeImage += 4;
647 *lpOut++ = 0;
648 *lpOut++ = 2;
649 *lpOut = min(pos - jumpx, 255);
650 x += *lpOut;
651 jumpx += *lpOut++;
652 *lpOut = min(jumpy, 255);
653 jumpy -= *lpOut++;
656 jumpy = 0;
659 jumpx = -1;
661 if (x < lpbiOut->biWidth) {
662 /* skipped the 'same' things corresponding to previous frame */
663 x = MSRLE32_CompressRLE4Line(pi, lpP, lpC, lpbiIn, lpIn, 0, x,
664 &lpOut, &lpbiOut->biSizeImage);
666 } while (x < lpbiOut->biWidth);
668 lpP += lLine;
669 lpC += lLine;
670 lpIn += lInLine;
672 if (jumpy == 0) {
673 assert(jumpx == -1);
675 /* add EOL -- end of line */
676 lpbiOut->biSizeImage += 2;
677 *((LPWORD)lpOut) = 0;
678 lpOut += sizeof(WORD);
679 assert(lpOut == lpOutStart + lpbiOut->biSizeImage);
683 /* add EOL -- will be changed to EOI */
684 lpbiOut->biSizeImage += 2;
685 *((LPWORD)lpOut) = 0;
686 lpOut += sizeof(WORD);
689 /* change EOL to EOI -- end of image */
690 lpOut[-1] = 1;
691 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
693 return ICERR_OK;
696 LRESULT MSRLE32_CompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
697 const BYTE *lpIn, LPBITMAPINFOHEADER lpbiOut,
698 LPBYTE lpOut, BOOL isKey)
700 LPWORD lpC;
701 LONG lInLine, lLine;
702 LPBYTE lpOutStart = lpOut;
704 assert(pi != NULL && lpbiOut != NULL);
705 assert(lpIn != NULL && lpOut != NULL);
706 assert(pi->pCurFrame != NULL);
708 lpC = pi->pCurFrame;
709 lInLine = DIBWIDTHBYTES(*lpbiIn);
710 lLine = WIDTHBYTES(lpbiOut->biWidth * 16) / 2;
712 lpbiOut->biSizeImage = 0;
713 if (isKey) {
714 /* keyframe -- convert internal frame to output format */
715 INT x, y;
717 for (y = 0; y < lpbiOut->biHeight; y++) {
718 x = 0;
720 do {
721 x = MSRLE32_CompressRLE8Line(pi, NULL, lpC, lpbiIn, lpIn, x,
722 &lpOut, &lpbiOut->biSizeImage);
723 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
724 } while (x < lpbiOut->biWidth);
726 lpC += lLine;
727 lpIn += lInLine;
729 /* add EOL -- end of line */
730 lpbiOut->biSizeImage += 2;
731 *((LPWORD)lpOut) = 0;
732 lpOut += sizeof(WORD);
733 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
735 } else {
736 /* delta-frame -- compute delta between last and this internal frame */
737 LPWORD lpP;
738 INT x, y;
739 INT jumpx, jumpy;
741 assert(pi->pPrevFrame != NULL);
743 lpP = pi->pPrevFrame;
744 jumpx = -1;
745 jumpy = 0;
747 for (y = 0; y < lpbiOut->biHeight; y++) {
748 x = 0;
750 do {
751 INT count, pos;
753 if (jumpx == -1)
754 jumpx = x;
755 for (count = 0, pos = x; pos < lpbiOut->biWidth; pos++, count++) {
756 if (ColorCmp(lpP[pos], lpC[pos]) > 0)
757 break;
760 if (pos == lpbiOut->biWidth && count > 4) {
761 /* (count > 4) secures that we will save space */
762 jumpy++;
763 break;
764 } else if (jumpy || jumpx != pos) {
765 /* time to jump */
766 assert(jumpx != -1);
768 if (pos < jumpx) {
769 /* can only jump in positive direction -- do an EOL then jump */
770 assert(jumpy > 0);
772 jumpx = 0;
773 jumpy--;
775 /* add EOL -- end of line */
776 lpbiOut->biSizeImage += 2;
777 *((LPWORD)lpOut) = 0;
778 lpOut += sizeof(WORD);
779 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
782 /* FIXME: if (jumpy == 0 && could encode all) then jump too expensive */
784 /* write out real jump(s) */
785 while (jumpy || pos != jumpx) {
786 lpbiOut->biSizeImage += 4;
787 *lpOut++ = 0;
788 *lpOut++ = 2;
789 *lpOut = min(pos - jumpx, 255);
790 jumpx += *lpOut++;
791 *lpOut = min(jumpy, 255);
792 jumpy -= *lpOut++;
794 x = pos;
796 jumpy = 0;
799 jumpx = -1;
801 if (x < lpbiOut->biWidth) {
802 /* skip the 'same' things corresponding to previous frame */
803 x = MSRLE32_CompressRLE8Line(pi, lpP, lpC, lpbiIn, lpIn, x,
804 &lpOut, &lpbiOut->biSizeImage);
805 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
807 } while (x < lpbiOut->biWidth);
809 lpP += lLine;
810 lpC += lLine;
811 lpIn += lInLine;
813 if (jumpy == 0) {
814 /* add EOL -- end of line */
815 lpbiOut->biSizeImage += 2;
816 *((LPWORD)lpOut) = 0;
817 lpOut += sizeof(WORD);
818 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
822 /* add EOL */
823 lpbiOut->biSizeImage += 2;
824 *((LPWORD)lpOut) = 0;
825 lpOut += sizeof(WORD);
828 /* add EOI -- end of image */
829 lpbiOut->biSizeImage += 2;
830 *lpOut++ = 0;
831 *lpOut++ = 1;
832 assert(lpOut == (lpOutStart + lpbiOut->biSizeImage));
834 return ICERR_OK;
837 /*****************************************************************************/
839 static LRESULT MSRLE32_DecompressRLE4(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
840 const BYTE *lpIn, LPBYTE lpOut)
842 int bytes_per_pixel;
843 int line_size;
844 int pixel_ptr = 0;
845 int i;
846 BOOL bEndFlag = FALSE;
848 assert(pi != NULL);
849 assert(lpbi != NULL && lpbi->biCompression == BI_RGB);
850 assert(lpIn != NULL && lpOut != NULL);
852 bytes_per_pixel = (lpbi->biBitCount + 1) / 8;
853 line_size = DIBWIDTHBYTES(*lpbi);
855 do {
856 BYTE code0, code1;
858 code0 = *lpIn++;
859 code1 = *lpIn++;
861 if (code0 == 0) {
862 int extra_byte;
864 switch (code1) {
865 case 0: /* EOL - end of line */
866 pixel_ptr = 0;
867 lpOut += line_size;
868 break;
869 case 1: /* EOI - end of image */
870 bEndFlag = TRUE;
871 break;
872 case 2: /* skip */
873 pixel_ptr += *lpIn++ * bytes_per_pixel;
874 lpOut += *lpIn++ * line_size;
875 if (pixel_ptr >= lpbi->biWidth * bytes_per_pixel) {
876 pixel_ptr = 0;
877 lpOut += line_size;
879 break;
880 default: /* absolute mode */
881 extra_byte = (((code1 + 1) & (~1)) / 2) & 0x01;
883 if (pixel_ptr/bytes_per_pixel + code1 > lpbi->biWidth)
884 return ICERR_ERROR;
886 code0 = code1;
887 for (i = 0; i < code0 / 2; i++) {
888 if (bytes_per_pixel == 1) {
889 code1 = lpIn[i];
890 lpOut[pixel_ptr++] = pi->palette_map[(code1 >> 4)];
891 if (2 * i + 1 <= code0)
892 lpOut[pixel_ptr++] = pi->palette_map[(code1 & 0x0F)];
893 } else if (bytes_per_pixel == 2) {
894 code1 = lpIn[i] >> 4;
895 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
896 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
898 if (2 * i + 1 <= code0) {
899 code1 = lpIn[i] & 0x0F;
900 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
901 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
903 } else {
904 code1 = lpIn[i] >> 4;
905 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
906 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
907 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
908 pixel_ptr += bytes_per_pixel;
910 if (2 * i + 1 <= code0) {
911 code1 = lpIn[i] & 0x0F;
912 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
913 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
914 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
915 pixel_ptr += bytes_per_pixel;
919 if (code0 & 0x01) {
920 if (bytes_per_pixel == 1) {
921 code1 = lpIn[i];
922 lpOut[pixel_ptr++] = pi->palette_map[(code1 >> 4)];
923 } else if (bytes_per_pixel == 2) {
924 code1 = lpIn[i] >> 4;
925 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 0];
926 lpOut[pixel_ptr++] = pi->palette_map[code1 * 2 + 1];
927 } else {
928 code1 = lpIn[i] >> 4;
929 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
930 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
931 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
932 pixel_ptr += bytes_per_pixel;
934 lpIn++;
936 lpIn += code0 / 2;
938 /* if the RLE code is odd, skip a byte in the stream */
939 if (extra_byte)
940 lpIn++;
942 } else {
943 /* coded mode */
944 if (pixel_ptr/bytes_per_pixel + code0 > lpbi->biWidth)
945 return ICERR_ERROR;
947 if (bytes_per_pixel == 1) {
948 BYTE c1 = pi->palette_map[(code1 >> 4)];
949 BYTE c2 = pi->palette_map[(code1 & 0x0F)];
951 for (i = 0; i < code0; i++) {
952 if ((i & 1) == 0)
953 lpOut[pixel_ptr++] = c1;
954 else
955 lpOut[pixel_ptr++] = c2;
957 } else if (bytes_per_pixel == 2) {
958 BYTE hi1 = pi->palette_map[(code1 >> 4) * 2 + 0];
959 BYTE lo1 = pi->palette_map[(code1 >> 4) * 2 + 1];
961 BYTE hi2 = pi->palette_map[(code1 & 0x0F) * 2 + 0];
962 BYTE lo2 = pi->palette_map[(code1 & 0x0F) * 2 + 1];
964 for (i = 0; i < code0; i++) {
965 if ((i & 1) == 0) {
966 lpOut[pixel_ptr++] = hi1;
967 lpOut[pixel_ptr++] = lo1;
968 } else {
969 lpOut[pixel_ptr++] = hi2;
970 lpOut[pixel_ptr++] = lo2;
973 } else {
974 BYTE b1 = pi->palette_map[(code1 >> 4) * 4 + 0];
975 BYTE g1 = pi->palette_map[(code1 >> 4) * 4 + 1];
976 BYTE r1 = pi->palette_map[(code1 >> 4) * 4 + 2];
978 BYTE b2 = pi->palette_map[(code1 & 0x0F) * 4 + 0];
979 BYTE g2 = pi->palette_map[(code1 & 0x0F) * 4 + 1];
980 BYTE r2 = pi->palette_map[(code1 & 0x0F) * 4 + 2];
982 for (i = 0; i < code0; i++) {
983 if ((i & 1) == 0) {
984 lpOut[pixel_ptr + 0] = b1;
985 lpOut[pixel_ptr + 1] = g1;
986 lpOut[pixel_ptr + 2] = r1;
987 } else {
988 lpOut[pixel_ptr + 0] = b2;
989 lpOut[pixel_ptr + 1] = g2;
990 lpOut[pixel_ptr + 2] = r2;
992 pixel_ptr += bytes_per_pixel;
996 } while (! bEndFlag);
998 return ICERR_OK;
1001 static LRESULT MSRLE32_DecompressRLE8(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbi,
1002 const BYTE *lpIn, LPBYTE lpOut)
1004 int bytes_per_pixel;
1005 int line_size;
1006 int pixel_ptr = 0;
1007 BOOL bEndFlag = FALSE;
1009 assert(pi != NULL);
1010 assert(lpbi != NULL && lpbi->biCompression == BI_RGB);
1011 assert(lpIn != NULL && lpOut != NULL);
1013 bytes_per_pixel = (lpbi->biBitCount + 1) / 8;
1014 line_size = DIBWIDTHBYTES(*lpbi);
1016 do {
1017 BYTE code0, code1;
1019 code0 = *lpIn++;
1020 code1 = *lpIn++;
1022 if (code0 == 0) {
1023 int extra_byte;
1025 switch (code1) {
1026 case 0: /* EOL - end of line */
1027 pixel_ptr = 0;
1028 lpOut += line_size;
1029 break;
1030 case 1: /* EOI - end of image */
1031 bEndFlag = TRUE;
1032 break;
1033 case 2: /* skip */
1034 pixel_ptr += *lpIn++ * bytes_per_pixel;
1035 lpOut += *lpIn++ * line_size;
1036 if (pixel_ptr >= lpbi->biWidth * bytes_per_pixel) {
1037 pixel_ptr = 0;
1038 lpOut += line_size;
1040 break;
1041 default: /* absolute mode */
1042 if (pixel_ptr/bytes_per_pixel + code1 > lpbi->biWidth) {
1043 WARN("aborted absolute: (%d=%d/%d+%d) > %d\n",pixel_ptr/bytes_per_pixel + code1,pixel_ptr,bytes_per_pixel,code1,lpbi->biWidth);
1044 return ICERR_ERROR;
1046 extra_byte = code1 & 0x01;
1048 code0 = code1;
1049 while (code0--) {
1050 code1 = *lpIn++;
1051 if (bytes_per_pixel == 1) {
1052 lpOut[pixel_ptr] = pi->palette_map[code1];
1053 } else if (bytes_per_pixel == 2) {
1054 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 2 + 0];
1055 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 2 + 1];
1056 } else {
1057 lpOut[pixel_ptr + 0] = pi->palette_map[code1 * 4 + 0];
1058 lpOut[pixel_ptr + 1] = pi->palette_map[code1 * 4 + 1];
1059 lpOut[pixel_ptr + 2] = pi->palette_map[code1 * 4 + 2];
1061 pixel_ptr += bytes_per_pixel;
1064 /* if the RLE code is odd, skip a byte in the stream */
1065 if (extra_byte)
1066 lpIn++;
1068 } else {
1069 /* coded mode */
1070 if (pixel_ptr/bytes_per_pixel + code0 > lpbi->biWidth) {
1071 WARN("aborted coded: (%d=%d/%d+%d) > %d\n",pixel_ptr/bytes_per_pixel + code1,pixel_ptr,bytes_per_pixel,code1,lpbi->biWidth);
1072 return ICERR_ERROR;
1075 if (bytes_per_pixel == 1) {
1076 code1 = pi->palette_map[code1];
1077 while (code0--)
1078 lpOut[pixel_ptr++] = code1;
1079 } else if (bytes_per_pixel == 2) {
1080 BYTE hi = pi->palette_map[code1 * 2 + 0];
1081 BYTE lo = pi->palette_map[code1 * 2 + 1];
1083 while (code0--) {
1084 lpOut[pixel_ptr + 0] = hi;
1085 lpOut[pixel_ptr + 1] = lo;
1086 pixel_ptr += bytes_per_pixel;
1088 } else {
1089 BYTE r = pi->palette_map[code1 * 4 + 2];
1090 BYTE g = pi->palette_map[code1 * 4 + 1];
1091 BYTE b = pi->palette_map[code1 * 4 + 0];
1093 while (code0--) {
1094 lpOut[pixel_ptr + 0] = b;
1095 lpOut[pixel_ptr + 1] = g;
1096 lpOut[pixel_ptr + 2] = r;
1097 pixel_ptr += bytes_per_pixel;
1101 } while (! bEndFlag);
1103 return ICERR_OK;
1106 /*****************************************************************************/
1108 static CodecInfo* Open(LPICOPEN icinfo)
1110 CodecInfo* pi = NULL;
1112 if (icinfo == NULL) {
1113 TRACE("(NULL)\n");
1114 return (LPVOID)0xFFFF0000;
1117 if (compare_fourcc(icinfo->fccType, ICTYPE_VIDEO)) return NULL;
1119 TRACE("(%p = {%u,0x%08X(%4.4s),0x%08X(%4.4s),0x%X,0x%X,...})\n", icinfo,
1120 icinfo->dwSize, icinfo->fccType, (char*)&icinfo->fccType,
1121 icinfo->fccHandler, (char*)&icinfo->fccHandler,
1122 icinfo->dwVersion,icinfo->dwFlags);
1124 switch (icinfo->fccHandler) {
1125 case FOURCC_RLE:
1126 case FOURCC_RLE4:
1127 case FOURCC_RLE8:
1128 case FOURCC_MRLE:
1129 break;
1130 case mmioFOURCC('m','r','l','e'):
1131 icinfo->fccHandler = FOURCC_MRLE;
1132 break;
1133 default:
1134 WARN("unknown FOURCC = 0x%08X(%4.4s) !\n",
1135 icinfo->fccHandler,(char*)&icinfo->fccHandler);
1136 return NULL;
1139 pi = LocalAlloc(LPTR, sizeof(CodecInfo));
1141 if (pi != NULL) {
1142 pi->fccHandler = icinfo->fccHandler;
1144 pi->bCompress = FALSE;
1145 pi->nPrevFrame = -1;
1146 pi->pPrevFrame = pi->pCurFrame = NULL;
1148 pi->bDecompress = FALSE;
1149 pi->palette_map = NULL;
1152 icinfo->dwError = (pi != NULL ? ICERR_OK : ICERR_MEMORY);
1154 return pi;
1157 static LRESULT Close(CodecInfo *pi)
1159 TRACE("(%p)\n", pi);
1161 /* pre-condition */
1162 assert(pi != NULL);
1164 if (pi->pPrevFrame != NULL || pi->pCurFrame != NULL)
1165 CompressEnd(pi);
1167 LocalFree(pi);
1168 return 1;
1171 static LRESULT GetInfo(const CodecInfo *pi, ICINFO *icinfo, DWORD dwSize)
1173 /* pre-condition */
1174 assert(pi != NULL);
1176 /* check parameters */
1177 if (icinfo == NULL)
1178 return sizeof(ICINFO);
1179 if (dwSize < sizeof(ICINFO))
1180 return 0;
1182 icinfo->dwSize = sizeof(ICINFO);
1183 icinfo->fccType = ICTYPE_VIDEO;
1184 icinfo->fccHandler = (pi != NULL ? pi->fccHandler : FOURCC_MRLE);
1185 icinfo->dwFlags = VIDCF_QUALITY | VIDCF_TEMPORAL | VIDCF_CRUNCH | VIDCF_FASTTEMPORALC;
1186 icinfo->dwVersion = ICVERSION;
1187 icinfo->dwVersionICM = ICVERSION;
1189 LoadStringW(MSRLE32_hModule, IDS_NAME, icinfo->szName, sizeof(icinfo->szName)/sizeof(WCHAR));
1190 LoadStringW(MSRLE32_hModule, IDS_DESCRIPTION, icinfo->szDescription, sizeof(icinfo->szDescription)/sizeof(WCHAR));
1192 return sizeof(ICINFO);
1195 static LRESULT Configure(const CodecInfo *pi, HWND hWnd)
1197 /* pre-condition */
1198 assert(pi != NULL);
1200 /* FIXME */
1201 return ICERR_OK;
1204 static LRESULT About(CodecInfo *pi, HWND hWnd)
1206 WCHAR szTitle[20];
1207 WCHAR szAbout[128];
1209 /* pre-condition */
1210 assert(MSRLE32_hModule != 0);
1212 LoadStringW(MSRLE32_hModule, IDS_NAME, szTitle, sizeof(szTitle)/sizeof(szTitle[0]));
1213 LoadStringW(MSRLE32_hModule, IDS_ABOUT, szAbout, sizeof(szAbout)/sizeof(szAbout[0]));
1215 MessageBoxW(hWnd, szAbout, szTitle, MB_OK|MB_ICONINFORMATION);
1217 return ICERR_OK;
1220 static LRESULT CompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1221 LPBITMAPINFOHEADER lpbiOut)
1223 LRESULT size;
1225 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1227 /* pre-condition */
1228 assert(pi != NULL);
1230 /* check parameters -- need at least input format */
1231 if (lpbiIn == NULL) {
1232 if (lpbiOut != NULL)
1233 return ICERR_BADPARAM;
1234 return 0;
1237 /* handle unsupported input format */
1238 if (CompressQuery(pi, lpbiIn, NULL) != ICERR_OK)
1239 return (lpbiOut == NULL ? ICERR_BADFORMAT : 0);
1241 assert(0 < lpbiIn->biBitCount && lpbiIn->biBitCount <= 8);
1243 switch (pi->fccHandler) {
1244 case FOURCC_RLE4:
1245 size = 1 << 4;
1246 break;
1247 case FOURCC_RLE8:
1248 size = 1 << 8;
1249 break;
1250 case FOURCC_RLE:
1251 case FOURCC_MRLE:
1252 size = (lpbiIn->biBitCount <= 4 ? 1 << 4 : 1 << 8);
1253 break;
1254 default:
1255 return ICERR_ERROR;
1258 if (lpbiIn->biClrUsed != 0)
1259 size = lpbiIn->biClrUsed;
1261 size = sizeof(BITMAPINFOHEADER) + size * sizeof(RGBQUAD);
1263 if (lpbiOut != NULL) {
1264 lpbiOut->biSize = sizeof(BITMAPINFOHEADER);
1265 lpbiOut->biWidth = lpbiIn->biWidth;
1266 lpbiOut->biHeight = lpbiIn->biHeight;
1267 lpbiOut->biPlanes = 1;
1268 if (pi->fccHandler == FOURCC_RLE4 ||
1269 lpbiIn->biBitCount <= 4) {
1270 lpbiOut->biCompression = BI_RLE4;
1271 lpbiOut->biBitCount = 4;
1272 } else {
1273 lpbiOut->biCompression = BI_RLE8;
1274 lpbiOut->biBitCount = 8;
1276 lpbiOut->biSizeImage = MSRLE32_GetMaxCompressedSize(lpbiOut);
1277 lpbiOut->biXPelsPerMeter = lpbiIn->biXPelsPerMeter;
1278 lpbiOut->biYPelsPerMeter = lpbiIn->biYPelsPerMeter;
1279 if (lpbiIn->biClrUsed == 0)
1280 size = 1<<lpbiIn->biBitCount;
1281 else
1282 size = lpbiIn->biClrUsed;
1283 lpbiOut->biClrUsed = min(size, 1 << lpbiOut->biBitCount);
1284 lpbiOut->biClrImportant = 0;
1286 memcpy((LPBYTE)lpbiOut + lpbiOut->biSize,
1287 (const BYTE*)lpbiIn + lpbiIn->biSize, lpbiOut->biClrUsed * sizeof(RGBQUAD));
1289 return ICERR_OK;
1290 } else
1291 return size;
1294 static LRESULT CompressGetSize(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1295 LPCBITMAPINFOHEADER lpbiOut)
1297 /* pre-condition */
1298 assert(pi != NULL);
1300 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1302 /* check parameter -- need at least one format */
1303 if (lpbiIn == NULL && lpbiOut == NULL)
1304 return 0;
1305 /* check if the given format is supported */
1306 if (CompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1307 return 0;
1309 /* the worst case is coding the complete image in absolute mode. */
1310 if (lpbiIn)
1311 return MSRLE32_GetMaxCompressedSize(lpbiIn);
1312 else
1313 return MSRLE32_GetMaxCompressedSize(lpbiOut);
1316 static LRESULT CompressQuery(const CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1317 LPCBITMAPINFOHEADER lpbiOut)
1319 /* pre-condition */
1320 assert(pi != NULL);
1322 /* need at least one format */
1323 if (lpbiIn == NULL && lpbiOut == NULL)
1324 return ICERR_BADPARAM;
1326 /* check input format if given */
1327 if (lpbiIn != NULL) {
1328 if (!isSupportedDIB(lpbiIn))
1329 return ICERR_BADFORMAT;
1331 /* for 4-bit need an even width */
1332 if (lpbiIn->biBitCount <= 4 && (lpbiIn->biWidth % 2))
1333 return ICERR_BADFORMAT;
1335 if (pi->fccHandler == FOURCC_RLE4 && lpbiIn->biBitCount > 4)
1336 return ICERR_UNSUPPORTED;
1337 else if (lpbiIn->biBitCount > 8)
1338 return ICERR_UNSUPPORTED;
1341 /* check output format if given */
1342 if (lpbiOut != NULL) {
1343 if (!isSupportedMRLE(lpbiOut))
1344 return ICERR_BADFORMAT;
1346 if (lpbiIn != NULL) {
1347 if (lpbiIn->biWidth != lpbiOut->biWidth)
1348 return ICERR_UNSUPPORTED;
1349 if (lpbiIn->biHeight != lpbiOut->biHeight)
1350 return ICERR_UNSUPPORTED;
1351 if (lpbiIn->biBitCount > lpbiOut->biBitCount)
1352 return ICERR_UNSUPPORTED;
1356 return ICERR_OK;
1359 static LRESULT CompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1360 LPCBITMAPINFOHEADER lpbiOut)
1362 const RGBQUAD *rgbIn;
1363 const RGBQUAD *rgbOut;
1364 UINT i;
1365 size_t size;
1367 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1369 /* pre-condition */
1370 assert(pi != NULL);
1372 /* check parameters -- need both formats */
1373 if (lpbiIn == NULL || lpbiOut == NULL)
1374 return ICERR_BADPARAM;
1375 /* And both must be supported */
1376 if (CompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1377 return ICERR_BADFORMAT;
1379 /* FIXME: cannot compress and decompress at same time! */
1380 if (pi->bDecompress) {
1381 FIXME("cannot compress and decompress at same time!\n");
1382 return ICERR_ERROR;
1385 if (pi->bCompress)
1386 CompressEnd(pi);
1388 size = WIDTHBYTES(lpbiOut->biWidth * 16) / 2 * lpbiOut->biHeight;
1389 pi->pPrevFrame = GlobalLock(GlobalAlloc(GPTR, size * sizeof(WORD)));
1390 if (pi->pPrevFrame == NULL)
1391 return ICERR_MEMORY;
1392 pi->pCurFrame = GlobalLock(GlobalAlloc(GPTR, size * sizeof(WORD)));
1393 if (pi->pCurFrame == NULL) {
1394 CompressEnd(pi);
1395 return ICERR_MEMORY;
1397 pi->nPrevFrame = -1;
1398 pi->bCompress = TRUE;
1400 rgbIn = (const RGBQUAD*)((const BYTE*)lpbiIn + lpbiIn->biSize);
1401 rgbOut = (const RGBQUAD*)((const BYTE*)lpbiOut + lpbiOut->biSize);
1403 switch (lpbiOut->biBitCount) {
1404 case 4:
1405 case 8:
1406 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed);
1407 if (pi->palette_map == NULL) {
1408 CompressEnd(pi);
1409 return ICERR_MEMORY;
1412 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1413 pi->palette_map[i] = MSRLE32_GetNearestPaletteIndex(lpbiOut->biClrUsed, rgbOut, rgbIn[i]);
1415 break;
1418 return ICERR_OK;
1421 static LRESULT Compress(CodecInfo *pi, ICCOMPRESS* lpic, DWORD dwSize)
1423 BOOL is_key;
1424 int i;
1426 TRACE("(%p,%p,%u)\n",pi,lpic,dwSize);
1428 /* pre-condition */
1429 assert(pi != NULL);
1431 /* check parameters */
1432 if (lpic == NULL || dwSize < sizeof(ICCOMPRESS))
1433 return ICERR_BADPARAM;
1434 if (!lpic->lpbiOutput || !lpic->lpOutput ||
1435 !lpic->lpbiInput || !lpic->lpInput)
1436 return ICERR_BADPARAM;
1438 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);
1440 if (! pi->bCompress) {
1441 LRESULT hr = CompressBegin(pi, lpic->lpbiInput, lpic->lpbiOutput);
1442 if (hr != ICERR_OK)
1443 return hr;
1444 } else if (CompressQuery(pi, lpic->lpbiInput, lpic->lpbiOutput) != ICERR_OK)
1445 return ICERR_BADFORMAT;
1447 if (lpic->lFrameNum >= pi->nPrevFrame + 1) {
1448 /* we continue in the sequence so we need to initialize
1449 * our internal framedata */
1451 computeInternalFrame(pi, lpic->lpbiInput, lpic->lpInput);
1452 } else if (lpic->lFrameNum == pi->nPrevFrame) {
1453 /* Oops, compress same frame again ? Okay, as you wish.
1454 * No need to recompute internal framedata, because we only swapped buffers */
1455 LPWORD pTmp = pi->pPrevFrame;
1457 pi->pPrevFrame = pi->pCurFrame;
1458 pi->pCurFrame = pTmp;
1459 } else if ((lpic->dwFlags & ICCOMPRESS_KEYFRAME) == 0) {
1460 LPWORD pTmp;
1462 WARN(": prev=%d cur=%d gone back? -- untested\n",pi->nPrevFrame,lpic->lFrameNum);
1463 if (lpic->lpbiPrev == NULL || lpic->lpPrev == NULL)
1464 return ICERR_GOTOKEYFRAME; /* Need a keyframe if you go back */
1465 if (CompressQuery(pi, lpic->lpbiPrev, lpic->lpbiOutput) != ICERR_OK)
1466 return ICERR_BADFORMAT;
1468 WARN(": prev=%d cur=%d compute swapped -- untested\n",pi->nPrevFrame,lpic->lFrameNum);
1469 computeInternalFrame(pi, lpic->lpbiPrev, lpic->lpPrev);
1471 /* swap buffers for current and previous frame */
1472 /* Don't free and alloc new -- costs too much time and they are of equal size ! */
1473 pTmp = pi->pPrevFrame;
1474 pi->pPrevFrame = pi->pCurFrame;
1475 pi->pCurFrame = pTmp;
1476 pi->nPrevFrame = lpic->lFrameNum;
1479 is_key = (lpic->dwFlags & ICCOMPRESS_KEYFRAME) != 0;
1481 for (i = 0; i < 3; i++) {
1482 lpic->lpbiOutput->biSizeImage = 0;
1484 if (lpic->lpbiOutput->biBitCount == 4)
1485 MSRLE32_CompressRLE4(pi, lpic->lpbiInput, lpic->lpInput, lpic->lpbiOutput, lpic->lpOutput, is_key);
1486 else
1487 MSRLE32_CompressRLE8(pi, lpic->lpbiInput, lpic->lpInput, lpic->lpbiOutput, lpic->lpOutput, is_key);
1489 if (lpic->dwFrameSize == 0 ||
1490 lpic->lpbiOutput->biSizeImage < lpic->dwFrameSize)
1491 break;
1493 if ((*lpic->lpdwFlags & ICCOMPRESS_KEYFRAME) == 0) {
1494 if (lpic->lpbiOutput->biBitCount == 4)
1495 MSRLE32_CompressRLE4(pi, lpic->lpbiInput, lpic->lpInput,
1496 lpic->lpbiOutput, lpic->lpOutput, TRUE);
1497 else
1498 MSRLE32_CompressRLE8(pi, lpic->lpbiInput, lpic->lpInput,
1499 lpic->lpbiOutput, lpic->lpOutput, TRUE);
1501 if (lpic->dwFrameSize == 0 ||
1502 lpic->lpbiOutput->biSizeImage < lpic->dwFrameSize) {
1503 WARN("switched to keyframe, was small enough!\n");
1504 is_key = TRUE;
1505 *lpic->lpckid = MAKEAVICKID(cktypeDIBbits,
1506 StreamFromFOURCC(*lpic->lpckid));
1507 break;
1511 if (lpic->dwQuality < 1000)
1512 break;
1514 lpic->dwQuality -= 1000; /* reduce quality by 10% */
1517 { /* swap buffer for current and previous frame */
1518 /* Don't free and alloc new -- costs too much time and they are of equal size ! */
1519 LPWORD pTmp = pi->pPrevFrame;
1521 pi->pPrevFrame = pi->pCurFrame;
1522 pi->pCurFrame = pTmp;
1523 pi->nPrevFrame = lpic->lFrameNum;
1526 /* FIXME: What is AVIIF_TWOCC? */
1527 *lpic->lpdwFlags |= AVIIF_TWOCC | (is_key ? AVIIF_KEYFRAME : 0);
1528 return ICERR_OK;
1531 static LRESULT CompressEnd(CodecInfo *pi)
1533 TRACE("(%p)\n",pi);
1535 if (pi != NULL) {
1536 if (pi->pPrevFrame != NULL)
1538 GlobalUnlock(GlobalHandle(pi->pPrevFrame));
1539 GlobalFree(GlobalHandle(pi->pPrevFrame));
1541 if (pi->pCurFrame != NULL)
1543 GlobalUnlock(GlobalHandle(pi->pCurFrame));
1544 GlobalFree(GlobalHandle(pi->pCurFrame));
1546 pi->pPrevFrame = NULL;
1547 pi->pCurFrame = NULL;
1548 pi->nPrevFrame = -1;
1549 pi->bCompress = FALSE;
1552 return ICERR_OK;
1555 static LRESULT DecompressGetFormat(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1556 LPBITMAPINFOHEADER lpbiOut)
1558 DWORD size;
1560 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1562 /* pre-condition */
1563 assert(pi != NULL);
1565 if (lpbiIn == NULL)
1566 return (lpbiOut != NULL ? ICERR_BADPARAM : 0);
1568 if (DecompressQuery(pi, lpbiIn, NULL) != ICERR_OK)
1569 return (lpbiOut != NULL ? ICERR_BADFORMAT : 0);
1571 size = lpbiIn->biSize;
1573 if (lpbiIn->biBitCount <= 8)
1574 size += lpbiIn->biClrUsed * sizeof(RGBQUAD);
1576 if (lpbiOut != NULL) {
1577 memcpy(lpbiOut, lpbiIn, size);
1578 lpbiOut->biCompression = BI_RGB;
1579 lpbiOut->biSizeImage = DIBWIDTHBYTES(*lpbiOut) * lpbiOut->biHeight;
1581 return ICERR_OK;
1582 } else
1583 return size;
1586 static LRESULT DecompressQuery(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1587 LPCBITMAPINFOHEADER lpbiOut)
1589 LRESULT hr = ICERR_OK;
1591 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1593 /* pre-condition */
1594 assert(pi != NULL);
1596 /* need at least one format */
1597 if (lpbiIn == NULL && lpbiOut == NULL)
1598 return ICERR_BADPARAM;
1600 /* check input format if given */
1601 if (lpbiIn != NULL) {
1602 if (!isSupportedMRLE(lpbiIn))
1603 return ICERR_BADFORMAT;
1606 /* check output format if given */
1607 if (lpbiOut != NULL) {
1608 if (!isSupportedDIB(lpbiOut))
1609 hr = ICERR_BADFORMAT;
1611 if (lpbiIn != NULL) {
1612 if (lpbiIn->biWidth != lpbiOut->biWidth)
1613 hr = ICERR_UNSUPPORTED;
1614 if (lpbiIn->biHeight != lpbiOut->biHeight)
1615 hr = ICERR_UNSUPPORTED;
1616 if (lpbiIn->biBitCount > lpbiOut->biBitCount)
1617 hr = ICERR_UNSUPPORTED;
1621 return hr;
1624 static LRESULT DecompressBegin(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1625 LPCBITMAPINFOHEADER lpbiOut)
1627 const RGBQUAD *rgbIn;
1628 const RGBQUAD *rgbOut;
1629 UINT i;
1631 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1633 /* pre-condition */
1634 assert(pi != NULL);
1636 /* check parameters */
1637 if (lpbiIn == NULL || lpbiOut == NULL)
1638 return ICERR_BADPARAM;
1639 if (DecompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1640 return ICERR_BADFORMAT;
1642 /* FIXME: cannot compress and decompress at a time! */
1643 if (pi->bCompress) {
1644 FIXME("cannot compress and decompress at same time!\n");
1645 return ICERR_ERROR;
1648 if (pi->bDecompress)
1649 DecompressEnd(pi);
1651 rgbIn = (const RGBQUAD*)((const BYTE*)lpbiIn + lpbiIn->biSize);
1652 rgbOut = (const RGBQUAD*)((const BYTE*)lpbiOut + lpbiOut->biSize);
1654 switch (lpbiOut->biBitCount) {
1655 case 4:
1656 case 8:
1657 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed);
1658 if (pi->palette_map == NULL)
1659 return ICERR_MEMORY;
1661 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1662 pi->palette_map[i] = MSRLE32_GetNearestPaletteIndex(lpbiOut->biClrUsed, rgbOut, rgbIn[i]);
1664 break;
1665 case 15:
1666 case 16:
1667 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed * 2);
1668 if (pi->palette_map == NULL)
1669 return ICERR_MEMORY;
1671 for (i = 0; i < lpbiIn->biClrUsed; i++) {
1672 WORD color;
1674 if (lpbiOut->biBitCount == 15)
1675 color = ((rgbIn[i].rgbRed >> 3) << 10)
1676 | ((rgbIn[i].rgbGreen >> 3) << 5) | (rgbIn[i].rgbBlue >> 3);
1677 else
1678 color = ((rgbIn[i].rgbRed >> 3) << 11)
1679 | ((rgbIn[i].rgbGreen >> 3) << 5) | (rgbIn[i].rgbBlue >> 3);
1681 pi->palette_map[i * 2 + 1] = color >> 8;
1682 pi->palette_map[i * 2 + 0] = color & 0xFF;
1684 break;
1685 case 24:
1686 case 32:
1687 pi->palette_map = LocalAlloc(LPTR, lpbiIn->biClrUsed * sizeof(RGBQUAD));
1688 if (pi->palette_map == NULL)
1689 return ICERR_MEMORY;
1690 memcpy(pi->palette_map, rgbIn, lpbiIn->biClrUsed * sizeof(RGBQUAD));
1691 break;
1694 pi->bDecompress = TRUE;
1696 return ICERR_OK;
1699 static LRESULT Decompress(CodecInfo *pi, ICDECOMPRESS *pic, DWORD dwSize)
1701 TRACE("(%p,%p,%u)\n",pi,pic,dwSize);
1703 /* pre-condition */
1704 assert(pi != NULL);
1706 /* check parameters */
1707 if (pic == NULL)
1708 return ICERR_BADPARAM;
1709 if (pic->lpbiInput == NULL || pic->lpInput == NULL ||
1710 pic->lpbiOutput == NULL || pic->lpOutput == NULL)
1711 return ICERR_BADPARAM;
1713 /* check formats */
1714 if (! pi->bDecompress) {
1715 LRESULT hr = DecompressBegin(pi, pic->lpbiInput, pic->lpbiOutput);
1716 if (hr != ICERR_OK)
1717 return hr;
1718 } else if (DecompressQuery(pi, pic->lpbiInput, pic->lpbiOutput) != ICERR_OK)
1719 return ICERR_BADFORMAT;
1721 assert(pic->lpbiInput->biWidth == pic->lpbiOutput->biWidth);
1722 assert(pic->lpbiInput->biHeight == pic->lpbiOutput->biHeight);
1724 pic->lpbiOutput->biSizeImage = DIBWIDTHBYTES(*pic->lpbiOutput) * pic->lpbiOutput->biHeight;
1725 if (pic->lpbiInput->biBitCount == 4)
1726 return MSRLE32_DecompressRLE4(pi, pic->lpbiOutput, pic->lpInput, pic->lpOutput);
1727 else
1728 return MSRLE32_DecompressRLE8(pi, pic->lpbiOutput, pic->lpInput, pic->lpOutput);
1731 static LRESULT DecompressEnd(CodecInfo *pi)
1733 TRACE("(%p)\n",pi);
1735 /* pre-condition */
1736 assert(pi != NULL);
1738 pi->bDecompress = FALSE;
1740 if (pi->palette_map != NULL) {
1741 LocalFree(pi->palette_map);
1742 pi->palette_map = NULL;
1745 return ICERR_OK;
1748 static LRESULT DecompressGetPalette(CodecInfo *pi, LPCBITMAPINFOHEADER lpbiIn,
1749 LPBITMAPINFOHEADER lpbiOut)
1751 int size;
1753 TRACE("(%p,%p,%p)\n",pi,lpbiIn,lpbiOut);
1755 /* pre-condition */
1756 assert(pi != NULL);
1758 /* check parameters */
1759 if (lpbiIn == NULL || lpbiOut == NULL)
1760 return ICERR_BADPARAM;
1762 if (DecompressQuery(pi, lpbiIn, lpbiOut) != ICERR_OK)
1763 return ICERR_BADFORMAT;
1765 if (lpbiOut->biBitCount > 8)
1766 return ICERR_ERROR;
1768 if (lpbiIn->biBitCount <= 8) {
1769 if (lpbiIn->biClrUsed > 0)
1770 size = lpbiIn->biClrUsed;
1771 else
1772 size = (1 << lpbiIn->biBitCount);
1774 lpbiOut->biClrUsed = size;
1776 memcpy((LPBYTE)lpbiOut + lpbiOut->biSize, (const BYTE*)lpbiIn + lpbiIn->biSize, size * sizeof(RGBQUAD));
1777 } /* else could never occur ! */
1779 return ICERR_OK;
1782 /* DriverProc - entry point for an installable driver */
1783 LRESULT CALLBACK MSRLE32_DriverProc(DWORD_PTR dwDrvID, HDRVR hDrv, UINT uMsg,
1784 LPARAM lParam1, LPARAM lParam2)
1786 CodecInfo *pi = (CodecInfo*)dwDrvID;
1788 TRACE("(%lx,%p,0x%04X,0x%08lX,0x%08lX)\n", dwDrvID, hDrv, uMsg, lParam1, lParam2);
1790 switch (uMsg) {
1791 /* standard driver messages */
1792 case DRV_LOAD:
1793 return DRVCNF_OK;
1794 case DRV_OPEN:
1795 return (LRESULT)Open((ICOPEN*)lParam2);
1796 case DRV_CLOSE:
1797 if (dwDrvID != 0xFFFF0000 && (LPVOID)dwDrvID != NULL)
1798 Close(pi);
1799 return DRVCNF_OK;
1800 case DRV_ENABLE:
1801 case DRV_DISABLE:
1802 return DRVCNF_OK;
1803 case DRV_FREE:
1804 return DRVCNF_OK;
1805 case DRV_QUERYCONFIGURE:
1806 return DRVCNF_CANCEL; /* FIXME */
1807 case DRV_CONFIGURE:
1808 return DRVCNF_OK; /* FIXME */
1809 case DRV_INSTALL:
1810 case DRV_REMOVE:
1811 return DRVCNF_OK;
1813 /* installable compression manager messages */
1814 case ICM_CONFIGURE:
1815 FIXME("ICM_CONFIGURE (%ld)\n",lParam1);
1816 if (lParam1 == -1)
1817 return ICERR_UNSUPPORTED; /* FIXME */
1818 else
1819 return Configure(pi, (HWND)lParam1);
1820 case ICM_ABOUT:
1821 if (lParam1 == -1)
1822 return ICERR_OK;
1823 else
1824 return About(pi, (HWND)lParam1);
1825 case ICM_GETSTATE:
1826 case ICM_SETSTATE:
1827 return 0; /* no state */
1828 case ICM_GETINFO:
1829 return GetInfo(pi, (ICINFO*)lParam1, (DWORD)lParam2);
1830 case ICM_GETDEFAULTQUALITY:
1831 if ((LPVOID)lParam1 != NULL) {
1832 *((LPDWORD)lParam1) = MSRLE32_DEFAULTQUALITY;
1833 return ICERR_OK;
1835 break;
1836 case ICM_COMPRESS_GET_FORMAT:
1837 return CompressGetFormat(pi, (LPCBITMAPINFOHEADER)lParam1,
1838 (LPBITMAPINFOHEADER)lParam2);
1839 case ICM_COMPRESS_GET_SIZE:
1840 return CompressGetSize(pi, (LPCBITMAPINFOHEADER)lParam1,
1841 (LPCBITMAPINFOHEADER)lParam2);
1842 case ICM_COMPRESS_QUERY:
1843 return CompressQuery(pi, (LPCBITMAPINFOHEADER)lParam1,
1844 (LPCBITMAPINFOHEADER)lParam2);
1845 case ICM_COMPRESS_BEGIN:
1846 return CompressBegin(pi, (LPCBITMAPINFOHEADER)lParam1,
1847 (LPCBITMAPINFOHEADER)lParam2);
1848 case ICM_COMPRESS:
1849 return Compress(pi, (ICCOMPRESS*)lParam1, (DWORD)lParam2);
1850 case ICM_COMPRESS_END:
1851 return CompressEnd(pi);
1852 case ICM_DECOMPRESS_GET_FORMAT:
1853 return DecompressGetFormat(pi, (LPCBITMAPINFOHEADER)lParam1,
1854 (LPBITMAPINFOHEADER)lParam2);
1855 case ICM_DECOMPRESS_QUERY:
1856 return DecompressQuery(pi, (LPCBITMAPINFOHEADER)lParam1,
1857 (LPCBITMAPINFOHEADER)lParam2);
1858 case ICM_DECOMPRESS_BEGIN:
1859 return DecompressBegin(pi, (LPCBITMAPINFOHEADER)lParam1,
1860 (LPCBITMAPINFOHEADER)lParam2);
1861 case ICM_DECOMPRESS:
1862 return Decompress(pi, (ICDECOMPRESS*)lParam1, (DWORD)lParam2);
1863 case ICM_DECOMPRESS_END:
1864 return DecompressEnd(pi);
1865 case ICM_DECOMPRESS_SET_PALETTE:
1866 FIXME("(...) -> SetPalette(%p,%p,%p): stub!\n", pi, (LPVOID)lParam1, (LPVOID)lParam2);
1867 return ICERR_UNSUPPORTED;
1868 case ICM_DECOMPRESS_GET_PALETTE:
1869 return DecompressGetPalette(pi, (LPBITMAPINFOHEADER)lParam1,
1870 (LPBITMAPINFOHEADER)lParam2);
1871 case ICM_GETDEFAULTKEYFRAMERATE:
1872 if ((LPVOID)lParam1 != NULL)
1873 *(LPDWORD)lParam1 = 15;
1874 return ICERR_OK;
1875 default:
1876 if (uMsg < DRV_USER)
1877 return DefDriverProc(dwDrvID, hDrv, uMsg, lParam1, lParam2);
1878 else
1879 FIXME("Unknown message uMsg=0x%08X lParam1=0x%08lX lParam2=0x%08lX\n",uMsg,lParam1,lParam2);
1882 return ICERR_UNSUPPORTED;
1885 /* DllMain - library initialization code */
1886 BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
1888 TRACE("(%p,%d,%p)\n",hModule,dwReason,lpReserved);
1890 switch (dwReason) {
1891 case DLL_PROCESS_ATTACH:
1892 DisableThreadLibraryCalls(hModule);
1893 MSRLE32_hModule = hModule;
1894 break;
1897 return TRUE;