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