X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c11bdf68b89e56b79828e67acc64d236f6623f7a..4e15d1caa03346c126015019c1fdf093033ef40b:/src/gtk/dcclient.cpp diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index 7c647971b0..0ad9159ef5 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////// // Name: src/gtk/dcclient.cpp -// Purpose: +// Purpose: wxWindowDCImpl implementation // Author: Robert Roebling // RCS-ID: $Id$ // Copyright: (c) 1998 Robert Roebling, Chris Breeze @@ -10,10 +10,6 @@ // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" -#ifdef __VMS -#define XCopyPlane XCOPYPLANE -#endif - #include "wx/gtk/dcclient.h" #ifndef WX_PRECOMP @@ -29,8 +25,7 @@ #include "wx/gtk/private.h" #include "wx/gtk/private/object.h" - -#include +#include "wx/private/textmeasure.h" //----------------------------------------------------------------------------- // local defines @@ -56,8 +51,6 @@ static GdkPixmap* hatches[wxBRUSHSTYLE_LAST_HATCH - wxBRUSHSTYLE_FIRST_HATCH + 1]; -extern GtkWidget *wxGetRootWindow(); - //----------------------------------------------------------------------------- // constants //----------------------------------------------------------------------------- @@ -79,59 +72,46 @@ static GdkPixmap* GetHatch(int style) const int i = style - wxBRUSHSTYLE_FIRST_HATCH; if (hatches[i] == NULL) { + // This macro creates a bitmap from an XBM file included above. Notice + // the need for the cast because gdk_bitmap_create_from_data() doesn't + // accept unsigned data but the arrays in XBM need to be unsigned to + // avoid warnings (and even errors in C+0x mode) from g++. +#define CREATE_FROM_XBM_DATA(name) \ + gdk_bitmap_create_from_data \ + ( \ + NULL, \ + reinterpret_cast(name ## _bits), \ + name ## _width, \ + name ## _height \ + ) + switch (style) { case wxBRUSHSTYLE_BDIAGONAL_HATCH: - hatches[i] = gdk_bitmap_create_from_data(NULL, bdiag_bits, bdiag_width, bdiag_height); + hatches[i] = CREATE_FROM_XBM_DATA(bdiag); break; case wxBRUSHSTYLE_CROSSDIAG_HATCH: - hatches[i] = gdk_bitmap_create_from_data(NULL, cdiag_bits, cdiag_width, cdiag_height); + hatches[i] = CREATE_FROM_XBM_DATA(cdiag); break; case wxBRUSHSTYLE_CROSS_HATCH: - hatches[i] = gdk_bitmap_create_from_data(NULL, cross_bits, cross_width, cross_height); + hatches[i] = CREATE_FROM_XBM_DATA(cross); break; case wxBRUSHSTYLE_FDIAGONAL_HATCH: - hatches[i] = gdk_bitmap_create_from_data(NULL, fdiag_bits, fdiag_width, fdiag_height); + hatches[i] = CREATE_FROM_XBM_DATA(fdiag); break; case wxBRUSHSTYLE_HORIZONTAL_HATCH: - hatches[i] = gdk_bitmap_create_from_data(NULL, horiz_bits, horiz_width, horiz_height); + hatches[i] = CREATE_FROM_XBM_DATA(horiz); break; case wxBRUSHSTYLE_VERTICAL_HATCH: - hatches[i] = gdk_bitmap_create_from_data(NULL, verti_bits, verti_width, verti_height); + hatches[i] = CREATE_FROM_XBM_DATA(verti); break; } + +#undef CREATE_FROM_XBM_DATA } return hatches[i]; } -//----------------------------------------------------------------------------- -// temporary implementation of the missing GDK function -//----------------------------------------------------------------------------- - -static -void gdk_wx_draw_bitmap(GdkDrawable *drawable, - GdkGC *gc, - GdkDrawable *src, - gint xsrc, - gint ysrc) -{ - wxCHECK_RET( drawable, _T("NULL drawable in gdk_wx_draw_bitmap") ); - wxCHECK_RET( src, _T("NULL src in gdk_wx_draw_bitmap") ); - wxCHECK_RET( gc, _T("NULL gc in gdk_wx_draw_bitmap") ); - - gint src_width, src_height; - gdk_drawable_get_size(src, &src_width, &src_height); - - XCopyPlane( GDK_WINDOW_XDISPLAY(drawable), - GDK_WINDOW_XID(src), - GDK_WINDOW_XID(drawable), - GDK_GC_XGC(gc), - xsrc, ysrc, - src_width, src_height, - 0, 0, - 1 ); -} - //----------------------------------------------------------------------------- // Implement Pool of Graphic contexts. Creating them takes too much time. //----------------------------------------------------------------------------- @@ -150,7 +130,11 @@ enum wxPoolGCType wxTEXT_SCREEN, wxBG_SCREEN, wxPEN_SCREEN, - wxBRUSH_SCREEN + wxBRUSH_SCREEN, + wxTEXT_COLOUR_ALPHA, + wxBG_COLOUR_ALPHA, + wxPEN_COLOUR_ALPHA, + wxBRUSH_COLOUR_ALPHA }; struct wxGC @@ -252,7 +236,7 @@ static GdkGC* wxGetPoolGC( GdkWindow *window, wxPoolGCType type ) // The realloc failed. Fall through to error. wxFAIL_MSG( wxT("No GC available") ); - return (GdkGC*) NULL; + return NULL; } static void wxFreePoolGC( GdkGC *gc ) @@ -278,16 +262,16 @@ IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl, wxGTKDCImpl) wxWindowDCImpl::wxWindowDCImpl( wxDC *owner ) : wxGTKDCImpl( owner ) { - m_gdkwindow = (GdkWindow*) NULL; - m_penGC = (GdkGC *) NULL; - m_brushGC = (GdkGC *) NULL; - m_textGC = (GdkGC *) NULL; - m_bgGC = (GdkGC *) NULL; - m_cmap = (GdkColormap *) NULL; + m_gdkwindow = NULL; + m_penGC = NULL; + m_brushGC = NULL; + m_textGC = NULL; + m_bgGC = NULL; + m_cmap = NULL; m_isScreenDC = false; - m_context = (PangoContext *)NULL; - m_layout = (PangoLayout *)NULL; - m_fontdesc = (PangoFontDescription *)NULL; + m_context = NULL; + m_layout = NULL; + m_fontdesc = NULL; } wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) : @@ -295,34 +279,35 @@ wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) : { wxASSERT_MSG( window, wxT("DC needs a window") ); - m_gdkwindow = (GdkWindow*) NULL; - m_penGC = (GdkGC *) NULL; - m_brushGC = (GdkGC *) NULL; - m_textGC = (GdkGC *) NULL; - m_bgGC = (GdkGC *) NULL; - m_cmap = (GdkColormap *) NULL; + m_gdkwindow = NULL; + m_penGC = NULL; + m_brushGC = NULL; + m_textGC = NULL; + m_bgGC = NULL; + m_cmap = NULL; m_isScreenDC = false; m_font = window->GetFont(); GtkWidget *widget = window->m_wxwindow; + m_gdkwindow = window->GTKGetDrawingWindow(); // Some controls don't have m_wxwindow - like wxStaticBox, but the user - // code should still be able to create wxClientDCs for them, so we will - // use the parent window here then. + // code should still be able to create wxClientDCs for them if ( !widget ) { - window = window->GetParent(); - widget = window->m_wxwindow; - } + widget = window->m_widget; + + wxCHECK_RET(widget, "DC needs a widget"); - wxASSERT_MSG( widget, wxT("DC needs a widget") ); + m_gdkwindow = widget->window; + if (!gtk_widget_get_has_window(widget)) + SetDeviceLocalOrigin(widget->allocation.x, widget->allocation.y); + } - m_context = window->GtkGetPangoDefaultContext(); + m_context = window->GTKGetPangoDefaultContext(); m_layout = pango_layout_new( m_context ); m_fontdesc = pango_font_description_copy( widget->style->font_desc ); - m_gdkwindow = widget->window; - // Window not realized ? if (!m_gdkwindow) { @@ -332,7 +317,7 @@ wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) : return; } - m_cmap = gtk_widget_get_colormap( widget ? widget : window->m_widget ); + m_cmap = gtk_widget_get_colormap(widget); SetUpDC(); @@ -345,7 +330,7 @@ wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) : m_window = window; - if (m_window && m_window->m_wxwindow && + if (m_window && m_window->m_wxwindow && (m_window->GetLayoutDirection() == wxLayout_RightToLeft)) { // reverse sense @@ -387,7 +372,7 @@ void wxWindowDCImpl::SetUpDC( bool isMemDC ) } if (!done) - { + { if (m_isScreenDC) { m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_SCREEN ); @@ -395,6 +380,18 @@ void wxWindowDCImpl::SetUpDC( bool isMemDC ) m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_SCREEN ); m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_SCREEN ); } +#if GTK_CHECK_VERSION(2,12,0) + // gdk_screen_get_rgba_colormap was added in 2.8, but this code is for + // compositing which requires 2.12 + else if (gtk_check_version(2,12,0) == NULL && + m_cmap == gdk_screen_get_rgba_colormap(gdk_colormap_get_screen(m_cmap))) + { + m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR_ALPHA ); + m_brushGC = wxGetPoolGC( m_gdkwindow, wxBRUSH_COLOUR_ALPHA ); + m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_COLOUR_ALPHA ); + m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_COLOUR_ALPHA ); + } +#endif else { m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR ); @@ -446,25 +443,25 @@ void wxWindowDCImpl::SetUpDC( bool isMemDC ) gdk_gc_set_function( m_penGC, GDK_COPY ); /* clipping */ - gdk_gc_set_clip_rectangle( m_penGC, (GdkRectangle *) NULL ); - gdk_gc_set_clip_rectangle( m_brushGC, (GdkRectangle *) NULL ); - gdk_gc_set_clip_rectangle( m_textGC, (GdkRectangle *) NULL ); - gdk_gc_set_clip_rectangle( m_bgGC, (GdkRectangle *) NULL ); + gdk_gc_set_clip_rectangle( m_penGC, NULL ); + gdk_gc_set_clip_rectangle( m_brushGC, NULL ); + gdk_gc_set_clip_rectangle( m_textGC, NULL ); + gdk_gc_set_clip_rectangle( m_bgGC, NULL ); } void wxWindowDCImpl::DoGetSize( int* width, int* height ) const { - wxCHECK_RET( m_window, _T("GetSize() doesn't work without window") ); + wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") ); m_window->GetSize(width, height); } bool wxWindowDCImpl::DoFloodFill(wxCoord x, wxCoord y, - const wxColour& col, int style) + const wxColour& col, wxFloodFillStyle style) { #if wxUSE_IMAGE extern bool wxDoFloodFill(wxDC *dc, wxCoord x, wxCoord y, - const wxColour & col, int style); + const wxColour & col, wxFloodFillStyle style); return wxDoFloodFill( GetOwner(), x, y, col, style); #else @@ -479,32 +476,40 @@ bool wxWindowDCImpl::DoFloodFill(wxCoord x, wxCoord y, bool wxWindowDCImpl::DoGetPixel( wxCoord x1, wxCoord y1, wxColour *col ) const { -#if wxUSE_IMAGE - // Generic (and therefore rather inefficient) method. - // Could be improved. - wxMemoryDC memdc; - wxBitmap bitmap(1, 1); - memdc.SelectObject(bitmap); - memdc.Blit(0, 0, 1, 1, GetOwner(), x1, y1); - memdc.SelectObject(wxNullBitmap); - - wxImage image = bitmap.ConvertToImage(); - col->Set(image.GetRed(0, 0), image.GetGreen(0, 0), image.GetBlue(0, 0)); + GdkImage* image = NULL; + if (m_gdkwindow) + { + const int x = LogicalToDeviceX(x1); + const int y = LogicalToDeviceY(y1); + wxRect rect; + gdk_drawable_get_size(m_gdkwindow, &rect.width, &rect.height); + if (rect.Contains(x, y)) + image = gdk_drawable_get_image(m_gdkwindow, x, y, 1, 1); + } + if (image == NULL) + { + *col = wxColour(); + return false; + } + GdkColormap* colormap = gdk_image_get_colormap(image); + const unsigned pixel = gdk_image_get_pixel(image, 0, 0); + if (colormap == NULL) + *col = pixel ? m_textForegroundColour : m_textBackgroundColour; + else + { + GdkColor c; + gdk_colormap_query_color(colormap, pixel, &c); + col->Set(c.red >> 8, c.green >> 8, c.blue >> 8); + } + g_object_unref(image); return true; -#else // !wxUSE_IMAGE - wxUnusedVar(x1); - wxUnusedVar(y1); - wxUnusedVar(col); - - return false; -#endif // wxUSE_IMAGE/!wxUSE_IMAGE } void wxWindowDCImpl::DoDrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2 ) { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + + if ( m_pen.IsNonTransparent() ) { if (m_gdkwindow) gdk_draw_line( m_gdkwindow, m_penGC, XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2) ); @@ -518,7 +523,7 @@ void wxWindowDCImpl::DoCrossHair( wxCoord x, wxCoord y ) { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) { int w = 0; int h = 0; @@ -616,7 +621,7 @@ void wxWindowDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, if (m_gdkwindow) { - if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) + if ( m_brush.IsNonTransparent() ) { GdkGC* gc; bool originChanged; @@ -628,11 +633,11 @@ void wxWindowDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, gdk_gc_set_ts_origin(gc, 0, 0); } - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) { gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 ); - if ((m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) && (alpha2 - alpha1 != 360*64)) + if ( m_brush.IsNonTransparent() && (alpha2 - alpha1 != 360*64) ) { gdk_draw_line( m_gdkwindow, m_penGC, xx1, yy1, xxc, yyc ); gdk_draw_line( m_gdkwindow, m_penGC, xxc, yyc, xx2, yy2 ); @@ -662,7 +667,7 @@ void wxWindowDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxC wxCoord start = wxCoord(sa * 64.0); wxCoord end = wxCoord((ea-sa) * 64.0); - if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) + if ( m_brush.IsNonTransparent() ) { GdkGC* gc; bool originChanged; @@ -674,7 +679,7 @@ void wxWindowDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxC gdk_gc_set_ts_origin(gc, 0, 0); } - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xx, yy, ww, hh, start, end ); } @@ -686,7 +691,7 @@ void wxWindowDCImpl::DoDrawPoint( wxCoord x, wxCoord y ) { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if ((m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) && m_gdkwindow) + if ( m_pen.IsNonTransparent() && m_gdkwindow ) gdk_draw_point( m_gdkwindow, m_penGC, XLOG2DEV(x), YLOG2DEV(y) ); CalcBoundingBox (x, y); @@ -696,9 +701,11 @@ void wxWindowDCImpl::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCo { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if (m_pen.GetStyle() == wxPENSTYLE_TRANSPARENT) return; if (n <= 0) return; + if ( m_pen.IsTransparent() ) + return; + //Check, if scaling is necessary const bool doScale = xoffset != 0 || yoffset != 0 || XLOG2DEV(10) != 10 || YLOG2DEV(10) != 10; @@ -726,7 +733,9 @@ void wxWindowDCImpl::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCo delete[] gpts; } -void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, int WXUNUSED(fillStyle) ) +void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[], + wxCoord xoffset, wxCoord yoffset, + wxPolygonFillMode WXUNUSED(fillStyle) ) { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); @@ -755,7 +764,7 @@ void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[], wxCoord xoffset, wx if (m_gdkwindow) { - if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) + if ( m_brush.IsNonTransparent() ) { GdkGC* gc; bool originChanged; @@ -767,7 +776,7 @@ void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[], wxCoord xoffset, wx gdk_gc_set_ts_origin(gc, 0, 0); } - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) { /* for (i = 0 ; i < n ; i++) @@ -806,7 +815,7 @@ void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoo if (m_gdkwindow) { - if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) + if ( m_brush.IsNonTransparent() ) { GdkGC* gc; bool originChanged; @@ -818,9 +827,8 @@ void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoo gdk_gc_set_ts_origin(gc, 0, 0); } - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) { -#if 1 if ((m_pen.GetWidth() == 2) && (m_pen.GetCap() == wxCAP_ROUND) && (m_pen.GetJoin() == wxJOIN_ROUND) && (m_pen.GetStyle() == wxPENSTYLE_SOLID)) { @@ -843,7 +851,6 @@ void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoo gdk_gc_set_line_attributes( m_penGC, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND ); } else -#endif { // Just use X11 for other cases gdk_draw_rectangle( m_gdkwindow, m_penGC, FALSE, xx, yy, ww-1, hh-1 ); @@ -884,7 +891,7 @@ void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width // CMB: adjust size if outline is drawn otherwise the result is // 1 pixel too wide and high - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) { ww--; hh--; @@ -899,7 +906,7 @@ void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width if (dd > hh) dd = hh; rr = dd / 2; - if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) + if ( m_brush.IsNonTransparent() ) { GdkGC* gc; bool originChanged; @@ -916,7 +923,7 @@ void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width gdk_gc_set_ts_origin(gc, 0, 0); } - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) { gdk_draw_line( m_gdkwindow, m_penGC, xx+rr+1, yy, xx+ww-rr, yy ); gdk_draw_line( m_gdkwindow, m_penGC, xx+rr+1, yy+hh, xx+ww-rr, yy+hh ); @@ -949,7 +956,7 @@ void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord if (m_gdkwindow) { - if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) + if ( m_brush.IsNonTransparent() ) { GdkGC* gc; bool originChanged; @@ -957,7 +964,7 @@ void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord // If the pen is transparent pen we increase the size // for better compatibility with other platforms. - if (m_pen.GetStyle() == wxPENSTYLE_TRANSPARENT) + if (m_pen.IsTransparent()) { ++ww; ++hh; @@ -969,7 +976,7 @@ void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord gdk_gc_set_ts_origin(gc, 0, 0); } - if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) + if ( m_pen.IsNonTransparent() ) gdk_draw_arc( m_gdkwindow, m_penGC, false, xx, yy, ww, hh, 0, 360*64 ); } @@ -983,74 +990,93 @@ void wxWindowDCImpl::DoDrawIcon( const wxIcon &icon, wxCoord x, wxCoord y ) DoDrawBitmap( (const wxBitmap&)icon, x, y, true ); } -// compare to current clipping region -bool wxWindowDCImpl::IsOutsideOfClippingRegion(int x, int y, int w, int h) -{ - if (m_currentClippingRegion.IsNull()) - return false; - - wxRegion region(x, y, w, h); - region.Intersect( m_currentClippingRegion ); - return region.IsEmpty(); -} - -void wxWindowDCImpl::RemoveClipMask(GdkGC *gc) -{ - gdk_gc_set_clip_mask(gc, NULL); - gdk_gc_set_clip_origin(gc, 0, 0); - if (!m_currentClippingRegion.IsNull()) - gdk_gc_set_clip_region(gc, m_currentClippingRegion.GetRegion()); -} - -// For drawing a mono-bitmap (XBitmap) we use the current text GC -void wxWindowDCImpl::DoDrawMonoBitmap(const wxBitmap& bitmap, - int bmp_w, int bmp_h, - int xsrc, int ysrc, - int xdest, int ydest, - int width, int height) -{ - wxGtkObject - bitmap2(gdk_pixmap_new( wxGetRootWindow()->window, bmp_w, bmp_h, -1 )); - wxGtkObject gc(gdk_gc_new( bitmap2 )); - gdk_gc_set_foreground( gc, m_textForegroundColour.GetColor() ); - gdk_gc_set_background( gc, m_textBackgroundColour.GetColor() ); - gdk_wx_draw_bitmap(bitmap2, gc, bitmap.GetPixmap(), 0, 0); - - gdk_draw_drawable(m_gdkwindow, m_textGC, bitmap2, xsrc, ysrc, xdest, ydest, - width, height); -} - -// Returns a new mask that is the intersection of the old mask -// and m_currentClippingRegion with proper offsets -GdkBitmap* wxWindowDCImpl::GetClippedMask(GdkBitmap* mask, int w, int h, - int x, int y, - int xsrcMask, int ysrcMask) -{ - // create monochrome bitmap that will be used as the new mask - GdkBitmap *new_mask = gdk_pixmap_new( wxGetRootWindow()->window, w, h, 1 ); - - GdkColor c0, c1; - c0.pixel = 0; - c1.pixel = 1; - wxGtkObject gc(gdk_gc_new( new_mask )); - - // zero-ing new_mask - gdk_gc_set_foreground( gc, &c0 ); - gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, w, h ); - - // clipping region - gdk_gc_set_clip_region( gc, m_currentClippingRegion.GetRegion() ); - gdk_gc_set_clip_origin( gc, -x, -y ); - - // copy the old mask to the new_mask in the clip region area - gdk_gc_set_background( gc, &c0 ); - gdk_gc_set_foreground( gc, &c1 ); - gdk_gc_set_fill( gc, GDK_OPAQUE_STIPPLED ); - gdk_gc_set_ts_origin( gc, -xsrcMask, -ysrcMask ); - gdk_gc_set_stipple( gc, mask ); - gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, w, h ); - - return new_mask; +// scale a pixbuf, return new pixbuf, unref old one +static GdkPixbuf* +Scale(GdkPixbuf* pixbuf, int dst_w, int dst_h, double sx, double sy) +{ + GdkPixbuf* pixbuf_scaled = gdk_pixbuf_new( + GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf), 8, dst_w, dst_h); + gdk_pixbuf_scale(pixbuf, pixbuf_scaled, + 0, 0, dst_w, dst_h, 0, 0, sx, sy, GDK_INTERP_NEAREST); + g_object_unref(pixbuf); + return pixbuf_scaled; +} + +// scale part of a pixmap using pixbuf scaling, return pixbuf +static GdkPixbuf* +Scale(GdkPixmap* pixmap, int x, int y, int w, int h, int dst_w, int dst_h, double sx, double sy) +{ + GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable( + NULL, pixmap, NULL, x, y, 0, 0, w, h); + return Scale(pixbuf, dst_w, dst_h, sx, sy); +} + +// scale part of a mask pixmap, return new mask, unref old one +static GdkPixmap* +ScaleMask(GdkPixmap* mask, int x, int y, int w, int h, int dst_w, int dst_h, double sx, double sy) +{ + GdkPixbuf* pixbuf = Scale(mask, x, y, w, h, dst_w, dst_h, sx, sy); + + // convert black and white pixbuf back to a mono pixmap + const unsigned out_rowstride = (dst_w + 7) / 8; + const size_t data_size = out_rowstride * size_t(dst_h); + char* data = new char[data_size]; + char* out = data; + const guchar* row = gdk_pixbuf_get_pixels(pixbuf); + const int rowstride = gdk_pixbuf_get_rowstride(pixbuf); + memset(data, 0, data_size); + for (int j = 0; j < dst_h; j++, row += rowstride, out += out_rowstride) + { + const guchar* in = row; + for (int i = 0; i < dst_w; i++, in += 3) + if (*in) + out[i >> 3] |= 1 << (i & 7); + } + g_object_unref(pixbuf); + GdkPixmap* pixmap = gdk_bitmap_create_from_data(mask, data, dst_w, dst_h); + delete[] data; + g_object_unref(mask); + return pixmap; +} + +// Make a new mask from part of a mask and a clip region. +// Return new mask, unref old one. +static GdkPixmap* +ClipMask(GdkPixmap* mask, GdkRegion* clipRegion, int x, int y, int dst_x, int dst_y, int w, int h) +{ + GdkGCValues gcValues; + gcValues.foreground.pixel = 0; + GdkGC* gc = gdk_gc_new_with_values(mask, &gcValues, GDK_GC_FOREGROUND); + GdkPixmap* pixmap = gdk_pixmap_new(mask, w, h, 1); + // clear new mask, so clipped areas will be masked + gdk_draw_rectangle(pixmap, gc, true, 0, 0, w, h); + gdk_gc_set_clip_region(gc, clipRegion); + gdk_gc_set_clip_origin(gc, -dst_x, -dst_y); + // draw old mask onto new one, with clip + gdk_draw_drawable(pixmap, gc, mask, x, y, 0, 0, w, h); + g_object_unref(gc); + g_object_unref(mask); + return pixmap; +} + +// make a color pixmap from part of a mono one, using text fg/bg colors +GdkPixmap* +wxWindowDCImpl::MonoToColor(GdkPixmap* monoPixmap, int x, int y, int w, int h) const +{ + GdkPixmap* pixmap = gdk_pixmap_new(m_gdkwindow, w, h, -1); + GdkGCValues gcValues; + gcValues.foreground.pixel = m_textForegroundColour.GetColor()->pixel; + gcValues.background.pixel = m_textBackgroundColour.GetColor()->pixel; + gcValues.stipple = monoPixmap; + gcValues.fill = GDK_OPAQUE_STIPPLED; + gcValues.ts_x_origin = -x; + gcValues.ts_y_origin = -y; + GdkGC* gc = gdk_gc_new_with_values(pixmap, &gcValues, GdkGCValuesMask( + GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_STIPPLE | GDK_GC_FILL | + GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN)); + gdk_draw_rectangle(pixmap, gc, true, 0, 0, w, h); + g_object_unref(gc); + return pixmap; } void wxWindowDCImpl::DoDrawBitmap( const wxBitmap &bitmap, @@ -1058,87 +1084,111 @@ void wxWindowDCImpl::DoDrawBitmap( const wxBitmap &bitmap, bool useMask ) { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - wxCHECK_RET( bitmap.IsOk(), wxT("invalid bitmap") ); - bool is_mono = bitmap.GetDepth() == 1; - - // scale/translate size and position - int xx = XLOG2DEV(x); - int yy = YLOG2DEV(y); - - int w = bitmap.GetWidth(); - int h = bitmap.GetHeight(); - - if (m_window && m_window->GetLayoutDirection() == wxLayout_RightToLeft) - xx -= w; - - CalcBoundingBox( x, y ); - CalcBoundingBox( x + w, y + h ); - if (!m_gdkwindow) return; - int ww = XLOG2DEVREL(w); - int hh = YLOG2DEVREL(h); + const int w = bitmap.GetWidth(); + const int h = bitmap.GetHeight(); - if (IsOutsideOfClippingRegion( xx,yy,ww,hh )) - return; + // notice that as the bitmap is not drawn upside down (or right to left) + // even if the corresponding axis direction is inversed, we need to take it + // into account when calculating its bounding box + CalcBoundingBox(x, y); + CalcBoundingBox(x + m_signX*w, y + m_signY*h); - // scale bitmap if required - wxBitmap use_bitmap = bitmap; - if ((w != ww) || (h != hh)) - use_bitmap = use_bitmap.Rescale( 0, 0, w, h, ww, hh ); + // device coords + int xx = LogicalToDeviceX(x); + const int yy = LogicalToDeviceY(y); + const int ww = LogicalToDeviceXRel(w); + const int hh = LogicalToDeviceYRel(h); - // get mask if any - GdkBitmap *mask = (GdkBitmap *) NULL; - if (useMask && use_bitmap.GetMask()) - mask = use_bitmap.GetMask()->GetBitmap(); + if (m_window && m_window->GetLayoutDirection() == wxLayout_RightToLeft) + xx -= ww; - // for drawing a mono-bitmap we use the current text GC - GdkGC* use_gc = is_mono ? m_textGC : m_penGC; + GdkRegion* const clipRegion = m_currentClippingRegion.GetRegion(); + // determine clip region overlap + int overlap = wxInRegion; + if (clipRegion) + { + overlap = m_currentClippingRegion.Contains(xx, yy, ww, hh); + if (overlap == wxOutRegion) + return; + } - bool mask_owned = false; + const bool isScaled = ww != w || hh != h; + const bool hasAlpha = bitmap.HasAlpha(); + GdkGC* const use_gc = m_penGC; - if (mask != NULL) + GdkPixmap* mask = NULL; + // mask does not work when drawing a pixbuf with alpha + if (useMask && !hasAlpha) + { + wxMask* m = bitmap.GetMask(); + if (m) + mask = m->GetBitmap(); + } + if (mask) { - if (!m_currentClippingRegion.IsNull()) + g_object_ref(mask); + if (isScaled) + mask = ScaleMask(mask, 0, 0, w, h, ww, hh, m_scaleX, m_scaleY); + if (overlap == wxPartRegion) { - mask = GetClippedMask(mask, ww, hh, xx, yy, 0, 0); - mask_owned = true; + // need a new mask that also masks the clipped area, + // because gc can't have both a mask and a clip region + mask = ClipMask(mask, clipRegion, 0, 0, xx, yy, ww, hh); } - gdk_gc_set_clip_mask(use_gc, mask); gdk_gc_set_clip_origin(use_gc, xx, yy); } - // Draw XPixmap or XBitmap, depending on what the wxBitmap contains. - if (is_mono) + // determine whether to use pixmap or pixbuf + GdkPixmap* pixmap = NULL; + GdkPixbuf* pixbuf = NULL; + if (bitmap.HasPixmap()) + pixmap = bitmap.GetPixmap(); + if (pixmap && gdk_drawable_get_depth(pixmap) == 1) + { + // convert mono pixmap to color using text fg/bg colors + pixmap = MonoToColor(pixmap, 0, 0, w, h); + } + else if (hasAlpha || pixmap == NULL) { - DoDrawMonoBitmap(use_bitmap, ww, hh, 0, 0, xx, yy, -1, -1); + pixmap = NULL; + pixbuf = bitmap.GetPixbuf(); + g_object_ref(pixbuf); } else { - if (use_bitmap.HasPixbuf()) - { - gdk_draw_pixbuf(m_gdkwindow, use_gc, - use_bitmap.GetPixbuf(), - 0, 0, xx, yy, -1, -1, - GDK_RGB_DITHER_NORMAL, xx, yy); - } + g_object_ref(pixmap); + } + + if (isScaled) + { + if (pixbuf) + pixbuf = Scale(pixbuf, ww, hh, m_scaleX, m_scaleY); else - { - gdk_draw_drawable(m_gdkwindow, use_gc, - use_bitmap.GetPixmap(), - 0, 0, xx, yy, -1, -1); - } + pixbuf = Scale(pixmap, 0, 0, w, h, ww, hh, m_scaleX, m_scaleY); } - // remove mask again if any - if (mask != NULL) + if (pixbuf) + { + gdk_draw_pixbuf(m_gdkwindow, use_gc, pixbuf, + 0, 0, xx, yy, ww, hh, GDK_RGB_DITHER_NORMAL, 0, 0); + g_object_unref(pixbuf); + } + else + { + gdk_draw_drawable(m_gdkwindow, use_gc, pixmap, 0, 0, xx, yy, ww, hh); + } + + if (pixmap) + g_object_unref(pixmap); + if (mask) { - RemoveClipMask(use_gc); - if (mask_owned) - g_object_unref(mask); + g_object_unref(mask); + gdk_gc_set_clip_region(use_gc, clipRegion); } } @@ -1146,214 +1196,183 @@ bool wxWindowDCImpl::DoBlit( wxCoord xdest, wxCoord ydest, wxCoord width, wxCoord height, wxDC *source, wxCoord xsrc, wxCoord ysrc, - int logical_func, + wxRasterOperationMode logical_func, bool useMask, wxCoord xsrcMask, wxCoord ysrcMask ) { wxCHECK_MSG( IsOk(), false, wxT("invalid window dc") ); - wxCHECK_MSG( source, false, wxT("invalid source dc") ); if (!m_gdkwindow) return false; - // transform the source DC coords to the device ones - xsrc = source->LogicalToDeviceX(xsrc); - ysrc = source->LogicalToDeviceY(ysrc); - - wxBitmap selected; - wxMemoryDC *memDC = wxDynamicCast(source, wxMemoryDC); - if ( memDC ) + GdkDrawable* srcDrawable = NULL; + GdkPixmap* mask = NULL; + wxMemoryDC* memDC = wxDynamicCast(source, wxMemoryDC); + if (memDC) { - selected = memDC->GetSelectedBitmap(); - if ( !selected.IsOk() ) + const wxBitmap& bitmap = memDC->GetSelectedBitmap(); + if (!bitmap.IsOk()) return false; + srcDrawable = bitmap.GetPixmap(); + if (useMask) + { + wxMask* m = bitmap.GetMask(); + if (m) + mask = m->GetBitmap(); + } } - - bool use_bitmap_method = false; - bool is_mono = false; - - if (xsrcMask == -1 && ysrcMask == -1) + else { - xsrcMask = xsrc; - ysrcMask = ysrc; + wxDCImpl* impl = source->GetImpl(); + wxWindowDCImpl* gtk_impl = wxDynamicCast(impl, wxWindowDCImpl); + if (gtk_impl) + srcDrawable = gtk_impl->GetGDKWindow(); + if (srcDrawable == NULL) + return false; } - if (selected.IsOk()) - { - is_mono = (selected.GetDepth() == 1); - - // we use the "XCopyArea" way to copy a memory dc into - // a different window if the memory dc BOTH - // a) doesn't have any mask or its mask isn't used - // b) it is clipped - // c) is not 1-bit - - if (useMask && (selected.GetMask())) - { - // we HAVE TO use the direct way for memory dcs - // that have mask since the XCopyArea doesn't know - // about masks - use_bitmap_method = true; - } - else if (is_mono) - { - // we HAVE TO use the direct way for memory dcs - // that are bitmaps because XCopyArea doesn't cope - // with different bit depths - use_bitmap_method = true; - } - else if ((xsrc == 0) && (ysrc == 0) && - (width == selected.GetWidth()) && - (height == selected.GetHeight())) + CalcBoundingBox(xdest, ydest); + CalcBoundingBox(xdest + width, ydest + height); + + // source device coords + int src_x = source->LogicalToDeviceX(xsrc); + int src_y = source->LogicalToDeviceY(ysrc); + int src_w = source->LogicalToDeviceXRel(width); + int src_h = source->LogicalToDeviceYRel(height); + + // Clip source rect to source dc. + // Only necessary when scaling, to avoid GDK errors when + // converting to pixbuf, but no harm in always doing it. + // If source rect changes, it also changes the dest rect. + wxRect clip; + gdk_drawable_get_size(srcDrawable, &clip.width, &clip.height); + clip.Intersect(wxRect(src_x, src_y, src_w, src_h)); + if (src_w != clip.width || src_h != clip.height) + { + if (clip.width == 0) + return true; + + src_w = clip.width; + src_h = clip.height; + width = source->DeviceToLogicalXRel(src_w); + height = source->DeviceToLogicalYRel(src_h); + if (src_x != clip.x || src_y != clip.y) { - // we SHOULD use the direct way if all of the bitmap - // in the memory dc is copied in which case XCopyArea - // wouldn't be able able to boost performace by reducing - // the area to be scaled - use_bitmap_method = true; + xdest += source->DeviceToLogicalXRel(clip.x - src_x); + ydest += source->DeviceToLogicalYRel(clip.y - src_y); + src_x = clip.x; + src_y = clip.y; } } - CalcBoundingBox( xdest, ydest ); - CalcBoundingBox( xdest + width, ydest + height ); - - // scale/translate size and position - wxCoord xx = XLOG2DEV(xdest); - wxCoord yy = YLOG2DEV(ydest); + // destination device coords + const int dst_x = LogicalToDeviceX(xdest); + const int dst_y = LogicalToDeviceY(ydest); + const int dst_w = LogicalToDeviceXRel(width); + const int dst_h = LogicalToDeviceYRel(height); - wxCoord ww = XLOG2DEVREL(width); - wxCoord hh = YLOG2DEVREL(height); - - // compare to current clipping region - if (IsOutsideOfClippingRegion( xx,yy,ww,hh )) - return true; - - int old_logical_func = m_logicalFunction; - SetLogicalFunction( logical_func ); - - if (use_bitmap_method) + GdkRegion* const clipRegion = m_currentClippingRegion.GetRegion(); + // determine dest clip region overlap + int overlap = wxInRegion; + if (clipRegion) { - // scale/translate bitmap size - wxCoord bm_width = selected.GetWidth(); - wxCoord bm_height = selected.GetHeight(); - - // Get clip coords for the bitmap. If we don't - // use wxBitmap::Rescale(), which can clip the - // bitmap, these are the same as the original - // coordinates - wxCoord cx = xx; - wxCoord cy = yy; - wxCoord cw = ww; - wxCoord ch = hh; - - // interpret userscale of src too - double xsc,ysc; - memDC->GetUserScale(&xsc,&ysc); - bm_width = (int) (bm_width / xsc); - bm_height = (int) (bm_height / ysc); - - wxCoord bm_ww = XLOG2DEVREL( bm_width ); - wxCoord bm_hh = YLOG2DEVREL( bm_height ); - - // Scale bitmap if required - wxBitmap use_bitmap = selected; - if ((selected.GetWidth()!= bm_ww) || ( selected.GetHeight()!= bm_hh)) - { - // This indicates that the blitting code below will get - // a clipped bitmap and therefore needs to move the origin - // accordingly - wxRegion tmp( xx,yy,ww,hh ); - if (!m_currentClippingRegion.IsNull()) - tmp.Intersect( m_currentClippingRegion ); - tmp.GetBox(cx,cy,cw,ch); - - // Scale and clipped bitmap - use_bitmap = selected.Rescale(cx-xx,cy-yy,cw,ch,bm_ww,bm_hh); - } - - // apply mask if any - GdkBitmap *mask = (GdkBitmap *) NULL; - if (useMask && use_bitmap.GetMask()) - mask = use_bitmap.GetMask()->GetBitmap(); - - GdkGC* use_gc = is_mono ? m_textGC : m_penGC; - - bool mask_owned = false; + overlap = m_currentClippingRegion.Contains(dst_x, dst_y, dst_w, dst_h); + if (overlap == wxOutRegion) + return true; + } - if (mask != NULL) - { - if (!m_currentClippingRegion.IsNull()) - { - mask = GetClippedMask(mask, bm_ww, bm_hh, cx, cy, - xsrcMask, ysrcMask); - mask_owned = true; - } + const bool isScaled = src_w != dst_w || src_h != dst_h; + double scale_x = 0; + double scale_y = 0; + if (isScaled) + { + // get source to dest scale + double usx, usy, lsx, lsy; + source->GetUserScale(&usx, &usy); + source->GetLogicalScale(&lsx, &lsy); + scale_x = m_scaleX / (usx * lsx); + scale_y = m_scaleY / (usy * lsy); + } - gdk_gc_set_clip_mask(use_gc, mask); - if (mask_owned) - gdk_gc_set_clip_origin(use_gc, cx, cy); - else - gdk_gc_set_clip_origin(use_gc, cx - xsrcMask, cy - ysrcMask); - } + GdkGC* const use_gc = m_penGC; - // Draw XPixmap or XBitmap, depending on what the wxBitmap contains. - if (is_mono) + if (mask) + { + g_object_ref(mask); + int srcMask_x = src_x; + int srcMask_y = src_y; + if (xsrcMask != -1 || ysrcMask != -1) { - DoDrawMonoBitmap(use_bitmap, bm_ww, bm_hh, - xsrc, ysrc, cx, cy, cw, ch); + srcMask_x = source->LogicalToDeviceX(xsrcMask); + srcMask_y = source->LogicalToDeviceY(ysrcMask); } - else + if (isScaled) { - // was: gdk_draw_drawable( m_gdkwindow, m_penGC, use_bitmap.GetPixmap(), xsrc, ysrc, xx, yy, ww, hh ); - gdk_draw_drawable(m_gdkwindow, use_gc, use_bitmap.GetPixmap(), xsrc, ysrc, cx, cy, cw, ch); + mask = ScaleMask(mask, srcMask_x, srcMask_y, + src_w, src_h, dst_w, dst_h, scale_x, scale_y); + srcMask_x = 0; + srcMask_y = 0; } - - // remove mask again if any - if (mask != NULL) + if (overlap == wxPartRegion) { - RemoveClipMask(use_gc); - if (mask_owned) - g_object_unref (mask); + // need a new mask that also masks the clipped area, + // because gc can't have both a mask and a clip region + mask = ClipMask(mask, clipRegion, + srcMask_x, srcMask_y, dst_x, dst_y, dst_w, dst_h); + srcMask_x = 0; + srcMask_y = 0; } + gdk_gc_set_clip_mask(use_gc, mask); + gdk_gc_set_clip_origin(use_gc, dst_x - srcMask_x, dst_y - srcMask_y); } - else // use_bitmap_method + + GdkPixmap* pixmap = NULL; + if (gdk_drawable_get_depth(srcDrawable) == 1) { - if (selected.IsOk() && ((width != ww) || (height != hh))) - { - wxBitmap bitmap = selected.Rescale( xsrc, ysrc, width, height, ww, hh ); - // draw scaled bitmap - gdk_draw_drawable( m_gdkwindow, m_penGC, bitmap.GetPixmap(), 0, 0, xx, yy, -1, -1 ); - } - else - { - // No scaling and not a memory dc with a mask either - GdkWindow* window = NULL; - wxDCImpl *impl = source->GetImpl(); - wxWindowDCImpl *gtk_impl = wxDynamicCast(impl, wxWindowDCImpl); - if (gtk_impl) - window = gtk_impl->GetGDKWindow(); - if ( !window ) - { - SetLogicalFunction( old_logical_func ); - return false; - } + // Convert mono pixmap to color using text fg/bg colors. + // Scaling/drawing is simpler if this is done first. + pixmap = MonoToColor(srcDrawable, src_x, src_y, src_w, src_h); + srcDrawable = pixmap; + src_x = 0; + src_y = 0; + } - // copy including child window contents - gdk_gc_set_subwindow( m_penGC, GDK_INCLUDE_INFERIORS ); - gdk_draw_drawable( m_gdkwindow, m_penGC, - window, - xsrc, ysrc, xx, yy, - width, height ); - gdk_gc_set_subwindow( m_penGC, GDK_CLIP_BY_CHILDREN ); - } + const wxRasterOperationMode logical_func_save = m_logicalFunction; + SetLogicalFunction(logical_func); + if (memDC == NULL) + gdk_gc_set_subwindow(use_gc, GDK_INCLUDE_INFERIORS); + + if (isScaled) + { + GdkPixbuf* pixbuf = Scale(srcDrawable, + src_x, src_y, src_w, src_h, dst_w, dst_h, scale_x, scale_y); + gdk_draw_pixbuf(m_gdkwindow, use_gc, pixbuf, + 0, 0, dst_x, dst_y, dst_w, dst_h, GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref(pixbuf); + } + else + { + gdk_draw_drawable(m_gdkwindow, use_gc, srcDrawable, + src_x, src_y, dst_x, dst_y, dst_w, dst_h); } - SetLogicalFunction( old_logical_func ); + SetLogicalFunction(logical_func_save); + if (memDC == NULL) + gdk_gc_set_subwindow(use_gc, GDK_CLIP_BY_CHILDREN); + if (pixmap) + g_object_unref(pixmap); + if (mask) + { + g_object_unref(mask); + gdk_gc_set_clip_region(use_gc, clipRegion); + } return true; } -void wxWindowDCImpl::DoDrawText( const wxString &text, wxCoord x, wxCoord y ) +void wxWindowDCImpl::DoDrawText(const wxString& text, + wxCoord xLogical, + wxCoord yLogical) { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); @@ -1361,8 +1380,8 @@ void wxWindowDCImpl::DoDrawText( const wxString &text, wxCoord x, wxCoord y ) if (text.empty()) return; - x = XLOG2DEV(x); - y = YLOG2DEV(y); + wxCoord x = XLOG2DEV(xLogical), + y = YLOG2DEV(yLogical); wxCHECK_RET( m_context, wxT("no Pango context") ); wxCHECK_RET( m_layout, wxT("no Pango layout") ); @@ -1370,69 +1389,12 @@ void wxWindowDCImpl::DoDrawText( const wxString &text, wxCoord x, wxCoord y ) gdk_pango_context_set_colormap( m_context, m_cmap ); // not needed in gtk+ >= 2.6 - bool underlined = m_font.IsOk() && m_font.GetUnderlined(); - wxCharBuffer data = wxGTK_CONV(text); if ( !data ) return; - size_t datalen = strlen(data); - - // in Pango >= 1.16 the "underline of leading/trailing spaces" bug - // has been fixed and thus the hack implemented below should never be used - static bool pangoOk = !wx_pango_version_check(1, 16, 0); - - bool needshack = underlined && !pangoOk; - - if (needshack) - { - // a PangoLayout which has leading/trailing spaces with underlined font - // is not correctly drawn by this pango version: Pango won't underline the spaces. - // This can be a problem; e.g. wxHTML rendering of underlined text relies on - // this behaviour. To workaround this problem, we use a special hack here - // suggested by pango maintainer Behdad Esfahbod: we prepend and append two - // empty space characters and give them a dummy colour attribute. - // This will force Pango to underline the leading/trailing spaces, too. - - wxCharBuffer data_tmp(datalen + 6); - // copy the leading U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format - memcpy(data_tmp.data(), "\342\200\214", 3); - // copy the user string - memcpy(data_tmp.data() + 3, data, datalen); - // copy the trailing U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format - memcpy(data_tmp.data() + 3 + datalen, "\342\200\214", 3); - - data = data_tmp; - datalen += 6; - } - - pango_layout_set_text(m_layout, data, datalen); - - if (underlined) - { - PangoAttrList *attrs = pango_attr_list_new(); - PangoAttribute *a = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); - a->start_index = 0; - a->end_index = datalen; - pango_attr_list_insert(attrs, a); - if (needshack) - { - // dummy colour for the leading space - a = pango_attr_foreground_new (0x0057, 0x52A9, 0xD614); - a->start_index = 0; - a->end_index = 1; - pango_attr_list_insert(attrs, a); - - // dummy colour for the trailing space - a = pango_attr_foreground_new (0x0057, 0x52A9, 0xD614); - a->start_index = datalen - 1; - a->end_index = datalen; - pango_attr_list_insert(attrs, a); - } - - pango_layout_set_attributes(m_layout, attrs); - pango_attr_list_unref(attrs); - } + pango_layout_set_text(m_layout, data, data.length()); + const bool setAttrs = m_font.GTKSetPangoAttrs(m_layout); int oldSize = 0; const bool isScaled = fabs(m_scaleY - 1.0) > 0.00001; @@ -1458,9 +1420,9 @@ void wxWindowDCImpl::DoDrawText( const wxString &text, wxCoord x, wxCoord y ) x_rtl -= w; const GdkColor* bg_col = NULL; - if (m_backgroundMode == wxBRUSHSTYLE_SOLID) + if (m_backgroundMode == wxBRUSHSTYLE_SOLID) bg_col = m_textBackgroundColour.GetColor(); - + gdk_draw_layout_with_colors(m_gdkwindow, m_textGC, x_rtl, y, m_layout, NULL, bg_col); if (isScaled) @@ -1471,17 +1433,17 @@ void wxWindowDCImpl::DoDrawText( const wxString &text, wxCoord x, wxCoord y ) // actually apply unscaled font pango_layout_set_font_description( m_layout, m_fontdesc ); } - if (underlined) + if (setAttrs) { // undo underline attributes setting: pango_layout_set_attributes(m_layout, NULL); } - CalcBoundingBox(x + int(w / m_scaleX), y + int(h / m_scaleY)); - CalcBoundingBox(x, y); + CalcBoundingBox(xLogical + int(w / m_scaleX), yLogical + int(h / m_scaleY)); + CalcBoundingBox(xLogical, yLogical); } -// TODO: When GTK2.6 is required, merge DoDrawText and DoDrawRotatedText to +// TODO: When GTK2.6 is required, merge DoDrawText and DoDrawRotatedText to // avoid code duplication void wxWindowDCImpl::DoDrawRotatedText( const wxString &text, wxCoord x, wxCoord y, double angle ) { @@ -1490,197 +1452,80 @@ void wxWindowDCImpl::DoDrawRotatedText( const wxString &text, wxCoord x, wxCoord wxCHECK_RET( IsOk(), wxT("invalid window dc") ); -#if __WXGTK26__ - if (!gtk_check_version(2,6,0)) - { - x = XLOG2DEV(x); - y = YLOG2DEV(y); - - pango_layout_set_text(m_layout, wxGTK_CONV(text), -1); + x = XLOG2DEV(x); + y = YLOG2DEV(y); - if (m_font.GetUnderlined()) - { - PangoAttrList *attrs = pango_attr_list_new(); - PangoAttribute *a = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); - pango_attr_list_insert(attrs, a); - pango_layout_set_attributes(m_layout, attrs); - pango_attr_list_unref(attrs); - } + pango_layout_set_text(m_layout, wxGTK_CONV(text), -1); + const bool setAttrs = m_font.GTKSetPangoAttrs(m_layout); + int oldSize = 0; + const bool isScaled = fabs(m_scaleY - 1.0) > 0.00001; + if (isScaled) + { + //TODO: when Pango >= 1.6 is required, use pango_matrix_scale() + // If there is a user or actually any scale applied to + // the device context, scale the font. - int oldSize = 0; - const bool isScaled = fabs(m_scaleY - 1.0) > 0.00001; - if (isScaled) - { - //TODO: when Pango >= 1.6 is required, use pango_matrix_scale() - // If there is a user or actually any scale applied to - // the device context, scale the font. + // scale font description + oldSize = pango_font_description_get_size(m_fontdesc); + pango_font_description_set_size(m_fontdesc, int(oldSize * m_scaleY)); - // scale font description - oldSize = pango_font_description_get_size(m_fontdesc); - pango_font_description_set_size(m_fontdesc, int(oldSize * m_scaleY)); + // actually apply scaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); + } - // actually apply scaled font - pango_layout_set_font_description( m_layout, m_fontdesc ); - } + int w, h; + pango_layout_get_pixel_size(m_layout, &w, &h); - int w, h; - pango_layout_get_pixel_size(m_layout, &w, &h); - - const GdkColor* bg_col = NULL; - if (m_backgroundMode == wxBRUSHSTYLE_SOLID) - bg_col = m_textBackgroundColour.GetColor(); - - // rotate the text - PangoMatrix matrix = PANGO_MATRIX_INIT; - pango_matrix_rotate (&matrix, angle); - pango_context_set_matrix (m_context, &matrix); - pango_layout_context_changed (m_layout); - - // To be compatible with MSW, the rotation axis must be in the old - // top-left corner. - // Calculate the vertices of the rotated rectangle containing the text, - // relative to the old top-left vertex. - // We could use the matrix for this, but it's simpler with trignonometry. - double rad = DegToRad(angle); - // the rectangle vertices are counted clockwise with the first one - // being at (0, 0) - double x2 = w * cos(rad); - double y2 = -w * sin(rad); // y axis points to the bottom, hence minus - double x4 = h * sin(rad); - double y4 = h * cos(rad); - double x3 = x4 + x2; - double y3 = y4 + y2; - // Then we calculate max and min of the rotated rectangle. - wxCoord maxX = (wxCoord)(dmax(dmax(0, x2), dmax(x3, x4)) + 0.5), - maxY = (wxCoord)(dmax(dmax(0, y2), dmax(y3, y4)) + 0.5), - minX = (wxCoord)(dmin(dmin(0, x2), dmin(x3, x4)) - 0.5), - minY = (wxCoord)(dmin(dmin(0, y2), dmin(y3, y4)) - 0.5); - - gdk_draw_layout_with_colors(m_gdkwindow, m_textGC, x+minX, y+minY, - m_layout, NULL, bg_col); - - if (m_font.GetUnderlined()) - pango_layout_set_attributes(m_layout, NULL); - - // clean up the transformation matrix - pango_context_set_matrix(m_context, NULL); + const GdkColor* bg_col = NULL; + if (m_backgroundMode == wxBRUSHSTYLE_SOLID) + bg_col = m_textBackgroundColour.GetColor(); - if (isScaled) - { - // reset unscaled size - pango_font_description_set_size( m_fontdesc, oldSize ); + // rotate the text + PangoMatrix matrix = PANGO_MATRIX_INIT; + pango_matrix_rotate (&matrix, angle); + pango_context_set_matrix (m_context, &matrix); + pango_layout_context_changed (m_layout); + + // To be compatible with MSW, the rotation axis must be in the old + // top-left corner. + // Calculate the vertices of the rotated rectangle containing the text, + // relative to the old top-left vertex. + // We could use the matrix for this, but it's simpler with trignonometry. + double rad = DegToRad(angle); + // the rectangle vertices are counted clockwise with the first one + // being at (0, 0) + double x2 = w * cos(rad); + double y2 = -w * sin(rad); // y axis points to the bottom, hence minus + double x4 = h * sin(rad); + double y4 = h * cos(rad); + double x3 = x4 + x2; + double y3 = y4 + y2; + // Then we calculate max and min of the rotated rectangle. + wxCoord maxX = (wxCoord)(dmax(dmax(0, x2), dmax(x3, x4)) + 0.5), + maxY = (wxCoord)(dmax(dmax(0, y2), dmax(y3, y4)) + 0.5), + minX = (wxCoord)(dmin(dmin(0, x2), dmin(x3, x4)) - 0.5), + minY = (wxCoord)(dmin(dmin(0, y2), dmin(y3, y4)) - 0.5); + + gdk_draw_layout_with_colors(m_gdkwindow, m_textGC, x+minX, y+minY, + m_layout, NULL, bg_col); + + if (setAttrs) + pango_layout_set_attributes(m_layout, NULL); - // actually apply unscaled font - pango_layout_set_font_description( m_layout, m_fontdesc ); - } + // clean up the transformation matrix + pango_context_set_matrix(m_context, NULL); - CalcBoundingBox(x+minX, y+minY); - CalcBoundingBox(x+maxX, y+maxY); - } - else -#endif //__WXGTK26__ - { -#if wxUSE_IMAGE - if ( wxIsNullDouble(angle) ) + if (isScaled) { - DoDrawText(text, x, y); - return; - } - - wxCoord w; - wxCoord h; - - // TODO: implement later without GdkFont for GTK 2.0 - DoGetTextExtent(text, &w, &h, NULL,NULL, &m_font); - - // draw the string normally - wxBitmap src(w, h); - wxMemoryDC dc; - dc.SelectObject(src); - dc.SetFont(GetFont()); - dc.SetBackground(*wxBLACK_BRUSH); - dc.SetBrush(*wxBLACK_BRUSH); - dc.Clear(); - dc.SetTextForeground( *wxWHITE ); - dc.DrawText(text, 0, 0); - dc.SelectObject(wxNullBitmap); + // reset unscaled size + pango_font_description_set_size( m_fontdesc, oldSize ); - // Calculate the size of the rotated bounding box. - double rad = DegToRad(angle); - double dx = cos(rad), - dy = sin(rad); - - // the rectngle vertices are counted clockwise with the first one being at - // (0, 0) (or, rather, at (x, y)) - double x2 = w*dx, - y2 = -w*dy; // y axis points to the bottom, hence minus - double x4 = h*dy, - y4 = h*dx; - double x3 = x4 + x2, - y3 = y4 + y2; - - // calc max and min - wxCoord maxX = (wxCoord)(dmax(x2, dmax(x3, x4)) + 0.5), - maxY = (wxCoord)(dmax(y2, dmax(y3, y4)) + 0.5), - minX = (wxCoord)(dmin(x2, dmin(x3, x4)) - 0.5), - minY = (wxCoord)(dmin(y2, dmin(y3, y4)) - 0.5); - - - wxImage image = src.ConvertToImage(); - - image.ConvertColourToAlpha( m_textForegroundColour.Red(), - m_textForegroundColour.Green(), - m_textForegroundColour.Blue() ); - image = image.Rotate( rad, wxPoint(0,0) ); - - int i_angle = (int) angle; - i_angle = i_angle % 360; - if (i_angle < 0) - i_angle += 360; - int xoffset = 0; - if ((i_angle >= 90.0) && (i_angle < 270.0)) - xoffset = image.GetWidth(); - int yoffset = 0; - if ((i_angle >= 0.0) && (i_angle < 180.0)) - yoffset = image.GetHeight(); - - if ((i_angle >= 0) && (i_angle < 90)) - yoffset -= (int)( cos(rad)*h ); - if ((i_angle >= 90) && (i_angle < 180)) - xoffset -= (int)( sin(rad)*h ); - if ((i_angle >= 180) && (i_angle < 270)) - yoffset -= (int)( cos(rad)*h ); - if ((i_angle >= 270) && (i_angle < 360)) - xoffset -= (int)( sin(rad)*h ); - - int i_x = x - xoffset; - int i_y = y - yoffset; - - src = image; - DoDrawBitmap( src, i_x, i_y, true ); - - - // it would be better to draw with non underlined font and draw the line - // manually here (it would be more straight...) -#if 0 - if ( m_font.GetUnderlined() ) - { - gdk_draw_line( m_gdkwindow, m_textGC, - XLOG2DEV(x + x4), YLOG2DEV(y + y4 + font->descent), - XLOG2DEV(x + x3), YLOG2DEV(y + y3 + font->descent)); - } -#endif // 0 - - // update the bounding box - CalcBoundingBox(x + minX, y + minY); - CalcBoundingBox(x + maxX, y + maxY); -#else // !wxUSE_IMAGE - wxUnusedVar(text); - wxUnusedVar(x); - wxUnusedVar(y); - wxUnusedVar(angle); -#endif // wxUSE_IMAGE/!wxUSE_IMAGE + // actually apply unscaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); } + + CalcBoundingBox(x+minX, y+minY); + CalcBoundingBox(x+maxX, y+maxY); } void wxWindowDCImpl::DoGetTextExtent(const wxString &string, @@ -1688,100 +1533,27 @@ void wxWindowDCImpl::DoGetTextExtent(const wxString &string, wxCoord *descent, wxCoord *externalLeading, const wxFont *theFont) const { - if ( width ) - *width = 0; - if ( height ) - *height = 0; - if ( descent ) - *descent = 0; - if ( externalLeading ) - *externalLeading = 0; - - if (string.empty()) - return; - - // ensure that theFont is always non-NULL + // ensure we work with a valid font + const wxFont *fontToUse; if ( !theFont || !theFont->IsOk() ) - theFont = &m_font; - - // and use it if it's valid - if ( theFont->IsOk() ) - { - pango_layout_set_font_description - ( - m_layout, - theFont->GetNativeFontInfo()->description - ); - } - - // Set layout's text - const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(string, *theFont); - if ( !dataUTF8 ) - { - // hardly ideal, but what else can we do if conversion failed? - return; - } - - pango_layout_set_text(m_layout, dataUTF8, -1); + fontToUse = &m_font; + else + fontToUse = theFont; - int h; - pango_layout_get_pixel_size(m_layout, width, &h); - if (descent) - { - PangoLayoutIter *iter = pango_layout_get_iter(m_layout); - int baseline = pango_layout_iter_get_baseline(iter); - pango_layout_iter_free(iter); - *descent = h - PANGO_PIXELS(baseline); - } - if (height) - *height = h; + wxCHECK_RET( fontToUse->IsOk(), wxT("invalid font") ); - // Reset old font description - if (theFont->IsOk()) - pango_layout_set_font_description( m_layout, m_fontdesc ); + wxTextMeasure txm(GetOwner(), fontToUse); + txm.GetTextExtent(string, width, height, descent, externalLeading); } bool wxWindowDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const { - const size_t len = text.length(); - widths.Empty(); - widths.Add(0, len); + wxCHECK_MSG( m_font.IsOk(), false, wxT("Invalid font") ); - if (text.empty()) - return true; - - // Set layout's text - const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(text, m_font); - if ( !dataUTF8 ) - { - // hardly ideal, but what else can we do if conversion failed? - wxLogLastError(wxT("DoGetPartialTextExtents")); - return false; - } - - pango_layout_set_text(m_layout, dataUTF8, -1); - - // Calculate the position of each character based on the widths of - // the previous characters - - // Code borrowed from Scintilla's PlatGTK - PangoLayoutIter *iter = pango_layout_get_iter(m_layout); - PangoRectangle pos; - pango_layout_iter_get_cluster_extents(iter, NULL, &pos); - size_t i = 0; - while (pango_layout_iter_next_cluster(iter)) - { - pango_layout_iter_get_cluster_extents(iter, NULL, &pos); - int position = PANGO_PIXELS(pos.x); - widths[i++] = position; - } - while (i < len) - widths[i++] = PANGO_PIXELS(pos.x + pos.width); - pango_layout_iter_free(iter); - - return true; + wxTextMeasure txm(GetOwner(), &m_font); + return txm.GetPartialTextExtents(text, widths, m_scaleX); } @@ -1796,10 +1568,10 @@ wxCoord wxWindowDCImpl::GetCharWidth() const wxCoord wxWindowDCImpl::GetCharHeight() const { PangoFontMetrics *metrics = pango_context_get_metrics (m_context, m_fontdesc, pango_context_get_language(m_context)); - wxCHECK_MSG( metrics, -1, _T("failed to get pango font metrics") ); + wxCHECK_MSG( metrics, -1, wxT("failed to get pango font metrics") ); wxCoord h = PANGO_PIXELS (pango_font_metrics_get_descent (metrics) + - pango_font_metrics_get_ascent (metrics)); + pango_font_metrics_get_ascent (metrics)); pango_font_metrics_unref (metrics); return h; } @@ -1831,7 +1603,7 @@ void wxWindowDCImpl::SetFont( const wxFont &font ) { PangoContext *oldContext = m_context; - m_context = m_window->GtkGetPangoDefaultContext(); + m_context = m_window->GTKGetPangoDefaultContext(); // If we switch back/forth between different contexts // we also have to create a new layout. I think so, @@ -1923,7 +1695,7 @@ void wxWindowDCImpl::SetPen( const wxPen &pen ) case wxPENSTYLE_SOLID: default: lineStyle = GDK_LINE_SOLID; - req_dash = (wxGTKDash*)NULL; + req_dash = NULL; req_nb_dash = 0; break; } @@ -2069,7 +1841,7 @@ void wxWindowDCImpl::SetBackground( const wxBrush &brush ) } } -void wxWindowDCImpl::SetLogicalFunction( int function ) +void wxWindowDCImpl::SetLogicalFunction( wxRasterOperationMode function ) { wxCHECK_RET( IsOk(), wxT("invalid window dc") ); @@ -2100,8 +1872,8 @@ void wxWindowDCImpl::SetLogicalFunction( int function ) case wxSRC_INVERT: mode = GDK_COPY_INVERT; break; case wxNOR: mode = GDK_NOR; break; default: - wxFAIL_MSG( wxT("unsupported logical function") ); - mode = GDK_COPY; + wxFAIL_MSG("unknown mode"); + return; } m_logicalFunction = function; @@ -2175,7 +1947,7 @@ void wxWindowDCImpl::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, w rect.width = XLOG2DEVREL(width); rect.height = YLOG2DEVREL(height); - if (m_window && m_window->m_wxwindow && + if (m_window && m_window->m_wxwindow && (m_window->GetLayoutDirection() == wxLayout_RightToLeft)) { rect.x -= rect.width; @@ -2245,13 +2017,13 @@ void wxWindowDCImpl::DestroyClippingRegion() void wxWindowDCImpl::Destroy() { if (m_penGC) wxFreePoolGC( m_penGC ); - m_penGC = (GdkGC*) NULL; + m_penGC = NULL; if (m_brushGC) wxFreePoolGC( m_brushGC ); - m_brushGC = (GdkGC*) NULL; + m_brushGC = NULL; if (m_textGC) wxFreePoolGC( m_textGC ); - m_textGC = (GdkGC*) NULL; + m_textGC = NULL; if (m_bgGC) wxFreePoolGC( m_bgGC ); - m_bgGC = (GdkGC*) NULL; + m_bgGC = NULL; } void wxWindowDCImpl::SetDeviceOrigin( wxCoord x, wxCoord y ) @@ -2267,7 +2039,7 @@ void wxWindowDCImpl::SetAxisOrientation( bool xLeftRight, bool yBottomUp ) m_signX = (xLeftRight ? 1 : -1); m_signY = (yBottomUp ? -1 : 1); - if (m_window && m_window->m_wxwindow && + if (m_window && m_window->m_wxwindow && (m_window->GetLayoutDirection() == wxLayout_RightToLeft)) m_signX = -m_signX; @@ -2302,7 +2074,6 @@ int wxWindowDCImpl::GetDepth() const return gdk_drawable_get_depth(m_gdkwindow); } - //----------------------------------------------------------------------------- // wxClientDCImpl //----------------------------------------------------------------------------- @@ -2317,20 +2088,20 @@ wxClientDCImpl::wxClientDCImpl( wxDC *owner ) wxClientDCImpl::wxClientDCImpl( wxDC *owner, wxWindow *win ) : wxWindowDCImpl( owner, win ) { - wxCHECK_RET( win, _T("NULL window in wxClientDCImpl::wxClientDC") ); + wxCHECK_RET( win, wxT("NULL window in wxClientDCImpl::wxClientDC") ); #ifdef __WXUNIVERSAL__ wxPoint ptOrigin = win->GetClientAreaOrigin(); SetDeviceOrigin(ptOrigin.x, ptOrigin.y); wxSize size = win->GetClientSize(); DoSetClippingRegion(0, 0, size.x, size.y); -#endif +#endif // __WXUNIVERSAL__ } void wxClientDCImpl::DoGetSize(int *width, int *height) const { - wxCHECK_RET( m_window, _T("GetSize() doesn't work without window") ); + wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") ); m_window->GetClientSize( width, height ); } @@ -2390,7 +2161,7 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *win ) gdk_gc_set_clip_region( m_textGC, region ); gdk_gc_set_clip_region( m_bgGC, region ); } -#endif +#endif } // ----------------------------------------------------------------------------