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 
copy() const63 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.
MessageReceiver()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.
ScrollView(const char * name,int x_pos,int y_pos,int x_size,int y_size,int x_canvas_size,int y_canvas_size,bool y_axis_reversed,const char * server_name)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_.
ScrollView(const char * name,int x_pos,int y_pos,int x_size,int y_size,int x_canvas_size,int y_canvas_size,bool y_axis_reversed)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.
ScrollView(const char * name,int x_pos,int y_pos,int x_size,int y_size,int x_canvas_size,int y_canvas_size)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.
Initialize(const char * name,int x_pos,int y_pos,int x_size,int y_size,int x_canvas_size,int y_canvas_size,bool y_axis_reversed,const char * server_name)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.
StartEventHandler()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 he is 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 
~ScrollView()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.
SendMsg(const char * format,...)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().
SendRawMessage(const char * msg)413 void ScrollView::SendRawMessage(const char *msg) {
414   stream_->Send(msg);
415 }
416 
417 /// Add an Event Listener to this ScrollView Window
AddEventHandler(SVEventHandler * listener)418 void ScrollView::AddEventHandler(SVEventHandler *listener) {
419   event_handler_ = listener;
420 }
421 
Signal()422 void ScrollView::Signal() {
423   semaphore_->Signal();
424 }
425 
SetEvent(const SVEvent * svevent)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!
AwaitEvent(SVEventType type)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.
SendPolygon()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).
SetCursor(int x,int 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.
DrawTo(int x,int y)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.
Line(int x1,int y1,int x2,int y2)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.
SetVisible(bool visible)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.
AlwaysOnTop(bool b)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.
AddMessage(const char * message)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 
AddMessageF(const char * format,...)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.
AddMessageBox()567 void ScrollView::AddMessageBox() {
568   SendMsg("addMessageBox()");
569 }
570 
571 // Exit the client completely (and notify the server of it).
Exit()572 void ScrollView::Exit() {
573   SendRawMessage("svmain:exit()");
574   exit(0);
575 }
576 
577 // Clear the canvas.
Clear()578 void ScrollView::Clear() {
579   SendMsg("clear()");
580 }
581 
582 // Set the stroke width.
Stroke(float 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.
Rectangle(int x1,int y1,int x2,int y2)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.
Ellipse(int x1,int y1,int width,int height)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.
Pen(int red,int green,int blue)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.
Pen(int red,int green,int blue,int alpha)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.
Brush(int red,int green,int blue)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.
Brush(int red,int green,int blue,int alpha)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.
TextAttributes(const char * font,int pixel_size,bool bold,bool italic,bool underlined)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.
Text(int x,int y,const char * mystring)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).
Draw(const char * image,int x_pos,int y_pos)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.
MenuItem(const char * parent,const char * name,int cmdEvent,bool flag)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.
MenuItem(const char * parent,const char * name,int cmdEvent)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.
MenuItem(const char * parent,const char * name)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.
PopupItem(const char * parent,const char * name)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.
PopupItem(const char * parent,const char * name,int cmdEvent,const char * value,const char * desc)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.
UpdateWindow()708 void ScrollView::UpdateWindow() {
709   SendMsg("update()");
710 }
711 
712 // Note: this is an update to all windows
Update()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)
Pen(Color color)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)
Brush(Color color)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
ShowInputDialog(const char * msg)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'
ShowYesNoDialog(const char * msg)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.
ZoomToRectangle(int x1,int y1,int x2,int y2)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.
Draw(Image image,int x_pos,int y_pos)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 he deletes the newly allocated item.
AddEscapeChars(const char * input)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.
TranslateYCoordinate(int y)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 
Wait()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
851