diff --git a/pdf_viewer/document.cpp b/pdf_viewer/document.cpp index 4791bd521..5a5b09f02 100644 --- a/pdf_viewer/document.cpp +++ b/pdf_viewer/document.cpp @@ -1817,3 +1817,52 @@ bool Document::needs_authentication() { return false; } } + +std::vector Document::get_highlighted_character_masks(int page) { + fz_stext_page* stext_page = get_stext_with_page_number(page); + std::vector flat_chars; + get_flat_chars_from_stext_page(stext_page, flat_chars); + + std::vector words; + std::vector> word_rects; + get_word_rect_list_from_flat_chars(flat_chars, words, word_rects); + + std::vector res; + + for (int i = 0; i < words.size(); i++) { + + std::vector highlighted_characters; + + if (words[i].size() == 1) { + highlighted_characters.push_back(word_rects[i][0]); + } + else if (words[i].size() == 2) { + highlighted_characters.push_back(word_rects[i][0]); + } + else if (words[i].size() == 3) { + highlighted_characters.push_back(word_rects[i][0]); + highlighted_characters.push_back(word_rects[i][1]); + } + else { + int num_highlighted = static_cast(words[i].size() * 0.4f); + for (int j = 0; j < num_highlighted; j++) { + highlighted_characters.push_back(word_rects[i][j]); + } + + } + res.push_back(create_word_rect(highlighted_characters)); + } + + //std::vector res; + + //std::vector pending_word; + //pending_word.push_back(flat_chars[0]); + + //for (int i = 1; i < flat_chars.size(); i++) { + // if (flat_chars[i - 1]->c == ' ') { + // res.push_back(fz_rect_from_quad(flat_chars[i]->quad)); + // } + //} + return res; +} + diff --git a/pdf_viewer/document.h b/pdf_viewer/document.h index 4fe6fe752..1259ea833 100644 --- a/pdf_viewer/document.h +++ b/pdf_viewer/document.h @@ -202,6 +202,7 @@ class Document { bool needs_password(); bool needs_authentication(); bool apply_password(const char* password); + std::vector get_highlighted_character_masks(int page); friend class DocumentManager; }; diff --git a/pdf_viewer/input.cpp b/pdf_viewer/input.cpp index 0eca9d1b8..fffc738d5 100644 --- a/pdf_viewer/input.cpp +++ b/pdf_viewer/input.cpp @@ -122,6 +122,7 @@ CommandManager::CommandManager() { commands.push_back({ "goto_prev_highlight_of_type", false, false, false, false}); commands.push_back({ "add_highlight_with_current_type", false, false, false, false}); commands.push_back({ "enter_password", true, false , false, false}); + commands.push_back({ "toggle_fastread", false, false , false, false}); } const Command* CommandManager::get_command_with_name(std::string name) { diff --git a/pdf_viewer/main_widget.cpp b/pdf_viewer/main_widget.cpp index 92b1b9508..471f9d781 100644 --- a/pdf_viewer/main_widget.cpp +++ b/pdf_viewer/main_widget.cpp @@ -2411,6 +2411,9 @@ void MainWidget::handle_command(const Command* command, int num_repeats) { } else if (command->name == "debug") { } + else if (command->name == "toggle_fastread") { + opengl_widget->toggle_fastread_mode(); + } else if (command->name == "smart_jump_under_cursor") { QPoint mouse_pos = mapFromGlobal(QCursor::pos()); smart_jump_under_pos(mouse_pos.x(), mouse_pos.y()); diff --git a/pdf_viewer/pdf_view_opengl_widget.cpp b/pdf_viewer/pdf_view_opengl_widget.cpp index e9c7a2e59..6486a3250 100644 --- a/pdf_viewer/pdf_view_opengl_widget.cpp +++ b/pdf_viewer/pdf_view_opengl_widget.cpp @@ -197,6 +197,7 @@ void PdfViewOpenGLWidget::initializeGL() { shared_gl_objects.vertical_line_dark_program = LoadShaders(shader_path.slash(L"simple.vertex"), shader_path .slash(L"vertical_bar_dark.fragment")); shared_gl_objects.custom_color_program = LoadShaders(shader_path.slash(L"simple.vertex"), shader_path.slash(L"custom_colors.fragment")); shared_gl_objects.separator_program = LoadShaders(shader_path.slash(L"simple.vertex"), shader_path.slash(L"separator.fragment")); + shared_gl_objects.stencil_program = LoadShaders(shader_path.slash(L"stencil.vertex"), shader_path.slash(L"stencil.fragment")); shared_gl_objects.dark_mode_contrast_uniform_location = glGetUniformLocation(shared_gl_objects.rendered_dark_program, "contrast"); @@ -688,6 +689,17 @@ void PdfViewOpenGLWidget::render(QPainter* painter) { } } + if (fastread_mode) { + + enable_stencil(); + write_to_stencil(); + auto rects = document_view->get_document()->get_highlighted_character_masks(document_view->get_current_page_number()); + draw_stencil_rects(document_view->get_current_page_number(), rects); + use_stencil_to_write(); + render_transparent_white(); + disable_stencil(); + } + #ifndef NDEBUG if (last_selected_block) { glUseProgram(shared_gl_objects.highlight_program); @@ -781,23 +793,6 @@ void PdfViewOpenGLWidget::render(QPainter* painter) { render_overview(overview_page.value()); } - enable_stencil(); - write_to_stencil(); - //glEnableVertexAttribArray(0); - //glEnableVertexAttribArray(1); - //glBindBuffer(GL_ARRAY_BUFFER, shared_gl_objects.uv_buffer_object); - //glBindBuffer(GL_ARRAY_BUFFER, shared_gl_objects.vertex_buffer_object); - //render_overview(OverviewState{ 0, 0, 0 }); - for (auto link : all_visible_links) { - render_highlight_document(shared_gl_objects.highlight_program, link.first, link.second->rect); - } - - use_stencil_to_write(); - for (int page : visible_pages) { - render_page(page); - } - render_transparent_white(); - disable_stencil(); painter->endNativePainting(); @@ -1375,3 +1370,35 @@ void PdfViewOpenGLWidget::render_transparent_white() { glDisableVertexAttribArray(1); glDisable(GL_BLEND); } + +void PdfViewOpenGLWidget::draw_stencil_rects(int page, const std::vector& rects) { + + std::vector window_rects; + for (auto rect : rects) { + fz_rect window_rect = document_view->document_to_window_rect(page, rect); + float triangle1[6] = { + window_rect.x0, window_rect.y0, + window_rect.x0, window_rect.y1, + window_rect.x1, window_rect.y0 + }; + float triangle2[6] = { + window_rect.x1, window_rect.y0, + window_rect.x0, window_rect.y1, + window_rect.x1, window_rect.y1 + }; + for (int i = 0; i < 6; i++) window_rects.push_back(triangle1[i]); + for (int i = 0; i < 6; i++) window_rects.push_back(triangle2[i]); + } + + glUseProgram(shared_gl_objects.stencil_program); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, shared_gl_objects.vertex_buffer_object); + glBufferData(GL_ARRAY_BUFFER, window_rects.size() * sizeof(float), window_rects.data(), GL_DYNAMIC_DRAW); + glDrawArrays(GL_TRIANGLES, 0, rects.size() * 6); + glDisableVertexAttribArray(0); + +} + +void PdfViewOpenGLWidget::toggle_fastread_mode() { + fastread_mode = !fastread_mode; +} diff --git a/pdf_viewer/pdf_view_opengl_widget.h b/pdf_viewer/pdf_view_opengl_widget.h index 5636f55cb..f225a6a35 100644 --- a/pdf_viewer/pdf_view_opengl_widget.h +++ b/pdf_viewer/pdf_view_opengl_widget.h @@ -53,6 +53,7 @@ struct OpenGLSharedResources { GLuint vertical_line_program; GLuint vertical_line_dark_program; GLuint separator_program; + GLuint stencil_program; GLint dark_mode_contrast_uniform_location; GLint highlight_color_uniform_location; @@ -123,6 +124,7 @@ class PdfViewOpenGLWidget : public QOpenGLWidget, protected QOpenGLExtraFunction int rotation_index = 0; bool is_dragging = false; + bool fastread_mode = false; int last_mouse_down_window_x = 0; int last_mouse_down_window_y = 0; @@ -157,6 +159,7 @@ class PdfViewOpenGLWidget : public QOpenGLWidget, protected QOpenGLExtraFunction void enable_stencil(); void write_to_stencil(); + void draw_stencil_rects(int page, const std::vector& rects); void use_stencil_to_write(); void disable_stencil(); @@ -233,6 +236,7 @@ class PdfViewOpenGLWidget : public QOpenGLWidget, protected QOpenGLExtraFunction void rotate_counterclockwise(); bool is_rotated(); + void toggle_fastread_mode(); }; diff --git a/pdf_viewer/shaders/stencil.fragment b/pdf_viewer/shaders/stencil.fragment new file mode 100644 index 000000000..aeac99fae --- /dev/null +++ b/pdf_viewer/shaders/stencil.fragment @@ -0,0 +1,9 @@ + +#version 330 core + +out vec4 color; +in vec2 screen_pos; + +void main(){ + color = vec4(0, 0, 0, 0); +} diff --git a/pdf_viewer/shaders/stencil.vertex b/pdf_viewer/shaders/stencil.vertex new file mode 100644 index 000000000..2e7e62dad --- /dev/null +++ b/pdf_viewer/shaders/stencil.vertex @@ -0,0 +1,10 @@ + +#version 330 core + +out vec2 screen_pos; +layout (location=0) in vec2 vertex_pos; + +void main(){ + screen_pos = vertex_pos; + gl_Position = vec4(vertex_pos, 0.0, 1.0); +} diff --git a/pdf_viewer/utils.cpp b/pdf_viewer/utils.cpp index 389bbf6f0..14c9d8984 100644 --- a/pdf_viewer/utils.cpp +++ b/pdf_viewer/utils.cpp @@ -387,6 +387,23 @@ bool is_start_of_new_word(fz_stext_char* prev_char, fz_stext_char* current_char) return is_start_of_new_line(prev_char, current_char); } +fz_rect create_word_rect(const std::vector& chars) { + fz_rect res; + res.x0 = res.x1 = res.y0 = res.y1 = 0; + if (chars.size() == 0) return res; + res = chars[0]; + + float min_x; + for (int i = 1; i < chars.size(); i++) { + if (res.x0 > chars[i].x0) res.x0 = chars[i].x0; + if (res.x1 < chars[i].x1) res.x1 = chars[i].x1; + if (res.y0 > chars[i].y0) res.y0 = chars[i].y0; + if (res.y1 < chars[i].y1) res.y1 = chars[i].y1; + } + + return res; +} + fz_rect create_word_rect(const std::vector& chars) { fz_rect res; res.x0 = res.x1 = res.y0 = res.y1 = 0; @@ -409,6 +426,7 @@ void get_flat_words_from_flat_chars(const std::vector& flat_char if (flat_chars.size() == 0) return; + std::vector res; std::vector pending_word; pending_word.push_back(flat_chars[0]); @@ -427,6 +445,45 @@ void get_flat_words_from_flat_chars(const std::vector& flat_char } } +void get_word_rect_list_from_flat_chars(const std::vector& flat_chars, + std::vector& words, + std::vector>& flat_word_rects) { + + if (flat_chars.size() == 0) return; + + std::vector pending_word; + pending_word.push_back(flat_chars[0]); + + auto get_rects = [&]() { + std::vector res; + for (auto chr : pending_word) { + res.push_back(fz_rect_from_quad(chr->quad)); + } + return res; + }; + + auto get_word = [&]() { + std::wstring res; + for (auto chr : pending_word) { + res.push_back(chr->c); + } + return res; + }; + + for (int i = 1; i < flat_chars.size(); i++) { + if (is_start_of_new_word(flat_chars[i - 1], flat_chars[i])) { + flat_word_rects.push_back(get_rects()); + words.push_back(get_word()); + + pending_word.clear(); + pending_word.push_back(flat_chars[i]); + } + else { + pending_word.push_back(flat_chars[i]); + } + } +} + int get_num_tag_digits(int n) { int res = 1; while (n > 26) { diff --git a/pdf_viewer/utils.h b/pdf_viewer/utils.h index 0cbf53402..5f3e122ad 100644 --- a/pdf_viewer/utils.h +++ b/pdf_viewer/utils.h @@ -145,7 +145,12 @@ fz_quad quad_from_rect(fz_rect r); std::vector quads_from_rects(const std::vector& rects); std::wifstream open_wifstream(const std::wstring& file_name); void get_flat_words_from_flat_chars(const std::vector& flat_chars, std::vector& flat_word_rects); +void get_word_rect_list_from_flat_chars(const std::vector& flat_chars, + std::vector& words, + std::vector>& flat_word_rects); + std::vector get_tags(int n); int get_index_from_tag(const std::string& tag); std::wstring truncate_string(const std::wstring& inp, int size); std::wstring get_page_formatted_string(int page); +fz_rect create_word_rect(const std::vector& chars);