[1] | 1 | /** |
---|
[18] | 2 | * The Paper Tape Project -- Visualisation sub project |
---|
| 3 | * lochstreifen.c - Painting Paper Tapes according to ECMA-10, with cairographics. |
---|
| 4 | * $Id$ |
---|
[1] | 5 | * |
---|
[18] | 6 | * This is a rewrite from the famous Cairo paper tape drawing routines. This |
---|
| 7 | * c file implements a (simple) object called "LOCHSTREIFEN". |
---|
[1] | 8 | * |
---|
| 9 | * |
---|
| 10 | * |
---|
| 11 | * |
---|
| 12 | * |
---|
[18] | 13 | * (c) Copyright 2007, 2008 Sven Köppel |
---|
[1] | 14 | * |
---|
[18] | 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 | * |
---|
[1] | 31 | **/ |
---|
[18] | 32 | |
---|
[1] | 33 | #include <math.h> |
---|
| 34 | #include <stdio.h> |
---|
| 35 | #include <stdlib.h> |
---|
| 36 | #include <string.h> |
---|
| 37 | #include "lochstreifen.h" |
---|
| 38 | |
---|
[18] | 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)); |
---|
[1] | 50 | |
---|
[18] | 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; |
---|
[19] | 68 | cairo_matrix_init(&l->matrix, 1, 0, 0, 1, 0, 0); // default = 1:1 original |
---|
| 69 | l->matrix_inverse = l->matrix; |
---|
[22] | 70 | l->row_callback = NULL; |
---|
| 71 | l->row_callback_user_data = NULL; |
---|
[1] | 72 | |
---|
[18] | 73 | return l; |
---|
| 74 | } |
---|
[1] | 75 | |
---|
[18] | 76 | /** |
---|
[19] | 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 | **/ |
---|
| 85 | LOCHSTREIFEN* 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; |
---|
[22] | 111 | l->row_callback = template->row_callback; |
---|
| 112 | l->row_callback_user_data = template->row_callback_user_data; |
---|
[19] | 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. |
---|
[22] | 121 | * This won't touch your row_callback_user_data. |
---|
[19] | 122 | * @return Nothing, because there's nothing left. Except your data, okay. |
---|
| 123 | **/ |
---|
| 124 | void 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 | /** |
---|
[18] | 137 | * A small debug function that will print out all fields of the LOCHSTREIFEN |
---|
| 138 | * structure well ordered to stderr. |
---|
| 139 | * |
---|
| 140 | **/ |
---|
| 141 | void 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); |
---|
[1] | 151 | |
---|
[18] | 152 | |
---|
| 153 | if(l != NULL) |
---|
[19] | 154 | fprintf(stderr, "Debugging papertape at %p:\n", l); |
---|
[18] | 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 |
---|
[19] | 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)); |
---|
[18] | 226 | |
---|
| 227 | // 6. Point: Debugging |
---|
| 228 | fprintf(stderr, " * Debugging: %s\n", l->debug ? "ENABLED" : "disabled"); |
---|
| 229 | |
---|
| 230 | fprintf(stderr, "END OF LOCHSTREIFEN\n"); |
---|
| 231 | } |
---|
[1] | 232 | |
---|
[18] | 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 | **/ |
---|
| 240 | void lochstreifen_set_data(LOCHSTREIFEN *l, int data_length, byte_t *data) { |
---|
| 241 | l->data_length = data_length; |
---|
[19] | 242 | //if(l->data != NULL) |
---|
| 243 | // free(l->data); |
---|
[18] | 244 | l->data = data; |
---|
| 245 | } |
---|
[1] | 246 | |
---|
[18] | 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 | **/ |
---|
| 253 | void lochstreifen_add_null_bytes(LOCHSTREIFEN *l, int start, int end) { |
---|
| 254 | byte_t *new_data; |
---|
[1] | 255 | |
---|
[18] | 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 | } |
---|
[1] | 277 | |
---|
[18] | 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 | **/ |
---|
| 284 | double lochstreifen_get_length(LOCHSTREIFEN *l) { |
---|
| 285 | return LOCHSTREIFEN_LENGTH; |
---|
| 286 | } |
---|
[1] | 287 | |
---|
| 288 | |
---|
[18] | 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 | **/ |
---|
[19] | 297 | void lochstreifen_set_highlight_region(LOCHSTREIFEN *l, row_t start, row_t end) { |
---|
[18] | 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 | } |
---|
[1] | 303 | |
---|
[18] | 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 | **/ |
---|
[19] | 309 | int 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) |
---|
[18] | 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; |
---|
[1] | 317 | } |
---|
| 318 | |
---|
[18] | 319 | /** |
---|
| 320 | * Remove the highlighted region (that is, make it no more highlighted). |
---|
| 321 | **/ |
---|
| 322 | void lochstreifen_remove_highlight_region(LOCHSTREIFEN *l) { |
---|
[19] | 323 | //l->highlight_region_color = NULL; |
---|
| 324 | // was stupid, this is more intelligent: |
---|
| 325 | l->highlight_region_start = LOCHSTREIFEN_NO_ROW; |
---|
[1] | 326 | } |
---|
| 327 | |
---|
[18] | 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 | **/ |
---|
[19] | 344 | void lochstreifen_set_highlight_row(LOCHSTREIFEN *l, row_t number_of_row) { |
---|
[18] | 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; |
---|
[1] | 348 | } |
---|
| 349 | |
---|
[18] | 350 | /** |
---|
[19] | 351 | * Get the highlighted special row. If there is no highlighted row, it will return |
---|
| 352 | * LOCHSTREIFEN_NO_ROW. |
---|
[18] | 353 | * @return the number of the special row, counting from zero. |
---|
| 354 | **/ |
---|
| 355 | int lochstreifen_get_highlight_row(LOCHSTREIFEN *l) { |
---|
[19] | 356 | return l->highlight_row_number; |
---|
[1] | 357 | } |
---|
| 358 | |
---|
[18] | 359 | /** |
---|
| 360 | * Remove the highlight at the special selected row. |
---|
| 361 | **/ |
---|
| 362 | void lochstreifen_remove_highlight_row(LOCHSTREIFEN *l) { |
---|
| 363 | //l->highlight_row_color = NULL; /// nein, das ist dumm, weil es den highlight komplett entfernen würde. |
---|
[19] | 364 | l->highlight_row_number = LOCHSTREIFEN_NO_ROW; // das ist schlau. |
---|
[3] | 365 | } |
---|
| 366 | |
---|
[18] | 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 | **/ |
---|
[19] | 391 | void lochstreifen_set_highlight_bit(LOCHSTREIFEN *l, row_t number_of_row, track_t number_of_bit) { |
---|
[18] | 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; |
---|
[1] | 396 | } |
---|
| 397 | |
---|
[18] | 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 | **/ |
---|
[19] | 403 | int 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) |
---|
[18] | 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; |
---|
[1] | 411 | } |
---|
| 412 | |
---|
[18] | 413 | /** |
---|
| 414 | * Remove the highlighting of the special bit. |
---|
| 415 | **/ |
---|
| 416 | void lochstreifen_remove_highlight_bit(LOCHSTREIFEN *l) { |
---|
[19] | 417 | //l->highlight_bit_color = NULL; // stupid. |
---|
| 418 | l->highlight_bit_row = LOCHSTREIFEN_NO_ROW; // more intelligent. |
---|
[1] | 419 | } |
---|
| 420 | |
---|
[18] | 421 | /** |
---|
[19] | 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 | **/ |
---|
| 425 | void 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 | /** |
---|
[18] | 434 | * If you want LOCHSTREIFEN only to paint a small area from the whole papertape, this |
---|
| 435 | * is the function you can call. |
---|
[19] | 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" ;-) |
---|
[18] | 439 | * |
---|
| 440 | **/ |
---|
| 441 | void 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)); |
---|
[19] | 444 | |
---|
| 445 | cairo_matrix_transform_point(&l->matrix_inverse, &x, &y); |
---|
| 446 | cairo_matrix_transform_distance(&l->matrix_inverse, &width, &height); |
---|
[18] | 447 | l->clip->x = x; |
---|
| 448 | l->clip->y = y; |
---|
| 449 | l->clip->width = width; |
---|
| 450 | l->clip->height = height; |
---|
[1] | 451 | } |
---|
| 452 | |
---|
[18] | 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 | **/ |
---|
| 457 | void lochstreifen_remove_clip(LOCHSTREIFEN *l) { |
---|
| 458 | free(l->clip); |
---|
| 459 | l->clip = NULL; // not neccessary, I suppose. |
---|
[1] | 460 | } |
---|
| 461 | |
---|
[18] | 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 | **/ |
---|
| 468 | void 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); |
---|
[19] | 472 | cairo_matrix_scale(&l->matrix, scale_factor, scale_factor); |
---|
| 473 | lochstreifen_check_matrix(l); |
---|
[1] | 474 | } |
---|
| 475 | |
---|
[18] | 476 | void lochstreifen_set_scaling_by_width(LOCHSTREIFEN *l, int width) { |
---|
| 477 | double scale_factor = ((double)width) / LOCHSTREIFEN_WIDTH; |
---|
[19] | 478 | cairo_matrix_scale(&l->matrix, scale_factor, scale_factor); |
---|
| 479 | lochstreifen_check_matrix(l); |
---|
[1] | 480 | } |
---|
| 481 | |
---|
[18] | 482 | void lochstreifen_set_scaling_by_code_hole(LOCHSTREIFEN *l, int diameter) { |
---|
| 483 | double scale_factor = ((double)diameter)/2 / LOCHSTREIFEN_RADIUS_CODE_HOLE; |
---|
[19] | 484 | cairo_matrix_scale(&l->matrix, scale_factor, scale_factor); |
---|
| 485 | lochstreifen_check_matrix(l); |
---|
[1] | 486 | } |
---|
| 487 | |
---|
[18] | 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 | **/ |
---|
| 499 | void 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++) { |
---|
[19] | 510 | cairo_matrix_transform_point(&l->matrix, &x[i], &y[i]); |
---|
[18] | 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 | } |
---|
[1] | 517 | } |
---|
| 518 | |
---|
[18] | 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 | **/ |
---|
| 523 | int lochstreifen_get_target_width(LOCHSTREIFEN *l) { |
---|
| 524 | int width; |
---|
| 525 | lochstreifen_get_target_dimensions(l, &width, NULL); |
---|
| 526 | return width; |
---|
[1] | 527 | } |
---|
| 528 | |
---|
[18] | 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 | **/ |
---|
| 533 | int lochstreifen_get_target_height(LOCHSTREIFEN *l) { |
---|
| 534 | int height; |
---|
| 535 | lochstreifen_get_target_dimensions(l, NULL, &height); |
---|
| 536 | return height; |
---|
[1] | 537 | } |
---|
| 538 | |
---|
[18] | 539 | /** |
---|
[19] | 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 | **/ |
---|
| 555 | void 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 |
---|
[18] | 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 | **/ |
---|
[19] | 629 | row_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 | } |
---|
[1] | 634 | |
---|
[19] | 635 | track_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 | **/ |
---|
| 651 | int 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. |
---|
[18] | 658 | |
---|
[19] | 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; |
---|
[3] | 676 | } |
---|
[1] | 677 | |
---|
[18] | 678 | /** |
---|
[19] | 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. |
---|
[18] | 682 | * |
---|
[19] | 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 | **/ |
---|
| 688 | int 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 | * |
---|
[18] | 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 | **/ |
---|
| 745 | void 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: |
---|
[19] | 752 | |
---|
[18] | 753 | case LOCHSTREIFEN_MOVEMENT_TO_BOTTOM: |
---|
[19] | 754 | |
---|
[18] | 755 | case LOCHSTREIFEN_MOVEMENT_NONE: |
---|
| 756 | default: |
---|
| 757 | break; |
---|
| 758 | } |
---|
[19] | 759 | lochstreifen_check_matrix(l); |
---|
[1] | 760 | } |
---|
| 761 | |
---|
[18] | 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 | **/ |
---|
[1] | 773 | void lochstreifen_draw(LOCHSTREIFEN *l, cairo_t *cr) { |
---|
[18] | 774 | // complete length of paper tape: |
---|
| 775 | double length = LOCHSTREIFEN_LENGTH; |
---|
[3] | 776 | |
---|
[18] | 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; |
---|
[1] | 783 | |
---|
[18] | 784 | // apply the matrix! |
---|
[19] | 785 | cairo_set_matrix(cr, &l->matrix); |
---|
[18] | 786 | |
---|
[19] | 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 | } |
---|
[1] | 793 | |
---|
[18] | 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 | } |
---|
[3] | 799 | |
---|
[18] | 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); |
---|
[3] | 803 | |
---|
[18] | 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); |
---|
[19] | 810 | cairo_fill(cr); |
---|
| 811 | |
---|
[18] | 812 | // long rectangular area over complete paper tape |
---|
[19] | 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 | |
---|
[1] | 841 | |
---|
[18] | 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); |
---|
[19] | 850 | |
---|
| 851 | } // paper tape background finished. |
---|
[18] | 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 | } |
---|
[1] | 864 | |
---|
[18] | 865 | |
---|
| 866 | if(l->clip != NULL) { |
---|
[19] | 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 | } |
---|
[18] | 891 | |
---|
[19] | 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; |
---|
[1] | 898 | |
---|
[19] | 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 | */ |
---|
[18] | 908 | // calculation of first track to begin with. |
---|
[19] | 909 | /* |
---|
| 910 | track_min = l->clip->y == 0 ? 0 : floor( l->clip->y / LOCHSTREIFEN_TRACK_DISTANCE ); |
---|
[18] | 911 | |
---|
[19] | 912 | |
---|
[18] | 913 | // calculation of last track to end with. |
---|
[19] | 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; |
---|
[18] | 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 | |
---|
[19] | 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 | |
---|
[18] | 946 | // loop all the rows (bytes) |
---|
| 947 | // x start: tear off + one half row offset + offset rows * length of row |
---|
[19] | 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) { |
---|
[22] | 953 | printf("CALLING ROW CALLBACK!\n"); |
---|
[19] | 954 | (*l->row_callback) (&row, cr, l->row_callback_user_data); |
---|
| 955 | // at least now that's all the magic ;) |
---|
| 956 | } |
---|
| 957 | |
---|
[18] | 958 | // highlight region? |
---|
[19] | 959 | if(l->highlight_region_color != NULL && l->highlight_region_start != LOCHSTREIFEN_NO_ROW && |
---|
[18] | 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/ |
---|
[19] | 966 | cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE / 2, 0, |
---|
[18] | 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) { |
---|
[19] | 974 | // if check again LOCHSTREIFEN_NO_ROW not neccessary because row iterator never |
---|
| 975 | // gets LOCHSTREIFEN_NO_ROW. |
---|
[18] | 976 | cairo_set_source(cr, l->highlight_row_color); |
---|
| 977 | // genauso abgerundetes Rechteck wie bei der region |
---|
| 978 | // überlegen. |
---|
[19] | 979 | cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE/2, 0, |
---|
[18] | 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 | |
---|
[22] | 1000 | printf("testing byte\n"); |
---|
[18] | 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) { |
---|
[19] | 1018 | // if check against l->highlight_bit_row == LOCHSTREIFEN_NO_ROW |
---|
| 1019 | // not neccessary because row never gets LOCHSTREIFEN_NO_ROW. |
---|
| 1020 | |
---|
[18] | 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); |
---|
[19] | 1027 | cairo_set_line_width(cr, 0.01); |
---|
[18] | 1028 | cairo_stroke(cr); // mal testen :-) |
---|
| 1029 | } |
---|
| 1030 | } // for tracks (bits) |
---|
| 1031 | } // for rows (bytes) |
---|
| 1032 | } // function lochstreifen_draw |
---|