1 // ----------------------------------------------------------------------------
2 // thorpic.cxx -- thor 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 gthor 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
24 #include <FL/Fl_Counter.H>
25 #include <FL/Fl_Choice.H>
26
27 #include "gettext.h"
28 #include "fileselect.h"
29 #include "qrunner.h"
30 #include "timeops.h"
31
32 void thor_createTxViewer();
33 void thor_showRxViewer(char);
34
35 Fl_Double_Window *thorpicRxWin = (Fl_Double_Window *)0;
36 picture *thorpicRx = (picture *)0;
37 Fl_Button *btnthorRxReset = (Fl_Button *)0;
38 Fl_Button *btnthorRxSave = (Fl_Button *)0;
39 Fl_Button *btnthorRxClose = (Fl_Button *)0;
40 Fl_Counter *thorcnt_phase = (Fl_Counter *)0;
41 Fl_Counter *thorcnt_slant = (Fl_Counter *)0;
42
43 Fl_Double_Window *thorpicTxWin = (Fl_Double_Window *)0;
44 picture *thorpicTx = (picture *)0;
45 Fl_Button *btnthorpicTransmit = (Fl_Button *)0;
46 Fl_Button *btnthorpicTxSendAbort = (Fl_Button *)0;
47 Fl_Button *btnthorpicTxLoad = (Fl_Button *)0;
48 Fl_Button *btnthorpicTxClose = (Fl_Button *)0;
49 Fl_Choice *selthorpicSize = (Fl_Choice *)0;
50 Fl_Check_Button *btnthorTxGrey = (Fl_Check_Button *)0;
51
52 void thor_showRxViewer(char c);
53 void thor_createRxViewer();
54
55 Fl_Shared_Image *thorTxImg = (Fl_Shared_Image *)0;
56 unsigned char *thorxmtimg = (unsigned char *)0;
57 unsigned char *thorxmtpicbuff = (unsigned char *)0;
58
59 #define RAWSIZE 640*(480 + 8)*3*thor::IMAGEspp
60
61 #define RAWSTART 640*4*3*thor::IMAGEspp
62
63 unsigned char *thor_rawvideo = 0;//[RAWSIZE + 1];
64
65 int thor_numpixels;
66 int thor_pixelptr;
67 int thor_rawcol;
68 int thor_rawrow;
69 int thor_rawrgb;
70 char thor_image_type = 'S';
71
72 char thor_txclr_tooltip[24];
73 char thor_txgry_tooltip[24];
74
75 static int translate = 0;
76 static bool enabled = false;
77
78 std::string thor::imageheader;
79 std::string thor::avatarheader;
80 int thor::IMAGEspp = THOR_IMAGESPP;
81
82 static string thor_fname;
83
thor_correct_video()84 void thor_correct_video()
85 {
86 int W = thorpicRx->w();
87 int H = thorpicRx->h();
88 int slant = thorcnt_slant->value();
89 int vidsize = W * H;
90 int index, rowptr, colptr;
91 float ratio = (((float)vidsize - (float)slant)/(float)vidsize);
92 unsigned char vid[W * H * 3];
93 for (int row = 0; row < H; row++) {
94 rowptr = W * 3 * row * thor::IMAGEspp;
95 for (int col = 0; col < W; col++) {
96 colptr = thor::IMAGEspp*col;
97 for (int rgb = 0; rgb < 3; rgb++) {
98 index = ratio*(rowptr + colptr + thor::IMAGEspp*W*rgb);
99 index += RAWSTART - thor::IMAGEspp*thor_pixelptr;
100 if (index < 2) index = 2;
101 if (index > RAWSIZE - 2) index = RAWSIZE - 2;
102 vid[rgb + 3 * (col + row * W)] = thor_rawvideo[index];
103 }
104 }
105 }
106 thorpicRx->video(vid, W*H*3);
107 }
108
thor_updateRxPic(unsigned char data,int pos)109 void thor_updateRxPic(unsigned char data, int pos)
110 {
111 if (!thorpicRxWin->shown()) thorpicRx->show();
112
113 thorpicRx->pixel(data, pos);
114
115 int W = thorpicRx->w();
116 if (thor_image_type == 'F' || thor_image_type == 'p' || thor_image_type == 'm' ||
117 thor_image_type == 'l' || thor_image_type == 's' || thor_image_type == 'v') {
118 int n = RAWSTART + thor::IMAGEspp*(thor_rawcol + W * (thor_rawrgb + 3 * thor_rawrow));
119 if (n < RAWSIZE)
120 for (int i = 0; i < thor::IMAGEspp; i++) thor_rawvideo[n + i] = data;
121 thor_rawrgb++;
122 if (thor_rawrgb == 3) {
123 thor_rawrgb = 0;
124 thor_rawcol++;
125 if (thor_rawcol == W) {
126 thor_rawcol = 0;
127 thor_rawrow++;
128 }
129 }
130 } else
131 for (int i = 0; i < thor::IMAGEspp; i++)
132 thor_rawvideo[RAWSTART + thor::IMAGEspp*thor_numpixels + i] = data;
133 thor_numpixels++;
134 if (thor_numpixels >= (RAWSIZE - RAWSTART - thor::IMAGEspp))
135 thor_numpixels = RAWSIZE - RAWSTART - thor::IMAGEspp;
136 }
137
cb_btnthorRxReset(Fl_Widget *,void *)138 void cb_btnthorRxReset(Fl_Widget *, void *)
139 {
140 // progStatus.thor_rx_abort = true;
141 }
142
thor_save_raw_video()143 void thor_save_raw_video()
144 {
145 time_t time_sec = time(0);
146 struct tm ztime;
147 (void)gmtime_r(&time_sec, &ztime);
148 char sztime[20];
149
150 strftime(sztime, sizeof(sztime), "%Y%m%d%H%M%Sz", &ztime);
151
152 thor_fname.assign(PicsDir).append("THOR").append(sztime);
153
154 FILE *raw = fl_fopen(string(thor_fname).append(".raw").c_str(), "wb");
155 fwrite(&thor_image_type, 1, 1, raw);
156 fwrite(thor_rawvideo, 1, RAWSIZE, raw);
157 fclose(raw);
158 }
159
thor_load_raw_video()160 void thor_load_raw_video()
161 {
162 // abort & close any Rx video processing
163 int image_type = 0;
164 string image_types = "TtSsLlFVvPpMm";
165
166 if (!thorpicRxWin)
167 thor_createRxViewer();
168 else
169 thorpicRxWin->hide();
170
171 const char *p = FSEL::select(
172 _("Load raw image file"), "Image\t*.raw\n", PicsDir.c_str());
173
174 if (!p || !*p) return;
175
176 thor_fname.assign(p);
177 size_t p_raw = thor_fname.find(".raw");
178 if (p_raw != std::string::npos) thor_fname.erase(p_raw);
179
180 FILE *raw = fl_fopen(p, "rb");
181 int numread = fread(&image_type, 1, 1, raw);
182 if (numread != 1) {
183 fclose(raw);
184 return;
185 }
186
187 if (image_types.find(thor_image_type) != string::npos) {
188
189 thor_showRxViewer(image_type);
190
191 numread = fread(thor_rawvideo, 1, RAWSIZE, raw);
192
193 if (numread == RAWSIZE) {
194 thorcnt_phase->activate();
195 thorcnt_slant->activate();
196 btnthorRxSave->activate();
197
198 thor_correct_video();
199 thorpicRxWin->redraw();
200 }
201 }
202
203 fclose(raw);
204 }
205
cb_btnthorRxSave(Fl_Widget *,void *)206 void cb_btnthorRxSave(Fl_Widget *, void *)
207 {
208 thorpicRx->save_png(string(thor_fname).append(".png").c_str());
209 }
210
cb_btnthorRxClose(Fl_Widget *,void *)211 void cb_btnthorRxClose(Fl_Widget *, void *)
212 {
213 thorpicRxWin->hide();
214 }
215
cb_thor_cnt_phase(Fl_Widget *,void * data)216 void cb_thor_cnt_phase(Fl_Widget *, void *data)
217 {
218 thor_pixelptr = thorcnt_phase->value();
219 if (thor_pixelptr >= RAWSTART/thor::IMAGEspp) {
220 thor_pixelptr = RAWSTART/thor::IMAGEspp - 1;
221 thorcnt_phase->value(thor_pixelptr);
222 }
223 if (thor_pixelptr < -RAWSTART/thor::IMAGEspp) {
224 thor_pixelptr = -RAWSTART/thor::IMAGEspp;
225 thorcnt_phase->value(thor_pixelptr);
226 }
227 thor_correct_video();
228 }
229
cb_thor_cnt_slant(Fl_Widget *,void *)230 void cb_thor_cnt_slant(Fl_Widget *, void *)
231 {
232 thor_correct_video();
233 }
234
thor_disableshift()235 void thor_disableshift()
236 {
237 if (!thorpicRxWin) return;
238 thorcnt_phase->deactivate();
239 thorcnt_slant->deactivate();
240 btnthorRxSave->deactivate();
241 thorpicRxWin->redraw();
242 }
243
thor_enableshift()244 void thor_enableshift()
245 {
246 if (!thorpicRxWin) return;
247
248 thorcnt_phase->activate();
249 thorcnt_slant->activate();
250 btnthorRxSave->activate();
251
252 thor_save_raw_video();
253
254 thorpicRxWin->redraw();
255 }
256
thor_createRxViewer()257 void thor_createRxViewer()
258 {
259
260 thorpicRxWin = new Fl_Double_Window(324, 274, _("thor Rx Image"));
261 thorpicRxWin->xclass(PACKAGE_NAME);
262 thorpicRxWin->begin();
263
264 thorpicRx = new picture(2, 2, 320, 240);
265 thorpicRx->noslant();
266
267 Fl_Group *buttons = new Fl_Group(0, thorpicRxWin->h() - 26, thorpicRxWin->w(), 26, "");
268 buttons->box(FL_FLAT_BOX);
269
270 btnthorRxReset = new Fl_Button(2, thorpicRxWin->h() - 26, 40, 24, "Reset");
271 btnthorRxReset->callback(cb_btnthorRxReset, 0);
272
273 thorcnt_phase = new Fl_Counter(46, thorpicRxWin->h() - 24, 80, 20, "");
274 thorcnt_phase->step(1);
275 thorcnt_phase->lstep(10);
276 thorcnt_phase->minimum(-RAWSTART + 1);
277 thorcnt_phase->maximum(RAWSTART - 1);
278 thorcnt_phase->value(0);
279 thorcnt_phase->callback(cb_thor_cnt_phase, 0);
280 thorcnt_phase->tooltip(_("Phase correction"));
281
282 thorcnt_slant = new Fl_Counter(140, thorpicRxWin->h() - 24, 80, 20, "");
283 thorcnt_slant->step(1);
284 thorcnt_slant->lstep(10);
285 thorcnt_slant->minimum(-200);
286 thorcnt_slant->maximum(200);
287 thorcnt_slant->value(0);
288 thorcnt_slant->callback(cb_thor_cnt_slant, 0);
289 thorcnt_slant->tooltip(_("Slant correction"));
290
291 btnthorRxSave = new Fl_Button(226, thorpicRxWin->h() - 26, 45, 24, _("Save"));
292 btnthorRxSave->callback(cb_btnthorRxSave, 0);
293
294 btnthorRxClose = new Fl_Button(273, thorpicRxWin->h() - 26, 45, 24, _("Close"));
295 btnthorRxClose->callback(cb_btnthorRxClose, 0);
296 buttons->end();
297
298 thorpicRxWin->end();
299 thorpicRxWin->resizable(thorpicRx);
300
301 thor_numpixels = 0;
302 }
303
thor_showRxViewer(char itype)304 void thor_showRxViewer(char itype)
305 {
306 int W = 320;
307 int H = 240;
308 switch (itype) {
309 case 'L' : case 'l' : W = 320; H = 240; break;
310 case 'S' : case 's' : W = 160; H = 120; break;
311 case 'V' : case 'F' : W = 640; H = 480; break;
312 case 'P' : case 'p' : W = 240; H = 300; break;
313 case 'M' : case 'm' : W = 120; H = 150; break;
314 case 'T' : W = 59; H = 74; break;
315 }
316
317 if (!thorpicRxWin) thor_createRxViewer();
318 int winW, winH;
319 int thorpicX, thorpicY;
320 winW = W < 320 ? 324 : W + 4;
321 winH = H < 240 ? 274 : H + 34;
322 thorpicX = (winW - W) / 2;
323 thorpicY = (winH - 30 - H) / 2;
324 thorpicRxWin->size(winW, winH);
325 thorpicRx->resize(thorpicX, thorpicY, W, H);
326 thorpicRxWin->init_sizes();
327
328 thorpicRx->clear();
329 thorpicRxWin->show();
330 thor_disableshift();
331
332 if (thor_rawvideo == 0) thor_rawvideo = new unsigned char [RAWSIZE + 1];
333 memset(thor_rawvideo, 0, RAWSIZE);
334 thor_numpixels = 0;
335 thor_pixelptr = 0;
336 thor_rawrow = thor_rawrgb = thor_rawcol = 0;
337 thor_image_type = itype;
338 }
339
thor_clear_rximage()340 void thor_clear_rximage()
341 {
342 thorpicRx->clear();
343 thor_disableshift();
344 translate = 0;
345 enabled = false;
346 thor_numpixels = 0;
347 thor_pixelptr = 0;
348 thorcnt_phase->value(0);
349 thorcnt_slant->value(0);
350 thor_rawrow = thor_rawrgb = thor_rawcol = 0;
351 }
352
353 //------------------------------------------------------------------------------
354 // image transmit functions
355 //------------------------------------------------------------------------------
356
thor_load_scaled_image(std::string fname,bool gray)357 void thor_load_scaled_image(std::string fname, bool gray)
358 {
359
360 if (!thorpicTxWin) thor_createTxViewer();
361
362 int D = 0;
363 unsigned char *img_data;
364 int W = 160;
365 int H = 120;
366 int winW = 644;
367 int winH = 512;
368 int thorpicX = 0;
369 int thorpicY = 0;
370 string picmode = "pic% \n";
371
372 if (thorTxImg) {
373 thorTxImg->release();
374 thorTxImg = 0;
375 }
376
377 thorTxImg = Fl_Shared_Image::get(fname.c_str());
378 if (!thorTxImg)
379 return;
380
381 int iW = thorTxImg->w();
382 int iH = thorTxImg->h();
383 int aspect = 0;
384
385 if (iW > iH ) {
386 if (iW >= 640) {
387 W = 640; H = 480;
388 winW = 644; winH = 484;
389 aspect = 5;
390 picmode[4] = 'V';
391 if (gray) {
392 picmode[4] = 'F';
393 }
394 }
395 else if (iW >= 320) {
396 W = 320; H = 240;
397 winW = 324; winH = 244;
398 aspect = 4;
399 picmode[4] = 'L';
400 if (gray) {
401 picmode[4] = 'l';
402 }
403 }
404 else {
405 W = 160; H = 120;
406 winW = 164; winH = 124;
407 aspect = 3;
408 picmode[4] = 'S';
409 if (gray) {
410 picmode[4] = 's';
411 }
412 }
413 } else {
414 if (iH >= 300) {
415 W = 240; H = 300;
416 winW = 244; winH = 304;
417 aspect = 2;
418 picmode[4] = 'P';
419 if (gray) {
420 picmode[4] = 'p';
421 }
422 }
423 else if (iH >= 150) {
424 W = 120; H = 150;
425 winW = 124; winH = 154;
426 aspect = 1;
427 picmode[4] = 'M';
428 if (gray) {
429 picmode[4] = 'm';
430 }
431 }
432 else {
433 W = 59; H = 74;
434 winW = 67; winH = 82;
435 aspect = 0;
436 picmode[4] = 'T';
437 if (gray) {
438 aspect = 0;
439 picmode[4] = 't';
440 }
441 }
442 }
443
444 {
445 Fl_Image *temp;
446 selthorpicSize->value(aspect);
447 temp = thorTxImg->copy(W, H);
448 thorTxImg->release();
449 thorTxImg = (Fl_Shared_Image *)temp;
450 }
451
452 if (thorTxImg->count() > 1) {
453 thorTxImg->release();
454 thorTxImg = 0;
455 return;
456 }
457
458 thorpicTx->hide();
459 thorpicTx->clear();
460
461 img_data = (unsigned char *)thorTxImg->data()[0];
462
463 D = thorTxImg->d();
464
465 if (thorxmtimg) delete [] thorxmtimg;
466
467 thorxmtimg = new unsigned char [W * H * 3];
468 if (D == 3)
469 memcpy(thorxmtimg, img_data, W*H*3);
470 else if (D == 4) {
471 int i, j, k;
472 for (i = 0; i < W*H; i++) {
473 j = i*3; k = i*4;
474 thorxmtimg[j] = img_data[k];
475 thorxmtimg[j+1] = img_data[k+1];
476 thorxmtimg[j+2] = img_data[k+2];
477 }
478 } else if (D == 1) {
479 int i, j;
480 for (i = 0; i < W*H; i++) {
481 j = i * 3;
482 thorxmtimg[j] = thorxmtimg[j+1] = thorxmtimg[j+2] = img_data[i];
483 }
484 } else
485 return;
486
487 char* label = strdup(fname.c_str());
488 thorpicTxWin->copy_label(basename(label));
489 free(label);
490
491 // load the thorpicture widget with the rgb image
492
493 thorpicTxWin->size(winW, winH);
494 thorpicX = (winW - W) / 2;
495 thorpicY = (winH - H) / 2;
496 thorpicTx->resize(thorpicX, thorpicY, W, H);
497
498 selthorpicSize->hide();
499 btnthorpicTransmit->hide();
500 btnthorpicTxLoad->hide();
501 btnthorpicTxClose->hide();
502 btnthorpicTxSendAbort->hide();
503
504 thorpicTx->video(thorxmtimg, W * H * 3);
505 thorpicTx->show();
506
507 thorpicTxWin->show();
508
509 active_modem->thor_send_image(picmode, gray);
510
511 return;
512 }
513
514
thor_load_image(const char * n)515 int thor_load_image(const char *n) {
516
517 int D = 0;
518 unsigned char *img_data;
519 int W = 640;
520 int H = 480;
521
522 switch (selthorpicSize->value()) {
523 case 0 : W = 59; H = 74; break;
524 case 1 : W = 120; H = 150; break;
525 case 2 : W = 240; H = 300; break;
526 case 3 : W = 160; H = 120; break;
527 case 4 : W = 320; H = 240; break;
528 case 5 : W = 640; H = 480; break;
529 }
530
531 if (thorTxImg) {
532 thorTxImg->release();
533 thorTxImg = 0;
534 }
535 thorTxImg = Fl_Shared_Image::get(n, W, H);
536
537 if (!thorTxImg)
538 return 0;
539
540 if (thorTxImg->count() > 1) {
541 thorTxImg->release();
542 thorTxImg = 0;
543 return 0;
544 }
545
546 thorpicTx->hide();
547 thorpicTx->clear();
548
549 img_data = (unsigned char *)thorTxImg->data()[0];
550
551 D = thorTxImg->d();
552
553 if (thorxmtimg) delete [] thorxmtimg;
554
555 thorxmtimg = new unsigned char [W * H * 3];
556 if (D == 3)
557 memcpy(thorxmtimg, img_data, W*H*3);
558 else if (D == 4) {
559 int i, j, k;
560 for (i = 0; i < W*H; i++) {
561 j = i*3; k = i*4;
562 thorxmtimg[j] = img_data[k];
563 thorxmtimg[j+1] = img_data[k+1];
564 thorxmtimg[j+2] = img_data[k+2];
565 }
566 } else if (D == 1) {
567 int i, j;
568 for (i = 0; i < W*H; i++) {
569 j = i * 3;
570 thorxmtimg[j] = thorxmtimg[j+1] = thorxmtimg[j+2] = img_data[i];
571 }
572 } else
573 return 0;
574
575 char* label = strdup(n);
576 thorpicTxWin->copy_label(basename(label));
577 free(label);
578 // load the thorpicture widget with the rgb image
579
580 thorpicTx->show();
581 thorpicTxWin->redraw();
582 thorpicTx->video(thorxmtimg, W * H * 3);
583
584 btnthorpicTransmit->activate();
585
586 return 1;
587 }
588
thor_updateTxPic(unsigned char data,int pos)589 void thor_updateTxPic(unsigned char data, int pos)
590 {
591 if (!thorpicTxWin->shown()) thorpicTx->show();
592 thorpicTx->pixel(data, pos);
593 }
594
cb_thorpicTxLoad(Fl_Widget *,void *)595 void cb_thorpicTxLoad(Fl_Widget *, void *)
596 {
597 const char *fn =
598 FSEL::select(_("Load image file"), "Image\t*.{png,,gif,jpg,jpeg}\n", PicsDir.c_str());
599 if (!fn) return;
600 if (!*fn) return;
601
602 thor_load_image(fn);
603 }
604
thor_clear_tximage()605 void thor_clear_tximage()
606 {
607 thorpicTx->clear();
608 }
609
cb_thorpicTxClose(Fl_Widget * w,void *)610 void cb_thorpicTxClose( Fl_Widget *w, void *)
611 {
612 thorpicTxWin->hide();
613 }
614
thorpic_TxGetPixel(int pos,int color)615 int thorpic_TxGetPixel(int pos, int color)
616 {
617 return thorxmtimg[3*pos + color]; // color = {RED, GREEN, BLUE}
618 }
619
cb_thorpicTransmit(Fl_Widget * w,void *)620 void cb_thorpicTransmit( Fl_Widget *w, void *)
621 {
622 std::string header = "pic% ";
623 bool grey = btnthorTxGrey->value();
624 char ch = ' ';
625 switch (selthorpicSize->value()) {
626 case 0 : thor_showTxViewer(ch = (grey ? 't' : 'T')); break; // 59 x 74
627 case 1 : thor_showTxViewer(ch = (grey ? 'm' : 'M')); break; // 120 x 150
628 case 2 : thor_showTxViewer(ch = (grey ? 'p' : 'P')); break; // 240 x 300
629 case 3 : thor_showTxViewer(ch = (grey ? 's' : 'S')); break; // 160 x 120
630 case 4 : thor_showTxViewer(ch = (grey ? 'l' : 'L')); break; // 320 x 240
631 case 5 : thor_showTxViewer(ch = (grey ? 'F' : 'V')); break; // 640 x 480
632 }
633 header[4] = ch;
634 active_modem->thor_send_image(header, grey);
635 }
636
cb_thorpicTxSendAbort(Fl_Widget * w,void *)637 void cb_thorpicTxSendAbort( Fl_Widget *w, void *)
638 {
639 }
640
641
cb_selthorpicSize(Fl_Widget * w,void *)642 void cb_selthorpicSize( Fl_Widget *w, void *)
643 {
644 switch (selthorpicSize->value()) {
645 case 0 : thor_showTxViewer('T'); break; // 59 x 74
646 case 1 : thor_showTxViewer('M'); break; // 120 x 150
647 case 2 : thor_showTxViewer('P'); break; // 240 x 300
648 case 3 : thor_showTxViewer('S'); break; // 160 x 120
649 case 4 : thor_showTxViewer('L'); break; // 320 x 240
650 case 5 : thor_showTxViewer('V'); break; // 640 x 480
651 }
652 }
653
thor_createTxViewer()654 void thor_createTxViewer()
655 {
656
657 thorpicTxWin = new Fl_Double_Window(324, 270, _("thor Send image"));
658 thorpicTxWin->xclass(PACKAGE_NAME);
659 thorpicTxWin->begin();
660
661 thorpicTx = new picture (2, 2, 320, 240);
662 thorpicTx->noslant();
663 thorpicTx->hide();
664
665 selthorpicSize = new Fl_Choice(5, 244, 90, 24);
666 selthorpicSize->add("59 x 74");
667 selthorpicSize->add("120x150");
668 selthorpicSize->add("240x300");
669 selthorpicSize->add("160x120");
670 selthorpicSize->add("320x240");
671 selthorpicSize->add("640x480");
672 selthorpicSize->value(1);
673 selthorpicSize->callback(cb_selthorpicSize, 0);
674
675 btnthorTxGrey = new Fl_Check_Button(99, 247, 18, 18);
676 btnthorTxGrey->value(0);
677 btnthorTxGrey->tooltip(_("Check for grey image"));
678
679 btnthorpicTxLoad = new Fl_Button(133, 244, 60, 24, _("Load"));
680 btnthorpicTxLoad->callback(cb_thorpicTxLoad, 0);
681
682 btnthorpicTransmit = new Fl_Button(thorpicTxWin->w() - 130, 244, 60, 24, "Xmt");
683 btnthorpicTransmit->callback(cb_thorpicTransmit, 0);
684
685 btnthorpicTxSendAbort = new Fl_Button(thorpicTxWin->w() - 130, 244, 60, 24, "Abort Xmt");
686 btnthorpicTxSendAbort->callback(cb_thorpicTxSendAbort, 0);
687
688 btnthorpicTxClose = new Fl_Button(thorpicTxWin->w() - 65, 244, 60, 24, _("Close"));
689 btnthorpicTxClose->callback(cb_thorpicTxClose, 0);
690
691 btnthorpicTxSendAbort->hide();
692 btnthorpicTransmit->deactivate();
693
694 thorpicTxWin->end();
695
696 }
697
thor_showTxViewer(char c)698 void thor_showTxViewer(char c)
699 {
700 if (!thorpicTxWin) thor_createTxViewer();
701
702 int winW = 644, winH = 512, W = 480, H = 320;
703 int thorpicX, thorpicY;
704
705 thorpicTx->clear();
706
707 switch (c) {
708 case 'T' : case 't' :
709 W = 59; H = 74; winW = 324; winH = 184;
710 selthorpicSize->value(0);
711 break;
712 case 'S' : case 's' :
713 W = 160; H = 120; winW = 324; winH = 154;
714 selthorpicSize->value(3);
715 break;
716 case 'L' : case 'l' :
717 W = 320; H = 240; winW = 324; winH = 274;
718 selthorpicSize->value(4);
719 break;
720 case 'F' : case 'V' :
721 W = 640; H = 480; winW = 644; winH = 514;
722 selthorpicSize->value(5);
723 break;
724 case 'P' : case 'p' :
725 W = 240; H = 300; winW = 324; winH = 334;
726 selthorpicSize->value(2);
727 break;
728 case 'M' : case 'm' :
729 W = 120; H = 150; winW = 324; winH = 184;
730 selthorpicSize->value(1);
731 break;
732 }
733
734 thorpicTxWin->size(winW, winH);
735 thorpicX = (winW - W) / 2;
736 thorpicY = (winH - 26 - H) / 2;
737 thorpicTx->resize(thorpicX, thorpicY, W, H);
738
739 selthorpicSize->resize(5, winH - 26, 90, 24);
740
741 btnthorTxGrey->resize(selthorpicSize->x() + selthorpicSize->w() + 4, winH - 23, 18, 18);
742
743 btnthorpicTxLoad->resize(btnthorTxGrey->x() + btnthorTxGrey->w() + 4, winH - 26, 60, 24);
744
745 btnthorpicTransmit->resize(winW - 130, winH - 26, 60, 24);
746 btnthorpicTxSendAbort->resize(winW - 130, winH - 26, 60, 24);
747
748 btnthorpicTxClose->resize(winW -65, winH - 26, 60, 24);
749
750 selthorpicSize->show();
751 btnthorpicTransmit->show();
752 btnthorpicTxLoad->show();
753 btnthorpicTxClose->show();
754 btnthorpicTxSendAbort->hide();
755
756 thorpicTxWin->show();
757
758 }
759
thor_deleteTxViewer()760 void thor_deleteTxViewer()
761 {
762 if (thorpicTxWin) thorpicTxWin->hide();
763 if (thorpicTx) {
764 delete thorpicTx;
765 thorpicTx = 0;
766 }
767 delete [] thorxmtimg;
768 thorxmtimg = 0;
769 delete [] thorxmtpicbuff;
770 thorxmtpicbuff = 0;
771 if (thorpicTxWin) delete thorpicTxWin;
772 thorpicTxWin = 0;
773 }
774
thor_deleteRxViewer()775 void thor_deleteRxViewer()
776 {
777 if (thorpicRxWin) thorpicRxWin->hide();
778 if (thorpicRx) {
779 delete thorpicRx;
780 thorpicRx = 0;
781 }
782 if (thorpicRxWin) {
783 delete thorpicRxWin;
784 thorpicRxWin = 0;
785 }
786 }
787
thor_print_time_left(float time_sec,char * str,size_t len,const char * prefix,const char * suffix)788 int thor_print_time_left(float time_sec, char *str, size_t len,
789 const char *prefix, const char *suffix)
790 {
791 int time_min = (int)(time_sec / 60);
792 time_sec -= time_min * 60;
793
794 if (time_min)
795 return snprintf(str, len, "%s %02dm %2.1fs%s",
796 prefix, time_min, time_sec, suffix);
797 else
798 return snprintf(str, len, "%s %2.1fs%s", prefix, time_sec, suffix);
799 }
800
801 // -----------------------------------------------------------------------------
802 // avatar send/recv
803 // -----------------------------------------------------------------------------
804
805 static Fl_Shared_Image *shared_avatar_img = (Fl_Shared_Image *)0;
806 static unsigned char *avatar_img = (unsigned char *)0;
807 static Fl_Shared_Image *my_avatar_img = (Fl_Shared_Image *)0;
808 static int avatar_phase_correction = 0;
809 static unsigned char avatar[59 * 74 * 3];
810
thor_clear_avatar()811 void thor_clear_avatar()
812 {
813 thor_avatar->clear();
814 avatar_phase_correction = 0;
815 thor_numpixels = 0;
816 thor_rawrow = thor_rawrgb = thor_rawcol = 0;
817 thor_avatar->video(tux_img, 59 * 74 * 3);
818 }
819
820
821 // W always 59, H always 74
thor_load_avatar(std::string image_fname,int W,int H)822 int thor_load_avatar(std::string image_fname, int W, int H)
823 {
824 W = 59; H = 74;
825 if (image_fname.empty()) {
826 thor_clear_avatar();
827 return 1;
828 }
829
830 int D = 0;
831 unsigned char *img_data;
832
833 if (shared_avatar_img) {
834 shared_avatar_img->release();
835 shared_avatar_img = 0;
836 }
837
838 for (size_t n = 0; n < image_fname.length(); n++)
839 image_fname[n] = tolower(image_fname[n]);
840 std::string fname = AvatarDir;
841 fname.append(image_fname).append(".png");
842
843 FILE *temp = fl_fopen(fname.c_str(), "rb");
844 if (temp) {
845 fseek(temp, 0L, SEEK_SET);
846 fclose(temp);
847 } else {
848 thor_avatar->video(tux_img, W * H * 3);
849 return 1;
850 }
851
852 shared_avatar_img = Fl_Shared_Image::get(fname.c_str(), W, H);
853
854 // force image to be retrieved from hard drive vice shared image memory
855 shared_avatar_img->reload();
856
857 if (!shared_avatar_img) {
858 thor_avatar->video(tux_img, W * H * 3);
859 return 1;
860 }
861
862 if (shared_avatar_img->count() > 1) {
863 shared_avatar_img->release();
864 shared_avatar_img = 0;
865 thor_avatar->video(tux_img, W * H * 3);
866 return 0;
867 }
868
869 img_data = (unsigned char *)shared_avatar_img->data()[0];
870
871 D = shared_avatar_img->d();
872
873 if (avatar_img) delete [] avatar_img;
874
875 avatar_img = new unsigned char [W * H * 3];
876 if (D == 3)
877 memcpy(avatar_img, img_data, W*H*3);
878 else if (D == 4) {
879 int i, j, k;
880 for (i = 0; i < W*H; i++) {
881 j = i*3; k = i*4;
882 avatar_img[j] = img_data[k];
883 avatar_img[j+1] = img_data[k+1];
884 avatar_img[j+2] = img_data[k+2];
885 }
886 } else if (D == 1) {
887 int i, j;
888 for (i = 0; i < W*H; i++) {
889 j = i * 3;
890 avatar_img[j] = avatar_img[j+1] = avatar_img[j+2] = img_data[i];
891 }
892 } else {
893 thor_avatar->video(tux_img, W * H * 3);
894 return 0;
895 }
896 thor_avatar->video(avatar_img, W * H * 3);
897
898 shared_avatar_img->release();
899 shared_avatar_img = 0;
900
901 return 1;
902 }
903
thor_correct_avatar()904 void thor_correct_avatar()
905 {
906 int W = 59;
907 int H = 74;
908 int index, rowptr, colptr;
909 unsigned char vid[W * H * 3];
910
911 if (avatar_phase_correction >= RAWSTART/thor::IMAGEspp) {
912 avatar_phase_correction = RAWSTART/thor::IMAGEspp - 1;
913 }
914 if (avatar_phase_correction < -RAWSTART/thor::IMAGEspp) {
915 avatar_phase_correction = -RAWSTART/thor::IMAGEspp;
916 }
917
918 for (int row = 0; row < H; row++) {
919 rowptr = W * 3 * row * thor::IMAGEspp;
920 for (int col = 0; col < W; col++) {
921 colptr = thor::IMAGEspp*col;
922 for (int rgb = 0; rgb < 3; rgb++) {
923 index = rowptr + colptr + W*rgb*thor::IMAGEspp;
924 index += RAWSTART - thor::IMAGEspp * avatar_phase_correction;
925 if (index < 2) index = 2;
926 if (index > RAWSIZE - 2) index = RAWSIZE - 2;
927 vid[rgb + 3 * (col + row * W)] = thor_rawvideo[index];
928 }
929 }
930 }
931 thor_avatar->video(vid, W*H*3);
932 }
933
thor_update_avatar(unsigned char data,int pos)934 void thor_update_avatar(unsigned char data, int pos)
935 {
936 if (thor_rawvideo == 0) {
937 thor_rawvideo = new unsigned char [RAWSIZE + 1];
938 memset(thor_rawvideo, 0, RAWSIZE);
939 }
940
941 thor_avatar->pixel(data, pos);
942 for (int i = 0; i < thor::IMAGEspp; i++)
943 thor_rawvideo[RAWSTART + thor::IMAGEspp*thor_numpixels + i] = data;
944
945 thor_numpixels++;
946
947 if (thor_numpixels >= (RAWSIZE - RAWSTART - thor::IMAGEspp))
948 thor_numpixels = RAWSIZE - RAWSTART - thor::IMAGEspp;
949
950 }
951
thor_get_avatar_pixel(int pos,int color)952 int thor_get_avatar_pixel(int pos, int color)
953 {
954 // color = {RED, GREEN, BLUE}
955 return (int)avatar[3*pos + color];
956
957 }
958
959 // ADD CALLBACK HANDLING OF PHASE CORRECTIONS
960
cb_thor_send_avatar(Fl_Widget * w,void *)961 void cb_thor_send_avatar( Fl_Widget *w, void *)
962 {
963 if (Fl::event_button() == FL_RIGHT_MOUSE) {
964 if (Fl::get_key (FL_Shift_L) || Fl::get_key(FL_Shift_R)) {
965 if (thor_numpixels == 0) return;
966 avatar_phase_correction += 5;
967 thor_correct_avatar();
968 return;
969 }
970 if (Fl::get_key (FL_Control_L) || Fl::get_key(FL_Control_R)) {
971 if (thor_numpixels == 0) return;
972 avatar_phase_correction++;
973 thor_correct_avatar();
974 return;
975 }
976 std::string mycall = progdefaults.myCall;
977 for (size_t n = 0; n < mycall.length(); n++)
978 mycall[n] = tolower(mycall[n]);
979 std::string fname = AvatarDir;
980 fname.append(mycall).append(".png");
981
982 my_avatar_img = Fl_Shared_Image::get(fname.c_str(), 59, 74);
983 if (!my_avatar_img) return;
984 unsigned char *img_data = (unsigned char *)my_avatar_img->data()[0];
985 memset(avatar, 0, sizeof(avatar));
986 int D = my_avatar_img->d();
987
988 if (D == 3)
989 memcpy(avatar, img_data, 59*74*3);
990 else if (D == 4) {
991 int i, j, k;
992 for (i = 0; i < 59*74; i++) {
993 j = i*3; k = i*4;
994 avatar[j] = img_data[k];
995 avatar[j+1] = img_data[k+1];
996 avatar[j+2] = img_data[k+2];
997 }
998 } else if (D == 1) {
999 int i, j;
1000 for (i = 0; i < 59*74; i++) {
1001 j = i * 3;
1002 avatar[j] = avatar[j+1] = avatar[j+2] = img_data[i];
1003 }
1004 } else
1005 return;
1006
1007 thor::avatarheader = "\npic%A";
1008 active_modem->thor_send_avatar();
1009
1010 return;
1011 }
1012
1013 if (Fl::event_button() == FL_LEFT_MOUSE) {
1014 if (Fl::get_key (FL_Shift_L) || Fl::get_key(FL_Shift_R)) {
1015 if (thor_numpixels == 0) return;
1016 avatar_phase_correction -= 5;
1017 thor_correct_avatar();
1018 return;
1019 }
1020 if (Fl::get_key (FL_Control_L) || Fl::get_key(FL_Control_R)) {
1021 if (thor_numpixels == 0) return;
1022 avatar_phase_correction--;
1023 thor_correct_avatar();
1024 return;
1025 }
1026 std::string mycall = inpCall->value();
1027 if (mycall.empty()) return;
1028 for (size_t n = 0; n < mycall.length(); n++)
1029 mycall[n] = tolower(mycall[n]);
1030 std::string fname = AvatarDir;
1031 fname.append(mycall).append(".png");
1032 thor_avatar->save_png(fname.c_str());
1033 }
1034 }
1035
1036
1037