1 // ----------------------------------------------------------------------------
2 // fsqpic.cxx  --  fsq image support functions
3 //
4 // Copyright (C) 2015
5 //		Dave Freese, W1HKJ
6 //
7 // This file is part of fldigi.  Adapted from code contained in gfsq source code
8 // distribution.
9 //
10 // Fldigi is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Fldigi is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
22 // ----------------------------------------------------------------------------
23 #include <FL/Fl_Counter.H>
24 #include <FL/Fl_Choice.H>
25 
26 #include "gettext.h"
27 #include "fileselect.h"
28 
29 Fl_Double_Window	*fsqpicRxWin = (Fl_Double_Window *)0;
30 picture				*fsqpicRx = (picture *)0;
31 Fl_Button			*btnfsqRxReset = (Fl_Button *)0;
32 Fl_Button			*btnfsqRxSave = (Fl_Button *)0;
33 Fl_Button			*btnfsqRxClose = (Fl_Button *)0;
34 Fl_Counter			*cnt_phase = (Fl_Counter *)0;
35 Fl_Counter			*cnt_slant = (Fl_Counter *)0;
36 
37 Fl_Double_Window	*fsqpicTxWin = (Fl_Double_Window *)0;
38 picture				*fsqpicTx = (picture *)0;
39 Fl_Button			*btnfsqpicTransmit = (Fl_Button *)0;
40 Fl_Button			*btnfsqpicTxSendAbort = (Fl_Button *)0;
41 Fl_Button			*btnfsqpicTxLoad = (Fl_Button *)0;
42 Fl_Button			*btnfsqpicTxClose = (Fl_Button *)0;
43 Fl_Choice			*selfsqpicSize = (Fl_Choice *)0;
44 
45 Fl_Shared_Image	*fsqTxImg = (Fl_Shared_Image *)0;
46 unsigned char *fsqxmtimg = (unsigned char *)0;
47 unsigned char *fsqxmtpicbuff = (unsigned char *)0;
48 
49 #define RAWSIZE 640*(480 + 8)*3*10
50 #define RAWSTART 640*4*3*10
51 unsigned char rawvideo[RAWSIZE + 1];
52 int numpixels;
53 int pixelptr;
54 int rawcol;
55 int rawrow;
56 int rawrgb;
57 char image_type = 'S';
58 
59 int	fsq_txSPP = 8;
60 
61 char fsq_txclr_tooltip[24];
62 char fsq_txgry_tooltip[24];
63 
64 static int translate = 0;
65 static bool enabled = false;
66 
correct_video()67 void correct_video()
68 {
69 	int W = fsqpicRx->w();
70 	int H = fsqpicRx->h();
71 	int slant = cnt_slant->value();
72 	int vidsize = W * H;
73 	int index, rowptr, colptr;
74 	float ratio = (((float)vidsize - (float)slant)/(float)vidsize);
75 	unsigned char vid[W * H * 3];
76 	for (int row = 0; row < H; row++) {
77 		rowptr = W * 3 * row * 10;
78 		for (int col = 0; col < W; col++) {
79 			colptr = 10*col;
80 			for (int rgb = 0; rgb < 3; rgb++) {
81 				index = ratio*(rowptr + colptr + 10*W*rgb);
82 				index += RAWSTART - 10*pixelptr;
83 				if (index < 2) index = 2;
84 				if (index > RAWSIZE - 2) index = RAWSIZE - 2;
85 				vid[2 - rgb + 3 * (col + row * W)] = rawvideo[index];
86 			}
87 		}
88 	}
89 	fsqpicRx->video(vid, W*H*3);
90 }
91 
fsq_updateRxPic(unsigned char data,int pos)92 void fsq_updateRxPic(unsigned char data, int pos)
93 {
94 	if (!fsqpicRxWin->shown()) fsqpicRx->show();
95 
96 	fsqpicRx->pixel(data, pos);
97 
98 	int W = fsqpicRx->w();
99 	if (image_type == 'F' || image_type == 'p' || image_type == 'm') {
100 		int n = RAWSTART + 10*(rawcol + W * (rawrgb + 3 * rawrow));
101 		if (n < RAWSIZE)
102 			for (int i = 0; i < 10; i++) rawvideo[n + i] = data;
103 		rawrgb++;
104 		if (rawrgb == 3) {
105 			rawrgb = 0;
106 			rawcol++;
107 			if (rawcol == W) {
108 				rawcol = 0;
109 				rawrow++;
110 			}
111 		}
112 	} else
113 		for (int i = 0; i < 10; i++) rawvideo[RAWSTART + 10*numpixels + i] = data;
114 	numpixels++;
115 	if (numpixels >= (RAWSIZE - RAWSTART - 10))
116 		numpixels = RAWSIZE - RAWSTART - 10;
117 }
118 
cb_btnfsqRxReset(Fl_Widget *,void *)119 void cb_btnfsqRxReset(Fl_Widget *, void *)
120 {
121 	progStatus.fsq_rx_abort = true;
122 }
123 
cb_btnfsqRxSave(Fl_Widget *,void *)124 void cb_btnfsqRxSave(Fl_Widget *, void *)
125 {
126 	fsqpicRx->save_png(PicsDir.c_str());
127 }
128 
cb_btnfsqRxClose(Fl_Widget *,void *)129 void cb_btnfsqRxClose(Fl_Widget *, void *)
130 {
131 	fsqpicRxWin->hide();
132 	progStatus.fsq_rx_abort = true;
133 }
134 
cb_cnt_phase(Fl_Widget *,void * data)135 void cb_cnt_phase(Fl_Widget *, void *data)
136 {
137 	pixelptr = cnt_phase->value();
138 	if (pixelptr >= RAWSTART/10) {
139 		pixelptr = RAWSTART/10 - 1;
140 		cnt_phase->value(pixelptr);
141 	}
142 	if (pixelptr < -RAWSTART/10) {
143 		pixelptr = -RAWSTART/10;
144 		cnt_phase->value(pixelptr);
145 	}
146 	correct_video();
147 }
148 
cb_cnt_slant(Fl_Widget *,void *)149 void cb_cnt_slant(Fl_Widget *, void *)
150 {
151 	correct_video();
152 }
153 
fsq_disableshift()154 void fsq_disableshift()
155 {
156 	if (!fsqpicRxWin) return;
157 	cnt_phase->deactivate();
158 	cnt_slant->deactivate();
159 	btnfsqRxSave->deactivate();
160 	fsqpicRxWin->redraw();
161 }
162 
fsq_enableshift()163 void fsq_enableshift()
164 {
165 	if (!fsqpicRxWin) return;
166 	cnt_phase->activate();
167 	cnt_slant->activate();
168 	btnfsqRxSave->activate();
169 	fsqpicRxWin->redraw();
170 }
171 
fsq_createRxViewer()172 void fsq_createRxViewer()
173 {
174 
175 	fsqpicRxWin = new Fl_Double_Window(324, 274, _("FSQ Rx Image"));
176 	fsqpicRxWin->xclass(PACKAGE_NAME);
177 	fsqpicRxWin->begin();
178 
179 	fsqpicRx = new picture(2, 2, 320, 240);
180 	fsqpicRx->noslant();
181 
182 	Fl_Group *buttons = new Fl_Group(0, fsqpicRxWin->h() - 26, fsqpicRxWin->w(), 26, "");
183 	buttons->box(FL_FLAT_BOX);
184 
185 	btnfsqRxReset = new Fl_Button(2, fsqpicRxWin->h() - 26, 40, 24, "Reset");
186 	btnfsqRxReset->callback(cb_btnfsqRxReset, 0);
187 
188 	cnt_phase = new Fl_Counter(46, fsqpicRxWin->h() - 24, 80, 20, "");
189 	cnt_phase->step(1);
190 	cnt_phase->lstep(10);
191 	cnt_phase->minimum(-RAWSTART + 1);
192 	cnt_phase->maximum(RAWSTART - 1);
193 	cnt_phase->value(0);
194 	cnt_phase->callback(cb_cnt_phase, 0);
195 	cnt_phase->tooltip(_("Phase correction"));
196 
197 	cnt_slant = new Fl_Counter(140, fsqpicRxWin->h() - 24, 80, 20, "");
198 	cnt_slant->step(1);
199 	cnt_slant->lstep(10);
200 	cnt_slant->minimum(-200);
201 	cnt_slant->maximum(200);
202 	cnt_slant->value(0);
203 	cnt_slant->callback(cb_cnt_slant, 0);
204 	cnt_slant->tooltip(_("Slant correction"));
205 
206 	btnfsqRxSave = new Fl_Button(226, fsqpicRxWin->h() - 26, 45, 24, _("Save"));
207 	btnfsqRxSave->callback(cb_btnfsqRxSave, 0);
208 
209 	btnfsqRxClose = new Fl_Button(273, fsqpicRxWin->h() - 26, 45, 24, _("Close"));
210 	btnfsqRxClose->callback(cb_btnfsqRxClose, 0);
211 	buttons->end();
212 
213 	fsqpicRxWin->end();
214 	fsqpicRxWin->resizable(fsqpicRx);
215 
216 	numpixels = 0;
217 }
218 
fsq_showRxViewer(int W,int H,char itype)219 void fsq_showRxViewer(int W, int H, char itype)
220 {
221 	if (!fsqpicRxWin) fsq_createRxViewer();
222 	int winW, winH;
223 	int fsqpicX, fsqpicY;
224 	winW = W < 320 ? 324 : W + 4;
225 	winH = H < 240 ? 274 : H + 34;
226 	fsqpicX = (winW - W) / 2;
227 	fsqpicY = (winH - 30 - H) / 2;
228 	fsqpicRxWin->size(winW, winH);
229 	fsqpicRx->resize(fsqpicX, fsqpicY, W, H);
230 	fsqpicRxWin->init_sizes();
231 
232 	fsqpicRx->clear();
233 	fsqpicRxWin->show();
234 	fsq_disableshift();
235 
236 	memset(rawvideo, 0, RAWSIZE);
237 	numpixels = 0;
238 	pixelptr = 0;
239 	rawrow = rawrgb = rawcol = 0;
240 	image_type = itype;
241 }
242 
fsq_clear_rximage()243 void fsq_clear_rximage()
244 {
245 	fsqpicRx->clear();
246 	fsq_disableshift();
247 	translate = 0;
248 	enabled = false;
249 	numpixels = 0;
250 	pixelptr = 0;
251 	cnt_phase->value(0);
252 	cnt_slant->value(0);
253 	rawrow = rawrgb = rawcol = 0;
254 }
255 
256 //------------------------------------------------------------------------------
257 // image transmit functions
258 //------------------------------------------------------------------------------
259 
fsq_load_image(const char * n,int W,int H)260 int fsq_load_image(const char *n, int W, int H) {
261 
262 	int D = 0;
263 	unsigned char *img_data;
264 
265 	switch (selfsqpicSize->value()) {
266 		case 0 : W = 160; H = 120; break;
267 		case 1 : W = 320; H = 240; break;
268 		case 2 : W = 640; H = 480; break;
269 		case 3 : W = 640; H = 480; break;
270 		case 4 : W = 240; H = 300; break;
271 		case 5 : W = 240; H = 300; break;
272 		case 6 : W = 120; H = 150; break;
273 		case 7 : W = 120; H = 150; break;
274 	}
275 
276 	if (fsqTxImg) {
277 		fsqTxImg->release();
278 		fsqTxImg = 0;
279 	}
280 	fsqTxImg = Fl_Shared_Image::get(n, W, H);
281 
282 	if (!fsqTxImg)
283 		return 0;
284 
285 	if (fsqTxImg->count() > 1) {
286 		fsqTxImg->release();
287 		fsqTxImg = 0;
288 		return 0;
289 	}
290 
291 	fsqpicTx->hide();
292 	fsqpicTx->clear();
293 
294 	img_data = (unsigned char *)fsqTxImg->data()[0];
295 
296 	D = fsqTxImg->d();
297 
298 	if (fsqxmtimg) delete [] fsqxmtimg;
299 
300 	fsqxmtimg = new unsigned char [W * H * 3];
301 	if (D == 3)
302 		memcpy(fsqxmtimg, img_data, W*H*3);
303 	else if (D == 4) {
304 		int i, j, k;
305 		for (i = 0; i < W*H; i++) {
306 			j = i*3; k = i*4;
307 			fsqxmtimg[j] = img_data[k];
308 			fsqxmtimg[j+1] = img_data[k+1];
309 			fsqxmtimg[j+2] = img_data[k+2];
310 		}
311 	} else if (D == 1) {
312 		int i, j;
313 		for (i = 0; i < W*H; i++) {
314 			j = i * 3;
315 			fsqxmtimg[j] = fsqxmtimg[j+1] = fsqxmtimg[j+2] = img_data[i];
316 		}
317 	} else
318 		return 0;
319 
320 //	fsq_showTxViewer(W, H);
321 	char* label = strdup(n);
322 	fsqpicTxWin->copy_label(basename(label));
323 	free(label);
324 // load the fsqpicture widget with the rgb image
325 
326 	fsqpicTx->show();
327 	fsqpicTxWin->redraw();
328 	fsqpicTx->video(fsqxmtimg, W * H * 3);
329 
330 	btnfsqpicTransmit->activate();
331 
332 	return 1;
333 }
334 
fsq_updateTxPic(unsigned char data,int pos)335 void fsq_updateTxPic(unsigned char data, int pos)
336 {
337 	if (!fsqpicTxWin->shown()) fsqpicTx->show();
338 	fsqpicTx->pixel(data, pos);
339 }
340 
cb_fsqpicTxLoad(Fl_Widget *,void *)341 void cb_fsqpicTxLoad(Fl_Widget *, void *)
342 {
343 	const char *fn =
344 		FSEL::select(_("Load image file"), "Image\t*.{png,,gif,jpg,jpeg}\n", PicsDir.c_str());
345 	if (!fn) return;
346 	if (!*fn) return;
347 	fsq_load_image(fn);
348 }
349 
fsq_clear_tximage()350 void fsq_clear_tximage()
351 {
352 	fsqpicTx->clear();
353 }
354 
cb_fsqpicTxClose(Fl_Widget * w,void *)355 void cb_fsqpicTxClose( Fl_Widget *w, void *)
356 {
357 	fsqpicTxWin->hide();
358 }
359 
fsqpic_TxGetPixel(int pos,int color)360 int fsqpic_TxGetPixel(int pos, int color)
361 {
362 	return fsqxmtimg[3*pos + color]; // color = {RED, GREEN, BLUE}
363 }
364 
cb_fsqpicTransmit(Fl_Widget * w,void *)365 void cb_fsqpicTransmit( Fl_Widget *w, void *)
366 {
367 	std::string image_txt;
368 	image_txt.assign(fsq_selected_call.c_str());
369 	switch (selfsqpicSize->value()) {
370 		case 0: image_txt.append("% S"); break;
371 		case 1: image_txt.append("% L"); break;
372 		case 2: image_txt.append("% F"); break;
373 		case 3: image_txt.append("% V"); break;
374 		case 4: image_txt.append("% P"); break;
375 		case 5: image_txt.append("% p"); break;
376 		case 6: image_txt.append("% M"); break;
377 		case 7: image_txt.append("% m"); break;
378 	}
379 	active_modem->fsq_send_image(image_txt);
380 }
381 
cb_fsqpicTxSendAbort(Fl_Widget * w,void *)382 void cb_fsqpicTxSendAbort( Fl_Widget *w, void *)
383 {
384 }
385 
cb_selfsqpicSize(Fl_Widget * w,void *)386 void cb_selfsqpicSize( Fl_Widget *w, void *)
387 {
388 	switch (selfsqpicSize->value()) {
389 		case 0 : fsq_showTxViewer('S'); break;
390 		case 1 : fsq_showTxViewer('L'); break;
391 		case 2 : fsq_showTxViewer('F'); break;
392 		case 3 : fsq_showTxViewer('V'); break;
393 		case 4 : fsq_showTxViewer('P'); break;
394 		case 5 : fsq_showTxViewer('p'); break;
395 		case 6 : fsq_showTxViewer('M'); break;
396 		case 7 : fsq_showTxViewer('m'); break;
397 	}
398 }
399 
fsq_createTxViewer()400 void fsq_createTxViewer()
401 {
402 
403 	fsqpicTxWin = new Fl_Double_Window(324, 270, _("Send image"));
404 	fsqpicTxWin->xclass(PACKAGE_NAME);
405 	fsqpicTxWin->begin();
406 
407 	fsqpicTx = new picture (2, 2, 320, 240);
408 	fsqpicTx->noslant();
409 	fsqpicTx->hide();
410 
411 	selfsqpicSize = new Fl_Choice(5, 244, 110, 24);
412 	selfsqpicSize->add("160x120 clr");	// case 0
413 	selfsqpicSize->add("320x240 clr");	// case 1
414 	selfsqpicSize->add("640x480 gry");	// case 2
415 	selfsqpicSize->add("640x480 clr");	// case 3
416 	selfsqpicSize->add("240x300 clr");	// case 4
417 	selfsqpicSize->add("240x300 gry");	// case 5
418 	selfsqpicSize->add("120x150 clr");	// case 6
419 	selfsqpicSize->add("120x150 gry");	// case 7
420 	selfsqpicSize->value(1);
421 	selfsqpicSize->callback(cb_selfsqpicSize, 0);
422 
423 	btnfsqpicTxLoad = new Fl_Button(120, 244, 60, 24, _("Load"));
424 	btnfsqpicTxLoad->callback(cb_fsqpicTxLoad, 0);
425 
426 	btnfsqpicTransmit = new Fl_Button(fsqpicTxWin->w() - 130, 244, 60, 24, "Xmt");
427 	btnfsqpicTransmit->callback(cb_fsqpicTransmit, 0);
428 
429 	btnfsqpicTxSendAbort = new Fl_Button(fsqpicTxWin->w() - 130, 244, 60, 24, "Abort Xmt");
430 	btnfsqpicTxSendAbort->callback(cb_fsqpicTxSendAbort, 0);
431 
432 	btnfsqpicTxClose = new Fl_Button(fsqpicTxWin->w() - 65, 244, 60, 24, _("Close"));
433 	btnfsqpicTxClose->callback(cb_fsqpicTxClose, 0);
434 
435 	btnfsqpicTxSendAbort->hide();
436 	btnfsqpicTransmit->deactivate();
437 
438 	fsqpicTxWin->end();
439 
440 }
441 
fsq_showTxViewer(char c)442 void fsq_showTxViewer(char c)
443 {
444 	if (!fsqpicTxWin) fsq_createTxViewer();
445 
446 	int winW = 644, winH = 512, W = 480, H = 320;
447 	int fsqpicX, fsqpicY;
448 
449 	fsqpicTx->clear();
450 
451 	switch (c) {
452 		case 'S' :
453 		case 's' :
454 			W = 160; H = 120; winW = 324; winH = 154;
455 			selfsqpicSize->value(0);
456 			break;
457 		case 'L' :
458 		case 'l' :
459 			W = 320; H = 240; winW = 324; winH = 274;
460 			selfsqpicSize->value(1);
461 			break;
462 		case 'F' :
463 			W = 640; H = 480; winW = 644; winH = 514;
464 			selfsqpicSize->value(2);
465 			break;
466 		case 'V' :
467 			W = 640; H = 480; winW = 644; winH = 514;
468 			selfsqpicSize->value(3);
469 			break;
470 		case 'P' :
471 			W = 240; H = 300; winW = 324; winH = 334;
472 			selfsqpicSize->value(4);
473 			break;
474 		case 'p' :
475 			W = 240; H = 300; winW = 324; winH = 334;
476 			selfsqpicSize->value(5);
477 			break;
478 		case 'M' :
479 			W = 120; H = 150; winW = 324; winH = 184;
480 			selfsqpicSize->value(6);
481 			break;
482 		case 'm' :
483 			W = 120; H = 150; winW = 324; winH = 184;
484 			selfsqpicSize->value(7);
485 	}
486 
487 	fsqpicTxWin->size(winW, winH);
488 	fsqpicX = (winW - W) / 2;
489 	fsqpicY = (winH - 26 - H) / 2;
490 	fsqpicTx->resize(fsqpicX, fsqpicY, W, H);
491 
492 	selfsqpicSize->resize(5, winH - 26, 110, 24);
493 
494 	btnfsqpicTxLoad->resize(120, winH - 26, 60, 24);
495 
496 	btnfsqpicTransmit->resize(winW - 130, winH - 26, 60, 24);
497 	btnfsqpicTxSendAbort->resize(winW - 130, winH - 26, 60, 24);
498 
499 	btnfsqpicTxClose->resize(winW -65, winH - 26, 60, 24);
500 
501 	selfsqpicSize->show();
502 	btnfsqpicTransmit->show();
503 	btnfsqpicTxLoad->show();
504 	btnfsqpicTxClose->show();
505 	btnfsqpicTxSendAbort->hide();
506 
507 	fsqpicTxWin->show();
508 
509 }
510 
fsq_deleteTxViewer()511 void fsq_deleteTxViewer()
512 {
513 	fsqpicTxWin->hide();
514 	if (fsqpicTx) delete fsqpicTx;
515 	delete [] fsqxmtimg;
516 	fsqxmtimg = 0;
517 	delete [] fsqxmtpicbuff;
518 	fsqxmtpicbuff = 0;
519 	delete fsqpicTxWin;
520 	fsqpicTxWin = 0;
521 }
522 
fsq_deleteRxViewer()523 void fsq_deleteRxViewer()
524 {
525 	fsqpicRxWin->hide();
526 	if (fsqpicRx) {
527 		delete fsqpicRx;
528 		fsqpicRx = 0;
529 	}
530 	delete fsqpicRxWin;
531 	fsqpicRxWin = 0;
532 }
533 
fsq_print_time_left(float time_sec,char * str,size_t len,const char * prefix,const char * suffix)534 int fsq_print_time_left(float time_sec, char *str, size_t len,
535 			  const char *prefix, const char *suffix)
536 {
537 	int time_min = (int)(time_sec / 60);
538 	time_sec -= time_min * 60;
539 
540 	if (time_min)
541 		return snprintf(str, len, "%s %02dm %2.1fs%s",
542 				prefix, time_min, time_sec, suffix);
543 	else
544 		return snprintf(str, len, "%s %2.1fs%s", prefix, time_sec, suffix);
545 }
546 
547