Canola  0.8.D001
lib/program_card/functor/image.cc
Go to the documentation of this file.
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 }