1 /*
2  * gEDA - GNU Electronic Design Automation
3  *
4  * render.c -- this file is a part of gerbv.
5  *
6  *   Copyright (C) 2007 Stuart Brorson (SDB@cloud9.net)
7  *
8  * $Id$
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23  */
24 
25 /** \file render.c
26     \brief Rendering support functions for gerbv
27     \ingroup gerbv
28 */
29 
30 #include "gerbv.h"
31 
32 #ifdef HAVE_STDLIB_H
33 # include <stdlib.h>
34 #endif
35 
36 #ifdef HAVE_STRING_H
37 # include <string.h>
38 #endif
39 
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 
44 #ifdef HAVE_LIBGEN_H
45 # include <libgen.h> /* dirname */
46 #endif
47 
48 #include <math.h>
49 
50 #include "common.h"
51 #include "main.h"
52 #include "callbacks.h"
53 #include "interface.h"
54 #include "render.h"
55 #include "selection.h"
56 
57 #ifdef WIN32
58 # include <cairo-win32.h>
59 #elif QUARTZ
60 # include <cairo-quartz.h>
61 #else
62 # include <cairo-xlib.h>
63 #endif
64 #include "draw.h"
65 
66 #define dprintf if(DEBUG) printf
67 
68 gerbv_render_info_t screenRenderInfo;
69 
70 /* ------------------------------------------------------ */
71 void
render_zoom_display(gint zoomType,gdouble scaleFactor,gdouble mouseX,gdouble mouseY)72 render_zoom_display (gint zoomType, gdouble scaleFactor, gdouble mouseX, gdouble mouseY)
73 {
74 	gdouble mouseCoordinateX = 0.0;
75 	gdouble mouseCoordinateY = 0.0;
76 	double oldWidth, oldHeight;
77 
78 	oldWidth = screenRenderInfo.displayWidth / screenRenderInfo.scaleFactorX;
79 	oldHeight = screenRenderInfo.displayHeight / screenRenderInfo.scaleFactorY;
80 
81 	if (zoomType == ZOOM_IN_CMOUSE || zoomType == ZOOM_OUT_CMOUSE) {
82 		/* calculate what user coordinate the mouse is pointing at */
83 		mouseCoordinateX = mouseX / screenRenderInfo.scaleFactorX + screenRenderInfo.lowerLeftX;
84 		mouseCoordinateY = (screenRenderInfo.displayHeight - mouseY) /
85 			screenRenderInfo.scaleFactorY + screenRenderInfo.lowerLeftY;
86 	}
87 
88 	switch(zoomType) {
89 		case ZOOM_IN : /* Zoom In */
90 		case ZOOM_IN_CMOUSE : /* Zoom In Around Mouse Pointer */
91 			screenRenderInfo.scaleFactorX = MIN((gdouble)GERBV_SCALE_MAX,
92 					(1 + 1/3.0)*screenRenderInfo.scaleFactorX);
93 			screenRenderInfo.scaleFactorY = screenRenderInfo.scaleFactorX;
94 			screenRenderInfo.lowerLeftX += (oldWidth - (screenRenderInfo.displayWidth /
95 						screenRenderInfo.scaleFactorX)) / 2.0;
96 			screenRenderInfo.lowerLeftY += (oldHeight - (screenRenderInfo.displayHeight /
97 						screenRenderInfo.scaleFactorY)) / 2.0;
98 			break;
99 		case ZOOM_OUT :  /* Zoom Out */
100 		case ZOOM_OUT_CMOUSE : /* Zoom Out Around Mouse Pointer */
101 			screenRenderInfo.scaleFactorX = MAX((gdouble)GERBV_SCALE_MIN,
102 					(1 - 1/3.0)*screenRenderInfo.scaleFactorX);
103 			screenRenderInfo.scaleFactorY = screenRenderInfo.scaleFactorX;
104 			screenRenderInfo.lowerLeftX += (oldWidth - (screenRenderInfo.displayWidth /
105 						screenRenderInfo.scaleFactorX)) / 2.0;
106 			screenRenderInfo.lowerLeftY += (oldHeight - (screenRenderInfo.displayHeight /
107 						screenRenderInfo.scaleFactorY)) / 2.0;
108 			break;
109 		case ZOOM_FIT : /* Zoom Fit */
110 			gerbv_render_zoom_to_fit_display (mainProject, &screenRenderInfo);
111 			break;
112 		case ZOOM_SET : /*explicit scale set by user */
113 			screenRenderInfo.scaleFactorX =
114 				MIN((gdouble)GERBV_SCALE_MAX, scaleFactor);
115 			screenRenderInfo.scaleFactorY = screenRenderInfo.scaleFactorX;
116 			screenRenderInfo.lowerLeftX += (oldWidth - (screenRenderInfo.displayWidth /
117 						screenRenderInfo.scaleFactorX)) / 2.0;
118 			screenRenderInfo.lowerLeftY += (oldHeight - (screenRenderInfo.displayHeight /
119 						screenRenderInfo.scaleFactorY)) / 2.0;
120 			break;
121 		default :
122 			GERB_MESSAGE(_("Illegal zoom direction %d"), zoomType);
123 	}
124 
125 	if (zoomType == ZOOM_IN_CMOUSE || zoomType == ZOOM_OUT_CMOUSE) {
126 		/* make sure the mouse is still pointing at the point calculated earlier */
127 		screenRenderInfo.lowerLeftX = mouseCoordinateX - mouseX / screenRenderInfo.scaleFactorX;
128 		screenRenderInfo.lowerLeftY = mouseCoordinateY - (screenRenderInfo.displayHeight - mouseY) /
129 			screenRenderInfo.scaleFactorY;
130 	}
131 	render_refresh_rendered_image_on_screen();
132 	return;
133 }
134 
135 
136 /* --------------------------------------------------------- */
137 /** Will determine the outline of the zoomed regions.
138  * In case region to be zoomed is too small (which correspondes
139  * e.g. to a double click) it is interpreted as a right-click
140  * and will be used to identify a part from the CURRENT selection,
141  * which is drawn on screen*/
142 void
render_calculate_zoom_from_outline(GtkWidget * widget,GdkEventButton * event)143 render_calculate_zoom_from_outline(GtkWidget *widget, GdkEventButton *event)
144 {
145 	int x1, y1, x2, y2, dx, dy;	/* Zoom outline (UR and LL corners) */
146 	double centerPointX, centerPointY;
147 	int half_x, half_y;		/* cache for half window dimensions */
148 
149 	x1 = MIN((gdouble)screen.start_x, event->x);
150 	y1 = MIN((gdouble)screen.start_y, event->y);
151 	x2 = MAX((gdouble)screen.start_x, event->x);
152 	y2 = MAX((gdouble)screen.start_y, event->y);
153 	dx = x2-x1;
154 	dy = y2-y1;
155 
156 	if ((dx >= 4) && (dy >= 4)) {
157 		if (screen.centered_outline_zoom) {
158 			/* Centered outline mode */
159 			x1 = screen.start_x - dx;
160 			y1 = screen.start_y - dy;
161 			dx *= 2;
162 			dy *= 2;
163 		}
164 		half_x = (x1+x2)/2;
165 		half_y = (y1+y2)/2;
166 		centerPointX = half_x/screenRenderInfo.scaleFactorX + screenRenderInfo.lowerLeftX;
167 		centerPointY = (screenRenderInfo.displayHeight - half_y)/screenRenderInfo.scaleFactorY +
168 				screenRenderInfo.lowerLeftY;
169 
170 		screenRenderInfo.scaleFactorX *=
171 			MIN((double)screenRenderInfo.displayWidth / dx,
172 				(double)screenRenderInfo.displayHeight / dy);
173 		screenRenderInfo.scaleFactorX = MIN((gdouble)GERBV_SCALE_MAX,
174 				screenRenderInfo.scaleFactorX);
175 		screenRenderInfo.scaleFactorY = screenRenderInfo.scaleFactorX;
176 		screenRenderInfo.lowerLeftX = centerPointX - (screenRenderInfo.displayWidth /
177 					2.0 / screenRenderInfo.scaleFactorX);
178 		screenRenderInfo.lowerLeftY = centerPointY - (screenRenderInfo.displayHeight /
179 					2.0 / screenRenderInfo.scaleFactorY);
180 	}
181 	render_refresh_rendered_image_on_screen();
182 }
183 
184 /* ------------------------------------------------------ */
185 void
render_draw_selection_box_outline(void)186 render_draw_selection_box_outline(void) {
187 	GdkGC *gc;
188 	GdkGCValues values;
189 	GdkGCValuesMask values_mask;
190 	gint x1, y1, x2, y2, dx, dy;
191 
192 	memset(&values, 0, sizeof(values));
193 	values.function = GDK_XOR;
194 	if (!screen.zoom_outline_color.pixel)
195 	 	gdk_colormap_alloc_color(gdk_colormap_get_system(), &screen.zoom_outline_color, FALSE, TRUE);
196 	values.foreground = screen.zoom_outline_color;
197 	values_mask = GDK_GC_FUNCTION | GDK_GC_FOREGROUND;
198 	gc = gdk_gc_new_with_values(screen.drawing_area->window, &values, values_mask);
199 
200 	x1 = MIN(screen.start_x, screen.last_x);
201 	y1 = MIN(screen.start_y, screen.last_y);
202 	x2 = MAX(screen.start_x, screen.last_x);
203 	y2 = MAX(screen.start_y, screen.last_y);
204 	dx = x2-x1;
205 	dy = y2-y1;
206 
207 	gdk_draw_rectangle(screen.drawing_area->window, gc, FALSE, x1, y1, dx, dy);
208 	gdk_gc_unref(gc);
209 }
210 
211 /* --------------------------------------------------------- */
212 void
render_draw_zoom_outline(gboolean centered)213 render_draw_zoom_outline(gboolean centered)
214 {
215 	GdkGC *gc;
216 	GdkGCValues values;
217 	GdkGCValuesMask values_mask;
218 	gint x1, y1, x2, y2, dx, dy;
219 
220 	memset(&values, 0, sizeof(values));
221 	values.function = GDK_XOR;
222 	if (!screen.zoom_outline_color.pixel)
223 	 	gdk_colormap_alloc_color(gdk_colormap_get_system(), &screen.zoom_outline_color, FALSE, TRUE);
224 	values.foreground = screen.zoom_outline_color;
225 	values_mask = GDK_GC_FUNCTION | GDK_GC_FOREGROUND;
226 	gc = gdk_gc_new_with_values(screen.drawing_area->window, &values, values_mask);
227 
228 	x1 = MIN(screen.start_x, screen.last_x);
229 	y1 = MIN(screen.start_y, screen.last_y);
230 	x2 = MAX(screen.start_x, screen.last_x);
231 	y2 = MAX(screen.start_y, screen.last_y);
232 	dx = x2-x1;
233 	dy = y2-y1;
234 
235 	if (centered) {
236 		/* Centered outline mode */
237 		x1 = screen.start_x - dx;
238 		y1 = screen.start_y - dy;
239 		dx *= 2;
240 		dy *= 2;
241 		x2 = x1+dx;
242 		y2 = y1+dy;
243 	}
244 
245 	gdk_draw_rectangle(screen.drawing_area->window, gc, FALSE, x1, y1, dx, dy);
246 	gdk_gc_unref(gc);
247 
248 	/* Draw actual zoom area in dashed lines */
249 	memset(&values, 0, sizeof(values));
250 	values.function = GDK_XOR;
251 	values.foreground = screen.zoom_outline_color;
252 	values.line_style = GDK_LINE_ON_OFF_DASH;
253 	values_mask = GDK_GC_FUNCTION | GDK_GC_FOREGROUND | GDK_GC_LINE_STYLE;
254 	gc = gdk_gc_new_with_values(screen.drawing_area->window, &values,
255 				values_mask);
256 
257 	if ((dy == 0) || ((double)dx/dy > (double)screen.drawing_area->allocation.width/
258 				screen.drawing_area->allocation.height)) {
259 		dy = dx * (double)screen.drawing_area->allocation.height/
260 			screen.drawing_area->allocation.width;
261 	}
262 	else {
263 		dx = dy * (double)screen.drawing_area->allocation.width/
264 			screen.drawing_area->allocation.height;
265 	}
266 
267 	gdk_draw_rectangle(screen.drawing_area->window, gc, FALSE, (x1+x2-dx)/2,
268 		(y1+y2-dy)/2, dx, dy);
269 
270 	gdk_gc_unref(gc);
271 }
272 
273 /* ------------------------------------------------------ */
274 /* Transforms board coordinates to screen ones */
275 static void
render_board2screen(gdouble * X,gdouble * Y,gdouble x,gdouble y)276 render_board2screen(gdouble *X, gdouble *Y, gdouble x, gdouble y) {
277 	*X = (x - screenRenderInfo.lowerLeftX) * screenRenderInfo.scaleFactorX;
278 	*Y = screenRenderInfo.displayHeight - (y - screenRenderInfo.lowerLeftY)
279 		* screenRenderInfo.scaleFactorY;
280 }
281 
282 /* Trims the coordinates to avoid overflows in gdk_draw_line */
283 static void
render_trim_point(gdouble * start_x,gdouble * start_y,gdouble last_x,gdouble last_y)284 render_trim_point(gdouble *start_x, gdouble *start_y, gdouble last_x, gdouble last_y)
285 {
286 	const gdouble max_coord = (1<<15) - 2;/* a value that causes no overflow
287 											 and lies out of screen */
288 	gdouble dx, dy;
289 
290     if (fabs (*start_x) < max_coord && fabs (*start_y) < max_coord)
291 		return;
292 
293 	dx = last_x - *start_x;
294 	dy = last_y - *start_y;
295 
296 	if (*start_x < -max_coord) {
297 		*start_x = -max_coord;
298 		if (last_x > -max_coord && fabs (dx) > 0.1)
299 			*start_y = last_y - (last_x + max_coord) / dx * dy;
300 	}
301 	if (*start_x > max_coord) {
302 		*start_x = max_coord;
303 		if (last_x < max_coord && fabs (dx) > 0.1)
304 			*start_y = last_y - (last_x - max_coord) / dx * dy;
305 	}
306 
307 	dx = last_x - *start_x;
308 	dy = last_y - *start_y;
309 
310 	if (*start_y < -max_coord) {
311 		*start_y = -max_coord;
312 		if (last_y > -max_coord && fabs (dy) > 0.1)
313 			*start_x = last_x - (last_y + max_coord) / dy * dx;
314 	}
315 	if (*start_y > max_coord) {
316 		*start_y = max_coord;
317 		if (last_y < max_coord && fabs (dy) > 0.1)
318 			*start_x = last_x - (last_y - max_coord) / dy * dx;
319 	}
320 }
321 
322 /* ------------------------------------------------------ */
323 /** Draws/erases measure line
324  */
325 void
render_toggle_measure_line(void)326 render_toggle_measure_line(void)
327 {
328 
329 	GdkGC *gc;
330 	GdkGCValues values;
331 	GdkGCValuesMask values_mask;
332 	gdouble start_x, start_y, last_x, last_y;
333 	memset(&values, 0, sizeof(values));
334 	values.function = GDK_XOR;
335 	values.line_width = 6;
336 	if (!screen.zoom_outline_color.pixel)
337 	 	gdk_colormap_alloc_color(gdk_colormap_get_system(), &screen.zoom_outline_color, FALSE, TRUE);
338 	values.foreground = screen.zoom_outline_color;
339 	values_mask = GDK_GC_FUNCTION | GDK_GC_LINE_WIDTH | GDK_GC_FOREGROUND;
340 	gc = gdk_gc_new_with_values(screen.drawing_area->window, &values,
341 				values_mask);
342 	render_board2screen(&start_x, &start_y,
343 				screen.measure_start_x, screen.measure_start_y);
344 	render_board2screen(&last_x, &last_y,
345 				screen.measure_stop_x, screen.measure_stop_y);
346 	render_trim_point(&start_x, &start_y, last_x, last_y);
347 	render_trim_point(&last_x, &last_y, start_x, start_y);
348 	gdk_draw_line(screen.drawing_area->window, gc, start_x,
349 		  start_y, last_x, last_y);
350 	gdk_gc_unref(gc);
351 } /* toggle_measure_line */
352 
353 /* ------------------------------------------------------ */
354 /** Displays a measured distance graphically on screen and in statusbar. */
355 void
render_draw_measure_distance(void)356 render_draw_measure_distance(void)
357 {
358 	gdouble dx, dy;
359 
360 	dx = fabs (screen.measure_start_x - screen.measure_stop_x);
361 	dy = fabs (screen.measure_start_y - screen.measure_stop_y);
362 
363 	screen.measure_last_x = dx;
364 	screen.measure_last_y = dy;
365 	callbacks_update_statusbar_measured_distance (dx, dy);
366 	render_toggle_measure_line();
367 }
368 
369 /* ------------------------------------------------------ */
render_selection(void)370 static void render_selection (void)
371 {
372 	gerbv_selection_item_t sel_item;
373 	gerbv_fileinfo_t *file;
374 	gdouble pixel_width;
375 	cairo_t *cr;
376 	int i;
377 	guint j;
378 
379 	if (selection_length (&screen.selectionInfo) == 0)
380 		return;
381 
382 	if (screen.selectionRenderData)
383 		cairo_surface_destroy (
384 				(cairo_surface_t *)screen.selectionRenderData);
385 
386 	screen.selectionRenderData =
387 		(gpointer) cairo_surface_create_similar (
388 			(cairo_surface_t *)screen.windowSurface,
389 			CAIRO_CONTENT_COLOR_ALPHA,
390 			screenRenderInfo.displayWidth,
391 			screenRenderInfo.displayHeight);
392 
393 	pixel_width = 1.0/MAX(screenRenderInfo.scaleFactorX,
394 				screenRenderInfo.scaleFactorY);
395 
396 	for (i = mainProject->last_loaded; i >= 0; i--) {
397 		file = mainProject->file[i];
398 
399 		if (!file ||
400 		(!mainProject->show_invisible_selection && !file->isVisible))
401 			continue;
402 
403 		for (j = 0; j < selection_length (&screen.selectionInfo); j++) {
404 			sel_item = selection_get_item_by_index (
405 					&screen.selectionInfo, j);
406 			if (file->image != sel_item.image)
407 				continue;
408 
409 			/* Have selected image(s) on this file, draw it */
410 
411 			cr = cairo_create(screen.selectionRenderData);
412 			gerbv_render_cairo_set_scale_and_translation(cr,
413 					&screenRenderInfo);
414 			cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.85);
415 			draw_image_to_cairo_target (cr,
416 				file->image, pixel_width,
417 				DRAW_SELECTIONS, &screen.selectionInfo,
418 				&screenRenderInfo, TRUE,
419 				file->transform, TRUE);
420 			cairo_destroy (cr);
421 
422 			break;	/* All done, go to next file */
423 		}
424 	}
425 }
426 
427 /* ------------------------------------------------------ */
render_refresh_rendered_image_on_screen(void)428 void render_refresh_rendered_image_on_screen (void) {
429 	GdkCursor *cursor;
430 
431 	dprintf("----> Entering redraw_pixmap...\n");
432 	cursor = gdk_cursor_new(GDK_WATCH);
433 	gdk_window_set_cursor(GDK_WINDOW(screen.drawing_area->window), cursor);
434 	gdk_cursor_destroy(cursor);
435 
436 	if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR){
437 	    if (screen.pixmap)
438 		gdk_pixmap_unref(screen.pixmap);
439 	    screen.pixmap = gdk_pixmap_new(screen.drawing_area->window, screenRenderInfo.displayWidth,
440 	    screenRenderInfo.displayHeight, -1);
441 	    gerbv_render_to_pixmap_using_gdk (mainProject, screen.pixmap, &screenRenderInfo, &screen.selectionInfo,
442 	    		&screen.selection_color);
443 	    dprintf("<---- leaving redraw_pixmap.\n");
444 	}
445 	else{
446 	    int i;
447 	    dprintf("    .... Now try rendering the drawing using cairo .... \n");
448 	    /*
449 	     * This now allows drawing several layers on top of each other.
450 	     * Higher layer numbers have higher priority in the Z-order.
451 	     */
452 	    for(i = mainProject->last_loaded; i >= 0; i--) {
453 		if (mainProject->file[i]) {
454 		    cairo_t *cr;
455 		    if (mainProject->file[i]->privateRenderData)
456 			cairo_surface_destroy ((cairo_surface_t *) mainProject->file[i]->privateRenderData);
457 		    mainProject->file[i]->privateRenderData =
458 			(gpointer) cairo_surface_create_similar ((cairo_surface_t *)screen.windowSurface,
459 			CAIRO_CONTENT_COLOR_ALPHA, screenRenderInfo.displayWidth,
460 			screenRenderInfo.displayHeight);
461 		    cr= cairo_create(mainProject->file[i]->privateRenderData );
462 		    gerbv_render_layer_to_cairo_target (cr, mainProject->file[i], &screenRenderInfo);
463 		    dprintf("    .... calling render_image_to_cairo_target on layer %d...\n", i);
464 		    cairo_destroy (cr);
465 		}
466 	    }
467 
468 	    render_recreate_composite_surface ();
469 	}
470 	/* remove watch cursor and switch back to normal cursor */
471 	callbacks_switch_to_correct_cursor ();
472 	callbacks_force_expose_event_for_screen();
473 }
474 
475 /* ------------------------------------------------------ */
476 void
render_remove_selected_objects_belonging_to_layer(gerbv_selection_info_t * sel_info,gerbv_image_t * image)477 render_remove_selected_objects_belonging_to_layer (
478 			gerbv_selection_info_t *sel_info, gerbv_image_t *image)
479 {
480 	guint i;
481 
482 	for (i = 0; i < selection_length (sel_info);) {
483 		gerbv_selection_item_t sItem =
484 			selection_get_item_by_index (sel_info, i);
485 
486 		if (image == (gerbv_image_t *) sItem.image)
487 			selection_clear_item_by_index (sel_info, i);
488 		else
489 			i++;
490 	}
491 }
492 
493 /* ------------------------------------------------------ */
494 gint
render_create_cairo_buffer_surface()495 render_create_cairo_buffer_surface () {
496 	if (screen.bufferSurface) {
497 		cairo_surface_destroy (screen.bufferSurface);
498 		screen.bufferSurface = NULL;
499 	}
500 	if (!screen.windowSurface)
501 		return 0;
502 
503 	screen.bufferSurface= cairo_surface_create_similar ((cairo_surface_t *)screen.windowSurface,
504 	                                    CAIRO_CONTENT_COLOR, screenRenderInfo.displayWidth,
505 	                                    screenRenderInfo.displayHeight);
506 	return 1;
507 }
508 
509 /* ------------------------------------------------------ */
510 static void
render_find_selected_objects_and_refresh_display(gint activeFileIndex,enum selection_action action)511 render_find_selected_objects_and_refresh_display (gint activeFileIndex,
512 		enum selection_action action)
513 {
514 	enum draw_mode mode = FIND_SELECTIONS;
515 
516 	/* clear the old selection array if desired */
517 	if ((action == SELECTION_REPLACE)
518 	 && (selection_length (&screen.selectionInfo) != 0))
519 		selection_clear (&screen.selectionInfo);
520 
521 	if (action == SELECTION_TOGGLE)
522 		mode = FIND_SELECTIONS_TOGGLE;
523 
524 	/* make sure we have a bufferSurface...if we start up in FAST mode, we may not
525 	   have one yet, but we need it for selections */
526 	if (!render_create_cairo_buffer_surface ())
527 		return;
528 
529 	/* call draw_image... passing the FILL_SELECTION mode to just search for
530 	   nets which match the selection, and fill the selection buffer with them */
531 	cairo_t *cr = cairo_create (screen.bufferSurface);
532 	gerbv_render_cairo_set_scale_and_translation (cr, &screenRenderInfo);
533 	draw_image_to_cairo_target (cr,
534 			mainProject->file[activeFileIndex]->image,
535 			1.0/MAX (screenRenderInfo.scaleFactorX,
536 				screenRenderInfo.scaleFactorY),
537 			mode, &screen.selectionInfo, &screenRenderInfo, TRUE,
538 			mainProject->file[activeFileIndex]->transform, TRUE);
539 	cairo_destroy (cr);
540 
541 	/* re-render the selection buffer layer */
542 	if (screenRenderInfo.renderType <= GERBV_RENDER_TYPE_GDK_XOR) {
543 		render_refresh_rendered_image_on_screen ();
544 	} else {
545 		render_recreate_composite_surface ();
546 		callbacks_force_expose_event_for_screen ();
547 	}
548 }
549 
550 /* ------------------------------------------------------ */
551 void
render_fill_selection_buffer_from_mouse_click(gint mouseX,gint mouseY,gint activeFileIndex,enum selection_action action)552 render_fill_selection_buffer_from_mouse_click (gint mouseX, gint mouseY,
553 		gint activeFileIndex, enum selection_action action)
554 {
555 	screen.selectionInfo.lowerLeftX = mouseX;
556 	screen.selectionInfo.lowerLeftY = mouseY;
557 	/* no need to populate the upperright coordinates for a point_click */
558 	screen.selectionInfo.type = GERBV_SELECTION_POINT_CLICK;
559 	render_find_selected_objects_and_refresh_display (
560 			activeFileIndex, action);
561 }
562 
563 /* ------------------------------------------------------ */
564 void
render_fill_selection_buffer_from_mouse_drag(gint corner1X,gint corner1Y,gint corner2X,gint corner2Y,gint activeFileIndex,enum selection_action action)565 render_fill_selection_buffer_from_mouse_drag (gint corner1X, gint corner1Y,
566 		gint corner2X, gint corner2Y,
567 		gint activeFileIndex, enum selection_action action)
568 {
569 	/* figure out the lower left corner of the box */
570 	screen.selectionInfo.lowerLeftX = MIN(corner1X, corner2X);
571 	screen.selectionInfo.lowerLeftY = MIN(corner1Y, corner2Y);
572 	/* figure out the upper right corner of the box */
573 	screen.selectionInfo.upperRightX = MAX(corner1X, corner2X);
574 	screen.selectionInfo.upperRightY = MAX(corner1Y, corner2Y);
575 
576 	screen.selectionInfo.type = GERBV_SELECTION_DRAG_BOX;
577 	render_find_selected_objects_and_refresh_display (
578 			activeFileIndex, action);
579 }
580 
581 /* ------------------------------------------------------ */
render_recreate_composite_surface()582 void render_recreate_composite_surface ()
583 {
584 	gint i;
585 
586 	if (!render_create_cairo_buffer_surface())
587 		return;
588 
589 	cairo_t *cr= cairo_create(screen.bufferSurface);
590 	/* fill the background with the appropriate color */
591 	cairo_set_source_rgba (cr, (double) mainProject->background.red/G_MAXUINT16,
592 		(double) mainProject->background.green/G_MAXUINT16,
593 		(double) mainProject->background.blue/G_MAXUINT16, 1);
594 	cairo_paint (cr);
595 
596 	for(i = mainProject->last_loaded; i >= 0; i--) {
597 		if (mainProject->file[i] && mainProject->file[i]->isVisible) {
598 			cairo_set_source_surface (cr, (cairo_surface_t *) mainProject->file[i]->privateRenderData,
599 			                              0, 0);
600 			/* ignore alpha if we are in high-speed render mode */
601 			if (((double) mainProject->file[i]->alpha < 65535)&&(screenRenderInfo.renderType != GERBV_RENDER_TYPE_GDK_XOR)) {
602 				cairo_paint_with_alpha(cr,(double) mainProject->file[i]->alpha/G_MAXUINT16);
603 			}
604 			else {
605 				cairo_paint (cr);
606 			}
607 		}
608 	}
609 
610 	/* render the selection layer at the end */
611 	if (selection_length (&screen.selectionInfo) != 0) {
612 		render_selection ();
613 		cairo_set_source_surface (cr, (cairo_surface_t *) screen.selectionRenderData,
614 			                              0, 0);
615 		cairo_paint_with_alpha (cr,1.0);
616 	}
617 	cairo_destroy (cr);
618 }
619 
620 /* ------------------------------------------------------ */
render_project_to_cairo_target(cairo_t * cr)621 void render_project_to_cairo_target (cairo_t *cr) {
622 	/* fill the background with the appropriate color */
623 	cairo_set_source_rgba (cr, (double) mainProject->background.red/G_MAXUINT16,
624 		(double) mainProject->background.green/G_MAXUINT16,
625 		(double) mainProject->background.blue/G_MAXUINT16, 1);
626 	cairo_paint (cr);
627 
628 	cairo_set_source_surface (cr, (cairo_surface_t *) screen.bufferSurface, 0 , 0);
629 
630 	cairo_paint (cr);
631 }
632 
633 void
render_free_screen_resources(void)634 render_free_screen_resources (void) {
635 	if (screen.selectionRenderData)
636 		cairo_surface_destroy ((cairo_surface_t *)
637 			screen.selectionRenderData);
638 	if (screen.bufferSurface)
639 		cairo_surface_destroy ((cairo_surface_t *)
640 			screen.bufferSurface);
641 	if (screen.windowSurface)
642 		cairo_surface_destroy ((cairo_surface_t *)
643 			screen.windowSurface);
644 	if (screen.pixmap)
645 		gdk_pixmap_unref(screen.pixmap);
646 }
647 
648 
649 /* ------------------------------------------------------------------ */
650 /*! This fills out the project's Gerber statistics table.
651  *  It is called from within callbacks.c when the user
652  *  asks for a Gerber report.  */
653 gerbv_stats_t *
generate_gerber_analysis(void)654 generate_gerber_analysis(void)
655 {
656 	int i;
657 	gerbv_stats_t *stats;
658 	gerbv_stats_t *instats;
659 
660 	/* Create new stats structure to hold report for whole project
661 	* (i.e. all layers together) */
662 	stats = gerbv_stats_new();
663 
664 	/* Loop through open layers and compile statistics by accumulating reports from each layer */
665 	for (i = 0; i <= mainProject->last_loaded; i++) {
666 		if (mainProject->file[i] && mainProject->file[i]->isVisible &&
667 				(mainProject->file[i]->image->layertype == GERBV_LAYERTYPE_RS274X) ) {
668 			instats = mainProject->file[i]->image->gerbv_stats;
669 			gerbv_stats_add_layer(stats, instats, i+1);
670 		}
671 	}
672 	return stats;
673 }
674 
675 
676 /* ------------------------------------------------------------------ */
677 /*! This fills out the project's Drill statistics table.
678  *  It is called from within callbacks.c when the user
679  *  asks for a Drill report.  */
680 gerbv_drill_stats_t *
generate_drill_analysis(void)681 generate_drill_analysis(void)
682 {
683 	int i;
684 	gerbv_drill_stats_t *stats;
685 	gerbv_drill_stats_t *instats;
686 
687 	stats = gerbv_drill_stats_new();
688 
689 	/* Loop through open layers and compile statistics by accumulating reports from each layer */
690 	for(i = mainProject->last_loaded; i >= 0; i--) {
691 		if (mainProject->file[i] &&
692 				mainProject->file[i]->isVisible &&
693 				(mainProject->file[i]->image->layertype == GERBV_LAYERTYPE_DRILL) ) {
694 			instats = mainProject->file[i]->image->drill_stats;
695 			/* add this batch of stats.  Send the layer
696 			* index for error reporting */
697 			gerbv_drill_stats_add_layer(stats, instats, i+1);
698 		}
699 	}
700 	return stats;
701 }
702 
703