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