d3d9: Replace color_near() with color_match().
[wine] / dlls / msvidc32 / msvideo1.c
1 /*
2  * Microsoft Video-1 Decoder
3  * Copyright (C) 2003 the ffmpeg project
4  *
5  * Portions Copyright (C) 2004 Mike McCormack for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  */
22
23 /**
24  * @file msvideo1.c
25  * Microsoft Video-1 Decoder by Mike Melanson (melanson@pcisys.net)
26  * For more information about the MS Video-1 format, visit:
27  *   http://www.pcisys.net/~melanson/codecs/
28  *
29  * This decoder outputs either PAL8 or RGB555 data, depending on the
30  * whether a RGB palette was passed through palctrl;
31  * if it's present, then the data is PAL8; RGB555 otherwise.
32  */
33
34 #include <stdarg.h>
35 #include "windef.h"
36 #include "winbase.h"
37 #include "wingdi.h"
38 #include "winuser.h" 
39 #include "commdlg.h"
40 #include "vfw.h"
41 #include "mmsystem.h"
42 #include "msvidc32_private.h"
43  
44 #include "wine/debug.h"
45  
46 WINE_DEFAULT_DEBUG_CHANNEL(msvidc32); 
47
48 static HINSTANCE MSVIDC32_hModule;
49
50 #define CRAM_MAGIC mmioFOURCC('C', 'R', 'A', 'M')
51 #define MSVC_MAGIC mmioFOURCC('M', 'S', 'V', 'C')
52 #define WHAM_MAGIC mmioFOURCC('W', 'H', 'A', 'M')
53
54 #define PALETTE_COUNT 256
55 #define LE_16(x)  ((((const uint8_t *)(x))[1] << 8) | ((const uint8_t *)(x))[0])
56
57 /* FIXME - check the stream size */
58 #define CHECK_STREAM_PTR(n) \
59   if ((stream_ptr + n) > buf_size ) { \
60     WARN("stream_ptr out of bounds (%d >= %d)\n", \
61       stream_ptr + n, buf_size); \
62     return; \
63   }
64
65 typedef BYTE uint8_t;
66
67 typedef struct Msvideo1Context {
68     DWORD dwMagic;
69     int mode_8bit;  /* if it's not 8-bit, it's 16-bit */
70 } Msvideo1Context;
71
72 static void 
73 msvideo1_decode_8bit( int width, int height, const unsigned char *buf, int buf_size,
74                       unsigned char *pixels, int stride)
75 {
76     int block_ptr, pixel_ptr;
77     int total_blocks;
78     int pixel_x, pixel_y;  /* pixel width and height iterators */
79     int block_x, block_y;  /* block width and height iterators */
80     int blocks_wide, blocks_high;  /* width and height in 4x4 blocks */
81     int block_inc;
82     int row_dec;
83
84     /* decoding parameters */
85     int stream_ptr;
86     unsigned char byte_a, byte_b;
87     unsigned short flags;
88     int skip_blocks;
89     unsigned char colors[8];
90
91     stream_ptr = 0;
92     skip_blocks = 0;
93     blocks_wide = width / 4;
94     blocks_high = height / 4;
95     total_blocks = blocks_wide * blocks_high;
96     block_inc = 4;
97     row_dec = stride + 4;
98
99     for (block_y = blocks_high; block_y > 0; block_y--) {
100         block_ptr = ((block_y * 4) - 1) * stride;
101         for (block_x = blocks_wide; block_x > 0; block_x--) {
102             /* check if this block should be skipped */
103             if (skip_blocks) {
104                 block_ptr += block_inc;
105                 skip_blocks--;
106                 total_blocks--;
107                 continue;
108             }
109
110             pixel_ptr = block_ptr;
111
112             /* get the next two bytes in the encoded data stream */
113             CHECK_STREAM_PTR(2);
114             byte_a = buf[stream_ptr++];
115             byte_b = buf[stream_ptr++];
116
117             /* check if the decode is finished */
118             if ((byte_a == 0) && (byte_b == 0) && (total_blocks == 0))
119                 return;
120             else if ((byte_b & 0xFC) == 0x84) {
121                 /* skip code, but don't count the current block */
122                 skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1;
123             } else if (byte_b < 0x80) {
124                 /* 2-color encoding */
125                 flags = (byte_b << 8) | byte_a;
126
127                 CHECK_STREAM_PTR(2);
128                 colors[0] = buf[stream_ptr++];
129                 colors[1] = buf[stream_ptr++];
130
131                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
132                     for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
133                     {
134 #if ORIGINAL
135                         pixels[pixel_ptr++] = colors[(flags & 0x1) ^ 1];
136 #else
137                         pixels[width*(height-(pixel_ptr/width)-1) + 
138                                pixel_ptr%width] = 
139                                colors[(flags & 0x1) ^ 1];
140                         pixel_ptr++;
141 #endif
142                     }
143                     pixel_ptr -= row_dec;
144                 }
145             } else if (byte_b >= 0x90) {
146                 /* 8-color encoding */
147                 flags = (byte_b << 8) | byte_a;
148
149                 CHECK_STREAM_PTR(8);
150                 memcpy(colors, &buf[stream_ptr], 8);
151                 stream_ptr += 8;
152
153                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
154                     for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
155                     {
156 #if ORIGINAL
157                         pixels[pixel_ptr++] = 
158                             colors[((pixel_y & 0x2) << 1) + 
159                                 (pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
160 #else
161                         pixels[width*(height-(pixel_ptr/width)-1) + 
162                                pixel_ptr%width] = 
163                             colors[((pixel_y & 0x2) << 1) + 
164                                 (pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
165                         pixel_ptr++;
166 #endif
167                     }
168                     pixel_ptr -= row_dec;
169                 }
170             } else {
171                 /* 1-color encoding */
172                 colors[0] = byte_a;
173
174                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
175                     for (pixel_x = 0; pixel_x < 4; pixel_x++)
176                     {
177 #if ORIGINAL
178                         pixels[pixel_ptr++] = colors[0];
179 #else
180                         pixels[width*(height-(pixel_ptr/width)-1) + 
181                                pixel_ptr%width] = colors[0];
182                         pixel_ptr++;
183 #endif
184                     }
185                     pixel_ptr -= row_dec;
186                 }
187             }
188
189             block_ptr += block_inc;
190             total_blocks--;
191         }
192     }
193 }
194
195 static void
196 msvideo1_decode_16bit( int width, int height, const unsigned char *buf, int buf_size,
197                        unsigned short *pixels, int stride)
198 {
199     int block_ptr, pixel_ptr;
200     int total_blocks;
201     int pixel_x, pixel_y;  /* pixel width and height iterators */
202     int block_x, block_y;  /* block width and height iterators */
203     int blocks_wide, blocks_high;  /* width and height in 4x4 blocks */
204     int block_inc;
205     int row_dec;
206
207     /* decoding parameters */
208     int stream_ptr;
209     unsigned char byte_a, byte_b;
210     unsigned short flags;
211     int skip_blocks;
212     unsigned short colors[8];
213
214     stream_ptr = 0;
215     skip_blocks = 0;
216     blocks_wide = width / 4;
217     blocks_high = height / 4;
218     total_blocks = blocks_wide * blocks_high;
219     block_inc = 4;
220     row_dec = stride + 4;
221
222     for (block_y = blocks_high; block_y > 0; block_y--) {
223         block_ptr = ((block_y * 4) - 1) * stride;
224         for (block_x = blocks_wide; block_x > 0; block_x--) {
225             /* check if this block should be skipped */
226             if (skip_blocks) {
227                 block_ptr += block_inc;
228                 skip_blocks--;
229                 total_blocks--;
230                 continue;
231             }
232
233             pixel_ptr = block_ptr;
234
235             /* get the next two bytes in the encoded data stream */
236             CHECK_STREAM_PTR(2);
237             byte_a = buf[stream_ptr++];
238             byte_b = buf[stream_ptr++];
239
240             /* check if the decode is finished */
241             if ((byte_a == 0) && (byte_b == 0) && (total_blocks == 0)) {
242                 return;
243             } else if ((byte_b & 0xFC) == 0x84) {
244                 /* skip code, but don't count the current block */
245                 skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1;
246             } else if (byte_b < 0x80) {
247                 /* 2- or 8-color encoding modes */
248                 flags = (byte_b << 8) | byte_a;
249
250                 CHECK_STREAM_PTR(4);
251                 colors[0] = LE_16(&buf[stream_ptr]);
252                 stream_ptr += 2;
253                 colors[1] = LE_16(&buf[stream_ptr]);
254                 stream_ptr += 2;
255
256                 if (colors[0] & 0x8000) {
257                     /* 8-color encoding */
258                     CHECK_STREAM_PTR(12);
259                     colors[2] = LE_16(&buf[stream_ptr]);
260                     stream_ptr += 2;
261                     colors[3] = LE_16(&buf[stream_ptr]);
262                     stream_ptr += 2;
263                     colors[4] = LE_16(&buf[stream_ptr]);
264                     stream_ptr += 2;
265                     colors[5] = LE_16(&buf[stream_ptr]);
266                     stream_ptr += 2;
267                     colors[6] = LE_16(&buf[stream_ptr]);
268                     stream_ptr += 2;
269                     colors[7] = LE_16(&buf[stream_ptr]);
270                     stream_ptr += 2;
271
272                     for (pixel_y = 0; pixel_y < 4; pixel_y++) {
273                         for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
274                             pixels[pixel_ptr++] = 
275                                 colors[((pixel_y & 0x2) << 1) + 
276                                     (pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
277                         pixel_ptr -= row_dec;
278                     }
279                 } else {
280                     /* 2-color encoding */
281                     for (pixel_y = 0; pixel_y < 4; pixel_y++) {
282                         for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
283                             pixels[pixel_ptr++] = colors[(flags & 0x1) ^ 1];
284                         pixel_ptr -= row_dec;
285                     }
286                 }
287             } else {
288                 /* otherwise, it's a 1-color block */
289                 colors[0] = (byte_b << 8) | byte_a;
290
291                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
292                     for (pixel_x = 0; pixel_x < 4; pixel_x++)
293                         pixels[pixel_ptr++] = colors[0];
294                     pixel_ptr -= row_dec;
295                 }
296             }
297
298             block_ptr += block_inc;
299             total_blocks--;
300         }
301     }
302 }
303
304 static LRESULT
305 CRAM_DecompressQuery( Msvideo1Context *info, LPBITMAPINFO in, LPBITMAPINFO out )
306 {
307     TRACE("ICM_DECOMPRESS_QUERY %p %p %p\n", info, in, out);
308
309     if( (info==NULL) || (info->dwMagic!=CRAM_MAGIC) )
310         return ICERR_BADPARAM;
311
312     TRACE("planes = %d\n", in->bmiHeader.biPlanes );
313     TRACE("bpp    = %d\n", in->bmiHeader.biBitCount );
314     TRACE("height = %d\n", in->bmiHeader.biHeight );
315     TRACE("width  = %d\n", in->bmiHeader.biWidth );
316     TRACE("compr  = %x\n", in->bmiHeader.biCompression );
317
318     if( ( in->bmiHeader.biCompression != CRAM_MAGIC ) &&
319         ( in->bmiHeader.biCompression != MSVC_MAGIC ) &&
320         ( in->bmiHeader.biCompression != WHAM_MAGIC ) )
321         return ICERR_UNSUPPORTED;
322
323     if( ( in->bmiHeader.biBitCount != 16 ) &&
324         ( in->bmiHeader.biBitCount != 8 ) )
325     {
326         TRACE("can't do %d bpp\n", in->bmiHeader.biBitCount );
327         return ICERR_UNSUPPORTED;
328     }
329
330     /* output must be same dimensions as input */
331     if( out )
332     {
333         if( in->bmiHeader.biBitCount != out->bmiHeader.biBitCount )
334             return ICERR_UNSUPPORTED;
335         if( in->bmiHeader.biPlanes != out->bmiHeader.biPlanes )
336             return ICERR_UNSUPPORTED;
337         if( in->bmiHeader.biHeight != out->bmiHeader.biHeight )
338             return ICERR_UNSUPPORTED;
339         if( in->bmiHeader.biWidth != out->bmiHeader.biWidth )
340             return ICERR_UNSUPPORTED;
341     }
342
343     TRACE("OK!\n");
344
345     return ICERR_OK;
346 }
347
348 static LRESULT 
349 CRAM_DecompressGetFormat( Msvideo1Context *info, LPBITMAPINFO in, LPBITMAPINFO out )
350 {
351     DWORD size;
352
353     TRACE("ICM_DECOMPRESS_GETFORMAT %p %p %p\n", info, in, out);
354
355     if( (info==NULL) || (info->dwMagic!=CRAM_MAGIC) )
356         return ICERR_BADPARAM;
357
358     size = in->bmiHeader.biSize;
359     if (in->bmiHeader.biBitCount <= 8)
360         size += in->bmiHeader.biClrUsed * sizeof(RGBQUAD);
361
362     if( out )
363     {
364         memcpy( out, in, size );
365         out->bmiHeader.biCompression = BI_RGB;
366         out->bmiHeader.biSizeImage = in->bmiHeader.biHeight
367                                    * in->bmiHeader.biWidth *4;
368         return ICERR_OK;
369     }
370
371     return size;
372 }
373
374 static LRESULT CRAM_DecompressBegin( Msvideo1Context *info, LPBITMAPINFO in, LPBITMAPINFO out )
375 {
376     TRACE("ICM_DECOMPRESS_BEGIN %p %p %p\n", info, in, out);
377
378     if( (info==NULL) || (info->dwMagic!=CRAM_MAGIC) )
379         return ICERR_BADPARAM;
380
381     TRACE("bitmap is %d bpp\n", in->bmiHeader.biBitCount);
382     if( in->bmiHeader.biBitCount == 8 )
383         info->mode_8bit = 1;
384     else if( in->bmiHeader.biBitCount == 16 )
385         info->mode_8bit = 0;
386     else
387     {
388         ERR("Bad output format\n");
389         return ICERR_BADPARAM;
390     }
391
392     return ICERR_OK;
393 }
394
395 static LRESULT CRAM_Decompress( Msvideo1Context *info, ICDECOMPRESS *icd, DWORD size )
396 {
397     LONG width, height, stride, sz;
398     WORD bit_per_pixel;
399
400     TRACE("ICM_DECOMPRESS %p %p %d\n", info, icd, size);
401
402     if( (info==NULL) || (info->dwMagic!=CRAM_MAGIC) )
403         return ICERR_BADPARAM;
404
405     /* FIXME: flags are ignored */
406
407     width  = icd->lpbiInput->biWidth;
408     height = icd->lpbiInput->biHeight;
409     bit_per_pixel = icd->lpbiInput->biBitCount;
410     stride = width*bit_per_pixel/8;
411     sz = icd->lpbiInput->biSizeImage;
412
413     if (info->mode_8bit)
414     {
415         msvideo1_decode_8bit( width, height, icd->lpInput, sz,
416                               icd->lpOutput, stride);
417     }
418     else
419     {
420         msvideo1_decode_16bit( width, height, icd->lpInput, sz,
421                                icd->lpOutput, stride);
422     }
423
424     return ICERR_OK;
425 }
426
427 static LRESULT CRAM_DecompressEx( Msvideo1Context *info, ICDECOMPRESSEX *icd, DWORD size )
428 {
429     LONG width, height, stride, sz;
430     WORD bit_per_pixel;
431
432     TRACE("ICM_DECOMPRESSEX %p %p %d\n", info, icd, size);
433
434     if( (info==NULL) || (info->dwMagic!=CRAM_MAGIC) )
435         return ICERR_BADPARAM;
436
437     /* FIXME: flags are ignored */
438
439     width  = icd->lpbiSrc->biWidth;
440     height = icd->lpbiSrc->biHeight;
441     bit_per_pixel = icd->lpbiSrc->biBitCount;
442     stride = width*bit_per_pixel/8;
443     sz = icd->lpbiSrc->biSizeImage;
444
445     if (info->mode_8bit)
446     {
447         msvideo1_decode_8bit( width, height, icd->lpSrc, sz, 
448                              icd->lpDst, stride);
449     }
450     else
451     {
452         msvideo1_decode_16bit( width, height, icd->lpSrc, sz,
453                               icd->lpDst, stride);
454     }
455
456     return ICERR_OK;
457 }
458
459 static LRESULT CRAM_GetInfo( const Msvideo1Context *info, ICINFO *icinfo, DWORD dwSize )
460 {
461     if (!icinfo) return sizeof(ICINFO);
462     if (dwSize < sizeof(ICINFO)) return 0;
463
464     icinfo->dwSize = sizeof(ICINFO);
465     icinfo->fccType = ICTYPE_VIDEO;
466     icinfo->fccHandler = info ? info->dwMagic : CRAM_MAGIC;
467     icinfo->dwFlags = 0;
468     icinfo->dwVersion = ICVERSION;
469     icinfo->dwVersionICM = ICVERSION;
470
471     LoadStringW(MSVIDC32_hModule, IDS_NAME, icinfo->szName, sizeof(icinfo->szName)/sizeof(WCHAR));
472     LoadStringW(MSVIDC32_hModule, IDS_DESCRIPTION, icinfo->szDescription, sizeof(icinfo->szDescription)/sizeof(WCHAR));
473     /* msvfw32 will fill icinfo->szDriver for us */
474
475     return sizeof(ICINFO);
476 }
477
478 /***********************************************************************
479  *              DriverProc (MSVIDC32.@)
480  */
481 LRESULT WINAPI CRAM_DriverProc( DWORD_PTR dwDriverId, HDRVR hdrvr, UINT msg,
482                                 LPARAM lParam1, LPARAM lParam2 )
483 {
484     Msvideo1Context *info = (Msvideo1Context *) dwDriverId;
485     LRESULT r = ICERR_UNSUPPORTED;
486
487     TRACE("%ld %p %04x %08lx %08lx\n", dwDriverId, hdrvr, msg, lParam1, lParam2);
488
489     switch( msg )
490     {
491     case DRV_LOAD:
492         TRACE("Loaded\n");
493         r = 1;
494         break;
495
496     case DRV_ENABLE:
497         break;
498
499     case DRV_OPEN:
500     {
501         ICINFO *icinfo = (ICINFO *)lParam2;
502
503         TRACE("Opened\n");
504
505         if (icinfo && icinfo->fccType != ICTYPE_VIDEO) return 0;
506
507         info = HeapAlloc( GetProcessHeap(), 0, sizeof (Msvideo1Context) );
508         if( info )
509         {
510             memset( info, 0, sizeof info );
511             info->dwMagic = CRAM_MAGIC;
512         }
513         r = (LRESULT) info;
514         break;
515     }
516
517     case DRV_CLOSE:
518         HeapFree( GetProcessHeap(), 0, info );
519         break;
520
521     case DRV_DISABLE:
522         break;
523
524     case DRV_FREE:
525         break;
526
527     case ICM_GETINFO:
528         r = CRAM_GetInfo( info, (ICINFO *)lParam1, (DWORD)lParam2 );
529         break;
530
531     case ICM_DECOMPRESS_QUERY:
532         r = CRAM_DecompressQuery( info, (LPBITMAPINFO) lParam1,
533                                        (LPBITMAPINFO) lParam2 );
534         break;
535
536     case ICM_DECOMPRESS_GET_FORMAT:
537         r = CRAM_DecompressGetFormat( info, (LPBITMAPINFO) lParam1,
538                                        (LPBITMAPINFO) lParam2 );
539         break;
540
541     case ICM_DECOMPRESS_GET_PALETTE:
542         FIXME("ICM_DECOMPRESS_GET_PALETTE\n");
543         break;
544
545     case ICM_DECOMPRESSEX_QUERY:
546         FIXME("ICM_DECOMPRESSEX_QUERY\n");
547         break;
548
549     case ICM_DECOMPRESS:
550         r = CRAM_Decompress( info, (ICDECOMPRESS*) lParam1,
551                                   (DWORD) lParam2 );
552         break;
553
554     case ICM_DECOMPRESS_BEGIN:
555         r = CRAM_DecompressBegin( info, (LPBITMAPINFO) lParam1,
556                                        (LPBITMAPINFO) lParam2 );
557         break;
558
559     case ICM_DECOMPRESSEX:
560         r = CRAM_DecompressEx( info, (ICDECOMPRESSEX*) lParam1,
561                                   (DWORD) lParam2 );
562         break;
563
564     case ICM_COMPRESS_QUERY:
565         FIXME("compression not implemented\n");
566         r = ICERR_BADFORMAT;
567         break;
568
569     case ICM_CONFIGURE:
570         r = ICERR_UNSUPPORTED;
571         break;
572
573     default:
574         FIXME("Unknown message: %04x %ld %ld\n", msg, lParam1, lParam2);
575     }
576
577     return r;
578 }
579
580 /***********************************************************************
581  *              DllMain
582  */
583 BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
584 {
585     TRACE("(%p,%d,%p)\n", hModule, dwReason, lpReserved);
586
587     switch (dwReason)
588     {
589     case DLL_PROCESS_ATTACH:
590         DisableThreadLibraryCalls(hModule);
591         MSVIDC32_hModule = hModule;
592         break;
593
594     case DLL_PROCESS_DETACH:
595         break;
596     }
597     return TRUE;
598 }