diff --git a/c_gtk_image/CMakeLists.txt b/c_gtk_image/CMakeLists.txt index 588efeb454b380cbd8f21d4b56167e6f13d56caa..d6a57c68f84142c47726a8f8bfd6631c1697eb5b 100644 --- a/c_gtk_image/CMakeLists.txt +++ b/c_gtk_image/CMakeLists.txt @@ -1,26 +1,23 @@ # Set the minimum version of cmake required to build this project -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.13) project(c_gtk_image) #This is to be able to debug and recompile automatically the libs (shared_blc). It is slower, you can remove this line and use : ${BLAR_BUILD_DIR}/lib/libblc.dylib instead of shared_blc -find_package(blc_network REQUIRED) -find_package(blc_channel REQUIRED) #For compatibilité with common.h - -find_package(blc_image REQUIRED) -find_package(blc_program REQUIRED) +find_package(blc REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 REQUIRED gtk+-3.0) find_package(JPEG REQUIRED) -add_definitions(-Wall ${BL_DEFINITIONS} -std=c++17 -Wno-deprecated-declarations) #device_manager (mouse) is deprecated -include_directories(${GTK3_INCLUDE_DIRS} ${BL_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR} ../include) -link_directories(${GTK3_LIBRARY_DIRS}) +add_definitions(-Wall -Wno-deprecated-declarations) #device_manager (mouse) is deprecated add_definitions(${GTK3_CFLAGS_OTHER}) -add_executable(c_gtk_image c_gtk_image.cpp ../src/image_display.cpp ../src/histogram.cpp) -target_link_libraries(c_gtk_image ${GTK3_LIBRARIES} ${BL_LIBRARIES} ${JPEG_LIBRARIES} ) +add_executable(c_gtk_image c_gtk_image.cpp ../src/Display.cpp ../src/Image.cpp ../src/Histogram.cpp ../src/UIN8_Y800.cpp ) +target_compile_features(c_gtk_image PRIVATE cxx_std_17) +target_include_directories(c_gtk_image PRIVATE ${GTK3_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR} ../include) +target_link_directories(c_gtk_image PRIVATE ${GTK3_LIBRARY_DIRS}) +target_link_libraries(c_gtk_image PRIVATE blc ${GTK3_LIBRARIES} ${JPEG_LIBRARIES} ) diff --git a/c_gtk_image/Histogram.hpp b/c_gtk_image/Histogram.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2ce45bbb8473e4d05adf20208e14833790701b98 --- /dev/null +++ b/c_gtk_image/Histogram.hpp @@ -0,0 +1,16 @@ +// +// Histogram.hpp +// c_gtk_image +// +// Created by Arnaud Blanchard on 23/11/2020. +// + +#ifndef Histogram_hpp +#define Histogram_hpp + +class Histogram:public blc_array{ +public: + Histogram(blc_array const &array); +} + +#endif /* Histogram_h */ diff --git a/c_gtk_image/c_gtk_image.cpp b/c_gtk_image/c_gtk_image.cpp index e5d4a4886b00162d4bfe1c4bfe53e41e3dfb73eb..cb27ccc16df1209b0276961f0ddf87e4cf6555d3 100644 --- a/c_gtk_image/c_gtk_image.cpp +++ b/c_gtk_image/c_gtk_image.cpp @@ -1,5 +1,3 @@ -#include "common.h" - #include <fcntl.h> // O_RDONLY ... #include <stdio.h> #include <gtk/gtk.h> @@ -8,25 +6,50 @@ //#include <jpeglib.h> #include <sys/mman.h> #include <errno.h> //errno -#include "blc_core.h" -#include "blc_program.h" -#include "blc_network.h" #include <thread> +#include <string> +#include "blc.h" +#include "Display.hpp" using namespace std; -float min_val, max_val; +class Client:public blc_array_tcp4_client{ + GtkWidget *window; +public: + Client(string const &address, string const &port); + void network_manager(); + unique_ptr<Display> display; + unique_ptr<thread> network_thread; +}; + +gboolean update_data(GtkWidget *widget, GdkFrameClock *frame_clock, void *user_data){ + auto client=reinterpret_cast<Client*>(user_data); + client->recv_data(); + client->display->update(); + return G_SOURCE_CONTINUE; +} + +Client::Client(string const &address, string const &port_name):blc_array_tcp4_client(address, port_name){ + string title(string(address)+":"+string(port_name)); + + window=gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), title.c_str()); + + display=make_unique<Display>(*this); + gtk_container_add(GTK_CONTAINER(window), display->widget); + gtk_widget_show_all(window); + gtk_widget_add_tick_callback (display->widget, update_data, this, nullptr); +} const char *channel_name, *fullscreen_option, *keyboard_mode, *address, *port_name; GtkWidget *window; GdkDisplay *main_display; -GdkDevice *pointer_device; int interactive_mode=0; GtkApplication *app; blc_array mouse_array; -blc_array input; +unique_ptr<Client> client; char const *input_name; @@ -38,19 +61,15 @@ uint32_t format; void ask_fullscreen(){ gtk_window_fullscreen(GTK_WINDOW(window)); - } -void ask_quit() -{ +void ask_quit(){ g_application_quit(G_APPLICATION(app)); blc_quit(); } -void on_key_press(GtkWidget *widget, GdkEventKey *event){ - +void on_key_press(GtkWidget *widget, GdkEventKey *event){ char key; - if (event->type == GDK_KEY_PRESS){ key=event->keyval; fwrite(&key, 1, 1, stdout); @@ -58,42 +77,23 @@ void on_key_press(GtkWidget *widget, GdkEventKey *event){ } } -void activate_cb(GApplication *app) -{ - GtkWidget *display=nullptr; - GtkWidget *grid; - string title(string(address)+":"+string(port_name)); - - main_display = gdk_display_get_default (); - GdkDeviceManager *device_manager = gdk_display_get_device_manager (main_display); - pointer_device = gdk_device_manager_get_client_pointer (device_manager); - + +void activate_cb(GApplication *app, gpointer){ window=gtk_application_window_new(GTK_APPLICATION(app)); - gtk_window_set_title(GTK_WINDOW(window), title.c_str()); - - grid=gtk_grid_new(); - - // for(i=0; input_names[i]; i++){ This is for displaying multiple images - // input=new blc_channel(/*input_names[i]*/ input_name, BLC_CHANNEL_READ); - display=create_image_display(&input); - if (display==NULL) EXIT_ON_ARRAY_ERROR(&input, "Format not managed."); - gtk_widget_set_hexpand(display, 1); - gtk_widget_set_vexpand(display, 1); - gtk_container_add(GTK_CONTAINER(grid), display); - // } - gtk_container_add(GTK_CONTAINER(window), grid); + gtk_window_set_title( GTK_WINDOW(window), "c_gtk_image"); + client=make_unique<Client>(address, port_name); + if (client->display==nullptr) EXIT_ON_ARRAY_ERROR(client.get(), "Format not managed."); + if (keyboard_mode) g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK (on_key_press), nullptr); gtk_widget_show_all(window); - if (keyboard_mode) g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK (on_key_press), NULL); - + } /** Classical GTK application. * The first optional argument is the name of the experience. Otherwise all the existing shared memory are used. * */ -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]){ int status=0; - char const *g_debug, *mouse_name, *min_str, *max_str; + char const *g_debug, *min_str, *max_str; blc_program_set_description("Display the content of the blc_channel depending on its type on format"); blc_program_add_option(&keyboard_mode, 'k', "keyboard", nullptr, "Send keyboard input to stdout", nullptr); @@ -101,45 +101,23 @@ int main(int argc, char *argv[]) blc_program_add_option(&max_str, 'M', "max", "FL32", "maximal value", nullptr); blc_program_add_option(&fullscreen_option, 'F', "fullscreen", nullptr, "Set the window in fullscreen", nullptr); blc_program_add_option(&g_debug, 'g', "g-fatal-warnings", nullptr, "Debug gtk.", nullptr); - blc_program_add_parameter(&address, "address", 1, "internet address", "localhost"); - blc_program_add_parameter(&port_name, "port", 1, "port name", "31440"); - + blc_program_add_parameter(&address, "address", 0, "internet address", "localhost"); + blc_program_add_parameter(&port_name, "port", 0, "port name", "31440"); blc_program_init(&argc, &argv, ask_quit); - blc_command_forward_blc_channels(); - - blc_array_tcp4_client input_client(&input, address, port_name); - input.allocate(); - fprintf(stderr, "Connecté\n"); - - min_val=0; - if (input.type=='UIN8') max_val=256; - if (input.type=='INT8'){ - min_val=-128; - max_val=128; - } - - if (input.type=='FL32' && input.format=='Y800'){ - if (min_str) SSCANF(1, min_str, "%f", &min_val); - else min_val=0; - if (max_str) SSCANF(1, max_str, "%f", &max_val); - else max_val=1; - }else if (min_str || max_str) EXIT_ON_ARRAY_ERROR(&input, "Min (-m) and max (-M) have only effect for type FL32 format Y800"); - - - thread input_thread([&input_client](){ - while(1) { - input_client.read_data(); - } - }); + if (g_debug) { + argc=2; + argv=MANY_ALLOCATIONS(3, char*); + argv[0]=strdup("Program"); + argv[1]=strdup("--g-fatal-warnings"); + argv[2]=nullptr; + } gtk_disable_setlocale(); gtk_init(&argc, &argv); app = gtk_application_new(nullptr, G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate_cb), nullptr); status = g_application_run(G_APPLICATION(app), 0, nullptr); g_object_unref(app); - - input_thread.join(); - return EXIT_SUCCESS; + return status; } diff --git a/gtk_channels/ChannelWidget.cpp b/gtk_channels/ChannelWidget.cpp index 9d562a14b47d5fd5962631c673447ca571e714bf..9cabb3347312180e54cbaa6e74088da2705b6d69 100644 --- a/gtk_channels/ChannelWidget.cpp +++ b/gtk_channels/ChannelWidget.cpp @@ -1,7 +1,4 @@ // -// Channel.cpp -// e_bash -// // Created by Arnaud Blanchard on 29/09/2019. // @@ -58,24 +55,25 @@ ChannelWidget::ChannelWidget(){ col->add_attribute(stop_cell_renderer, "sensitive", columns.stop_sensitive); append_column(*col); - - - commands.emplace("graph", Command(bin_dir, "o_gnuplot", {}, [](vector<blc_channel const*> channels ){ + default_commands.emplace("graph", Command(bin_dir, "o_gnuplot", {}, [](vector<blc_channel const*> channels ){ if (channels.size()>1) return false; - })); - commands.emplace("graph history", Command(bin_dir, "o_gnuplot", {"--history=100"}, [](vector<blc_channel const*> channels ){ - int total_length=channels[0]->total_length; - return all_of(channels.begin()+1,channels.end(),[total_length](blc_channel const *channel){ - if (channel->dims_nb==1 && channel->total_length!=total_length) return false; - else if (channel->dims_nb==2) return false; - return true; - }); - })); - commands.emplace("keyboard", Command(bin_dir, "i_keyboard", {"--debug"}, [](vector<blc_channel const*> channels ){ + return true; + })); + + default_commands.emplace("graph history", Command(bin_dir, "o_gnuplot", {"--history=100"}, [](vector<blc_channel const*> channels ){ + int total_length=channels[0]->total_length; + return all_of(channels.begin()+1,channels.end(),[total_length](blc_channel const *channel){ + if (channel->dims_nb==1 && channel->total_length!=total_length) return false; + else if (channel->dims_nb==2) return false; + return true; + }); + })); + + default_commands.emplace("keyboard", Command(bin_dir, "i_keyboard", {"--debug"}, [](vector<blc_channel const*> channels ){ if (channels.size()==1 && channels[0]->total_length<=64) return true; else return false; })); - + signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){ int x, y; @@ -110,7 +108,7 @@ ChannelWidget::ChannelWidget(){ signal_button_release_event().connect([this](GdkEventButton const *event){ if (event->button == 3){ - ContextMenu *menu=create_menu(); + ContextMenu *menu=create_popup_menu(); menu->popup_at_pointer((GdkEvent*)event); } return true; @@ -122,8 +120,9 @@ ChannelWidget::ChannelWidget(){ show_all_children(); } + blc_channel *ChannelWidget::find_channel(int id){ - blc_channel *channel= lower_bound(channels, channels+channels_nb, id, [](blc_channel const &a, int id){ + blc_channel *channel = &lower_bound(channels.begin(), channels.end(), id, [](blc_channel const &a, int id){ return (a.id < id); }); return channel; @@ -174,41 +173,41 @@ void ChannelWidget::on_quit(Process *process, int status, std::vector<TreeModel: } } /* -void ChannelWidget::create_and_execute_command(string program_name, initializer_list<const char*> arguments){ - commands.emplace_back(bin_dir, program_name, arguments)); - auto command=&commands.back(); - command->execute(); -}*/ + void ChannelWidget::create_and_execute_command(string program_name, initializer_list<const char*> arguments){ + commands.emplace_back(bin_dir, program_name, arguments)); + auto command=&commands.back(); + command->execute(); + }*/ /* -void ContextMenu::add_program(char const *menu_name, std::string program_name, initializer_list<const char*> const optional_arguments, function <void(Process *process)> after_launch){ - auto item=new MenuItem(menu_name); - auto args_vec=make_shared<vector<const char*>>(optional_arguments); //This will be destroyed by process - append(*item); - - item->signal_activate().connect([this, args_vec, program_name, after_launch]{ - args_vec->reserve(args_vec->size()+selected_channels.size()); - for(auto const &channel:selected_channels){ - args_vec->push_back(strdup(channel->name)); - } - auto process = new Process(program_dir, program_name, args_vec); - process->run(after_launch); - }); -}*/ + void ContextMenu::add_program(char const *menu_name, std::string program_name, initializer_list<const char*> const optional_arguments, function <void(Process *process)> after_launch){ + auto item=new MenuItem(menu_name); + auto args_vec=make_shared<vector<const char*>>(optional_arguments); //This will be destroyed by process + append(*item); + + item->signal_activate().connect([this, args_vec, program_name, after_launch]{ + args_vec->reserve(args_vec->size()+selected_channels.size()); + for(auto const &channel:selected_channels){ + args_vec->push_back(strdup(channel->name)); + } + auto process = new Process(program_dir, program_name, args_vec); + process->run(after_launch); + }); + }*/ /* -item->signal_activate().connect([this, args_vec, program_name, after_launch]{ - args_vec->reserve(args_vec->size()+selected_channels.size()); - for(auto const &channel:selected_channels){ - args_vec->push_back(strdup(channel->name)); - } - auto process = new Process(program_dir, program_name, args_vec); - process->run(after_launch); -}); + item->signal_activate().connect([this, args_vec, program_name, after_launch]{ + args_vec->reserve(args_vec->size()+selected_channels.size()); + for(auto const &channel:selected_channels){ + args_vec->push_back(strdup(channel->name)); + } + auto process = new Process(program_dir, program_name, args_vec); + process->run(after_launch); + }); */ -ContextMenu *ChannelWidget::create_menu(){ +ContextMenu *ChannelWidget::create_popup_menu(){ bool only_channels=true; vector<Command*> context_commands; vector<blc_channel const *> selected_channels; @@ -216,12 +215,12 @@ ContextMenu *ChannelWidget::create_menu(){ menu->row_selection=get_selection(); get_selection()->selected_foreach_iter ([&](TreeModel::iterator const &iter){ - if (tree_store->iter_depth(iter)==0){ - blc_channel *channel=find_channel((*iter)[columns.id]); - selected_channels.emplace_back(channel); - } - else only_channels=false; - }); + if (tree_store->iter_depth(iter)==0){ + blc_channel *channel=find_channel((*iter)[columns.id]); + selected_channels.emplace_back(channel); + } + else only_channels=false; + }); if (only_channels){ menu->add_item("_Remove", true, [this]{ @@ -231,12 +230,46 @@ ContextMenu *ChannelWidget::create_menu(){ }); menu->add_separator(); - for ( const auto& [name, command]:commands){ + for ( const auto& [name, command]:default_commands){ Command const *command_ptr=&command; //wihout ptr, command cannot be pass to lambda if (command.valid_channels(selected_channels)); menu->add_item(name, false, [this, command_ptr, selected_channels](){ auto command =used_commands.emplace_back(*command_ptr, selected_channels); - auto process=new Process(command.args.data(), [](Process*){}); + Glib::RefPtr<TreeModel> tree_model = tree_store; + auto paths=this->get_selection()->get_selected_rows(tree_model); + vector<TreeRow> command_rows; + + for (auto const &path: paths){ + string full_command = accumulate(command.args.begin()+1, command.args.end()-1, command.program_name,[](string const a, string const b){ + return a + " " +b; + }); + + auto iter=tree_store->get_iter(path); + if (tree_store->iter_depth(iter)==0) { + TreeRow &row=command_rows.emplace_back(*tree_store->append((iter)->children())); + row[columns.name]=full_command; + row[columns.terminal_icon]="utilities-terminal"; + expand_row(path, true); + } + } + + auto process=new Process(command.args.data(), [command_rows](Process* process){ + for (auto const &row:command_rows){ + row[columns.play_pause_icon]="media-playback-pause"; + row[columns.play_pause_sensitive]=true; + row[columns.stop_icon]="media-playback-stop"; + row[columns.stop_sensitive]=true; + row[columns.process]= process; + } + process->on_quit([command_rows](int status){ + for (auto const &row :command_rows){ + if (status==0) row[columns.terminal_icon]="process-stop"; + else row[columns.terminal_icon]="dialog-error"; + row[columns.play_pause_icon]="media-playback-start"; + row[columns.stop_sensitive]=false; + } + }); + }); }); } } @@ -244,38 +277,53 @@ ContextMenu *ChannelWidget::create_menu(){ return menu; } +template <class _Container> +insert_iterator<_Container> remove_channel(_Container& __x, typename _Container::iterator __i) +{ + return insert_iterator<_Container>(__x, __i); +} + /** We look for all channels. We get a list of actual row and look for differences to add or remove channel in the known list. This suppose that a channel with a same id has the same properties. It is a property of blc_channel. */ void ChannelWidget::refresh_channel_list(){ - /// We define a functor to compare blc_channel and TreeIter by id - struct compare_id{ - bool operator()(blc_channel const &a, TreeIter const &b) { return a.id < (*b)[columns.id]; }; - bool operator()(TreeIter const &a, blc_channel const &b) { return (*a)[columns.id]< b.id ; }; - }; - vector<blc_channel> news_channels; - vector<TreeIter> channels_to_remove; - - FREE(channels); - tie(channels, channels_nb)=blc_channel::get_all_infos(); - - //We get children to be able to use iterator - auto children=tree_store->children(); - - //We get a list of new channels - set_difference(channels, channels+channels_nb, children.begin(), children.end(), inserter(news_channels, news_channels.begin()), compare_id()); - - //We get a list of channels to remove - set_difference(children.begin(), children.end(), channels, channels+channels_nb, inserter(channels_to_remove, channels_to_remove.begin()), compare_id()); - - for(auto const &tree_iter : channels_to_remove) tree_store->erase(tree_iter); - for(auto const &channel :news_channels) columns.add_channel(tree_store, channel); - - this->show_all_children(); - - //We free the list of channel before getting a new list. This is not optimum but it does not happen often. - for_each(channels, channels+channels_nb, [](blc_channel channel){ - channel.~blc_channel(); //We destroy the dims inside - }); - } + auto [local_channels, local_channels_nb] = blc_channel::get_all_infos(); + + //We get children to be able to use iterator + auto children=tree_store->children(); + + for(auto const &child : children){ + int id = child[columns.id]; + auto it=lower_bound(local_channels,local_channels+local_channels_nb, id); + if (it==local_channels+local_channels_nb){ + tree_store->erase(child); + channels.erase(id); + } + } + + for_each(local_channels, local_channels+local_channels_nb, [](blc_channel const &channel){ + // find_if([](){ + // columns.add_channel(tree_store, channel); + + }); + }); + + + + for(auto const &tree_iter : channels_to_remove) { + tree_store->erase(tree_iter); + } + + for(auto const &channel :news_channels) { + } + + this->show_all_children(); + + //We free the list of channel before getting a new list. This is not optimum but it does not happen often. + for_each(local_channels, local_channels+local_channels_nb, [](blc_channel channel){ + channel.~blc_channel(); //We destroy the dims inside + }); + FREE(channels); + +} diff --git a/gtk_channels/ChannelWidget.hpp b/gtk_channels/ChannelWidget.hpp index b6b5e8887baa10072eb296475a5938b7b22f2993..9996c8535f892b33db69a7006b587b9f7b190849 100644 --- a/gtk_channels/ChannelWidget.hpp +++ b/gtk_channels/ChannelWidget.hpp @@ -21,22 +21,23 @@ struct ChannelWidget:Gtk::TreeView{ - blc_channel* channels; //This structure is coherent with blc_channel implementation + std::map<int, blc_channel> channels; ContextMenu *menu=nullptr; Gtk::CellRendererPixbuf terminal_cell_renderer, play_pause_cell_renderer, stop_cell_renderer; Glib::RefPtr<Gtk::TreeStore> tree_store; - int channels_nb; std::string bin_dir; - std::map<std::string, Command> commands; std::vector<Command> used_commands; + + std::vector<Gtk::TreeRow> channel_rows; + + static std::map<std::string, Command> default_commands; static ChannelColumns columns; ChannelWidget(); blc_channel *find_channel(int id); - ContextMenu *create_menu(); + ContextMenu *create_popup_menu(); void create_and_execute_command(std::string program_name, std::initializer_list<const char*> const optional_arguments); - void display_process(Gtk::TreeModel::Row &row); void refresh_channel_list(); void after_launch(Process *process, std::vector<Gtk::TreeModel::Path> rows); diff --git a/gtk_channels/Command.hpp b/gtk_channels/Command.hpp index ac6f02f472d8eb9e680c99c416443f4d70f46f0a..ebd3df6b1ecdca034ad09a32d0e689a64dcb406a 100644 --- a/gtk_channels/Command.hpp +++ b/gtk_channels/Command.hpp @@ -10,15 +10,18 @@ #include <string> #include <vector> +#include <gtkmm.h> #include "Process.h" #include "blc_channel.h" + /* One command may have many processes */ struct Command{ std::string binary; std::string program_name; std::function<bool(std::vector<blc_channel const*> &channels)> valid_channels; std::vector<const char *> args; + std::vector<Gtk::TreeRow> command_rows; Command(std::string const &bin_dir, std::string program_name, std::initializer_list<char const*> default_args, std::function<bool(std::vector<blc_channel const*>)>validity); Command(Command const &command, std::vector<blc_channel const*> const channel); //Copy command and add arguments diff --git a/gtk_channels/Process.cpp b/gtk_channels/Process.cpp index 41832ede7eae0ccf7a4e0fd1062e2cc2dac0b4fd..8f0430aa6d7cfc86ae8fff418dd77abc40dbbdbc 100644 --- a/gtk_channels/Process.cpp +++ b/gtk_channels/Process.cpp @@ -53,7 +53,6 @@ Process::Process(char const * const *argv, function<void(Process *)>after_launch }; - void Process::run(){ vte_terminal_spawn_async(terminal, VTE_PTY_DEFAULT , nullptr, (char**)arg_vector.data(), nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, 1000, nullptr, TerminalSpawnAsyncCallback, (void*)this); } diff --git a/include/Display.hpp b/include/Display.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e6930e686231ab3796f40061bc6dd03654f13b03 --- /dev/null +++ b/include/Display.hpp @@ -0,0 +1,50 @@ +// +// Display.hpp +// c_gtk_image +// +// Created by Arnaud Blanchard on 12/11/2020. +// + +#ifndef Display_hpp +#define Display_hpp + +#include "UIN8_Y800.hpp" +#include "blc_array.h" +#include "cairo.h" +#include "gtk/gtk.h" + +class Display:public blc_array{ +public: + Display(blc_array const &array); + ~Display(); + void update(); + void framerate(); + void update_zoom(); + + static void histogram_cb(); + + double min_val, max_val; + int iterations_nb; + + GtkWidget *histogram_button; + GtkWidget *widget; + GtkWidget *fit_button; + std::unique_ptr<Image> image; + std::unique_ptr<Histogram> histogram; + GtkWidget *toolbar, *general_statusbar, *framerate_statusbar, *pointer_statusbar, *panel; +}; + + +class UIN8_UYVY_display:public Display{ + void snprint_pixel(char *text, int size_max, int i, int j); + void snprint_histogram(char *text, int size_max, int i); +public: + UIN8_UYVY_display(blc_array const &array); + void update(); + void show_histogram(); + void hide_histogram(); + + uint32_t *image_data; + static uint32_t *RGBA_from_YUYV; +}; +#endif /* Display_hpp */ diff --git a/include/Histogram.hpp b/include/Histogram.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f1fbcba6e8a0d0ffe1fb5f189aa9323cfb586357 --- /dev/null +++ b/include/Histogram.hpp @@ -0,0 +1,40 @@ +// +// Histogram.hpp +// c_gtk_image +// +// Created by Arnaud Blanchard on 12/11/2020. +// + +#ifndef Histogram_h +#define Histogram_h + +#include <vector> +#include <blc_array.h> +#include <cairo.h> +#include <gtk/gtk.h> +#include <string> + +class Histogram:public blc_array{ + static void resize_cb(GtkWidget *widget, GdkRectangle *allocation, Histogram *histogram); +protected: + void refresh(); + double x_scale, y_scale; +public: + Histogram(blc_array const &array); + virtual ~Histogram() = default; + bool get_pointer_position(int &i); + + virtual void update() = 0; + virtual void update_pointer_info()=0 ; + + std::string pointer_info; + cairo_surface_t *surface; + size_t width, height; + int iterations_nb; + uchar *surface_data; + GdkWindow *window; + GdkDevice *pointer_device; + GtkWidget *toolbar, *general_statusbar, *framerate_statusbar, *pointer_statusbar; + GtkWidget *widget, *image, *scrolled_window, *legend_box; +}; +#endif /* Histogram_h */ diff --git a/include/Image.hpp b/include/Image.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dd015ba58ae1bfd444398c9c08c49b249d57e1b6 --- /dev/null +++ b/include/Image.hpp @@ -0,0 +1,49 @@ +// +// Image.hpp +// c_gtk_image +// +// Created by Arnaud Blanchard on 23/11/2020. +// + +#ifndef Image_hpp +#define Image_hpp + +#include "blc_array.h" +#include <gtk/gtk.h> +#include <cairo.h> +#include <string> + + +class Image:public blc_array{ + bool fit_size=true; +public: + + Image(blc_array const &array); + virtual ~Image(); + void resize(int allocated_width, int allocated_height); + void zoom_original(); + void zoom(double factor); + void active_fit_size(); + + /** set pointer position (i, j) in the window. return false if the pointer is not in the window. */ + bool get_pointer_position(int &i, int &j); + + + /** Display the new image with the content array.*/ + virtual void update()=0; + + static void resize_cb(GtkWidget *widget, GdkRectangle *allocation, Image *image); + + + std::string pointer_info; + double x_scale, y_scale; + std::string pixel_info(); + cairo_surface_t *surface; + GdkDevice* pointer_device; + GdkWindow *window; + GtkWidget *widget, *image; + size_t width, height; +}; + + +#endif /* Image_h */ diff --git a/include/UIN8_Y800.hpp b/include/UIN8_Y800.hpp new file mode 100644 index 0000000000000000000000000000000000000000..71381bed5bb5740c250fbc646f12a94f4c0951bd --- /dev/null +++ b/include/UIN8_Y800.hpp @@ -0,0 +1,35 @@ +// +// UIN8_Y800.hpp +// c_gtk_image +// +// Created by Arnaud Blanchard on 23/11/2020. +// + +#ifndef UIN8_Y800_hpp +#define UIN8_Y800_hpp + +#include "Image.hpp" +#include "Histogram.hpp" +#include <vector> + +class UIN8_Y800_image:public Image{ + std::vector<uint32_t> color_map; + uint32_t *pixels; + +public: + UIN8_Y800_image(blc_array const &array); + void update(); + void update_pointer_info(); +}; + +class UIN8_Y800_histogram:public Histogram{ + std::vector<uint32_t>color_map; + std::vector<uint8_t>histogram; + uint32_t *pixels; + +public: + UIN8_Y800_histogram(blc_array const &array); + void update(); + void update_pointer_info(); +}; +#endif /* UIN8_Y800_h */ diff --git a/include/common.h b/include/common.h deleted file mode 100644 index 880b852e20e294de4bbaf9e5c2546861dc6e81bb..0000000000000000000000000000000000000000 --- a/include/common.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef COMMON_H -#define COMMON_H - -#include <gtk/gtk.h> -#include <blc_core.h> - - -extern float min_val, max_val; - -extern blc_array *array; -extern GtkWidget *legend; - -extern u_int32_t false_colors[256]; -extern uint32_t gray_colors[256]; -extern uint32_t u_colors[256]; -extern uint32_t v_colors[256]; -extern uint32_t r_colors[256]; -extern uint32_t g_colors[256]; -extern uint32_t b_colors[256]; -extern uint32_t *color_map; - -extern GtkWidget *window, *paned; -extern GdkDevice *pointer_device; -extern blc_array mouse_array; - -extern char const *fullscreen_option; - - - -void histogram_cb(GtkToolButton *toolbutton, gpointer pointer_statusbar ); -GtkWidget *create_image_display(blc_array *array); - -#endif - diff --git a/o_gtk_image/CMakeLists.txt b/o_gtk_image/CMakeLists.txt index a9f91ec1a2837a6f3eee0fb6db9d8b97808103e8..89e6e96c740b69a2833dcc321c9349cda00a6cae 100644 --- a/o_gtk_image/CMakeLists.txt +++ b/o_gtk_image/CMakeLists.txt @@ -1,24 +1,22 @@ # Set the minimum version of cmake required to build this project -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.13) project(o_gtk_image) #This is to be able to debug and recompile automatically the libs (shared_blc). It is slower, you can remove this line and use : ${BLAR_BUILD_DIR}/lib/libblc.dylib instead of shared_blc -find_package(blc_channel REQUIRED) -find_package(blc_image REQUIRED) -find_package(blc_program REQUIRED) +find_package(blc REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 REQUIRED gtk+-3.0) find_package(JPEG REQUIRED) -add_definitions(-Wall ${BL_DEFINITIONS} -Wno-deprecated-declarations) #device_manager (mouse) is deprecated +add_definitions(-Wall -Wno-deprecated-declarations) #device_manager (mouse) is deprecated include_directories(${GTK3_INCLUDE_DIRS} ${BL_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR} ../include) link_directories(${GTK3_LIBRARY_DIRS}) add_definitions(${GTK3_CFLAGS_OTHER}) -add_executable(o_gtk_image o_gtk_image.cpp ../src/image_display.cpp ../src/histogram.cpp) -target_link_libraries(o_gtk_image ${GTK3_LIBRARIES} ${BL_LIBRARIES} ${JPEG_LIBRARIES} ) +add_executable(o_gtk_image o_gtk_image.cpp ../src/image_display.cpp ../src/histogram.cpp) +target_link_libraries(o_gtk_image PRIVATE blc ${GTK3_LIBRARIES} ${JPEG_LIBRARIES} ) diff --git a/src/Display.cpp b/src/Display.cpp new file mode 100644 index 0000000000000000000000000000000000000000..87592721f26711f1815210b6a77acfc41ba99519 --- /dev/null +++ b/src/Display.cpp @@ -0,0 +1,190 @@ +// +// Display.cpp +// c_gtk_image +// +// Created by Arnaud Blanchard on 12/11/2020. +// +#include "Display.hpp" + +using namespace std; + +static GtkToggleToolButton *blgtk_add_toggle_tool_button(GtkWidget *toolbar, char const *label, char const *icon_name, GCallback callback, void *user_data) +{ + GtkToggleToolButton *toggle_tool_button; + + toggle_tool_button=GTK_TOGGLE_TOOL_BUTTON(gtk_toggle_tool_button_new()); + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toggle_tool_button), icon_name); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(toggle_tool_button), label); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(toggle_tool_button)); + if (callback) g_signal_connect(G_OBJECT(toggle_tool_button), "clicked", callback, user_data); + return toggle_tool_button; +} + +static GtkWidget *blgtk_add_tool_button(GtkWidget *toolbar, gchar const *label, gchar const *icon_name, GCallback callback, gpointer user_data) +{ + GtkWidget *tool_button; + + tool_button=GTK_WIDGET(gtk_tool_button_new(gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR), label)); + gtk_container_add(GTK_CONTAINER(toolbar), tool_button); + if (callback) g_signal_connect(G_OBJECT(tool_button), "clicked", callback, user_data); + return tool_button; +} + +static void toggle_fullscreen(GtkWidget *widget, GdkEventWindowState *event, Display *display){ + + if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN ){ + gtk_widget_hide(display->toolbar); + gtk_widget_hide(display->general_statusbar); + } + else{ + gtk_widget_show(display->toolbar); + gtk_widget_show(display->general_statusbar); + } +} + +static void zoom_original_cb(GtkToolButton *, Display *display){ + display->image->zoom_original(); +} + +static void zoom_out_cb(GtkToolButton *toolbutton, Display *display){ + display->image->zoom(2); +} + +static void zoom_in_cb(GtkToolButton *toolbutton, Display *display){ + display->image->zoom(0.5); +} + +static void false_color_cb(GtkToolButton *toolbutton, struct update_cb *){ + GtkWidget *label, *legend_box, *legend_image; + char text[NAME_MAX+1]; + int i; + /* + + if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toolbutton))){ + color_map=false_colors; + legend_surface=cairo_image_surface_create_for_data ((uchar*)false_colors, CAIRO_FORMAT_RGB24, 256, 1, 256*4); + legend=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); + legend_image = gtk_image_new_from_surface(legend_surface); + legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); + + FOR(i, 7) + { + SPRINTF(text, "%d", (i+1)*256/8); + label = gtk_label_new(text); + gtk_box_pack_start(GTK_BOX(legend_box), label, TRUE, TRUE, 0); + } + gtk_container_add(GTK_CONTAINER(legend), legend_image); + gtk_container_add(GTK_CONTAINER(legend), legend_box); + gtk_paned_add2(GTK_PANED(paned),legend ); + gtk_widget_show_all(legend); + } + else{ + color_map=gray_colors; + if (legend){ + gtk_container_remove(GTK_CONTAINER(paned), legend); + gtk_widget_destroy(legend); + legend=NULL; + } + }*/ +} + +static gboolean frame_rate(gpointer user_data){ + auto display = reinterpret_cast<Display*>(user_data); + display->framerate(); + return G_SOURCE_CONTINUE; +} + +Display::Display(blc_array const &array):blc_array(array){ + uint32_t type_string,format_string; + char label_text[NAME_MAX + 1]; + + switch (format){ + case 'Y800':switch (type){ + case 'UIN8':image=make_unique<UIN8_Y800_image>(*this); //*this because it is a copy constructor of blc_array + break; + } + break; + case 'yuv2':case 'UYVY': + switch (type){ + // case 'UIN8':image=make_unique<UIN8_UYVY_display>(*this); + break; + } + break; + default: + EXIT_ON_ERROR("Unknow image type or format"); + } + + widget=gtk_box_new(GTK_ORIENTATION_VERTICAL, 3); + toolbar = gtk_toolbar_new(); + general_statusbar=gtk_statusbar_new(); + framerate_statusbar=gtk_statusbar_new(); + pointer_statusbar=gtk_statusbar_new(); + + gtk_container_add(GTK_CONTAINER(general_statusbar), framerate_statusbar); + gtk_container_add(GTK_CONTAINER(general_statusbar), pointer_statusbar); + gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH); + + g_timeout_add(1000, frame_rate, this); + + SPRINTF(label_text, "%.4s, %.4s, %zxx%zu", UINT32_TO_STRING(type_string, type), UINT32_TO_STRING(format_string, format), image->width, image->height); + gtk_statusbar_push(GTK_STATUSBAR(general_statusbar), 0, label_text); + + blgtk_add_tool_button(toolbar, "zoom\n1:1", "zoom-original", G_CALLBACK(zoom_original_cb), this); + blgtk_add_tool_button(toolbar, "zoom\nx0.5", "zoom-out", G_CALLBACK(zoom_out_cb), this); + blgtk_add_tool_button(toolbar, "zoom\nx2", "zoom-in", G_CALLBACK(zoom_in_cb), this); + fit_button=GTK_WIDGET(blgtk_add_toggle_tool_button(toolbar, "zoom\nfit", "zoom-fit-best", nullptr, nullptr)); + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(fit_button), TRUE); + // blgtk_add_toggle_tool_button(toolbar, "cursor\nvalues", "system-search", NULL, NULL);/ + // histogram_button=GTK_WIDGET(blgtk_add_toggle_tool_button(toolbar, "histogram", "x-office-presentation", G_CALLBACK(histogram_cb), this)); + +// GtkWidget *paned=gtk_paned_new(GTK_ORIENTATION_VERTICAL); +// gtk_paned_set_wide_handle (GTK_PANED(paned), 1); + + // gtk_paned_add1(GTK_PANED(paned), image->widget); + // gtk_paned_add2(GTK_PANED(paned), gtk_label_new("bottom")); + + gtk_container_add(GTK_CONTAINER(widget), toolbar); + gtk_container_add(GTK_CONTAINER(widget), image->widget); + //gtk_container_add(GTK_CONTAINER(widget), paned); + gtk_container_add(GTK_CONTAINER(widget), general_statusbar); + +// image->zoom_original(); + gtk_widget_show_all(widget); + + // g_signal_connect(G_OBJECT(widget), "size-allocate", G_CALLBACK(Display::resize_cb), this); + + /* gtk_widget_set_vexpand(image, TRUE); + gtk_widget_set_hexpand(image, TRUE);*/ + + /*g_signal_connect(window, "window-state-event", G_CALLBACK (toggle_fullscreen), this); + if (fullscreen_option) gtk_window_fullscreen(GTK_WINDOW(window));*/ +} + +Display::~Display(){ + data=nullptr; +} + +void Display::update(){ + if (image) { + image->update(); + gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, image->pointer_info.data()); + } + + if (histogram) { + histogram->update(); + gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, histogram->pointer_info.data()); + } + iterations_nb++; +} + +void Display::histogram_cb(){ + +} + +void Display::framerate(){ + char text[64]; + + SPRINTF(text, "%dHz", iterations_nb); + gtk_statusbar_push(GTK_STATUSBAR(framerate_statusbar), 0, text); + iterations_nb=0; +} diff --git a/src/Image.cpp b/src/Image.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd4a375ba7eb3b7da59961ef7e9f12cafbf3fc67 --- /dev/null +++ b/src/Image.cpp @@ -0,0 +1,92 @@ + +// +// Display.cpp +// c_gtk_image +// +// Created by Arnaud Blanchard on 12/11/2020. +// +#include "Image.hpp" + +Image::Image(blc_array const &array):blc_array(array){ + widget=gtk_scrolled_window_new(nullptr, nullptr); + // gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS); + GdkDisplay* default_display = gdk_display_get_default(); + GdkSeat* seat = gdk_display_get_default_seat(default_display); + pointer_device = gdk_seat_get_pointer(seat); + window=gtk_widget_get_window(widget); + + gtk_widget_set_vexpand(GTK_WIDGET(widget), TRUE); + image = gtk_image_new(); + gtk_container_add(GTK_CONTAINER(widget), image ); + + + +// GtkWidget* event_box=gtk_event_box_new(); + // gtk_container_add(GTK_CONTAINER(event_box), image); + // gtk_container_add(GTK_CONTAINER(widget), event_box ); + +// gtk_widget_set_vexpand(widget, TRUE); +// gtk_widget_set_size_request(widget, width, height); + + g_signal_connect(G_OBJECT(image), "size-allocate", G_CALLBACK(resize_cb), this); +} + +Image::~Image(){ + data=nullptr; //To avoid to free the data twice here, and by the calling array; +} + +bool Image::get_pointer_position(int &i, int &j){ + int x, y; + gdk_window_get_device_position(window, pointer_device, &x, &y, nullptr); + i=x*x_scale; + j=y*y_scale; + + return (i >=0 && i<width && j>=0 && j<height); +} + +void Image::active_fit_size(){ + fit_size=true; + // cairo_surface_set_device_scale(surface, (double)width/allocation->width, (double)height/(double)allocation->height); +} + +void Image::zoom_original(){ + fit_size=false; + x_scale=1; + y_scale=1; + cairo_surface_set_device_scale(surface, x_scale, y_scale); + gtk_widget_set_size_request(widget, width/x_scale, height/y_scale); + + // auto image_height=gtk_widget_get_allocated_height(image); + // auto window_height = gtk_widget_get_allocated_height(image); + + // if (fit_size) gtk_window_resize(GTK_WINDOW(image), width, window_height-image_height+height );*/ +} + +void Image::zoom(double factor){ + fit_size=false; + x_scale*=factor; + y_scale*=factor; + cairo_surface_set_device_scale(surface, x_scale, y_scale); + + gtk_widget_set_size_request(widget, width/x_scale, height/y_scale); + /* auto image_height=gtk_widget_get_allocated_height(image); + auto image_width=gtk_widget_get_allocated_width(image);*/ + + +// gtk_widget_size_request(GTK_WINDOW(image), width, height ); + +} + +void Image::resize(int allocated_width, int allocated_height){ + x_scale=double(width)/double(allocated_width); + y_scale=double(height)/double(allocated_height); + cairo_surface_set_device_scale(surface, x_scale, y_scale); +} + +// static +void Image::resize_cb(GtkWidget *widget, GdkRectangle *allocation, Image *image){ + if (image->fit_size) image->resize(allocation->width, allocation->height); + + + // else if (display->legend_surface) cairo_surface_set_device_scale(display->legend_surface, 256/(double)allocation->width, 1/(double)allocation->height); +} diff --git a/src/UIN8_UYVY.cpp b/src/UIN8_UYVY.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a6d3ba73a908e89712952b5107740b190faebab --- /dev/null +++ b/src/UIN8_UYVY.cpp @@ -0,0 +1,82 @@ +#include "UIN8_UYVY.hpp" + +UIN8_UYVY_display::UIN8_UYVY_display(blc_array const &array):Display(array){ + int Y, Cb, Cr; + int i, j; + uint8_t G_tmp, B; + uint8_t R[256]; + + width=dims[1].length; + height=dims[2].length; + image_surface=cairo_image_surface_create( CAIRO_FORMAT_RGB24, width, height); + + if (RGBA_from_YUYV==nullptr){ + RGBA_from_YUYV= MANY_ALLOCATIONS(256*256*256, uint32_t); + FOR(Y, 256){ + FOR(j,256) R[j]= CLIP_UCHAR(Y+1.13983*(j-128)); //It does not depend on Cb + FOR(Cb, 256) + { + B = CLIP_UCHAR(Y+2.03211*(Cb-128)); // It does not depend on Cr + G_tmp = - 0.58060*(Cb-128); + + FOR(Cr, 256){ + i = Y + (Cb << 8) + (Cr << 16); + // Version Wikipedia + RGBA_from_YUYV[i] = (R[Cr]<<16) + (((uchar)CLIP_UCHAR(Y-0.39465*(Cr-128) + G_tmp))<< 8)+ B + (255<<24); + } + } + } + } +} + +void UIN8_UYVY_display::update(){ + int Y, U = 128, V = 128, j; + //uints32 is the array data, surface_data is the data of the image + uint32_t* tmp_pixels=reinterpret_cast<uint32_t*>(image_data); + for (int i=0; i!=size;){ + U = uchars[i++]; + Y = uchars[i++]; + j = Y + (U << 8) + (V << 16); + tmp_pixels[0]=RGBA_from_YUYV[j]; + + V = uchars[i++]; + Y = uchars[i++]; + j = Y + (U << 8) + (V << 16); + tmp_pixels[1]=RGBA_from_YUYV[j]; + tmp_pixels+=2; + } +} + + +void UIN8_UYVY_display::snprint_pixel(char *text, int size_max, int i, int j){ + uint8_t Y, U, V; + Y=uchars[i*2+j*width*2+1]; + U=uchars[i*4+j*width*2]; + V=uchars[i*4+j*width*2+2]; + snprintf(text, size_max, "%d,%d: Y:%d, U:%d, V:%d", i, j, Y, U, V); +} + +void UIN8_UYVY_display::snprint_histogram(char *text, int size_max, int i){ + uint8_t Y, U, V; +} + + +void UIN8_UYVY_display::show_histogram(){ + + +} + +void UIN8_UYVY_display::hide_histogram(){ + +} + +uint32_t *UIN8_UYVY_display::RGBA_from_YUYV = nullptr; + + +void UIN8_UYVY_histogram::snprint(char *text, int size_max, int i, int j){ + uint8_t Y, U, V; + Y=array->uchars[i*2+j*width*2+1]; + U=array->uchars[i*4+j*width*2]; + V=array->uchars[i*4+j*width*2+2]; + SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100/(float)array->size); +} diff --git a/src/UIN8_Y800.cpp b/src/UIN8_Y800.cpp new file mode 100644 index 0000000000000000000000000000000000000000..03d090786f918dd8b6dadbc70361e5ba64ab3a8b --- /dev/null +++ b/src/UIN8_Y800.cpp @@ -0,0 +1,71 @@ +#include "UIN8_Y800.hpp" +#include <blc_image.h> + +using namespace std; + +#define Y800_to_RGBA(val) (val+(val<<8)+(val<<16)+(255<<24)) + +UIN8_Y800_image::UIN8_Y800_image(blc_array const &array):Image(array){ + width=dims[0].length; + height=dims[1].length; + surface=cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); + pixels=reinterpret_cast<uint32_t*>(cairo_image_surface_get_data(surface)); + gtk_image_set_from_surface(GTK_IMAGE(image), surface); + + color_map.resize(256); + for (int i=0; i!=color_map.size(); i++) color_map[i]=Y800_to_RGBA(i); + + // false_colors_button=blgtk_add_toggle_tool_button(toolbar, "false\ncolors", "applications-graphics", G_CALLBACK(false_color_cb), nullptr); +} + +void UIN8_Y800_image::update_pointer_info(){ + int i, j; + if (get_pointer_position(i, j)){ + pointer_info=to_string(i)+","+to_string(j)+" Y:"+to_string(uchars[i+j*width]); + } +} + +void UIN8_Y800_image::update(){ + uint8_t grey; + + for (int i=0; i<total_length; i++) { + grey = uchars[i]; + pixels[i]=color_map[grey]; + } + gtk_widget_queue_draw(image); +} + + + + + +// *** Histogram *** + +UIN8_Y800_histogram::UIN8_Y800_histogram(blc_array const &array):Histogram(array){ + surface=cairo_image_surface_create(CAIRO_FORMAT_RGB24, 256, 256); + pixels=reinterpret_cast<uint32_t*>(cairo_image_surface_get_data(surface)); +} + +void UIN8_Y800_histogram::update(){ + int i, j; + uint8_t grey, hist_max; + FOR(i, 256) { + grey=uchars[i]; + histogram[grey]++; + } + hist_max = *max_element(uchars, uchars+size); + FOR(i, 256){ + FOR(j, histogram[i]*255/hist_max) pixels[(255-j)*256+i]=color_map[i]; + pixels[(255-histogram[i]*255/hist_max)*256+i]=255<<24; + FOR(j, 255-histogram[i]*255/hist_max) pixels[j*256+i]=0; + } + gtk_widget_queue_draw(image); +} + +void UIN8_Y800_histogram::update_pointer_info(){ + int i; + + if (get_pointer_position(i)){ + pointer_info="Y["+to_string(i)+"]="+to_string(histogram[i]*100/static_cast<float>(size))+"%"; + } +} diff --git a/src/histogram.cpp b/src/histogram.cpp index 76871eeb2ef0c481bb16ae10576946ebafbade57..4dfc38503090b79bfbcbcd6a707684885a6331ff 100644 --- a/src/histogram.cpp +++ b/src/histogram.cpp @@ -1,220 +1,34 @@ -#include "common.h" +#include "Histogram.hpp" +#include <vector> -#include <fcntl.h> // O_RDONLY ... -#include <stdio.h> -#include <gtk/gtk.h> -#include <string.h> -#include <stdint.h> //uint32_t -#include <jpeglib.h> -#include <sys/mman.h> -#include <errno.h> //errno -#include "blc_image.h" -#include "blc_realtime.h" -#include "blc_program.h" //blc_status +using namespace std; -//cairo_surface_t *legend_surface; -guint histogram_tick_id; -uint32_t *histogram_data=NULL; -GtkWidget *histogram_image, *histogram, *histogram_scrolled_window; -cairo_surface_t *histogram_surface; -static void draw_histogram(uint32_t *drawing, int height, int histogram[256], uint32_t color_map[256], int max){ - int i, j, val; - FOR_INV(i, 256) - { - val = height-histogram[i]*(height-1)/max; - for(j=height; j!=val; j--) drawing[j*256+i]=color_map[i]; - drawing[val*256+i]=255<<24; - FOR_INV(j, val) drawing[j*256+i]=0; - } -} - -static gboolean update_histogram_cb(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar){ - int i, j, x, y; - double sx, sy; - int histogram[256]={0}, histogram1[256]={0}, histogram2[256]={0}, hist_max=0, max1=0, max2=0; - char text[64]; - struct timeval timer; - - CLEAR(timer); - gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL); - cairo_surface_get_device_scale(histogram_surface, &sx, &sy); +Histogram::Histogram(blc_array const &array):blc_array(array){ + pointer_info.resize(64); + widget=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); + GdkDisplay* default_display = gdk_display_get_default(); + GdkSeat* seat = gdk_display_get_default_seat(default_display); + pointer_device = gdk_seat_get_pointer(seat); + window=gtk_widget_get_window(widget); - x*=sx; - y*=sy; - - switch (array->format){ - case 'Y800': - switch (array->type){ - case 'UIN8': - FOR(i, array->size) histogram[array->uchars[i]]++; - FOR(i, 256) hist_max=MAX(histogram[i], hist_max); - - FOR(i, 256) - { - FOR_INV(j, histogram[i]*255/hist_max) histogram_data[(255-j)*256+i]=color_map[i]; - histogram_data[(255-histogram[i]*255/hist_max)*256+i]=255<<24; - FOR_INV(j, 255-histogram[i]*255/hist_max) histogram_data[j*256+i]=0; - } - if (y>=0 && y<256 && x >=0 && x<256) - { - SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100/(float)array->size); - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - break; - case 'FL32': - FOR(i, array->total_length) histogram[(int)CLIP_UCHAR(((array->floats[i]-min_val)/(max_val-min_val)*256+0.5))]++; - FOR(i, 256) hist_max=MAX(histogram[i], hist_max); - FOR(i, 256) - { - FOR_INV(j, histogram[i]*255/hist_max) histogram_data[(255-j)*256+i]=color_map[i]; - histogram_data[(255-histogram[i]*255/hist_max)*256+i]=255<<24; - FOR_INV(j, 255-histogram[i]*255/hist_max) histogram_data[j*256+i]=0; - } - if (y>=0 && y<256 && x >=0 && x<256) - { - SPRINTF(text, "Y[%f]=%f.2%%", x*(max_val-min_val-0.5)/256+min_val, histogram[x]*100/(float)array->total_length); - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - break; - default: EXIT_ON_ARRAY_ERROR( array, "Type not managed for format 'Y800'. Only 'UIN8' is"); - } - break; - case 'RGB3': - FOR_INV(i, array->size/3){ - histogram[array->uchars[i*3]]++; - histogram1[array->uchars[i*3+1]]++; - histogram2[array->uchars[i*3+2]]++; - } - - FOR_INV(i, 256) - { - hist_max=MAX(histogram[i], hist_max); - max1=MAX(histogram1[i], max1); - max2=MAX(histogram2[i], max2); - } - draw_histogram(histogram_data, 85, histogram, r_colors, hist_max); - draw_histogram(histogram_data+256*85, 85, histogram1, g_colors, max1); - draw_histogram(histogram_data+256*170, 85, histogram2, b_colors, max2); - - if (y>=0 && y<256 && x >=0 && x<256) - { - if (y<85) SPRINTF(text, "R[%d]=%f.2%%", x, histogram[x]*100*2/(float)array->size); - else if (y<170) SPRINTF(text, "G[%d]=%.2f%%", x, histogram1[x]*100*4/(float)array->size); - else SPRINTF(text, "B[%d]=%.2f%%", x, histogram2[x]*100*4/(float)array->size); - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - - break; - case 'YUYV': - for(i=array->size-2;i!=-1;i-=2) histogram[array->uchars[i]]++; - for(i=array->size-1;i!=-2;i-=4) histogram1[array->uchars[i]]++; - for(i=array->size-3;i!=-4;i-=4) histogram2[array->uchars[i]]++; - - FOR_INV(i, 256) - { - hist_max=MAX(histogram[i], hist_max); - max1=MAX(histogram1[i], max1); - max2=MAX(histogram2[i], max2); - } - - draw_histogram(histogram_data, 127, histogram, gray_colors, hist_max); - draw_histogram(histogram_data+256*128, 63, histogram1, u_colors, max1); - draw_histogram(histogram_data+256*192, 63, histogram2, v_colors, max2); - - if (y>=0 && y<256 && x >=0 && x<256) - { - if (y<128) SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100*2/(float)array->size); - else if (y<192) SPRINTF(text, "U[%d]=%.2f%%", x, histogram1[x]*100*4/(float)array->size); - else SPRINTF(text, "V[%d]=%.2f%%", x, histogram2[x]*100*4/(float)array->size); - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - break; - case 'yuv2': - for(i=array->size-1;i!=-1;i-=2) histogram[array->uchars[i]]++; - for(i=array->size-2;i!=-2;i-=4) histogram1[array->uchars[i]]++; - for(i=array->size-4;i!=-4;i-=4) histogram2[array->uchars[i]]++; - - FOR_INV(i, 256) - { - hist_max=MAX(histogram[i], hist_max); - max1=MAX(histogram1[i], max1); - max2=MAX(histogram2[i], max2); - } - - draw_histogram(histogram_data, 127, histogram, gray_colors, hist_max); - draw_histogram(histogram_data+256*128, 63, histogram1, u_colors, max1); - draw_histogram(histogram_data+256*192, 63, histogram2, v_colors, max2); - - if (y>=0 && y<256 && x >=0 && x<256) - { - if (y<128) SPRINTF(text, "Y[%d]=%f.2%%", x, histogram[x]*100*2/(float)array->size); - else if (y<192) SPRINTF(text, "U[%d]=%.2f%%", x, histogram1[x]*100*4/(float)array->size); - else SPRINTF(text, "V[%d]=%.2f%%", x, histogram2[x]*100*4/(float)array->size); - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - - } - break; - - default: - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, "No histogram for this format. Only Y800,RGB3 or yuv2."); - - } - gtk_image_set_from_surface(image, histogram_surface); - return G_SOURCE_CONTINUE; + image=gtk_image_new_from_surface(surface); + scrolled_window=gtk_scrolled_window_new(nullptr, nullptr); + legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); + gtk_container_add(GTK_CONTAINER(scrolled_window), image); + g_signal_connect(G_OBJECT(scrolled_window), "size-allocate", G_CALLBACK(resize_cb), this); } - -static void histogram_resize_cb(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data) -{ - (void) widget; - (void) user_data; - cairo_surface_set_device_scale(histogram_surface, (double)256/(double)allocation->width, (double)256/(double)allocation->height); +bool Histogram::get_pointer_position(int &i){ + int j, x,y; + gdk_window_get_device_position(window, pointer_device, &x, &y, nullptr); + i=x*x_scale; + j=y*y_scale; + return (i>=0 && i<256 && j>=0 && j<256); } -void histogram_cb(GtkToolButton *toolbutton, gpointer pointer_statusbar ) -{ - char text[NAME_MAX]; - GtkWidget *legend_box, *label; +// static +void Histogram::resize_cb(GtkWidget *widget, GdkRectangle *allocation, Histogram *histogram){ - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toolbutton))) - { - histogram_data=MANY_ALLOCATIONS(256*256, uint32_t); - histogram_surface=cairo_image_surface_create_for_data ((uchar*)histogram_data, CAIRO_FORMAT_ARGB32, 256, 256, 256*4); - histogram_image=gtk_image_new_from_surface(histogram_surface); - histogram_scrolled_window=gtk_scrolled_window_new(NULL, NULL); - histogram=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); - legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); - gtk_container_add(GTK_CONTAINER(histogram_scrolled_window), histogram_image); - gtk_container_add(GTK_CONTAINER(histogram), histogram_scrolled_window); - gtk_container_add(GTK_CONTAINER(histogram), legend_box); - - SPRINTF(text, "%f", min_val); - label=gtk_label_new(text); - gtk_box_pack_start(GTK_BOX(legend_box), label, FALSE, FALSE, 0); - SPRINTF(text, "%f", max_val); - label=gtk_label_new(text); - gtk_box_pack_end(GTK_BOX(legend_box), label, FALSE, FALSE, 0); - - if (legend) - { - gtk_container_remove(GTK_CONTAINER(paned), legend); - // gtk_widget_destroy(legend); - legend=NULL; - } - gtk_paned_add2(GTK_PANED(paned), histogram); - gtk_widget_set_vexpand(histogram_image, TRUE); - gtk_paned_set_position(GTK_PANED(paned), gtk_widget_get_allocated_height(paned)-256); - gtk_widget_show_all(paned); - g_signal_connect(G_OBJECT(histogram_scrolled_window), "size-allocate", G_CALLBACK(histogram_resize_cb), NULL); - histogram_tick_id = gtk_widget_add_tick_callback(GTK_WIDGET(histogram_image), (GtkTickCallback) update_histogram_cb, pointer_statusbar, NULL); - } - else - { - gtk_widget_remove_tick_callback(GTK_WIDGET(histogram_image), histogram_tick_id); - gtk_container_remove(GTK_CONTAINER(paned), histogram); - FREE(histogram_data); - // gtk_widget_destroy(histogram); - histogram=NULL; - } + cairo_surface_set_device_scale(histogram->surface, (double)256/(double)allocation->width, (double)256/(double)allocation->height); } diff --git a/src/image_display.cpp b/src/image_display.cpp deleted file mode 100644 index c523d33108b4293c9822802f2dbf620c5dcc150c..0000000000000000000000000000000000000000 --- a/src/image_display.cpp +++ /dev/null @@ -1,637 +0,0 @@ -#include "common.h" - -#include <fcntl.h> // O_RDONLY ... -#include <stdio.h> -#include <gtk/gtk.h> -#include <string.h> -#include <stdint.h> //uint32_t -#include <jpeglib.h> -#include <sys/mman.h> -#include <errno.h> //errno -#include "blc_image.h" -#include "blc_realtime.h" -#include "blc_program.h" //blc_status - -uint32_t false_colors[256]; -uint32_t gray_colors[256]; -uint32_t u_colors[256]; -uint32_t v_colors[256]; -uint32_t r_colors[256]; -uint32_t g_colors[256]; -uint32_t b_colors[256]; -uint32_t *color_map; - -int iterations=0; -int colors_initilized = 0; - -int mouse_image_x, mouse_image_y; - -cairo_surface_t *legend_surface, *image_surface; -gulong pointer_value_handler; -GtkWidget *paned; - -static uint32_t *RGBA_from_YUYV = NULL; - -static GtkWidget *vbox, *scrolled_window, *event_box, *toolbar, *general_statusbar; -static GtkToggleToolButton *zoom_button, *false_colors_button, *histogram_button; -GtkWidget *image; - -static uchar *image_buffer; - -static pthread_t init_table_thread; -double zoom_in_factor=2, zoom_out_factor=0.5; - -GtkWidget *legend=NULL; - -static int width=0, height=0; -blc_array *array=NULL; - -static int g_source_continue=G_SOURCE_CONTINUE; - -#define SWAP_RGBA_TO_CAIRO_ARGB32(x) ((((x) & 0x000000FF) << 16) | (((x) & 0x00FF0000) >> 16) | ((x) & 0xFF00FF00) ) - - -static GtkToggleToolButton *blgtk_add_toggle_tool_button(GtkWidget *toolbar, char const *label, char const *icon_name, GCallback callback, void *user_data) -{ - GtkToggleToolButton *toggle_tool_button; - - toggle_tool_button=GTK_TOGGLE_TOOL_BUTTON(gtk_toggle_tool_button_new()); - - gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toggle_tool_button), icon_name); - gtk_tool_button_set_label(GTK_TOOL_BUTTON(toggle_tool_button), label); - gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(toggle_tool_button)); - if (callback) g_signal_connect(G_OBJECT(toggle_tool_button), "clicked", callback, user_data); - return toggle_tool_button; -} - -static GtkWidget *blgtk_add_tool_button(GtkWidget *toolbar, gchar const *label, gchar const *icon_name, GCallback callback, gpointer user_data) -{ - GtkWidget *tool_button; - - tool_button=GTK_WIDGET(gtk_tool_button_new(gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR), label)); - gtk_container_add(GTK_CONTAINER(toolbar), tool_button); - if (callback) g_signal_connect(G_OBJECT(tool_button), "clicked", callback, user_data); - return tool_button; -} - -static void toggle_fullscreen(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data){ - - if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN ){ - gtk_widget_hide(toolbar); - gtk_widget_hide(general_statusbar); - } - else{ - gtk_widget_show(toolbar); - gtk_widget_show(general_statusbar); - } -} - -void* create_RGBA_from_YUYV(void *widget) -{ - int Y, Cb, Cr; - int i, j; - float G_tmp; - static uchar R[256], B; - (void)widget; - - if (RGBA_from_YUYV == NULL){ - RGBA_from_YUYV= MANY_ALLOCATIONS(256*256*256, uint32_t); - FOR_INV(Y, 256){ - FOR_INV(j,256) R[j]= CLIP_UCHAR(Y+1.13983*(j-128)); //It does not depend on Cb - FOR_INV(Cb, 256) - { - B = CLIP_UCHAR(Y+2.03211*(Cb-128)); // It does not depend on Cr - G_tmp = - 0.58060*(Cb-128); - - FOR_INV(Cr, 256) - { - i = Y + (Cb << 8) + (Cr << 16); - - // Version Wikipedia - RGBA_from_YUYV[i] = (R[Cr]<<16) + (((uchar)CLIP_UCHAR(Y-0.39465*(Cr-128) + G_tmp))<< 8)+ B + (255<<24); - } - } - } - } - return NULL; -} - -void init_false_colors() -{ - int i, val; - int min; - - FOR_INV(i, 256) - { - min=(int)(i*6/256)*256/6; - if ((i-min) > 256/6) min++; - val=(i-min)*6; - switch (i*6/256) - { - case 0:false_colors[i]=(val)+(255<<24); - break; - case 1:false_colors[i]=255+(val<<8)+(255<<24); - break; - case 2:false_colors[i]=(255-val)+(255<<8)+(255<<24); - break; - case 3:false_colors[i]=(255<<8)+(val<<16)+(255<<24); - break; - case 4:false_colors[i]=((255-val)<<8)+(255<<16)+(255<<24); - break; - case 5:false_colors[i]=(255<<16)+(val)+(val<<8)+(255<<24); - break; - } - } -} - -static void init_color_maps(){ - int i; - if (RGBA_from_YUYV==NULL) create_RGBA_from_YUYV(NULL); - - FOR_INV(i, 256){ - gray_colors[i]= i+(i<<8)+(i<<16)+(255<<24); - u_colors[i]= RGBA_from_YUYV[128+(i<<8)+(128<<16)]+(255<<24); - v_colors[i]=RGBA_from_YUYV[127+(127<<8)+(i<<16)]; - r_colors[i]= 128+(128<<8)+(i<<16)+(255<<24); - g_colors[i]= 128+(i<<8)+(128<<16)+(255<<24); - b_colors[i]= i+(128<<8)+(128<<16)+(255<<24); - } - init_false_colors(); -} - -/** Gtk cannot display a black and white image, therefore we convert it before updating it*/ -gboolean update_Y800_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar) -{ - float gain; - int i, x, y, j; - double sx, sy; - uint32_t *values; - char text[64]; - - gain=1/(max_val-min_val); - - if (blc_command_loop_start()){ - values = (uint32_t*)image_buffer; - if (array->type=='UIN8') - FOR(i, array->size) values[i]=color_map[array->uchars[i]]; - else if (array->type=='FL32') - FOR(i, array->total_length) values[i]=color_map[CLIP_UCHAR((array->floats[i]-min_val)*256*gain-0.5f)]; - else EXIT_ON_ARRAY_ERROR(array, "Type not managed"); - gtk_image_set_from_surface(image, image_surface); - - gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL); - cairo_surface_get_device_scale(image_surface, &sx, &sy); - i=x*sx; - j=y*sy; - if (i<width && j<height && i>=0 && j>=0){ - if (array->type=='UIN8') SPRINTF(text, "%4d,%4d: Y:%3d", i,j, array->uchars[i+j*width]); - else if (array->type=='FL32') SPRINTF(text, "%4d,%4d: Y:%.3f", i,j, array->floats[i+j*width]); - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - iterations++; - blc_command_loop_end(); - } else exit(EXIT_SUCCESS); - return G_SOURCE_CONTINUE; -} - -gboolean update_RGB3_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar) -{ - char text[NAME_MAX]; - double sx, sy; - int x, y, i=0, j=0; - int R, G, B; - - if (blc_command_loop_start()){ - gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL); - cairo_surface_get_device_scale(image_surface, &sx, &sy); - - while(i!=array->size){ - image_buffer[j++]=array->uchars[i+2]; - image_buffer[j++]=array->uchars[i+1]; - image_buffer[j++]=array->uchars[i]; - j++; - i+=3; - } - - i=x*sx; - j=y*sy; - - if (i<width && j<height && i>=0 && j>=0){ - if (mouse_array.dims!=nullptr) - { - mouse_array.uints32[0]=i; - mouse_array.uints32[1]=j; - gdk_device_get_state(pointer_device, gtk_widget_get_window(GTK_WIDGET(image)), NULL, (GdkModifierType*)(&mouse_array.uints32[2])); - } - - R=array->uchars[i*4+j*width*3]; - G=array->uchars[i*4+j*width*3+1]; - B=array->uchars[i*4+j*width*3+2]; - SPRINTF(text, "%4d,%4d : R:%3d,G:%3d,B:%3d", i, j, R, G, B); - - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - gtk_image_set_from_surface(image, image_surface); - iterations++; - blc_command_loop_end(); - } - else exit (EXIT_SUCCESS); - return G_SOURCE_CONTINUE; - -} - -gboolean update_RGBA_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar) -{ - double sx, sy; - int x, y; - int i, j=0; - char text[NAME_MAX]; - int R, G, B, A; - - if (blc_command_loop_start()==0) exit(0); - gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL); - cairo_surface_get_device_scale(image_surface, &sx, &sy); - - FOR_INV(i, array->size/4) ((uint32_t*)image_buffer)[i]=SWAP_RGBA_TO_CAIRO_ARGB32(array->uints32[i]); - - i=x*sx; - j=y*sy; - - if (i<width && j<height && i>=0 && j>=0){ - if (mouse_array.dims!=nullptr){ - mouse_array.uints32[0]=i; - mouse_array.uints32[1]=j; - gdk_device_get_state(pointer_device, gtk_widget_get_window(GTK_WIDGET(image)), NULL, (GdkModifierType*)(&mouse_array.uints32[2])); - } - - R=array->uchars[i*4+j*width*4]; - G=array->uchars[i*4+j*width*4+1]; - B=array->uchars[i*4+j*width*4+2]; - A=array->uchars[i*4+j*width*4+3]; - SPRINTF(text, "%4d,%4d: R:%d G:%d B:%d A:%d", i, j, R, G, B, A); - - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - gtk_image_set_from_surface(image, image_surface); - blc_command_loop_end(); - iterations++; - return G_SOURCE_CONTINUE; -} - - -// Soon we should the implementation of blc_image */ -void jpeg_error(j_common_ptr cinfo, int msg_level) -{ - (void) msg_level; - cinfo->err->num_warnings++; -} - -gboolean update_JPEG_image(GtkImage *image, GdkFrameClock *frame_clock, blc_array *array) -{ - JSAMPROW row_pt[1]; - GdkPixbuf *pixbuf; - int row_stride; - uchar *pixels; - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - (void) frame_clock; - - if (blc_command_loop_start()==0) exit(0); - - - pixbuf = gtk_image_get_pixbuf(image); - pixels = gdk_pixbuf_get_pixels(pixbuf); - - cinfo.err = jpeg_std_error(&jerr); - cinfo.err->emit_message = jpeg_error; - jpeg_create_decompress(&cinfo); - jpeg_mem_src(&cinfo, (uchar*) array->data, array->size); - jpeg_read_header(&cinfo, TRUE); - jpeg_start_decompress(&cinfo); - row_stride = cinfo.output_width * cinfo.output_components; - row_pt[0] = pixels; - - while (cinfo.output_scanline < cinfo.output_height) - { - jpeg_read_scanlines(&cinfo, row_pt, 1); - row_pt[0] += row_stride; - } - - if (cinfo.err->num_warnings != 0) - { - PRINT_WARNING("Drop image : %s", cinfo.err->jpeg_message_table[cinfo.err->last_jpeg_message]); - cinfo.err->num_warnings = 0; - } - gtk_image_set_from_pixbuf(image, pixbuf); - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - - blc_command_loop_end(); - - - return G_SOURCE_CONTINUE; -} - -// 4:2:2 - -gboolean update_YUYV_image(GtkImage *image, GdkFrameClock *frame_clock, void*) -{ - int i, j; - uchar *data = (uchar*) array->data; - uint32_t *pixels; - int Y, Cb = 128, Cr = 128; - - (void) frame_clock; - - if (blc_command_loop_start()==0) exit(0); - - pixels = (uint32_t*)image_buffer; - i = 0; - while (i != (int) array->size) - { - Y = data[i++]; - Cb = data[i++]; - j = Y + (Cb << 8) + (Cr << 16); - pixels[0]=RGBA_from_YUYV[j]; - - Y = data[i++]; - Cr = data[i++]; - j = Y + (Cb << 8) + (Cr << 16); - pixels[1]=RGBA_from_YUYV[j]; - pixels+=2; - } - iterations++; - gtk_image_set_from_surface(image, image_surface); - blc_command_loop_end(); - - return G_SOURCE_CONTINUE; -} - -// 4:2:2 - -gboolean update_yuv2_image(GtkImage *image, GdkFrameClock *, gpointer pointer_statusbar) -{ - double sx, sy; - int x, y; - int i, j; - - uchar *data = (uchar*) array->data; - char text[NAME_MAX]; - uint32_t *tmp_pixels; - int Y, U = 128, V = 128; - - if (blc_command_loop_start()==0) exit(0); - - tmp_pixels = (uint32_t*)image_buffer; - i = 0; - - while (i != (int) array->size) - { - U = data[i++]; - Y = data[i++]; - - j = Y + (U << 8) + (V << 16); - tmp_pixels[0]=RGBA_from_YUYV[j]; - - V = data[i++]; - Y = data[i++]; - - j = Y + (U << 8) + (V << 16); - tmp_pixels[1]=RGBA_from_YUYV[j]; - tmp_pixels+=2; - } - - gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(image)), pointer_device, &x, &y, NULL); - cairo_surface_get_device_scale(image_surface, &sx, &sy); - - i=x*sx; - j=y*sy; - - - if (i<width && j<height && i>=0 && j>=0) - { - if (mouse_array.dims!=nullptr) - { - mouse_array.uints32[0]=i; - mouse_array.uints32[1]=j; - gdk_device_get_state(pointer_device, gtk_widget_get_window(GTK_WIDGET(image)), NULL, (GdkModifierType*)(&mouse_array.uints32[2])); - } - - Y=array->uchars[i*2+j*width*2+1]; - U=array->uchars[i*4+j*width*2]; - V=array->uchars[i*4+j*width*2+2]; - SPRINTF(text, "%d,%d: Y:%d, U:%d, V:%d", i, j, Y, U, V); - - gtk_statusbar_push(GTK_STATUSBAR(pointer_statusbar), 0, text); - } - gtk_image_set_from_surface(image, image_surface); - iterations++; - - blc_command_loop_end(); - return G_SOURCE_CONTINUE; -} - - - -static void gtk_image_resize_cb(GtkWidget *image, GdkRectangle *allocation, gpointer user_data) -{ - (void) image; - (void) user_data; - - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(zoom_button))) cairo_surface_set_device_scale(image_surface, (double)width/(double)allocation->width, (double)height/(double)allocation->height); - if (legend) cairo_surface_set_device_scale(legend_surface, 256/(double)allocation->width, 1/(double)allocation->height); -} - - - -static void zoom_original_cb(GtkToolButton *, gpointer) -{ - int image_height, window_height; - - image_height=gtk_widget_get_allocated_height(image); - window_height = gtk_widget_get_allocated_height(window); - - cairo_surface_set_device_scale(image_surface, 1, 1); - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(zoom_button))) - gtk_window_resize(GTK_WINDOW(window), width, window_height-image_height+height ); -} - -static void zoom_cb(GtkToolButton *toolbutton, double *factor) -{ - double sx, sy; - (void)toolbutton; - - gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(zoom_button), FALSE); - cairo_surface_get_device_scale(image_surface, &sx, &sy); - cairo_surface_set_device_scale(image_surface, sx*(*factor), sy*(*factor)); -} - - -static void false_color_cb(GtkToolButton *toolbutton, struct update_cb *){ - GtkWidget *label, *legend_box, *legend_image; - char text[NAME_MAX+1]; - int i; - - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toolbutton))){ - color_map=false_colors; - legend_surface=cairo_image_surface_create_for_data ((uchar*)false_colors, CAIRO_FORMAT_RGB24, 256, 1, 256*4); - legend=gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); - legend_image = gtk_image_new_from_surface(legend_surface); - legend_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); - - FOR(i, 7) - { - SPRINTF(text, "%d", (i+1)*256/8); - label = gtk_label_new(text); - gtk_box_pack_start(GTK_BOX(legend_box), label, TRUE, TRUE, 0); - } - gtk_container_add(GTK_CONTAINER(legend), legend_image); - gtk_container_add(GTK_CONTAINER(legend), legend_box); - gtk_paned_add2(GTK_PANED(paned),legend ); - gtk_widget_show_all(legend); - } - else{ - color_map=gray_colors; - if (legend){ - gtk_container_remove(GTK_CONTAINER(paned), legend); - gtk_widget_destroy(legend); - legend=NULL; - } - } -} - -int frame_rate(gpointer data) -{ - char text[64]; - GtkWidget *framerate_statusbar=(GtkWidget*)data; - - SPRINTF(text, "%dHz", iterations); - gtk_statusbar_push(GTK_STATUSBAR(framerate_statusbar), 0, text); - iterations=0; - return G_SOURCE_CONTINUE; -} - -GtkWidget *create_image_display(blc_array *tmp_array) -{ - GtkWidget *pointer_statusbar, *framerate_statusbar; - char label_text[NAME_MAX + 1]; - uint32_t type_string, format_string; - - array=tmp_array; - toolbar = gtk_toolbar_new(); - general_statusbar=gtk_statusbar_new(); - framerate_statusbar=gtk_statusbar_new(); - pointer_statusbar=gtk_statusbar_new(); - - blc_command_loop_init(0); - switch (array->format) - { - case 'NDEF': - case 'Y800': - width=array->dims[0].length; - height=array->dims[1].length; - image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar); - image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4); - image=gtk_image_new_from_surface(image_surface); - gtk_widget_add_tick_callback(image, (GtkTickCallback) update_Y800_image, pointer_statusbar, NULL); - false_colors_button=blgtk_add_toggle_tool_button(toolbar, "false\ncolors", "applications-graphics", G_CALLBACK(false_color_cb), NULL); - break; - case 'BA81': - EXIT_ON_ARRAY_ERROR(array, "format, Not fully implemented"); - width=array->dims[1].length; - height=array->dims[2].length; - image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar); - image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4); - image=gtk_image_new_from_surface(image_surface); - gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_RGB3_image, &g_source_continue, NULL); - break; - case 'RGB3': - width=array->dims[1].length; - height=array->dims[2].length; - image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar); - image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4); - image=gtk_image_new_from_surface(image_surface); - gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_RGB3_image, pointer_statusbar, NULL); - break; - case 'RGBA': - width=array->dims[1].length; - height=array->dims[2].length; - image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar); - image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_ARGB32, width, height, width * 4); - image=gtk_image_new_from_surface(image_surface); - gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_RGBA_image, pointer_statusbar, NULL); - break; - /* case 'JPEG': - width=channel->lengths[1]; - height=channel->lengths[2]; - image_buffer = MANY_ALLOCATIONS(width*height*3, uchar); - pixbuf = gdk_pixbuf_new_from_data( image_buffer, GDK_COLORSPACE_RGB, 0, 8, width, height, width * 3, NULL, NULL); - image = gtk_image_new_from_pixbuf(pixbuf); - gtk_widget_add_tick_callback(image, (GtkTickCallback) update_JPEG_image, channel, NULL);*/ - case 'YUYV': - width=array->dims[1].length; - height=array->dims[2].length; - image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar); //In order to be sure that the rowstride will be 'gui.width * 3' - image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4); - image=gtk_image_new_from_surface(image_surface); - gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_YUYV_image, pointer_statusbar, NULL); - break; - case 'yuv2':case 'UYVY': - pthread_create(&init_table_thread, NULL, create_RGBA_from_YUYV, image); - width=array->dims[1].length; - height=array->dims[2].length; - image_buffer = MANY_ALLOCATIONS(width*height * 4, uchar); - image_surface=cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, width * 4); - image=gtk_image_new_from_surface(image_surface); - gtk_widget_add_tick_callback(GTK_WIDGET(image), (GtkTickCallback) update_yuv2_image, pointer_statusbar, NULL); - break; - default:return NULL; - } - - vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3); - - scrolled_window=gtk_scrolled_window_new(NULL, NULL); - - gtk_container_add(GTK_CONTAINER(general_statusbar), framerate_statusbar); - gtk_container_add(GTK_CONTAINER(general_statusbar), pointer_statusbar); - gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH); - - init_color_maps(); - color_map=gray_colors; - - g_timeout_add(1000, frame_rate, (gpointer)framerate_statusbar); - SPRINTF(label_text, "%.4s, %.4s, %dx%d", UINT32_TO_STRING(type_string, array->type), UINT32_TO_STRING(format_string, array->format), width, height); - gtk_statusbar_push(GTK_STATUSBAR(general_statusbar), 0, label_text); - gtk_widget_set_vexpand(scrolled_window, TRUE); - gtk_widget_set_halign(image, GTK_ALIGN_START); - gtk_widget_set_valign(image, GTK_ALIGN_START); - - blgtk_add_tool_button(toolbar, "zoom\n1:1", "zoom-original", G_CALLBACK(zoom_original_cb), NULL); - blgtk_add_tool_button(toolbar, "zoom\nx0.5", "zoom-out", G_CALLBACK(zoom_cb), &zoom_in_factor); - blgtk_add_tool_button(toolbar, "zoom\nx2", "zoom-in", G_CALLBACK(zoom_cb), &zoom_out_factor); - zoom_button=blgtk_add_toggle_tool_button(toolbar, "zoom\nfit", "zoom-fit-best", NULL, NULL); - // blgtk_add_toggle_tool_button(toolbar, "cursor\nvalues", "system-search", NULL, NULL);/ - histogram_button=blgtk_add_toggle_tool_button(toolbar, "histogram", "x-office-presentation", G_CALLBACK(histogram_cb), pointer_statusbar); - - gtk_toggle_tool_button_set_active(zoom_button, TRUE); - - g_signal_connect(G_OBJECT(scrolled_window), "size-allocate", G_CALLBACK(gtk_image_resize_cb), NULL); - - event_box=gtk_event_box_new(); - paned=gtk_paned_new(GTK_ORIENTATION_VERTICAL); - // gtk_paned_set_wide_handle (GTK_PANED(paned), TRUE); - gtk_container_add(GTK_CONTAINER(scrolled_window), event_box); - gtk_paned_add1(GTK_PANED(paned), scrolled_window); - gtk_container_add(GTK_CONTAINER(event_box), image); - gtk_container_add(GTK_CONTAINER(vbox), toolbar); - gtk_container_add(GTK_CONTAINER(vbox), paned); - gtk_container_add(GTK_CONTAINER(vbox), general_statusbar); - zoom_original_cb(NULL, NULL); - - g_signal_connect(window, "window-state-event", G_CALLBACK (toggle_fullscreen), NULL); - if (fullscreen_option) gtk_window_fullscreen(GTK_WINDOW(window)); - - // g_signal_connect(G_OBJECT(vbox), "realize", G_CALLBACK(image_realize_cb), NULL); - return vbox; -} -