/**************************************************************************

    HALFTONE.C - A halftoning demo for WinG

 **************************************************************************/
/**************************************************************************

    (C) Copyright 1994 Microsoft Corp.  All rights reserved.

    You have a royalty-free right to use, modify, reproduce and 
    distribute the Sample Files (and/or any modified version) in 
    any way you find useful, provided that you agree that 
    Microsoft has no warranty obligations or liability for any 
    Sample Application Files which are modified. 

 **************************************************************************/


#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>

#include <wing.h>

#include "halftone.h"
#include "..\utils\utils.h"

/*----------------------------------------------------------------------------*\
|                                                                              |
|   g l o b a l   v a r i a b l e s                                            |
|                                                                              |
\*----------------------------------------------------------------------------*/
static  char       szAppName[]="WinG Halftoning Sample";
static  char       szAppFilter[]="Bitmaps\0*.bmp;*.dib\0";
static  HINSTANCE  hInstApp;
static  HWND       hwndApp;
static  HPALETTE   hpalApp;
static  BOOL       fAppActive;
static  BOOL       fCustomDither = TRUE;
static  PDIB       pdibOriginal = 0;
static  PDIB       pdibHalftone = 0;

#ifdef WIN32
    #define _export
#endif

/*----------------------------------------------------------------------------*\
|                                                                              |
|   f u n c t i o n   d e f i n i t i o n s                                    |
|                                                                              |
\*----------------------------------------------------------------------------*/

LONG FAR PASCAL _export  AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
LONG  AppCommand (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);

void  AppExit(void);
BOOL  AppIdle(void);
void  AppOpenFile(HWND hwnd, LPSTR szFileName);

PDIB  DibHalftoneDIB( PDIB pSource );

/*----------------------------------------------------------------------------*\
|   AppAbout( hDlg, uiMessage, wParam, lParam )                                |
|                                                                              |
|   Description:                                                               |
|       This function handles messages belonging to the "About" dialog box.    |
|       The only message that it looks for is WM_COMMAND, indicating the use   |
|       has pressed the "OK" button.  When this happens, it takes down         |
|       the dialog box.                                                        |
|                                                                              |
|   Arguments:                                                                 |
|       hDlg            window handle of about dialog window                   |
|       uiMessage       message number                                         |
|       wParam          message-dependent                                      |
|       lParam          message-dependent                                      |
|                                                                              |
|   Returns:                                                                   |
|       TRUE if message has been processed, else FALSE                         |
|                                                                              |
\*----------------------------------------------------------------------------*/
BOOL FAR PASCAL _export AppAbout(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch (msg)
    {
        case WM_COMMAND:
          if (LOWORD(wParam) == IDOK)
            {
                EndDialog(hwnd,TRUE);
            }
            break;

        case WM_INITDIALOG:
            return TRUE;
    }
    return FALSE;
}

/*----------------------------------------------------------------------------*\
|   AppInit( hInst, hPrev)                                                     |
|                                                                              |
|   Description:                                                               |
|       This is called when the application is first loaded into               |
|       memory.  It performs all initialization that doesn't need to be done   |
|       once per instance.                                                     |
|                                                                              |
|   Arguments:                                                                 |
|       hInstance       instance handle of current instance                    |
|       hPrev           instance handle of previous instance                   |
|                                                                              |
|   Returns:                                                                   |
|       TRUE if successful, FALSE if not                                       |
|                                                                              |
\*----------------------------------------------------------------------------*/
BOOL AppInit(HINSTANCE hInst,HINSTANCE hPrev,int sw,LPSTR szCmdLine)
{
    WNDCLASS cls;
    int      dx,dy;
    HMENU hMenu;

    /* Save instance handle for DialogBoxes */
    hInstApp = hInst;

// Clear the System Palette so that WinG blting runs at full speed.
    ClearSystemPalette();

    if (!hPrev)
    {
        /*
         *  Register a class for the main application window
         */
        cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
        cls.hIcon          = LoadIcon(hInst,"AppIcon");
        cls.lpszMenuName   = "AppMenu";
        cls.lpszClassName  = szAppName;
        cls.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
        cls.hInstance      = hInst;
        cls.style          = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
        cls.lpfnWndProc    = (WNDPROC)AppWndProc;
        cls.cbWndExtra     = 0;
        cls.cbClsExtra     = 0;

        if (!RegisterClass(&cls))
            return FALSE;
    }

    dx = GetSystemMetrics (SM_CXSCREEN) / 2;
    dy = GetSystemMetrics (SM_CYSCREEN) / 2;

  hpalApp = WinGCreateHalftonePalette();

    hwndApp = CreateWindow (szAppName,              // Class name
                            szAppName,              // Caption
                            WS_OVERLAPPEDWINDOW,    // Style bits
                            CW_USEDEFAULT, 0,       // Position
                            dx,dy,                  // Size
                            (HWND)NULL,             // Parent window (no parent)
                            (HMENU)NULL,            // use class menu
                            hInst,                  // handle to window instance
                            (LPSTR)NULL             // no params to pass on
                           );
    ShowWindow(hwndApp,sw);

  //*** Check the default dither selection
  hMenu = GetMenu(hwndApp);
  CheckMenuItem(hMenu, MENU_WING, fCustomDither ? MF_CHECKED : MF_UNCHECKED);
  CheckMenuItem(hMenu, MENU_GDI, fCustomDither ? MF_UNCHECKED : MF_CHECKED);

    if (*szCmdLine)
  AppOpenFile(hwndApp, szCmdLine);

    return TRUE;
}


/*----------------------------------------------------------------------------*\
|   AppExit()                                                                  |
|                                                                              |
|   Description:                                                               |
|     App is just about to exit, cleanup.                                      |
|                                                                              |
\*----------------------------------------------------------------------------*/
void AppExit()
{
  if (hpalApp)
    DeleteObject(hpalApp);
  if (pdibOriginal)
    DibFree(pdibOriginal);
  if (pdibHalftone)
    DibFree(pdibHalftone);
}

/*----------------------------------------------------------------------------*\
|   WinMain( hInst, hPrev, lpszCmdLine, cmdShow )                              |
|                                                                              |
|   Description:                                                               |
|       The main procedure for the App.  After initializing, it just goes      |
|       into a message-processing loop until it gets a WM_QUIT message         |
|       (meaning the app was closed).                                          |
|                                                                              |
|   Arguments:                                                                 |
|       hInst           instance handle of this instance of the app            |
|       hPrev           instance handle of previous instance, NULL if first    |
|       szCmdLine       ->null-terminated command line                         |
|       cmdShow         specifies how the window is initially displayed        |
|                                                                              |
|   Returns:                                                                   |
|       The exit code as specified in the WM_QUIT message.                     |
|                                                                              |
\*----------------------------------------------------------------------------*/
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
    MSG     msg;

    /* Call initialization procedure */
    if (!AppInit(hInst,hPrev,sw,szCmdLine))
      return FALSE;

    /*
     * Polling messages from event queue
     */
    for (;;)
    {
        if (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
  {
      if (AppIdle())
            WaitMessage();
        }
    }

    AppExit();
    return msg.wParam;
}

/*----------------------------------------------------------------------------*\
|   AppIdle()                                                                  |
|                                                                              |
|   Description:                                                               |
|     Place to do idle time stuff.                                             |
|                                                                              |
\*----------------------------------------------------------------------------*/
BOOL AppIdle()
{
    if (fAppActive)
    {
  //
  // we are the foreground app.
  //
  return TRUE;      // nothing to do.
    }
    else
    {
  //
  // we are a background app.
  //
  return TRUE;      // nothing to do.
    }
}

/*----------------------------------------------------------------------------*\
|   AppOpenFile()                                                              |
|                                                                              |
|   Description:                                                               |
|     Open a file.                                                             |
|                                                                              |
\*----------------------------------------------------------------------------*/
void AppOpenFile(HWND hwnd, LPSTR szFileName)
{
  HCURSOR hCur = LoadCursor(NULL, IDC_WAIT);
  PDIB pdibNew;

  hCur = SetCursor(hCur);
  
  pdibNew = DibOpenFile(szFileName);

  if (pdibNew)
  {
    if (pdibOriginal)
      DibFree(pdibOriginal);
    if (pdibHalftone)
      DibFree(pdibHalftone);

    pdibOriginal = pdibNew;
    pdibHalftone = DibHalftoneDIB(pdibOriginal);

    if (DibBitCount(pdibOriginal) != 24)
    {
      MessageBox(hwnd, "Not a 24-bit DIB!", "Oops", MB_OK);
    }

    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
  }

  hCur = SetCursor(hCur);
}

/*----------------------------------------------------------------------------*\
|   AppPaint(hwnd, hdc)                                                        |
|                                                                              |
|   Description:                                                               |
|       The paint function.                                                    |
|                                                                              |
|   Arguments:                                                                 |
|       hwnd             window painting into                                  |
|       hdc              display context to paint to                           |
|                                                                              |
\*----------------------------------------------------------------------------*/
AppPaint (HWND hwnd, HDC hdc)
{
  if (fCustomDither && pdibHalftone)
  {
    StretchDIBits(hdc,
      0, 0, DibWidth(pdibHalftone), DibHeight(pdibHalftone),
      0, 0, DibWidth(pdibHalftone), DibHeight(pdibHalftone),
      DibPtr(pdibHalftone), DibInfo(pdibHalftone),
      DIB_RGB_COLORS, SRCCOPY);
  }
  else if (pdibOriginal)
  {
    StretchDIBits(hdc,
      0, 0, DibWidth(pdibOriginal), DibHeight(pdibOriginal),
      0, 0, DibWidth(pdibOriginal), DibHeight(pdibOriginal),
      DibPtr(pdibOriginal), DibInfo(pdibOriginal),
      DIB_RGB_COLORS, SRCCOPY);
  }

  return TRUE;
}

/*----------------------------------------------------------------------------*\
|   AppWndProc( hwnd, uiMessage, wParam, lParam )                              |
|                                                                              |
|   Description:                                                               |
|       The window proc for the app's main (tiled) window.  This processes all |
|       of the parent window's messages.                                       |
|                                                                              |
\*----------------------------------------------------------------------------*/
LONG FAR PASCAL _export AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    BOOL f;

    switch (msg)
    {
        case WM_CREATE:
            break;

        case WM_ACTIVATEAPP:
            fAppActive = (BOOL)wParam;
            break;

        case WM_TIMER:
            break;

        case WM_ERASEBKGND:
            break;

        case WM_INITMENU:
            break;

        case WM_COMMAND:
            return AppCommand(hwnd,msg,wParam,lParam);

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        case WM_CLOSE:
            break;

        case WM_PALETTECHANGED:
            if ((HWND)wParam == hwnd)
                break;

      // fall through to WM_QUERYNEWPALETTE

        case WM_QUERYNEWPALETTE:
            hdc = GetDC(hwnd);

            if (hpalApp)
                SelectPalette(hdc, hpalApp, FALSE);

            f = RealizePalette(hdc);
            ReleaseDC(hwnd,hdc);

            if (f)
                InvalidateRect(hwnd,NULL,TRUE);

            return f;

        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);

            if (hpalApp)
            {
                SelectPalette(hdc, hpalApp, FALSE);
                RealizePalette(hdc);
            }
            AppPaint (hwnd,hdc);
            EndPaint(hwnd,&ps);
            return 0L;
    }
    return DefWindowProc(hwnd,msg,wParam,lParam);
}

/*----------------------------------------------------------------------------*\
|   AppCommand(hwnd, msg, wParam, lParam )                                     |
|                                                                              |
|   Description:                                                               |
|     Handles WM_COMMAND messages for the main window (hwndApp)                |
|   of the parent window's messages.                                           |
|                                                                              |
\*----------------------------------------------------------------------------*/
LONG AppCommand (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    char achFileName[128];
    OPENFILENAME ofn;

    switch(wParam)
    {
        case MENU_ABOUT:
            DialogBox(hInstApp,"AppAbout",hwnd,(DLGPROC)AppAbout);
            break;

        case MENU_OPEN:
            achFileName[0] = 0;

            /* prompt user for file to open */
            ofn.lStructSize = sizeof(OPENFILENAME);
            ofn.hwndOwner = hwnd;
            ofn.hInstance = NULL;
            ofn.lpstrFilter = szAppFilter;
            ofn.lpstrCustomFilter = NULL;
            ofn.nMaxCustFilter = 0;
            ofn.nFilterIndex = 0;
            ofn.lpstrFile = achFileName;
            ofn.nMaxFile = sizeof(achFileName);
            ofn.lpstrFileTitle = NULL;
            ofn.nMaxFileTitle = 0;
            ofn.lpstrInitialDir = NULL;
            ofn.lpstrTitle = NULL;
            ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
            ofn.nFileOffset = 0;
            ofn.nFileExtension = 0;
            ofn.lpstrDefExt = NULL;
            ofn.lCustData = 0;
            ofn.lpfnHook = NULL;
            ofn.lpTemplateName = NULL;

            if (GetOpenFileName(&ofn))
            {
                AppOpenFile(hwnd,achFileName);
            }

            break;

        case MENU_EXIT:
            PostMessage(hwnd,WM_CLOSE,0,0L);
            break;

        case MENU_WING:
        case MENU_GDI:
        {
            HMENU hMenu;

            fCustomDither = (wParam == MENU_WING);

            hMenu = GetMenu(hwnd);
            CheckMenuItem(hMenu, MENU_WING,
            fCustomDither ? MF_CHECKED : MF_UNCHECKED);
            CheckMenuItem(hMenu, MENU_GDI,
            fCustomDither ? MF_UNCHECKED : MF_CHECKED);

            InvalidateRect(hwnd, NULL, FALSE);
            UpdateWindow(hwnd);
        }
        break;
  }
    return 0L;
}

/***************************************************************************
  DibHalftoneDIB

  Halftone a 24-bit DIB to the 8-bit WinG Halftone Palette
*/

//*** These tables are defined in httables.c
extern char unsigned  aDividedBy51Rounded[256];
extern char unsigned  aDividedBy51[256];
extern char unsigned  aModulo51[256];
extern char unsigned  aTimes6[6];
extern char unsigned  aTimes36[6];

extern char unsigned  aHalftone8x8[64];

extern char unsigned  aWinGHalftoneTranslation[216];

PDIB DibHalftoneDIB( PDIB pSource )
{
  //*** Create an 8-bit DIB to halftone to
  PDIB pDestination = DibCreate(8,DibWidth(pSource),DibHeight(pSource));

  //*** Only work on 24-bit sources
  if(pDestination && hpalApp && (DibBitCount(pSource) == 24))
  {
    long PixelsPerScanline = DibWidth(pSource);
    int Scanline;

    //*** Fill in the DIB color table with the halftone palette
    DibSetUsage(pDestination, hpalApp, DIB_RGB_COLORS);

    //*** Step through, converting each pixel in each scan line
    //*** to the nearest halftone match
    for(Scanline = 0;Scanline < (int)DibHeight(pSource);Scanline++)
    {
      char unsigned _huge *pSourceScanline =
          (char unsigned _huge *)DibXY(pSource,0,Scanline);
          
      char unsigned _huge *pDestinationScanline =
          (char unsigned _huge *)DibXY(pDestination,0,Scanline);

      int Pixel;

      for(Pixel = 0;Pixel < (int)DibWidth(pSource);Pixel++)
      {
        //*** This is the meat of the halftoning algorithm:
        //*** Convert an RGB into an index into the halftone palette.

        //*** First, extract the raw RGB information
        int Red = pSourceScanline[Pixel*3 + 2];
        int Green = pSourceScanline[Pixel*3 + 1];
        int Blue = pSourceScanline[Pixel*3];

        //*** Now, look up each value in the halftone matrix
        //*** using an 8x8 ordered dither.
        char unsigned RedTemp = aDividedBy51[Red]
          + (aModulo51[Red] > aHalftone8x8[(Pixel%8)*8
          + Scanline%8]);
        char unsigned GreenTemp = aDividedBy51[(char unsigned)Green]
          + (aModulo51[Green] > aHalftone8x8[
          (Pixel%8)*8 + Scanline%8]);
        char unsigned BlueTemp = aDividedBy51[(char unsigned)Blue]
          + (aModulo51[Blue] > aHalftone8x8[
          (Pixel%8)*8 +Scanline%8]);

        //*** Recombine the halftoned RGB values into a palette index
        char unsigned PaletteIndex =
          RedTemp + aTimes6[GreenTemp] + aTimes36[BlueTemp];

        //*** And translate through the WinG Halftone Palette
        //*** translation vector to give the correct value.
        pDestinationScanline[Pixel] = aWinGHalftoneTranslation[PaletteIndex];
      }
    }
  }
  else
  {
    //*** Punt on anything but 24bpp
    DibFree(pDestination);
    pDestination = 0;
  }

  return pDestination;
}
