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