]> git.sesse.net Git - nageru/blob - analyzer.cpp
Make it possible to hover over the grabbed image to get out RGB values.
[nageru] / analyzer.cpp
1 #include "analyzer.h"
2
3 #include <QDialogButtonBox>
4 #include <QMouseEvent>
5 #include <QSurface>
6
7 #include <movit/resource_pool.h>
8 #include <movit/util.h>
9
10 #include "context.h"
11 #include "flags.h"
12 #include "mixer.h"
13 #include "ui_analyzer.h"
14
15 using namespace std;
16
17 Analyzer::Analyzer()
18         : ui(new Ui::Analyzer),
19           grabbed_image(global_flags.width, global_flags.height, QImage::Format_ARGB32_Premultiplied)
20 {
21         ui->setupUi(this);
22
23         //connect(ui->button_box, &QDialogButtonBox::accepted, [this]{ this->close(); });
24
25         ui->input_box->addItem("Live", Mixer::OUTPUT_LIVE);
26         ui->input_box->addItem("Preview", Mixer::OUTPUT_PREVIEW);
27         unsigned num_channels = global_mixer->get_num_channels();
28         for (unsigned channel_idx = 0; channel_idx < num_channels; ++channel_idx) {
29                 Mixer::Output channel = static_cast<Mixer::Output>(Mixer::OUTPUT_INPUT0 + channel_idx); 
30                 string name = global_mixer->get_channel_name(channel);
31                 ui->input_box->addItem(QString::fromStdString(name), channel);
32         }
33
34         connect(ui->grab_btn, &QPushButton::clicked, bind(&Analyzer::grab_clicked, this));
35         connect(ui->input_box, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), bind(&Analyzer::signal_changed, this));
36         signal_changed();
37         ui->grabbed_frame_label->installEventFilter(this);
38
39         surface = create_surface(QSurfaceFormat::defaultFormat());
40         context = create_context(surface);
41
42         if (!make_current(context, surface)) {
43                 printf("oops\n");
44                 exit(1);
45         }
46
47         glGenBuffers(1, &pbo);
48         glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
49         glBufferData(GL_PIXEL_PACK_BUFFER_ARB, global_flags.width * global_flags.height * 4, NULL, GL_STREAM_READ);
50 }
51
52 Analyzer::~Analyzer()
53 {
54         if (!make_current(context, surface)) {
55                 printf("oops\n");
56                 exit(1);
57         }
58         glDeleteBuffers(1, &pbo);
59         check_error();
60         if (resource_pool != nullptr) {
61                 resource_pool->clean_context();
62         }
63         delete_context(context);
64         delete surface;  // TODO?
65 }
66
67 void Analyzer::grab_clicked()
68 {
69         Mixer::Output channel = static_cast<Mixer::Output>(ui->input_box->currentData().value<int>());
70
71         if (!make_current(context, surface)) {
72                 printf("oops\n");
73                 exit(1);
74         }
75
76         Mixer::DisplayFrame frame;
77         if (!global_mixer->get_display_frame(channel, &frame)) {
78                 printf("Not ready yet\n");
79                 return;
80         }
81
82         // Set up an FBO to render into.
83         if (resource_pool == nullptr) {
84                 resource_pool = frame.chain->get_resource_pool();
85         } else {
86                 assert(resource_pool == frame.chain->get_resource_pool());
87         }
88         GLuint fbo_tex = resource_pool->create_2d_texture(GL_RGBA8, global_flags.width, global_flags.height);
89         check_error();
90         GLuint fbo = resource_pool->create_fbo(fbo_tex);
91         check_error();
92
93         glWaitSync(frame.ready_fence.get(), /*flags=*/0, GL_TIMEOUT_IGNORED);
94         check_error();
95         frame.setup_chain();
96         check_error();
97         glDisable(GL_FRAMEBUFFER_SRGB);
98         check_error();
99         frame.chain->render_to_fbo(fbo, global_flags.width, global_flags.height);
100         check_error();
101
102         // Read back to memory.
103         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
104         check_error();
105         glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
106         check_error();
107         glReadPixels(0, 0, global_flags.width, global_flags.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, BUFFER_OFFSET(0));
108         check_error();
109
110         unsigned char *buf = (unsigned char *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
111         check_error();
112
113         size_t pitch = global_flags.width * 4;
114         for (int y = 0; y < global_flags.height; ++y) {
115                 memcpy(grabbed_image.scanLine(global_flags.height - y - 1), buf + y * pitch, pitch);
116         }
117
118         QPixmap pixmap;
119         pixmap.convertFromImage(grabbed_image);
120         ui->grabbed_frame_label->setPixmap(pixmap);
121
122         int r_hist[256] = {0}, g_hist[256] = {0}, b_hist[256] = {0};
123         const unsigned char *ptr = buf;
124         for (int i = 0; i < global_flags.height * global_flags.width; ++i) {
125                 uint8_t b = *ptr++;
126                 uint8_t g = *ptr++;
127                 uint8_t r = *ptr++;
128                 ++ptr;
129
130                 ++r_hist[r];
131                 ++g_hist[g];
132                 ++b_hist[b];
133         }
134
135         glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
136         check_error();
137         glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
138         check_error();
139         glBindFramebuffer(GL_FRAMEBUFFER, 0);
140         check_error();
141
142         printf("R hist:");
143         for (unsigned i = 0; i < 256; ++i) { printf(" %d", r_hist[i]); }
144         printf("\n");
145         printf("G hist:");
146         for (unsigned i = 0; i < 256; ++i) { printf(" %d", g_hist[i]); }
147         printf("\n");
148         printf("B hist:");
149         for (unsigned i = 0; i < 256; ++i) { printf(" %d", b_hist[i]); }
150         printf("\n");
151
152         resource_pool->release_2d_texture(fbo_tex);
153         check_error();
154         resource_pool->release_fbo(fbo);
155         check_error();
156 }
157
158 void Analyzer::signal_changed()
159 {
160         Mixer::Output channel = static_cast<Mixer::Output>(ui->input_box->currentData().value<int>());
161         ui->display->set_output(channel);
162 }
163
164 bool Analyzer::eventFilter(QObject *watched, QEvent *event)
165 {
166         if (event->type() == QEvent::MouseMove &&
167             watched->isWidgetType()) {
168                 const QMouseEvent *mouse_event = (QMouseEvent *)event;
169                 const QPixmap *pixmap = ui->grabbed_frame_label->pixmap();
170                 if (pixmap != nullptr) {
171                         int x = lrint(mouse_event->x() * double(pixmap->width()) / ui->grabbed_frame_label->width());
172                         int y = lrint(mouse_event->y() * double(pixmap->height()) / ui->grabbed_frame_label->height());
173                         x = std::min(x, pixmap->width() - 1);
174                         y = std::min(y, pixmap->height() - 1);
175                         QRgb pixel = grabbed_image.pixel(x, y);
176                         ui->red_label->setText(QString::fromStdString(to_string(qRed(pixel))));
177                         ui->green_label->setText(QString::fromStdString(to_string(qGreen(pixel))));
178                         ui->blue_label->setText(QString::fromStdString(to_string(qBlue(pixel))));
179
180                         char buf[256];
181                         snprintf(buf, sizeof(buf), "#%02x%02x%02x", qRed(pixel), qGreen(pixel), qBlue(pixel));
182                         ui->hex_label->setText(buf);
183                 }
184         }
185         return false;
186 }