Animated gifs are working now

This commit is contained in:
Torsten Schulz
2024-04-08 09:35:35 +02:00
parent ba976db321
commit e05a9429b9
5 changed files with 48 additions and 94 deletions

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>&nbsp;").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);

View File

@@ -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);
};

View File

@@ -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)}
};
}