This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| RichEdit Overlay Submitted by

This snippet demonstrates how to make a RichEdit control transparent -- so that the text draws over top of the contents of the parent window (or even a memory DC). Best of all, it should work all the way back to Win95 / NT 3.1 and only relies on the Win32 APIs.
The basic concept is to draw the contents of the control into a memory DC, then use BitBlt() techniques (or TransparentBlt() if available) to generate a mask and overlay the text directly over the target DC.
Obvious enhancements include preallocating the memory DC and mask, then imaging the text only when it changes.
The image shows the graphical output of the enclosed test application.
This code is hereby placed in the public domain.
Matt Slot, 8/2/00
fprefect@ambrosiasw.com
http://www.ambrosiasw.com/~fprefect/

Currently browsing [overlay.zip] (169,393 bytes) - [OVERLAY/OVERLAY.C] - (10,149 bytes)

    /* File "OVERLAY.C" - Code to overlay a RichText control over a background    */
    /* Code by Matt Slot <fprefect@ambrosiasw.com> and placed into public domain  */

    #include <COMMCTRL.H>
    #include <RICHEDIT.H>
    #include <WINDOWSX.H>
    #include <WINDOWS.H>

    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* Preprocessor Definitions */

    #define OVERLAY_CLASS   "OVERLAY"
    #define PROGRAM_NAME    "RichEdit Overlay"
    #define STRING          "abcde fghij klmno pqrst uvwxyz 01234 56789 "

    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* Function Prototypes */

    typedef BOOL WINAPI (*TransparentBltProc)(IN HDC,IN int,IN int,IN int,
                                IN int,IN HDC,IN int,IN int,IN int,IN int,IN UINT);

    BOOL RichEditOverlayHWND(HWND hwnd, HDC hdc, HWND hwndRich);
    BOOL RichEditOverlayHDC(HDC hdc, DWORD x, DWORD y, HWND hwndRich);

    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* Overlay the contents of a HWND with a RichEdit control (its child window)  */

    BOOL RichEditOverlayHWND(HWND hwnd, HDC hdc, HWND hwndRich) {
        POINT           point = { 0, 0 };

        /* Just get the window location from our parent window */
        if (IsChild(hwnd, hwndRich)) MapWindowPoints(hwndRich, hwnd, &point, 1);

        return(RichEditOverlayHDC(hdc, point.x, point.y, hwndRich));
        }

    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* Overlay the contents of the HDC at the given point with a RichEdit control */

    BOOL RichEditOverlayHDC(HDC hdc, DWORD x, DWORD y, HWND hwndRich) {
        static TransparentBltProc gTransparentBltProc = NULL;
        static BOOL     gTransparentBltFirst = TRUE;
        BOOL            success = TRUE;
        HDC             hdcSave = NULL;
        HDC             hdcRich = NULL, hdcMask = NULL;
        HBITMAP         hbmRich = NULL, hbmSaveRich = NULL;
        HBITMAP         hbmMask = NULL, hbmSaveMask = NULL;
        COLORREF        bkColor;
        DWORD           bits, w, h;
        POINT           point;
        RECT            rect;

        /* On our first call, we try to dynamically load TransparentBlt() */
        if (gTransparentBltFirst) {
            HINSTANCE   hModule = LoadLibrary("MSIMG32.DLL");
            if (hModule)
                gTransparentBltProc = GetProcAddress(hModule, "TransparentBlt");
            gTransparentBltFirst = FALSE; /* Don't bother trying again */
            }

        /* Establish our coordinates from the start. */
        GetClientRect(hwndRich, &rect);
        w = rect.right - rect.left;
        h = rect.bottom - rect.top;

        /* Get the relevant characteristics of the source window */
        hdcSave = GetDC(hwndRich);
        if (!hdcSave) { success = FALSE; goto CLEANUP; }
        bkColor = GetBkColor(hdcSave);
        bits = GetDeviceCaps(hdcSave, BITSPIXEL);

        /* Create offscreen drawing contexts and bitmap for the pixels */
        hdcRich = CreateCompatibleDC(hdcSave);
        if (!hdcRich) { success = FALSE; goto CLEANUP; }
        hbmRich = CreateBitmap(w, h, 1, bits, NULL);
        if (!hbmRich) { success = FALSE; goto CLEANUP; }
        hbmSaveRich = SelectBitmap(hdcRich, hbmRich);

        /* Create offscreen drawing contexts and bitmap for the mask */
        if (!gTransparentBltProc) {
            hdcMask = CreateCompatibleDC(hdcSave);
            if (!hdcMask) { success = FALSE; goto CLEANUP; }
            hbmMask = CreateBitmap(w, h, 1, 1, NULL);
            if (!hbmMask) { success = FALSE; goto CLEANUP; }
            hbmSaveMask = SelectBitmap(hdcMask, hbmMask);
            }

        /* Draw the control into our offscreen drawing context */
        SendMessage(hwndRich, WM_PRINT, (WPARAM) hdcRich,
                PRF_CLIENT | PRF_ERASEBKGND);

        if (gTransparentBltProc)
            /* We'll gladly use it if it's available */
            success = (*gTransparentBltProc)(hdc,x,y,w,h,hdcRich,0,0,w,h,bkColor);
          else {
            /* Otherwise use a mask and draw it manually */
            BitBlt(hdcMask,0,0,w,h,hdcRich,0,0,SRCCOPY);
            BitBlt(hdc,x,y,w,h,hdcMask,0,0,SRCAND);
            BitBlt(hdcRich,0,0,w,h,hdcMask,0,0,0x00220326); /* SRCNOTAND */
            success = BitBlt(hdc,x,y,w,h,hdcRich,0,0,SRCPAINT);
            }

    CLEANUP:

        /* Release all of our objects in order */
        if (hbmSaveMask) SelectBitmap(hdcMask, hbmSaveMask);
        if (hbmMask) DeleteObject(hbmMask);
        if (hbmSaveRich) SelectBitmap(hdcRich, hbmSaveRich);
        if (hbmRich) DeleteObject(hbmRich);
        if (hdcSave) ReleaseDC(hwndRich, hdcSave);

        return(success);
        }

    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
    /* Private utility routines */

    #define kFirstChildWindowID     10000

    static BOOL CALLBACK CalcChildWindowProc(HWND hwnd, LPARAM param) {
        HMENU *     child = (HMENU *) param;

        if (*child == (HMENU) GetDlgCtrlID(hwnd)) *child = (HMENU) NULL;

        return((*child) ? TRUE : FALSE);
        }

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

    static HMENU CalcChildWindowID(HWND hwnd) {
        HMENU       child;
        UINT        i;

        for(i=kFirstChildWindowID, child=(HMENU) NULL; !child; child = (HMENU) ++i)
            EnumChildWindows(hwnd, CalcChildWindowProc, (LPARAM) &child);

        return(child);
        }

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

    static LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam,
            LPARAM lParam) {
        LRESULT         result = 0;
        HWND            hwndRich;
        HDC             hdc;
        PAINTSTRUCT     ps;

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

                /* Grab the RichEdit handle from the parent window */
                hwndRich = (HWND) GetWindowLong(hwnd, GWL_USERDATA);
                RichEditOverlayHWND(hwnd, hdc, hwndRich);

                EndPaint(hwnd, &ps);
                result = 1;
                break;

            case WM_DESTROY:
                PostQuitMessage(0);
                break;

            default:
                result = DefWindowProc(hwnd, msg, wParam, lParam);
            }

        return(result);
        }

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

    int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszCmdLine,
            int nCmdShow) {
        HWND            hwnd = (HWND) NULL;
        HWND            hwndRich = (HWND) NULL;
        MSG             lpMsg;
        WNDCLASS        wc;
        RECT            rect;
        HDC             hdc;
        UINT            i, j;
        CHARFORMAT      cf;

        /* Create our class and use an interesting background brush */
        if (!hPrev) {
            wc.lpszClassName    = OVERLAY_CLASS;
            wc.hInstance        = hInst;
            wc.lpfnWndProc      = MyWndProc;
            wc.hCursor          = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
            wc.hIcon            = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
            wc.lpszMenuName     = (char *) NULL;
            wc.hbrBackground    = CreateHatchBrush(HS_BDIAGONAL, RGB(255,0,0));
            wc.style            = 0;
            wc.cbClsExtra       = 0;
            wc.cbWndExtra       = 0;

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

        /* Create the window */
        hwnd = CreateWindow(OVERLAY_CLASS, PROGRAM_NAME, WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                (HWND) NULL, (HMENU) NULL, (HINSTANCE) hInst, (LPSTR) NULL);

        /* Create the RichEdit control -- notice, we never make it visible! */
        InitCommonControls();
        LoadLibrary("RICHED32.DLL");
        GetClientRect(hwnd, &rect);
        hwndRich = CreateWindowEx(0, "RICHEDIT", "", WS_CHILD | ES_LEFT |
                ES_SAVESEL | ES_MULTILINE | ES_AUTOVSCROLL, rect.left + 50,
                rect.top + 50, rect.right - rect.left - 100, rect.bottom -
                rect.top - 100, hwnd, CalcChildWindowID(hwnd), (HINSTANCE)
                GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
        Edit_Enable(hwndRich, FALSE);
        hdc = GetDC(hwndRich);

        /* Fill the image with some interesting text, 1 run at a time */
        ZeroMemory(&cf, sizeof(CHARFORMAT));
        cf.cbSize = sizeof(CHARFORMAT);
        cf.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_ITALIC |
                            CFM_SIZE | CFM_UNDERLINE;
        cf.dwEffects = 0;
        cf.yHeight = 120 / GetDeviceCaps(hdc, LOGPIXELSY);
        cf.yOffset = 0;
        cf.crTextColor = RGB(0,0,0);
        cf.bCharSet = ANSI_CHARSET;
        cf.bPitchAndFamily = DEFAULT_PITCH;
        strcpy(cf.szFaceName, "System");
        for(i=0, j=sizeof(STRING)-sizeof(""); i<27; i++) {
            Edit_SetSel(hwndRich, j*i, j*i);
            Edit_ReplaceSel(hwndRich, STRING);
            Edit_SetSel(hwndRich, j*i, j*(i+1));

            /* Format the text */
            cf.crTextColor = RGB((i%3)*0x44,((i/3)%3)*0x44,((i/9)%3)*0x44);
            SendMessage(hwndRich, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
            }
        Edit_SetSel(hwndRich, 0, 0);
        ReleaseDC(hwndRich, hdc);

        /* Store the handle to the child window so we can access it */
        SetWindowLong(hwnd, GWL_USERDATA, (LONG) hwndRich);

        /* Sit and spin */
        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);
        while(GetMessage(&lpMsg, (HWND) NULL, 0, 0)) {
            TranslateMessage(&lpMsg);
            DispatchMessage(&lpMsg);
            }

        DestroyWindow(hwnd);
        return(lpMsg.wParam);
        }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.