Bug 28541

Summary: No drawing after InvalidRect for partial window content
Product: cairo Reporter: Tim Teulings <tim>
Component: win32 backendAssignee: cairo-bugs mailing list <cairo-bugs>
Status: RESOLVED FIXED QA Contact: cairo-bugs mailing list <cairo-bugs>
Severity: critical    
Priority: medium CC: ibaldo, kalevlember, mkbosmans
Version: 1.9.8   
Hardware: x86 (IA32)   
OS: Windows (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: Ingmar's patch
Test case

Description Tim Teulings 2010-06-14 14:36:02 UTC
I now started using cairo not only under X11 but also under Windows. My code is designed that way, that I only draw within the WM_PAINT event, creating a cairo surface using the HDC returned by BeginPaint (AFAIK this is the recommended way). For getting parts of the windows repainted because of changes (in reaction to user input) I call InvalidateRect (I also tried RedrawWindow with same results) with the RECT to get redrawn to trigger WM_PAINT message for the given region (AFAIK this is also the recommended way).

This works fine for WM_PAINT messages created by the system (initial WM_PAINT on window open, WM_PAINT messages on window resize) but it fails for WM_PAINT messages created by InvalidateRect - if a valid RECT* is handed over. While the content of WM_PAINT is correct and cairo_clip_extents() on the temporary surface shows reasonable values in all cases (while having the coordinate origin obviously in lower left, instead upper left) , nothing gets drawn at all. As soon as I pass NULL as RECT*, redraw works like a charm. However doing full screen refresh on every redraw is obviously not a long term solution ;-) I assume that cases where the cliping reagion on the HDC is not the full window are differently (and possibly wrongly?) handled or I'm doing something wrong (e.g. are there any coordinate system transformations necessary on the Windows or cairo side I'm missing?).

I'm using cairo-1.9.6 together with pixmap-0.18.2, the bug also occurs unde 1.9.8, but not under 1.8.10!

For a demo compile below program and press a key in the window. It show do a InvalidRect for a part of the window, switch alternate to "true" and then draw in a different color over the fulkl window size. I you now see a red rectangle on a green background everything is fine, if you only see the initial green background, the second draw did nothing! I assume that something is wrong witht he handling of DC that have a cliping rectangle attached. If you change the InvalidRect rectangle to the full window size (0,0-256,256), everything works again.

You can compile the application undermingw using the following command line:
g++ cairo-gdi-demo.cpp `pkg-config.exe --cflags cairo
` `pkg-config.exe --libs cairo` -o cairo-gdi-demo.exe

As you can see from the copyright, this is a modified exampl by Andrew Lim.

/**
cairo-gdi-demo.cpp

Demonstrates how to get Cairo Graphics working with the Windows API and GDI.
                     
Author: Andrew Lim
Email:  danteshamest@gmail.com
Site: windrealm.com
*/

#include <windows.h>
#include <cmath>
#include <cairo.h>
#include <cairo-win32.h>

#include <iostream>

static bool alternative=false;

/**
  Gradient demonstration.
  Taken from http://cairographics.org/samples/
*/
void gradientExample( cairo_t* cr ) {
  if (!alternative) {
    cairo_set_source_rgb(cr,0,1,0);
    cairo_rectangle (cr, 0, 0, 256, 256);
    cairo_fill (cr);
  }
  else {
    cairo_set_source_rgb(cr,1,0,0);
    cairo_rectangle (cr, 0, 0, 256, 256);
    cairo_fill (cr);
  }
}

/**
  Changes the dimensions of a window's client area.
*/
void SetClientSize( HWND hwnd, int clientWidth, int clientHeight ) {
  if ( IsWindow( hwnd ) ) {
    DWORD dwStyle = GetWindowLongPtr( hwnd, GWL_STYLE ) ;
    DWORD dwExStyle = GetWindowLongPtr( hwnd, GWL_EXSTYLE ) ;
    HMENU menu = GetMenu( hwnd ) ;
    RECT rc = { 0, 0, clientWidth, clientHeight } ;
    AdjustWindowRectEx( &rc, dwStyle, menu ? TRUE : FALSE, dwExStyle );
    SetWindowPos( hwnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
                  SWP_NOZORDER | SWP_NOMOVE ) ;
  }
}

/**
  Handles WM_PAINT.
*/
LRESULT onPaint( HWND hwnd, WPARAM wParam, LPARAM lParam ) {
  PAINTSTRUCT ps ;
  HDC hdc = BeginPaint( hwnd, &ps );

  // Create the cairo surface and context.
  cairo_surface_t *surface = cairo_win32_surface_create (hdc);
  cairo_t *cr = cairo_create (surface);
  
  // Draw on the cairo context.
  gradientExample( cr );

  // Cleanup.
  cairo_destroy (cr);
  cairo_surface_destroy (surface);

  EndPaint( hwnd, &ps );
  return 0 ;
}

LRESULT onKey( HWND hwnd, WPARAM wParam, LPARAM lParam ) {
  RECT rect;
  
  rect.left=128;
  rect.top=128;
  rect.right=64;
  rect.bottom=64;

  alternative=true;
  
  InvalidateRect(hwnd,&rect,false);
}
/**
  Handles WM_CLOSE.
*/
LRESULT onClose( HWND hwnd, WPARAM wParam, LPARAM lParam ) {
  PostQuitMessage( 0 );
  return 0 ;
}

/**
  Handles our window's messages.
*/
LRESULT CALLBACK WndProc( HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam ) {
  switch(msg) {
    case WM_PAINT:
      return onPaint( hwnd, wParam, lParam );
    case WM_CLOSE:
      return onClose( hwnd, wParam, lParam );
    case WM_KEYDOWN:
      std::cout << "Key down!" << std::endl;
      return onKey( hwnd, wParam, lParam );
      break;
    default:
      return DefWindowProc(hwnd,msg,wParam,lParam);
  }
}

int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrev, LPSTR args, int nShow ) {
  MSG  msg ;
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "CairoGdiWndClass" );
  wc.hInstance     = hInst ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);

  RegisterClass(&wc);
  HWND hwnd = CreateWindow( wc.lpszClassName,TEXT("Cairo & GDI Demo"),
                            WS_OVERLAPPEDWINDOW, 0,0,256,256,0,0,hInst,0);
  SetClientSize( hwnd, 256, 256 );
  ShowWindow( hwnd, SW_SHOWNORMAL );

  while( GetMessage(&msg,0,0,0) > 0 ) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int)msg.wParam;
}
Comment 1 Ivan Baldo 2010-10-07 05:59:45 UTC
Patch here http://sprunge.us/KOZU by Ingmar Runge ingmar@irsoft.de .
There was a discussion on the mailing list but amazingly nobody cared to update this bug report with the information from the mailing list, I guessed the authors from that discussion would do that but they didn't.
Someone with good knowledge about Cairo should review that patch and apply if it is correct or contact Ingmar to further discuss.
Thanks a lot!!!
Comment 2 rainman 2010-11-21 19:11:21 UTC
I tested on windows, it is fiexed.
Comment 3 Uli Schlachter 2010-12-21 10:56:49 UTC
Reopening since nothing was committed anywhere yet, there's just a patch attached here.
Comment 4 Andrea Canciani 2011-07-26 01:23:44 UTC
Created attachment 49554 [details] [review]
Ingmar's patch

This (i.e. Ingmar's) patch has been reviewed and deemed incomplete.

Two possible solutions were suggested:
 - the simple way to fix the bug would be to make sure that
   the top-left corner of the surface are (0,0)

- the best way to fix it would be to handle arbitrary extents
   for surfaces, which is presumably needed for handling unlimited
   surfaces (cairo has some support for them, but a lot of places
   still assume that surfaces are bounded and have (0,0) as
   top-left corner).

FTR, I believe that https://bugs.freedesktop.org/show_bug.cgi?id=37140
might be related, or even the same bug.
Comment 5 Andrea Canciani 2011-07-26 06:08:52 UTC
Created attachment 49576 [details]
Test case

This is a self-contained cairo-style test case, which is fixed by Ingmar's patch.
Comment 6 Andrea Canciani 2011-07-27 01:18:10 UTC
The preferred solution seems to be implemented by:

commit 91faf9c1cf79f44b48c0f14d2d551a68bf38b5a5
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Tue Jul 26 15:50:32 2011 +0100

    composite: Pass unbounded extents to initialisation
    
    For an unbounded surface we cannot assume (0, 0, surface_width,
    surface_height) as that is wrong and causes the operation to appear
    clipped.
    
    Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

The cairo-gdi-demo.cpp now works as expected on cairo-master for me.
Please reopen if you manage to reproduce the bug.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.