KSeExpr 6.0.0.0
imageEditor.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2011-2019 Disney Enterprises, Inc.
2// SPDX-License-Identifier: LicenseRef-Apache-2.0
3// SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
4// SPDX-License-Identifier: GPL-3.0-or-later
5
10#include <iostream>
11#include <string>
12
13#include <QApplication>
14#include <QDialog>
15#include <QDir>
16#include <QImage>
17#include <QLabel>
18#include <QMessageBox>
19#include <QPushButton>
20#include <QScrollArea>
21#include <QVBoxLayout>
22
23#include <KSeExpr/Expression.h>
28
29#include "ImageEditorDialog.h"
30
31//-- IMAGE SYNTHESIZER CLASSES AND METHODS --//
32
33constexpr double clamp(double x)
34{
35 return std::max(0., std::min(255., x)); // NOLINT readability-magic-numbers
36}
37
38// Simple image synthesizer expression class to support demo image editor
39class ImageSynthExpression : public KSeExpr::Expression
40{
41public:
42 // Constructor that takes the expression to parse
43 ImageSynthExpression(const std::string &expr)
44 : KSeExpr::Expression(expr)
45 {
46 }
47
48 // Simple variable that just returns its internal value
49 struct Var : public KSeExpr::ExprVarRef {
50 Var(const double val)
51 : KSeExpr::ExprVarRef(KSeExpr::ExprType().FP(1).Varying())
52 , val(val)
53 {
54 }
55 Var()
56 : KSeExpr::ExprVarRef(KSeExpr::ExprType().FP(1).Varying())
57 {
58 }
59 double val {0.0}; // independent variable
60 void eval(double *result) override
61 {
62 result[0] = val;
63 }
64 void eval(const char **) override
65 {
66 assert(false);
67 }
68 };
69 // variable map
70 mutable std::map<std::string, Var> vars;
71
72 // resolve function that only supports one external variable 'x'
73 KSeExpr::ExprVarRef *resolveVar(const std::string &name) const override
74 {
75 auto i = vars.find(name);
76 if (i != vars.end())
77 return &i->second;
78 return nullptr;
79 }
80};
81
82class ImageSynthesizer
83{
84public:
85 using ImageData = std::shared_ptr<std::vector<unsigned char>>;
86 ImageSynthesizer() = default;
87 ImageData evaluateExpression(const std::string &exprStr) const
88 {
89 ImageSynthExpression expr(exprStr);
90
91 // make variables
92 expr.vars["u"] = ImageSynthExpression::Var(0.);
93 expr.vars["v"] = ImageSynthExpression::Var(0.);
94 expr.vars["w"] = ImageSynthExpression::Var(_width);
95 expr.vars["h"] = ImageSynthExpression::Var(_height);
96
97 // check if expression is valid
98 bool valid = expr.isValid();
99 if (!valid) {
100 std::cerr << "Invalid expression " << std::endl;
101 auto message = ErrorMessages::message(expr.parseError());
102 for (const auto &arg : expr.parseErrorArgs()) {
103 message = message.arg(QString::fromStdString(arg));
104 }
105 std::cerr << "Parse error: " << message.toStdString() << std::endl;
106 for (const auto &occurrence : expr.getErrors()) {
107 QString message = ErrorMessages::message(occurrence.error);
108 for (const auto &arg : occurrence.ids) {
109 message = message.arg(QString::fromStdString(arg));
110 }
111 std::cerr << "Prep error: " << message.toStdString() << std::endl;
112 }
113 return nullptr;
114 }
115
116 // evaluate expression
117 std::cerr << "Evaluating expression..." << std::endl;
118 std::vector<unsigned char> image(_width * _height * 4);
119 double one_over_width = 1. / _width;
120 double one_over_height = 1. / _height;
121 double &u = expr.vars["u"].val;
122 double &v = expr.vars["v"].val;
123 for (size_t row {}; row < _height; row++) {
124 for (size_t col {}; col < _width; col++) {
125 auto i = (row * _width + col) * 4;
126 u = one_over_width * (col + .5); // NOLINT readability-magic-constants
127 v = one_over_height * (row + .5); // NOLINT readability-magic-constants
128 KSeExpr::Vec3d result = KSeExpr::Vec3dConstRef(expr.evalFP());
129 image[i] = clamp(result[2] * 256.); // NOLINT readability-magic-numbers
130 image[i + 1] = clamp(result[1] * 256.); // NOLINT readability-magic-numbers
131 image[i + 2] = clamp(result[0] * 256.); // NOLINT readability-magic-numbers
132 image[i + 3] = 255; // NOLINT readability-magic-numbers
133 }
134 }
135
136 return std::make_shared<std::vector<unsigned char>>(image);
137 }
138
139private:
140 size_t _width {256}; // NOLINT readability-magic-numbers
141 size_t _height {256}; // NOLINT readability-magic-numbers
142};
143
144//-- IMAGE EDITOR DIALOG METHODS --//
145
146ImageEditorDialog::ImageEditorDialog(QWidget *parent)
147 : QDialog(parent)
148{
149 _imageSynthesizer = new ImageSynthesizer();
150
151 this->setWindowTitle("Image Synthesis Editor");
152
153 // Image Previewer
154 _imageLabel = new QLabel();
155 _imageLabel->setFixedSize(256, 256); // NOLINT readability-magic-numbers
156 _imageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
157
158 auto *imagePreviewWidget = new QWidget();
159 auto *imagePreviewLayout = new QHBoxLayout(imagePreviewWidget);
160 imagePreviewLayout->addStretch();
161 imagePreviewLayout->addWidget(_imageLabel);
162 imagePreviewLayout->addStretch();
163
164 // Expression controls
165 auto *controls = new ExprControlCollection();
166 auto *scrollArea = new QScrollArea();
167 scrollArea->setMinimumHeight(100); // NOLINT readability-magic-numbers
168 scrollArea->setFixedWidth(450); // NOLINT readability-magic-numbers
169 scrollArea->setWidgetResizable(true);
170 scrollArea->setWidget(controls);
171
172 // Expression editor
173 _editor = new ExprEditor(this);
174 _editor->setControlCollectionWidget(controls);
175
176 // Expression browser
177 auto *browser = new ExprBrowser(nullptr, _editor);
178
179 // Add user expressions, example expressions to browser list.
180 browser->addUserExpressionPath("imageEditor");
181#ifdef IMAGE_EDITOR_ROOT
182 browser->addPath("Examples", QDir::toNativeSeparators(QString("%1/share/KSeExpr/expressions").arg(IMAGE_EDITOR_ROOT)).toStdString());
183#else
184 browser->addPath("Examples", "./src/demos/imageEditor");
185#endif
186 browser->update();
187
188 // Create apply button and connect to image preview.
189 auto *applyButton = new QPushButton("Apply");
190 connect(applyButton, SIGNAL(clicked()), (ImageEditorDialog *)this, SLOT(applyExpression()));
191
192 // Layout widgets: Top section contains left side with previewer and
193 // controls, right side with browser. Bottom section contains editor
194 // and apply button.
195 auto *rootLayout = new QVBoxLayout();
196 this->setLayout(rootLayout);
197
198 auto *topWidget = new QWidget();
199 auto *topLayout = new QHBoxLayout();
200 topLayout->setContentsMargins(0, 0, 0, 0);
201 topWidget->setLayout(topLayout);
202
203 auto *leftWidget = new QWidget();
204 auto *leftLayout = new QVBoxLayout();
205 leftLayout->setContentsMargins(0, 0, 0, 0);
206 leftWidget->setLayout(leftLayout);
207 leftLayout->addWidget(imagePreviewWidget);
208 leftLayout->addWidget(scrollArea, 1);
209
210 auto *bottomWidget = new QWidget();
211 auto *bottomLayout = new QVBoxLayout();
212 bottomLayout->setContentsMargins(0, 0, 0, 0);
213 bottomWidget->setLayout(bottomLayout);
214
215 auto *buttonWidget = new QWidget();
216 auto *buttonLayout = new QHBoxLayout(nullptr);
217 buttonWidget->setLayout(buttonLayout);
218 buttonLayout->addWidget(applyButton);
219
220 topLayout->addWidget(leftWidget);
221 topLayout->addWidget(browser, 1);
222
223 bottomLayout->addWidget(_editor);
224 bottomLayout->addWidget(buttonWidget);
225
226 rootLayout->addWidget(topWidget);
227 rootLayout->addWidget(bottomWidget);
228}
229
230// Apply expression, if any, from the editor contents to the preview image
231void ImageEditorDialog::applyExpression()
232{
233 std::string exprStr = _editor->getExpr().toStdString();
234 if (exprStr.empty()) {
235 QMessageBox::information(this, this->windowTitle(), "No expression entered in the editor.");
236 } else {
237 auto data = _imageSynthesizer->evaluateExpression(exprStr);
238 if (!data) {
239 QMessageBox::critical(this, this->windowTitle(), "Error evaluating expression to create preview image.");
240 } else {
241 QImage image(data->data(), 256, 256, QImage::Format_RGB32); // NOLINT readability-magic-numbers
242 QPixmap imagePixmap = QPixmap::fromImage(image);
243 _imageLabel->setPixmap(imagePixmap);
244 // amyspark: ensure the old image gets dropped only after it was replaced
245 imageData = data;
246 }
247 }
248}
249
250//-- MAIN --//
251
252int main(int argc, char *argv[])
253{
254 auto app = std::make_unique<QApplication>(argc, argv);
255 auto dialog = std::make_unique<ImageEditorDialog>(nullptr);
256 dialog->show();
257 app->exec();
258 return 0;
259}
static QString message(KSeExpr::ErrorCode code)
abstract class for implementing variable references
Definition Expression.h:36
virtual void eval(double *result)=0
returns this variable's value by setting result
main expression class
Definition Expression.h:67
Expression(EvaluationStrategy be=Expression::defaultEvaluationStrategy)
virtual ExprVarRef * resolveVar(const std::string &) const
Definition Expression.h:201
int main(int argc, char *argv[])
constexpr double clamp(double x)
Vec< const double, 3, true > Vec3dConstRef
Definition Vec.h:368