@@ -44,18 +44,17 @@ macro_rules! clone {
44
44
// | 2 | 3 |
45
45
// +---+---+
46
46
//
47
- // Each worker thread waits for a buffer (Box<[u8]>) to render into, wraps
48
- // it into ImageSurface, sleeps for a while, does the drawing, then sends the
49
- // underlying buffer back and waits for the next one.
47
+ // Each worker thread waits for an image (cairo::ImageSurface) to render into, sleeps for a while,
48
+ // does the drawing, then sends the image back and waits for the next one.
50
49
//
51
50
// The GUI thread holds an ImageSurface per image part at all times, these
52
51
// surfaces are painted on a DrawingArea in its 'draw' signal handler. This
53
- // thread periodically checks if any worker has sent a freshly rendered buffer .
52
+ // thread periodically checks if any worker has sent a freshly rendered image .
54
53
// Upon receipt it's wrapped in ImageSurface and swapped with the previously
55
- // held surface, whose buffer is sent to the worker thread again. Then the
54
+ // held surface, whose image is sent to the worker thread again. Then the
56
55
// appropriate part of the DrawingArea is invalidated prompting a redraw.
57
56
//
58
- // The two buffers per thread are allocated and initialized once and sent back
57
+ // The two images per thread are allocated and initialized once and sent back
59
58
// and forth repeatedly.
60
59
61
60
fn build_ui ( application : & gtk:: Application ) {
@@ -73,7 +72,7 @@ fn build_ui(application: >k::Application) {
73
72
let height = 200 ;
74
73
area. set_size_request ( width * 2 , height * 2 ) ;
75
74
76
- let ( initial_buf , stride ) = draw_initial ( format, width, height) ;
75
+ let initial_image = draw_initial ( format, width, height) ;
77
76
let ( ready_tx, ready_rx) = mpsc:: channel ( ) ;
78
77
79
78
let mut images = Vec :: new ( ) ;
@@ -82,15 +81,13 @@ fn build_ui(application: >k::Application) {
82
81
83
82
for thread_num in 0 ..4 {
84
83
let ( tx, rx) = mpsc:: channel ( ) ;
85
- // allocate two buffers and copy the initial pattern into them
86
- let buf0 = initial_buf. clone ( ) ;
87
- let buf1 = initial_buf. clone ( ) ;
88
- // wrap the first one in a surface and set it up to be sent to the
89
- // worker when the surface is destroyed
90
- images. push ( ImageSurface :: create_for_data ( buf0, clone ! ( tx => move |b| { let _ = tx. send( b) ; } ) ,
91
- format, width, height, stride) . expect ( "Can't create surface" ) ) ;
84
+ // copy the initial image in two images for the workers
85
+ let image0 = duplicate_image ( & initial_image) ;
86
+ let image1 = duplicate_image ( & initial_image) ;
87
+ // store the first one in our vec
88
+ images. push ( image0) ;
92
89
// send the second one immediately
93
- let _ = tx. send ( buf1 ) ;
90
+ let _ = tx. send ( image1 ) ;
94
91
origins. push ( match thread_num {
95
92
0 => ( 0 , 0 ) ,
96
93
1 => ( width, 0 ) ,
@@ -106,16 +103,13 @@ fn build_ui(application: >k::Application) {
106
103
// spawn the worker thread
107
104
thread:: spawn ( clone ! ( ready_tx => move || {
108
105
let mut n = 0 ;
109
- for buf in rx. iter( ) {
106
+ for image in rx. iter( ) {
110
107
n = ( n + 1 ) % 0x10000 ;
111
- // create the surface and send the buffer back when it's destroyed
112
- let image = ImageSurface :: create_for_data( buf,
113
- clone!( ready_tx => move |b| { let _ = ready_tx. send( ( thread_num, b) ) ; } ) ,
114
- format, width, height, stride) . expect( "Can't create surface" ) ;
115
108
let cr = Context :: new( & image) ;
116
109
// draw an arc with a weirdly calculated radius
117
110
draw_slow( & cr, delay, x, y, 1.2_f64 . powi( ( ( n as i32 ) << thread_num) % 32 ) ) ;
118
111
image. flush( ) ;
112
+ let _ = ready_tx. send( ( thread_num, image) ) ;
119
113
}
120
114
} ) ) ;
121
115
}
@@ -128,21 +122,16 @@ fn build_ui(application: >k::Application) {
128
122
let ( ref images, ref origins, _) = * cell. borrow( ) ;
129
123
for ( image, origin) in images. iter( ) . zip( origins. iter( ) ) {
130
124
draw_image_if_dirty( & cr, image, * origin, ( width, height) ) ;
131
- // if we don't reset the source, the context may hold on to
132
- // the surface indefinitely, the buffer will be stuck there
133
- // and the worker thread will starve
134
- cr. set_source_rgb( 0. , 0. , 0. ) ;
135
125
}
136
126
Inhibit ( false )
137
127
} ) ) ;
138
128
139
129
gtk:: timeout_add ( 100 , move || {
140
- while let Ok ( ( thread_num, buf ) ) = ready_rx. try_recv ( ) {
130
+ while let Ok ( ( thread_num, mut image ) ) = ready_rx. try_recv ( ) {
141
131
let & mut ( ref mut images, ref origins, ref workers) = & mut * cell. borrow_mut ( ) ;
142
- let tx = workers[ thread_num] . clone ( ) ;
143
- let mut image = ImageSurface :: create_for_data ( buf, move |b| { let _ = tx. send ( b) ; } ,
144
- format, width, height, stride) . expect ( "Can't create surface" ) ;
132
+ let tx = & workers[ thread_num] ;
145
133
mem:: swap ( & mut images[ thread_num] , & mut image) ;
134
+ let _ = tx. send ( image) ;
146
135
area. queue_draw_area ( origins[ thread_num] . 0 , origins[ thread_num] . 1 , width, height) ;
147
136
}
148
137
Continue ( true )
@@ -164,17 +153,16 @@ fn main() {
164
153
application. run ( & args ( ) . collect :: < Vec < _ > > ( ) ) ;
165
154
}
166
155
167
- fn draw_initial ( format : Format , width : i32 , height : i32 ) -> ( Box < [ u8 ] > , i32 ) {
168
- let mut image = ImageSurface :: create ( format, width, height) . expect ( "Can't create surface" ) ;
156
+ fn draw_initial ( format : Format , width : i32 , height : i32 ) -> ImageSurface {
157
+ let image = ImageSurface :: create ( format, width, height) . expect ( "Can't create surface" ) ;
169
158
{
170
159
let cr = Context :: new ( & image) ;
171
160
cr. set_source_rgb ( 0. , 1. , 0. ) ;
172
161
cr. paint ( ) ;
173
162
// Destroying the context releases its reference to `image`.
174
163
}
175
- // We have a unique reference to `image` again.
176
- let buf = image. get_data ( ) . expect ( "Couldn't get data from image" ) . to_vec ( ) ;
177
- ( buf. into_boxed_slice ( ) , image. get_stride ( ) )
164
+ // We have a unique reference to `image` again and return it
165
+ image
178
166
}
179
167
180
168
fn draw_slow ( cr : & Context , delay : Duration , x : f64 , y : f64 , radius : f64 ) {
@@ -199,3 +187,14 @@ fn draw_image_if_dirty(cr: &Context, image: &ImageSurface, origin: (i32, i32),
199
187
cr. set_source_surface ( image, x, y) ;
200
188
cr. paint ( ) ;
201
189
}
190
+
191
+ fn duplicate_image ( image : & ImageSurface ) -> ImageSurface {
192
+ let image_dup = ImageSurface :: create ( image. get_format ( ) , image. get_width ( ) , image. get_height ( ) ) . expect ( "Can't create surface" ) ;
193
+ {
194
+ let cr = Context :: new ( & image_dup) ;
195
+ cr. set_source_surface ( image, 0.0 , 0.0 ) ;
196
+ cr. paint ( ) ;
197
+ }
198
+
199
+ image_dup
200
+ }
0 commit comments