source: projects/paper-tape-project/trunk/visualisator/lochstreifen.c @ 78

Last change on this file since 78 was 78, checked in by sven, 11 years ago

Major improvements in making the Paper Tape Projects ready for deploying:

  • Written a 21 page manual in German (LaTeX, PDF)
  • Visualisator is running, export works, lesser bugs
  • some new perl-tools
  • a bin directory with softlinks
  • You can "install" the paper tape projects right now.
File size: 40.4 KB
Line 
1/**
2 * The Paper Tape Project -- Visualisation sub project
3 * lochstreifen.c - Painting Paper Tapes according to ECMA-10, with cairographics.
4 * $Id$
5 *
6 * This is a rewrite from the famous Cairo paper tape drawing routines. This
7 * c file implements a (simple) object called "LOCHSTREIFEN".
8 *
9 *
10 *
11 *
12 *
13 * (c) Copyright 2007, 2008 Sven Köppel
14 *
15 *  This program is free software; you can redistribute
16 *  it and/or modify it under the terms of the GNU General
17 *  Public License as published by the Free Software
18 *  Foundation; either version 3 of the License, or (at
19 *  your option) any later version.
20 *
21 *  This program is distributed in the hope that it will
22 *  be useful, but WITHOUT ANY WARRANTY; without even the
23 *  implied warranty of MERCHANTABILITY or FITNESS FOR A
24 *  PARTICULAR PURPOSE. See the GNU General Public License
25 *  for more details.
26 *
27 *  You should have received a copy of the GNU General
28 *  Public License along with this program; if not, see
29 *  http://www.gnu.org/licenses/
30 *
31 **/
32
33#include <math.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include "lochstreifen.h"
38
39/**
40 * Create a new LOCHSTREIFEN object and return it. This is a quite light
41 * object system implementation, as you get a reference to the object.
42 * You are not supposed to manipulate the elements of the LOCHSTREIFEN
43 * structure directly.
44 *
45 * Btw, the german word "LOCHSTREIFEN" only means "PAPER TAPE".
46 *
47 **/
48LOCHSTREIFEN *lochstreifen_new() {
49        LOCHSTREIFEN *l = malloc (sizeof(LOCHSTREIFEN));
50
51        // set default values:
52        l->data_length                = 0;
53        l->data                       = NULL;
54        l->highlight_region_start     = 0;
55        l->highlight_region_end       = 0;
56        l->highlight_region_color     = NULL;
57        l->highlight_row_number       = 0;
58        l->highlight_row_color        = NULL;
59        l->highlight_bit_row          = 0;
60        l->highlight_bit_track        = 0;
61        l->highlight_bit_color        = NULL;
62        l->clip                       = NULL;
63        l->outer_background_color     = LOCHSTREIFEN_DEFAULT_OUTER_BACKGROUND_COLOR;
64        l->papertape_background_color = LOCHSTREIFEN_DEFAULT_PAPERTAPE_BACKGROUND_COLOR;
65        l->one_code_hole_color        = LOCHSTREIFEN_DEFAULT_ONE_CODE_HOLE_COLOR;
66        l->zero_code_hole_color       = LOCHSTREIFEN_DEFAULT_ZERO_CODE_HOLE_COLOR;
67        l->feed_hole_color            = LOCHSTREIFEN_DEFAULT_FEED_HOLE_COLOR;
68        cairo_matrix_init(&l->matrix, 1, 0, 0, 1, 0, 0); // default = 1:1 original
69        l->matrix_inverse             = l->matrix;
70
71        return l;
72}
73
74/**
75 * Copy everything from the LOCHSTREIFEN template, except the data. They
76 * won't be copied, just set, because LOCHSTREIFEN doesn't manage your
77 * data. If you want to copy them, call something like
78 *   LOCHSTREIFEN* copy = lochstreifen_copy(old);
79 *   byte_t* new_data = malloc(old->data_length);
80 *   memcpy(new_data, old->data, old->data_length);
81 *   lochstreifen_set_data(copy, old->data_length, new_data);
82 **/
83LOCHSTREIFEN* lochstreifen_copy(const LOCHSTREIFEN* template) {
84        LOCHSTREIFEN *l = lochstreifen_new();
85       
86        #define COPY_RGBA_PATTERN(name) { double r,g,b,a; \
87                if(template->name != NULL && \
88                   !cairo_pattern_get_rgba(template->name, &r, &g, &b, &a)) \
89                        l->name = cairo_pattern_create_rgba(r,g,b,a); }
90       
91        // make it quite verbose ;-)
92        l->data_length                = template->data_length;
93        l->data                       = template->data;
94        l->highlight_region_start     = template->highlight_region_start;
95        l->highlight_region_end       = template->highlight_region_end;
96        COPY_RGBA_PATTERN(highlight_region_color);
97        l->highlight_row_number       = template->highlight_row_number;
98        COPY_RGBA_PATTERN(highlight_row_color);
99        l->highlight_bit_row          = template->highlight_bit_row;
100        l->highlight_bit_track        = template->highlight_bit_track;
101        COPY_RGBA_PATTERN(highlight_bit_color);
102        l->clip                       = malloc(sizeof(cairo_rectangle_t));
103        memcpy(l->clip, template->clip, sizeof(cairo_rectangle_t));
104        COPY_RGBA_PATTERN(outer_background_color);
105        COPY_RGBA_PATTERN(papertape_background_color);
106        COPY_RGBA_PATTERN(one_code_hole_color);
107        COPY_RGBA_PATTERN(zero_code_hole_color);
108        COPY_RGBA_PATTERN(feed_hole_color);
109        l->matrix                     = template->matrix;
110        l->matrix_inverse             = template->matrix_inverse;
111        l->row_callback               = template->row_callback;
112        l->row_callback_user_data     = template->row_callback_user_data;
113
114        return l;
115}
116
117/**
118 * Delete every mallocated structure in the LOCHSTREIFEN (colors, etc.),
119 * *except* from the data. You have to manage them on your own.
120 * In the end, it will free the LOCHSTREIFEN itself.
121 * This won't touch your row_callback_user_data.
122 * @return Nothing, because there's nothing left. Except your data, okay.
123 **/
124void lochstreifen_free(LOCHSTREIFEN* l) {
125        if(l->highlight_region_color     != NULL)  free(l->highlight_region_color);
126        if(l->highlight_row_color        != NULL)  free(l->highlight_row_color);
127        if(l->highlight_bit_color        != NULL)  free(l->highlight_bit_color);
128        if(l->clip                       != NULL)  free(l->clip);
129        if(l->outer_background_color     != NULL)  free(l->outer_background_color);
130        if(l->papertape_background_color != NULL)  free(l->papertape_background_color);
131        if(l->one_code_hole_color        != NULL)  free(l->one_code_hole_color);
132        if(l->zero_code_hole_color       != NULL)  free(l->zero_code_hole_color);
133        if(l->feed_hole_color            != NULL)  free(l->feed_hole_color);
134}
135
136/**
137 * A small debug function that will print out all fields of the LOCHSTREIFEN
138 * structure well ordered to stderr.
139 *
140 **/
141void lochstreifen_print_debug(LOCHSTREIFEN* l) {
142        int x;
143        #define COLORP(color) if(color == NULL) fprintf(stderr, "   color ist NULL.\n"); \
144                else if(cairo_pattern_get_type(color) == CAIRO_PATTERN_TYPE_SOLID) {\
145                double r,g,b,a; cairo_pattern_get_rgba(color, &r, &g, &b, &a); \
146                fprintf(stderr, "   SOLID: R=%f, G=%f, B=%f, A=%f\n", r, g, b, a); \
147                } else fprintf(stderr, "    NO SOLID PATTERN (special pattern)\n");
148        #define COLORP_VERBOSE(color1, name) \
149                fprintf(stderr, " * "name": %s\n", (color1) != NULL ? "ENABLED" : "disabled");\
150                COLORP(color1);
151
152       
153        if(l != NULL)
154                fprintf(stderr, "Debugging papertape at %p:\n", l);
155        else {
156                fprintf(stderr, "Cannot debug: Papertape is NULL!\n");
157                return;
158        }
159       
160        // 1. Point: Print out data.
161        fprintf(stderr, "1# DATA AND OUTPUT IMAGE SIZE INFORMATION\n");
162        fprintf(stderr, " * Data: length=%i bytes", l->data_length);
163        if(l->data_length > 0 && l->data != NULL) {
164                // there are data
165                fprintf(stderr, ":\n   { ");
166                for(x=0;;) {
167                        fprintf(stderr, "0x%x", l->data[x]);
168                        if(++x == l->data_length) {
169                                fprintf(stderr, " };\n");
170                                break;
171                        } else if(x == 10) { // magic number: print first 10 bytes.
172                                fprintf(stderr, ", ... };\n");
173                                break;
174                        } else
175                                fprintf(stderr, ", ");
176                }
177        } else if(l->data == NULL) {
178                fprintf(stderr, " BUT data are NULL!\n");
179        } else  // data_length == 0.
180                fprintf(stderr, " (empty data)\n");
181        fprintf(stderr, " * Unscaled dimensions:\n");
182        fprintf(stderr, "   width of image (length of papertape) = %f\n", lochstreifen_get_length(l));
183        fprintf(stderr, "   height of image (width of papertape) = 1\n");
184       
185        // 2. Point: Print out colors
186        fprintf(stderr, "\n2# ELEMENT COLORS\n");
187        COLORP_VERBOSE(l->outer_background_color, "outer background");
188        COLORP_VERBOSE(l->papertape_background_color, "papertape background");
189        COLORP_VERBOSE(l->one_code_hole_color, "one code holes");
190        COLORP_VERBOSE(l->zero_code_hole_color, "zero code holes");
191        COLORP_VERBOSE(l->feed_hole_color, "feed holes");
192       
193        // 3. Point: Print out highlighted thingies
194        fprintf(stderr, "\n3# HIGHLIGHT COLORS\n");
195        fprintf(stderr, l->highlight_region_color != NULL ? " * Highlight region: start=%i end=%i\n"
196                : " * Highlight region: disabled\n", l->highlight_region_start, l->highlight_region_end);
197        COLORP(l->highlight_region_color);
198        fprintf(stderr, l->highlight_row_color != NULL ? " * Highlight row: number=%i\n"
199                : " * Highlight row: disabled\n", l->highlight_row_number);
200        COLORP(l->highlight_row_color);
201        fprintf(stderr, l->highlight_bit_color != NULL ? " * Higlight bit: row=%i track=%i\n"
202                : " * Highlight bit: disabled\n", l->highlight_bit_row, l->highlight_bit_track);
203        COLORP(l->highlight_bit_color);
204       
205        // 4. Point: Print out information about clippings
206        fprintf(stderr, "\n4# SPECIAL STRUCTURES\n");
207        if(l->clip == NULL)
208                fprintf(stderr, " * Clipping: disabled\n");
209        else {
210                fprintf(stderr, " * Clipping: ENABLED\n");
211                fprintf(stderr, "  x=%f, y=%f, width=%f, height=%f\n",
212                        l->clip->x, l->clip->y, l->clip->width, l->clip->height);
213        }
214       
215        // 5. Point: Affine Matrix
216        fprintf(stderr, " * Current transformation matrix:\n");
217        fprintf(stderr, "   x_new = [xx=%f]*x + [xy=%f]*y + [x0=%f]\n",
218                l->matrix.xx, l->matrix.xy, l->matrix.x0);
219        fprintf(stderr, "   y_new = [yx=%f]*x + [yy=%f]*y + [y0=%f]\n",
220                l->matrix.yx, l->matrix.yy, l->matrix.y0);
221        fprintf(stderr, "   source dimensions: x_max = %f x y_max = %f\n",
222                LOCHSTREIFEN_LENGTH, LOCHSTREIFEN_WIDTH);
223        fprintf(stderr, "   target dimensions: width = %i x height = %i\n",
224                lochstreifen_get_target_width(l),
225                lochstreifen_get_target_height(l));
226       
227        // 6. Point: Debugging
228        fprintf(stderr, " * Debugging: %s\n", l->debug ? "ENABLED" : "disabled");
229       
230        fprintf(stderr, "END OF LOCHSTREIFEN\n");
231}
232
233/**
234 * Set the data which LOCHSTREIFEN is supposed to punch out. You shall give
235 * the LOCSTREIFEN object only a copy of your data, never give it a sensible
236 * data array. The LOCHSTREIFEN object won't write into your data, but afterwards
237 * it will free them. So it treats the data as it's own.
238 *
239 **/
240void lochstreifen_set_data(LOCHSTREIFEN *l, int data_length, byte_t *data) {
241        l->data_length = data_length;
242        //if(l->data != NULL)
243        //      free(l->data);
244        l->data = data;
245}
246
247/**
248 * Adds Null Bytes (0x00) at start and/or end of the data array. This dynamically
249 * increases the data array.
250 *
251 *
252 **/
253void lochstreifen_add_null_bytes(LOCHSTREIFEN *l, int start, int end) {
254        byte_t *new_data;
255
256        // only to be sure realloc won't decrease the size of the array:
257        if(start < 0) start = 0;
258        if(end < 0)   end = 0;
259       
260        // make array bigger to contain new start/end chunks
261        new_data = realloc(l->data, l->data_length + start + end);
262        // check wheter realloc exited successfully. We don't want segfaults
263        if(new_data == NULL)
264                return;
265        // move data to the middle
266        memmove(new_data + start, new_data, l->data_length);
267        // fill new start chunk with zero bytes
268        memset(new_data, 0x00, start);
269        // fill new end chunk with zero bytes
270        memset(new_data + start + l->data_length, 0x00, end);
271        // that's it.
272       
273        // fix the data link. Don't free the old data. Don't know exactly
274        // how realloc works, sorry ;-)
275        l->data = new_data;
276}
277
278/**
279 * Get the length of a LOCHSTREIFEN. It depends on the length of the data array
280 * and the papertape dimension constants.
281 *
282 *
283 **/
284double lochstreifen_get_length(LOCHSTREIFEN *l) {
285        return LOCHSTREIFEN_LENGTH;
286}
287
288
289/**
290 * Tell LOCHSTREIFEN to highlight a region of rows. Start counts from 0 and is already
291 * in the selection, end is no more in selection. Example with start = 2; end = 5:
292 *
293 * 0    1     2     3     4     5    6  <- rows
294 *         |_________________|
295 *          highlight region
296 **/
297void lochstreifen_set_highlight_region(LOCHSTREIFEN *l, row_t start, row_t end) {
298        l->highlight_region_start = start;
299        l->highlight_region_end   = end;
300        if(l->highlight_region_color == NULL)
301                l->highlight_row_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_REGION_COLOR;
302}
303
304/**
305 * Get the selection bounds of the highlighted rows. Give Links to your variables
306 * where the function stores the start/end integers.
307 * @return TRUE if there is an highlight selection, FALSE otherwiese
308 **/
309int lochstreifen_get_highlight_region(LOCHSTREIFEN *l, row_t *start, row_t *end) {
310        if(l->highlight_region_color == NULL || l->highlight_region_start == LOCHSTREIFEN_NO_ROW)
311                return 0;
312        if(start != NULL)
313                *start = l->highlight_region_start;
314        if(end != NULL)
315                *end   = l->highlight_region_end;
316        return 1;
317}
318
319/**
320 * Remove the highlighted region (that is, make it no more highlighted).
321 **/
322void lochstreifen_remove_highlight_region(LOCHSTREIFEN *l) {
323        //l->highlight_region_color = NULL;
324        // was stupid, this is more intelligent:
325        l->highlight_region_start = LOCHSTREIFEN_NO_ROW;
326}
327
328/**
329 * Tell LOCHSTREIFEN to highlight one, only one special row. Starts counting from 0.
330 * Example with start = 3:
331 *
332 * 0   1    2     3     4     5
333 *             |_____|
334 *        highlighted row
335 *
336 * Principally this is the same feature like lochstreifen_set_highlight_region, having
337 * (in the above example) start = 3 and end = 4. You can just use another color and
338 * use both techniques concurrently, whereby the highlighted row will ALWAYS be ABOVE
339 * the highlighted chunk of rows. An application example is e.g.: The highlighted region
340 * of rows are selected rows, the highlighted special row is the cursor position row.
341 * This would be quite usable with an implementation of the GtkEditable interface, like
342 * GtkPaperTape.
343 **/
344void lochstreifen_set_highlight_row(LOCHSTREIFEN *l, row_t number_of_row) {
345        l->highlight_row_number = number_of_row;
346        if(l->highlight_row_color == NULL)
347                l->highlight_row_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_ROW_COLOR;
348}
349
350/**
351 * Get the highlighted special row. If there is no highlighted row, it will return
352 * LOCHSTREIFEN_NO_ROW.
353 * @return the number of the special row, counting from zero.
354 **/
355int lochstreifen_get_highlight_row(LOCHSTREIFEN *l) {
356        return l->highlight_row_number;
357}
358
359/**
360 * Remove the highlight at the special selected row.
361 **/
362void lochstreifen_remove_highlight_row(LOCHSTREIFEN *l) {
363        //l->highlight_row_color = NULL;  /// nein, das ist dumm, weil es den highlight komplett entfernen würde.
364        l->highlight_row_number = LOCHSTREIFEN_NO_ROW; // das ist schlau.
365}
366
367/**
368 * Tell LOCHSTREIFEN to highlight one special bit on the complete papertape. To identify
369 * this bit, you give the row and track number (byte id and bit id) for this bit. E.g.
370 * with row = 3, bit = 5 you'll get something like:
371 *
372 * ------- DIRECTION OF MOVEMENT ------->
373 * track 8     8     8     8     8     8
374 * track 7     7     7     7     7     7
375 * track 6     6     6  +-----+  6     6
376 * track 5     5     5  |  5  |  5     5       (of course the highlight won't affect any
377 * track 4     4     4  +-----+  4     4        other bit, row 3, track 6 and track 4
378 * feed  -     -     -     -     -     -        are not visible due to ASCII limitations
379 * track 3     3     3     3     3     3        ;-) )
380 * track 2     2     2     2     2     2
381 * track 1     1     1     1     1     1
382 *
383 * row:  0     1     2     3     4     5
384 *                         ^-- this row
385 *
386 * The bit highlight will appear on top of the row highlight and region highlight. You
387 * can combine the bit highlight with row highlight (e.g. in the example above you can
388 * highlight row 3, too) and even with the region highlight (e.g. having a region from
389 * row 2 to row 5).
390 **/
391void lochstreifen_set_highlight_bit(LOCHSTREIFEN *l, row_t number_of_row, track_t number_of_bit) {
392        l->highlight_bit_row = number_of_row;
393        l->highlight_bit_track = number_of_bit;
394        if(l->highlight_bit_color == NULL)
395                l->highlight_bit_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_BIT_COLOR;
396}
397
398/**
399 * Get the highlighted special bit. This works exactly like lochstreifen_get_highlight_region,
400 * returning FALSE (0) if there's no bit selected.
401 *
402 **/
403int lochstreifen_get_highlight_bit(LOCHSTREIFEN *l, row_t *number_of_row, track_t *number_of_bit) {
404        if(l->highlight_bit_row == LOCHSTREIFEN_NO_ROW)
405                return 0;
406        if(number_of_row != NULL)
407                *number_of_row = l->highlight_bit_row;
408        if(number_of_bit != NULL)
409                *number_of_bit = l->highlight_bit_track;
410        return 1;
411}
412
413/**
414 * Remove the highlighting of the special bit.
415 **/
416void lochstreifen_remove_highlight_bit(LOCHSTREIFEN *l) {
417        //l->highlight_bit_color = NULL; // stupid.
418        l->highlight_bit_row = LOCHSTREIFEN_NO_ROW; // more intelligent.
419}
420
421/**
422 * Whenever you change the matrix, call this function. It will just perform some
423 * cleanup work, espacially calculate the inverse matrix for the other-side calculations.
424 **/
425void lochstreifen_check_matrix(LOCHSTREIFEN* l) {
426        l->matrix_inverse = l->matrix; // copy matrix
427        if(cairo_matrix_invert(&l->matrix_inverse) != CAIRO_STATUS_SUCCESS) { // invert the copy
428                // there are some matrizes which are not invertable
429                fprintf(stderr, "lochstreifen_check_matrix: Matrix is not invertable!\n");
430        }
431}
432
433/**
434 * If you want LOCHSTREIFEN only to paint a small area from the whole papertape, this
435 * is the function you can call.
436 * If you are using an affine transformation matrix for scaling/rotation/... purpose,
437 * this function will automatically transform these values. So don't give here values
438 * like "LOCHSTREIFEN_WIDTH" or "LOCHSTREIFEN_HEIGHT" ;-)
439 *
440 **/
441void lochstreifen_set_clip(LOCHSTREIFEN *l, double x, double y, double width, double height) {
442        if(l->clip == NULL)
443                l->clip = malloc(sizeof(cairo_rectangle_t));
444
445        cairo_matrix_transform_point(&l->matrix_inverse, &x, &y);
446        cairo_matrix_transform_distance(&l->matrix_inverse, &width, &height);
447        l->clip->x = x;
448        l->clip->y = y;
449        l->clip->width = width;
450        l->clip->height = height;
451}
452
453/**
454 * I fyou want LOCHSTREIFEN no more to paint only a small area but the whole paper tape,
455 * simply call this function.
456 **/
457void lochstreifen_remove_clip(LOCHSTREIFEN *l) {
458        if(l->clip != NULL)
459                free(l->clip);
460        l->clip = NULL; // not neccessary, I suppose.
461}
462
463/**
464 * Affect the current transformation matrix so the papertape finally has a length of
465 * exactly the value given in the argument, in pixels. The length depends from the
466 * number of rows on the papertape, so the length will increase if you increase the
467 * number of rows after calling lochstreifen_set_scaling_by_length.
468 **/
469void lochstreifen_set_scaling_by_length(LOCHSTREIFEN *l, int length) {
470        double scale_factor = ((double)length) / LOCHSTREIFEN_LENGTH;
471        fprintf(stderr, "Scaling to length=%i, having length=%f, getting %f as scale factor\n",
472                length, LOCHSTREIFEN_LENGTH, scale_factor);
473        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
474        l->matrix.x0 *= scale_factor; l->matrix.y0 *= scale_factor;
475        lochstreifen_check_matrix(l);
476}
477
478void lochstreifen_set_scaling_by_width(LOCHSTREIFEN *l, int width) {
479        double scale_factor = ((double)width) / LOCHSTREIFEN_WIDTH;
480        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
481        l->matrix.x0 *= scale_factor; l->matrix.y0 *= scale_factor;
482        lochstreifen_check_matrix(l);
483}
484
485void lochstreifen_set_scaling_by_code_hole(LOCHSTREIFEN *l, int diameter) {
486        double scale_factor = ((double)diameter)/2 / LOCHSTREIFEN_RADIUS_CODE_HOLE;
487        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
488        l->matrix.x0 *= scale_factor; l->matrix.y0 *= scale_factor;
489        lochstreifen_check_matrix(l);
490}
491
492/**
493 * Get the dimensions of the paper tape in the output image. Be careful: While
494 * width/length are well defined in the LOCHSTREIFEN process, hereby width and
495 * height mean the dimensions of the rectangular surface picture.
496 * If you need only the value of one dimension, use
497 * lochstreifen_get_target_width or lochstreifen_get_target_height, respectively.
498 *
499 * The calculation of these values is quite expensive, so you do well when
500 * caching the results.
501 * @return nothing, because Call-by-Reference.
502 **/
503void lochstreifen_get_target_dimensions(LOCHSTREIFEN *l, int *width, int *height) {
504        int i;
505        // preset the dimensions to 0
506        if(width  != NULL)  *width = 0;
507        if(height != NULL)  *height = 0;
508        // define the "bounding box" where the papertape resides in
509        // user space cordinates (like lochstreifen_draw paints)
510        double x[] = { 0, LOCHSTREIFEN_LENGTH,                  0, LOCHSTREIFEN_LENGTH };
511        double y[] = { 0,                   0, LOCHSTREIFEN_WIDTH, LOCHSTREIFEN_WIDTH  };
512        // transform the "bounding box" throught the matrix
513        for(i=0; i < (sizeof(x) / sizeof(double)); i++) {
514                cairo_matrix_transform_point(&l->matrix, &x[i], &y[i]);
515                // store biggest x/y values
516                if(width != NULL && x[i] > *width)
517                        *width = x[i];
518                if(height != NULL && y[i] > *height)
519                        *height = y[i];
520        }
521}
522
523/**
524 * Get width of the output picture. This is only a nicer frontend to
525 * lochstreifen_get_target_dimensions that returns the width value.
526 **/
527int lochstreifen_get_target_width(LOCHSTREIFEN *l) {
528        int width;
529        lochstreifen_get_target_dimensions(l, &width, NULL);
530        return width;
531}
532
533/**
534 * Get height of the output picture. This is only a nicer frontend to
535 * lochstreifen_get_target_dimensions that returns the height value.
536 **/
537int lochstreifen_get_target_height(LOCHSTREIFEN *l) {
538        int height;
539        lochstreifen_get_target_dimensions(l, NULL, &height);
540        return height;
541}
542
543/**
544 * Get a bit tuple from the positions on the target surface. This function
545 * translates the points x/y with the inverse current translation matrix. Then
546 * it calculates the row number that equals with the x coordinate and afterwards
547 * the track number that equals with the y coordinate.
548 *
549 * If you need only one dimension, set the other NULL.
550 *
551 * You can use lochstreifen_get_traget_row_from_point and
552 * lochstreifen_get_target_track_from_point if you need only one dimension. Furthermore
553 * they will nicely return the requested data -- no need for call by reference.
554 *
555 * @param bit The target lochstreifen_bit_t (call by reference). Should not be NULL.
556 * @param x  The x coordinate you want to check
557 * @param y  The y coordinate you want to check
558 **/
559void lochstreifen_get_target_bit_from_point(LOCHSTREIFEN* l, row_t* row, track_t* track, double x, double y) {
560        cairo_matrix_transform_point(&l->matrix_inverse, &x, &y); // transform points
561       
562        if(y < 0 || y > LOCHSTREIFEN_WIDTH) {
563                // we are not at data, at all
564                if(row != NULL)
565                        *row = LOCHSTREIFEN_NO_ROW;
566                if(track != NULL)
567                        *track = LOCHSTREIFEN_NO_TRACK;
568                return;
569        }
570       
571        if(row == NULL) {
572                // if only the track was requested, create a temporary row buffer, because
573                // we need the row number as a basis for the correct track calculation.
574                row_t row_buffer;
575                row = &row_buffer;
576        }
577       
578        // Row calculation is fairly easy:
579        if(x < LOCHSTREIFEN_TEAR_OFF_LENGTH || x > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH)) {
580                // we are not at data
581                *row = LOCHSTREIFEN_NO_ROW;
582        } else {
583                // getting the row from the x value. This is pretty much the same algorithm
584                // like used in papertape_draw for checking up the rectangle bounding box (clipping).
585                *row = floor(
586                        (x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
587                );
588        }
589       
590        // Track calculation is much more complex:
591        if(track != NULL) {
592                /* get the track from the y value. This is not simply something like
593                 *   *track = floor( y / LOCHSTREIFEN_TRACK_DISTANCE );
594                 * because we must take care that a track is a round thing with a
595                 * specific height.
596                 */
597                double M_x, M_y; // the centers of the circles
598                int i; // some iterator
599                // The center of this circle, fairly complex to calculate, see lochstreifen_draw.
600                M_x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (*row + 0.5) * LOCHSTREIFEN_ROW_DISTANCE;
601                M_y = LOCHSTREIFEN_TRACK_DISTANCE; // the first track, to get onto papertape
602                // this a genious canidate for loop unrolling:
603                for(i=0; i < 8; i++) {
604                        // simple linear algebra: Check if point is in the circle.
605                        //fprintf(stderr, "bit %d: (%f|%f) <-> P(%f|%f) <= %f\n", i, M_x, M_y, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE);
606                        if(hypot( (x-M_x) , (y-M_y) ) <= LOCHSTREIFEN_RADIUS_CODE_HOLE) {
607                                // the point is in this circle, so we have found the correct track.
608                                //fprintf(stderr, "FOUND %d\n", i);
609                                *track = i;
610                                return;
611                        } else {
612                                // go on, set M for next circle
613                                M_y += LOCHSTREIFEN_TRACK_DISTANCE;
614                                // jump over feed track before i == 3.
615                                if(i == 2)
616                                        M_y += LOCHSTREIFEN_TRACK_DISTANCE;
617                        }
618                } // for tracks
619               
620                // cursor not over circle:
621                *track = LOCHSTREIFEN_NO_TRACK;
622                //fprintf(stderr, "NOT FOUND TRACK.\n");
623        } // y dimension
624} // lochstreifen_get_target_bit_on_point
625
626/**
627 * Get the row number from an x/y target coordinate. That is: At first, translate
628 * the points with the current translation matrix, afterwards calculate the row
629 * number for the position.
630 *
631 * @returns Row number, counting from zero. Negative value if point not on papertape data chunk.
632 **/
633row_t lochstreifen_get_target_row_from_point(LOCHSTREIFEN* l, double x, double y) {
634        row_t row;
635        lochstreifen_get_target_bit_from_point(l, &row, NULL, x, y);
636        return row;
637}
638
639track_t lochstreifen_get_target_track_from_point(LOCHSTREIFEN* l, double x, double y) {
640        track_t track;
641        lochstreifen_get_target_bit_from_point(l, NULL, &track, x, y);
642        return track;
643}
644
645
646/**
647 * Calculate the rectangle that contains the complete row on the LOCHSTREIFEN,
648 * in target surface dimensions (using the transformation matrix). If the operation
649 * fails (if the row doesn't exist), the rect is not touched at all.
650 *
651 * @param row Number of Row you want
652 * @param rect Call by reference like rectangle type
653 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW.
654 **/
655int lochstreifen_get_target_rect_from_row(LOCHSTREIFEN* l, row_t row, cairo_rectangle_t* rect) {
656        // check wheter we are in bounds
657        if(row < 0 || row > l->data_length)
658                return LOCHSTREIFEN_NO_ROW;
659
660        // This rectangle is a bit bigger than the real track, so that outlines, etc.
661        // could be drawn there, too.
662       
663        rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
664        rect->y = 0;
665        rect->width = LOCHSTREIFEN_ROW_DISTANCE;
666        rect->height = LOCHSTREIFEN_WIDTH;
667       
668        // take this as padding:
669        #define TARGET_RECT_HOW_MUCH_BIGGER  (LOCHSTREIFEN_ROW_DISTANCE * 0.1)
670        rect->x -= TARGET_RECT_HOW_MUCH_BIGGER;
671        rect->y -= TARGET_RECT_HOW_MUCH_BIGGER;
672        rect->width += 2*TARGET_RECT_HOW_MUCH_BIGGER;
673        rect->height += 2*TARGET_RECT_HOW_MUCH_BIGGER;
674       
675        // transform the points
676        cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
677        cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
678       
679        return LOCHSTREIFEN_SUCCESS;
680}
681
682/**
683 * This is just the same like lochstreifen_get_target_rect_from_row, just with
684 * bits. This is still a rectangle which contains the complete circle that represents
685 * the bit, useful e.g. for GDK expose events.
686 *
687 * @param row Number of Row
688 * @param track Number of Track
689 * @param rect Call by reference like rectangle type
690 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW or LOCHSTREIFEN_NO_TRACK
691 **/
692int lochstreifen_get_target_rect_from_bit(LOCHSTREIFEN* l, row_t row, track_t track, cairo_rectangle_t* rect) {
693        // check bounds
694        if(row < 0 || row > l->data_length)
695                return LOCHSTREIFEN_NO_ROW;
696        if(track < 0 || track > 7)
697                return LOCHSTREIFEN_NO_TRACK;
698
699        // the rectangle is doesn't touch the circle, but leaves quite a bit space around
700        // it, so outlines, etc. should not be a problem.
701
702        // this code is quite like the beginning x/y dimensions in lochstreifen_draw():
703        rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
704                  //+ (LOCHSTREIFEN_ROW_DISTANCE - LOCHSTREIFEN_RADIUS_CODE_HOLE*2) / 2; // the center
705        rect->y = LOCHSTREIFEN_TRACK_DISTANCE * (track + (track > 2 ? 1 : 0) );
706        // The correct values for width/height would be:
707        //    rect->width = LOCHSTREIFEN_RADIUS_CODE_HOLE * 2;
708        // but that won't at exposures if the hole is painted e.g. with an outline (cairo_stroke).
709        // We therefore simply take the complete track as width
710        rect->width = LOCHSTREIFEN_ROW_DISTANCE;
711        rect->height = LOCHSTREIFEN_TRACK_DISTANCE * 2;
712       
713        // transform to user space
714        cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
715        cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
716       
717        return LOCHSTREIFEN_SUCCESS;
718}
719
720/**
721 *
722 * Just to be sure that there are no misunderstandings: Rotation (wiht cairo)
723 * principally works like this:
724 *
725 *              |  B----D
726 *                 | V  |
727 *    180°      |  | V  |
728 * D--------C      | V  | 270°
729 * |>>>>>>>>|   |  | V  |
730 * B--------A      A----C
731 *              |
732 *   - - - - - -+------------------------>   x axis
733 *              |                    .
734 *      C----A  |  A------------B    .
735 *      | ^  |  |  |<<<<<<<<<<<<|    .
736 *  90° | ^  |  |  C------------D    .
737 *      | ^  |  |     original       .  <- borders of the target surface
738 *      | ^  |  |....................+
739 *      D----B  |
740 *              V
741 *           y axis
742 *
743 * Unfortunately every rotated box would be outside of the target surface
744 * (e.g. an 200x100px PNG image), thus we must move the matrix inside the
745 * positive x/y axis intercept.
746 * This can be performed quite simple
747 *
748 **/
749void lochstreifen_set_rotation(LOCHSTREIFEN *l, enum lochstreifen_direction direction) {
750        switch(direction) {
751                case LOCHSTREIFEN_MOVEMENT_TO_LEFT:
752                        // standard
753                case LOCHSTREIFEN_MOVEMENT_TO_TOP:
754                       
755                case LOCHSTREIFEN_MOVEMENT_TO_RIGHT:
756                       
757                case LOCHSTREIFEN_MOVEMENT_TO_BOTTOM:
758                       
759                case LOCHSTREIFEN_MOVEMENT_NONE:
760                default:
761                        break;
762        }
763        lochstreifen_check_matrix(l);
764}
765
766
767/**
768 * The very central main function for the LOCHSTREIFEN object: Drawing.
769 * You are supposed to create an adequate cairo context.
770 *
771 * This basic drawing function will draw relative to the width of the
772 * paper tape, like defined as 1 inch in the ECMA-10 standard. This
773 * width will have the value "1".
774 *
775 * Your cropping/... blub shall treat hereby. blabla.
776 **/
777void lochstreifen_draw(LOCHSTREIFEN *l, cairo_t *cr) {
778        // complete length of paper tape:
779        double length = LOCHSTREIFEN_LENGTH;
780
781        // iterators for the byte/bit loops
782        int row,track;
783        // position while iterating
784        double x,y;
785        // max positions to iterate to
786        int row_max, track_min, track_max;
787
788        // apply the matrix!
789        cairo_set_matrix(cr, &l->matrix);
790       
791        // check if we need to repaint the papertape
792        if(l->clip != NULL && (l->clip->y > LOCHSTREIFEN_WIDTH || l->clip->x > LOCHSTREIFEN_LENGTH) ) {
793                // Clipping region is outside of papertape!
794                //printf("draw: Outside papertape.\n");
795                return;
796        }
797
798        // paint outer background color.
799        if(l->outer_background_color != NULL) {
800                cairo_set_source(cr, l->outer_background_color);
801                cairo_paint(cr);
802        }
803
804        // paint the paper tape outline, including the tear-off
805        if(l->papertape_background_color != NULL) {
806                cairo_set_source(cr, l->papertape_background_color);
807
808                // tear-off at the beginning (0.5 of paper tape width)
809                cairo_move_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.8, 0);
810                cairo_line_to(cr, 0, 4*LOCHSTREIFEN_TRACK_DISTANCE);
811                cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
812                cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
813                cairo_close_path(cr);
814                cairo_fill(cr);
815               
816                // long rectangular area over complete paper tape
817                if(l->clip == NULL) {
818                        cairo_rectangle(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0.0,
819                                length - 2*LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
820                        // irc.gnome.org, #gtk+:
821                        // <owen> sven__: a basic comment is that if you draw a *single* shape with cairo
822                        // that's bigger than 32k pixels in any dimension, it's unlikely to work
823                        // [17:51] <owen> sven__: for the Xlib backend. this is a lot more fixable now
824                        // that Cairo switched to 24.8 fixed point internally, but I don't think anybody
825                        // has done the work to fix it
826                } else {
827                        // calculate the neccessary width
828                        // if clipping region exceeds lochstreifen width.... make it shorter, else let it.
829                        double width = ( (l->clip->x + l->clip->width) > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
830                                        ? ( LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH - l->clip->x )
831                                        : l->clip->width;
832                        // don't have the clipping region paint in the tear off
833                        // right, so crop it at the left.
834                        if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH)
835                                width -= LOCHSTREIFEN_TEAR_OFF_LENGTH;
836                        cairo_rectangle(cr,
837                                (l->clip->x >= LOCHSTREIFEN_TEAR_OFF_LENGTH) ? 
838                                        l->clip->x : LOCHSTREIFEN_TEAR_OFF_LENGTH,
839                                0,
840                                width,
841                                LOCHSTREIFEN_WIDTH);
842                }
843                cairo_fill(cr);
844               
845
846                // tear-off at the end (0.5 of paper tape width)
847                cairo_move_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
848                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
849                cairo_line_to(cr, length, LOCHSTREIFEN_WIDTH);
850                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 4*LOCHSTREIFEN_TRACK_DISTANCE);
851                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.2, 0);
852                cairo_close_path(cr);
853                cairo_fill(cr);
854               
855        } // paper tape background finished.
856       
857        // paint feed holes in the tear-off at the beginning
858        // only if in clipped area
859        if( ( l->clip == NULL || (l->clip->x <= LOCHSTREIFEN_TEAR_OFF_LENGTH) )
860         && (l->feed_hole_color != NULL) ) {
861                cairo_set_source(cr, l->feed_hole_color);
862                for(y=4*LOCHSTREIFEN_TRACK_DISTANCE,
863                    x=LOCHSTREIFEN_TEAR_OFF_LENGTH - LOCHSTREIFEN_ROW_DISTANCE/2;
864                    x > 0; x -= LOCHSTREIFEN_ROW_DISTANCE)
865                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
866                cairo_fill(cr);
867        }
868
869       
870        if(l->clip != NULL) {
871                /* calculate clipping in dimension X (rows, bytes).
872                 * Clipping in this dimension is very important, especially at very long
873                 * papertapes. Application software (like GtkPaperTape) usually zooms the
874                 * papertape, so it won't fit completely on the screen. So skipping bytes
875                 * which are not displayed is very performant.
876                 */
877                if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH) {
878                        // begin before tear off
879                        row = 0;
880                        row_max = floor(
881                                ( l->clip->width - (l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
882                                / LOCHSTREIFEN_ROW_DISTANCE
883                        );
884                } else {
885                        // after tear off
886                        row = floor (
887                                (l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
888                        );
889                       
890                        // end with just one more row as neccessary
891                        row_max = row + 1 + ceil(
892                                ( l->clip->width ) / LOCHSTREIFEN_ROW_DISTANCE
893                        );
894                }
895               
896                // don't go over the data range
897                if(row_max > l->data_length)
898                        row_max = l->data_length;
899                // dont' have row_max smaller than row
900                //if(row_max < row)
901                //      row_max = row;
902
903                /* calculate clipping in dimension Y (tracks, bits)
904                 * Y clipping is not as important, because there are only 9 circles
905                 * to paint. The feed hole (which is painted just in the loop for the
906                 * 3. track hole is not counted, therefore the calculations become somewhat
907                 * inneccessary complex, compared to the painting overhead for the complete
908                 * 8 tracks instead of e.g. only 6 tracks. Apart from that, application software
909                 * usually doesn't clip the papertape this way, because users usually want to
910                 * see a complete byte and not only a half byte.
911                 */
912                // calculation of first track to begin with.
913                /*
914                track_min = l->clip->y == 0 ? 0 : floor( l->clip->y / LOCHSTREIFEN_TRACK_DISTANCE );
915               
916               
917                // calculation of last track to end with.
918                if(l->clip->y + l->clip->height > LOCHSTREIFEN_WIDTH)
919                        track_max = 8; // clipping region is *much* bigger than the papertape width
920                else {
921                        // just paint one more than neccessary
922                        track_max = 1 + 8 - floor(
923                                (LOCHSTREIFEN_WIDTH - (l->clip->y + l->clip->height))
924                                / LOCHSTREIFEN_TRACK_DISTANCE
925                        );
926                        // dont't go over the track range
927                        if(track_max > 8)
928                                track_max = 8;
929                }*/
930                track_min = 0;
931                track_max = 8;
932        } else {
933                // no clipping. Paint *everything*.
934                row = 0;
935                row_max = l->data_length;
936               
937                track_min = 0;
938                track_max = 8;
939        }
940       
941        if(l->clip != NULL) {
942                double x,y,width,height;
943                x=l->clip->x; y=l->clip->y; width=l->clip->width; height=l->clip->height;
944                cairo_user_to_device(cr, &x, &y);
945                cairo_user_to_device_distance(cr, &width, &height);
946                fprintf(stderr, "draw: %i|%i %ix%i ", (int)x, (int)y, (int)width, (int)height);
947        } else   fprintf(stderr, "draw: ");
948        fprintf(stderr, "row %i->%i track %i->%i\n", row, row_max, track_min, track_max);
949       
950        // loop all the rows (bytes)
951        // x start: tear off + one half row offset + offset rows * length of row
952        for(x=LOCHSTREIFEN_TEAR_OFF_LENGTH + LOCHSTREIFEN_ROW_DISTANCE/2 + row*LOCHSTREIFEN_ROW_DISTANCE;
953            row < row_max; row++, x += LOCHSTREIFEN_ROW_DISTANCE) {
954
955                // call row callback
956                if(l->row_callback != NULL) {
957                        (*l->row_callback) (&row, cr, l->row_callback_user_data);
958                        // at least now that's all the magic ;)
959                }
960               
961                // highlight region?
962                if(l->highlight_region_color != NULL && l->highlight_region_start != LOCHSTREIFEN_NO_ROW &&
963                    (row >= l->highlight_region_start || row < l->highlight_region_end) ) {
964                        cairo_set_source(cr, l->highlight_region_color);
965                        // hier was schlaues schickeres als ein RECHTECK
966                        // einfallen lassen. Vielleicht mit abgerundetem
967                        // Rechteck? Wuerde zu den Löchern passen.
968                        // http://www.cairographics.org/cookbook/roundedrectangles/
969                        cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE / 2, 0,
970                           LOCHSTREIFEN_TRACK_DISTANCE * (l->highlight_region_end - l->highlight_region_start),
971                           LOCHSTREIFEN_WIDTH);
972                        cairo_fill(cr);
973                }
974               
975                // highlight (only) this row?
976                if(l->highlight_row_color != NULL && row == l->highlight_row_number) {
977                        // if check again LOCHSTREIFEN_NO_ROW not neccessary because row iterator never
978                        // gets LOCHSTREIFEN_NO_ROW.
979                        cairo_set_source(cr, l->highlight_row_color);
980                        // genauso abgerundetes Rechteck wie bei der region
981                        // überlegen.
982                        cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE/2, 0,
983                            LOCHSTREIFEN_TRACK_DISTANCE, LOCHSTREIFEN_WIDTH);
984                        cairo_fill(cr);
985                }
986               
987                // loop all the tracks (bits)
988                // y start: offset tracks * width of track + one track (to get onto the papertape)
989                for(track=track_min, y = LOCHSTREIFEN_TRACK_DISTANCE*(track+1);
990                    track < track_max; track++, y += LOCHSTREIFEN_TRACK_DISTANCE) {
991                        if(track == 3) {
992                                // the feed hole is at position 3
993                                if(l->feed_hole_color != NULL) {
994                                        // we paint a feed hole :-)
995                                        cairo_set_source(cr, l->feed_hole_color);
996                                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
997                                        cairo_fill(cr);
998                                }
999                                // jump one track, in every case.
1000                                y += 0.1;
1001                        }
1002                       
1003                        if( ((l->data[row] >> track) & 0x01) != 0x01) {
1004                                // bit is logical ZERO (0)
1005                                if(l->zero_code_hole_color != NULL) {
1006                                        // we want to paint zeros.
1007                                        cairo_set_source(cr, l->zero_code_hole_color);
1008                                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1009                                        cairo_fill(cr);
1010                                }
1011                        } else if(l->one_code_hole_color != NULL) {
1012                                // we want to paint logical ONEs.
1013                                cairo_set_source(cr, l->one_code_hole_color);
1014                                cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1015                                cairo_fill(cr);
1016                        }
1017                       
1018                        if(l->highlight_bit_row == row && l->highlight_bit_track == track
1019                           && l->highlight_bit_color != NULL) {
1020                                // if check against l->highlight_bit_row == LOCHSTREIFEN_NO_ROW
1021                                // not neccessary because row never gets LOCHSTREIFEN_NO_ROW.
1022                               
1023                                // eigentlich wollen wir hier eine Umrahmung zeichnen,
1024                                // damit der Zustand (0/1) nicht unsichtbar wird. Jetzt
1025                                // erst mal aber die Billigversion mit der dümmlichen
1026                                // Vollfarbe ;-)
1027                                cairo_set_source(cr, l->highlight_bit_color);
1028                                cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1029                                cairo_set_line_width(cr, 0.01);
1030                                cairo_stroke(cr); // mal testen :-)
1031                        }
1032                } // for tracks (bits)
1033        } // for rows (bytes)
1034} // function lochstreifen_draw
1035
1036
1037/**
1038 * Helper function: A simple closure for a cairo_surface_t (PNG and SVG) to write out data
1039 * to a C file stream. Used internally by lochstreifen_export and is not listened in lochstreifen.h.
1040 **/
1041cairo_status_t lochstreifen_export_closure(void *closure, unsigned char *data, unsigned int length) {
1042        unsigned int length_written = fwrite(data, length, 1, (FILE *)closure);
1043        cairo_status_t status = (length != length_written)
1044                ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
1045        //printf("Writing data, length %i=>%i, status %s\n", length, length_written, cairo_status_to_string(status));
1046        if(status == CAIRO_STATUS_WRITE_ERROR) {
1047                perror("Lochstreifen export closure");
1048        }
1049        return status;
1050}
1051
1052/**
1053 * Exporting the Lochstreifen. This is both done in cli.c
1054 * (actually cli.c is a wrapper around this function) and
1055 * gtkpapertapeexporter.cc, so they both rely on this function
1056 **/
1057cairo_status_t lochstreifen_export(LOCHSTREIFEN *l, lochstreifen_export_type file_type, FILE *out_stream) {
1058        cairo_t *cr;                ///< A cairo context, given from...
1059        cairo_surface_t *surface;   ///< ...this generic cairo surface
1060
1061        fprintf(stderr, "Lochstreifen: Exporting %s with %i x %i px\n",
1062                file_type == LOCHSTREIFEN_FORMAT_PNG ? "PNG" : "SVG",
1063                lochstreifen_get_target_width(l),
1064                lochstreifen_get_target_height(l)
1065        );
1066       
1067        // setting up the surface and painting...
1068        if(file_type == LOCHSTREIFEN_FORMAT_PNG) {
1069                surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1070                        lochstreifen_get_target_width(l),
1071                        lochstreifen_get_target_height(l)
1072                );
1073                cr = cairo_create(surface);
1074                lochstreifen_draw(l, cr);
1075                cairo_surface_flush(surface);
1076
1077                cairo_status_t status = cairo_surface_write_to_png_stream(surface, (cairo_write_func_t)lochstreifen_export_closure, out_stream);
1078               
1079                cairo_destroy(cr);
1080                cairo_surface_destroy(surface);
1081                return status;
1082        } else { // if(file_type == LOCHSTREIFEN_FORMAT_SVG) { // no other case possible
1083                surface = cairo_svg_surface_create_for_stream(
1084                        (cairo_write_func_t)lochstreifen_export_closure, out_stream,
1085                        lochstreifen_get_target_width(l),
1086                        lochstreifen_get_target_height(l)
1087                );
1088                cr = cairo_create(surface);
1089                lochstreifen_draw(l, cr);
1090                cairo_surface_flush(surface);
1091               
1092                cairo_status_t status = cairo_surface_status(surface);
1093               
1094                cairo_destroy(cr);
1095                cairo_surface_destroy(surface);
1096               
1097                return status;
1098        } // end case file_type
1099}
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License