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