How to fix graphic artefacts in this example

ProMiNick 40 Reputation points
2023-04-06T08:21:28.6233333+00:00

Theory: In this example present implementation of adding linenumbers subclass of standard EDIT control. linenumbers are sent to NC area of control, creation of NC area reached with handling WM_NCCALCSIZE message and adding 40 to left field of first RECT pointed by lparam of that message, that reserves a rectangle 40 pix width from left of client area. Process of drawing linenumbers itself passed to WM_NCPAINT handler. Because this drawing goes to our special added area, after that drawing execution moved to standart EDIT handler of WM_NCPAINT to draw borders and scrolls. In addition to WM_NCCALCSIZE & WM_NCPAINT handling added handlers of WM_PAINT & WM_KEYDOWN (I guess last two would be enought to catch all cases that change content of linenumbers). handling of WM_PAINT (happend when something appear infront of client area - another window (for ex. context menu), or when client visible area have to be scrolled). When adding or removing char or line dont causing scrolling of visible area - repeant (in form of passing WM_PAINT message) is not happend, there goes trick of filling rects with background color for lines or characters that are removed or drawing text for chars or lines that are added. (I guessed for this cases would be perfect to catch WM_KEYDOWN and in addition to standard processing make InvalidateRect in it). This approach avoid any flickering usualy specific to linenumbers located in left margin of text of client area. My override of WM_PAINT & WM_KEYDOWN handling in some manner breach standard processing (at winXP and below for ex.) after parts of window stoped to be covered by another window they rarely have some grey graphic artefacts (narrow horisontal rectangle or wide vertical rectangle or both) that lay on border, scroll bar, client area (that never happend for nonsubclassed EDIT). Could it be related that I pass "invoke BaseEditProc,[hwnd],WM_PAINT,0,0" to standard message processing, and wparam & lparam have not only zero values(like in official documentation) and I should pass "invoke BaseEditProc,[hwnd],WM_PAINT,[wparam],[lparam]". I cant understand where I trick myself. In windows 10 I could not reproduce problem, so attached screen holds application look, not artifacts over it. I will attach them later. Critic & suggestions are very appreciated.

; Simple text editor - fasm example program

format PE GUI 4.0
entry start

include 'win32a.inc'

IDR_ICON = 17
IDR_MENU = 37

IDM_NEW   = 101
IDM_EXIT  = 102
IDM_ABOUT = 901

section '.text' code readable executable

  start: RTL_C

        invoke  GetModuleHandle,0
        mov     [wc.hInstance],eax

        invoke  LoadIcon,eax,IDR_ICON
        mov     [wc.hIcon],eax
        invoke  LoadCursor,0,IDC_ARROW
        mov     [wc.hCursor],eax
        invoke  RegisterClass,wc
        test    eax,eax
        jz      error

        invoke  LoadMenu,[wc.hInstance],IDR_MENU
        invoke  CreateWindowEx,0,_class,_title,WS_VISIBLE+WS_OVERLAPPEDWINDOW,144,128,256,256,NULL,eax,[wc.hInstance],NULL
        test    eax,eax
        jz      error

  msg_loop:
        invoke  GetMessage,msg,NULL,0,0
        cmp     eax,1
        jb      end_loop
        jne     msg_loop
        invoke  TranslateMessage,msg
        invoke  DispatchMessage,msg
        jmp     msg_loop

  error:
        invoke  MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK

  end_loop:
        invoke  ExitProcess,[msg.wParam]
flush_locals

WindowProc: RTL_C hwnd,wmsg,wparam,lparam
        frame   ebp
        push    ebx esi edi
        mov     eax,[wmsg]
        cmp     eax,WM_CREATE
        je      .wmcreate
        cmp     eax,WM_SIZE
        je      .wmsize
        cmp     eax,WM_SETFOCUS
        je      .wmsetfocus
        cmp     eax,WM_COMMAND
        je      .wmcommand
        cmp     eax,WM_DESTROY
        je      .wmdestroy
  .defwndproc:
        invoke  DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
        jmp     .finish
  .wmcreate:
        invoke  CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+ES_MULTILINE,0,0,0,0,[hwnd],0,[wc.hInstance],NULL
        or      eax,eax
        jz      .failed
        mov     [edithwnd],eax
        invoke  SetWindowLong,eax,GWL_WNDPROC,NewEditProc
        mov     [BaseEditProc],eax
        invoke  CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_RASTER_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH+FF_DONTCARE,NULL
        or      eax,eax
        jz      .failed
        mov     [editfont],eax
        invoke  SendMessage,[edithwnd],WM_SETFONT,eax,FALSE
        ;xor     eax,eax
        jmp     .wmsize
      .failed:
        or      eax,-1
        jmp     .finish
  .wmsize:
        invoke  GetClientRect,[hwnd],client
        invoke  MoveWindow,[edithwnd],[client.left],[client.top],[client.right],[client.bottom],TRUE
        xor     eax,eax
        jmp     .finish
  .wmsetfocus:
        invoke  SetFocus,[edithwnd]
        xor     eax,eax
        jmp     .finish
  .wmcommand:
        mov     eax,[wparam]
        and     eax,0FFFFh
        cmp     eax,IDM_NEW
        je      .new
        cmp     eax,IDM_ABOUT
        je      .about
        cmp     eax,IDM_EXIT
        je      .wmdestroy
        jmp     .defwndproc
      .new:
        invoke  SendMessage,[edithwnd],WM_SETTEXT,0,0
        jmp     .finish
      .about:
        invoke  MessageBox,[hwnd],_about_text,_about_title,MB_OK
        jmp     .finish
  .wmdestroy:
        invoke  DeleteObject,[editfont]
        invoke  PostQuitMessage,0
        xor     eax,eax
  .finish:
        pop     edi esi ebx
        exitf
        stack_cleanup callee
flush_locals


NewEditProc: RTL_C hwnd,wmsg,wparam,lparam
        ;frame   esp
        mov     eax,[wmsg]
        cmp     eax,WM_NCCALCSIZE
        je      .make_ncarea
        cmp     eax,WM_NCPAINT
        je      .wmncpaint
        cmp     eax,WM_PAINT
        je      .wmpaint
        cmp     eax,WM_KEYDOWN
        jnz     .default
        invoke  InvalidateRect,[hwnd],0,FALSE
  .default:
        jmp     [BaseEditProc]
  .make_ncarea:
        mov     eax,[lparam]
        add     dword[eax+RECT.left],40
        jmp     .default
        ;exitf
flush_locals


NewEditProc.wmpaint: RTL_C hwnd,wmsg,wparam,lparam
        frame   ebp
        invoke  BaseEditProc,[hwnd],WM_PAINT,0,0
        invoke  GetWindowRgn,[hwnd],[wparam]
        mov     [wmsg],WM_NCPAINT
        exitf
flush_locals


NewEditProc.wmncpaint: RTL_C hwnd,wmsg,wparam,lparam
        frame   ebp
        invoke  SendMessage,[hwnd], EM_GETLINECOUNT, 0, 0
        push    esi
        mov     esi,eax
        invoke  SendMessage,[hwnd], EM_GETFIRSTVISIBLELINE, 0, 0
        sub     esi,eax
        inc     eax
        mov     ecx,10
        xor     edx,edx
        div     ecx
        add     edx,'0'
        mov     byte[ntext+3],dl
        xor     edx,edx
        div     ecx
        add     edx,'0'
        mov     byte[ntext+2],dl
        xor     edx,edx
        div     ecx
        add     edx,'0'
        add     eax,'0'
        mov     byte[ntext+1],dl
        mov     byte[ntext+0],al
        invoke  GetWindowDC,[hwnd]
        mov     [NC_DC],eax
        invoke  GetTextExtentPoint32,[NC_DC],ntext,1,size
        invoke  GetClientRect,[hwnd],client
        invoke  GetSystemMetrics,SM_CYEDGE
        add     [client.left],eax
        add     [client.top],eax
        mov     [client.right],40
        sub     [client.right],eax
        add     [client.bottom],eax
        invoke  GetSystemMetrics,SM_CYHSCROLL
        add     [client.bottom],eax
        invoke  SelectObject,[NC_DC], [editfont]
        invoke  FillRect,[NC_DC],client,HOLLOW_BRUSH
        invoke  SetBkMode,[NC_DC], TRANSPARENT
        invoke  GetSystemMetrics,SM_CYEDGE
        invoke  TextOut,[NC_DC], eax, eax, ntext, 4
        label loop_TextOut at $-6
        sub     esp,5*4
        inc     byte[ntext+3]
        cmp     byte[ntext+3],'9'
        jbe     @F
        sub     byte[ntext+3],10
        inc     byte[ntext+2]
        cmp     byte[ntext+2],'9'
        jbe     @F
        sub     byte[ntext+2],10
        inc     byte[ntext+1]
        cmp     byte[ntext+1],'9'
        jbe     @F
        sub     byte[ntext+1],10
        inc     byte[ntext+0]
  @@:
        mov     eax,[size.Y]
        add     dword[esp+8],eax
        mov     eax,dword[esp+8]
        sub     eax,[size.Y]
        dec     esi
        jz      @F
        cmp     eax,[client.bottom]
        jb      loop_TextOut
  @@:
        add     esp,5*4
        pop     esi
        invoke  ReleaseDC,[hwnd],[NC_DC]
        xor     eax,eax
        exitf
        jmp     [BaseEditProc]
flush_locals

section '.data' data readable writeable

  _title TCHAR 'MiniPad',0
  _about_title TCHAR 'About MiniPad',0
  _about_text TCHAR 'This is Win32 example program created with flat assembler.',0
  _error TCHAR 'Startup failed.',0

  _class TCHAR 'MINIPAD32',0
  _edit TCHAR 'EDIT',0
  ntext TCHAR '0001',0

  wc WNDCLASS 0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class

  edithwnd dd ?
  editfont dd ?
  BaseEditProc dd ?
  NC_DC dd ?
  msg MSG
  client RECT
  size:
        .X dd ?
        .Y dd ?

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL',\
          gdi,'GDI32.DLL'

  import kernel,\
         GetModuleHandle,'GetModuleHandleA',\
         ExitProcess,'ExitProcess'

  import user,\
         RegisterClass,'RegisterClassA',\
         CreateWindowEx,'CreateWindowExA',\
         DefWindowProc,'DefWindowProcA',\
         SetWindowLong,'SetWindowLongA',\
         RedrawWindow,'RedrawWindow',\
         GetMessage,'GetMessageA',\
         TranslateMessage,'TranslateMessage',\
         DispatchMessage,'DispatchMessageA',\
         SendMessage,'SendMessageA',\
         LoadCursor,'LoadCursorA',\
         LoadIcon,'LoadIconA',\
         LoadMenu,'LoadMenuA',\
         GetClientRect,'GetClientRect',\
         DrawText,'DrawTextA',\
         MoveWindow,'MoveWindow',\
         SetFocus,'SetFocus',\
         MessageBox,'MessageBoxA',\
         GetDCEx,'GetDCEx',\
         GetWindowDC,'GetWindowDC',\
         GetWindowRgn,'GetWindowRgn',\
         ReleaseDC,'ReleaseDC',\
         FillRect,'FillRect',\
         InvalidateRect,'InvalidateRect',\
         GetSystemMetrics,'GetSystemMetrics',\
         PostQuitMessage,'PostQuitMessage'

  import gdi,\
         CreateFont,'CreateFontA',\
         GetTextExtentPoint32,'GetTextExtentPoint32A',\
         TextOut,'TextOutA',\
         SetTextColor,'SetTextColor',\
         SetBkColor,'SetBkColor',\
         SetTextAlign,'SetTextAlign',\
         SetBkMode,'SetBkMode',\
         SelectObject,'SelectObject',\
         DeleteObject,'DeleteObject'

section '.rsrc' resource data readable

  ; resource directory

  directory RT_MENU,menus,\
            RT_ICON,icons,\
            RT_GROUP_ICON,group_icons,\
            RT_VERSION,versions

  ; resource subdirectories

  resource menus,\
           IDR_MENU,LANG_ENGLISH+SUBLANG_DEFAULT,main_menu

  resource icons,\
           1,LANG_NEUTRAL,icon_data

  resource group_icons,\
           IDR_ICON,LANG_NEUTRAL,main_icon

  resource versions,\
           1,LANG_NEUTRAL,version

  menu main_menu
       menuitem '&File',0,MFR_POPUP
                menuitem '&New',IDM_NEW
                menuseparator
                menuitem 'E&xit',IDM_EXIT,MFR_END
       menuitem '&Help',0,MFR_POPUP + MFR_END
                menuitem '&About...',IDM_ABOUT,MFR_END

  icon main_icon,icon_data,'minipad.ico'

  versioninfo version,VOS__WINDOWS32,VFT_APP,VFT2_UNKNOWN,LANG_ENGLISH+SUBLANG_DEFAULT,0,\
              'FileDescription','MiniPad - example program',\
              'LegalCopyright','No rights reserved.',\
              'FileVersion','1.0',\
              'ProductVersion','1.0',\
              'OriginalFilename','MINIPAD.EXE'

LINENUMB

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,723 questions
0 comments No comments
{count} votes

Accepted answer
  1. Castorix31 86,701 Reputation points
    2023-04-06T13:40:56.3733333+00:00

    To simplify, you can use EM_SETRECT and just draw in WM_PAINT

    A test :

    		HWND hEdit1 = CreateWindowEx(0, L"EDIT", L"Edit1", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE, 100, 30, 380, 300, hWnd, (HMENU)10, hInst, NULL);
    		RECT rect1;
    		GetClientRect(hEdit1, &rect1);
    		rect1.top += 20;
    		rect1.left += 20;
    		SendMessage(hEdit1, EM_SETRECT, 0, (LPARAM)(LPRECT)&rect1);
    		BOOL bRet = SetWindowSubclass(hEdit1, EditSubclassProc, 0, 0);
    
    

    Proc :

    LRESULT CALLBACK EditSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
    {
    	switch (uMsg)
    	{
    		case WM_PAINT:
    		{			
    			DefSubclassProc(hWnd, uMsg, wParam, lParam);
    			HDC hDC = GetDC(hWnd);
    			RECT rectClient;
    			GetClientRect(hWnd, &rectClient);
    
    			HBRUSH hBrushRed = CreateSolidBrush(RGB(255, 0, 0));
    			RECT rect = { 0, 0, rectClient.right - rectClient.left, 20 };
    			FillRect(hDC, &rect, hBrushRed);
    			SetBkMode(hDC, TRANSPARENT);
    			DrawText(hDC, L"This is a test", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    			DeleteObject(hBrushRed);
    
    			HBRUSH hBrushBlue = CreateSolidBrush(RGB(0, 0, 255));
    			RECT rect2 = { 0, 0, 20, rectClient.bottom - rectClient.top };
    			FillRect(hDC, &rect2, hBrushBlue);
    			DeleteObject(hBrushBlue);
    			ReleaseDC(hWnd, hDC);
    			return 0;	
    		}
    		break;
    	}
    	return DefSubclassProc(hWnd, uMsg, wParam, lParam);
    }
    
    
    
    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. ProMiNick 40 Reputation points
    2023-04-06T13:08:00.32+00:00

    Sorry, all. graphical artifacts were present until I not changed

    NewEditProc.wmpaint: RTL_C hwnd,wmsg,wparam,lparam
            frame   ebp
            invoke  BaseEditProc,[hwnd],WM_PAINT,0,0
            invoke  GetWindowRgn,[hwnd],[wparam]
            mov     [wmsg],WM_NCPAINT ; without this one were graphic artifacts in client,scroll,border areas after moving out covering windows, but just before post it here I already add this line
            exitf
    flush_locals
    

    and not changed

            invoke  GetClientRect,[hwnd],client
            invoke  GetSystemMetrics,SM_CYEDGE
            add     [client.left],eax
            add     [client.top],eax
            mov     [client.right],40
            sub     [client.right],eax
            add     [client.bottom],eax ; without this line, parts of linenumbers text are lay down (near) the bottom border, and border not repainted, but just before post it here I already add this line 
            invoke  GetSystemMetrics,SM_CYHSCROLL
            add     [client.bottom],eax
            invoke  SelectObject,[NC_DC], [editfont]
            invoke  FillRect,[NC_DC],client,HOLLOW_BRUSH
    

    I couldnt reproduce problem at win10 (not because something special in win10), because at that moment it was already solved. Reputation that could be counted by number isnt valueble for me at all. You are free to remove that question without question. Is here ability to rephrase question? However, I still will be very glad for any usable critic or suggestions related to improvement of above one, I accept any programming languages or even pure theory, no matter.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.