1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/region.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Modified:    VZ at 05.10.00: use AllocExclusive(), comparison fixed
6 // Copyright:   (c) 1998 Robert Roebling
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #include "wx/region.h"
22 
23 #include <gdk/gdk.h>
24 
25 // ----------------------------------------------------------------------------
26 // wxRegionRefData: private class containing the information about the region
27 // ----------------------------------------------------------------------------
28 
29 class wxRegionRefData : public wxGDIRefData
30 {
31 public:
wxRegionRefData()32     wxRegionRefData()
33     {
34         m_region = NULL;
35     }
36 
wxRegionRefData(const wxRegionRefData & refData)37     wxRegionRefData(const wxRegionRefData& refData)
38         : wxGDIRefData()
39     {
40 #ifdef __WXGTK3__
41         m_region = cairo_region_copy(refData.m_region);
42 #else
43         m_region = gdk_region_copy(refData.m_region);
44 #endif
45     }
46 
~wxRegionRefData()47     virtual ~wxRegionRefData()
48     {
49         if (m_region)
50         {
51 #ifdef __WXGTK3__
52             cairo_region_destroy(m_region);
53 #else
54             gdk_region_destroy( m_region );
55 #endif
56         }
57     }
58 
59 #ifdef __WXGTK3__
60     cairo_region_t* m_region;
61 #else
62     GdkRegion  *m_region;
63 #endif
64 };
65 
66 // ----------------------------------------------------------------------------
67 // macros
68 // ----------------------------------------------------------------------------
69 
70 #define M_REGIONDATA static_cast<wxRegionRefData*>(m_refData)
71 #define M_REGIONDATA_OF(r) static_cast<wxRegionRefData*>(r.m_refData)
72 
IMPLEMENT_DYNAMIC_CLASS(wxRegion,wxGDIObject)73 IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
74 IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator,wxObject)
75 
76 // ----------------------------------------------------------------------------
77 // wxRegion construction
78 // ----------------------------------------------------------------------------
79 
80 void wxRegion::InitRect(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
81 {
82     GdkRectangle rect;
83     rect.x = x;
84     rect.y = y;
85     rect.width = w;
86     rect.height = h;
87 
88     m_refData = new wxRegionRefData();
89 
90 #ifdef __WXGTK3__
91     M_REGIONDATA->m_region = cairo_region_create_rectangle(&rect);
92 #else
93     M_REGIONDATA->m_region = gdk_region_rectangle( &rect );
94 #endif
95 }
96 
97 #ifndef __WXGTK3__
wxRegion(const GdkRegion * region)98 wxRegion::wxRegion(const GdkRegion* region)
99 {
100     m_refData = new wxRegionRefData();
101     M_REGIONDATA->m_region = gdk_region_copy(const_cast<GdkRegion*>(region));
102 }
103 #endif
104 
wxRegion(size_t n,const wxPoint * points,wxPolygonFillMode fillStyle)105 wxRegion::wxRegion( size_t n, const wxPoint *points,
106                     wxPolygonFillMode fillStyle )
107 {
108 #ifdef __WXGTK3__
109     // Make a cairo path from the points, draw it onto an image surface, use
110     // gdk_cairo_region_create_from_surface() to get a cairo region
111 
112     // need at least 3 points to make a useful polygon
113     if (n < 3)
114         return;
115     // get bounding rect
116     int min_x = points[0].x;
117     int max_x = min_x;
118     int min_y = points[0].y;
119     int max_y = min_y;
120     size_t i;
121     for (i = 1; i < n; i++)
122     {
123         const int x = points[i].x;
124         if (min_x > x)
125             min_x = x;
126         else if (max_x < x)
127             max_x = x;
128         const int y = points[i].y;
129         if (min_y > y)
130             min_y = y;
131         else if (max_y < y)
132             max_y = y;
133     }
134     const int w = max_x - min_x + 1;
135     const int h = max_y - min_y + 1;
136     // make surface just big enough to contain polygon (A1 is native format
137     //   for gdk_cairo_region_create_from_surface)
138     cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A1, w, h);
139     memset(cairo_image_surface_get_data(surface), 0, cairo_image_surface_get_stride(surface) * h);
140     cairo_surface_mark_dirty(surface);
141     cairo_surface_set_device_offset(surface, -min_x, -min_y);
142     cairo_t* cr = cairo_create(surface);
143     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
144     if (fillStyle == wxODDEVEN_RULE)
145         cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
146     // make path
147     cairo_move_to(cr, points[0].x, points[0].y);
148     for (i = 1; i < n; i++)
149         cairo_line_to(cr, points[i].x, points[i].y);
150     cairo_close_path(cr);
151     cairo_fill(cr);
152     cairo_destroy(cr);
153     cairo_surface_flush(surface);
154     m_refData = new wxRegionRefData;
155     M_REGIONDATA->m_region = gdk_cairo_region_create_from_surface(surface);
156     cairo_surface_destroy(surface);
157 #else
158     GdkPoint *gdkpoints = new GdkPoint[n];
159     for ( size_t i = 0 ; i < n ; i++ )
160     {
161         gdkpoints[i].x = points[i].x;
162         gdkpoints[i].y = points[i].y;
163     }
164 
165     m_refData = new wxRegionRefData();
166 
167     GdkRegion* reg = gdk_region_polygon
168                      (
169                         gdkpoints,
170                         n,
171                         fillStyle == wxWINDING_RULE ? GDK_WINDING_RULE
172                                                     : GDK_EVEN_ODD_RULE
173                      );
174 
175     M_REGIONDATA->m_region = reg;
176 
177     delete [] gdkpoints;
178 #endif
179 }
180 
~wxRegion()181 wxRegion::~wxRegion()
182 {
183     // m_refData unrefed in ~wxObject
184 }
185 
CreateGDIRefData() const186 wxGDIRefData *wxRegion::CreateGDIRefData() const
187 {
188     // should never be called
189     wxFAIL;
190     return NULL;
191 }
192 
CloneGDIRefData(const wxGDIRefData * data) const193 wxGDIRefData *wxRegion::CloneGDIRefData(const wxGDIRefData *data) const
194 {
195     return new wxRegionRefData(*static_cast<const wxRegionRefData*>(data));
196 }
197 
198 // ----------------------------------------------------------------------------
199 // wxRegion comparison
200 // ----------------------------------------------------------------------------
201 
DoIsEqual(const wxRegion & region) const202 bool wxRegion::DoIsEqual(const wxRegion& region) const
203 {
204 #ifdef __WXGTK3__
205     return cairo_region_equal(
206         M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
207 #else
208     return gdk_region_equal(M_REGIONDATA->m_region,
209                             M_REGIONDATA_OF(region)->m_region) != 0;
210 #endif
211 }
212 
213 // ----------------------------------------------------------------------------
214 // wxRegion operations
215 // ----------------------------------------------------------------------------
216 
Clear()217 void wxRegion::Clear()
218 {
219     UnRef();
220 }
221 
DoUnionWithRect(const wxRect & r)222 bool wxRegion::DoUnionWithRect(const wxRect& r)
223 {
224     // workaround for a strange GTK/X11 bug: taking union with an empty
225     // rectangle results in an empty region which is definitely not what we
226     // want
227     if ( r.IsEmpty() )
228         return true;
229 
230     if ( !m_refData )
231     {
232         InitRect(r.x, r.y, r.width, r.height);
233     }
234     else
235     {
236         AllocExclusive();
237 
238         GdkRectangle rect;
239         rect.x = r.x;
240         rect.y = r.y;
241         rect.width = r.width;
242         rect.height = r.height;
243 
244 #ifdef __WXGTK3__
245         cairo_region_union_rectangle(M_REGIONDATA->m_region, &rect);
246 #else
247         gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
248 #endif
249     }
250 
251     return true;
252 }
253 
DoUnionWithRegion(const wxRegion & region)254 bool wxRegion::DoUnionWithRegion( const wxRegion& region )
255 {
256     if (region.m_refData == NULL)
257         { }
258     else if (m_refData == NULL)
259     {
260         m_refData = new wxRegionRefData(*M_REGIONDATA_OF(region));
261     }
262     else
263     {
264         AllocExclusive();
265 #ifdef __WXGTK3__
266         cairo_region_union(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
267 #else
268         gdk_region_union( M_REGIONDATA->m_region, region.GetRegion() );
269 #endif
270     }
271 
272     return true;
273 }
274 
DoIntersect(const wxRegion & region)275 bool wxRegion::DoIntersect( const wxRegion& region )
276 {
277     if (region.m_refData == NULL || m_refData == NULL)
278         return false;
279 
280     AllocExclusive();
281 
282 #ifdef __WXGTK3__
283     cairo_region_intersect(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
284 #else
285     gdk_region_intersect( M_REGIONDATA->m_region, region.GetRegion() );
286 #endif
287 
288     return true;
289 }
290 
DoSubtract(const wxRegion & region)291 bool wxRegion::DoSubtract( const wxRegion& region )
292 {
293     if (region.m_refData == NULL || m_refData == NULL)
294         return false;
295 
296     AllocExclusive();
297 
298 #ifdef __WXGTK3__
299     cairo_region_subtract(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
300 #else
301     gdk_region_subtract( M_REGIONDATA->m_region, region.GetRegion() );
302 #endif
303 
304     return true;
305 }
306 
DoXor(const wxRegion & region)307 bool wxRegion::DoXor( const wxRegion& region )
308 {
309     if (region.m_refData == NULL)
310         { }
311     else if (m_refData == NULL)
312     {
313         // XOR-ing with an invalid region is the same as XOR-ing with an empty
314         // one, i.e. it is simply a copy.
315         m_refData = new wxRegionRefData(*M_REGIONDATA_OF(region));
316     }
317     else
318     {
319         AllocExclusive();
320 
321 #ifdef __WXGTK3__
322         cairo_region_xor(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
323 #else
324         gdk_region_xor( M_REGIONDATA->m_region, region.GetRegion() );
325 #endif
326     }
327 
328     return true;
329 }
330 
DoOffset(wxCoord x,wxCoord y)331 bool wxRegion::DoOffset( wxCoord x, wxCoord y )
332 {
333     wxCHECK_MSG( m_refData, false, wxS("invalid region") );
334 
335     AllocExclusive();
336 
337 #ifdef __WXGTK3__
338     cairo_region_translate(M_REGIONDATA->m_region, x, y);
339 #else
340     gdk_region_offset( M_REGIONDATA->m_region, x, y );
341 #endif
342 
343     return true;
344 }
345 
346 // ----------------------------------------------------------------------------
347 // wxRegion tests
348 // ----------------------------------------------------------------------------
349 
DoGetBox(wxCoord & x,wxCoord & y,wxCoord & w,wxCoord & h) const350 bool wxRegion::DoGetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const
351 {
352     if ( m_refData )
353     {
354         GdkRectangle rect;
355 #ifdef __WXGTK3__
356         cairo_region_get_extents(M_REGIONDATA->m_region, &rect);
357 #else
358         gdk_region_get_clipbox( M_REGIONDATA->m_region, &rect );
359 #endif
360         x = rect.x;
361         y = rect.y;
362         w = rect.width;
363         h = rect.height;
364 
365         return true;
366     }
367     else
368     {
369         x = 0;
370         y = 0;
371         w = -1;
372         h = -1;
373 
374         return false;
375     }
376 }
377 
IsEmpty() const378 bool wxRegion::IsEmpty() const
379 {
380 #ifdef __WXGTK3__
381     return m_refData == NULL || cairo_region_is_empty(M_REGIONDATA->m_region);
382 #else
383     return m_refData == NULL || gdk_region_empty(M_REGIONDATA->m_region);
384 #endif
385 }
386 
DoContainsPoint(wxCoord x,wxCoord y) const387 wxRegionContain wxRegion::DoContainsPoint( wxCoord x, wxCoord y ) const
388 {
389 #ifdef __WXGTK3__
390     if (m_refData == NULL || !cairo_region_contains_point(M_REGIONDATA->m_region, x, y))
391 #else
392     if (m_refData == NULL || !gdk_region_point_in(M_REGIONDATA->m_region, x, y))
393 #endif
394         return wxOutRegion;
395 
396     return wxInRegion;
397 }
398 
DoContainsRect(const wxRect & r) const399 wxRegionContain wxRegion::DoContainsRect(const wxRect& r) const
400 {
401     if (!m_refData)
402         return wxOutRegion;
403 
404     GdkRectangle rect;
405     rect.x = r.x;
406     rect.y = r.y;
407     rect.width = r.width;
408     rect.height = r.height;
409 #ifdef __WXGTK3__
410     switch (cairo_region_contains_rectangle(M_REGIONDATA->m_region, &rect))
411     {
412         case CAIRO_REGION_OVERLAP_IN:   return wxInRegion;
413         case CAIRO_REGION_OVERLAP_PART: return wxPartRegion;
414         default: break;
415     }
416 #else
417     GdkOverlapType res = gdk_region_rect_in( M_REGIONDATA->m_region, &rect );
418     switch (res)
419     {
420         case GDK_OVERLAP_RECTANGLE_IN:   return wxInRegion;
421         case GDK_OVERLAP_RECTANGLE_OUT:  return wxOutRegion;
422         case GDK_OVERLAP_RECTANGLE_PART: return wxPartRegion;
423     }
424 #endif
425     return wxOutRegion;
426 }
427 
428 #ifdef __WXGTK3__
GetRegion() const429 cairo_region_t* wxRegion::GetRegion() const
430 #else
431 GdkRegion *wxRegion::GetRegion() const
432 #endif
433 {
434     if (!m_refData)
435         return NULL;
436 
437     return M_REGIONDATA->m_region;
438 }
439 
440 // ----------------------------------------------------------------------------
441 // wxRegionIterator
442 // ----------------------------------------------------------------------------
443 
wxRegionIterator()444 wxRegionIterator::wxRegionIterator()
445 {
446     Init();
447     Reset();
448 }
449 
wxRegionIterator(const wxRegion & region)450 wxRegionIterator::wxRegionIterator( const wxRegion& region )
451 {
452     Init();
453     Reset(region);
454 }
455 
Init()456 void wxRegionIterator::Init()
457 {
458     m_rects = NULL;
459     m_numRects = 0;
460 }
461 
~wxRegionIterator()462 wxRegionIterator::~wxRegionIterator()
463 {
464     wxDELETEA(m_rects);
465 }
466 
CreateRects(const wxRegion & region)467 void wxRegionIterator::CreateRects( const wxRegion& region )
468 {
469     wxDELETEA(m_rects);
470     m_numRects = 0;
471 
472 #ifdef __WXGTK3__
473     cairo_region_t* cairoRegion = region.GetRegion();
474     if (cairoRegion == NULL)
475         return;
476     m_numRects = cairo_region_num_rectangles(cairoRegion);
477 
478     if (m_numRects)
479     {
480         m_rects = new wxRect[m_numRects];
481         for (int i = 0; i < m_numRects; i++)
482         {
483             GdkRectangle gr;
484             cairo_region_get_rectangle(cairoRegion, i, &gr);
485             wxRect &wr = m_rects[i];
486             wr.x = gr.x;
487             wr.y = gr.y;
488             wr.width = gr.width;
489             wr.height = gr.height;
490         }
491     }
492 #else
493     GdkRegion *gdkregion = region.GetRegion();
494     if (!gdkregion)
495         return;
496 
497     GdkRectangle* gdkrects;
498     gdk_region_get_rectangles(gdkregion, &gdkrects, &m_numRects);
499 
500     if (m_numRects)
501     {
502         m_rects = new wxRect[m_numRects];
503         for (int i = 0; i < m_numRects; ++i)
504         {
505             GdkRectangle &gr = gdkrects[i];
506             wxRect &wr = m_rects[i];
507             wr.x = gr.x;
508             wr.y = gr.y;
509             wr.width = gr.width;
510             wr.height = gr.height;
511         }
512     }
513     g_free( gdkrects );
514 #endif
515 }
516 
Reset(const wxRegion & region)517 void wxRegionIterator::Reset( const wxRegion& region )
518 {
519     m_region = region;
520     CreateRects(region);
521     Reset();
522 }
523 
HaveRects() const524 bool wxRegionIterator::HaveRects() const
525 {
526     return m_current < m_numRects;
527 }
528 
operator ++()529 wxRegionIterator& wxRegionIterator::operator ++ ()
530 {
531     if (HaveRects())
532         ++m_current;
533 
534     return *this;
535 }
536 
operator ++(int)537 wxRegionIterator wxRegionIterator::operator ++ (int)
538 {
539     wxRegionIterator tmp = *this;
540 
541     if (HaveRects())
542         ++m_current;
543 
544     return tmp;
545 }
546 
GetX() const547 wxCoord wxRegionIterator::GetX() const
548 {
549     wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
550 
551     return m_rects[m_current].x;
552 }
553 
GetY() const554 wxCoord wxRegionIterator::GetY() const
555 {
556     wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
557 
558     return m_rects[m_current].y;
559 }
560 
GetW() const561 wxCoord wxRegionIterator::GetW() const
562 {
563     wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
564 
565     return m_rects[m_current].width;
566 }
567 
GetH() const568 wxCoord wxRegionIterator::GetH() const
569 {
570     wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
571 
572     return m_rects[m_current].height;
573 }
574 
GetRect() const575 wxRect wxRegionIterator::GetRect() const
576 {
577     wxRect r;
578     if( HaveRects() )
579         r = m_rects[m_current];
580 
581     return r;
582 }
583 
operator =(const wxRegionIterator & ri)584 wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& ri)
585 {
586     if (this != &ri)
587     {
588         wxDELETEA(m_rects);
589 
590         m_current = ri.m_current;
591         m_numRects = ri.m_numRects;
592         if ( m_numRects )
593         {
594             m_rects = new wxRect[m_numRects];
595             memcpy(m_rects, ri.m_rects, m_numRects * sizeof m_rects[0]);
596         }
597     }
598     return *this;
599 }
600