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