Canola
0.8.D001
|
00001 // 00002 // canola - canon canola 1614p emulator 00003 // Copyright (C) 2011, 2012 Peter Miller 00004 // 00005 // This program is free software; you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License, version 3, as 00007 // published by the Free Software Foundation. 00008 // 00009 // This program is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License along 00015 // with this program. If not, see <http://www.gnu.org/licenses/>. 00016 // 00017 00018 #include <lib/ac/assert.h> 00019 #include <pangomm.h> 00020 #include <pangomm/init.h> 00021 00022 #include <lib/context_saver.h> 00023 #include <lib/opcode.h> 00024 #include <lib/program_card/functor/image.h> 00025 #include <lib/std_string_printf.h> 00026 00027 00028 program_card_functor_image::~program_card_functor_image() 00029 { 00030 } 00031 00032 00033 program_card_functor_image::program_card_functor_image() : 00034 trim_corners(false), 00035 perforations(true), 00036 disassemble(false), 00037 arrow(false), 00038 card_num(0), 00039 card_count(0), 00040 color("blue"), 00041 style(style_aus), 00042 accuracy(0), 00043 step_count(0) 00044 { 00045 } 00046 00047 00048 void 00049 program_card_functor_image::set_position(int cn, int cc) 00050 { 00051 assert(cc >= 1); 00052 assert(cn >= 1); 00053 assert(cn <= cc); 00054 card_num = cn; 00055 card_count = cc; 00056 } 00057 00058 00059 #define card_width 1360 00060 #define card_height 4425 00061 #define hole_width 73.6 00062 #define hole_height 30.8 00063 00064 00065 static inline double 00066 y_from_row(double row) 00067 { 00068 // floating arg because sometimes want halfway in between 00069 return 199.910199833 + 104.489383847 * row; 00070 } 00071 00072 00073 static inline double 00074 x_from_bit(double bit) 00075 { 00076 // floating arg because sometimes want halfway in between 00077 return 1056.19285714 - 151.321428571 * bit; 00078 } 00079 00080 00081 void 00082 program_card_functor_image::outline(const Cairo::RefPtr<Cairo::Context> &cr) 00083 { 00084 // 1360x4425 00085 cr->move_to(0, 4425); 00086 cr->line_to(1360, 4425); 00087 if (trim_corners) 00088 { 00089 cr->line_to(1360, 109); 00090 cr->line_to(1360 - 120, 0); 00091 cr->line_to(120, 0); 00092 cr->line_to(0, 109); 00093 } 00094 else 00095 { 00096 cr->line_to(1360, 0); 00097 cr->line_to(0, 0); 00098 } 00099 cr->close_path(); 00100 } 00101 00102 00103 void 00104 program_card_functor_image::draw(const Cairo::RefPtr<Cairo::Context> &cr, 00105 const unsigned char *data, size_t data_size) 00106 { 00107 assert(data_size > 0); 00108 assert(data_size <= 40); 00109 00110 // we get our drawing context scaled so that its units of 00111 // measurements are points. Rescale to 600dpi, because that is the 00112 // scale of the card images the measurements were taken from. 00113 context_saver cs1(cr); 00114 { 00115 double dpi = 600; 00116 double s = 72. / dpi; 00117 cr->scale(s, s); 00118 } 00119 00120 // Set the clipping region. 00121 outline(cr); 00122 // We add all of the holes to the clipping region 00123 // so that we have actual holes in the PNG and SVG outputs. 00124 for (unsigned row = 0; row < 40 && row < data_size; ++row) 00125 { 00126 double row_top = y_from_row(row) - (hole_height / 2); 00127 double row_bot = row_top + hole_height; 00128 unsigned char op = data[row]; 00129 for (unsigned bit = 0; bit < 7; ++bit) 00130 { 00131 if (op & (1 << bit)) 00132 { 00133 double col_lhs = x_from_bit(bit) - (hole_width / 2); 00134 double col_rhs = col_lhs + hole_width; 00135 // clockwise for holes 00136 cr->begin_new_sub_path(); 00137 cr->move_to(col_rhs, row_top); 00138 cr->line_to(col_rhs, row_bot); 00139 cr->line_to(col_lhs, row_bot); 00140 cr->line_to(col_lhs, row_top); 00141 cr->close_path(); 00142 } 00143 } 00144 } 00145 cr->clip(); 00146 00147 // Card background. 00148 { 00149 context_saver cs2(cr); 00150 if (color == "green") 00151 cr->set_source_rgb(133./255., 168./255., 135./255.); 00152 else if (color == "pink" || color == "red") 00153 cr->set_source_rgb(240./255., 128./255., 144./255.); 00154 else if (color == "gray" || color == "grey") 00155 cr->set_source_rgb(184./255., 199./255., 193./255.); 00156 else if (color == "cyan") 00157 cr->set_source_rgb(116./255., 170./255., 171./255.); 00158 else 00159 cr->set_source_rgb(95./255., 153./255., 168./255.); // blue 00160 cr->move_to(0, 4425); 00161 cr->line_to(1360, 4425); 00162 cr->line_to(1360, 0); 00163 cr->line_to(0, 0); 00164 cr->close_path(); 00165 cr->fill(); 00166 } 00167 00168 // 00169 // Draw an outline around the card, it gives the card a more 00170 // definite edge. 00171 // 00172 outline(cr); 00173 cr->set_line_width(4); 00174 cr->stroke(); 00175 00176 cr->set_source_rgb(0, 0, 0); 00177 switch (style) 00178 { 00179 case style_blank: 00180 // dividing line 00181 { 00182 context_saver cs3(cr); 00183 cr->set_line_width(6); 00184 cr->move_to(1123, 0); 00185 cr->line_to(1123, 4425); 00186 cr->stroke(); 00187 } 00188 00189 // The bottom of the card gets labeled as being for the Canon 00190 // Canola 1614P. 00191 { 00192 context_saver cs4(cr); 00193 std::string text = "CANON CANOLA 1614P"; 00194 cr->set_font_size(57); 00195 Cairo::TextExtents ext; 00196 cr->get_text_extents(text, ext); 00197 cr->move_to((1120 - ext.width) / 2, 4395); 00198 cr->show_text(text); 00199 } 00200 break; 00201 00202 case style_ibm: 00203 { 00204 context_saver cs4(cr); 00205 std::string text = "IBM 6905920132 (46.2) X71-9263"; 00206 cr->set_font_size(36); 00207 Cairo::TextExtents ext; 00208 cr->get_text_extents(text, ext); 00209 cr->move_to((1360 - ext.width) / 2, 4401); 00210 cr->show_text(text); 00211 } 00212 goto aus_ibm_common; 00213 00214 case style_aus: 00215 { 00216 context_saver csx(cr); 00217 std::string text = "IBM-/30131 PRINTED IN AUSTRALIA"; 00218 cr->set_font_size(34); 00219 Cairo::TextExtents ext; 00220 cr->get_text_extents(text, ext); 00221 cr->move_to((1360 - ext.width) / 2, 4383); 00222 cr->show_text(text); 00223 } 00224 default: 00225 aus_ibm_common: 00226 { 00227 context_saver cs4(cr); 00228 double y2 = y_from_row(-0.5); 00229 double y1 = y2 / 2; 00230 double y3 = y_from_row(39.5); 00231 00232 double x1 = x_from_bit(6.5); 00233 double x2 = x_from_bit(-0.5); 00234 00235 cr->set_line_width(6); 00236 cr->move_to(x1, y1); 00237 cr->line_to(x2, y1); 00238 cr->move_to(0, y2); 00239 cr->line_to(card_width, y2); 00240 cr->move_to(0, y3); 00241 cr->line_to(card_width, y3); 00242 cr->move_to(x1, 0); 00243 cr->line_to(x1, y3); 00244 cr->move_to(x2, 0); 00245 cr->line_to(x2, y3); 00246 cr->move_to(x_from_bit(3.5), y1); 00247 cr->rel_line_to(0, y3 - y1); 00248 cr->stroke(); 00249 00250 cr->set_line_width(3); 00251 for (unsigned bit = 0; bit <= 6; ++bit) 00252 { 00253 cr->move_to(x_from_bit(bit), y2); 00254 cr->rel_line_to(0, y3 - y2); 00255 } 00256 cr->stroke(); 00257 00258 for (unsigned row = 0; row < 40; ++row) 00259 { 00260 if ((row % 10) == 9) 00261 continue; 00262 cr->move_to(x1, y_from_row(row)); 00263 cr->rel_line_to(x2 - x1, 0); 00264 } 00265 cr->stroke(); 00266 00267 cr->set_line_width(5); 00268 for (unsigned row = 9; row < 40; row += 10) 00269 { 00270 cr->move_to(x1, y_from_row(row)); 00271 cr->rel_line_to(x2 - x1, 0); 00272 } 00273 cr->stroke(); 00274 00275 std::string text = "CANON PROGRAM CARD"; 00276 cr->set_font_size(49); 00277 Cairo::TextExtents ext; 00278 cr->get_text_extents(text, ext); 00279 cr->move_to((x2 + x1 - ext.width) / 2, (y1 + ext.height) / 2); 00280 cr->show_text(text); 00281 00282 cr->set_font_size(36); 00283 cr->move_to(x2 + 10, y2 - 46); 00284 cr->show_text("KEY"); 00285 cr->move_to(x2 + 10, y2 - 12); 00286 cr->show_text("SYMBOL"); 00287 00288 { 00289 context_saver csa(cr); 00290 cr->rotate_degrees(90); 00291 text = "STEP"; 00292 cr->get_text_extents(text, ext); 00293 cr->move_to(y2 - ext.width - 12, -(x1 - ext.height) + 10); 00294 cr->show_text(text); 00295 } 00296 00297 for (int bit = 0; bit < 7; ++bit) 00298 { 00299 static const char *t[] = 00300 { "1", "2", "4", "8", "10", "20", "40" }; 00301 std::string text(t[bit]); 00302 double x = x_from_bit(bit); 00303 double y = y2 - 12; 00304 cr->get_text_extents(text, ext); 00305 cr->move_to(x - ext.width/2, y); 00306 cr->show_text(text); 00307 } 00308 00309 for (unsigned row = 0; row < 40; ++row) 00310 { 00311 std::string text = std_string_printf("%d", row + 1); 00312 cr->get_text_extents(text, ext); 00313 double x = (x1 - ext.width) / 2; 00314 double y = y_from_row(row) + (ext.height / 2); 00315 cr->move_to(x, y); 00316 cr->show_text(text); 00317 } 00318 } 00319 break; 00320 } 00321 00322 // 00323 // draw a directional arrow 00324 // 00325 if (arrow) 00326 { 00327 enum { x1 = 108, x2 = 230 }; 00328 enum { y1 = 540, y2 = 1614 }; 00329 context_saver cs9(cr); 00330 double cx = x_from_bit(3); 00331 double cy = y_from_row(-1); 00332 cr->move_to(cx, cy), 00333 cr->rel_line_to(-x2, y1), 00334 cr->rel_line_to(x2 - x1, 0), 00335 cr->rel_line_to(0, y2 - y1), 00336 cr->rel_line_to(2 * x1, 0), 00337 cr->rel_line_to(0, y1 - y2), 00338 cr->rel_line_to(x2 - x1, 0), 00339 cr->close_path(); 00340 cr->clip(); 00341 00342 enum { w = 2 }; 00343 cr->set_line_width(2 * w); 00344 for (int n = -15; n <= 15; ++n) 00345 { 00346 double x = cx + n * ((x2 - w) / 15.); 00347 cr->move_to(x, cy); 00348 cr->rel_line_to(0, y2); 00349 cr->stroke(); 00350 } 00351 } 00352 00353 // 00354 // note where this card falls in the set 00355 // 00356 if (card_count > 0 && card_num >= 1 && card_num <= card_count) 00357 { 00358 std::string text = std_string_printf("%d of %d", card_num, card_count); 00359 context_saver cs9(cr); 00360 cr->set_font_size(600); 00361 Cairo::TextExtents ext; 00362 cr->get_text_extents(text, ext); 00363 cr->rotate_degrees(90); 00364 double baseline = y_from_row(38.5); 00365 cr->move_to(baseline - ext.width, -(1200 - ext.height) / 2); 00366 cr->set_source_rgba(0, 0, 0, 0.2); 00367 cr->show_text(text); 00368 } 00369 card_num = 0; 00370 card_count = 0; 00371 00372 if (!title.empty()) 00373 { 00374 context_saver cs9(cr); 00375 cr->set_font_size(180); 00376 Cairo::TextExtents ext; 00377 cr->get_text_extents(title, ext); 00378 cr->translate(card_width, 325); 00379 double ht = card_width - 1123; 00380 cr->rotate_degrees(90); 00381 double y = (ht + ext.height) / 2; 00382 if (disassemble) 00383 y += ht; 00384 cr->move_to(0, y); 00385 cr->show_text(title); 00386 } 00387 00388 // The bits are the holes in the clipping region, so there is no 00389 // need to draw them, but we need to draw the perforations. 00390 if (perforations) 00391 { 00392 bool stroke = false; 00393 for (unsigned row = 0; row < 40; ++row) 00394 { 00395 double row_top = y_from_row(row) - (hole_height / 2); 00396 double row_bot = row_top + hole_height; 00397 unsigned char op = (row < data_size ? data[row] : 0); 00398 for (unsigned bit = 0; bit < 7; ++bit) 00399 { 00400 double col_lhs = x_from_bit(bit) - (hole_width / 2); 00401 double col_rhs = col_lhs + hole_width; 00402 if (!(op & (1 << bit))) 00403 { 00404 cr->move_to(col_lhs, row_top); 00405 cr->line_to(col_lhs, row_bot); 00406 cr->line_to(col_rhs, row_bot); 00407 cr->line_to(col_rhs, row_top); 00408 cr->close_path(); 00409 stroke = true; 00410 } 00411 } 00412 } 00413 if (stroke) 00414 { 00415 cr->set_source_rgba(0, 0, 0, 0.2); 00416 cr->set_line_width(2); 00417 cr->stroke(); 00418 } 00419 } 00420 00421 // Draw the opcodes down the right hand side. 00422 if (disassemble) 00423 { 00424 cr->set_font_size(60); 00425 for (unsigned row = 0; row < 40 && row < data_size; ++row) 00426 { 00427 double row_top = y_from_row(row) - (hole_height / 2); 00428 double row_bot = row_top + hole_height; 00429 opcode_t op = (opcode_t)data[row]; 00430 if (opcode_red(op)) 00431 cr->set_source_rgb(0.8, 0, 0); 00432 else 00433 cr->set_source_rgb(0, 0, 0); 00434 std::string text = opcode_uname(op); 00435 Cairo::TextExtents ext; 00436 cr->get_text_extents(text, ext); 00437 cr->move_to((1360 + 1126 - ext.width) / 2, row_bot); 00438 cr->show_text(text); 00439 } 00440 } 00441 } 00442 00443 00444 void 00445 program_card_functor_image::set_title(const std::string &text) 00446 { 00447 title = text; 00448 } 00449 00450 00451 void 00452 program_card_functor_image::set_arrow(bool value) 00453 { 00454 arrow = value; 00455 } 00456 00457 00458 void 00459 program_card_functor_image::set_color(const std::string &text) 00460 { 00461 color = text; 00462 } 00463 00464 00465 void 00466 program_card_functor_image::set_disassemble(bool value) 00467 { 00468 disassemble = value; 00469 } 00470 00471 00472 void 00473 program_card_functor_image::set_perforations(bool value) 00474 { 00475 perforations = value; 00476 } 00477 00478 00479 void 00480 program_card_functor_image::set_trim_corners(bool value) 00481 { 00482 trim_corners = value; 00483 } 00484 00485 00486 void 00487 program_card_functor_image::set_style(const std::string &text) 00488 { 00489 if (text == "aus") 00490 { 00491 style = style_aus; 00492 color = "blue"; 00493 perforations = true; 00494 arrow = false; 00495 trim_corners = false; 00496 disassemble = true; 00497 } 00498 else if (text == "ibm") 00499 { 00500 style = style_ibm; 00501 color = "grey"; 00502 perforations = true; 00503 arrow = false; 00504 trim_corners = true; 00505 disassemble = true; 00506 } 00507 else if (text == "blank") 00508 { 00509 style = style_blank; 00510 color = "green"; 00511 perforations = false; 00512 arrow = true; 00513 trim_corners = true; 00514 disassemble = false; 00515 } 00516 } 00517 00518 00519 void 00520 program_card_functor_image::draw_envelope( 00521 const Cairo::RefPtr<Cairo::Context> &cr) 00522 { 00523 context_saver cs1(cr); 00524 { 00525 double dpi = 600; 00526 double s = 72. / dpi; 00527 cr->scale(s, s); 00528 } 00529 00530 enum { margin = 25 }; 00531 enum { flap = 250 }; 00532 enum { thumb = 300 }; // half inch 00533 00534 const double ew = card_width + 2 * margin; 00535 const double eh = card_height + margin; 00536 00537 cr->move_to(0, 0); 00538 cr->line_to(0, eh); 00539 cr->line_to(ew, eh); 00540 cr->line_to(ew + margin, eh + flap); 00541 cr->line_to(2 * ew - margin, eh + flap); 00542 cr->line_to(2 * ew, eh); 00543 cr->line_to(2 * ew + flap, card_height); 00544 cr->line_to(2 * ew + flap, margin); 00545 cr->line_to(2 * ew, 0); 00546 cr->line_to(ew + thumb, 0); 00547 cr->line_to(ew, thumb); 00548 cr->line_to(ew - thumb, 0); 00549 cr->close_path(); 00550 cr->set_source_rgb(82./255, 171./255, 205./255); 00551 cr->fill_preserve(); 00552 cr->set_source_rgb(0, 0, 0); 00553 cr->set_line_width(6); 00554 cr->stroke(); 00555 00556 cr->set_line_width(3); 00557 cr->move_to(ew, thumb); 00558 cr->line_to(ew, eh); 00559 cr->move_to(2 * ew, 0); 00560 cr->line_to(2 * ew, eh); 00561 cr->move_to(ew, eh); 00562 cr->line_to(2 * ew, eh); 00563 cr->stroke(); 00564 00565 // front of envelope 00566 { 00567 const double inset = 600/8; 00568 context_saver cs2(cr); 00569 cr->translate(ew, eh); 00570 cr->rotate_degrees(-90); 00571 00572 //cr->move_to(0, 100); 00573 //cr->set_font_size(100); 00574 //cr->show_text("You are here."); 00575 00576 cr->move_to(inset, inset); 00577 cr->line_to(inset, ew - inset); 00578 cr->line_to(eh - inset, ew - inset); 00579 if (inset < thumb * M_SQRT2) 00580 { 00581 double thumb_inset = thumb + inset * (M_SQRT2 - 1); 00582 cr->line_to(eh - inset, thumb_inset); 00583 cr->line_to(eh - thumb_inset, inset); 00584 } 00585 else 00586 cr->line_to(eh - inset, inset); 00587 cr->close_path(); 00588 cr->stroke_preserve(); 00589 cr->clip(); 00590 cr->translate(inset, inset); 00591 double iw = eh - 2 * inset; 00592 00593 // title 00594 if (!title.empty()) 00595 { 00596 context_saver cs3(cr); 00597 cr->set_font_size(180); 00598 Cairo::TextExtents ext; 00599 cr->get_text_extents(title, ext); 00600 double x = 600/8; 00601 double y = (237 + ext.height) / 2; 00602 cr->move_to(x, y); 00603 cr->show_text(title); 00604 } 00605 cr->move_to(0, 237); 00606 cr->line_to(iw, 237); 00607 cr->stroke(); 00608 00609 std::string text; 00610 if (!sfj_list.empty()) 00611 { 00612 text += "Subroutine Flag for Jump Symbol Codes Used: "; 00613 text += sfj_list + "\n"; 00614 } 00615 if (accuracy > 0) 00616 text += std_string_printf("Accuracy: %d digits\n", accuracy); 00617 if (step_count) 00618 text += std_string_printf("Number of Steps: %d\n", step_count); 00619 text += std_string_printf("Number of Cards: %d\n", card_count); 00620 if (!fj_list.empty()) 00621 text += "Flag for Jump Symbol Codes Used: " + fj_list + "\n"; 00622 if (!memory_list.empty()) 00623 text += "Memories Used: " + memory_list + "\n"; 00624 00625 cr->translate(iw / 2, 237); 00626 double ps = 600./72; 00627 cr->scale(ps, ps); 00628 Pango::init(); 00629 cr->move_to(0, 0); 00630 Glib::RefPtr<Pango::Layout> plp = Pango::Layout::create(cr); 00631 Pango::FontDescription pango_font = Pango::FontDescription("sans 9"); 00632 pango_font.set_weight(Pango::WEIGHT_NORMAL); 00633 plp->set_font_description(pango_font); 00634 plp->set_indent(-10 * Pango::SCALE); 00635 plp->set_width(Pango::SCALE * (iw / 2) / ps); 00636 plp->set_text(text); 00637 plp->add_to_cairo_context(cr); 00638 cr->fill(); 00639 } 00640 } 00641 00642 00643 void 00644 program_card_functor_image::set_sfj_list(const std::string &text) 00645 { 00646 sfj_list = text; 00647 } 00648 00649 00650 void 00651 program_card_functor_image::set_fj_list(const std::string &text) 00652 { 00653 fj_list = text; 00654 } 00655 00656 00657 void 00658 program_card_functor_image::set_memory_list(const std::string &text) 00659 { 00660 memory_list = text; 00661 } 00662 00663 00664 void 00665 program_card_functor_image::set_steps(int value) 00666 { 00667 step_count = value; 00668 }