"Fossies" - the Fresh Open Source Software Archive

Member "tesseract-5.2.0/src/viewer/scrollview.cpp" (6 Jul 2022, 27409 Bytes) of package /linux/misc/tesseract-5.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "scrollview.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.1.0_vs_5.2.0.

    1 ///////////////////////////////////////////////////////////////////////
    2 // File:        scrollview.cpp
    3 // Description: ScrollView
    4 // Author:      Joern Wanke
    5 //
    6 // (C) Copyright 2007, Google Inc.
    7 // Licensed under the Apache License, Version 2.0 (the "License");
    8 // you may not use this file except in compliance with the License.
    9 // You may obtain a copy of the License at
   10 // http://www.apache.org/licenses/LICENSE-2.0
   11 // Unless required by applicable law or agreed to in writing, software
   12 // distributed under the License is distributed on an "AS IS" BASIS,
   13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14 // See the License for the specific language governing permissions and
   15 // limitations under the License.
   16 //
   17 ///////////////////////////////////////////////////////////////////////
   18 //
   19 
   20 // Include automatically generated configuration file if running autoconf.
   21 #ifdef HAVE_CONFIG_H
   22 #  include "config_auto.h"
   23 #endif
   24 
   25 #include "scrollview.h"
   26 
   27 #include "svutil.h" // for SVNetwork
   28 
   29 #include <allheaders.h>
   30 
   31 #include <algorithm>
   32 #include <climits>
   33 #include <cstdarg>
   34 #include <cstring>
   35 #include <map>
   36 #include <memory> // for std::unique_ptr
   37 #include <mutex> // for std::mutex
   38 #include <string>
   39 #include <thread> // for std::thread
   40 #include <utility>
   41 #include <vector>
   42 
   43 namespace tesseract {
   44 
   45 const int kSvPort = 8461;
   46 const int kMaxMsgSize = 4096;
   47 const int kMaxIntPairSize = 45; // Holds %d,%d, for up to 64 bit.
   48 
   49 struct SVPolyLineBuffer {
   50   bool empty; // Independent indicator to allow SendMsg to call SendPolygon.
   51   std::vector<int> xcoords;
   52   std::vector<int> ycoords;
   53 };
   54 
   55 // A map between the window IDs and their corresponding pointers.
   56 static std::map<int, ScrollView *> svmap;
   57 static std::mutex *svmap_mu;
   58 // A map of all semaphores waiting for a specific event on a specific window.
   59 static std::map<std::pair<ScrollView *, SVEventType>, std::pair<SVSemaphore *, SVEvent *>>
   60     waiting_for_events;
   61 static std::mutex *waiting_for_events_mu;
   62 
   63 SVEvent *SVEvent::copy() const {
   64   auto *any = new SVEvent;
   65   any->command_id = command_id;
   66   any->counter = counter;
   67   any->parameter = new char[strlen(parameter) + 1];
   68   strcpy(any->parameter, parameter);
   69   any->type = type;
   70   any->x = x;
   71   any->y = y;
   72   any->x_size = x_size;
   73   any->y_size = y_size;
   74   any->window = window;
   75   return any;
   76 }
   77 
   78 // Destructor.
   79 // It is defined here, so the compiler can create a single vtable
   80 // instead of weak vtables in every compilation unit.
   81 SVEventHandler::~SVEventHandler() = default;
   82 
   83 #ifndef GRAPHICS_DISABLED
   84 /// This is the main loop which handles the ScrollView-logic from the server
   85 /// to the client. It basically loops through messages, parses them to events
   86 /// and distributes it to the waiting handlers.
   87 /// It is run from a different thread and synchronizes via SVSync.
   88 void ScrollView::MessageReceiver() {
   89   int counter_event_id = 0; // ongoing counter
   90   char *message = nullptr;
   91   // Wait until a new message appears in the input stream_.
   92   do {
   93     message = ScrollView::GetStream()->Receive();
   94   } while (message == nullptr);
   95 
   96   // This is the main loop which iterates until the server is dead (strlen =
   97   // -1). It basically parses for 3 different messagetypes and then distributes
   98   // the events accordingly.
   99   while (true) {
  100     // The new event we create.
  101     std::unique_ptr<SVEvent> cur(new SVEvent);
  102     // The ID of the corresponding window.
  103     int window_id;
  104 
  105     int ev_type;
  106 
  107     int n;
  108     // Fill the new SVEvent properly.
  109     sscanf(message, "%d,%d,%d,%d,%d,%d,%d,%n", &window_id, &ev_type, &cur->x, &cur->y, &cur->x_size,
  110            &cur->y_size, &cur->command_id, &n);
  111     char *p = (message + n);
  112 
  113     svmap_mu->lock();
  114     cur->window = svmap[window_id];
  115 
  116     if (cur->window != nullptr) {
  117       auto length = strlen(p);
  118       cur->parameter = new char[length + 1];
  119       strcpy(cur->parameter, p);
  120       if (length > 0) { // remove the last \n
  121         cur->parameter[length - 1] = '\0';
  122       }
  123       cur->type = static_cast<SVEventType>(ev_type);
  124       // Correct selection coordinates so x,y is the min pt and size is +ve.
  125       if (cur->x_size > 0) {
  126         cur->x -= cur->x_size;
  127       } else {
  128         cur->x_size = -cur->x_size;
  129       }
  130       if (cur->y_size > 0) {
  131         cur->y -= cur->y_size;
  132       } else {
  133         cur->y_size = -cur->y_size;
  134       }
  135       // Returned y will be the bottom-left if y is reversed.
  136       if (cur->window->y_axis_is_reversed_) {
  137         cur->y = cur->window->TranslateYCoordinate(cur->y + cur->y_size);
  138       }
  139       cur->counter = counter_event_id;
  140       // Increase by 2 since we will also create an SVET_ANY event from cur,
  141       // which will have a counter_id of cur + 1 (and thus gets processed
  142       // after cur).
  143       counter_event_id += 2;
  144 
  145       // In case of an SVET_EXIT event, quit the whole application.
  146       if (ev_type == SVET_EXIT) {
  147         SendRawMessage("svmain:exit()");
  148         break;
  149       }
  150 
  151       // Place two copies of it in the table for the window.
  152       cur->window->SetEvent(cur.get());
  153 
  154       // Check if any of the threads currently waiting want it.
  155       std::pair<ScrollView *, SVEventType> awaiting_list(cur->window, cur->type);
  156       std::pair<ScrollView *, SVEventType> awaiting_list_any(cur->window, SVET_ANY);
  157       std::pair<ScrollView *, SVEventType> awaiting_list_any_window((ScrollView *)nullptr,
  158                                                                     SVET_ANY);
  159       waiting_for_events_mu->lock();
  160       if (waiting_for_events.count(awaiting_list) > 0) {
  161         waiting_for_events[awaiting_list].second = cur.get();
  162         waiting_for_events[awaiting_list].first->Signal();
  163       } else if (waiting_for_events.count(awaiting_list_any) > 0) {
  164         waiting_for_events[awaiting_list_any].second = cur.get();
  165         waiting_for_events[awaiting_list_any].first->Signal();
  166       } else if (waiting_for_events.count(awaiting_list_any_window) > 0) {
  167         waiting_for_events[awaiting_list_any_window].second = cur.get();
  168         waiting_for_events[awaiting_list_any_window].first->Signal();
  169       }
  170       waiting_for_events_mu->unlock();
  171       // Signal the corresponding semaphore twice (for both copies).
  172       ScrollView *sv = svmap[window_id];
  173       if (sv != nullptr) {
  174         sv->Signal();
  175         sv->Signal();
  176       }
  177     }
  178     svmap_mu->unlock();
  179 
  180     // Wait until a new message appears in the input stream_.
  181     do {
  182       message = ScrollView::GetStream()->Receive();
  183     } while (message == nullptr);
  184   }
  185 }
  186 
  187 // Table to implement the color index values in the old system.
  188 static const uint8_t table_colors[ScrollView::GREEN_YELLOW + 1][4] = {
  189     {0, 0, 0, 0},         // NONE (transparent)
  190     {0, 0, 0, 255},       // BLACK.
  191     {255, 255, 255, 255}, // WHITE.
  192     {255, 0, 0, 255},     // RED.
  193     {255, 255, 0, 255},   // YELLOW.
  194     {0, 255, 0, 255},     // GREEN.
  195     {0, 255, 255, 255},   // CYAN.
  196     {0, 0, 255, 255},     // BLUE.
  197     {255, 0, 255, 255},   // MAGENTA.
  198     {0, 128, 255, 255},   // AQUAMARINE.
  199     {0, 0, 64, 255},      // DARK_SLATE_BLUE.
  200     {128, 128, 255, 255}, // LIGHT_BLUE.
  201     {64, 64, 255, 255},   // MEDIUM_BLUE.
  202     {0, 0, 32, 255},      // MIDNIGHT_BLUE.
  203     {0, 0, 128, 255},     // NAVY_BLUE.
  204     {192, 192, 255, 255}, // SKY_BLUE.
  205     {64, 64, 128, 255},   // SLATE_BLUE.
  206     {32, 32, 64, 255},    // STEEL_BLUE.
  207     {255, 128, 128, 255}, // CORAL.
  208     {128, 64, 0, 255},    // BROWN.
  209     {128, 128, 0, 255},   // SANDY_BROWN.
  210     {192, 192, 0, 255},   // GOLD.
  211     {192, 192, 128, 255}, // GOLDENROD.
  212     {0, 64, 0, 255},      // DARK_GREEN.
  213     {32, 64, 0, 255},     // DARK_OLIVE_GREEN.
  214     {64, 128, 0, 255},    // FOREST_GREEN.
  215     {128, 255, 0, 255},   // LIME_GREEN.
  216     {192, 255, 192, 255}, // PALE_GREEN.
  217     {192, 255, 0, 255},   // YELLOW_GREEN.
  218     {192, 192, 192, 255}, // LIGHT_GREY.
  219     {64, 64, 128, 255},   // DARK_SLATE_GREY.
  220     {64, 64, 64, 255},    // DIM_GREY.
  221     {128, 128, 128, 255}, // GREY.
  222     {64, 192, 0, 255},    // KHAKI.
  223     {255, 0, 192, 255},   // MAROON.
  224     {255, 128, 0, 255},   // ORANGE.
  225     {255, 128, 64, 255},  // ORCHID.
  226     {255, 192, 192, 255}, // PINK.
  227     {128, 0, 128, 255},   // PLUM.
  228     {255, 0, 64, 255},    // INDIAN_RED.
  229     {255, 64, 0, 255},    // ORANGE_RED.
  230     {255, 0, 192, 255},   // VIOLET_RED.
  231     {255, 192, 128, 255}, // SALMON.
  232     {128, 128, 0, 255},   // TAN.
  233     {0, 255, 255, 255},   // TURQUOISE.
  234     {0, 128, 128, 255},   // DARK_TURQUOISE.
  235     {192, 0, 255, 255},   // VIOLET.
  236     {128, 128, 0, 255},   // WHEAT.
  237     {128, 255, 0, 255}    // GREEN_YELLOW
  238 };
  239 
  240 /*******************************************************************************
  241  * Scrollview implementation.
  242  *******************************************************************************/
  243 
  244 SVNetwork *ScrollView::stream_ = nullptr;
  245 int ScrollView::nr_created_windows_ = 0;
  246 int ScrollView::image_index_ = 0;
  247 
  248 /// Calls Initialize with all arguments given.
  249 ScrollView::ScrollView(const char *name, int x_pos, int y_pos, int x_size, int y_size,
  250                        int x_canvas_size, int y_canvas_size, bool y_axis_reversed,
  251                        const char *server_name) {
  252   Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size, y_axis_reversed,
  253              server_name);
  254 }
  255 
  256 /// Calls Initialize with default argument for server_name_.
  257 ScrollView::ScrollView(const char *name, int x_pos, int y_pos, int x_size, int y_size,
  258                        int x_canvas_size, int y_canvas_size, bool y_axis_reversed) {
  259   Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size, y_axis_reversed,
  260              "localhost");
  261 }
  262 
  263 /// Calls Initialize with default argument for server_name_ & y_axis_reversed.
  264 ScrollView::ScrollView(const char *name, int x_pos, int y_pos, int x_size, int y_size,
  265                        int x_canvas_size, int y_canvas_size) {
  266   Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size, false, "localhost");
  267 }
  268 
  269 /// Sets up a ScrollView window, depending on the constructor variables.
  270 void ScrollView::Initialize(const char *name, int x_pos, int y_pos, int x_size, int y_size,
  271                             int x_canvas_size, int y_canvas_size, bool y_axis_reversed,
  272                             const char *server_name) {
  273   // If this is the first ScrollView Window which gets created, there is no
  274   // network connection yet and we have to set it up in a different thread.
  275   if (stream_ == nullptr) {
  276     nr_created_windows_ = 0;
  277     stream_ = new SVNetwork(server_name, kSvPort);
  278     waiting_for_events_mu = new std::mutex();
  279     svmap_mu = new std::mutex();
  280     SendRawMessage("svmain = luajava.bindClass('com.google.scrollview.ScrollView')\n");
  281     std::thread t(&ScrollView::MessageReceiver);
  282     t.detach();
  283   }
  284 
  285   // Set up the variables on the clientside.
  286   nr_created_windows_++;
  287   event_handler_ = nullptr;
  288   event_handler_ended_ = false;
  289   y_axis_is_reversed_ = y_axis_reversed;
  290   y_size_ = y_canvas_size;
  291   window_name_ = name;
  292   window_id_ = nr_created_windows_;
  293   // Set up polygon buffering.
  294   points_ = new SVPolyLineBuffer;
  295   points_->empty = true;
  296 
  297   svmap_mu->lock();
  298   svmap[window_id_] = this;
  299   svmap_mu->unlock();
  300 
  301   for (auto &i : event_table_) {
  302     i = nullptr;
  303   }
  304 
  305   semaphore_ = new SVSemaphore();
  306 
  307   // Set up an actual Window on the client side.
  308   char message[kMaxMsgSize];
  309   snprintf(message, sizeof(message),
  310            "w%u = luajava.newInstance('com.google.scrollview.ui"
  311            ".SVWindow','%s',%u,%u,%u,%u,%u,%u,%u)\n",
  312            window_id_, window_name_, window_id_, x_pos, y_pos, x_size, y_size, x_canvas_size,
  313            y_canvas_size);
  314   SendRawMessage(message);
  315 
  316   std::thread t(&ScrollView::StartEventHandler, this);
  317   t.detach();
  318 }
  319 
  320 /// Sits and waits for events on this window.
  321 void ScrollView::StartEventHandler() {
  322   SVEvent *new_event;
  323 
  324   for (;;) {
  325     stream_->Flush();
  326     semaphore_->Wait();
  327     new_event = nullptr;
  328     int serial = -1;
  329     int k = -1;
  330     mutex_.lock();
  331     // Check every table entry if it is valid and not already processed.
  332 
  333     for (int i = 0; i < SVET_COUNT; i++) {
  334       if (event_table_[i] != nullptr && (serial < 0 || event_table_[i]->counter < serial)) {
  335         new_event = event_table_[i];
  336         serial = event_table_[i]->counter;
  337         k = i;
  338       }
  339     }
  340     // If we didn't find anything we had an old alarm and just sleep again.
  341     if (new_event != nullptr) {
  342       event_table_[k] = nullptr;
  343       mutex_.unlock();
  344       if (event_handler_ != nullptr) {
  345         event_handler_->Notify(new_event);
  346       }
  347       if (new_event->type == SVET_DESTROY) {
  348         // Signal the destructor that it is safe to terminate.
  349         event_handler_ended_ = true;
  350         delete new_event; // Delete the pointer after it has been processed.
  351         return;
  352       }
  353       delete new_event; // Delete the pointer after it has been processed.
  354     } else {
  355       mutex_.unlock();
  356     }
  357     // The thread should run as long as its associated window is alive.
  358   }
  359 }
  360 #endif // !GRAPHICS_DISABLED
  361 
  362 ScrollView::~ScrollView() {
  363 #ifndef GRAPHICS_DISABLED
  364   svmap_mu->lock();
  365   if (svmap[window_id_] != nullptr) {
  366     svmap_mu->unlock();
  367     // So the event handling thread can quit.
  368     SendMsg("destroy()");
  369 
  370     SVEvent *sve = AwaitEvent(SVET_DESTROY);
  371     delete sve;
  372     svmap_mu->lock();
  373     svmap[window_id_] = nullptr;
  374     svmap_mu->unlock();
  375     // The event handler thread for this window *must* receive the
  376     // destroy event and set its pointer to this to nullptr before we allow
  377     // the destructor to exit.
  378     while (!event_handler_ended_) {
  379       Update();
  380     }
  381   } else {
  382     svmap_mu->unlock();
  383   }
  384   delete semaphore_;
  385   delete points_;
  386   for (auto &i : event_table_) {
  387     delete i;
  388   }
  389 #endif // !GRAPHICS_DISABLED
  390 }
  391 
  392 #ifndef GRAPHICS_DISABLED
  393 /// Send a message to the server, attaching the window id.
  394 void ScrollView::SendMsg(const char *format, ...) {
  395   if (!points_->empty) {
  396     SendPolygon();
  397   }
  398   va_list args;
  399   char message[kMaxMsgSize - 4];
  400 
  401   va_start(args, format); // variable list
  402   vsnprintf(message, sizeof(message), format, args);
  403   va_end(args);
  404 
  405   char form[kMaxMsgSize];
  406   snprintf(form, sizeof(form), "w%u:%s\n", window_id_, message);
  407 
  408   stream_->Send(form);
  409 }
  410 
  411 /// Send a message to the server without a
  412 /// window id. Used for global events like exit().
  413 void ScrollView::SendRawMessage(const char *msg) {
  414   stream_->Send(msg);
  415 }
  416 
  417 /// Add an Event Listener to this ScrollView Window
  418 void ScrollView::AddEventHandler(SVEventHandler *listener) {
  419   event_handler_ = listener;
  420 }
  421 
  422 void ScrollView::Signal() {
  423   semaphore_->Signal();
  424 }
  425 
  426 void ScrollView::SetEvent(const SVEvent *svevent) {
  427   // Copy event
  428   SVEvent *any = svevent->copy();
  429   SVEvent *specific = svevent->copy();
  430   any->counter = specific->counter + 1;
  431 
  432   // Place both events into the queue.
  433   std::lock_guard<std::mutex> guard(mutex_);
  434   // Delete the old objects..
  435   delete event_table_[specific->type];
  436   delete event_table_[SVET_ANY];
  437   // ...and put the new ones in the table.
  438   event_table_[specific->type] = specific;
  439   event_table_[SVET_ANY] = any;
  440 }
  441 
  442 /// Block until an event of the given type is received.
  443 /// Note: The calling function is responsible for deleting the returned
  444 /// SVEvent afterwards!
  445 SVEvent *ScrollView::AwaitEvent(SVEventType type) {
  446   // Initialize the waiting semaphore.
  447   auto *sem = new SVSemaphore();
  448   std::pair<ScrollView *, SVEventType> ea(this, type);
  449   waiting_for_events_mu->lock();
  450   waiting_for_events[ea] = std::pair<SVSemaphore *, SVEvent *>(sem, (SVEvent *)nullptr);
  451   waiting_for_events_mu->unlock();
  452   // Wait on it, but first flush.
  453   stream_->Flush();
  454   sem->Wait();
  455   // Process the event we got woken up for (its in waiting_for_events pair).
  456   waiting_for_events_mu->lock();
  457   SVEvent *ret = waiting_for_events[ea].second;
  458   waiting_for_events.erase(ea);
  459   delete sem;
  460   waiting_for_events_mu->unlock();
  461   return ret;
  462 }
  463 
  464 // Send the current buffered polygon (if any) and clear it.
  465 void ScrollView::SendPolygon() {
  466   if (!points_->empty) {
  467     points_->empty = true; // Allows us to use SendMsg.
  468     int length = points_->xcoords.size();
  469     // length == 1 corresponds to 2 SetCursors in a row and only the
  470     // last setCursor has any effect.
  471     if (length == 2) {
  472       // An isolated line!
  473       SendMsg("drawLine(%d,%d,%d,%d)", points_->xcoords[0], points_->ycoords[0],
  474               points_->xcoords[1], points_->ycoords[1]);
  475     } else if (length > 2) {
  476       // A polyline.
  477       SendMsg("createPolyline(%d)", length);
  478       char coordpair[kMaxIntPairSize];
  479       std::string decimal_coords;
  480       for (int i = 0; i < length; ++i) {
  481         snprintf(coordpair, kMaxIntPairSize, "%d,%d,", points_->xcoords[i], points_->ycoords[i]);
  482         decimal_coords += coordpair;
  483       }
  484       decimal_coords += '\n';
  485       SendRawMessage(decimal_coords.c_str());
  486       SendMsg("drawPolyline()");
  487     }
  488     points_->xcoords.clear();
  489     points_->ycoords.clear();
  490   }
  491 }
  492 
  493 /*******************************************************************************
  494  * LUA "API" functions.
  495  *******************************************************************************/
  496 
  497 // Sets the position from which to draw to (x,y).
  498 void ScrollView::SetCursor(int x, int y) {
  499   SendPolygon();
  500   DrawTo(x, y);
  501 }
  502 
  503 // Draws from the current position to (x,y) and sets the new position to it.
  504 void ScrollView::DrawTo(int x, int y) {
  505   points_->xcoords.push_back(x);
  506   points_->ycoords.push_back(TranslateYCoordinate(y));
  507   points_->empty = false;
  508 }
  509 
  510 // Draw a line using the current pen color.
  511 void ScrollView::Line(int x1, int y1, int x2, int y2) {
  512   if (!points_->xcoords.empty() && x1 == points_->xcoords.back() &&
  513       TranslateYCoordinate(y1) == points_->ycoords.back()) {
  514     // We are already at x1, y1, so just draw to x2, y2.
  515     DrawTo(x2, y2);
  516   } else if (!points_->xcoords.empty() && x2 == points_->xcoords.back() &&
  517              TranslateYCoordinate(y2) == points_->ycoords.back()) {
  518     // We are already at x2, y2, so just draw to x1, y1.
  519     DrawTo(x1, y1);
  520   } else {
  521     // This is a new line.
  522     SetCursor(x1, y1);
  523     DrawTo(x2, y2);
  524   }
  525 }
  526 
  527 // Set the visibility of the window.
  528 void ScrollView::SetVisible(bool visible) {
  529   if (visible) {
  530     SendMsg("setVisible(true)");
  531   } else {
  532     SendMsg("setVisible(false)");
  533   }
  534 }
  535 
  536 // Set the alwaysOnTop flag.
  537 void ScrollView::AlwaysOnTop(bool b) {
  538   if (b) {
  539     SendMsg("setAlwaysOnTop(true)");
  540   } else {
  541     SendMsg("setAlwaysOnTop(false)");
  542   }
  543 }
  544 
  545 // Adds a message entry to the message box.
  546 void ScrollView::AddMessage(const char *message) {
  547   char form[kMaxMsgSize];
  548   snprintf(form, sizeof(form), "w%u:%s", window_id_, message);
  549 
  550   char *esc = AddEscapeChars(form);
  551   SendMsg("addMessage(\"%s\")", esc);
  552   delete[] esc;
  553 }
  554 
  555 void ScrollView::AddMessageF(const char *format, ...) {
  556   va_list args;
  557   char message[kMaxMsgSize - 4];
  558 
  559   va_start(args, format); // variable list
  560   vsnprintf(message, sizeof(message), format, args);
  561   va_end(args);
  562 
  563   AddMessage(message);
  564 }
  565 
  566 // Set a messagebox.
  567 void ScrollView::AddMessageBox() {
  568   SendMsg("addMessageBox()");
  569 }
  570 
  571 // Exit the client completely (and notify the server of it).
  572 void ScrollView::Exit() {
  573   SendRawMessage("svmain:exit()");
  574   exit(0);
  575 }
  576 
  577 // Clear the canvas.
  578 void ScrollView::Clear() {
  579   SendMsg("clear()");
  580 }
  581 
  582 // Set the stroke width.
  583 void ScrollView::Stroke(float width) {
  584   SendMsg("setStrokeWidth(%f)", width);
  585 }
  586 
  587 // Draw a rectangle using the current pen color.
  588 // The rectangle is filled with the current brush color.
  589 void ScrollView::Rectangle(int x1, int y1, int x2, int y2) {
  590   if (x1 == x2 && y1 == y2) {
  591     return; // Scrollviewer locks up.
  592   }
  593   SendMsg("drawRectangle(%d,%d,%d,%d)", x1, TranslateYCoordinate(y1), x2, TranslateYCoordinate(y2));
  594 }
  595 
  596 // Draw an ellipse using the current pen color.
  597 // The ellipse is filled with the current brush color.
  598 void ScrollView::Ellipse(int x1, int y1, int width, int height) {
  599   SendMsg("drawEllipse(%d,%d,%u,%u)", x1, TranslateYCoordinate(y1), width, height);
  600 }
  601 
  602 // Set the pen color to the given RGB values.
  603 void ScrollView::Pen(int red, int green, int blue) {
  604   SendMsg("pen(%d,%d,%d)", red, green, blue);
  605 }
  606 
  607 // Set the pen color to the given RGB values.
  608 void ScrollView::Pen(int red, int green, int blue, int alpha) {
  609   SendMsg("pen(%d,%d,%d,%d)", red, green, blue, alpha);
  610 }
  611 
  612 // Set the brush color to the given RGB values.
  613 void ScrollView::Brush(int red, int green, int blue) {
  614   SendMsg("brush(%d,%d,%d)", red, green, blue);
  615 }
  616 
  617 // Set the brush color to the given RGB values.
  618 void ScrollView::Brush(int red, int green, int blue, int alpha) {
  619   SendMsg("brush(%d,%d,%d,%d)", red, green, blue, alpha);
  620 }
  621 
  622 // Set the attributes for future Text(..) calls.
  623 void ScrollView::TextAttributes(const char *font, int pixel_size, bool bold, bool italic,
  624                                 bool underlined) {
  625   const char *b;
  626   const char *i;
  627   const char *u;
  628 
  629   if (bold) {
  630     b = "true";
  631   } else {
  632     b = "false";
  633   }
  634   if (italic) {
  635     i = "true";
  636   } else {
  637     i = "false";
  638   }
  639   if (underlined) {
  640     u = "true";
  641   } else {
  642     u = "false";
  643   }
  644   SendMsg("textAttributes('%s',%u,%s,%s,%s)", font, pixel_size, b, i, u);
  645 }
  646 
  647 // Draw text at the given coordinates.
  648 void ScrollView::Text(int x, int y, const char *mystring) {
  649   SendMsg("drawText(%d,%d,'%s')", x, TranslateYCoordinate(y), mystring);
  650 }
  651 
  652 // Open and draw an image given a name at (x,y).
  653 void ScrollView::Draw(const char *image, int x_pos, int y_pos) {
  654   SendMsg("openImage('%s')", image);
  655   SendMsg("drawImage('%s',%d,%d)", image, x_pos, TranslateYCoordinate(y_pos));
  656 }
  657 
  658 // Add new checkboxmenuentry to menubar.
  659 void ScrollView::MenuItem(const char *parent, const char *name, int cmdEvent, bool flag) {
  660   if (parent == nullptr) {
  661     parent = "";
  662   }
  663   if (flag) {
  664     SendMsg("addMenuBarItem('%s','%s',%d,true)", parent, name, cmdEvent);
  665   } else {
  666     SendMsg("addMenuBarItem('%s','%s',%d,false)", parent, name, cmdEvent);
  667   }
  668 }
  669 
  670 // Add new menuentry to menubar.
  671 void ScrollView::MenuItem(const char *parent, const char *name, int cmdEvent) {
  672   if (parent == nullptr) {
  673     parent = "";
  674   }
  675   SendMsg("addMenuBarItem('%s','%s',%d)", parent, name, cmdEvent);
  676 }
  677 
  678 // Add new submenu to menubar.
  679 void ScrollView::MenuItem(const char *parent, const char *name) {
  680   if (parent == nullptr) {
  681     parent = "";
  682   }
  683   SendMsg("addMenuBarItem('%s','%s')", parent, name);
  684 }
  685 
  686 // Add new submenu to popupmenu.
  687 void ScrollView::PopupItem(const char *parent, const char *name) {
  688   if (parent == nullptr) {
  689     parent = "";
  690   }
  691   SendMsg("addPopupMenuItem('%s','%s')", parent, name);
  692 }
  693 
  694 // Add new submenuentry to popupmenu.
  695 void ScrollView::PopupItem(const char *parent, const char *name, int cmdEvent, const char *value,
  696                            const char *desc) {
  697   if (parent == nullptr) {
  698     parent = "";
  699   }
  700   char *esc = AddEscapeChars(value);
  701   char *esc2 = AddEscapeChars(desc);
  702   SendMsg("addPopupMenuItem('%s','%s',%d,'%s','%s')", parent, name, cmdEvent, esc, esc2);
  703   delete[] esc;
  704   delete[] esc2;
  705 }
  706 
  707 // Send an update message for a single window.
  708 void ScrollView::UpdateWindow() {
  709   SendMsg("update()");
  710 }
  711 
  712 // Note: this is an update to all windows
  713 void ScrollView::Update() {
  714   std::lock_guard<std::mutex> guard(*svmap_mu);
  715   for (auto &iter : svmap) {
  716     if (iter.second != nullptr) {
  717       iter.second->UpdateWindow();
  718     }
  719   }
  720 }
  721 
  722 // Set the pen color, using an enum value (e.g. ScrollView::ORANGE)
  723 void ScrollView::Pen(Color color) {
  724   Pen(table_colors[color][0], table_colors[color][1], table_colors[color][2],
  725       table_colors[color][3]);
  726 }
  727 
  728 // Set the brush color, using an enum value (e.g. ScrollView::ORANGE)
  729 void ScrollView::Brush(Color color) {
  730   Brush(table_colors[color][0], table_colors[color][1], table_colors[color][2],
  731         table_colors[color][3]);
  732 }
  733 
  734 // Shows a modal Input Dialog which can return any kind of String
  735 char *ScrollView::ShowInputDialog(const char *msg) {
  736   SendMsg("showInputDialog(\"%s\")", msg);
  737   SVEvent *ev;
  738   // wait till an input event (all others are thrown away)
  739   ev = AwaitEvent(SVET_INPUT);
  740   char *p = new char[strlen(ev->parameter) + 1];
  741   strcpy(p, ev->parameter);
  742   delete ev;
  743   return p;
  744 }
  745 
  746 // Shows a modal Yes/No Dialog which will return 'y' or 'n'
  747 int ScrollView::ShowYesNoDialog(const char *msg) {
  748   SendMsg("showYesNoDialog(\"%s\")", msg);
  749   SVEvent *ev;
  750   // Wait till an input event (all others are thrown away)
  751   ev = AwaitEvent(SVET_INPUT);
  752   int a = ev->parameter[0];
  753   delete ev;
  754   return a;
  755 }
  756 
  757 // Zoom the window to the rectangle given upper left corner and
  758 // lower right corner.
  759 void ScrollView::ZoomToRectangle(int x1, int y1, int x2, int y2) {
  760   y1 = TranslateYCoordinate(y1);
  761   y2 = TranslateYCoordinate(y2);
  762   SendMsg("zoomRectangle(%d,%d,%d,%d)", std::min(x1, x2), std::min(y1, y2), std::max(x1, x2),
  763           std::max(y1, y2));
  764 }
  765 
  766 // Send an image of type Pix.
  767 void ScrollView::Draw(Image image, int x_pos, int y_pos) {
  768   l_uint8 *data;
  769   size_t size;
  770   pixWriteMem(&data, &size, image, IFF_PNG);
  771   int base64_len = (size + 2) / 3 * 4;
  772   y_pos = TranslateYCoordinate(y_pos);
  773   SendMsg("readImage(%d,%d,%d)", x_pos, y_pos, base64_len);
  774   // Base64 encode the data.
  775   const char kBase64Table[64] = {
  776       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  777       'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  778       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  779       'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
  780   };
  781   char *base64 = new char[base64_len + 1];
  782   memset(base64, '=', base64_len);
  783   base64[base64_len] = '\0';
  784   int remainder = 0;
  785   int bits_left = 0;
  786   int code_len = 0;
  787   for (size_t i = 0; i < size; ++i) {
  788     int code = (data[i] >> (bits_left + 2)) | remainder;
  789     base64[code_len++] = kBase64Table[code & 63];
  790     bits_left += 2;
  791     remainder = data[i] << (6 - bits_left);
  792     if (bits_left == 6) {
  793       base64[code_len++] = kBase64Table[remainder & 63];
  794       bits_left = 0;
  795       remainder = 0;
  796     }
  797   }
  798   if (bits_left > 0) {
  799     base64[code_len++] = kBase64Table[remainder & 63];
  800   }
  801   SendRawMessage(base64);
  802   delete[] base64;
  803   lept_free(data);
  804 }
  805 
  806 // Escapes the ' character with a \, so it can be processed by LUA.
  807 // Note: The caller will have to make sure it deletes the newly allocated item.
  808 char *ScrollView::AddEscapeChars(const char *input) {
  809   const char *nextptr = strchr(input, '\'');
  810   const char *lastptr = input;
  811   char *message = new char[kMaxMsgSize];
  812   int pos = 0;
  813   while (nextptr != nullptr) {
  814     strncpy(message + pos, lastptr, nextptr - lastptr);
  815     pos += nextptr - lastptr;
  816     message[pos] = '\\';
  817     pos += 1;
  818     lastptr = nextptr;
  819     nextptr = strchr(nextptr + 1, '\'');
  820   }
  821   strcpy(message + pos, lastptr);
  822   return message;
  823 }
  824 
  825 // Inverse the Y axis if the coordinates are actually inversed.
  826 int ScrollView::TranslateYCoordinate(int y) {
  827   if (!y_axis_is_reversed_) {
  828     return y;
  829   } else {
  830     return y_size_ - y;
  831   }
  832 }
  833 
  834 char ScrollView::Wait() {
  835   // Wait till an input or click event (all others are thrown away)
  836   char ret = '\0';
  837   SVEventType ev_type = SVET_ANY;
  838   do {
  839     std::unique_ptr<SVEvent> ev(AwaitEvent(SVET_ANY));
  840     ev_type = ev->type;
  841     if (ev_type == SVET_INPUT) {
  842       ret = ev->parameter[0];
  843     }
  844   } while (ev_type != SVET_INPUT && ev_type != SVET_CLICK);
  845   return ret;
  846 }
  847 
  848 #endif // !GRAPHICS_DISABLED
  849 
  850 } // namespace tesseract