Update Portuguese translation
[gegl.git] / examples / sdl-draw.c
blobe62db276462f2e6bfbb803d5e42ac093883eeb4c
2 #include <gegl.h>
3 #include <SDL.h>
4 #include <math.h>
6 typedef struct
8 SDL_Window *window;
9 SDL_Renderer *renderer;
10 SDL_Surface *surface;
11 SDL_Texture *texture;
12 GeglBuffer *paint_buffer;
13 GeglNode *graph;
14 GeglNode *output_node;
16 gboolean in_stroke;
17 int last_x;
18 int last_y;
19 } MainContext;
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[])
30 int retval;
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);
36 return retval;
39 if (SDL_CreateWindowAndRenderer (640, 480, 0,
40 &context.window, &context.renderer))
42 printf("SDL failed to create a window\n");
43 SDL_Quit();
44 return 1;
47 context.surface = SDL_CreateRGBSurfaceWithFormat (0,
48 640, 480, 24, SDL_PIXELFORMAT_RGB24);
49 if (!context.surface)
51 fprintf (stderr, "Unable to create surface: %s\n",
52 SDL_GetError ());
53 return 1;
56 context.texture = SDL_CreateTextureFromSurface (context.renderer, context.surface);
57 if (!context.surface)
59 fprintf (stderr, "Unable to create texture: %s\n",
60 SDL_GetError ());
61 return 1;
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'"),
71 babl_type ("u8"),
72 babl_component ("B'"),
73 babl_component ("G'"),
74 babl_component ("R'"),
75 NULL);
77 init_main_context (&context);
79 run_main_loop (&context);
81 destroy_main_context (&context);
83 gegl_exit ();
84 SDL_Quit ();
86 return 0;
89 /* init_main_context:
90 * @context: The context.
92 * Initialize the main context object that will hold our graph.
94 void
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",
109 "color1", color1,
110 "color2", color2,
111 NULL);
112 over = gegl_node_new_child (ptn,
113 "operation", "gegl:over",
114 NULL);
115 buffer_src = gegl_node_new_child (ptn,
116 "operation", "gegl:buffer-source",
117 "buffer", paint_buffer,
118 NULL);
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.
138 void
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
159 * to the sdl window.
161 static void
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,
173 1.0,
174 &output_rect,
175 sdl_format,
176 blit_origin,
177 surface->pitch,
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);
187 /* draw_circle:
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.
195 void
196 draw_circle (GeglBuffer *buffer, int x, int y, float r)
198 GeglRectangle roi;
199 GeglBufferIterator *iter;
201 float color_pixel[4] = {0.0f, 0.0f, 0.0f, 1.0f};
202 float r_sqr = r*r;
204 roi.x = x - r - 0.5;
205 roi.y = y - r - 0.5;
206 roi.width = 2.0 * r + 1.5;
207 roi.height = 2.0 * r + 1.5;
209 if (roi.width < 1)
210 return;
211 if (roi.height < 1)
212 return;
214 iter = gegl_buffer_iterator_new (buffer, &roi, 0,
215 babl_format ("RGBA float"),
216 GEGL_ACCESS_READWRITE,
217 GEGL_ABYSS_NONE, 1);
219 while (gegl_buffer_iterator_next (iter))
221 float *pixel = (float *)iter->items[0].data;
222 int iy, ix;
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);
231 if (d_sqr < r_sqr)
233 float dist = sqrt(d_sqr);
234 if (dist < r - 1)
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];
243 else
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;
254 pixel[0] = r / a;
255 pixel[1] = g / a;
256 pixel[2] = b / a;
258 if (pixel[3] < alpha)
259 pixel[3] = alpha;
263 pixel += 4;
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);
287 while(1)
289 SDL_Event event;
291 SDL_WaitEvent (&event);
293 switch (event.type)
295 case SDL_QUIT:
296 return 0;
297 break;
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);
306 break;
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);
315 break;
316 case SDL_MOUSEBUTTONUP:
317 if (event.button.button == SDL_BUTTON_LEFT)
319 context->in_stroke = FALSE;
321 break;