9 SDL_Renderer
*renderer
;
12 GeglBuffer
*paint_buffer
;
14 GeglNode
*output_node
;
21 int run_main_loop (MainContext
*context
);
22 void init_main_context (MainContext
*context
);
23 void destroy_main_context (MainContext
*context
);
24 void draw_circle (GeglBuffer
*buffer
, int x
, int y
, float r
);
26 const Babl
*sdl_format
= NULL
;
28 int main(int argc
, char *argv
[])
31 MainContext context
= {0, };
33 if((retval
= SDL_Init (SDL_INIT_VIDEO
| SDL_INIT_TIMER
)) > 0)
35 printf("SDL failed with return value %d\n", retval
);
39 if (SDL_CreateWindowAndRenderer (640, 480, 0,
40 &context
.window
, &context
.renderer
))
42 printf("SDL failed to create a window\n");
47 context
.surface
= SDL_CreateRGBSurfaceWithFormat (0,
48 640, 480, 24, SDL_PIXELFORMAT_RGB24
);
51 fprintf (stderr
, "Unable to create surface: %s\n",
56 context
.texture
= SDL_CreateTextureFromSurface (context
.renderer
, context
.surface
);
59 fprintf (stderr
, "Unable to create texture: %s\n",
64 gegl_init (NULL
, NULL
);
66 /* We don't have a native format that matches SDL, but we can use
67 * babl to generate the needed conversions automatically.
70 sdl_format
= babl_format_new (babl_model ("R'G'B'"),
72 babl_component ("B'"),
73 babl_component ("G'"),
74 babl_component ("R'"),
77 init_main_context (&context
);
79 run_main_loop (&context
);
81 destroy_main_context (&context
);
90 * @context: The context.
92 * Initialize the main context object that will hold our graph.
95 init_main_context (MainContext
*context
)
97 GeglNode
*ptn
= gegl_node_new ();
98 GeglNode
*background_node
, *over
, *buffer_src
;
100 GeglColor
*color1
= gegl_color_new ("rgb(0.4, 0.4, 0.4)");
101 GeglColor
*color2
= gegl_color_new ("rgb(0.6, 0.6, 0.6)");
102 GeglBuffer
*paint_buffer
= gegl_buffer_new (GEGL_RECTANGLE (0, 0, 0, 0), babl_format ("RGBA float"));
104 g_object_set (ptn
, "cache-policy", GEGL_CACHE_POLICY_NEVER
, NULL
);
106 /* Our graph represents a single drawing layer over a fixed background */
107 background_node
= gegl_node_new_child (ptn
,
108 "operation", "gegl:checkerboard",
112 over
= gegl_node_new_child (ptn
,
113 "operation", "gegl:over",
115 buffer_src
= gegl_node_new_child (ptn
,
116 "operation", "gegl:buffer-source",
117 "buffer", paint_buffer
,
120 /* The "aux" node of "gegl:over" is the image on top */
121 gegl_node_connect (background_node
, "output", over
, "input");
122 gegl_node_connect (buffer_src
, "output", over
, "aux");
124 g_object_unref (color1
);
125 g_object_unref (color2
);
127 context
->graph
= ptn
;
128 context
->output_node
= over
;
129 context
->paint_buffer
= paint_buffer
;
133 /* destroy_main_context:
134 * @context: The context.
136 * Clean up the main context object.
139 destroy_main_context (MainContext
*context
)
141 g_object_unref (context
->graph
);
142 g_object_unref (context
->paint_buffer
);
144 SDL_FreeSurface (context
->surface
);
145 SDL_DestroyTexture (context
->texture
);
146 SDL_DestroyRenderer (context
->renderer
);
148 context
->graph
= NULL
;
149 context
->output_node
= NULL
;
150 context
->paint_buffer
= NULL
;
153 /* invalidate_signal:
154 * @node: The node that was invalidated.
155 * @rect: The area that changed.
156 * @SDL_Surface: Our user_data param, the window we will write to.
158 * Whenever the output of the graph changes this function will copy the new data
162 invalidate_signal (GeglNode
*node
, GeglRectangle
*rect
, MainContext
*context
)
164 SDL_Surface
*surface
= context
->surface
;
165 GeglRectangle output_rect
= {0, 0, surface
->w
, surface
->h
};
166 guchar
*blit_origin
= NULL
;
168 gegl_rectangle_intersect (&output_rect
, &output_rect
, rect
);
170 blit_origin
= (guchar
*)surface
->pixels
+ (output_rect
.x
* surface
->format
->BytesPerPixel
+ output_rect
.y
* surface
->pitch
);
172 gegl_node_blit (node
,
180 SDL_UpdateTexture (context
->texture
, NULL
, surface
->pixels
, surface
->pitch
);
182 SDL_RenderClear (context
->renderer
);
183 SDL_RenderCopy (context
->renderer
, context
->texture
, NULL
, NULL
);
184 SDL_RenderPresent (context
->renderer
);
188 * @buffer: The buffer to draw on.
189 * @x: Center of the circle.
190 * @y: Center of the circle.
191 * @r: Radius of the circle.
193 * Draw a black circle with soft edges on @buffer at @x, @y.
196 draw_circle (GeglBuffer
*buffer
, int x
, int y
, float r
)
199 GeglBufferIterator
*iter
;
201 float color_pixel
[4] = {0.0f
, 0.0f
, 0.0f
, 1.0f
};
206 roi
.width
= 2.0 * r
+ 1.5;
207 roi
.height
= 2.0 * r
+ 1.5;
214 iter
= gegl_buffer_iterator_new (buffer
, &roi
, 0,
215 babl_format ("RGBA float"),
216 GEGL_ACCESS_READWRITE
,
219 while (gegl_buffer_iterator_next (iter
))
221 float *pixel
= (float *)iter
->items
[0].data
;
223 GeglRectangle roi
= iter
->items
[0].roi
;
226 for (iy
= roi
.y
; iy
< roi
.y
+ roi
.height
; iy
++)
228 for (ix
= roi
.x
; ix
< roi
.x
+ roi
.width
; ix
++)
230 float d_sqr
= powf(x
-ix
, 2.0f
) + powf(y
-iy
, 2.0f
);
233 float dist
= sqrt(d_sqr
);
236 pixel
[0] = color_pixel
[0];
237 pixel
[1] = color_pixel
[1];
238 pixel
[2] = color_pixel
[2];
240 if (pixel
[3] < color_pixel
[3])
241 pixel
[3] = color_pixel
[3];
245 float alpha
= (r
- dist
) * (color_pixel
[3]);
246 float dst_alpha
= pixel
[3];
248 float a
= alpha
+ dst_alpha
* (1.0 - alpha
);
249 float a_term
= dst_alpha
* (1.0 - alpha
);
250 float r
= color_pixel
[0] * alpha
+ pixel
[0] * a_term
;
251 float g
= color_pixel
[1] * alpha
+ pixel
[1] * a_term
;
252 float b
= color_pixel
[2] * alpha
+ pixel
[2] * a_term
;
258 if (pixel
[3] < alpha
)
270 run_main_loop (MainContext
*context
)
272 SDL_Surface
*surface
= context
->surface
;
273 GeglRectangle initial_rect
= {0, 0, surface
->w
, surface
->h
};
275 gegl_buffer_set_extent (context
->paint_buffer
, GEGL_RECTANGLE (0, 0, surface
->w
, surface
->h
));
277 /* initial buffers update */
278 invalidate_signal (context
->output_node
, &initial_rect
, context
);
280 /* This signal will trigger to update the surface when the output node's
281 * contents change. Updating instantly is very inefficient but is good
282 * enough for this example.
284 g_signal_connect (context
->output_node
, "invalidated",
285 G_CALLBACK (invalidate_signal
), context
);
291 SDL_WaitEvent (&event
);
298 case SDL_MOUSEBUTTONDOWN
:
299 if (event
.button
.button
== SDL_BUTTON_LEFT
)
301 context
->in_stroke
= TRUE
;
302 context
->last_x
= event
.button
.x
;
303 context
->last_y
= event
.button
.y
;
304 draw_circle (context
->paint_buffer
, event
.button
.x
, event
.button
.y
, 20);
307 case SDL_MOUSEMOTION
:
308 if (context
->in_stroke
&&
309 (context
->last_x
!= event
.motion
.x
|| context
->last_y
!= event
.motion
.y
))
311 context
->last_x
= event
.motion
.x
;
312 context
->last_y
= event
.motion
.y
;
313 draw_circle (context
->paint_buffer
, event
.motion
.x
, event
.motion
.y
, 20);
316 case SDL_MOUSEBUTTONUP
:
317 if (event
.button
.button
== SDL_BUTTON_LEFT
)
319 context
->in_stroke
= FALSE
;