--- a/CLIParser.h Wed Dec 27 01:50:38 2023 +0100
+++ b/CLIParser.h Wed Dec 27 10:34:12 2023 +0100
@@ -25,7 +25,7 @@
class CLIParser {
private:
- static const std::string OPTION_TEXTURE;
+ static const std::string OPTION_DOCUMENT;
static const std::string OPTION_SHADER;
static const std::string OPTION_BACKGROUND_COLOR;
static const std::string OPTION_ROOT_WINDOW;
@@ -69,10 +69,10 @@
for (int i = 0; i < arguments.size();) {
const std::string& option = readNext(arguments, i);
- if (option == OPTION_TEXTURE) {
- Configuration::Texture tex;
- tex.fileName = readNext(arguments, i);
- c.textures.push_back(tex);
+ if (option == OPTION_DOCUMENT) {
+ Configuration::Document document;
+ document.fileName = readNext(arguments, i);
+ c.documents.push_back(document);
} else if (option == OPTION_SHADER) {
const auto type = readNext(arguments, i);
const auto file = readNext(arguments, i);
@@ -88,7 +88,7 @@
}
};
-const std::string CLIParser::OPTION_TEXTURE = "--texture";
+const std::string CLIParser::OPTION_DOCUMENT = "--document";
const std::string CLIParser::OPTION_SHADER = "--shader";
const std::string CLIParser::OPTION_BACKGROUND_COLOR = "--background-color";
const std::string CLIParser::OPTION_ROOT_WINDOW = "--root-window";
--- a/Configuration.h Wed Dec 27 01:50:38 2023 +0100
+++ b/Configuration.h Wed Dec 27 10:34:12 2023 +0100
@@ -29,7 +29,7 @@
std::string fileName;
};
- class Texture : public File {
+ class Document : public File {
};
class Shader : public File {
@@ -43,8 +43,8 @@
};
- // TODO: support loading whole directory and monitoring using inotify
- std::vector<Texture> textures;
+ double dpi = 600.;
+ std::vector<Document> documents;
std::vector<Shader> shaders;
unsigned long backgroundColor = (0x33 << 16 | 0x33 << 8 | 0x33);
--- a/ImageLoader.cpp Wed Dec 27 01:50:38 2023 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/**
- * OHP3D
- * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <iostream>
-
-#include <Magick++.h>
-
-#include "ImageLoader.h"
-#include "AllocatedBuffer.h"
-
-ImageLoader::ImageBuffer* ImageLoader::loadImage(const Buffer& input) {
- Magick::Blob inputBlob(input.getData(), input.getSize());
- Magick::Image image(inputBlob);
- // Magick::Image image("magick:LOGO");
- // Magick::Image image("magick:WIZARD");
- // Magick::Image image("https://.../remote-image.png");
- // Magick::Image image("x:root"); // whole desktop
- // Magick::Image image("x:0x6a0001c"); // single window, use e.g. xwininfo
-
- // Magick::PixelPacket* parsedPixels = parsedImage.getPixels(
- // 0, 0, parsedImage.size().width(), parsedImage.size().height());
-
- Magick::Blob outputBlob;
- image.magick("RGBA");
- image.depth(8); // TODO: pass more bits to OpenGL?
- image.write(&outputBlob);
-
- ImageBuffer* outputBuffer = new ImageBuffer(
- (void*) outputBlob.data(),
- outputBlob.length(),
- image.size().width(),
- image.size().height());
-
- return outputBuffer;
-}
\ No newline at end of file
--- a/ImageLoader.h Wed Dec 27 01:50:38 2023 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/**
- * OHP3D
- * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#pragma once
-
-#include <string.h>
-
-#include "Buffer.h"
-#include "AllocatedBuffer.h"
-
-class ImageLoader {
-public:
-
- class ImageBuffer : public AllocatedBuffer {
- public:
- int width;
- int height;
-
- ImageBuffer(void* buffer, const size_t size,
- const int width, const int height) :
- AllocatedBuffer(width * height * 4),
- width(width), height(height) {
- if (size == width * height * 4) memcpy(data, buffer, size);
- else throw std::invalid_argument("invalid image size");
- }
-
- };
-
- ImageBuffer* loadImage(const Buffer& inputBuffer);
-};
--- a/Makefile Wed Dec 27 01:50:38 2023 +0100
+++ b/Makefile Wed Dec 27 10:34:12 2023 +0100
@@ -26,7 +26,6 @@
SRC= \
OHP3D.cpp \
ohp3d.cpp \
- ImageLoader.cpp \
Texture.cpp \
Shader.cpp \
Program.cpp \
@@ -36,4 +35,4 @@
build/ohp3d: $(SRC) *.h
mkdir -p build
$(CXX) -std=c++20 -g -o $(@) $(SRC) $$(pkg-config --cflags --libs \
- epoxy x11 glu glm Magick++)
+ epoxy x11 glu glm poppler-cpp)
--- a/OHP3D.cpp Wed Dec 27 01:50:38 2023 +0100
+++ b/OHP3D.cpp Wed Dec 27 10:34:12 2023 +0100
@@ -23,13 +23,21 @@
#include <functional>
#include <sstream>
#include <vector>
+#include <chrono>
+#include <unistd.h>
+
+#include <poppler/cpp/poppler-document.h>
+#include <poppler/cpp/poppler-page.h>
+#include <poppler/cpp/poppler-toc.h>
+#include <sys/stat.h>
+#include <poppler/cpp/poppler-page-renderer.h>
#include "x11.h"
#include "opengl.h"
#include "EPoll.h"
#include "Logger.h"
#include "MappedFile.h"
-#include "ImageLoader.h"
+#include "PageBuffer.h"
#include "Texture.h"
#include "Shader.h"
#include "Program.h"
@@ -166,7 +174,6 @@
FileMonitor fileMonitor;
std::vector<WatchedFile> watchedFiles;
- ImageLoader imageLoader;
std::vector<std::shared_ptr<Shader>> shaders;
std::shared_ptr<Program> shaderProgram;
std::vector<std::shared_ptr<Texture>> textures;
@@ -184,9 +191,7 @@
void log(LogLevel level, std::string message);
int setNonBlocking(int fd);
void loadVertices();
- void parametrizeTexture(std::shared_ptr<Texture> tex);
- bool reloadTexture(const std::string& fileName);
- void loadTextures();
+ void loadDocuments();
void loadShaders();
void updateVariableLocations();
bool reloadShader(const std::string& fileName);
@@ -271,9 +276,8 @@
glXSwapBuffers(dpy, win);
- // Load GLSL shaders:
loadShaders();
- loadTextures();
+ loadDocuments();
loadVertices();
auto toggleFullscreen = [&]() {
@@ -409,7 +413,6 @@
<< " mask=" << fe.mask
<< std::endl;
try {
- redraw |= reloadTexture(fe.fileName);
redraw |= reloadShader(fe.fileName);
setTitle();
} catch (const std::exception& e) {
@@ -548,66 +551,57 @@
}
}
-void OHP3D::Impl::parametrizeTexture(std::shared_ptr<Texture> tex) {
- XAttrs xa(tex->getFileName());
- std::string magf = xa["ohp3d.texture.mag-filter"];
- std::string minf = xa["ohp3d.texture.min-filter"];
- std::string scale = xa["ohp3d.texture.scale"];
+void OHP3D::Impl::loadDocuments() {
+ for (const Configuration::File& document : cfg.documents) {
+ std::cerr << "loading document: " << document.fileName.c_str() << "\n";
+
+ MappedFile buffer(document.fileName);
- auto GLT2D = GL_TEXTURE_2D;
- auto MAG = GL_TEXTURE_MAG_FILTER;
- auto MIN = GL_TEXTURE_MIN_FILTER;
+ namespace pp = poppler;
+ using Document = pp::document;
+
+ std::shared_ptr<Document> doc = std::shared_ptr<Document>(
+ Document::load_from_raw_data(buffer.getData(), buffer.getSize()));
- if (magf == "linear") glTexParameteri(GLT2D, MAG, GL_LINEAR);
- else if (magf == "nearest") glTexParameteri(GLT2D, MAG, GL_NEAREST);
-
- if (minf == "linear") glTexParameteri(GLT2D, MIN, GL_LINEAR);
- else if (minf == "nearest") glTexParameteri(GLT2D, MIN, GL_NEAREST);
+ log(LogLevel::INFO, std::string("PDF parsed:")
+ + " pages=" + std::to_string(doc->pages()));
- if (scale.size()) {
- float sc;
- if (std::from_chars(scale.data(), scale.data() + scale.size(), sc).ec
- == std::errc{}) tex->setScale(sc);
- else std::cerr << "Invalid texture scale value - expecting float\n";
- // tex->setScale(std::stof(scale)); // locale-dependent (. vs ,)
- }
-}
+ log(LogLevel::INFO, "loadig pages to textures...");
+ auto timingStart = std::chrono::steady_clock::now();
+ pp::page_renderer renderer;
+ renderer.set_image_format(pp::image::format_rgb24);
+ double dpi = cfg.dpi;
+ for (int i = 0, limit = doc->pages(); i < limit; i++) {
+ std::shared_ptr<pp::page> page(doc->create_page(i));
+ pp::image pageImage = renderer.render_page(page.get(), dpi, dpi);
-void OHP3D::Impl::loadTextures() {
- // Load default texture if there is no configured:
- if (cfg.textures.empty())
- cfg.textures.push_back({getDefaultFile("textures/default.png")});
+ PageBuffer img(
+ pageImage.data(),
+ pageImage.bytes_per_row() * pageImage.height(),
+ pageImage.width(), pageImage.height());
+
+ textures.push_back(std::make_shared<Texture>(
+ pageImage.width(), pageImage.height(),
+ img, document.fileName));
+ log(LogLevel::INFO, " page " + std::to_string(i + 1)
+ + "/" + std::to_string(limit));
+ }
- for (const Configuration::Texture& tex : cfg.textures) {
- std::shared_ptr<ImageLoader::ImageBuffer>
- img(imageLoader.loadImage(MappedFile(tex.fileName)));
- textures.push_back(std::make_shared<Texture>(
- img->width, img->height, *img, tex.fileName));
- parametrizeTexture(textures.back());
- // static const uint32_t watchMask = IN_CLOSE_WRITE | IN_ATTRIB;
- // watchedFiles.push_back(fileMonitor.watch(tex.fileName, watchMask));
- watchedFiles.push_back(fileMonitor.watch(tex.fileName));
- // TODO: review texture loading and binding
- // works even without this - default texture
- // glUniform1i(..., ...);
- // checkError(&std::cerr);
+ auto timingEnd = std::chrono::steady_clock::now();
+ auto timingTotal = std::chrono::duration_cast<std::chrono::microseconds>
+ (timingEnd - timingStart).count();
+
+ std::stringstream timingMsg;
+ timingMsg.imbue(std::locale(""));
+ timingMsg << "PDF to texture load time:"
+ << " page average = " << (timingTotal / doc->pages()) << " µs,"
+ << " total = " << timingTotal << " µs ("
+ << doc->pages() << " pages)";
+ log(LogLevel::INFO, timingMsg.str());
+ if (timingTotal < 400000) logOutput << ">>> Doherty threshold met! <<<\n";
}
}
-bool OHP3D::Impl::reloadTexture(const std::string& fileName) {
- for (std::shared_ptr<Texture> tex : textures) {
- if (tex->getFileName() == fileName) {
- std::shared_ptr<ImageLoader::ImageBuffer>
- img(imageLoader.loadImage(MappedFile(fileName)));
- tex->update(img->width, img->height, *img);
- parametrizeTexture(tex);
- loadVertices();
- return true;
- }
- }
- return false;
-}
-
void OHP3D::Impl::loadShaders() {
// Vertex Array Object (VAO)
GLuint vao;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/PageBuffer.h Wed Dec 27 10:34:12 2023 +0100
@@ -0,0 +1,37 @@
+/**
+ * OHP3D
+ * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <string.h>
+
+#include "AllocatedBuffer.h"
+
+class PageBuffer : public AllocatedBuffer {
+public:
+ int width;
+ int height;
+
+ PageBuffer(void* buffer, const size_t size,
+ const int width, const int height) :
+ AllocatedBuffer(width * height * 3),
+ width(width), height(height) {
+ if (size == width * height * 3) memcpy(data, buffer, size);
+ else throw std::invalid_argument("invalid image size");
+ }
+
+};
--- a/Texture.cpp Wed Dec 27 01:50:38 2023 +0100
+++ b/Texture.cpp Wed Dec 27 10:34:12 2023 +0100
@@ -77,19 +77,19 @@
impl->width = width;
impl->height = height;
- if (img.getSize() != impl->width * impl->height * 4)
+ if (img.getSize() != impl->width * impl->height * 3)
throw std::invalid_argument("invalid image size");
glBindTexture(GL_TEXTURE_2D, impl->id);
auto GLT2D = GL_TEXTURE_2D;
- glTexImage2D(GLT2D, 0, GL_RGBA,
+ glTexImage2D(GLT2D, 0, GL_RGB,
impl->width, impl->height,
- 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ 0, GL_RGB, GL_UNSIGNED_BYTE,
img.getData());
glTexParameteri(GLT2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GLT2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GLT2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // blurry upscale
- glTexParameteri(GLT2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // sharp upscale
+ // glTexParameteri(GLT2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); sharp upscale
glTexParameteri(GLT2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GLT2D);
checkError(&std::cerr);
--- a/bash-completion.sh Wed Dec 27 01:50:38 2023 +0100
+++ b/bash-completion.sh Wed Dec 27 10:34:12 2023 +0100
@@ -33,7 +33,7 @@
"fragment"
)
- if [[ "$w1" == "--texture" ]]; then
+ if [[ "$w1" == "--document" ]]; then
COMPREPLY=($(compgen -f -- "$w0"; compgen -d -S / -- "$w0"))
elif [[ "$w1" == "--shader" ]]; then
COMPREPLY=($(compgen -W "${SHADER_TYPE[*]}" -- "$w0"))
@@ -43,7 +43,7 @@
COMPREPLY=($(compgen -W "${COLOR[*]}" -- "$w0"))
else
local OPTIONS=(
- "--texture"
+ "--document"
"--shader"
"--background-color"
"--root-window"
--- a/nbproject/configurations.xml Wed Dec 27 01:50:38 2023 +0100
+++ b/nbproject/configurations.xml Wed Dec 27 10:34:12 2023 +0100
@@ -3,10 +3,9 @@
<logicalFolder name="root" displayName="root" projectFiles="true" kind="ROOT">
<df root="." name="0">
<in>FileMonitor.cpp</in>
- <in>ImageLoader.cpp</in>
+ <in>OHP3D.cpp</in>
<in>Program.cpp</in>
<in>Shader.cpp</in>
- <in>OHP3D.cpp</in>
<in>Texture.cpp</in>
<in>XAttrs.cpp</in>
<in>ohp3d.cpp</in>
@@ -46,6 +45,8 @@
<incDir>
<pElem>/usr/include/x86_64-linux-gnu/ImageMagick-6</pElem>
<pElem>/usr/include/ImageMagick-6</pElem>
+ <pElem>/usr/include/poppler/cpp</pElem>
+ <pElem>/usr/include/poppler</pElem>
</incDir>
<preprocessorList>
<Elem>MAGICKCORE_HDRI_ENABLE=0</Elem>
@@ -62,7 +63,7 @@
<ccTool flags="0">
</ccTool>
</item>
- <item path="ImageLoader.cpp" ex="false" tool="1" flavor2="0">
+ <item path="OHP3D.cpp" ex="false" tool="1" flavor2="0">
<ccTool flags="0">
</ccTool>
</item>
@@ -74,10 +75,6 @@
<ccTool flags="0">
</ccTool>
</item>
- <item path="OHP3D.cpp" ex="false" tool="1" flavor2="0">
- <ccTool flags="0">
- </ccTool>
- </item>
<item path="Texture.cpp" ex="false" tool="1" flavor2="0">
<ccTool flags="0">
</ccTool>