kopia lustrzana https://github.com/jamescoxon/dl-fldigi
555 wiersze
14 KiB
C++
555 wiersze
14 KiB
C++
// ----------------------------------------------------------------------------
|
|
// fsqpic.cxx -- fsq image support functions
|
|
//
|
|
// Copyright (C) 2015
|
|
// Dave Freese, W1HKJ
|
|
//
|
|
// This file is part of fldigi. Adapted from code contained in gfsq source code
|
|
// distribution.
|
|
//
|
|
// Fldigi is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Fldigi is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with fldigi. If not, see <http://www.gnu.org/licenses/>.
|
|
// ----------------------------------------------------------------------------
|
|
#include <FL/Fl_Counter.H>
|
|
#include <FL/Fl_Choice.H>
|
|
|
|
#include "gettext.h"
|
|
#include "fileselect.h"
|
|
|
|
Fl_Double_Window *fsqpicRxWin = (Fl_Double_Window *)0;
|
|
picture *fsqpicRx = (picture *)0;
|
|
Fl_Button *btnfsqRxReset = (Fl_Button *)0;
|
|
Fl_Button *btnfsqRxSave = (Fl_Button *)0;
|
|
Fl_Button *btnfsqRxClose = (Fl_Button *)0;
|
|
Fl_Counter *cnt_phase = (Fl_Counter *)0;
|
|
Fl_Counter *cnt_slant = (Fl_Counter *)0;
|
|
|
|
Fl_Double_Window *fsqpicTxWin = (Fl_Double_Window *)0;
|
|
picture *fsqpicTx = (picture *)0;
|
|
Fl_Button *btnfsqpicTransmit = (Fl_Button *)0;
|
|
Fl_Button *btnfsqpicTxSendAbort = (Fl_Button *)0;
|
|
Fl_Button *btnfsqpicTxLoad = (Fl_Button *)0;
|
|
Fl_Button *btnfsqpicTxClose = (Fl_Button *)0;
|
|
Fl_Choice *selfsqpicSize = (Fl_Choice *)0;
|
|
|
|
Fl_Shared_Image *fsqTxImg = (Fl_Shared_Image *)0;
|
|
unsigned char *fsqxmtimg = (unsigned char *)0;
|
|
unsigned char *fsqxmtpicbuff = (unsigned char *)0;
|
|
|
|
#define RAWSIZE 640*(480 + 8)*3*10
|
|
#define RAWSTART 640*4*3*10
|
|
unsigned char rawvideo[RAWSIZE + 1];
|
|
int numpixels;
|
|
int pixelptr;
|
|
int rawcol;
|
|
int rawrow;
|
|
int rawrgb;
|
|
char image_type = 'S';
|
|
|
|
int fsq_txSPP = 8;
|
|
|
|
char fsq_txclr_tooltip[24];
|
|
char fsq_txgry_tooltip[24];
|
|
|
|
static int translate = 0;
|
|
static bool enabled = false;
|
|
|
|
void correct_video()
|
|
{
|
|
int W = fsqpicRx->w();
|
|
int H = fsqpicRx->h();
|
|
int slant = cnt_slant->value();
|
|
int vidsize = W * H;
|
|
int index, rowptr, colptr;
|
|
float ratio = (((float)vidsize - (float)slant)/(float)vidsize);
|
|
unsigned char vid[W * H * 3];
|
|
for (int row = 0; row < H; row++) {
|
|
rowptr = W * 3 * row * 10;
|
|
for (int col = 0; col < W; col++) {
|
|
colptr = 10*col;
|
|
for (int rgb = 0; rgb < 3; rgb++) {
|
|
index = ratio*(rowptr + colptr + 10*W*rgb);
|
|
index += RAWSTART - 10*pixelptr;
|
|
if (index < 2) index = 2;
|
|
if (index > RAWSIZE - 2) index = RAWSIZE - 2;
|
|
vid[2 - rgb + 3 * (col + row * W)] = rawvideo[index];
|
|
}
|
|
}
|
|
}
|
|
fsqpicRx->video(vid, W*H*3);
|
|
}
|
|
|
|
void fsq_updateRxPic(unsigned char data, int pos)
|
|
{
|
|
if (!fsqpicRxWin->shown()) fsqpicRx->show();
|
|
|
|
fsqpicRx->pixel(data, pos);
|
|
|
|
int W = fsqpicRx->w();
|
|
if (image_type == 'F' || image_type == 'p' || image_type == 'm') {
|
|
int n = RAWSTART + 10*(rawcol + W * (rawrgb + 3 * rawrow));
|
|
if (n < RAWSIZE)
|
|
for (int i = 0; i < 10; i++) rawvideo[n + i] = data;
|
|
rawrgb++;
|
|
if (rawrgb == 3) {
|
|
rawrgb = 0;
|
|
rawcol++;
|
|
if (rawcol == W) {
|
|
rawcol = 0;
|
|
rawrow++;
|
|
}
|
|
}
|
|
} else
|
|
for (int i = 0; i < 10; i++) rawvideo[RAWSTART + 10*numpixels + i] = data;
|
|
numpixels++;
|
|
if (numpixels >= (RAWSIZE - RAWSTART - 10))
|
|
numpixels = RAWSIZE - RAWSTART - 10;
|
|
}
|
|
|
|
void cb_btnfsqRxReset(Fl_Widget *, void *)
|
|
{
|
|
progStatus.fsq_rx_abort = true;
|
|
}
|
|
|
|
void cb_btnfsqRxSave(Fl_Widget *, void *)
|
|
{
|
|
fsqpicRx->save_png(PicsDir.c_str());
|
|
// FILE *raw = fopen("image.raw", "wb");
|
|
// std::cout << "wrote " << fwrite(rawvideo, 1, RAWSIZE, raw) << "\n";
|
|
// fclose(raw);
|
|
}
|
|
|
|
void cb_btnfsqRxClose(Fl_Widget *, void *)
|
|
{
|
|
fsqpicRxWin->hide();
|
|
progStatus.fsq_rx_abort = true;
|
|
// fsqpicRxWin->hide();
|
|
// FILE *raw = fopen("image.raw", "rb");
|
|
// std::cout << "read " << fread(rawvideo, 1, RAWSIZE, raw) << "\n";
|
|
// fclose(raw);
|
|
// correct_video();
|
|
}
|
|
|
|
void cb_cnt_phase(Fl_Widget *, void *data)
|
|
{
|
|
pixelptr = cnt_phase->value();
|
|
if (pixelptr >= RAWSTART/10) {
|
|
pixelptr = RAWSTART/10 - 1;
|
|
cnt_phase->value(pixelptr);
|
|
}
|
|
if (pixelptr < -RAWSTART/10) {
|
|
pixelptr = -RAWSTART/10;
|
|
cnt_phase->value(pixelptr);
|
|
}
|
|
correct_video();
|
|
}
|
|
|
|
void cb_cnt_slant(Fl_Widget *, void *)
|
|
{
|
|
correct_video();
|
|
}
|
|
|
|
void fsq_disableshift()
|
|
{
|
|
if (!fsqpicRxWin) return;
|
|
cnt_phase->deactivate();
|
|
cnt_slant->deactivate();
|
|
btnfsqRxSave->deactivate();
|
|
fsqpicRxWin->redraw();
|
|
}
|
|
|
|
void fsq_enableshift()
|
|
{
|
|
if (!fsqpicRxWin) return;
|
|
cnt_phase->activate();
|
|
cnt_slant->activate();
|
|
btnfsqRxSave->activate();
|
|
fsqpicRxWin->redraw();
|
|
}
|
|
|
|
void fsq_createRxViewer()
|
|
{
|
|
|
|
fsqpicRxWin = new Fl_Double_Window(324, 274, _("FSQ Rx Image"));
|
|
fsqpicRxWin->xclass(PACKAGE_NAME);
|
|
fsqpicRxWin->begin();
|
|
|
|
fsqpicRx = new picture(2, 2, 320, 240);
|
|
fsqpicRx->noslant();
|
|
|
|
Fl_Group *buttons = new Fl_Group(0, fsqpicRxWin->h() - 26, fsqpicRxWin->w(), 26, "");
|
|
buttons->box(FL_FLAT_BOX);
|
|
|
|
btnfsqRxReset = new Fl_Button(2, fsqpicRxWin->h() - 26, 40, 24, "Reset");
|
|
btnfsqRxReset->callback(cb_btnfsqRxReset, 0);
|
|
|
|
cnt_phase = new Fl_Counter(46, fsqpicRxWin->h() - 24, 80, 20, "");
|
|
cnt_phase->step(1);
|
|
cnt_phase->lstep(10);
|
|
cnt_phase->minimum(-RAWSTART + 1);
|
|
cnt_phase->maximum(RAWSTART - 1);
|
|
cnt_phase->value(0);
|
|
cnt_phase->callback(cb_cnt_phase, 0);
|
|
cnt_phase->tooltip(_("Phase correction"));
|
|
|
|
cnt_slant = new Fl_Counter(140, fsqpicRxWin->h() - 24, 80, 20, "");
|
|
cnt_slant->step(1);
|
|
cnt_slant->lstep(10);
|
|
cnt_slant->minimum(-200);
|
|
cnt_slant->maximum(200);
|
|
cnt_slant->value(0);
|
|
cnt_slant->callback(cb_cnt_slant, 0);
|
|
cnt_slant->tooltip(_("Slant correction"));
|
|
|
|
btnfsqRxSave = new Fl_Button(226, fsqpicRxWin->h() - 26, 45, 24, _("Save"));
|
|
btnfsqRxSave->callback(cb_btnfsqRxSave, 0);
|
|
|
|
btnfsqRxClose = new Fl_Button(273, fsqpicRxWin->h() - 26, 45, 24, _("Close"));
|
|
btnfsqRxClose->callback(cb_btnfsqRxClose, 0);
|
|
buttons->end();
|
|
|
|
fsqpicRxWin->end();
|
|
fsqpicRxWin->resizable(fsqpicRx);
|
|
|
|
numpixels = 0;
|
|
}
|
|
|
|
void fsq_showRxViewer(int W, int H, char itype)
|
|
{
|
|
if (!fsqpicRxWin) fsq_createRxViewer();
|
|
int winW, winH;
|
|
int fsqpicX, fsqpicY;
|
|
winW = W < 320 ? 324 : W + 4;
|
|
winH = H < 240 ? 274 : H + 34;
|
|
fsqpicX = (winW - W) / 2;
|
|
fsqpicY = (winH - 30 - H) / 2;
|
|
fsqpicRxWin->size(winW, winH);
|
|
fsqpicRx->resize(fsqpicX, fsqpicY, W, H);
|
|
fsqpicRxWin->init_sizes();
|
|
|
|
fsqpicRx->clear();
|
|
fsqpicRxWin->show();
|
|
fsq_disableshift();
|
|
|
|
memset(rawvideo, 0, RAWSIZE);
|
|
numpixels = 0;
|
|
pixelptr = 0;
|
|
rawrow = rawrgb = rawcol = 0;
|
|
image_type = itype;
|
|
}
|
|
|
|
void fsq_clear_rximage()
|
|
{
|
|
fsqpicRx->clear();
|
|
fsq_disableshift();
|
|
translate = 0;
|
|
enabled = false;
|
|
numpixels = 0;
|
|
pixelptr = 0;
|
|
cnt_phase->value(0);
|
|
cnt_slant->value(0);
|
|
rawrow = rawrgb = rawcol = 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// image transmit functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
int fsq_load_image(const char *n, int W, int H) {
|
|
|
|
int D = 0;
|
|
unsigned char *img_data;
|
|
|
|
switch (selfsqpicSize->value()) {
|
|
case 0 : W = 160; H = 120; break;
|
|
case 1 : W = 320; H = 240; break;
|
|
case 2 : W = 640; H = 480; break;
|
|
case 3 : W = 640; H = 480; break;
|
|
case 4 : W = 240; H = 300; break;
|
|
case 5 : W = 240; H = 300; break;
|
|
case 6 : W = 120; H = 150; break;
|
|
case 7 : W = 120; H = 150; break;
|
|
}
|
|
|
|
if (fsqTxImg) {
|
|
fsqTxImg->release();
|
|
fsqTxImg = 0;
|
|
}
|
|
fsqTxImg = Fl_Shared_Image::get(n, W, H);
|
|
|
|
if (!fsqTxImg)
|
|
return 0;
|
|
|
|
if (fsqTxImg->count() > 1) {
|
|
fsqTxImg->release();
|
|
fsqTxImg = 0;
|
|
return 0;
|
|
}
|
|
|
|
fsqpicTx->hide();
|
|
fsqpicTx->clear();
|
|
|
|
img_data = (unsigned char *)fsqTxImg->data()[0];
|
|
|
|
D = fsqTxImg->d();
|
|
|
|
if (fsqxmtimg) delete [] fsqxmtimg;
|
|
|
|
fsqxmtimg = new unsigned char [W * H * 3];
|
|
if (D == 3)
|
|
memcpy(fsqxmtimg, img_data, W*H*3);
|
|
else if (D == 4) {
|
|
int i, j, k;
|
|
for (i = 0; i < W*H; i++) {
|
|
j = i*3; k = i*4;
|
|
fsqxmtimg[j] = img_data[k];
|
|
fsqxmtimg[j+1] = img_data[k+1];
|
|
fsqxmtimg[j+2] = img_data[k+2];
|
|
}
|
|
} else if (D == 1) {
|
|
int i, j;
|
|
for (i = 0; i < W*H; i++) {
|
|
j = i * 3;
|
|
fsqxmtimg[j] = fsqxmtimg[j+1] = fsqxmtimg[j+2] = img_data[i];
|
|
}
|
|
} else
|
|
return 0;
|
|
|
|
// fsq_showTxViewer(W, H);
|
|
char* label = strdup(n);
|
|
fsqpicTxWin->copy_label(basename(label));
|
|
free(label);
|
|
// load the fsqpicture widget with the rgb image
|
|
|
|
fsqpicTx->show();
|
|
fsqpicTxWin->redraw();
|
|
fsqpicTx->video(fsqxmtimg, W * H * 3);
|
|
|
|
btnfsqpicTransmit->activate();
|
|
|
|
return 1;
|
|
}
|
|
|
|
void fsq_updateTxPic(unsigned char data, int pos)
|
|
{
|
|
if (!fsqpicTxWin->shown()) fsqpicTx->show();
|
|
fsqpicTx->pixel(data, pos);
|
|
}
|
|
|
|
void cb_fsqpicTxLoad(Fl_Widget *, void *)
|
|
{
|
|
const char *fn =
|
|
FSEL::select(_("Load image file"), "Image\t*.{png,,gif,jpg,jpeg}\n", PicsDir.c_str());
|
|
if (!fn) return;
|
|
if (!*fn) return;
|
|
fsq_load_image(fn);
|
|
}
|
|
|
|
void fsq_clear_tximage()
|
|
{
|
|
fsqpicTx->clear();
|
|
}
|
|
|
|
void cb_fsqpicTxClose( Fl_Widget *w, void *)
|
|
{
|
|
fsqpicTxWin->hide();
|
|
}
|
|
|
|
int fsqpic_TxGetPixel(int pos, int color)
|
|
{
|
|
return fsqxmtimg[3*pos + color]; // color = {RED, GREEN, BLUE}
|
|
}
|
|
|
|
void cb_fsqpicTransmit( Fl_Widget *w, void *)
|
|
{
|
|
std::string image_txt;
|
|
image_txt.assign(fsq_selected_call.c_str());
|
|
switch (selfsqpicSize->value()) {
|
|
case 0: image_txt.append("% S"); break;
|
|
case 1: image_txt.append("% L"); break;
|
|
case 2: image_txt.append("% F"); break;
|
|
case 3: image_txt.append("% V"); break;
|
|
case 4: image_txt.append("% P"); break;
|
|
case 5: image_txt.append("% p"); break;
|
|
case 6: image_txt.append("% M"); break;
|
|
case 7: image_txt.append("% m"); break;
|
|
}
|
|
active_modem->fsq_send_image(image_txt);
|
|
}
|
|
|
|
void cb_fsqpicTxSendAbort( Fl_Widget *w, void *)
|
|
{
|
|
}
|
|
|
|
void cb_selfsqpicSize( Fl_Widget *w, void *)
|
|
{
|
|
switch (selfsqpicSize->value()) {
|
|
case 0 : fsq_showTxViewer('S'); break;
|
|
case 1 : fsq_showTxViewer('L'); break;
|
|
case 2 : fsq_showTxViewer('F'); break;
|
|
case 3 : fsq_showTxViewer('V'); break;
|
|
case 4 : fsq_showTxViewer('P'); break;
|
|
case 5 : fsq_showTxViewer('p'); break;
|
|
case 6 : fsq_showTxViewer('M'); break;
|
|
case 7 : fsq_showTxViewer('m'); break;
|
|
}
|
|
}
|
|
|
|
void fsq_createTxViewer()
|
|
{
|
|
|
|
fsqpicTxWin = new Fl_Double_Window(324, 270, _("Send image"));
|
|
fsqpicTxWin->xclass(PACKAGE_NAME);
|
|
fsqpicTxWin->begin();
|
|
|
|
fsqpicTx = new picture (2, 2, 320, 240);
|
|
fsqpicTx->noslant();
|
|
fsqpicTx->hide();
|
|
|
|
selfsqpicSize = new Fl_Choice(5, 244, 110, 24);
|
|
selfsqpicSize->add("160x120 clr"); // case 0
|
|
selfsqpicSize->add("320x240 clr"); // case 1
|
|
selfsqpicSize->add("640x480 gry"); // case 2
|
|
selfsqpicSize->add("640x480 clr"); // case 3
|
|
selfsqpicSize->add("240x300 clr"); // case 4
|
|
selfsqpicSize->add("240x300 gry"); // case 5
|
|
selfsqpicSize->add("120x150 clr"); // case 6
|
|
selfsqpicSize->add("120x150 gry"); // case 7
|
|
selfsqpicSize->value(1);
|
|
selfsqpicSize->callback(cb_selfsqpicSize, 0);
|
|
|
|
btnfsqpicTxLoad = new Fl_Button(120, 244, 60, 24, _("Load"));
|
|
btnfsqpicTxLoad->callback(cb_fsqpicTxLoad, 0);
|
|
|
|
btnfsqpicTransmit = new Fl_Button(fsqpicTxWin->w() - 130, 244, 60, 24, "Xmt");
|
|
btnfsqpicTransmit->callback(cb_fsqpicTransmit, 0);
|
|
|
|
btnfsqpicTxSendAbort = new Fl_Button(fsqpicTxWin->w() - 130, 244, 60, 24, "Abort Xmt");
|
|
btnfsqpicTxSendAbort->callback(cb_fsqpicTxSendAbort, 0);
|
|
|
|
btnfsqpicTxClose = new Fl_Button(fsqpicTxWin->w() - 65, 244, 60, 24, _("Close"));
|
|
btnfsqpicTxClose->callback(cb_fsqpicTxClose, 0);
|
|
|
|
btnfsqpicTxSendAbort->hide();
|
|
btnfsqpicTransmit->deactivate();
|
|
|
|
fsqpicTxWin->end();
|
|
|
|
}
|
|
|
|
void fsq_showTxViewer(char c)
|
|
{
|
|
if (!fsqpicTxWin) fsq_createTxViewer();
|
|
|
|
int winW = 644, winH = 512, W = 480, H = 320;
|
|
int fsqpicX, fsqpicY;
|
|
|
|
fsqpicTx->clear();
|
|
|
|
switch (c) {
|
|
case 'S' :
|
|
case 's' :
|
|
W = 160; H = 120; winW = 324; winH = 154;
|
|
selfsqpicSize->value(0);
|
|
break;
|
|
case 'L' :
|
|
case 'l' :
|
|
W = 320; H = 240; winW = 324; winH = 274;
|
|
selfsqpicSize->value(1);
|
|
break;
|
|
case 'F' :
|
|
W = 640; H = 480; winW = 644; winH = 514;
|
|
selfsqpicSize->value(2);
|
|
break;
|
|
case 'V' :
|
|
W = 640; H = 480; winW = 644; winH = 514;
|
|
selfsqpicSize->value(3);
|
|
break;
|
|
case 'P' :
|
|
W = 240; H = 300; winW = 324; winH = 334;
|
|
selfsqpicSize->value(4);
|
|
break;
|
|
case 'p' :
|
|
W = 240; H = 300; winW = 324; winH = 334;
|
|
selfsqpicSize->value(5);
|
|
break;
|
|
case 'M' :
|
|
W = 120; H = 150; winW = 324; winH = 184;
|
|
selfsqpicSize->value(6);
|
|
break;
|
|
case 'm' :
|
|
W = 120; H = 150; winW = 324; winH = 184;
|
|
selfsqpicSize->value(7);
|
|
}
|
|
|
|
fsqpicTxWin->size(winW, winH);
|
|
fsqpicX = (winW - W) / 2;
|
|
fsqpicY = (winH - 26 - H) / 2;
|
|
fsqpicTx->resize(fsqpicX, fsqpicY, W, H);
|
|
|
|
selfsqpicSize->resize(5, winH - 26, 110, 24);
|
|
|
|
btnfsqpicTxLoad->resize(120, winH - 26, 60, 24);
|
|
|
|
btnfsqpicTransmit->resize(winW - 130, winH - 26, 60, 24);
|
|
btnfsqpicTxSendAbort->resize(winW - 130, winH - 26, 60, 24);
|
|
|
|
btnfsqpicTxClose->resize(winW -65, winH - 26, 60, 24);
|
|
|
|
selfsqpicSize->show();
|
|
btnfsqpicTransmit->show();
|
|
btnfsqpicTxLoad->show();
|
|
btnfsqpicTxClose->show();
|
|
btnfsqpicTxSendAbort->hide();
|
|
|
|
fsqpicTxWin->show();
|
|
|
|
}
|
|
|
|
void fsq_deleteTxViewer()
|
|
{
|
|
fsqpicTxWin->hide();
|
|
if (fsqpicTx) delete fsqpicTx;
|
|
delete [] fsqxmtimg;
|
|
fsqxmtimg = 0;
|
|
delete [] fsqxmtpicbuff;
|
|
fsqxmtpicbuff = 0;
|
|
delete fsqpicTxWin;
|
|
fsqpicTxWin = 0;
|
|
}
|
|
|
|
void fsq_deleteRxViewer()
|
|
{
|
|
fsqpicRxWin->hide();
|
|
if (fsqpicRx) {
|
|
delete fsqpicRx;
|
|
fsqpicRx = 0;
|
|
}
|
|
delete fsqpicRxWin;
|
|
fsqpicRxWin = 0;
|
|
}
|
|
|
|
int fsq_print_time_left(float time_sec, char *str, size_t len,
|
|
const char *prefix, const char *suffix)
|
|
{
|
|
int time_min = (int)(time_sec / 60);
|
|
time_sec -= time_min * 60;
|
|
|
|
if (time_min)
|
|
return snprintf(str, len, "%s %02dm %2.1fs%s",
|
|
prefix, time_min, time_sec, suffix);
|
|
else
|
|
return snprintf(str, len, "%s %2.1fs%s", prefix, time_sec, suffix);
|
|
}
|
|
|