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
20 * - some improvements possible
21 * - implement DecompressSetPalette? -- do we need it for anything?
26 #include "msrle_private.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
)
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
);
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
)
104 assert(lpbi
!= NULL
);
106 if (lpbi
->biSize
< sizeof(BITMAPINFOHEADER
) ||
110 if (lpbi
->biCompression
== BI_RLE4
) {
111 if (lpbi
->biBitCount
!= 4 ||
112 (lpbi
->biWidth
% 2) != 0)
114 } else if (lpbi
->biCompression
== BI_RLE8
) {
115 if (lpbi
->biBitCount
!= 8)
123 static BOOL
isSupportedDIB(LPCBITMAPINFOHEADER lpbi
)
126 assert(lpbi
!= NULL
);
128 /* check structure version/planes/compression */
129 if (lpbi
->biSize
< sizeof(BITMAPINFOHEADER
) ||
132 if (lpbi
->biCompression
!= BI_RGB
&&
133 lpbi
->biCompression
!= BI_BITFIELDS
)
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)
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)
159 static BYTE
MSRLE32_GetNearestPaletteIndex(UINT count
, const RGBQUAD
*clrs
, RGBQUAD clr
)
161 INT diff
= 0x00FFFFFF;
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
);
186 /*****************************************************************************/
188 void computeInternalFrame(CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
, const BYTE
*lpIn
)
190 WORD wIntensityTbl
[256];
191 DWORD lInLine
, lOutLine
;
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);
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
++) {
217 switch (lpbiIn
->biBitCount
) {
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];
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)];
231 for (x
= 0; x
< lpbiIn
->biWidth
; x
++)
232 lpOut
[x
] = wIntensityTbl
[lpIn
[x
]];
241 static LONG
MSRLE32_GetMaxCompressedSize(LPCBITMAPINFOHEADER lpbi
)
246 assert(lpbi
!= NULL
);
248 a
= lpbi
->biWidth
/ 255;
249 b
= lpbi
->biWidth
% 255;
250 if (lpbi
->biBitCount
<= 4) {
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
)
269 assert(lpA
&& lpB
&& lDist
>= 0 && width
> 0);
280 while (pos
+ 1 < width
) {
284 if (pos
+ 1 >= width
)
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 */
298 } else if (lpP
!= NULL
&& ColorCmp(lpP
[pos
], lpB
[pos
]) <= lDist
) {
299 /* 'compare' with previous frame for end of diff */
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
)
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
)
331 if (pos
+ 2 < width
&& ColorCmp(lpB
[pos
+1], lpB
[pos
+2]) <= lDist
)
333 } else if (lpP
!= NULL
&& ColorCmp(lpP
[pos
], lpB
[pos
]) <= lDist
) {
334 /* 'compare' with previous frame for end of diff */
337 for (count2
= 0, pos
++; pos
< width
&& count2
<= 5; pos
++, count2
++) {
338 if (ColorCmp(lpP
[pos
], lpB
[pos
]) > lDist
)
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
,
357 LPBYTE lpOut
= *ppOut
;
361 /* try to encode as many pixel as possible */
365 if (pos
< lpbi
->biWidth
) {
367 for (++count
; pos
+ 1 < lpbi
->biWidth
; ) {
369 if (ColorCmp(clr1
, lpC
[pos
]) > lDist
)
372 if (pos
+ 1 >= lpbi
->biWidth
)
375 if (ColorCmp(clr2
, lpC
[pos
]) > lDist
)
382 /* add some pixel for absoluting if possible */
383 count
+= countDiffRLE4(lpP
, lpC
- 1, lpC
, pos
-1, lDist
, lpbi
->biWidth
);
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 */
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);
403 for (i
= 0; i
< size
; i
+= 2) {
404 clr1
= pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)];
407 clr2
= pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)];
412 *lpOut
++ = (clr1
<< 4) | clr2
;
419 /* too little for absoluting so we must encode them */
423 clr1
= pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)];
426 clr2
= pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)];
431 *lpOut
++ = (clr1
<< 4) | clr2
;
434 /* encode count pixel(s) */
435 clr1
= ((pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)] << 4) |
436 pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
+ 1)]);
440 INT size
= min(count
, 254);
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
,
459 LPBYTE lpOut
= *ppOut
;
463 assert(lpbi
->biBitCount
<= 8);
464 assert(lpbi
->biCompression
== BI_RGB
);
466 /* try to encode as much as possible */
469 for (count
= 1; pos
< lpbi
->biWidth
; count
++) {
470 if (ColorCmp(clr
, lpC
[pos
++]) > 0)
475 /* add some more pixels for absoluting if possible */
476 count
+= countDiffRLE8(lpP
, lpC
- 1, lpC
, pos
-1, 0, lpbi
->biWidth
);
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 */
487 INT size
= min(count
, 255);
488 int extra_byte
= size
% 2;
490 *lpSizeImage
+= 2 + size
+ extra_byte
;
494 for (i
= 0; i
< size
; i
++) {
495 *lpOut
++ = pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)];
502 /* too little for absoluting so we must encode them even if it's expensive! */
505 *lpSizeImage
+= 2 * count
;
507 *lpOut
++ = pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)];
512 *lpOut
++ = pi
->palette_map
[GetRawPixel(lpbi
,lpIn
,x
)];
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
)
526 INT size
= min(count
, 255);
540 LRESULT
MSRLE32_CompressRLE4(const CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
541 const BYTE
*lpIn
, LPBITMAPINFOHEADER lpbiOut
,
542 LPBYTE lpOut
, BOOL isKey
)
546 LPBYTE lpOutStart
= lpOut
;
549 assert(pi
!= NULL
&& lpbiOut
!= NULL
);
550 assert(lpIn
!= NULL
&& lpOut
!= NULL
);
551 assert(pi
->pCurFrame
!= NULL
);
554 lInLine
= DIBWIDTHBYTES(*lpbiIn
);
555 lLine
= WIDTHBYTES(lpbiOut
->biWidth
* 16) / 2;
557 lpbiOut
->biSizeImage
= 0;
559 /* keyframe -- convert internal frame to output format */
562 for (y
= 0; y
< lpbiOut
->biHeight
; y
++) {
566 x
= MSRLE32_CompressRLE4Line(pi
, NULL
, lpC
, lpbiIn
, lpIn
, 0, x
,
567 &lpOut
, &lpbiOut
->biSizeImage
);
568 } while (x
< lpbiOut
->biWidth
);
573 /* add EOL -- end of line */
574 lpbiOut
->biSizeImage
+= 2;
576 lpOut
+= sizeof(WORD
);
577 assert(lpOut
== (lpOutStart
+ lpbiOut
->biSizeImage
));
580 /* delta-frame -- compute delta between last and this internal frame */
585 assert(pi
->pPrevFrame
!= NULL
);
587 lpP
= pi
->pPrevFrame
;
591 for (y
= 0; y
< lpbiOut
->biHeight
; y
++) {
599 for (count
= 0, pos
= x
; pos
< lpbiOut
->biWidth
; pos
++, count
++) {
600 if (ColorCmp(lpP
[pos
], lpC
[pos
]) > 0)
604 if (pos
== lpbiOut
->biWidth
&& count
> 8) {
605 /* (count > 8) secures that we will save space */
608 } else if (jumpy
|| jumpx
!= pos
) {
613 /* can only jump in positive direction -- jump until EOL, EOL */
614 INT w
= lpbiOut
->biWidth
- jumpx
;
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
626 lpbiOut
->biSizeImage
+= 4;
629 *lpOut
= min(w
, 255);
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;
646 *lpOut
= min(pos
- jumpx
, 255);
649 *lpOut
= min(jumpy
, 255);
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
);
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 */
688 assert(lpOut
== (lpOutStart
+ lpbiOut
->biSizeImage
));
693 LRESULT
MSRLE32_CompressRLE8(const CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
694 const BYTE
*lpIn
, LPBITMAPINFOHEADER lpbiOut
,
695 LPBYTE lpOut
, BOOL isKey
)
699 LPBYTE lpOutStart
= lpOut
;
701 assert(pi
!= NULL
&& lpbiOut
!= NULL
);
702 assert(lpIn
!= NULL
&& lpOut
!= NULL
);
703 assert(pi
->pCurFrame
!= NULL
);
706 lInLine
= DIBWIDTHBYTES(*lpbiIn
);
707 lLine
= WIDTHBYTES(lpbiOut
->biWidth
* 16) / 2;
709 lpbiOut
->biSizeImage
= 0;
711 /* keyframe -- convert internal frame to output format */
714 for (y
= 0; y
< lpbiOut
->biHeight
; y
++) {
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
);
726 /* add EOL -- end of line */
727 lpbiOut
->biSizeImage
+= 2;
728 *((LPWORD
)lpOut
) = 0;
729 lpOut
+= sizeof(WORD
);
730 assert(lpOut
== (lpOutStart
+ lpbiOut
->biSizeImage
));
733 /* delta-frame -- compute delta between last and this internal frame */
738 assert(pi
->pPrevFrame
!= NULL
);
740 lpP
= pi
->pPrevFrame
;
744 for (y
= 0; y
< lpbiOut
->biHeight
; y
++) {
752 for (count
= 0, pos
= x
; pos
< lpbiOut
->biWidth
; pos
++, count
++) {
753 if (ColorCmp(lpP
[pos
], lpC
[pos
]) > 0)
757 if (pos
== lpbiOut
->biWidth
&& count
> 4) {
758 /* (count > 4) secures that we will save space */
761 } else if (jumpy
|| jumpx
!= pos
) {
766 /* can only jump in positive direction -- do an EOL then jump */
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;
786 *lpOut
= min(pos
- jumpx
, 255);
788 *lpOut
= min(jumpy
, 255);
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
);
811 /* add EOL -- end of line */
812 lpbiOut
->biSizeImage
+= 2;
813 *((LPWORD
)lpOut
) = 0;
814 lpOut
+= sizeof(WORD
);
815 assert(lpOut
== (lpOutStart
+ lpbiOut
->biSizeImage
));
820 lpbiOut
->biSizeImage
+= 2;
821 *((LPWORD
)lpOut
) = 0;
822 lpOut
+= sizeof(WORD
);
825 /* add EOI -- end of image */
826 lpbiOut
->biSizeImage
+= 2;
829 assert(lpOut
== (lpOutStart
+ lpbiOut
->biSizeImage
));
834 /*****************************************************************************/
836 static LRESULT
MSRLE32_DecompressRLE4(const CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbi
,
837 const BYTE
*lpIn
, LPBYTE lpOut
)
843 BOOL bEndFlag
= FALSE
;
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
);
862 case 0: /* EOL - end of line */
866 case 1: /* EOI - end of image */
870 pixel_ptr
+= *lpIn
++ * bytes_per_pixel
;
871 lpOut
+= *lpIn
++ * line_size
;
872 if (pixel_ptr
>= lpbi
->biWidth
* bytes_per_pixel
) {
877 default: /* absolute mode */
878 extra_byte
= (((code1
+ 1) & (~1)) / 2) & 0x01;
880 if (pixel_ptr
/bytes_per_pixel
+ code1
> lpbi
->biWidth
)
884 for (i
= 0; i
< code0
/ 2; i
++) {
885 if (bytes_per_pixel
== 1) {
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];
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
;
917 if (bytes_per_pixel
== 1) {
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];
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
;
935 /* if the RLE code is odd, skip a byte in the stream */
941 if (pixel_ptr
/bytes_per_pixel
+ code0
> lpbi
->biWidth
)
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
++) {
950 lpOut
[pixel_ptr
++] = c1
;
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
++) {
963 lpOut
[pixel_ptr
++] = hi1
;
964 lpOut
[pixel_ptr
++] = lo1
;
966 lpOut
[pixel_ptr
++] = hi2
;
967 lpOut
[pixel_ptr
++] = lo2
;
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
++) {
981 lpOut
[pixel_ptr
+ 0] = b1
;
982 lpOut
[pixel_ptr
+ 1] = g1
;
983 lpOut
[pixel_ptr
+ 2] = r1
;
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
);
998 static LRESULT
MSRLE32_DecompressRLE8(const CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbi
,
999 const BYTE
*lpIn
, LPBYTE lpOut
)
1001 int bytes_per_pixel
;
1004 BOOL bEndFlag
= FALSE
;
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
);
1023 case 0: /* EOL - end of line */
1027 case 1: /* EOI - end of image */
1031 pixel_ptr
+= *lpIn
++ * bytes_per_pixel
;
1032 lpOut
+= *lpIn
++ * line_size
;
1033 if (pixel_ptr
>= lpbi
->biWidth
* bytes_per_pixel
) {
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
);
1043 extra_byte
= code1
& 0x01;
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];
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 */
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
);
1072 if (bytes_per_pixel
== 1) {
1073 code1
= pi
->palette_map
[code1
];
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];
1081 lpOut
[pixel_ptr
+ 0] = hi
;
1082 lpOut
[pixel_ptr
+ 1] = lo
;
1083 pixel_ptr
+= bytes_per_pixel
;
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];
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
);
1103 /*****************************************************************************/
1105 static CodecInfo
* Open(LPICOPEN icinfo
)
1107 CodecInfo
* pi
= NULL
;
1109 if (icinfo
== NULL
) {
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
) {
1127 case mmioFOURCC('m','r','l','e'):
1128 icinfo
->fccHandler
= FOURCC_MRLE
;
1131 WARN("unknown FOURCC = 0x%08X(%4.4s) !\n",
1132 icinfo
->fccHandler
,(char*)&icinfo
->fccHandler
);
1136 pi
= LocalAlloc(LPTR
, sizeof(CodecInfo
));
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
);
1154 static LRESULT
Close(CodecInfo
*pi
)
1156 TRACE("(%p)\n", pi
);
1161 if (pi
->pPrevFrame
!= NULL
|| pi
->pCurFrame
!= NULL
)
1168 static LRESULT
GetInfo(const CodecInfo
*pi
, ICINFO
*icinfo
, DWORD dwSize
)
1173 /* check parameters */
1175 return sizeof(ICINFO
);
1176 if (dwSize
< sizeof(ICINFO
))
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
)
1201 static LRESULT
About(CodecInfo
*pi
, HWND hWnd
)
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
);
1217 static LRESULT
CompressGetFormat(CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
1218 LPBITMAPINFOHEADER lpbiOut
)
1222 TRACE("(%p,%p,%p)\n",pi
,lpbiIn
,lpbiOut
);
1227 /* check parameters -- need at least input format */
1228 if (lpbiIn
== NULL
) {
1229 if (lpbiOut
!= NULL
)
1230 return ICERR_BADPARAM
;
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
) {
1249 size
= (lpbiIn
->biBitCount
<= 4 ? 1 << 4 : 1 << 8);
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;
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
;
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
));
1291 static LRESULT
CompressGetSize(CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
1292 LPCBITMAPINFOHEADER lpbiOut
)
1297 TRACE("(%p,%p,%p)\n",pi
,lpbiIn
,lpbiOut
);
1299 /* check parameter -- need at least one format */
1300 if (lpbiIn
== NULL
&& lpbiOut
== NULL
)
1302 /* check if the given format is supported */
1303 if (CompressQuery(pi
, lpbiIn
, lpbiOut
) != ICERR_OK
)
1306 /* the worst case is coding the complete image in absolute mode. */
1308 return MSRLE32_GetMaxCompressedSize(lpbiIn
);
1310 return MSRLE32_GetMaxCompressedSize(lpbiOut
);
1313 static LRESULT
CompressQuery(const CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
1314 LPCBITMAPINFOHEADER lpbiOut
)
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
;
1356 static LRESULT
CompressBegin(CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
1357 LPCBITMAPINFOHEADER lpbiOut
)
1359 const RGBQUAD
*rgbIn
;
1360 const RGBQUAD
*rgbOut
;
1364 TRACE("(%p,%p,%p)\n",pi
,lpbiIn
,lpbiOut
);
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");
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
) {
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
) {
1403 pi
->palette_map
= LocalAlloc(LPTR
, lpbiIn
->biClrUsed
);
1404 if (pi
->palette_map
== NULL
) {
1406 return ICERR_MEMORY
;
1409 for (i
= 0; i
< lpbiIn
->biClrUsed
; i
++) {
1410 pi
->palette_map
[i
] = MSRLE32_GetNearestPaletteIndex(lpbiOut
->biClrUsed
, rgbOut
, rgbIn
[i
]);
1418 static LRESULT
Compress(CodecInfo
*pi
, ICCOMPRESS
* lpic
, DWORD dwSize
)
1423 TRACE("(%p,%p,%u)\n",pi
,lpic
,dwSize
);
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
);
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) {
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
);
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
)
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
);
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");
1502 *lpic
->lpckid
= MAKEAVICKID(cktypeDIBbits
,
1503 StreamFromFOURCC(*lpic
->lpckid
));
1508 if (lpic
->dwQuality
< 1000)
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);
1528 static LRESULT
CompressEnd(CodecInfo
*pi
)
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
;
1552 static LRESULT
DecompressGetFormat(CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
1553 LPBITMAPINFOHEADER lpbiOut
)
1557 TRACE("(%p,%p,%p)\n",pi
,lpbiIn
,lpbiOut
);
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
;
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
);
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
;
1621 static LRESULT
DecompressBegin(CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
1622 LPCBITMAPINFOHEADER lpbiOut
)
1624 const RGBQUAD
*rgbIn
;
1625 const RGBQUAD
*rgbOut
;
1628 TRACE("(%p,%p,%p)\n",pi
,lpbiIn
,lpbiOut
);
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");
1645 if (pi
->bDecompress
)
1648 rgbIn
= (const RGBQUAD
*)((const BYTE
*)lpbiIn
+ lpbiIn
->biSize
);
1649 rgbOut
= (const RGBQUAD
*)((const BYTE
*)lpbiOut
+ lpbiOut
->biSize
);
1651 switch (lpbiOut
->biBitCount
) {
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
]);
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
++) {
1671 if (lpbiOut
->biBitCount
== 15)
1672 color
= ((rgbIn
[i
].rgbRed
>> 3) << 10)
1673 | ((rgbIn
[i
].rgbGreen
>> 3) << 5) | (rgbIn
[i
].rgbBlue
>> 3);
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;
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
));
1691 pi
->bDecompress
= TRUE
;
1696 static LRESULT
Decompress(CodecInfo
*pi
, ICDECOMPRESS
*pic
, DWORD dwSize
)
1698 TRACE("(%p,%p,%u)\n",pi
,pic
,dwSize
);
1703 /* check parameters */
1705 return ICERR_BADPARAM
;
1706 if (pic
->lpbiInput
== NULL
|| pic
->lpInput
== NULL
||
1707 pic
->lpbiOutput
== NULL
|| pic
->lpOutput
== NULL
)
1708 return ICERR_BADPARAM
;
1711 if (! pi
->bDecompress
) {
1712 LRESULT hr
= DecompressBegin(pi
, pic
->lpbiInput
, pic
->lpbiOutput
);
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
);
1725 return MSRLE32_DecompressRLE8(pi
, pic
->lpbiOutput
, pic
->lpInput
, pic
->lpOutput
);
1728 static LRESULT
DecompressEnd(CodecInfo
*pi
)
1735 pi
->bDecompress
= FALSE
;
1737 if (pi
->palette_map
!= NULL
) {
1738 LocalFree(pi
->palette_map
);
1739 pi
->palette_map
= NULL
;
1745 static LRESULT
DecompressGetPalette(CodecInfo
*pi
, LPCBITMAPINFOHEADER lpbiIn
,
1746 LPBITMAPINFOHEADER lpbiOut
)
1750 TRACE("(%p,%p,%p)\n",pi
,lpbiIn
,lpbiOut
);
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)
1765 if (lpbiIn
->biBitCount
<= 8) {
1766 if (lpbiIn
->biClrUsed
> 0)
1767 size
= lpbiIn
->biClrUsed
;
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 ! */
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
);
1788 /* standard driver messages */
1792 return (LRESULT
)Open((ICOPEN
*)lParam2
);
1794 if (dwDrvID
!= 0xFFFF0000 && (LPVOID
)dwDrvID
!= NULL
)
1802 case DRV_QUERYCONFIGURE
:
1803 return DRVCNF_CANCEL
; /* FIXME */
1805 return DRVCNF_OK
; /* FIXME */
1810 /* installable compression manager messages */
1812 FIXME("ICM_CONFIGURE (%ld)\n",lParam1
);
1814 return ICERR_UNSUPPORTED
; /* FIXME */
1816 return Configure(pi
, (HWND
)lParam1
);
1821 return About(pi
, (HWND
)lParam1
);
1824 return 0; /* no state */
1826 return GetInfo(pi
, (ICINFO
*)lParam1
, (DWORD
)lParam2
);
1827 case ICM_GETDEFAULTQUALITY
:
1828 if ((LPVOID
)lParam1
!= NULL
) {
1829 *((LPDWORD
)lParam1
) = MSRLE32_DEFAULTQUALITY
;
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
);
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;
1873 if (uMsg
< DRV_USER
)
1874 return DefDriverProc(dwDrvID
, hDrv
, uMsg
, lParam1
, lParam2
);
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
);
1888 case DLL_PROCESS_ATTACH
:
1889 DisableThreadLibraryCalls(hModule
);
1890 MSRLE32_hModule
= hModule
;