home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Multimedia Jumpstart 1.1a / CD_ROM.BIN / develpmt / source / mergedib / mergedib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-25  |  19.6 KB  |  718 lines

  1. /* MergeDIB.c
  2.  *
  3.  * MergeDIB main loop etc.
  4.  */
  5. /* 
  6.      (C) Copyright Microsoft Corp. 1991-1992.  All rights reserved.
  7.  
  8.      You have a royalty-free right to use, modify, reproduce and 
  9.      distribute the Sample Files (and/or any modified version) in 
  10.      any way you find useful, provided that you agree that 
  11.      Microsoft has no warranty obligations or liability for any 
  12.      Sample Application Files which are modified. 
  13.      
  14.      
  15.  Merges a primary DIB (with any palette) and a secondary DIB (with a different
  16.  palette) into a single, merged DIB (with special palette).
  17.  
  18.  The special dib and palette are a combination of the two images and
  19.  palettes so that when the palette is gradually crossfaded (animated),
  20.  the first and second DIB are partially displayed.  At complete fade,
  21.  only one of the bitmaps is 'visible', while at a 50-50 mix, both
  22.  are equally visible (merged).  Pixels are not dithered betweent he
  23.  images, but are mixed in the palettes.
  24.  
  25.  This technique is limited in the number of pixels that have different
  26.  targets between bitmaps, but it can create very nice effects when just
  27.  text is 'faded' in for the target bitmap.
  28.  
  29.  This code is limited so that the two DIBs must be the same size, but this
  30.  limitation could easily be eliminated by creating an artificial bitmap
  31.  that is the desired size with a 'blank' (where blank is a chosen color)
  32.  background.  The smaller image could be centered or otherwise placed
  33.  in the background (easy to do using the DIB Driver).
  34.  
  35.      
  36.  */
  37.  
  38. #define WinAssert(x)    (x)
  39.  
  40. #include "nocrap.h"
  41. #include <windows.h>
  42. #include "MergeDIB.h"
  43. #include "dib.h"
  44. #include "gmem.h"
  45.  
  46.  
  47. /* globals */
  48. HWND        ghwndApp;    // main application window
  49. HMENU        ghmenuApp;    // main application menu
  50. HANDLE        ghInst;        // program instance handle
  51. GLOBALHANDLE    gahdib[3];    // primary/secondary/merged DIB
  52. HPALETTE    gahpal[3];    // primary/secondary/merged palette
  53. BOOL        giDIBViewed = PRIMARY_DIB; // which DIB is being viewed?
  54. NPLOGPALETTE    gaplogpal[3];    // two sets of colors for merged DIB
  55. WORD        gwFadeDirection = SB_LINEDOWN; // SB_LINEUP or SB_LINEDOWN
  56. BOOL        gfFading = FALSE; // currently doing fade?
  57. BOOL        gfPal;        // does display support palettes?
  58.  
  59.  
  60. /* fOK = CreateMergedDIB()
  61.  *
  62.  * Merge <gahdib[PRIMARY_DIB]> (with palette <gahpal[PRIMARY_DIB]>)
  63.  * and <gahdib[SECONDARY_DIB]> (with palette <gahpal[SECONDARY_DIB]>)
  64.  * into a single DIB <gahdib[MERGED_DIB]> (with palette <gahpal[MERGED_DIB]>).
  65.  * Also, create <gaplogpal> so that if the merged DIB's palette is set to
  66.  * <gaplogpal[PRIMARY_DIB]> then the merged DIB will look like the primary
  67.  * DIB, and if the merged DIB's palette is set to <gaplogpal[SECONDARY_DIB]>
  68.  * then the merged DIB will look like the secondary DIB.
  69.  *
  70.  * On success, return TRUE.  On error, display an error message and
  71.  * return FALSE.
  72.  */
  73. BOOL FAR PASCAL
  74. CreateMergedDIB(void)
  75. {
  76.     BOOL        fOK = TRUE;    // function return status
  77.     LPBITMAPINFOHEADER lpbiP;    // header of primary DIB
  78.     LPBITMAPINFOHEADER lpbiS;    // header of secondary DIB
  79.     LPBITMAPINFOHEADER lpbiM;    // header of merged DIB
  80.     BYTE huge *    pBitsP;        // ptr. into bits of primary DIB
  81.     BYTE huge *    pBitsS;        // ptr. into bits of secondary DIB
  82.     BYTE huge *    pBitsM;        // ptr. into bits of merged DIB
  83.     LPBYTE        pbMergeMap = NULL; // map (P,S) DIB to merged DIB
  84.     LPBYTE        pbCell;        // pointer into merge map
  85.     int        r, c;        // row/column of <aabMergMap>
  86.     int        x, y;        // coordinate in a DIB
  87.     DWORD        dwMergedColors = 0L; // no. colors in merged map
  88.     BYTE        bPal;        // palette index
  89.     HCURSOR        hcurPrev = NULL; // cursor before hourglass
  90.     int        i;
  91.     LPWORD        pw;
  92.  
  93.     /* show hourglass cursor */
  94.     hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  95.  
  96.     /* get pointers to the DIBs */
  97.     lpbiP = GLock(gahdib[PRIMARY_DIB]);
  98.     lpbiS = GLock(gahdib[SECONDARY_DIB]);
  99.  
  100.     /* create the merged DIB */
  101.     if ((gahdib[MERGED_DIB] = CreateDib(8,
  102.     (int) lpbiP->biWidth, (int) lpbiP->biHeight)) == NULL)
  103.     goto ERROR_OUTOFMEM;
  104.     lpbiM = GLock(gahdib[MERGED_DIB]);
  105.     for (pw = (LPWORD) ((LPSTR) lpbiM + lpbiM->biSize), i = 0; i < 256; i++)
  106.     *pw++ = i;
  107.     lpbiM->biClrUsed = 256;        // for now, will contain unused entries
  108.  
  109.     /* <pbMergeMap> is a 256x256 2-dimensional array of bytes;
  110.     * if a pixel in <gahdib[0]> contains palette entry number <i>
  111.     * and the pixel in the same location in <gahdib[0]> contains
  112.     * palette entry number <j>, then <pbMergeMap[i][j]> will contain
  113.     * <k> where <k> is the palette entry number in the merged palette
  114.     */
  115. #define MERGEMAP(r, c)    (pbMergeMap + ((((WORD) (r)) << 8) | (c)))
  116. #define UNUSEDENTRY    0xFF        // color combination not used
  117.     if ((pbMergeMap = (LPBYTE) GAllocPtr(0x10000L)) == NULL)
  118.     goto ERROR_OUTOFMEM;
  119.     for (pbCell = pbMergeMap, r = 0; r < 256; r++)
  120.     for (c = 0; c < 256; c++)
  121.         *pbCell++ = UNUSEDENTRY;
  122.  
  123.     /* palette entry <i> of the merged DIB will contain either the i-th
  124.     * entry of <gaplogpal[0]> (if the merged DIB is being displayed
  125.     * to look like the primary DIB), or the i-th entry of <gaplogpal[1]>
  126.     * (if the merged DIB is being displayed to look like the secondary
  127.     * DIB), or some value in between
  128.     */
  129.     for (i = PRIMARY_DIB; i <= MERGED_DIB; i++)
  130.     {
  131.     if ((gaplogpal[i] = (NPLOGPALETTE) LocalAlloc(LMEM_FIXED,
  132.         sizeof(LOGPALETTE) +
  133.         MAXPALSIZE * sizeof(PALETTEENTRY))) == NULL)
  134.         goto ERROR_OUTOFMEM;
  135.     gaplogpal[i]->palVersion = 0x300;
  136.     gaplogpal[i]->palNumEntries = 0;
  137.     }
  138.  
  139.     /* traverse the primary, secondary and merged DIBs simultaneously */
  140.     for (y = 0; y < (int) lpbiM->biHeight; y++)
  141.     {
  142.     HDC        hdc;
  143.     char        ach[100];
  144.     RECT        rc;
  145.  
  146.     hdc = GetDC(ghwndApp);
  147.     GetClientRect(ghwndApp, &rc);
  148.     wsprintf(ach, "Merging DIBs: %ld%% done; %lu colors",
  149.         ((long) y * 100L) / lpbiM->biHeight, dwMergedColors);
  150.     DrawText(hdc, ach, lstrlen(ach), &rc,
  151.         DT_LEFT | DT_TOP | DT_NOPREFIX);
  152.     ReleaseDC(ghwndApp, hdc);
  153.  
  154.     for (x = 0; x < (int) lpbiM->biWidth; x++)
  155.     {
  156.         pBitsP = DibXY(lpbiP, x, y);
  157.         pBitsS = DibXY(lpbiS, x, y);
  158.         pBitsM = DibXY(lpbiM, x, y);
  159.         pbCell = MERGEMAP(*pBitsP, *pBitsS);
  160.  
  161.         if (*pbCell == UNUSEDENTRY)
  162.         {
  163.         /* allocate a new entry in merged palette;
  164.         * if there are too many entries, keep track
  165.         * of the total no. entries for the
  166.         * error box
  167.         */
  168.         bPal = (BYTE) dwMergedColors;
  169.         if (++dwMergedColors > (DWORD) MAXPALSIZE)
  170.             continue;    // too many colors
  171.  
  172.         /* add an entry to gaplogpal[PRIMARY_DIB] */
  173.         GetPaletteEntries(gahpal[PRIMARY_DIB],
  174.             *pBitsP, 1, gaplogpal[PRIMARY_DIB]
  175.             ->palPalEntry + bPal);
  176.         gaplogpal[PRIMARY_DIB]->palNumEntries++;
  177.  
  178.         /* add an entry to gaplogpal[SECONDARY_DIB] */
  179.         GetPaletteEntries(gahpal[SECONDARY_DIB],
  180.             *pBitsS, 1, gaplogpal[SECONDARY_DIB]
  181.             ->palPalEntry + bPal);
  182.         gaplogpal[SECONDARY_DIB]->palNumEntries++;
  183.  
  184.         /* add an entry to gaplogpal[MERGED_DIB] */
  185.         gaplogpal[MERGED_DIB]->palPalEntry[bPal] =
  186.             gaplogpal[PRIMARY_DIB]->palPalEntry[bPal];
  187.         gaplogpal[MERGED_DIB]
  188.             ->palPalEntry[bPal].peFlags = PC_RESERVED;
  189.         gaplogpal[MERGED_DIB]->palNumEntries++;
  190.  
  191.         /* update <pbMergeMap> */
  192.         *pbCell = bPal;
  193.         }
  194.  
  195.         /* fill in a pixel in the merged DIB */
  196.         *pBitsM = *pbCell;
  197.     }
  198.     }
  199.  
  200.     if (dwMergedColors > (DWORD) MAXPALSIZE)
  201.     {
  202. #ifdef DOERRORS        
  203.     ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK, 
  204.         IDS_APPNAME, IDS_MERGEPALEXCEEDED,
  205.         dwMergedColors, (long) MAXPALSIZE);
  206. #endif            
  207.     goto RETURN_ERROR;
  208.     }
  209.  
  210.     /* create the merged palette */
  211.     if ((gahpal[MERGED_DIB] = CreatePalette(gaplogpal[MERGED_DIB])) == NULL)
  212.     goto ERROR_OUTOFMEM;
  213.  
  214.     /* make unused entries in the palette-index table in the DIB
  215.     * point to palette index zero
  216.     */
  217.     for (i = gaplogpal[MERGED_DIB]->palNumEntries,
  218.     pw = ((LPWORD) ((LPSTR) lpbiM + lpbiM->biSize)) + i;
  219.     i < 256; i++)
  220.     *pw++ = 0;
  221.  
  222.     goto RETURN_SUCCESS;
  223.  
  224. ERROR_OUTOFMEM:                // out of memory
  225. #ifdef DOERRORS
  226.     ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK, 
  227.     IDS_APPNAME, IDS_OUTOFMEM);
  228. #endif        
  229.     goto RETURN_ERROR;
  230.  
  231. RETURN_ERROR:
  232.  
  233.     fOK = FALSE;
  234.     FreeDIB(MERGED_DIB);
  235.     /* fall through */
  236.  
  237. RETURN_SUCCESS:
  238.  
  239.     if (pbMergeMap != NULL)
  240.     GFreePtr(pbMergeMap);
  241.  
  242.     if (hcurPrev != NULL)
  243.     SetCursor(hcurPrev);
  244.  
  245. return fOK;
  246. }
  247.  
  248.  
  249. /* AppPaint(hwnd, hdc)
  250.  *
  251.  * Repaint window <hwnd>.
  252.  */
  253. void FAR PASCAL
  254. AppPaint(hwnd, hdc)
  255. HWND        hwnd;        // window to paint into
  256. HDC        hdc;        // DC to paint into
  257. {
  258.     RECT        rcClient;
  259.     BITMAPINFOHEADER bih;        // information about this DIB
  260.  
  261.     GetClientRect(hwnd, &rcClient);
  262.  
  263.     /* save clipping region */
  264.     SaveDC(hdc);
  265.  
  266.     if (gahdib[giDIBViewed] != NULL)
  267.     {
  268.     /* get the size etc. of the bitmap */
  269.     DibInfo(gahdib[giDIBViewed], &bih);
  270.  
  271.     /* draw the DIB and then exclude its rectangle from the
  272.     * clipping region of <hdc>
  273.     */
  274.     DrawDib(hdc, 0, 0, gahdib[giDIBViewed], gahpal[giDIBViewed],
  275.         DIB_PAL_COLORS);
  276.     ExcludeClipRect(hdc, 0, 0,
  277.         (int) bih.biWidth, (int) bih.biHeight);
  278.  
  279.     /* draw the black border between the DIB and the gray
  280.      * background
  281.      */
  282.     PatBlt(hdc, 0, 0, (int) bih.biWidth + 1, (int) bih.biHeight + 1,
  283.         BLACKNESS);
  284.     ExcludeClipRect(hdc, 0, 0,
  285.         (int) bih.biWidth + 1, (int) bih.biHeight + 1);
  286.     }
  287.     
  288.     /* draw the gray background */
  289.     SelectObject(hdc, GetStockObject(GRAY_BRUSH));
  290.     PatBlt(hdc, rcClient.left, rcClient.top, rcClient.right - rcClient.left,
  291.         rcClient.bottom - rcClient.top, PATCOPY);
  292.  
  293.     /* restore clipping region */
  294.     RestoreDC(hdc, -1);
  295. }
  296.  
  297.  
  298. /* InitMenus()
  299.  *
  300.  * Check, uncheck, gray, and enable menus items in response to a
  301.  * WM_INITMENU message.
  302.  */
  303. void FAR PASCAL
  304. InitMenus(void)
  305. {
  306.     /* update the Edit menu */
  307.     EnableMenuItem(ghmenuApp, IDM_COPYMERGEDPALETTE, MF_BYCOMMAND |
  308.         (gahdib[MERGED_DIB] != NULL) ? MF_ENABLED : MF_GRAYED);
  309.  
  310.     /* update the View menu */
  311.     EnableMenuItem(ghmenuApp, IDM_VIEWPRIMARYDIB, MF_BYCOMMAND |
  312.         (gahdib[PRIMARY_DIB] != NULL) ? MF_ENABLED : MF_GRAYED);
  313.     EnableMenuItem(ghmenuApp, IDM_VIEWSECONDARYDIB, MF_BYCOMMAND |
  314.         (gahdib[SECONDARY_DIB] != NULL) ? MF_ENABLED : MF_GRAYED);
  315.     EnableMenuItem(ghmenuApp, IDM_VIEWMERGEDDIB, MF_BYCOMMAND |
  316.         (((gahdib[PRIMARY_DIB] != NULL) &&
  317.         (gahdib[SECONDARY_DIB] != NULL)) ? MF_ENABLED : MF_GRAYED));
  318.     EnableMenuItem(ghmenuApp, IDM_FADE, MF_BYCOMMAND |
  319.         ((gahdib[MERGED_DIB] != NULL) && gfPal ? MF_ENABLED : MF_GRAYED));
  320.     CheckMenuItem(ghmenuApp, IDM_VIEWPRIMARYDIB, MF_BYCOMMAND |
  321.         ((giDIBViewed == 0) ? MF_CHECKED : MF_UNCHECKED));
  322.     CheckMenuItem(ghmenuApp, IDM_VIEWSECONDARYDIB, MF_BYCOMMAND |
  323.         ((giDIBViewed == 1) ? MF_CHECKED : MF_UNCHECKED));
  324.     CheckMenuItem(ghmenuApp, IDM_VIEWMERGEDDIB, MF_BYCOMMAND |
  325.         ((giDIBViewed == 2) ? MF_CHECKED : MF_UNCHECKED));
  326. }
  327.  
  328.  
  329. /* WinMain(hInst, hPrev, lpszCmdLine, cmdShow)
  330.  * 
  331.  * The main procedure for the App.  After initializing, it just goes
  332.  * into a message-processing loop until it gets a WM_QUIT message
  333.  * (meaning the app was closed).
  334.  */
  335. int PASCAL            // returns exit code specified in WM_QUIT
  336. WinMain(hInst, hPrev, lpszCmdLine, iCmdShow)
  337. HANDLE        hInst;        // instance handle of current instance
  338. HANDLE        hPrev;        // instance handle of previous instance
  339. LPSTR        lpszCmdLine;    // null-terminated command line
  340. int        iCmdShow;    // how window should be initially displayed
  341. {
  342.     HANDLE    hAccel;        // accelerator table
  343.     MSG        msg;        // message from queue
  344.  
  345.     /* save instance handle for dialog boxes */
  346.     ghInst = hInst;
  347.  
  348.     /* call initialization procedure */
  349.     if (!AppInit(hInst, hPrev))
  350.     return FALSE;
  351.  
  352.     /* create the application's window */
  353. #define APPWNDSTYLE (WS_OVERLAPPEDWINDOW | WS_VSCROLL)
  354.  
  355.     ghwndApp = CreateWindow
  356.     (
  357.         gachAppName,        // window class
  358.         gachAppName,        // window caption
  359.         APPWNDSTYLE,        // window style
  360.         CW_USEDEFAULT, 0,    // initial position
  361.         CW_USEDEFAULT, 0,    // initial size
  362.         NULL,             // parent window handle
  363.         NULL,            // window menu handle
  364.         hInst,            // program instance handle
  365.         NULL            // create parameters
  366.     );
  367.     WinAssert(ghwndApp != NULL);
  368.     SetScrollRange(ghwndApp, SB_VERT, 0, 0, FALSE);
  369.     ShowWindow(ghwndApp, iCmdShow);
  370.  
  371.     /* load accelerator table */
  372.     hAccel = LoadAccelerators(hInst, "AppAccel");
  373.     WinAssert(hAccel != NULL);
  374.  
  375.     /* poll for messages from the event queue */
  376.     while (GetMessage(&msg, NULL, 0, 0))
  377.     {
  378.     if (!TranslateAccelerator(ghwndApp, hAccel, &msg))
  379.     {
  380.         TranslateMessage(&msg);
  381.         DispatchMessage(&msg);
  382.     }
  383.     }
  384.  
  385.     return msg.wParam;
  386. }
  387.  
  388.  
  389. /* AppWndProc(hwnd, wMsg, wParam, lParam)
  390.  * 
  391.  * The window proc for the app's main window.
  392.  */
  393. LONG FAR PASCAL _export        // returns 0 iff processed message
  394. AppWndProc(hwnd, wMsg, wParam, lParam)
  395. HWND        hwnd;        // window's handle
  396. WORD        wMsg;        // message number
  397. WORD        wParam;        // message-dependent parameter
  398. LONG        lParam;        // message-dependent parameter
  399. {
  400.     FARPROC    fpfn;
  401.     PALETTEENTRY NEAR *ppeP;    // palette entry in primary palette
  402.     PALETTEENTRY NEAR *ppeS;    // palette entry in secondary palette
  403.     PALETTEENTRY NEAR *ppeM;    // palette entry in merged palette
  404.     PAINTSTRUCT    ps;
  405.     HDC        hdc;
  406.     HPALETTE    hpalPrev;
  407.     HPALETTE    hpal;
  408.     int        iPos;
  409.     BYTE    bPal;        // palette index
  410.     BOOL    f;
  411.  
  412.     switch (wMsg)
  413.     {
  414.  
  415.     case WM_CREATE: 
  416.  
  417.     ghmenuApp = GetMenu(hwnd);
  418.     hdc=GetDC(hwnd);
  419.     gfPal=GetDeviceCaps(hdc,RASTERCAPS) & RC_PALETTE;
  420.     if(!gfPal)
  421.     {
  422.         MessageBeep(MB_ICONEXCLAMATION);
  423.         MessageBox(hwnd, (LPSTR)"Display driver does not support palettes\n"
  424.             "This sample program demonstrates palettes.",
  425.                          (LPSTR)"GetDeviceCaps", MB_ICONEXCLAMATION);
  426.     }
  427.     break;
  428.  
  429.     case WM_INITMENU:
  430.  
  431.     InitMenus();
  432.     break;
  433.  
  434.     case WM_QUERYENDSESSION:
  435.     case WM_CLOSE:
  436.  
  437.     /* clean up */
  438.     AppExit(hwnd);
  439.     break;
  440.  
  441.     case WM_DESTROY:
  442.  
  443.     /* exit this program */
  444.     PostQuitMessage(0);
  445.     break;
  446.  
  447.  
  448.     case WM_PALETTECHANGED:
  449.  
  450.     /* if my window caused the palette change, do nothing */
  451.     if (wParam == hwnd)
  452.         break;
  453.  
  454.     /* fall through */
  455.  
  456.     case WM_QUERYNEWPALETTE:
  457.  
  458.     /* process WM_PALETTECHANGED or WM_QUERYNEWPALETTE:
  459.      * temporarily select and realize <gahpal[giDIBViewed]> to see
  460.      * if a full redraw is required (due to some of the physical
  461.      * palette entries <hwnd> uses being changed)
  462.      */
  463.     if (gahpal[giDIBViewed] == NULL)
  464.         return (long) FALSE;    // no palette to realize
  465.  
  466.     hdc = GetDC(hwnd);
  467.     hpalPrev = SelectPalette(hdc, gahpal[giDIBViewed], FALSE);
  468.     f = RealizePalette(hdc);
  469.     SelectPalette(hdc, hpalPrev, FALSE);
  470.     ReleaseDC(hwnd, hdc);
  471.  
  472.     if (f)
  473.         InvalidateRect(hwnd, NULL, TRUE);
  474.  
  475.     return (LONG) f;
  476.  
  477.     case WM_ERASEBKGND:
  478.  
  479.     return 0L;            // WM_PAINT does erase
  480.  
  481.     case WM_PAINT:
  482.  
  483.     BeginPaint(hwnd, &ps);
  484.     AppPaint(hwnd, ps.hdc);
  485.     EndPaint(hwnd, &ps);
  486.     break;
  487.  
  488.     case WM_COMMAND:
  489.  
  490.     switch (wParam)
  491.     {
  492.  
  493.         case IDM_OPENPRIMARYDIB:
  494.  
  495.         FileOpen(FALSE, NULL);
  496.         break;
  497.  
  498.         case IDM_OPENSECONDARYDIB:
  499.  
  500.         FileOpen(TRUE, NULL);
  501.         break;
  502.  
  503.         case IDM_ABOUT:
  504.  
  505.         /* request to display "About" dialog box */
  506.         fpfn = MakeProcInstance((FARPROC) AboutDlgProc, ghInst);
  507.         DialogBox(ghInst, "AboutBox", hwnd, fpfn);
  508.         FreeProcInstance(fpfn);
  509.         break;
  510.  
  511.         case IDM_EXIT:
  512.  
  513.         /* request to exit this program */
  514.         PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L);
  515.         break;
  516.  
  517.         case IDM_COPYMERGEDPALETTE:
  518.  
  519.         /* copy the current merged palette */
  520.         hpal = CreatePalette(gaplogpal[MERGED_DIB]);
  521.         if (hpal == NULL)
  522.         {
  523. #ifdef DOERRORS                
  524.             ErrorResBox(ghwndApp, ghInst,
  525.             MB_ICONEXCLAMATION | MB_OK,
  526.             IDS_APPNAME, IDS_OUTOFMEM);
  527. #endif                    
  528.             return 0L;
  529.         }
  530.  
  531.         /* copy the palette to the clipboard */
  532.         if (OpenClipboard(hwnd))
  533.         {
  534.             EmptyClipboard();
  535.             SetClipboardData(CF_PALETTE, hpal);
  536.             CloseClipboard();
  537.         }
  538.         break;
  539.  
  540.         case IDM_VIEWPRIMARYDIB:
  541.  
  542.         if (gahdib[PRIMARY_DIB] == NULL)
  543.             return 0L;
  544.         giDIBViewed = PRIMARY_DIB;
  545.         SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  546.         InvalidateRect(hwnd, NULL, TRUE);
  547.         break;
  548.  
  549.         case IDM_VIEWSECONDARYDIB:
  550.  
  551.         if (gahdib[SECONDARY_DIB] == NULL)
  552.             return 0L;
  553.         giDIBViewed = SECONDARY_DIB;
  554.         SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  555.         InvalidateRect(hwnd, NULL, TRUE);
  556.         break;
  557.  
  558.         case IDM_VIEWMERGEDDIB:
  559.  
  560.         if ((gahdib[PRIMARY_DIB] == NULL) ||
  561.             (gahdib[SECONDARY_DIB] == NULL))
  562.             return 0L;
  563.  
  564.         if (gahdib[MERGED_DIB] == NULL)
  565.         {
  566.             /* create the merged DIB */
  567.             if (!CreateMergedDIB())
  568.             break;
  569.         }
  570.  
  571.         giDIBViewed = MERGED_DIB;
  572.         if(gfPal)
  573.         {
  574.             SetScrollRange(hwnd, SB_VERT, 0, SCROLL_RANGE, FALSE);
  575.             SetScrollPos(hwnd, SB_VERT, 0, TRUE);
  576.         }
  577.         InvalidateRect(hwnd, NULL, TRUE);
  578.         break;
  579.  
  580.         case IDM_FADE:
  581.  
  582.         if(!gfPal)
  583.             return 0L;
  584.         
  585.         if (gahdib[MERGED_DIB] == NULL)
  586.             return 0L;
  587.  
  588.         /* change fade direction at either end of scroll bar */
  589.         iPos = GetScrollPos(hwnd, SB_VERT);
  590.         if (iPos == 0)
  591.             gwFadeDirection = SB_LINEDOWN;
  592.         else
  593.             if (iPos == SCROLL_RANGE)
  594.             gwFadeDirection = SB_LINEUP;
  595.  
  596.         /* toggle fade on/off */
  597.         gfFading = !gfFading;
  598.         if (gfFading)
  599.             SetTimer(hwnd, 1, 50, NULL);
  600.         break;
  601.  
  602.     }
  603.     return 0L;
  604.  
  605.     case WM_VSCROLL:
  606.  
  607.     if(!gfPal)
  608.         break;
  609.  
  610.     if (gahdib[MERGED_DIB] == NULL)
  611.         break;
  612.     iPos = GetScrollPos(hwnd, SB_VERT);
  613.     switch(wParam)
  614.     {
  615.         case SB_LINEUP:
  616.         iPos -= SCROLL_LINE;
  617.         break;
  618.         case SB_LINEDOWN:
  619.         iPos += SCROLL_LINE;
  620.         break;
  621.         case SB_PAGEUP:
  622.         iPos -= SCROLL_PAGE;
  623.         break;
  624.         case SB_PAGEDOWN:
  625.         iPos += SCROLL_PAGE;
  626.         break;
  627.         case SB_THUMBTRACK:
  628.         case SB_THUMBPOSITION:
  629.         iPos = LOWORD(lParam);
  630.         break;
  631.         case SB_TOP:
  632.         iPos = 0;
  633.         break;
  634.         case SB_BOTTOM:
  635.         iPos = SCROLL_RANGE;
  636.         break;
  637.         default:
  638.         return 0L;
  639.     }
  640.  
  641.     if (iPos <= 0)
  642.         iPos = 0, gfFading = FALSE;
  643.     if (iPos >= SCROLL_RANGE)
  644.         iPos = SCROLL_RANGE, gfFading = FALSE;
  645.  
  646.     /* update <gaplogpal[MERGED_DIB]> */
  647.     ppeP = gaplogpal[PRIMARY_DIB]->palPalEntry;
  648.     ppeS = gaplogpal[SECONDARY_DIB]->palPalEntry;
  649.     ppeM = gaplogpal[MERGED_DIB]->palPalEntry;
  650.     for (bPal = 0;
  651.         bPal < (BYTE) gaplogpal[MERGED_DIB]->palNumEntries;
  652.         bPal++, ppeP++, ppeS++, ppeM++)
  653.     {
  654.         ppeM->peRed = (BYTE) (((long) ppeS->peRed * iPos +
  655.         (long) ppeP->peRed * (SCROLL_RANGE - iPos))
  656.         / SCROLL_RANGE);
  657.  
  658.         ppeM->peGreen = (BYTE) (((long) ppeS->peGreen * iPos +
  659.         (long) ppeP->peGreen * (SCROLL_RANGE - iPos))
  660.         / SCROLL_RANGE);
  661.  
  662.         ppeM->peBlue = (BYTE) (((long) ppeS->peBlue * iPos +
  663.         (long) ppeP->peBlue * (SCROLL_RANGE - iPos))
  664.         / SCROLL_RANGE);
  665.     }
  666.  
  667.     /* animate the palette */
  668.     hdc = GetDC(hwnd);
  669.     SelectPalette(hdc, gahpal[MERGED_DIB], FALSE);
  670.     RealizePalette(hdc);
  671.     AnimatePalette(gahpal[MERGED_DIB], 0,
  672.         gaplogpal[MERGED_DIB]->palNumEntries,
  673.         gaplogpal[MERGED_DIB]->palPalEntry);
  674.     ReleaseDC(hwnd, hdc);
  675.  
  676.     /* update the scroll bar position */
  677.     SetScrollPos(hwnd, SB_VERT, iPos, TRUE);
  678.     return 0L;
  679.  
  680.     case WM_KEYDOWN:
  681.  
  682.     switch (wParam)
  683.     {
  684.         case VK_LEFT:
  685.         case VK_UP:
  686.         SendMessage(ghwndApp, WM_VSCROLL, SB_LINEUP, 0L);
  687.         return 0L;
  688.         case VK_RIGHT:
  689.         case VK_DOWN:
  690.         SendMessage(ghwndApp, WM_VSCROLL, SB_LINEDOWN, 0L);
  691.         return 0L;
  692.         case VK_PRIOR:
  693.         SendMessage(ghwndApp, WM_VSCROLL, SB_PAGEUP, 0L);
  694.         return 0L;
  695.         case VK_NEXT:
  696.         SendMessage(ghwndApp, WM_VSCROLL, SB_PAGEDOWN, 0L);
  697.         return 0L;
  698.         case VK_HOME:
  699.         SendMessage(ghwndApp, WM_VSCROLL, SB_TOP, 0L);
  700.         return 0L;
  701.         case VK_END:
  702.         SendMessage(ghwndApp, WM_VSCROLL, SB_BOTTOM, 0L);
  703.         return 0L;
  704.     }
  705.     break;
  706.  
  707.     case WM_TIMER:
  708.     
  709.     if (!gfFading)
  710.         KillTimer(hwnd, 1);
  711.     SendMessage(hwnd, WM_VSCROLL, gwFadeDirection, 0L);
  712.     break;
  713.  
  714.     }
  715.  
  716.     return DefWindowProc(hwnd, wMsg, wParam, lParam);
  717. }
  718.