Animated gifs are working now
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(singlechat.wt LANGUAGES CXX)
|
||||
add_compile_options(-Wno-deprecated-declarations)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_COMPILER "/usr/bin/g++-12")
|
||||
set(CMAKE_PREFIX_PATH "/usr")
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
@@ -14,14 +16,16 @@ add_executable(${PROJECT_NAME}
|
||||
docroot/ads.txt
|
||||
docroot/links.csv
|
||||
)
|
||||
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
wt
|
||||
wthttp
|
||||
curl
|
||||
xml2
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
find_package(Boost COMPONENTS system filesystem REQUIRED)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 12.0.1, 2024-01-18T15:05:43. -->
|
||||
<!-- Written by QtCreator 9.0.2, 2024-04-08T09:10:17. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -37,7 +37,6 @@
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
@@ -77,7 +76,6 @@
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">8</value>
|
||||
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
@@ -100,7 +98,6 @@
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="CMake.Build.Type">Debug</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
@@ -154,7 +151,6 @@
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
|
||||
<value type="QString" key="CMake.Build.Type">Release</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
@@ -206,7 +202,6 @@
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
|
||||
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
@@ -258,7 +253,6 @@
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.3">
|
||||
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
@@ -311,7 +305,6 @@
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.4">
|
||||
<value type="QString" key="CMake.Build.Type">MinSizeRel</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
@@ -378,20 +371,19 @@
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">SingleChat</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.SingleChat</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">SingleChat</value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">singlechat.wt</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.singlechat.wt</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">singlechat.wt</value>
|
||||
<value type="QString" key="RunConfiguration.Arguments">--docroot ../docroot/ --http-port=4500 --http-address=0.0.0.0</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/torsten/Programs/SingleChat/build</value>
|
||||
</valuemap>
|
||||
|
||||
108
src/app.cpp
108
src/app.cpp
@@ -469,57 +469,22 @@ void App::sendImage() {
|
||||
|
||||
void App::imageUploaded(Wt::WFileUpload *fileWidget, std::shared_ptr<Magick::Blob> localImage, Wt::WImage *image, Wt::WPushButton *okButton) {
|
||||
try {
|
||||
const std::string uploadedFile = fileWidget->spoolFileName();
|
||||
std::list<Magick::Image> originalImages;
|
||||
Magick::readImages(&originalImages, fileWidget->spoolFileName());
|
||||
std::list<Magick::Image> previewList;
|
||||
std::list<Magick::Image> localList;
|
||||
|
||||
// Maximale Größen für Vorschau und lokale Liste festlegen
|
||||
int maxPreviewWidth = 100;
|
||||
int maxPreviewHeight = 100;
|
||||
int maxLocalWidth = 500;
|
||||
int maxLocalHeight = 500;
|
||||
|
||||
// Originalgröße des ersten Bildes erhalten
|
||||
int originalWidth = originalImages.front().columns();
|
||||
int originalHeight = originalImages.front().rows();
|
||||
|
||||
// Vorschau- und lokale Größe berechnen
|
||||
int previewWidth = std::min(originalWidth, maxPreviewWidth);
|
||||
int previewHeight = std::min(originalHeight, maxPreviewHeight);
|
||||
int localWidth = std::min(originalWidth, maxLocalWidth);
|
||||
int localHeight = std::min(originalHeight, maxLocalHeight);
|
||||
|
||||
// Größenänderung für alle Frames durchführen
|
||||
for (const auto& img : originalImages) {
|
||||
// Größenänderung nur durchführen, wenn das Bild größer als die maximalen Größen ist
|
||||
if (originalWidth > maxPreviewWidth || originalHeight > maxPreviewHeight) {
|
||||
Magick::Image resizedPreview = img;
|
||||
resizedPreview.resize(Magick::Geometry(previewWidth, previewHeight));
|
||||
previewList.push_back(resizedPreview);
|
||||
} else {
|
||||
previewList.push_back(img); // Originalgröße beibehalten
|
||||
}
|
||||
|
||||
if (originalWidth > maxLocalWidth || originalHeight > maxLocalHeight) {
|
||||
Magick::Image resizedLocal = img;
|
||||
resizedLocal.resize(Magick::Geometry(localWidth, localHeight));
|
||||
localList.push_back(resizedLocal);
|
||||
} else {
|
||||
localList.push_back(img); // Originalgröße beibehalten
|
||||
}
|
||||
}
|
||||
|
||||
Magick::Blob resizedBlob;
|
||||
Magick::writeImages(previewList.begin(), previewList.end(), &resizedBlob);
|
||||
Magick::writeImages(localList.begin(), localList.end(), &(*localImage));
|
||||
auto resizedBase64 = resizedBlob.base64();
|
||||
auto resizedImageString = Wt::Utils::base64Decode(resizedBase64);
|
||||
std::string imageFormat = previewList.front().magick();
|
||||
auto imageResource = std::make_shared<Wt::WMemoryResource>(
|
||||
"image/" + imageFormat,
|
||||
std::vector<unsigned char>(resizedImageString.begin(), resizedImageString.end()));
|
||||
image->setImageLink(Wt::WLink(imageResource));
|
||||
Magick::readImages(&originalImages, uploadedFile);
|
||||
std::list<Magick::Image> resizedForLocalImage;
|
||||
std::list<Magick::Image> resizedForDisplayImage;
|
||||
resizedForLocalImage = resizeImages(originalImages, 500, 500);
|
||||
resizedForDisplayImage = resizeImages(originalImages, 100, 100);
|
||||
std::string imageType = originalImages.front().magick();
|
||||
std::string mimeType = "image/" + imageType;
|
||||
Magick::writeImages(resizedForLocalImage.begin(), resizedForLocalImage.end(), localImage.get(), true);
|
||||
Magick::Blob displayBlob;
|
||||
Magick::writeImages(resizedForDisplayImage.begin(), resizedForDisplayImage.end(), &displayBlob, true);
|
||||
auto memoryResource = std::make_shared<Wt::WMemoryResource>(mimeType);
|
||||
memoryResource->setData(static_cast<const unsigned char*>(displayBlob.data()), displayBlob.length());
|
||||
image->setImageLink(Wt::WLink(memoryResource));
|
||||
image->setAlternateText("Hochgeladenes und verarbeitetes Bild");
|
||||
okButton->setEnabled(true);
|
||||
triggerUpdate();
|
||||
} catch (const std::exception& e) {
|
||||
@@ -527,16 +492,19 @@ void App::imageUploaded(Wt::WFileUpload *fileWidget, std::shared_ptr<Magick::Blo
|
||||
}
|
||||
}
|
||||
|
||||
Magick::Image App::scaleImage(const Magick::Image& originalImage, int maxSize) const {
|
||||
int scale = 100;
|
||||
int maxDimension = std::max(originalImage.size().width(), originalImage.size().height());
|
||||
if (maxDimension > maxSize) {
|
||||
scale = (maxSize * 100) / maxDimension;
|
||||
std::list<Magick::Image> App::resizeImages(std::list<Magick::Image> &images, int maxWidth, int maxHeight) {
|
||||
std::list<Magick::Image> resizedImages;
|
||||
for (auto& img : images) {
|
||||
Magick::Geometry newSize = img.size();
|
||||
newSize.aspect(true);
|
||||
if (newSize.width() > maxWidth || newSize.height() > maxHeight) {
|
||||
newSize.width(maxWidth);
|
||||
newSize.height(maxHeight);
|
||||
img.resize(newSize);
|
||||
}
|
||||
resizedImages.push_back(img);
|
||||
}
|
||||
scale = (scale < 100) ? scale : 100;
|
||||
Magick::Image scaledImage(originalImage);
|
||||
scaledImage.scale(Magick::Geometry(originalImage.size().width() * scale / 100, originalImage.size().height() * scale / 100));
|
||||
return scaledImage;
|
||||
return resizedImages;
|
||||
}
|
||||
|
||||
Wt::WContainerWidget* App::createSmileyButton(Wt::WHBoxLayout* inputLayout, Wt::WLineEdit* inputLine, std::shared_ptr<int> cursorPosition) {
|
||||
@@ -696,30 +664,16 @@ void App::createImprintContainer(Wt::WVBoxLayout *containerLayout) {
|
||||
|
||||
Wt::WWebWidget* App::createImageElement(Wt::Json::Object& line, const std::string& writer, Wt::WContainerWidget* outputContainer, std::string id) {
|
||||
Wt::Json::Object imageDescription = line["image"];
|
||||
std::cout << __LINE__ << std::endl;
|
||||
auto imageLineItem = outputContainer->addNew<Wt::WContainerWidget>();
|
||||
std::cout << __LINE__ << std::endl;
|
||||
auto outputText = Wt::WString("<b>{1}:</b> ").arg(writer);
|
||||
std::cout << __LINE__ << std::endl;
|
||||
imageLineItem->addNew<Wt::WText>(outputText)->setStyleClass("output-line");
|
||||
std::cout << __LINE__ << std::endl;
|
||||
auto image = imageLineItem->addNew<Wt::WImage>();
|
||||
std::cout << __LINE__ << std::endl;
|
||||
std::string base64Data = (std::string)imageDescription["imageblobbase64"];
|
||||
std::string decodedData = Wt::Utils::base64Decode(base64Data); // Decode Base64 data
|
||||
Magick::Blob imageBlob(decodedData.data(), decodedData.size());
|
||||
Magick::Image magickImage;
|
||||
magickImage.read(imageBlob);
|
||||
std::string imageFormat = magickImage.magick();
|
||||
std::cout << __LINE__ << std::endl;
|
||||
std::cout << imageFormat << std::endl;
|
||||
Magick::Blob blob;
|
||||
magickImage.write(&blob);
|
||||
auto resizedBase64 = blob.base64();
|
||||
auto resizedImageString = Wt::Utils::base64Decode(resizedBase64);
|
||||
std::string decodedData = Wt::Utils::base64Decode(base64Data);
|
||||
std::string imageType = imageDescription["type"].toString(); // Lese den Bildtyp
|
||||
auto imageResource = std::make_shared<Wt::WMemoryResource>(
|
||||
"image/" + imageFormat,
|
||||
std::vector<unsigned char>(resizedImageString.begin(), resizedImageString.end())); // Verwenden Sie den direkt decodierten Inhalt
|
||||
"image/" + imageType,
|
||||
std::vector<unsigned char>(decodedData.begin(), decodedData.end()));
|
||||
std::cout << __LINE__ << std::endl;
|
||||
image->setImageLink(Wt::WLink(imageResource));
|
||||
imageLineItem->setAttributeValue("dummy", id);
|
||||
|
||||
@@ -183,7 +183,6 @@ private:
|
||||
void setCurlOptions(CURL *curl, const std::string &apiUrl);
|
||||
std::string buildApiUrl(const std::string &userIP);
|
||||
std::string getUserIP();
|
||||
Magick::Image scaleImage(const Magick::Image &originalImage, int maxSize) const;
|
||||
Wt::WWebWidget *createImageElement(Wt::Json::Object &line, const std::string &writer, Wt::WContainerWidget *outputContainer, std::string id);
|
||||
Wt::WWebWidget *createTextElement(const std::string &writer, const std::string &text, Wt::WContainerWidget *outputContainer, std::string id);
|
||||
void createImprintContainer(Wt::WVBoxLayout *containerLayout);
|
||||
@@ -209,6 +208,7 @@ private:
|
||||
void showPartnerSites();
|
||||
void sendImage();
|
||||
void imageUploaded(Wt::WFileUpload *fileWidget, std::shared_ptr<Magick::Blob> localImage, Wt::WImage *image, Wt::WPushButton *okButton);
|
||||
std::list<Magick::Image> resizeImages(std::list<Magick::Image>& images, int maxWidth, int maxHeight);
|
||||
bool isAnimatedGIF(const Magick::Blob &blob);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <Wt/Json/Serializer.h>
|
||||
#include <Wt/Auth/HashFunction.h>
|
||||
#include <Wt/WLocalDateTime.h>
|
||||
#include <Wt/Utils.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
@@ -669,10 +670,13 @@ Broadcast::Message::Message(std::string fromSessionId_, std::shared_ptr<Magick::
|
||||
sendTime = Wt::WDateTime::currentDateTime();
|
||||
Magick::Image image_;
|
||||
image_.read(*imageBlob);
|
||||
std::string imageData(reinterpret_cast<const char*>(imageBlob->data()), imageBlob->length());
|
||||
std::string imageFormat = image_.magick();
|
||||
image = {
|
||||
{"width", Wt::Json::Value((int)image_.columns())},
|
||||
{"height", Wt::Json::Value((int)image_.rows())},
|
||||
{"imageblobbase64", Wt::Json::Value(imageBlob->base64())}
|
||||
{"imageblobbase64", Wt::Json::Value(Wt::Utils::base64Encode(imageData))},
|
||||
{"type", Wt::Json::Value(imageFormat)}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user