1 // ----------------------------------------------------------------------------
2 // wefax-pic.cxx  --  wefax support functions
3 //
4 // Copyright (C) 2010
5 //		Remi Chateauneu, F4ECW
6 //
7 // This file is part of fldigi.  Adapted from code contained in HAMFAX source code
8 // distribution.
9 //  Hamfax Copyright (C) Christof Schmitt
10 //
11 // fldigi is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
15 //
16 // fldigi is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 // ----------------------------------------------------------------------------
24 
25 #include <config.h>
26 #include <libgen.h>
27 #include <string>
28 #include <iostream>
29 #include <sstream>
30 #include <fstream>
31 #include <iomanip>
32 
33 
34 #include <time.h>
35 #include <unistd.h>
36 
37 #include <FL/Fl_Widget.H>
38 #include <FL/Fl_Button.H>
39 #include <FL/Fl_Light_Button.H>
40 #include <FL/Fl_Spinner.H>
41 #include <FL/Fl_Output.H>
42 #include <FL/Fl_Int_Input.H>
43 #include <FL/Fl_Counter.H>
44 #include <FL/Fl_Scroll.H>
45 #include <FL/Fl_Counter.H>
46 #include <FL/Fl_Select_Browser.H>
47 #include <FL/Fl_Choice.H>
48 #include <FL/Fl_Chart.H>
49 #include <FL/Fl_Shared_Image.H>
50 #include <FL/Enumerations.H>
51 
52 #include "wefax-pic.h"
53 
54 #include "debug.h"
55 #include "configuration.h"
56 #include "wefax.h"
57 #include "trx.h"
58 #include "fl_digi.h"
59 #include "main.h"
60 #include "fileselect.h"
61 #include "wefax_map.h"
62 #include "gettext.h"
63 
64 //Fl_Double_Window	*wefax_pic_rx_win = (Fl_Double_Window *)0;
65 Fl_Group *wefax_pic_rx_win = (Fl_Group *)0;
66 
67 Fl_Scroll       *wefax_pic_rx_scroll          = (Fl_Scroll *)0 ;
68 static wefax_map       *wefax_pic_rx_wefax_map       = (wefax_map *)0;
69 static Fl_Button       *wefax_btn_rx_save            = (Fl_Button *)0;
70 static Fl_Button       *wefax_btn_rx_abort           = (Fl_Button *)0;
71 static Fl_Button       *wefax_btn_rx_pause           = (Fl_Button *)0;
72 static Fl_Button       *wefax_btn_rx_resume          = (Fl_Button *)0;
73 static Fl_Choice       *wefax_choice_rx_zoom         = (Fl_Choice *)0;
74 //static Fl_Browser      *wefax_browse_rx_events       = (Fl_Browser *)0;
75 static Fl_Button       *wefax_btn_rx_skip_apt        = (Fl_Button *)0;
76 static Fl_Button       *wefax_btn_rx_skip_phasing    = (Fl_Button *)0;
77 Fl_Light_Button *wefax_round_rx_noise_removal = (Fl_Light_Button *)0;
78 Fl_Light_Button *wefax_round_rx_binary        = (Fl_Light_Button *)0;
79 static Fl_Spinner      *wefax_spinner_rx_binary      = (Fl_Spinner *)0;
80 Fl_Light_Button *wefax_round_rx_non_stop      = (Fl_Light_Button *)0;
81 static Fl_Output       *wefax_out_rx_row_num         = (Fl_Output *)0;
82 //static Fl_Output       *wefax_out_rx_width           = (Fl_Output *)0;
83 static Fl_Counter      *wefax_cnt_rx_ratio           = (Fl_Counter *)0;
84 static Fl_Counter       *wefax_rx_center       = (Fl_Counter *)0;
85 static Fl_Light_Button *wefax_autocenter   = (Fl_Light_Button *)0;
86 
87 Fl_Double_Window	*wefax_pic_tx_win = (Fl_Double_Window *)0;
88 
89 static Fl_Scroll *wefax_pic_tx_scroll           = (Fl_Scroll *)0 ;
90 static wefax_map *wefax_pic_tx_wefax_map        = (wefax_map *)0;
91 static wefax_picbox    *wefax_pic_tx_box        = (wefax_picbox *)0;
92 static Fl_Choice *wefax_choice_tx_zoom          = (Fl_Choice *)0;
93 static Fl_Choice *wefax_choice_tx_lpm           = (Fl_Choice *)0;
94 static Fl_Button *wefax_btn_tx_send_color       = (Fl_Button *)0;
95 static Fl_Button *wefax_btn_tx_send_grey        = (Fl_Button *)0;
96 static Fl_Output *wefax_out_tx_row_num          = (Fl_Output *)0;
97 static Fl_Output *wefax_out_tx_col_num          = (Fl_Output *)0;
98 static Fl_Button *wefax_btn_tx_send_abort       = (Fl_Button *)0;
99 static Fl_Button *wefax_btn_tx_load             = (Fl_Button *)0;
100 static Fl_Button *wefax_btn_tx_clear            = (Fl_Button *)0;
101 static Fl_Button *wefax_btn_tx_close            = (Fl_Button *)0;
102 
103 static void wefax_cb_pic_rx_center( Fl_Widget *, void *);
104 
105 /// The image to send.
106 static Fl_Shared_Image *wefax_shared_tx_img = (Fl_Shared_Image *)0;
107 
108 /// This contains the original content of the image to send,
109 /// converted into three bytes per pixel.
110 static unsigned char *wefax_xmtimg = (unsigned char *)0;
111 
112 /// This indicates whether an image to send is loaded in the GUI.
113 /// It allows to acquire twice when re-loading an image without sending.
114 static bool wefax_image_loaded_in_gui = false ;
115 
116 /// Used for shifting the received image left and right.
117 static  int center_val_prev = 0 ;
118 
119 /// Global pointer to the current wefax modem.
120 static wefax *wefax_serviceme = 0;
121 
122 /// TODO: This should be hidden in the class wefax_map. It is in wefax too.
123 static const int bytes_per_pix = 3 ;
124 
125 /// Initial size of the reception image. A typical fax has about 1300 lines.
126 static const int curr_pix_h_default = 1300 ;
127 
128 /// Always reset before loading a new image.
129 static  int curr_pix_height = curr_pix_h_default ;
130 
131 /// The antepenultimate line of the rx image is filtered to remove noise.
132 static int rx_last_filtered_row = 0 ;
133 
134 static bool noise_removal = false ;
135 
136 /// Alters the slanting of the image based on LPM adjustments.
137 static double rx_slant_ratio = 0.0 ;
138 
139 /// This transforms the user slant ratio (Small number around 0.0)
140 /// into a ratio used to stretch the image (Very very small mantissa added to 1.0).
slant_factor_default(void)141 static double slant_factor_default(void)
142 {
143 	return 100.0 / ( rx_slant_ratio + 100.0 );
144 }
145 
slant_factor_with_ratio(double ratio_percent)146 static double slant_factor_with_ratio( double ratio_percent )
147 {
148 	return ( ratio_percent + 100.0 ) / ( rx_slant_ratio + 100.0 );
149 }
150 
151 /// When set by the user, no new pixel is added or printed.
152 /// However, when the printing resumes, the position is not altered.
153 static  bool reception_paused = false ;
154 
155 /// Sets the label of the received or sent image.
set_win_label(Fl_Window * wefax_pic,const std::string & lab)156 static void set_win_label( Fl_Window * wefax_pic, const std::string & lab)
157 {
158 	char* label = strdup(lab.c_str());
159 	wefax_pic->copy_label(label);
160 	free(label);
161 	wefax_pic->redraw();
162 }
163 
164 /// Called when clearing the image to send.
clear_image(void)165 static void clear_image(void)
166 {
167 	ENSURE_THREAD(FLMAIN_TID);
168 	if (wefax_xmtimg)
169 	{
170 		delete [] wefax_xmtimg;
171 		wefax_xmtimg = NULL ;
172 	}
173 	if (wefax_shared_tx_img) {
174 		wefax_shared_tx_img->release();
175 		wefax_shared_tx_img = 0;
176 	}
177 	set_win_label(wefax_pic_tx_win,"");
178 }
179 
180 /// Clears the loaded image. It allows XML-RPC clients to send an image.
wefax_cb_pic_tx_clear(Fl_Widget *,void *)181 static void wefax_cb_pic_tx_clear( Fl_Widget *, void *)
182 {
183 	ENSURE_THREAD(FLMAIN_TID);
184 	wefax_image_loaded_in_gui = false ;
185 	clear_image();
186 	wefax_pic_tx_wefax_map->clear();
187 	wefax_pic_tx_wefax_map->resize(0,0,0,0);
188 	wefax_pic::restart_tx_viewer();
189 	/// Now the lock can be acquired by XML-RPC.
190 	wefax_serviceme->transmit_lock_release( "Cleared" );
191 }
192 
193 /// According to config flags, shows or hides the transmission window, and resizes both windows if needed.
wefax_pic_show_tx()194 static void wefax_pic_show_tx()
195 {
196 	ENSURE_THREAD(FLMAIN_TID);
197 		wefax_pic_tx_win->show();
198 }
199 
wefax_cb_pic_tx_close(Fl_Widget *,void *)200 static void wefax_cb_pic_tx_close( Fl_Widget *, void *)
201 {
202 	ENSURE_THREAD(FLMAIN_TID);
203 	wefax_pic_tx_win->hide();
204 }
205 
206 /// Usual LPM values.
207 static const struct {
208 	int          m_value ;
209 	const char * m_label ;
210 } all_lpm_values[] = {
211 	{ 240, "240" },
212 	{ 120, "120" },
213 	{  90,  "90" },
214 	{  60,  "60" }
215 };
216 
217 static const int nb_lpm_values = sizeof(all_lpm_values) / sizeof(all_lpm_values[0]);
218 
219 /// Returns the LPM value choosed on the TX or RX window.
get_choice_lpm_value(Fl_Choice * the_choice_lpm)220 static int get_choice_lpm_value( Fl_Choice * the_choice_lpm )
221 {
222 	int idx_lpm = the_choice_lpm->value();
223 	if( ( idx_lpm < 0 ) || ( idx_lpm >= nb_lpm_values ) ) {
224 		LOG_WARN( "Invalid LPM index=%d", idx_lpm );
225 		idx_lpm = 0 ;
226 	}
227 	return all_lpm_values[ idx_lpm ].m_value ;
228 }
229 
230 /// Lpm=120 is by far the most common value, therefore used by default if nothing else works.
231 /// wefax.cxx will always try 120 for wefax576 or 60 for wefax288.
232 
233 static const int lpm_default_idx = 1 ;
234 
235 static const char * title_choice_lpm = "LPM" ;
236 
237 /// Fills a FLTK widget with LPM vpossible values. Used for transmission and reception.
make_lpm_choice(int width_offset,int y_btn,int width_btn,int hei_tx_btn)238 static Fl_Choice * make_lpm_choice( int width_offset, int y_btn, int width_btn, int hei_tx_btn )
239 {
240 	Fl_Choice * choice_lpm = new Fl_Choice(width_offset, y_btn, width_btn, hei_tx_btn, title_choice_lpm );
241 	for( int ix_lpm = 0 ; ix_lpm < nb_lpm_values ; ++ix_lpm ) {
242 		choice_lpm->add( all_lpm_values[ ix_lpm ].m_label );
243 	};
244 	choice_lpm->value(lpm_default_idx);
245 	choice_lpm->tooltip(_("Set the LPM value"));
246 	return choice_lpm ;
247 }
248 
249 /// Sometimes the LPM can be calculated to 122.0 when it should be 120.0.
normalize_lpm(double the_lpm)250 int wefax_pic::normalize_lpm( double the_lpm )
251 {
252 	for( int ix_lpm = 0 ; ix_lpm < nb_lpm_values ; ++ix_lpm ) {
253 		int curr_lpm = all_lpm_values[ ix_lpm ].m_value ;
254 		if( std::fabs( the_lpm - curr_lpm ) < 3.0 ) {
255 			return curr_lpm ;
256 		}
257 	};
258 	int dflt_lpm = all_lpm_values[ lpm_default_idx ].m_value ;
259 	LOG_VERBOSE("Out of bounds LPM=%f. Setting to default:%d", the_lpm, dflt_lpm );
260 	return dflt_lpm ;
261 }
262 
263 /// Called for each new color component.
update_rx_pic_col(unsigned char data,int pix_pos)264 int wefax_pic::update_rx_pic_col(unsigned char data, int pix_pos )
265 {
266 	/// Each time the received image becomes too high, we increase its height.
267 	static const int curr_pix_incr_height = 100 ;
268 
269 	/// Three ints per pixel. It is safer to recalculate the
270 	/// row index to avoid any buffer overflow, because the given
271 	/// row is invalid if the image was horizontally moved.
272 	int row_number = 1 + ( pix_pos / ( wefax_pic_rx_wefax_map->pix_width() * bytes_per_pix ) );
273 
274 	/// Maybe we must increase the image height.
275 	if( curr_pix_height <= row_number )
276 	{
277 		curr_pix_height = row_number + curr_pix_incr_height ;
278 		wefax_pic_rx_wefax_map->resize_height( curr_pix_height, false );
279 
280 		int y_pos = wefax_pic_rx_wefax_map->h() - wefax_pic_rx_scroll->h() ;
281 		if( y_pos < 0 )
282 		{
283 			y_pos = 0 ;
284 		}
285 		else
286 		{
287 			// Small margin at the bottom, so we can immediately see new lines.
288 			y_pos += 20 ;
289 		}
290 		wefax_pic_rx_wefax_map->position( wefax_pic_rx_wefax_map->x(), -y_pos );
291 		wefax_pic_rx_scroll->redraw();
292 	}
293 	wefax_pic_rx_wefax_map->pixel(data, pix_pos);
294 	return row_number ;
295 }
296 
297 /// This estimates the colum of where the horizontal center of the image is,
298 /// or rather, the beginning of the left margin. The estimation is done on
299 /// a range of rows. It looks for a vertical band of X pixels, where the image
300 /// derivative is the lowest. It works well with faxes because they always have
301 /// a wide blank margin.
302 
303 //static int rowcount = 2;
304 
estimate_rx_image_center(int row_end,int numrows)305 static int estimate_rx_image_center( int row_end , int numrows )
306 {
307 if (row_end < numrows) return 0;
308 	/// This works as well with color images.
309 	int img_wid = wefax_pic_rx_wefax_map->pix_width() * bytes_per_pix ;
310 	unsigned const char * img_start = wefax_pic_rx_wefax_map->buffer();
311 
312 	/// The width of the image band on which we compute the minima
313 	/// of the absolute value of the video signal.
314 	/// Equal to the WEFAX start phasing width.
315 	#define AVG_WID 180
316 
317 	const unsigned char *pch = img_start;
318 
319 	int avgs[img_wid];
320 	memset(avgs, 0, img_wid * sizeof(*avgs));
321 	int img_avg[img_wid];
322 	memset(img_avg, 0, img_wid * sizeof(*img_avg));
323 
324 // averages over numrows
325 	for (int col = 0; col < img_wid; col++) {
326 		int avgval = 0;
327 		Fl::awake();
328 		for (int row = row_end - numrows; row < row_end; ++row) {
329 			avgval += (int)(pch[row * img_wid + col]);
330 		}
331 		avgs[col] = avgval / numrows;
332 	}
333 
334 	int min_idx = -1;
335 	int min_val = 255;
336 
337 	for (int col = 0; col < img_wid; col++) {
338 		for (int n = 0; n < AVG_WID; n++)
339 			img_avg[col] += avgs[(col + n) % img_wid];
340 		img_avg[col] /= AVG_WID;
341 		if (img_avg[col] < min_val) {
342 			min_val = img_avg[col];
343 			min_idx = col;
344 		}
345 	}
346 
347 	if (min_idx > img_wid / 2) min_idx -= img_wid;
348 	min_idx += AVG_WID / 2;
349 	min_idx /= bytes_per_pix;
350 
351 //	if (!rowcount) {
352 //		ofstream csvfile("img.csv");
353 //		for (int col = 0; col < img_wid; ++col)
354 //			csvfile << avgs[col] << "," << img_avg[col] << std::endl;
355 //		csvfile.close();
356 //	} else
357 //		--rowcount;
358 
359 	if (min_val > 40) return 0;
360 
361 	return min_idx;
362 
363 }
364 
365 /// Called for each bw pixel.
update_rx_pic_bw(unsigned char data,int pix_pos)366 void wefax_pic::update_rx_pic_bw(unsigned char data, int pix_pos )
367 {
368 	/// No pixel is added nor printed until this flag is reset to false.
369 	if( reception_paused  ) {
370 		return ;
371 	};
372 	/// The image must be horizontally shifted.
373 	pix_pos += center_val_prev * bytes_per_pix ;
374 	if( pix_pos < 0 ) {
375 		pix_pos = 0 ;
376 	}
377 
378 	/// Maybe there is a slant.
379 	pix_pos = ( double )pix_pos * slant_factor_default() + 0.5 ;
380 
381 	/// Must be a multiple of the number of bytes per pixel.
382 	pix_pos = ( pix_pos / bytes_per_pix ) * bytes_per_pix ;
383 
384 	static int prev_row = 0;
385 	int row_number = 0;
386 	for (int n = 0; n < bytes_per_pix; n++)
387 		row_number = update_rx_pic_col(data, pix_pos + n);
388 
389 	if (prev_row != row_number) {
390 		char row_num_buffer[20];
391 		snprintf( row_num_buffer, sizeof(row_num_buffer), "%d", row_number );
392 		wefax_out_rx_row_num->value( row_num_buffer );
393 		prev_row = row_number;
394 	}
395 
396 	if( noise_removal ) {
397 		if( ( row_number > wefax_map::noise_height_margin - 2 ) && ( row_number != rx_last_filtered_row ) ) {
398 			wefax_pic_rx_wefax_map->remove_noise(
399 				row_number,
400 				progdefaults.WEFAX_NoiseMargin,
401 				progdefaults.WEFAX_NoiseThreshold );
402 			rx_last_filtered_row = row_number ;
403 		}
404 	}
405 
406 /// Recenter every X-th row
407 
408 	static int last_row = 0;
409 	if ((row_number != last_row) &&
410 		(row_number < progdefaults.wefax_align_stop) &&
411 		(row_number > progdefaults.wefax_auto_after) &&
412 		(row_number % progdefaults.wefax_align_rows == 0)) {
413 			last_row = row_number;
414 		int new_center = estimate_rx_image_center( row_number, progdefaults.wefax_align_rows );
415 		if (progdefaults.wefax_autocenter) {
416 			if ( abs(new_center) > 3) { // 2 x depth of image
417 				wefax_rx_center->value(wefax_rx_center->value() - new_center);
418 				wefax_cb_pic_rx_center(NULL, NULL);
419 			}
420 		}
421 	}
422 }
423 
wefax_cb_pic_rx_pause(Fl_Widget *,void *)424 static void wefax_cb_pic_rx_pause( Fl_Widget *, void *)
425 {
426 	wefax_btn_rx_pause->hide();
427 	wefax_btn_rx_resume->show();
428 	reception_paused = true ;
429 	wefax_serviceme->update_rx_label();
430 }
431 
wefax_cb_pic_rx_resume(Fl_Widget *,void *)432 static void wefax_cb_pic_rx_resume( Fl_Widget *, void *)
433 {
434 	wefax_btn_rx_pause->show();
435 	wefax_btn_rx_resume->hide();
436 	reception_paused = false ;
437 	wefax_serviceme->update_rx_label();
438 }
439 
wefax_cb_pic_rx_abort(Fl_Widget *,void *)440 static void wefax_cb_pic_rx_abort( Fl_Widget *, void *)
441 {
442 	ENSURE_THREAD(FLMAIN_TID);
443 	if (wefax_serviceme != active_modem) return;
444 
445 	/// This will call wefax_pic::abort_rx_viewer
446 	wefax_serviceme->end_reception();
447 	wefax_round_rx_non_stop->value(false);
448 	wefax_serviceme->set_rx_manual_mode(false);
449 	wefax_round_rx_non_stop->redraw();
450 	wefax_btn_rx_pause->show();
451 	wefax_btn_rx_resume->hide();
452 }
453 
set_manual(bool manual)454 void wefax_pic::set_manual( bool manual )
455 {
456 	wefax_round_rx_non_stop->value(manual);
457 }
458 
wefax_cb_pic_rx_manual(Fl_Widget *,void *)459 static void wefax_cb_pic_rx_manual( Fl_Widget *, void *)
460 {
461 	if (wefax_serviceme != active_modem) return;
462 
463 	if( wefax_round_rx_non_stop->value() )
464 	{
465 		wefax_serviceme->set_rx_manual_mode(true);
466 		wefax_serviceme->skip_apt();
467 		wefax_serviceme->skip_phasing(true);
468 	}
469 	else
470 	{
471 		wefax_serviceme->set_rx_manual_mode(false);
472 	}
473 }
474 
wefax_cb_pic_rx_center(Fl_Widget *,void *)475 static void wefax_cb_pic_rx_center( Fl_Widget *, void *)
476 {
477 	if (wefax_serviceme != active_modem) return;
478 	ENSURE_THREAD(FLMAIN_TID);
479 
480 	int center_new_val = wefax_rx_center->value();
481 	int center_delta = center_new_val - center_val_prev ;
482 	center_val_prev = center_new_val ;
483 
484 	wefax_pic_rx_wefax_map->shift_horizontal_center( center_delta );
485 
486 }
487 
cb_wefax_autocenter(Fl_Widget *,void *)488 static void cb_wefax_autocenter( Fl_Widget *, void *)
489 {
490 	if (wefax_serviceme != active_modem) return;
491 	progdefaults.wefax_autocenter = wefax_autocenter->value();
492 	return;
493 }
494 
495 /// This gets the directory where images are accessed by default.
default_dir_get(const std::string & config_dir)496 static std::string default_dir_get( const std::string & config_dir )
497 {
498 	std::string tmp_dir = config_dir.empty() ? PicsDir : config_dir ;
499 	/// Valid dir names must end with a slash.
500 	if( ! tmp_dir.empty() ) {
501 		char termin = tmp_dir[ tmp_dir.size() - 1 ];
502 		if( ( termin != '/' ) && ( termin != '\\' ) ) tmp_dir += '/';
503 	}
504 	return tmp_dir ;
505 }
506 
507 /// This sets the directory where images are accessed by default.
508 /// Receives a file name, not a directory name.
default_dir_set(std::string & config_dir,const std::string & fil_name)509 static void default_dir_set( std::string & config_dir, const std::string & fil_name )
510 {
511 	char * fil_nam_copy = strdup( fil_name.c_str() );
512 	/// dirname() is a POSIX function.
513 	const char * dir_nam = dirname( fil_nam_copy );
514 	config_dir = dir_nam + std::string("/");
515 	LOG_VERBOSE("Setting default dir to %s", dir_nam );
516 	free( fil_nam_copy );
517 }
518 
519 /// Adds the file name to log to the adif file.
qso_notes(const char * direction,const std::string & file_name)520 static void qso_notes( const char * direction, const std::string & file_name )
521 {
522 	if( progdefaults.WEFAX_AdifLog == false ) {
523 		return ;
524 	}
525 	const std::string tmp_notes = direction + file_name ;
526 	wefax_serviceme->qso_rec().putField( NOTES, tmp_notes.c_str() );
527 }
528 
wefax_cb_pic_rx_save(Fl_Widget *,void *)529 static void wefax_cb_pic_rx_save( Fl_Widget *, void *)
530 {
531 	ENSURE_THREAD(FLMAIN_TID);
532 	const char ffilter[] = "Portable Network Graphics\t*.png\n";
533 	std::string dfname = default_dir_get( progdefaults.wefax_save_dir );
534 	dfname.append( wefax_serviceme->suggested_filename()  );
535 
536 	const char *file_name = FSEL::saveas(_("Save image as:"), ffilter, dfname.c_str(), NULL);
537 	/// Beware that no extra comments are saved here.
538 	if (!file_name) return;
539 	if (!*file_name) return;
540 
541 	wefax_pic_rx_wefax_map->save_png(file_name);
542 	qso_notes( "RX:", file_name );
543 	wefax_serviceme->qso_rec_save();
544 	/// Next time, image will be saved at the same place.
545 	default_dir_set( progdefaults.wefax_save_dir, file_name );
546 
547 }
548 
549 /// Beware, might be called by another thread. Called by the GUI
550 /// or when APT start is detected.
skip_rx_apt(void)551 void wefax_pic::skip_rx_apt(void)
552 {
553 	ENSURE_THREAD(FLMAIN_TID);
554 
555 	wefax_btn_rx_abort->hide();
556 	wefax_btn_rx_skip_apt->hide();
557 	wefax_btn_rx_skip_phasing->show();
558 
559 }
560 
561 /// Called when the user clicks "Skip APT"
wefax_cb_pic_rx_skip_apt(Fl_Widget *,void *)562 static void wefax_cb_pic_rx_skip_apt( Fl_Widget *, void *)
563 {
564 	if (wefax_serviceme != active_modem) return;
565 	ENSURE_THREAD(FLMAIN_TID);
566 	wefax_serviceme->skip_apt();
567 }
568 
569 /// Called when clicking "Skip phasing" or by wefax.cxx
570 /// when end of phasing is detected. Beware, might be called by another thread.
skip_rx_phasing(bool auto_center)571 void wefax_pic::skip_rx_phasing(bool auto_center)
572 {
573 	ENSURE_THREAD(FLMAIN_TID);
574 	/// Theoretically, this widget should already be hidden, but sometimes
575 	/// it seems that a call to skip_apt is lost... ?
576 	wefax_btn_rx_abort->show();
577 	wefax_btn_rx_skip_apt->hide();
578 	wefax_btn_rx_skip_phasing->hide();
579 
580 	wefax_round_rx_noise_removal->show();
581 	wefax_round_rx_binary->show();
582 	wefax_spinner_rx_binary->show();
583 
584 	wefax_out_rx_row_num->show();
585 	wefax_cnt_rx_ratio->show();
586 
587 }
588 
589 /// Called when clicking "Skip phasing".
wefax_cb_pic_rx_skip_phasing(Fl_Widget * w,void *)590 static void wefax_cb_pic_rx_skip_phasing( Fl_Widget *w, void *)
591 {
592 	if (wefax_serviceme != active_modem) return;
593 	wefax_serviceme->skip_phasing(true);
594 }
595 
596 
wefax_cb_pic_rx_noise_removal(Fl_Widget *,void *)597 static void wefax_cb_pic_rx_noise_removal( Fl_Widget *, void *)
598 {
599 	if (wefax_serviceme != active_modem) return;
600 	ENSURE_THREAD(FLMAIN_TID);
601 
602 	char rndVal = wefax_round_rx_noise_removal->value();
603 	noise_removal = rndVal ? true : false;
604 }
605 
wefax_cb_pic_rx_binary(Fl_Widget *,void *)606 static void wefax_cb_pic_rx_binary( Fl_Widget *, void *)
607 {
608 	if (wefax_serviceme != active_modem) return;
609 	ENSURE_THREAD(FLMAIN_TID);
610 
611 	char rndVal = wefax_round_rx_binary->value();
612 	if( rndVal ) {
613 		wefax_pic_rx_wefax_map->set_binary( true );
614 		wefax_spinner_rx_binary->activate();
615 	} else {
616 		wefax_pic_rx_wefax_map->set_binary( false );
617 		wefax_spinner_rx_binary->deactivate();
618 	}
619 }
620 
wefax_cb_pic_rx_bin_threshold(Fl_Widget *,void *)621 static void wefax_cb_pic_rx_bin_threshold( Fl_Widget *, void *)
622 {
623 	if (wefax_serviceme != active_modem) return;
624 	ENSURE_THREAD(FLMAIN_TID);
625 
626 	int rndVal = wefax_spinner_rx_binary->value();
627 	wefax_pic_rx_wefax_map->set_binary_threshold( rndVal );
628 }
629 
630 
wefax_cb_pic_ratio(Fl_Widget *,void *)631 static void wefax_cb_pic_ratio( Fl_Widget *, void * )
632 {
633 	if (wefax_serviceme != active_modem) return;
634 	ENSURE_THREAD(FLMAIN_TID);
635 	double ratio_percent = wefax_cnt_rx_ratio->value();
636 	double current_ratio = slant_factor_with_ratio( ratio_percent );
637 	wefax_pic_rx_wefax_map->stretch( current_ratio );
638 	rx_slant_ratio = ratio_percent ;
639 
640 	/// And we update the configuration structure.
641 	progdefaults.wefax_slant = rx_slant_ratio ;
642 
643 	/// Will prompt for saving configuration when exiting.
644 	progdefaults.changed = true;
645 }
646 
647 /// Possible zooms. The value is processed by class wefax_map.
648 static const struct {
649 	int          m_value ;
650 	const char * m_label ;
651 } all_zooms[] = {
652 	{ -3,  "25%" },
653 	{ -2,  "33%" },
654 	{ -1,  "50%" },
655 	{  0, "100%" }
656 };
657 //,
658 //	{  1, "200%" },
659 //	{  2, "300%" },
660 //	{  3, "400%" },
661 //};
662 
663 // Index in all_zooms.
664 static const int idx_default_zoom = 2 ;
665 
666 static int zoom_nb = sizeof(all_zooms) / sizeof(all_zooms[0]);
667 
wefax_cb_pic_zoom(Fl_Widget * wefax_choice_zoom,void *)668 static void wefax_cb_pic_zoom( Fl_Widget * wefax_choice_zoom, void * )
669 {
670 	if (wefax_serviceme != active_modem) return;
671 	ENSURE_THREAD(FLMAIN_TID);
672 	int idx_zoom = dynamic_cast<Fl_Choice *>(wefax_choice_zoom)->value();
673 	if( ( idx_zoom < 0 ) || ( idx_zoom >= zoom_nb ) ) {
674 		LOG_WARN( "Invalid zoom index=%d", idx_zoom );
675 		idx_zoom = idx_default_zoom ;
676 	}
677 
678 	/// Not very elegant but OK if two possibilities only.
679 	if( wefax_choice_zoom == wefax_choice_rx_zoom )
680 	{
681 		wefax_pic_rx_wefax_map->set_zoom( all_zooms[ idx_zoom ].m_value );
682 		wefax_pic_rx_win->redraw();
683 	}
684 	else if( wefax_choice_zoom == wefax_choice_tx_zoom )
685 	{
686 		wefax_pic_tx_wefax_map->set_zoom( all_zooms[ idx_zoom ].m_value );
687 		wefax_pic_tx_win->redraw();
688 	}
689 	else
690 	{
691 		LOG_ERROR("Inconsistent possibility");
692 	}
693 }
694 
wefax_create_zoom(int fax_grp_x,int fax_grp_y,int wid_btn_curr,int H_BUTTON)695 static Fl_Choice * wefax_create_zoom(int fax_grp_x, int fax_grp_y, int wid_btn_curr, int H_BUTTON)
696 {
697 	ENSURE_THREAD(FLMAIN_TID);
698 	static const char * title_zoom = "Zoom" ;
699 	Fl_Choice * wefax_choice_zoom = new Fl_Choice(fax_grp_x, fax_grp_y, wid_btn_curr, H_BUTTON, _(title_zoom));
700 	wefax_choice_zoom->callback(wefax_cb_pic_zoom, 0);
701 	for( int ix_zoom = 0; ix_zoom < zoom_nb ; ++ix_zoom ) {
702 		wefax_choice_zoom->add( all_zooms[ ix_zoom ].m_label );
703 	};
704 	wefax_choice_zoom->value(idx_default_zoom);
705 	wefax_choice_zoom->tooltip(_("Window zoom"));
706 	wefax_choice_zoom->align(FL_ALIGN_LEFT);
707 	return wefax_choice_zoom ;
708 }
709 
abort_rx_viewer(void)710 void wefax_pic::abort_rx_viewer(void)
711 {
712 	if (wefax_serviceme != active_modem) return;
713 	ENSURE_THREAD(FLMAIN_TID);
714 	put_status("");
715 
716 	/// Maybe the image is too high, we make it shorter.
717 	wefax_pic_rx_wefax_map->resize_height( curr_pix_h_default, true );
718 
719 	/// Now returns to the top of the image, and refresh the scrolling.
720 	wefax_pic_rx_wefax_map->position( wefax_pic_rx_wefax_map->x(), 0 );
721 	wefax_pic_rx_scroll->redraw();
722 
723 	curr_pix_height = curr_pix_h_default ;
724 	rx_last_filtered_row = 0;
725 	center_val_prev = 0 ;
726 	wefax_rx_center->value(0.0);
727 
728 	wefax_btn_rx_abort->hide();
729 	wefax_btn_rx_skip_apt->show();
730 	wefax_btn_rx_skip_phasing->hide();
731 
732 // Back to the first line before reading next image.
733 	wefax_pic_rx_scroll->scroll_to( 0, 0 );
734 }
735 
736 /// The resizing is different from the base class.
737 class wefax_map_scroll : public wefax_map
738 {
739 	public:
740 	/// Background color is gray.
wefax_map_scroll(int X,int Y,int W,int H)741 	wefax_map_scroll(int X, int Y, int W, int H) :
742 		wefax_map (X, Y, W, H, 255)  {};
743 
~wefax_map_scroll()744 	virtual ~wefax_map_scroll() {};
745 
746 	/// wefax_map::resize destroys the image, we do not want that when displaying.
resize(int x,int y,int w,int h)747 	virtual void resize(int x, int y, int w, int h)
748 	{
749 		Fl_Widget::resize( x, y, w, h );
750 	}
751 
752 	/// This must not process slant this way, so inhibits wefax_map::handle.
handle(int event)753 	virtual int handle(int event)
754 	{
755 		return 0 ;
756 	}
757 };
758 
759 static void wefax_load_image(const char * fil_name);
760 static const int extra_win_wid = 800 ;
761 
create_wefax_rx_viewer(int pos_x,int pos_y,int win_wid,int hei_win)762 Fl_Group *create_wefax_rx_viewer(int pos_x, int pos_y,int win_wid, int hei_win)
763 {
764 	rx_slant_ratio = progdefaults.wefax_slant ;
765 
766 	wefax_pic_rx_win = new Fl_Group(pos_x, pos_y, win_wid, hei_win);
767 
768 	wefax_pic_rx_win->color(
769 			fl_rgb_color(
770 				progdefaults.RxColor.R,
771 				progdefaults.RxColor.G,
772 				progdefaults.RxColor.B),
773 			progdefaults.RxTxSelectcolor);
774 	wefax_pic_rx_win->align(FL_ALIGN_CLIP);
775 
776 
777 	/// A bit wider so that it does not scroll at the beginning.
778 
779 	int H_MARGIN = 1;
780 	int W_MARGIN = 1;
781 	int H_BUTTON = 22;
782 
783 	Fl_Font font = fl_font();
784 	int fsize = fl_size();
785 	fl_font(FL_HELVETICA, 12);
786 
787 	int wid_img = win_wid - 2 * W_MARGIN;
788 	int hei_scroll = hei_win - (H_BUTTON + 3 * H_MARGIN);
789 
790 	wefax_pic_rx_win->begin();
791 
792 	wefax_pic_rx_scroll = new Fl_Scroll( pos_x + W_MARGIN, pos_y, wid_img, hei_scroll );
793 	wefax_pic_rx_scroll->type(Fl_Scroll::HORIZONTAL | Fl_Scroll::VERTICAL | Fl_Scroll::ALWAYS_ON);
794 	wefax_pic_rx_scroll->color(
795 				fl_rgb_color(
796 					255,
797 					255,
798 					255),
799 				progdefaults.RxTxSelectcolor);
800 	wefax_pic_rx_scroll->box(FL_ENGRAVED_FRAME);
801 	wefax_pic_rx_scroll->begin();
802 
803 	/// It will be resized immediately.
804 	wefax_pic_rx_wefax_map = new wefax_map_scroll(
805 			wefax_pic_rx_scroll->x(), wefax_pic_rx_scroll->y(),
806 			wid_img, curr_pix_height);
807 	wefax_pic_rx_wefax_map->align(FL_ALIGN_TOP);
808 	wefax_pic_rx_wefax_map->set_zoom( all_zooms[ idx_default_zoom ].m_value );
809 
810 	wefax_pic_rx_scroll->end();
811 
812 	/// Sets the group at a big size, we will resize the width at the end.
813 	Fl_Group * tmpGroup = new Fl_Group(
814 		W_MARGIN, pos_y + hei_scroll,
815 		wid_img, H_BUTTON + 2 * H_MARGIN);
816 
817 	tmpGroup->begin();
818 	tmpGroup->box( FL_DOWN_BOX);
819 	tmpGroup->color(fl_rgb_color(144, 175, 200));
820 
821 	wefax_btn_rx_save = new Fl_Button(
822 		tmpGroup->x() + W_MARGIN, tmpGroup->y() + H_MARGIN,
823 		35, H_BUTTON, _("Save"));
824 	wefax_btn_rx_save->callback(wefax_cb_pic_rx_save, 0);
825 	wefax_btn_rx_save->tooltip(_("Save current image in a file."));
826 
827 	/// Clear, Skipt APT and Skip phasing are at the same place
828 	wefax_btn_rx_abort = new Fl_Button(
829 		wefax_btn_rx_save->x() + wefax_btn_rx_save->w() + W_MARGIN,
830 		wefax_btn_rx_save->y(),
831 		45, H_BUTTON, _("Clear"));
832 	wefax_btn_rx_abort->callback(wefax_cb_pic_rx_abort, 0);
833 	wefax_btn_rx_abort->tooltip(_("End and clear current image reception."));
834 
835 	wefax_btn_rx_skip_apt = new Fl_Button(
836 		wefax_btn_rx_abort->x(), wefax_btn_rx_abort->y(),
837 		wefax_btn_rx_abort->w(), wefax_btn_rx_abort->h(),
838 		_("APT"));
839 	wefax_btn_rx_skip_apt->callback(wefax_cb_pic_rx_skip_apt, 0);
840 	wefax_btn_rx_skip_apt->tooltip(_("Skip Automatic Picture Transmission step"));
841 
842 	wefax_btn_rx_skip_phasing = new Fl_Button(
843 		wefax_btn_rx_abort->x(), wefax_btn_rx_abort->y(),
844 		wefax_btn_rx_abort->w(), wefax_btn_rx_abort->h(),
845 		_("Phase"));
846 	wefax_btn_rx_skip_phasing->callback(wefax_cb_pic_rx_skip_phasing, 0);
847 	wefax_btn_rx_skip_phasing->tooltip(_("Skip phasing step"));
848 
849 	wefax_btn_rx_pause = new Fl_Button(
850 		wefax_btn_rx_skip_phasing->x() + wefax_btn_rx_skip_phasing->w() + W_MARGIN,
851 		wefax_btn_rx_skip_phasing->y(),
852 		45, H_BUTTON, _("Pause"));
853 	wefax_btn_rx_pause->callback(wefax_cb_pic_rx_pause, 0);
854 	wefax_btn_rx_pause->tooltip(_("Pause reception."));
855 
856 	wefax_btn_rx_resume = new Fl_Button(
857 		wefax_btn_rx_skip_phasing->x(), wefax_btn_rx_skip_phasing->y(),
858 		wefax_btn_rx_skip_phasing->w(), wefax_btn_rx_skip_phasing->h(),
859 		_("Resume"));
860 	wefax_btn_rx_resume->callback(wefax_cb_pic_rx_resume, 0);
861 	wefax_btn_rx_resume->tooltip(_("Resume reception."));
862 	wefax_btn_rx_resume->hide();
863 
864 	wefax_round_rx_non_stop = new Fl_Light_Button(
865 		wefax_btn_rx_resume->x() + wefax_btn_rx_resume->w() + W_MARGIN,
866 		wefax_btn_rx_resume->y(),
867 		50, H_BUTTON, _("Cont'"));
868 	wefax_round_rx_non_stop->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
869 	wefax_round_rx_non_stop->selection_color(progdefaults.default_btn_color);
870 	wefax_round_rx_non_stop->callback(wefax_cb_pic_rx_manual, 0);
871 	wefax_round_rx_non_stop->tooltip(_("Continuous reception mode"));
872 
873 	int zw = fl_width(_("Mag")) + 5;
874 	wefax_choice_rx_zoom = new Fl_Choice(
875 		wefax_round_rx_non_stop->x() + wefax_round_rx_non_stop->w() + W_MARGIN + zw,
876 		wefax_round_rx_non_stop->y(),
877 		60, H_BUTTON, _("Mag") );
878 	wefax_choice_rx_zoom->callback(wefax_cb_pic_zoom, 0);
879 	for( int ix_zoom = 0; ix_zoom < zoom_nb ; ++ix_zoom )
880 		wefax_choice_rx_zoom->add( all_zooms[ ix_zoom ].m_label );
881 	wefax_choice_rx_zoom->value(idx_default_zoom);
882 	wefax_choice_rx_zoom->tooltip(_("Image magnification"));
883 	wefax_choice_rx_zoom->align(FL_ALIGN_LEFT);
884 
885 	zw = fl_width(_("Row")) + 5;
886 	wefax_out_rx_row_num = new Fl_Output(
887 		wefax_choice_rx_zoom->x() + wefax_choice_rx_zoom->w() + zw + W_MARGIN,
888 		wefax_choice_rx_zoom->y(),
889 		50, H_BUTTON, _("Row"));
890 	wefax_out_rx_row_num->align(FL_ALIGN_LEFT);
891 	wefax_out_rx_row_num->tooltip(_("Fax line number being read. Image is saved when reaching max lines."));
892 
893 	zw = fl_width(_("Tilt")) + 5;
894 	wefax_cnt_rx_ratio = new Fl_Counter(
895 		wefax_out_rx_row_num->x() + wefax_out_rx_row_num->w() + zw,
896 		wefax_out_rx_row_num->y(),
897 		120, H_BUTTON, _("Tilt"));
898 	wefax_cnt_rx_ratio->align(FL_ALIGN_LEFT);
899 	wefax_cnt_rx_ratio->step(.0001);
900 	wefax_cnt_rx_ratio->lstep(.001);
901 	wefax_cnt_rx_ratio->precision( 4 );
902 	wefax_cnt_rx_ratio->callback(wefax_cb_pic_ratio, 0);
903 	wefax_cnt_rx_ratio->value(rx_slant_ratio);
904 	wefax_cnt_rx_ratio->tooltip(_("Adjust image slant to correct soundcard clock error."));
905 
906 	zw = fl_width(_("Align")) + 5;
907 	wefax_rx_center = new Fl_Counter(
908 		wefax_cnt_rx_ratio->x() + wefax_cnt_rx_ratio->w() + zw,
909 		wefax_cnt_rx_ratio->y(),
910 		130, H_BUTTON, _("Align"));
911 	wefax_rx_center->align(FL_ALIGN_LEFT);
912 	/// The range is set when the image size in pixels is known.
913 	wefax_rx_center->step(1.0);
914 	wefax_rx_center->lstep(10.0);
915 	wefax_rx_center->callback(wefax_cb_pic_rx_center, 0);
916 	wefax_rx_center->tooltip(_("Align image horizontally."));
917 	center_val_prev = 0 ;
918 
919 	wefax_autocenter = new Fl_Light_Button(
920 		wefax_rx_center->x() + wefax_rx_center->w() + W_MARGIN,
921 		wefax_rx_center->y(),
922 		45, H_BUTTON,
923 		"Auto");
924 	wefax_autocenter->selection_color(progdefaults.default_btn_color);
925 	wefax_autocenter->callback(cb_wefax_autocenter, 0);
926 	wefax_autocenter->tooltip(_("Enable automatic image centering"));
927 	wefax_autocenter->value(progdefaults.wefax_autocenter);
928 
929 	wefax_round_rx_noise_removal = new Fl_Light_Button(
930 		wefax_autocenter->x() + wefax_autocenter->w() + W_MARGIN,
931 		wefax_autocenter->y(),
932 		50, H_BUTTON, _("Noise"));
933 	wefax_round_rx_noise_removal->callback(wefax_cb_pic_rx_noise_removal, 0);
934 	wefax_round_rx_noise_removal->tooltip(_("Removes noise when ticked"));
935 	wefax_round_rx_noise_removal->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
936 	wefax_round_rx_noise_removal->selection_color(progdefaults.default_btn_color);
937 
938 	wefax_round_rx_binary = new Fl_Light_Button(
939 		wefax_round_rx_noise_removal->x() + wefax_round_rx_noise_removal->w() + W_MARGIN,
940 		wefax_round_rx_noise_removal->y(),
941 		35, H_BUTTON, _("Bin"));
942 	wefax_round_rx_binary->callback(wefax_cb_pic_rx_binary, 0);
943 	wefax_round_rx_binary->tooltip(_("Binary image when ticked"));
944 	wefax_round_rx_binary->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
945 	wefax_round_rx_binary->selection_color(progdefaults.default_btn_color);
946 	wefax_round_rx_binary->value(0);
947 
948 	wefax_spinner_rx_binary = new Fl_Spinner(
949 		wefax_round_rx_binary->x() + wefax_round_rx_binary->w() + W_MARGIN,
950 		wefax_round_rx_binary->y(),
951 		50, H_BUTTON );
952 	wefax_spinner_rx_binary->callback(wefax_cb_pic_rx_bin_threshold, 0);
953 	wefax_spinner_rx_binary->tooltip(_("Set binarization level"));
954 	wefax_spinner_rx_binary->format("%d");
955 	wefax_spinner_rx_binary->type(FL_INT_INPUT);
956 	wefax_spinner_rx_binary->range(0.0, 255.0);
957 	wefax_spinner_rx_binary->step(1.0);
958 	wefax_spinner_rx_binary->value(wefax_pic_rx_wefax_map->get_binary_threshold());
959 
960 	Fl_Box *filler = new Fl_Box(
961 		wefax_spinner_rx_binary->x() + wefax_spinner_rx_binary->w(),
962 		wefax_spinner_rx_binary->y(),
963 		2, H_BUTTON );
964 	filler->box(FL_NO_BOX);
965 
966 	tmpGroup->end();
967 	tmpGroup->resizable(filler);//wefax_browse_rx_events);
968 	tmpGroup->init_sizes();
969 
970 	wefax_pic_rx_win->end();
971 
972 	wefax_pic_rx_win->resizable( wefax_pic_rx_scroll );
973 
974 	wefax_pic_rx_win->init_sizes();
975 	wefax_pic_rx_win->redraw();
976 
977 	fl_font(font, fsize);
978 
979 	return wefax_pic_rx_win;
980 }
981 
resize_rx_viewer(int wid_img)982 void wefax_pic::resize_rx_viewer(int wid_img)
983 {
984 //	return;
985 	ENSURE_THREAD(FLMAIN_TID);
986 
987 	abort_rx_viewer();
988 	wefax_pic_rx_wefax_map->wefax_map::resize(
989 		wefax_pic_rx_wefax_map->x(), wefax_pic_rx_wefax_map->y(),
990 		wid_img, curr_pix_h_default );
991 	wefax_pic_rx_win->redraw();
992 }
993 
set_rx_label(const std::string & win_label)994 void wefax_pic::set_rx_label(const std::string & win_label)
995 {
996 	std::string tmp_label( win_label );
997 	if( reception_paused ) {
998 		tmp_label += " paused" ;
999 	}
1000 }
1001 
save_image(const std::string & fil_name,const std::string & extra_comments)1002 void wefax_pic::save_image(const std::string & fil_name, const std::string & extra_comments )
1003 {
1004 	std::string dfname = default_dir_get( progdefaults.wefax_save_dir ) + fil_name ;
1005 
1006 	std::stringstream local_comments;
1007 	local_comments << extra_comments ;
1008 	local_comments << "Slant:" << rx_slant_ratio << "\n" ;
1009 	local_comments << "Auto-Center:" << ( progdefaults.wefax_autocenter ? "On" : "Off" ) << "\n" ;
1010 	wefax_pic_rx_wefax_map->save_png(
1011 		dfname.c_str(),
1012 		local_comments.str().c_str());
1013 //	add_to_files_list( dfname );
1014 	qso_notes( "RX:", fil_name );
1015 	wefax_serviceme->qso_rec_save();
1016 }
1017 
1018 /// Protected by an exclusive mutex.
wefax_load_image_after_acquire(const char * fil_name)1019 static std::string wefax_load_image_after_acquire(const char * fil_name)
1020 {
1021 	if (wefax_serviceme != active_modem) return "Not in WEFAX mode";
1022 	ENSURE_THREAD(FLMAIN_TID);
1023 
1024 	wefax_serviceme->qso_rec_init();
1025 	qso_notes( "TX:", fil_name );
1026 
1027 	clear_image();
1028 	wefax_shared_tx_img = Fl_Shared_Image::get(fil_name);
1029 	if (!wefax_shared_tx_img) {
1030 		std::string err_msg("Cannot call Fl_Shared_Image::get on file:" + std::string(fil_name) );
1031 		LOG_ERROR("%s",err_msg.c_str());
1032 		return err_msg;
1033 	}
1034 	if (wefax_shared_tx_img->count() > 1) { // we only handle rgb images
1035 		std::string err_msg("Handle only RGB images: " + std::string(fil_name)  );
1036 		LOG_ERROR("%s",err_msg.c_str());
1037 		clear_image();
1038 		return err_msg;
1039 	}
1040 	unsigned char * img_data = (unsigned char *)wefax_shared_tx_img->data()[0];
1041 	int img_wid = wefax_shared_tx_img->w();
1042 	int img_hei = wefax_shared_tx_img->h();
1043 	int img_depth = wefax_shared_tx_img->d();
1044 	wefax_xmtimg = new unsigned char [img_wid * img_hei * bytes_per_pix];
1045 	if (img_depth == bytes_per_pix)
1046 		memcpy(wefax_xmtimg, img_data, img_wid*img_hei*bytes_per_pix);
1047 	else if (img_depth == 4) {
1048 		int i, j, k;
1049 		for (i = 0; i < img_wid*img_hei; i++) {
1050 			j = i*bytes_per_pix; k = i*4;
1051 			wefax_xmtimg[j] = img_data[k];
1052 			wefax_xmtimg[j+1] = img_data[k+1];
1053 			wefax_xmtimg[j+2] = img_data[k+2];
1054 		}
1055 	} else if (img_depth == 1) {
1056 		int i, j;
1057 		for (i = 0; i < img_wid*img_hei; i++) {
1058 			j = i * bytes_per_pix;
1059 			wefax_xmtimg[j] = wefax_xmtimg[j+1] = wefax_xmtimg[j+2] = img_data[i];
1060 		}
1061 	} else {
1062 		std::stringstream err_strm ;
1063 		err_strm << "Inconsistent img_depth=" << img_depth << " for " << fil_name ;
1064 		std::string err_msg = err_strm.str();
1065 		LOG_ERROR("%s",err_msg.c_str());
1066 		return err_msg ;
1067 	};
1068 
1069 	wefax_pic::tx_viewer_resize(img_wid, img_hei);
1070 
1071 	set_win_label(wefax_pic_tx_win, fil_name);
1072 
1073 	wefax_pic_tx_box->label(0);
1074 
1075 	// load the wefax_map widget with the rgb image
1076 	wefax_pic_tx_wefax_map->show();
1077 	wefax_pic_tx_wefax_map->clear();
1078 	wefax_pic_tx_wefax_map->video(wefax_xmtimg, img_wid * img_hei * bytes_per_pix);
1079 
1080 	int tim_color = wefax_serviceme->tx_time( img_wid * img_hei * bytes_per_pix );
1081 	static char wefax_txclr_tooltip[24];
1082 	snprintf( wefax_txclr_tooltip, sizeof(wefax_txclr_tooltip),
1083 		_("Time needed: %dm %ds (Color)"), tim_color/60, tim_color % 60 );
1084 	wefax_btn_tx_send_color->tooltip(wefax_txclr_tooltip);
1085 	if( false ) {
1086 		// No color transmission now because no information this format.
1087 		wefax_btn_tx_send_color->activate();
1088 	}
1089 
1090 	int tim_grey = wefax_serviceme->tx_time( img_wid * img_hei );
1091 	static char wefax_txgry_tooltip[24];
1092 	snprintf( wefax_txgry_tooltip, sizeof(wefax_txgry_tooltip),
1093 		_("Time needed: %dm %ds (B/W)"), tim_grey/60, tim_grey % 60 );
1094 	wefax_btn_tx_send_grey->tooltip(wefax_txgry_tooltip);
1095 	wefax_btn_tx_send_grey->activate();
1096 
1097 	wefax_choice_tx_zoom->activate();
1098 	wefax_btn_tx_clear->activate();
1099 
1100 	return std::string();
1101 }
1102 
wefax_load_image(const char * fil_name)1103 static void wefax_load_image(const char * fil_name)
1104 {
1105 	if (wefax_serviceme != active_modem) return;
1106 	if( !wefax_image_loaded_in_gui ) {
1107 		/// So we do not re-acquire the exclusive access to wefax transmission.
1108 		wefax_serviceme->transmit_lock_acquire(fil_name);
1109 		wefax_image_loaded_in_gui = true ;
1110 	}
1111 	wefax_load_image_after_acquire(fil_name);
1112 }
1113 
set_tx_pic(unsigned char data,int col,int row,bool is_color)1114 void wefax_pic::set_tx_pic(unsigned char data, int col, int row, bool is_color )
1115 {
1116 	if (wefax_serviceme != active_modem) return;
1117 	ENSURE_THREAD(FLMAIN_TID);
1118 	if( ( col >= wefax_shared_tx_img->w() )
1119 	 || ( row >= wefax_shared_tx_img->h() ) ) {
1120 		LOG_ERROR("invalid col=%d row=%d w=%d h=%d", col, row, wefax_shared_tx_img->w(), wefax_shared_tx_img->h() );
1121 		exit(EXIT_FAILURE);
1122 	}
1123 
1124 	int offset = row * wefax_shared_tx_img->w() + col ;
1125 
1126 	if (is_color) {
1127 		wefax_pic_tx_wefax_map->pixel( data, offset );
1128 	} else {
1129 		int tripleOffset = bytes_per_pix * offset ;
1130 		wefax_pic_tx_wefax_map->pixel( data, tripleOffset );
1131 		wefax_pic_tx_wefax_map->pixel( data, tripleOffset + 1 );
1132 		wefax_pic_tx_wefax_map->pixel( data, tripleOffset + 2 );
1133 	}
1134 
1135 	static int previous_row = -1 ;
1136 	if( row != previous_row )
1137 	{
1138 		previous_row = row ;
1139 		char num_buffer[20];
1140 		snprintf( num_buffer, sizeof(num_buffer), "%d", row );
1141 		wefax_out_tx_row_num->value( num_buffer );
1142 		snprintf( num_buffer, sizeof(num_buffer), "%d", wefax_shared_tx_img->w() );
1143 		wefax_out_tx_col_num->value( num_buffer );
1144 	}
1145 }
1146 
wefax_cb_pic_tx_load(Fl_Widget *,void *)1147 static void wefax_cb_pic_tx_load(Fl_Widget *, void *)
1148 {
1149 	const char *fil_name =
1150 		FSEL::select(_("Load image file"), "Portable Network Graphics\t*.png\n"
1151 			"Independent JPEG Group\t*.{jpg,jif,jpeg,jpe}\n"
1152 			"Graphics Interchange Format\t*.gif",
1153 			default_dir_get( progdefaults.wefax_load_dir ).c_str() );
1154 	if (!fil_name) return;
1155 	if (!*fil_name) return;
1156 
1157 	/// Next time, image will be saved at the same place.
1158 	default_dir_set( progdefaults.wefax_load_dir, fil_name );
1159 	wefax_load_image(fil_name);
1160 }
1161 
1162 /// Called whether color or b/w image.
wefax_pic_tx_send_common(bool is_color)1163 static void wefax_pic_tx_send_common(
1164 		bool is_color )
1165 {
1166 	ENSURE_THREAD(FLMAIN_TID);
1167 
1168 	if (wefax_serviceme != active_modem) return;
1169 
1170 	int img_w = wefax_shared_tx_img->w();
1171 	int img_h = wefax_shared_tx_img->h();
1172 
1173 	wefax_choice_tx_lpm->hide();
1174 	wefax_btn_tx_send_color->hide();
1175 	wefax_btn_tx_send_grey->hide();
1176 	wefax_btn_tx_load->hide();
1177 	wefax_choice_tx_zoom->hide();
1178 	wefax_btn_tx_clear->hide();
1179 	wefax_btn_tx_close->hide();
1180 	wefax_out_tx_row_num->show();
1181 	wefax_out_tx_col_num->show();
1182 	wefax_btn_tx_send_abort->show();
1183 	wefax_pic_tx_wefax_map->clear();
1184 
1185 	wefax_out_tx_row_num->value( "Init" );
1186 	wefax_out_tx_col_num->value( "" );
1187 
1188 	wefax_serviceme->set_tx_parameters(
1189 			get_choice_lpm_value( wefax_choice_tx_lpm ),
1190 			wefax_xmtimg,
1191 			is_color,
1192 			img_w,
1193 			img_h );
1194 
1195 	start_tx();
1196 }
1197 
wefax_cb_pic_tx_send_color(Fl_Widget *,void *)1198 static void wefax_cb_pic_tx_send_color( Fl_Widget * , void *)
1199 {
1200 	wefax_pic_tx_send_common(true);
1201 }
1202 
wefax_cb_pic_tx_send_grey(Fl_Widget *,void *)1203 static void wefax_cb_pic_tx_send_grey( Fl_Widget *, void *)
1204 {
1205 	wefax_pic_tx_send_common(false);
1206 }
1207 
1208 
wefax_cb_pic_tx_send_abort(Fl_Widget *,void *)1209 static void wefax_cb_pic_tx_send_abort( Fl_Widget *, void *)
1210 {
1211 	if (wefax_serviceme != active_modem) return;
1212 
1213 	/// Maybe we are not sending an image.
1214 	if( wefax_shared_tx_img ) {
1215 		wefax_serviceme->set_tx_abort_flag();
1216 		// reload the wefax_map widget with the rgb image
1217 		wefax_pic_tx_wefax_map->video(wefax_xmtimg, wefax_shared_tx_img->w() * wefax_shared_tx_img->h() * bytes_per_pix);
1218 	}
1219 }
1220 
restart_tx_viewer(void)1221 void wefax_pic::restart_tx_viewer(void)
1222 {
1223 	ENSURE_THREAD(FLMAIN_TID);
1224 	wefax_out_tx_row_num->hide();
1225 	wefax_out_tx_col_num->hide();
1226 	wefax_btn_tx_send_abort->hide();
1227 	wefax_btn_tx_load->show();
1228 	wefax_btn_tx_close->show();
1229 	if( wefax_image_loaded_in_gui )
1230 	{
1231 // If the image was loaded from the GUI.
1232 		wefax_choice_tx_lpm->show();
1233 		wefax_btn_tx_send_color->show();
1234 		wefax_btn_tx_send_grey->show();
1235 		wefax_choice_tx_zoom->show();
1236 		wefax_btn_tx_clear->show();
1237 	}
1238 	else
1239 	{
1240 		/// If the image was loaded and sent from XML-RPC, or no image present.
1241 		wefax_choice_tx_lpm->deactivate();
1242 		wefax_btn_tx_send_color->deactivate();
1243 		wefax_btn_tx_send_grey->deactivate();
1244 		wefax_choice_tx_zoom->deactivate();
1245 		wefax_btn_tx_clear->deactivate();
1246 	}
1247 }
1248 
1249 //void wefax_pic::create_wefax_tx_viewer(int pos_x, int pos_y,int win_wid, int hei_win)
create_wefax_tx_viewer(int pos_x,int pos_y,int win_wid,int hei_win)1250 void create_wefax_tx_viewer(int pos_x, int pos_y,int win_wid, int hei_win)
1251 {
1252 	ENSURE_THREAD(FLMAIN_TID);
1253 
1254 	int wid_btn_margin = 5 ;
1255 
1256 	Fl_Double_Window * tmpWin = new Fl_Double_Window(win_wid, hei_win, _("Send image"));
1257 	wefax_pic_tx_win = tmpWin;
1258 
1259 	wefax_pic_tx_win->color(
1260 			fl_rgb_color(
1261 				progdefaults.TxColor.R,
1262 				progdefaults.TxColor.G,
1263 				progdefaults.TxColor.B),
1264 			progdefaults.RxTxSelectcolor);
1265 
1266 	wefax_pic_tx_win->begin();
1267 
1268 	wefax_pic_tx_scroll = new Fl_Scroll( 1, 1, win_wid-2, hei_win - 23 );
1269 	wefax_pic_tx_scroll->type(Fl_Scroll::HORIZONTAL | Fl_Scroll::VERTICAL);
1270 	wefax_pic_tx_scroll->color(
1271 				fl_rgb_color(
1272 					255,
1273 					255,
1274 					255),
1275 				progdefaults.RxTxSelectcolor);
1276 	wefax_pic_tx_scroll->box(FL_ENGRAVED_FRAME);
1277 	wefax_pic_tx_scroll->begin();
1278 
1279 	/// It will be resized immediately when an image is loaded.
1280 	wefax_pic_tx_wefax_map = new wefax_map_scroll( 0,0,0,0);
1281 	wefax_pic_tx_wefax_map->align(FL_ALIGN_TOP);
1282 	wefax_pic_tx_wefax_map->set_zoom( all_zooms[ idx_default_zoom ].m_value );
1283 
1284 	wefax_pic_tx_scroll->end();
1285 
1286 	wefax_pic_tx_win->resizable( wefax_pic_tx_scroll );
1287 	wefax_pic_tx_box = new wefax_picbox(
1288 			wefax_pic_tx_scroll->x(),
1289 			wefax_pic_tx_scroll->y(),
1290 			wefax_pic_tx_scroll->w(),
1291 			wefax_pic_tx_scroll->h(),
1292 		      _("Loads an image file\nSupported types: PNG, JPEG, BMP"));
1293 	wefax_pic_tx_box->labelfont(FL_HELVETICA_ITALIC);
1294 
1295 	static const int last_margin = 21 ;
1296 	static const int y_btn = hei_win - last_margin ;
1297 	static const int hei_tx_btn = 20 ;
1298 
1299 	Fl_Group * tmpGroup = new Fl_Group( 0, y_btn, extra_win_wid, last_margin );
1300 	tmpGroup->begin();
1301 	int width_btn = 30;
1302 	int width_offset = 30;
1303 
1304 	width_btn = 50 ;
1305 	wefax_choice_tx_lpm = make_lpm_choice( width_offset, y_btn, width_btn, hei_tx_btn );
1306 
1307 	width_offset += width_btn + wid_btn_margin ;
1308 	width_btn = 55 ;
1309 	wefax_btn_tx_send_color = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, "Tx Color");
1310 	wefax_btn_tx_send_color->callback(wefax_cb_pic_tx_send_color, 0);
1311 	wefax_btn_tx_send_color->tooltip(_("Starts transmit in color mode"));
1312 
1313 	width_offset += width_btn + wid_btn_margin ;
1314 	width_btn = 45 ;
1315 	wefax_btn_tx_send_grey = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, "Tx B/W");
1316 	wefax_btn_tx_send_grey->callback( wefax_cb_pic_tx_send_grey, 0);
1317 	wefax_btn_tx_send_grey->tooltip(_("Starts transmit in gray level mode"));
1318 
1319 	width_offset += width_btn + wid_btn_margin ;
1320 	width_btn = 55 ;
1321 	wefax_btn_tx_load = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, _("Load..."));
1322 	wefax_btn_tx_load->callback(wefax_cb_pic_tx_load, 0);
1323 	wefax_btn_tx_load->tooltip(_("Load image to send"));
1324 
1325 	width_offset += width_btn + wid_btn_margin + 40 ;
1326 	width_btn = 58 ;
1327 	wefax_choice_tx_zoom = wefax_create_zoom( width_offset, y_btn, width_btn, hei_tx_btn );
1328 
1329 	width_offset += width_btn + wid_btn_margin ;
1330 	width_btn = 45 ;
1331 	wefax_btn_tx_clear = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, _("Clear"));
1332 	wefax_btn_tx_clear->callback(wefax_cb_pic_tx_clear, 0);
1333 	wefax_btn_tx_clear->tooltip(_("Clear image to transmit"));
1334 
1335 	width_offset += width_btn + wid_btn_margin ;
1336 	width_btn = 45 ;
1337 	wefax_btn_tx_close = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, _("Close"));
1338 	wefax_btn_tx_close->callback(wefax_cb_pic_tx_close, 0);
1339 	wefax_btn_tx_close->tooltip(_("Close transmit window"));
1340 
1341 	wefax_out_tx_row_num = new Fl_Output(20, y_btn, 50, hei_tx_btn );
1342 	wefax_out_tx_row_num->align(FL_ALIGN_LEFT);
1343 	wefax_out_tx_row_num->tooltip(_("Fax line number being sent."));
1344 
1345 	wefax_out_tx_col_num = new Fl_Output(80, y_btn, 50, hei_tx_btn, "x" );
1346 	wefax_out_tx_col_num->align(FL_ALIGN_LEFT);
1347 	wefax_out_tx_col_num->tooltip(_("Fax column number."));
1348 
1349 	wefax_btn_tx_send_abort = new Fl_Button(180, y_btn, 100, hei_tx_btn, _("Abort Transmit") );
1350 	wefax_btn_tx_send_abort->callback(wefax_cb_pic_tx_send_abort, 0);
1351 	wefax_btn_tx_send_abort->tooltip(_("Abort transmission"));
1352 
1353 	wefax_out_tx_row_num->hide();
1354 	wefax_out_tx_col_num->hide();
1355 	wefax_btn_tx_send_abort->hide();
1356 	wefax_btn_tx_send_color->deactivate();
1357 	wefax_btn_tx_send_grey->deactivate();
1358 	wefax_choice_tx_zoom->deactivate();
1359 	wefax_btn_tx_clear->deactivate();
1360 
1361 	tmpGroup->end();
1362 	wefax_pic_tx_win->end();
1363 	wefax_pic_rx_win->init_sizes();
1364 	wefax_pic_rx_win->redraw();
1365 }
1366 
abort_tx_viewer(void)1367 void wefax_pic::abort_tx_viewer(void)
1368 {
1369 	wefax_cb_pic_tx_send_abort(NULL,NULL);
1370 	wefax_cb_pic_tx_close(NULL,NULL);
1371 }
1372 
tx_viewer_resize(int the_width,int the_height)1373 void wefax_pic::tx_viewer_resize(int the_width, int the_height)
1374 {
1375 	ENSURE_THREAD(FLMAIN_TID);
1376 	LOG_DEBUG("the_width=%d the_height=%d", the_width, the_height );
1377 
1378 	int win_width = the_width < 288 ? 290 : the_width + 4;
1379 	int win_height = the_height < 180 ? 180 : the_height + 30;
1380 	int pic_x = (win_width - the_width) / 2;
1381 	int pic_y =  (win_height - 30 - the_height)/2;
1382 
1383 	/// This because it is a wefax_map_scroll object.
1384 	wefax_pic_tx_wefax_map->wefax_map::resize(pic_x, pic_y, the_width, the_height);
1385 
1386 	wefax_pic_tx_wefax_map->clear();
1387 	wefax_pic_tx_box->size(win_width, win_height);
1388 }
1389 
1390 /// TODO: This crashes but should be called.
delete_tx_viewer()1391 void wefax_pic::delete_tx_viewer()
1392 {
1393 	ENSURE_THREAD(FLMAIN_TID);
1394 	wefax_pic_tx_win->hide();
1395 	wefax_serviceme = 0;
1396 
1397 	/// First delete the Fl_Widget.
1398 	delete wefax_pic_tx_win;
1399 	wefax_pic_tx_win = 0;
1400 
1401 	delete [] wefax_xmtimg;
1402 	wefax_xmtimg = 0;
1403 }
1404 
1405 /// TODO: This crashes.
delete_rx_viewer()1406 void wefax_pic::delete_rx_viewer()
1407 {
1408 	ENSURE_THREAD(FLMAIN_TID);
1409 	wefax_pic_rx_win->hide();
1410 	wefax_serviceme = 0;
1411 
1412 	/// These objects are deleted with the main window.
1413 	wefax_pic_tx_win = 0;
1414 	wefax_pic_rx_win = 0;
1415 }
1416 
setwefax_map_link(wefax * me)1417 void wefax_pic::setwefax_map_link(wefax *me)
1418 {
1419 	wefax_serviceme = me;
1420 }
1421 
1422 /// Called by the main menu bar to open explicitely a wefax transmission window.
cb_mnu_pic_viewer_tx(Fl_Menu_ *,void *)1423 void wefax_pic::cb_mnu_pic_viewer_tx(Fl_Menu_ *, void * ) {
1424 	if ( ! wefax_pic_tx_win) {
1425 		LOG_ERROR("wefax_tx_win should be created");
1426 		return ;
1427 	}
1428 	progdefaults.WEFAX_HideTx = ! progdefaults.WEFAX_HideTx ;
1429 	wefax_pic_show_tx();
1430 }
1431 
1432 /// Called from XML-RPC thread.
send_image(const std::string & fil_nam)1433 void wefax_pic::send_image( const std::string & fil_nam )
1434 {
1435 	LOG_VERBOSE("Sending %s", fil_nam.c_str() );
1436 	/// Here, transmit_lock_acquire is called by the XML-RPC client.
1437 	std::string err_msg = wefax_load_image_after_acquire( fil_nam.c_str() );
1438 	if( ! err_msg.empty() )
1439 	{
1440 		if (wefax_serviceme == active_modem)
1441 		{
1442 			/// Allows another XML-RPC client or the GUI to send an image.
1443 			wefax_serviceme->transmit_lock_release( err_msg );
1444 		}
1445 		return ;
1446 	}
1447 	wefax_cb_pic_tx_send_grey( NULL, NULL );
1448 	LOG_VERBOSE("Sent %s", fil_nam.c_str() );
1449 }
1450 
1451 /// This function is called at two places:
1452 // - When creating the main window.
1453 // - When initializing the fax modem.
create_both(bool called_from_fl_digi)1454 void wefax_pic::create_both(bool called_from_fl_digi)
1455 {
1456 	return;
1457 	if( wefax_pic_rx_win ) return ;
1458 	ENSURE_THREAD(FLMAIN_TID);
1459 
1460 //	Fl_Window *temp = new Fl_Window(0,0,400,400);
1461 //	wefax_pic_rx_wefax_map = new wefax_map_scroll(0, 0, 400, 200);
1462 //	wefax_pic_rx_wefax_map->align(FL_ALIGN_TOP);
1463 //	wefax_pic_rx_wefax_map->set_zoom( all_zooms[ idx_default_zoom ].m_value );
1464 //	temp->end();
1465 }
1466 
1467 
1468