GDS-Render v1.2.1
gds-output-renderer.c
Go to the documentation of this file.
1/*
2 * GDSII-Converter
3 * Copyright (C) 2019 Mario Hüttel <mario.huettel@gmx.net>
4 *
5 * This file is part of GDSII-Converter.
6 *
7 * GDSII-Converter is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * GDSII-Converter is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GDSII-Converter. If not, see <http://www.gnu.org/licenses/>.
18 */
19
31#include <glib/gi18n.h>
32
34 struct gds_cell *cell;
35 double scale;
36};
37
41};
42
43typedef struct {
45 LayerSettings *layer_settings;
48 GTask *task;
49 GMainContext *main_context;
50 struct renderer_params async_params;
51 struct idle_function_params idle_function_parameters;
52 gpointer padding[11];
54
55enum {
59};
60
61G_DEFINE_TYPE_WITH_PRIVATE(GdsOutputRenderer, gds_output_renderer, G_TYPE_OBJECT)
62
65
66static int gds_output_renderer_render_dummy(GdsOutputRenderer *renderer,
67 struct gds_cell *cell,
68 double scale)
69{
70 (void)renderer;
71 (void)cell;
72 (void)scale;
73
74 g_warning(_("Output renderer does not define a render_output function!"));
75 return 0;
76}
77
78static void gds_output_renderer_dispose(GObject *self_obj)
79{
80 GdsOutputRenderer *renderer = GDS_RENDER_OUTPUT_RENDERER(self_obj);
82
83 priv = gds_output_renderer_get_instance_private(renderer);
84
85 if (priv->mutex_init_status) {
86 /* Try locking the mutex, to test if it's free */
87 g_mutex_lock(&priv->settings_lock);
88 g_mutex_unlock(&priv->settings_lock);
89 g_mutex_clear(&priv->settings_lock);
90
91 g_mutex_lock(&priv->idle_function_parameters.message_lock);
92 g_mutex_unlock(&priv->idle_function_parameters.message_lock);
93 g_mutex_clear(&priv->idle_function_parameters.message_lock);
94
95 priv->mutex_init_status = FALSE;
96 }
97
98 g_clear_object(&priv->task);
99
100 if (priv->output_file)
101 g_free(priv->output_file);
102
106 }
107
108 g_clear_object(&priv->layer_settings);
109
110 /* Chain up to parent class */
111 G_OBJECT_CLASS(gds_output_renderer_parent_class)->dispose(self_obj);
112}
113
114static void gds_output_renderer_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec)
115{
116 GdsOutputRenderer *self = GDS_RENDER_OUTPUT_RENDERER(obj);
118
119 priv = gds_output_renderer_get_instance_private(self);
120
121 switch (property_id) {
122 case PROP_OUTPUT_FILE:
123 g_value_set_string(value, priv->output_file);
124 break;
126 g_value_set_object(value, priv->layer_settings);
127 break;
128 default:
129 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
130 break;
131 }
132}
133
134static void gds_output_renderer_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec)
135{
136 GdsOutputRenderer *self = GDS_RENDER_OUTPUT_RENDERER(obj);
138
139 priv = gds_output_renderer_get_instance_private(self);
140
141 switch (property_id) {
142 case PROP_OUTPUT_FILE:
143 g_mutex_lock(&priv->settings_lock);
144 if (priv->output_file)
145 g_free(priv->output_file);
146 priv->output_file = g_strdup(g_value_get_string(value));
147 g_mutex_unlock(&priv->settings_lock);
148 break;
150 g_mutex_lock(&priv->settings_lock);
151 g_clear_object(&priv->layer_settings);
152 priv->layer_settings = g_value_get_object(value);
153 g_object_ref(priv->layer_settings);
154 g_mutex_unlock(&priv->settings_lock);
155 break;
156 default:
157 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
158 break;
159 }
160}
161
162static GParamSpec *gds_output_renderer_properties[N_PROPERTIES] = {NULL};
163
164static void gds_output_renderer_class_init(GdsOutputRendererClass *klass)
165{
166 GObjectClass *oclass = G_OBJECT_CLASS(klass);
167 GType progress_changed_param_types[1] = {G_TYPE_POINTER};
168
169 klass->render_output = gds_output_renderer_render_dummy;
170
171 oclass->dispose = gds_output_renderer_dispose;
172 oclass->set_property = gds_output_renderer_set_property;
173 oclass->get_property = gds_output_renderer_get_property;
174
175 /* Setup properties */
177 g_param_spec_string(N_("output-file"), N_("output file"), N_("Output file for renderer"),
178 NULL, G_PARAM_READWRITE);
180 g_param_spec_object(N_("layer-settings"), N_("Layer Settings object"),
181 N_("Object containing the layer rendering information"),
182 GDS_RENDER_TYPE_LAYER_SETTINGS, G_PARAM_READWRITE);
183 g_object_class_install_properties(oclass, N_PROPERTIES, gds_output_renderer_properties);
184
185 /* Setup output signals */
187 g_signal_newv(N_("async-finished"), GDS_RENDER_TYPE_OUTPUT_RENDERER,
188 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
189 NULL,
190 NULL,
191 NULL,
192 NULL,
193 G_TYPE_NONE,
194 0,
195 NULL);
197 g_signal_newv(N_("progress-changed"), GDS_RENDER_TYPE_OUTPUT_RENDERER,
198 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
199 NULL,
200 NULL,
201 NULL,
202 NULL,
203 G_TYPE_NONE,
204 1,
205 progress_changed_param_types);
206}
207
208void gds_output_renderer_init(GdsOutputRenderer *self)
209{
211
212 priv = gds_output_renderer_get_instance_private(self);
213
214 priv->layer_settings = NULL;
215 priv->output_file = NULL;
216 priv->task = NULL;
217 priv->mutex_init_status = TRUE;
218 priv->main_context = NULL;
220 g_mutex_init(&priv->settings_lock);
221 g_mutex_init(&priv->idle_function_parameters.message_lock);
222}
223
224GdsOutputRenderer *gds_output_renderer_new()
225{
226 return GDS_RENDER_OUTPUT_RENDERER(g_object_new(GDS_RENDER_TYPE_OUTPUT_RENDERER, NULL));
227}
228
229GdsOutputRenderer *gds_output_renderer_new_with_props(const char *output_file, LayerSettings *layer_settings)
230{
231 return GDS_RENDER_OUTPUT_RENDERER(g_object_new(GDS_RENDER_TYPE_OUTPUT_RENDERER,
232 N_("layer-settings"), layer_settings,
233 N_("output-file"), output_file,
234 NULL));
235}
236
237void gds_output_renderer_set_output_file(GdsOutputRenderer *renderer, const gchar *file_name)
238{
239 g_return_if_fail(GDS_RENDER_IS_OUTPUT_RENDERER(renderer));
240
241 /* Check if the filename is actually filled */
242 if (!file_name || !file_name[0])
243 return;
244 g_object_set(renderer, N_("output-file"), file_name, NULL);
245}
246
247const char *gds_output_renderer_get_output_file(GdsOutputRenderer *renderer)
248{
249 const char *file = NULL;
250
251 g_object_get(renderer, N_("output-file"), &file, NULL);
252 return file;
253}
254
255LayerSettings *gds_output_renderer_get_and_ref_layer_settings(GdsOutputRenderer *renderer)
256{
257 LayerSettings *ret = NULL;
259
260 priv = gds_output_renderer_get_instance_private(renderer);
261
262 /* Acquire settings lock */
263 g_mutex_lock(&priv->settings_lock);
264
265 /* This function seems to already reference the LayerSettings object */
266 g_object_get(renderer, N_("layer-settings"), &ret, NULL);
267
268 /* It is now safe to clear the lock */
269 g_mutex_unlock(&priv->settings_lock);
270
271 return ret;
272}
273
274void gds_output_renderer_set_layer_settings(GdsOutputRenderer *renderer, LayerSettings *settings)
275{
276 g_return_if_fail(GDS_RENDER_IS_LAYER_SETTINGS(settings));
277
278 g_object_set(renderer, N_("layer-settings"), settings, NULL);
279}
280
281int gds_output_renderer_render_output(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale)
282{
283 int ret;
284 GdsOutputRendererClass *klass;
285 GdsOutputRendererPrivate *priv = gds_output_renderer_get_instance_private(renderer);
286
287 if (GDS_RENDER_IS_OUTPUT_RENDERER(renderer) == FALSE) {
288 g_error(_("Output Renderer not valid."));
290 }
291
292 if (!priv->output_file || !priv->output_file[0]) {
293 g_error(_("No/invalid output file set."));
295 }
296
297 if (!priv->layer_settings) {
298 g_error(_("No layer specification supplied."));
300 }
301
302 if (!cell) {
303 g_error(_("Output renderer called without cell to render."));
305 }
306
307 klass = GDS_RENDER_OUTPUT_RENDERER_GET_CLASS(renderer);
308 if (klass->render_output == NULL) {
309 g_critical(_("Output Renderer: Rendering function broken. This is a bug."));
311 }
312
313 ret = klass->render_output(renderer, cell, scale);
314
315 return ret;
316}
317
318static void gds_output_renderer_async_wrapper(GTask *task,
319 gpointer source_object,
320 gpointer task_data,
321 GCancellable *cancellable)
322{
323 GdsOutputRenderer *renderer;
325 int ret;
326 (void)task_data;
327 (void)cancellable;
328
329 renderer = GDS_RENDER_OUTPUT_RENDERER(source_object);
330 priv = gds_output_renderer_get_instance_private(renderer);
331 if (!priv) {
332 ret = -1000;
333 goto ret_from_task;
334 }
335 if (!priv->mutex_init_status) {
336 ret = -1001;
337 goto ret_from_task;
338 }
339
341
342ret_from_task:
343 g_task_return_int(task, ret);
344}
345
346static void gds_output_renderer_async_finished(GObject *src_obj, GAsyncResult *res, gpointer user_data)
347{
349 (void)user_data;
350 (void)res; /* Will hopefully be destroyed later */
351
352 priv = gds_output_renderer_get_instance_private(GDS_RENDER_OUTPUT_RENDERER(src_obj));
353
354 priv->main_context = NULL;
355
356 g_signal_emit(src_obj, gds_output_renderer_signals[ASYNC_FINISHED], 0);
357 g_clear_object(&priv->task);
358
359 /* Clear reference set in gds_output_renderer_render_output_async() */
360 g_object_unref(src_obj);
361}
362
363int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale)
364{
366 int ret = -1;
367
368 priv = gds_output_renderer_get_instance_private(renderer);
369 if (priv->task) {
370 g_warning(_("Renderer already started asynchronously"));
371 return -2000;
372 }
373
374 priv->task = g_task_new(renderer, NULL, gds_output_renderer_async_finished, NULL);
375
376 /* This function is not available on current debian distros. */
377 /* g_task_set_name(priv->task, "Rendering Thread"); */
378
379 g_mutex_lock(&priv->settings_lock);
380 priv->async_params.cell = cell;
381 priv->async_params.scale = scale;
382 priv->main_context = g_main_context_default();
383 g_mutex_unlock(&priv->settings_lock);
384
385 /* Self reference. This could end up being nasty... */
386 g_object_ref(renderer);
387
388 /* Do the magic */
389 g_task_run_in_thread(priv->task, gds_output_renderer_async_wrapper);
390
391 return ret;
392}
393
394static gboolean idle_event_processor_callback(gpointer user_data)
395{
396 GdsOutputRenderer *renderer;
398 char *status_message;
399
400 /* If the rendering is finished before the mainloop gets to this point
401 * the renderer is already disposed. Catch this!
402 */
403 if (!GDS_RENDER_IS_OUTPUT_RENDERER(user_data))
404 return FALSE;
405
406 renderer = GDS_RENDER_OUTPUT_RENDERER(user_data);
407 priv = gds_output_renderer_get_instance_private(renderer);
408
409 if (g_mutex_trylock(&priv->idle_function_parameters.message_lock)) {
414 g_mutex_unlock(&priv->idle_function_parameters.message_lock);
415 } else {
416 return TRUE;
417 }
418
419 return FALSE;
420}
421
422void gds_output_renderer_update_async_progress(GdsOutputRenderer *renderer, const char *status)
423{
424 GSource *idle_event_processor;
426 gboolean skip_source = FALSE;
427
428 g_return_if_fail(GDS_RENDER_IS_OUTPUT_RENDERER(renderer));
429 if (!status)
430 return;
431
432 priv = gds_output_renderer_get_instance_private(renderer);
433
434 /* If rendering is not async */
435 if (!priv->main_context)
436 return;
437
438 g_mutex_lock(&priv->idle_function_parameters.message_lock);
441
442 /* Skip adding new idle source because there's already an active one */
443 skip_source = TRUE;
444 }
445 priv->idle_function_parameters.status_message = g_strdup(status);
446 g_mutex_unlock(&priv->idle_function_parameters.message_lock);
447
448 if (!skip_source) {
449 idle_event_processor = g_idle_source_new();
450 g_source_set_callback(idle_event_processor, idle_event_processor_callback, (gpointer)renderer, NULL);
451 g_source_attach(idle_event_processor, priv->main_context);
452 }
453}
454
Header for output renderer base class.
static GParamSpec * gds_output_renderer_properties[N_PROPERTIES]
static void gds_output_renderer_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec)
static guint gds_output_renderer_signals[GDS_OUTPUT_RENDERER_SIGNAL_COUNT]
GdsOutputRenderer * gds_output_renderer_new()
Create a new GdsOutputRenderer GObject.
GdsOutputRenderer * gds_output_renderer_new_with_props(const char *output_file, LayerSettings *layer_settings)
Create a new GdsOutputRenderer GObject with its properties.
static void gds_output_renderer_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec)
int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale)
Render output asynchronously.
static int gds_output_renderer_render_dummy(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale)
void gds_output_renderer_set_output_file(GdsOutputRenderer *renderer, const gchar *file_name)
Convenience function for setting the "output-file" property.
static void gds_output_renderer_class_init(GdsOutputRendererClass *klass)
const char * gds_output_renderer_get_output_file(GdsOutputRenderer *renderer)
Convenience function for getting the "output-file" property.
static void gds_output_renderer_dispose(GObject *self_obj)
int gds_output_renderer_render_output(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale)
gds_output_renderer_render_output
void gds_output_renderer_update_async_progress(GdsOutputRenderer *renderer, const char *status)
This function emits the 'progress-changed' in the thread/context that triggered an asynchronous rende...
LayerSettings * gds_output_renderer_get_and_ref_layer_settings(GdsOutputRenderer *renderer)
Get layer settings.
#define GDS_RENDER_TYPE_OUTPUT_RENDERER
gds_output_renderer_signal_ids
static gboolean idle_event_processor_callback(gpointer user_data)
static void gds_output_renderer_async_finished(GObject *src_obj, GAsyncResult *res, gpointer user_data)
static void gds_output_renderer_async_wrapper(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
void gds_output_renderer_set_layer_settings(GdsOutputRenderer *renderer, LayerSettings *settings)
Set layer settings.
void gds_output_renderer_init(GdsOutputRenderer *self)
@ GDS_OUTPUT_RENDERER_PARAM_ERR
Error set by the _GdsOutputRendererClass::render_output virtual function, if parameters are faulty.
@ GDS_OUTPUT_RENDERER_GEN_ERR
Error set by the _GdsOutputRendererClass::render_output virtual function, if renderer is invalid.
@ GDS_OUTPUT_RENDERER_SIGNAL_COUNT
@ ASYNC_FINISHED
@ ASYNC_PROGRESS_CHANGED
@ PROP_OUTPUT_FILE
@ N_PROPERTIES
@ PROP_LAYER_SETTINGS
#define GDS_RENDER_TYPE_LAYER_SETTINGS
struct idle_function_params idle_function_parameters
struct renderer_params async_params
A Cell inside a gds_library.
Definition: gds-types.h:122
struct gds_cell * cell