source: projects/visualisator/lochstreifen.c @ 22

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

Great directory restructuring: The GtkPaperTape files are now splitted
up in their own directory and all using shorter names and labels:
PaperTape::Color in favour of GtkPaperTapeColor. Furthermore all files
are in revision progress, started a bit with zoom.h/zoom.cc and
greatly with file.h. Work still in progress -- this commit won't
compile at all!

Fixed a small bug in lochstreifen.c that caused crashing at repainting
because the callback function pointer was not initialized to NULL.

-- Sven @ workstation

File size: 38.0 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        l->row_callback               = NULL;
71        l->row_callback_user_data     = NULL;
72
73        return l;
74}
75
76/**
77 * Copy everything from the LOCHSTREIFEN template, except the data. They
78 * won't be copied, just set, because LOCHSTREIFEN doesn't manage your
79 * data. If you want to copy them, call something like
80 *   LOCHSTREIFEN* copy = lochstreifen_copy(old);
81 *   byte_t* new_data = malloc(old->data_length);
82 *   memcpy(new_data, old->data, old->data_length);
83 *   lochstreifen_set_data(copy, old->data_length, new_data);
84 **/
85LOCHSTREIFEN* lochstreifen_copy(const LOCHSTREIFEN* template) {
86        LOCHSTREIFEN *l = malloc (sizeof(LOCHSTREIFEN));
87        #define COPY_RGBA_PATTERN(name) { double r,g,b,a; \
88                if(!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        free(l->clip);
459        l->clip = NULL; // not neccessary, I suppose.
460}
461
462/**
463 * Affect the current transformation matrix so the papertape finally has a length of
464 * exactly the value given in the argument, in pixels. The length depends from the
465 * number of rows on the papertape, so the length will increase if you increase the
466 * number of rows after calling lochstreifen_set_scaling_by_length.
467 **/
468void lochstreifen_set_scaling_by_length(LOCHSTREIFEN *l, int length) {
469        double scale_factor = ((double)length) / LOCHSTREIFEN_LENGTH;
470        printf("Scaling to length=%i, having length=%f, getting %f as scale factor\n",
471                length, LOCHSTREIFEN_LENGTH, scale_factor);
472        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
473        lochstreifen_check_matrix(l);
474}
475
476void lochstreifen_set_scaling_by_width(LOCHSTREIFEN *l, int width) {
477        double scale_factor = ((double)width) / LOCHSTREIFEN_WIDTH;
478        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
479        lochstreifen_check_matrix(l);
480}
481
482void lochstreifen_set_scaling_by_code_hole(LOCHSTREIFEN *l, int diameter) {
483        double scale_factor = ((double)diameter)/2 / LOCHSTREIFEN_RADIUS_CODE_HOLE;
484        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
485        lochstreifen_check_matrix(l);
486}
487
488/**
489 * Get the dimensions of the paper tape in the output image. Be careful: While
490 * width/length are well defined in the LOCHSTREIFEN process, hereby width and
491 * height mean the dimensions of the rectangular surface picture.
492 * If you need only the value of one dimension, use
493 * lochstreifen_get_target_width or lochstreifen_get_target_height, respectively.
494 *
495 * The calculation of these values is quite expensive, so you do well when
496 * caching the results.
497 * @return nothing, because Call-by-Reference.
498 **/
499void lochstreifen_get_target_dimensions(LOCHSTREIFEN *l, int *width, int *height) {
500        int i;
501        // preset the dimensions to 0
502        if(width  != NULL)  *width = 0;
503        if(height != NULL)  *height = 0;
504        // define the "bounding box" where the papertape resides in
505        // user space cordinates (like lochstreifen_draw paints)
506        double x[] = { 0, LOCHSTREIFEN_LENGTH,                  0, LOCHSTREIFEN_LENGTH };
507        double y[] = { 0,                   0, LOCHSTREIFEN_WIDTH, LOCHSTREIFEN_WIDTH  };
508        // transform the "bounding box" throught the matrix
509        for(i=0; i < (sizeof(x) / sizeof(double)); i++) {
510                cairo_matrix_transform_point(&l->matrix, &x[i], &y[i]);
511                // store biggest x/y values
512                if(width != NULL && x[i] > *width)
513                        *width = x[i];
514                if(height != NULL && y[i] > *height)
515                        *height = y[i];
516        }
517}
518
519/**
520 * Get width of the output picture. This is only a nicer frontend to
521 * lochstreifen_get_target_dimensions that returns the width value.
522 **/
523int lochstreifen_get_target_width(LOCHSTREIFEN *l) {
524        int width;
525        lochstreifen_get_target_dimensions(l, &width, NULL);
526        return width;
527}
528
529/**
530 * Get height of the output picture. This is only a nicer frontend to
531 * lochstreifen_get_target_dimensions that returns the height value.
532 **/
533int lochstreifen_get_target_height(LOCHSTREIFEN *l) {
534        int height;
535        lochstreifen_get_target_dimensions(l, NULL, &height);
536        return height;
537}
538
539/**
540 * Get a bit tuple from the positions on the target surface. This function
541 * translates the points x/y with the inverse current translation matrix. Then
542 * it calculates the row number that equals with the x coordinate and afterwards
543 * the track number that equals with the y coordinate.
544 *
545 * If you need only one dimension, set the other NULL.
546 *
547 * You can use lochstreifen_get_traget_row_from_point and
548 * lochstreifen_get_target_track_from_point if you need only one dimension. Furthermore
549 * they will nicely return the requested data -- no need for call by reference.
550 *
551 * @param bit The target lochstreifen_bit_t (call by reference). Should not be NULL.
552 * @param x  The x coordinate you want to check
553 * @param y  The y coordinate you want to check
554 **/
555void lochstreifen_get_target_bit_from_point(LOCHSTREIFEN* l, row_t* row, track_t* track, double x, double y) {
556        cairo_matrix_transform_point(&l->matrix_inverse, &x, &y); // transform points
557       
558        if(y < 0 || y > LOCHSTREIFEN_WIDTH) {
559                // we are not at data, at all
560                if(row != NULL)
561                        *row = LOCHSTREIFEN_NO_ROW;
562                if(track != NULL)
563                        *track = LOCHSTREIFEN_NO_TRACK;
564                return;
565        }
566       
567        if(row == NULL) {
568                // if only the track was requested, create a temporary row buffer, because
569                // we need the row number as a basis for the correct track calculation.
570                row_t row_buffer;
571                row = &row_buffer;
572        }
573       
574        // Row calculation is fairly easy:
575        if(x < LOCHSTREIFEN_TEAR_OFF_LENGTH || x > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH)) {
576                // we are not at data
577                *row = LOCHSTREIFEN_NO_ROW;
578        } else {
579                // getting the row from the x value. This is pretty much the same algorithm
580                // like used in papertape_draw for checking up the rectangle bounding box (clipping).
581                *row = floor(
582                        (x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
583                );
584        }
585       
586        // Track calculation is much more complex:
587        if(track != NULL) {
588                /* get the track from the y value. This is not simply something like
589                 *   *track = floor( y / LOCHSTREIFEN_TRACK_DISTANCE );
590                 * because we must take care that a track is a round thing with a
591                 * specific height.
592                 */
593                double M_x, M_y; // the centers of the circles
594                int i; // some iterator
595                // The center of this circle, fairly complex to calculate, see lochstreifen_draw.
596                M_x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (*row + 0.5) * LOCHSTREIFEN_ROW_DISTANCE;
597                M_y = LOCHSTREIFEN_TRACK_DISTANCE; // the first track, to get onto papertape
598                // this a genious canidate for loop unrolling:
599                for(i=0; i < 8; i++) {
600                        // simple linear algebra: Check if point is in the circle.
601                        //fprintf(stderr, "bit %d: (%f|%f) <-> P(%f|%f) <= %f\n", i, M_x, M_y, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE);
602                        if(hypot( (x-M_x) , (y-M_y) ) <= LOCHSTREIFEN_RADIUS_CODE_HOLE) {
603                                // the point is in this circle, so we have found the correct track.
604                                //fprintf(stderr, "FOUND %d\n", i);
605                                *track = i;
606                                return;
607                        } else {
608                                // go on, set M for next circle
609                                M_y += LOCHSTREIFEN_TRACK_DISTANCE;
610                                // jump over feed track before i == 3.
611                                if(i == 2)
612                                        M_y += LOCHSTREIFEN_TRACK_DISTANCE;
613                        }
614                } // for tracks
615               
616                // cursor not over circle:
617                *track = LOCHSTREIFEN_NO_TRACK;
618                //fprintf(stderr, "NOT FOUND TRACK.\n");
619        } // y dimension
620} // lochstreifen_get_target_bit_on_point
621
622/**
623 * Get the row number from an x/y target coordinate. That is: At first, translate
624 * the points with the current translation matrix, afterwards calculate the row
625 * number for the position.
626 *
627 * @returns Row number, counting from zero. Negative value if point not on papertape data chunk.
628 **/
629row_t lochstreifen_get_target_row_from_point(LOCHSTREIFEN* l, double x, double y) {
630        row_t row;
631        lochstreifen_get_target_bit_from_point(l, &row, NULL, x, y);
632        return row;
633}
634
635track_t lochstreifen_get_target_track_from_point(LOCHSTREIFEN* l, double x, double y) {
636        track_t track;
637        lochstreifen_get_target_bit_from_point(l, NULL, &track, x, y);
638        return track;
639}
640
641
642/**
643 * Calculate the rectangle that contains the complete row on the LOCHSTREIFEN,
644 * in target surface dimensions (using the transformation matrix). If the operation
645 * fails (if the row doesn't exist), the rect is not touched at all.
646 *
647 * @param row Number of Row you want
648 * @param rect Call by reference like rectangle type
649 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW.
650 **/
651int lochstreifen_get_target_rect_from_row(LOCHSTREIFEN* l, row_t row, cairo_rectangle_t* rect) {
652        // check wheter we are in bounds
653        if(row < 0 || row > l->data_length)
654                return LOCHSTREIFEN_NO_ROW;
655
656        // This rectangle is a bit bigger than the real track, so that outlines, etc.
657        // could be drawn there, too.
658       
659        rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
660        rect->y = 0;
661        rect->width = LOCHSTREIFEN_ROW_DISTANCE;
662        rect->height = LOCHSTREIFEN_WIDTH;
663       
664        // take this as padding:
665        #define TARGET_RECT_HOW_MUCH_BIGGER  (LOCHSTREIFEN_ROW_DISTANCE * 0.1)
666        rect->x -= TARGET_RECT_HOW_MUCH_BIGGER;
667        rect->y -= TARGET_RECT_HOW_MUCH_BIGGER;
668        rect->width += 2*TARGET_RECT_HOW_MUCH_BIGGER;
669        rect->height += 2*TARGET_RECT_HOW_MUCH_BIGGER;
670       
671        // transform the points
672        cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
673        cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
674       
675        return LOCHSTREIFEN_SUCCESS;
676}
677
678/**
679 * This is just the same like lochstreifen_get_target_rect_from_row, just with
680 * bits. This is still a rectangle which contains the complete circle that represents
681 * the bit, useful e.g. for GDK expose events.
682 *
683 * @param row Number of Row
684 * @param track Number of Track
685 * @param rect Call by reference like rectangle type
686 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW or LOCHSTREIFEN_NO_TRACK
687 **/
688int lochstreifen_get_target_rect_from_bit(LOCHSTREIFEN* l, row_t row, track_t track, cairo_rectangle_t* rect) {
689        // check bounds
690        if(row < 0 || row > l->data_length)
691                return LOCHSTREIFEN_NO_ROW;
692        if(track < 0 || track > 7)
693                return LOCHSTREIFEN_NO_TRACK;
694
695        // the rectangle is doesn't touch the circle, but leaves quite a bit space around
696        // it, so outlines, etc. should not be a problem.
697
698        // this code is quite like the beginning x/y dimensions in lochstreifen_draw():
699        rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
700                  //+ (LOCHSTREIFEN_ROW_DISTANCE - LOCHSTREIFEN_RADIUS_CODE_HOLE*2) / 2; // the center
701        rect->y = LOCHSTREIFEN_TRACK_DISTANCE * (track + (track > 2 ? 1 : 0) );
702        // The correct values for width/height would be:
703        //    rect->width = LOCHSTREIFEN_RADIUS_CODE_HOLE * 2;
704        // but that won't at exposures if the hole is painted e.g. with an outline (cairo_stroke).
705        // We therefore simply take the complete track as width
706        rect->width = LOCHSTREIFEN_ROW_DISTANCE;
707        rect->height = LOCHSTREIFEN_TRACK_DISTANCE * 2;
708       
709        // transform to user space
710        cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
711        cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
712       
713        return LOCHSTREIFEN_SUCCESS;
714}
715
716/**
717 *
718 * Just to be sure that there are no misunderstandings: Rotation (wiht cairo)
719 * principally works like this:
720 *
721 *              |  B----D
722 *                 | V  |
723 *    180°      |  | V  |
724 * D--------C      | V  | 270°
725 * |>>>>>>>>|   |  | V  |
726 * B--------A      A----C
727 *              |
728 *   - - - - - -+------------------------>   x axis
729 *              |                    .
730 *      C----A  |  A------------B    .
731 *      | ^  |  |  |<<<<<<<<<<<<|    .
732 *  90° | ^  |  |  C------------D    .
733 *      | ^  |  |     original       .  <- borders of the target surface
734 *      | ^  |  |....................+
735 *      D----B  |
736 *              V
737 *           y axis
738 *
739 * Unfortunately every rotated box would be outside of the target surface
740 * (e.g. an 200x100px PNG image), thus we must move the matrix inside the
741 * positive x/y axis intercept.
742 * This can be performed quite simple
743 *
744 **/
745void lochstreifen_set_rotation(LOCHSTREIFEN *l, enum lochstreifen_direction direction) {
746        switch(direction) {
747                case LOCHSTREIFEN_MOVEMENT_TO_LEFT:
748                        // standard
749                case LOCHSTREIFEN_MOVEMENT_TO_TOP:
750                        //cairo_matrix_rotate();
751                case LOCHSTREIFEN_MOVEMENT_TO_RIGHT:
752                       
753                case LOCHSTREIFEN_MOVEMENT_TO_BOTTOM:
754               
755                case LOCHSTREIFEN_MOVEMENT_NONE:
756                default:
757                        break;
758        }
759        lochstreifen_check_matrix(l);
760}
761
762
763/**
764 * The very central main function for the LOCHSTREIFEN object: Drawing.
765 * You are supposed to create an adequate cairo context.
766 *
767 * This basic drawing function will draw relative to the width of the
768 * paper tape, like defined as 1 inch in the ECMA-10 standard. This
769 * width will have the value "1".
770 *
771 * Your cropping/... blub shall treat hereby. blabla.
772 **/
773void lochstreifen_draw(LOCHSTREIFEN *l, cairo_t *cr) {
774        // complete length of paper tape:
775        double length = LOCHSTREIFEN_LENGTH;
776
777        // iterators for the byte/bit loops
778        int row,track;
779        // position while iterating
780        double x,y;
781        // max positions to iterate to
782        int row_max, track_min, track_max;
783
784        // apply the matrix!
785        cairo_set_matrix(cr, &l->matrix);
786       
787        // check if we need to repaint the papertape
788        if(l->clip != NULL && (l->clip->y > LOCHSTREIFEN_WIDTH || l->clip->x > LOCHSTREIFEN_LENGTH) ) {
789                // Clipping region is outside of papertape!
790                //printf("draw: Outside papertape.\n");
791                return;
792        }
793
794        // paint outer background color.
795        if(l->outer_background_color != NULL) {
796                cairo_set_source(cr, l->outer_background_color);
797                cairo_paint(cr);
798        }
799
800        // paint the paper tape outline, including the tear-off
801        if(l->papertape_background_color != NULL) {
802                cairo_set_source(cr, l->papertape_background_color);
803
804                // tear-off at the beginning (0.5 of paper tape width)
805                cairo_move_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.8, 0);
806                cairo_line_to(cr, 0, 4*LOCHSTREIFEN_TRACK_DISTANCE);
807                cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
808                cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
809                cairo_close_path(cr);
810                cairo_fill(cr);
811               
812                // long rectangular area over complete paper tape
813                if(l->clip == NULL) {
814                        cairo_rectangle(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0.0,
815                                length - 2*LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
816                        // irc.gnome.org, #gtk+:
817                        // <owen> sven__: a basic comment is that if you draw a *single* shape with cairo
818                        // that's bigger than 32k pixels in any dimension, it's unlikely to work
819                        // [17:51] <owen> sven__: for the Xlib backend. this is a lot more fixable now
820                        // that Cairo switched to 24.8 fixed point internally, but I don't think anybody
821                        // has done the work to fix it
822                } else {
823                        // calculate the neccessary width
824                        // if clipping region exceeds lochstreifen width.... make it shorter, else let it.
825                        double width = ( (l->clip->x + l->clip->width) > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
826                                        ? ( LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH - l->clip->x )
827                                        : l->clip->width;
828                        // don't have the clipping region paint in the tear off
829                        // right, so crop it at the left.
830                        if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH)
831                                width -= LOCHSTREIFEN_TEAR_OFF_LENGTH;
832                        cairo_rectangle(cr,
833                                (l->clip->x >= LOCHSTREIFEN_TEAR_OFF_LENGTH) ? 
834                                        l->clip->x : LOCHSTREIFEN_TEAR_OFF_LENGTH,
835                                0,
836                                width,
837                                LOCHSTREIFEN_WIDTH);
838                }
839                cairo_fill(cr);
840               
841
842                // tear-off at the end (0.5 of paper tape width)
843                cairo_move_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
844                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
845                cairo_line_to(cr, length, LOCHSTREIFEN_WIDTH);
846                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 4*LOCHSTREIFEN_TRACK_DISTANCE);
847                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.2, 0);
848                cairo_close_path(cr);
849                cairo_fill(cr);
850               
851        } // paper tape background finished.
852       
853        // paint feed holes in the tear-off at the beginning
854        // only if in clipped area
855        if( ( l->clip == NULL || (l->clip->x <= LOCHSTREIFEN_TEAR_OFF_LENGTH) )
856         && (l->feed_hole_color != NULL) ) {
857                cairo_set_source(cr, l->feed_hole_color);
858                for(y=4*LOCHSTREIFEN_TRACK_DISTANCE,
859                    x=LOCHSTREIFEN_TEAR_OFF_LENGTH - LOCHSTREIFEN_ROW_DISTANCE/2;
860                    x > 0; x -= LOCHSTREIFEN_ROW_DISTANCE)
861                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
862                cairo_fill(cr);
863        }
864
865       
866        if(l->clip != NULL) {
867                /* calculate clipping in dimension X (rows, bytes).
868                 * Clipping in this dimension is very important, especially at very long
869                 * papertapes. Application software (like GtkPaperTape) usually zooms the
870                 * papertape, so it won't fit completely on the screen. So skipping bytes
871                 * which are not displayed is very performant.
872                 */
873                if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH) {
874                        // begin before tear off
875                        row = 0;
876                        row_max = floor(
877                                ( l->clip->width - (l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
878                                / LOCHSTREIFEN_ROW_DISTANCE
879                        );
880                } else {
881                        // after tear off
882                        row = floor (
883                                (l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
884                        );
885                       
886                        // end with just one more row as neccessary
887                        row_max = row + 1 + ceil(
888                                ( l->clip->width ) / LOCHSTREIFEN_ROW_DISTANCE
889                        );
890                }
891               
892                // don't go over the data range
893                if(row_max > l->data_length)
894                        row_max = l->data_length;
895                // dont' have row_max smaller than row
896                //if(row_max < row)
897                //      row_max = row;
898
899                /* calculate clipping in dimension Y (tracks, bits)
900                 * Y clipping is not as important, because there are only 9 circles
901                 * to paint. The feed hole (which is painted just in the loop for the
902                 * 3. track hole is not counted, therefore the calculations become somewhat
903                 * inneccessary complex, compared to the painting overhead for the complete
904                 * 8 tracks instead of e.g. only 6 tracks. Apart from that, application software
905                 * usually doesn't clip the papertape this way, because users usually want to
906                 * see a complete byte and not only a half byte.
907                 */
908                // calculation of first track to begin with.
909                /*
910                track_min = l->clip->y == 0 ? 0 : floor( l->clip->y / LOCHSTREIFEN_TRACK_DISTANCE );
911               
912               
913                // calculation of last track to end with.
914                if(l->clip->y + l->clip->height > LOCHSTREIFEN_WIDTH)
915                        track_max = 8; // clipping region is *much* bigger than the papertape width
916                else {
917                        // just paint one more than neccessary
918                        track_max = 1 + 8 - floor(
919                                (LOCHSTREIFEN_WIDTH - (l->clip->y + l->clip->height))
920                                / LOCHSTREIFEN_TRACK_DISTANCE
921                        );
922                        // dont't go over the track range
923                        if(track_max > 8)
924                                track_max = 8;
925                }*/
926                track_min = 0;
927                track_max = 8;
928        } else {
929                // no clipping. Paint *everything*.
930                row = 0;
931                row_max = l->data_length;
932               
933                track_min = 0;
934                track_max = 8;
935        }
936       
937        if(l->clip != NULL) {
938                double x,y,width,height;
939                x=l->clip->x; y=l->clip->y; width=l->clip->width; height=l->clip->height;
940                cairo_user_to_device(cr, &x, &y);
941                cairo_user_to_device_distance(cr, &width, &height);
942                printf("draw: %i|%i %ix%i ", (int)x, (int)y, (int)width, (int)height);
943        } else    printf("draw: ");
944        printf("row %i->%i track %i->%i\n", row, row_max, track_min, track_max);
945       
946        // loop all the rows (bytes)
947        // x start: tear off + one half row offset + offset rows * length of row
948        for(x=LOCHSTREIFEN_TEAR_OFF_LENGTH + LOCHSTREIFEN_ROW_DISTANCE/2 + row*LOCHSTREIFEN_ROW_DISTANCE;
949            row < row_max; row++, x += LOCHSTREIFEN_ROW_DISTANCE) {
950
951                // call row callback
952                if(l->row_callback != NULL) {
953                        printf("CALLING ROW CALLBACK!\n");
954                        (*l->row_callback) (&row, cr, l->row_callback_user_data);
955                        // at least now that's all the magic ;)
956                }
957               
958                // highlight region?
959                if(l->highlight_region_color != NULL && l->highlight_region_start != LOCHSTREIFEN_NO_ROW &&
960                    (row >= l->highlight_region_start || row < l->highlight_region_end) ) {
961                        cairo_set_source(cr, l->highlight_region_color);
962                        // hier was schlaues schickeres als ein RECHTECK
963                        // einfallen lassen. Vielleicht mit abgerundetem
964                        // Rechteck? Wuerde zu den Löchern passen.
965                        // http://www.cairographics.org/cookbook/roundedrectangles/
966                        cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE / 2, 0,
967                           LOCHSTREIFEN_TRACK_DISTANCE * (l->highlight_region_end - l->highlight_region_start),
968                           LOCHSTREIFEN_WIDTH);
969                        cairo_fill(cr);
970                }
971               
972                // highlight (only) this row?
973                if(l->highlight_row_color != NULL && row == l->highlight_row_number) {
974                        // if check again LOCHSTREIFEN_NO_ROW not neccessary because row iterator never
975                        // gets LOCHSTREIFEN_NO_ROW.
976                        cairo_set_source(cr, l->highlight_row_color);
977                        // genauso abgerundetes Rechteck wie bei der region
978                        // überlegen.
979                        cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE/2, 0,
980                            LOCHSTREIFEN_TRACK_DISTANCE, LOCHSTREIFEN_WIDTH);
981                        cairo_fill(cr);
982                }
983               
984                // loop all the tracks (bits)
985                // y start: offset tracks * width of track + one track (to get onto the papertape)
986                for(track=track_min, y = LOCHSTREIFEN_TRACK_DISTANCE*(track+1);
987                    track < track_max; track++, y += LOCHSTREIFEN_TRACK_DISTANCE) {
988                        if(track == 3) {
989                                // the feed hole is at position 3
990                                if(l->feed_hole_color != NULL) {
991                                        // we paint a feed hole :-)
992                                        cairo_set_source(cr, l->feed_hole_color);
993                                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
994                                        cairo_fill(cr);
995                                }
996                                // jump one track, in every case.
997                                y += 0.1;
998                        }
999                       
1000                        printf("testing byte\n");
1001                        if( ((l->data[row] >> track) & 0x01) != 0x01) {
1002                                // bit is logical ZERO (0)
1003                                if(l->zero_code_hole_color != NULL) {
1004                                        // we want to paint zeros.
1005                                        cairo_set_source(cr, l->zero_code_hole_color);
1006                                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1007                                        cairo_fill(cr);
1008                                }
1009                        } else if(l->one_code_hole_color != NULL) {
1010                                // we want to paint logical ONEs.
1011                                cairo_set_source(cr, l->one_code_hole_color);
1012                                cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1013                                cairo_fill(cr);
1014                        }
1015                       
1016                        if(l->highlight_bit_row == row && l->highlight_bit_track == track
1017                           && l->highlight_bit_color != NULL) {
1018                                // if check against l->highlight_bit_row == LOCHSTREIFEN_NO_ROW
1019                                // not neccessary because row never gets LOCHSTREIFEN_NO_ROW.
1020                               
1021                                // eigentlich wollen wir hier eine Umrahmung zeichnen,
1022                                // damit der Zustand (0/1) nicht unsichtbar wird. Jetzt
1023                                // erst mal aber die Billigversion mit der dümmlichen
1024                                // Vollfarbe ;-)
1025                                cairo_set_source(cr, l->highlight_bit_color);
1026                                cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1027                                cairo_set_line_width(cr, 0.01);
1028                                cairo_stroke(cr); // mal testen :-)
1029                        }
1030                } // for tracks (bits)
1031        } // for rows (bytes)
1032} // function lochstreifen_draw
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