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