1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2014-2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include <nel/misc/types_nl.h>
18 #include "panoply_preview.h"
23 #include <QVBoxLayout>
24 #include <QDockWidget>
29 #include <QScrollArea>
30 #include <QPushButton>
35 #include <nel/misc/debug.h>
36 #include <nel/misc/command.h>
37 #include <nel/misc/path.h>
38 #include <nel/misc/thread.h>
39 #include <nel/misc/mutex.h>
40 #include <nel/misc/bitmap.h>
41 #include <nel/misc/file.h>
44 #include "main_window.h"
45 #include "../panoply_maker/color_modifier.h"
48 using namespace NLMISC
;
52 class CColorThread
: public NLMISC::IRunnable
55 // Called when a thread is run.
60 SettingsMutex
.enter();
63 SettingsMutex
.leave();
68 SettingsMutex
.leave();
69 nlSleep(10); // TODO: Should wait on an event signal...
72 // nldebug("Update color modifier");
73 m_ColorModifier
.Hue
= Hue
;
74 m_ColorModifier
.Lightness
= Lightness
;
75 m_ColorModifier
.Saturation
= Saturation
;
76 m_ColorModifier
.Luminosity
= Luminosity
;
77 m_ColorModifier
.Contrast
= Contrast
;
79 SettingsMutex
.leave();
89 nldebug("Bitmaps not ready");
95 DestBitmap
= ColorBitmap
;
96 m_ColorModifier
.convertBitmap(DestBitmap
, ColorBitmap
, MaskBitmap
, retDeltaHue
);
99 PanoplyPreview
->displayBitmap(DestBitmap
);
101 nlSleep(10); // TODO: Should wait on an event signal...
105 CColorThread() : PanoplyPreview(NULL
), BitmapsOk(false), Hue(0), Lightness(0), Saturation(0), Luminosity(0), Contrast(0), Process(false), Running(true) { }
106 virtual ~CColorThread() { }
107 virtual void getName (std::string
&result
) const { result
= "CColorThread"; }
110 CColorModifier m_ColorModifier
;
113 CPanoplyPreview
*PanoplyPreview
;
115 NLMISC::CMutex BitmapMutex
;
116 NLMISC::CBitmap ColorBitmap
;
117 NLMISC::CBitmap MaskBitmap
;
119 NLMISC::CBitmap DestBitmap
;
121 NLMISC::CMutex SettingsMutex
;
132 // *****************************************************************
134 CPanoplyPreview::CPanoplyPreview(CMainWindow
*parent
) : QWidget(parent
)
136 connect(this, SIGNAL(tSigBitmap()), this, SLOT(tSlotBitmap()));
138 createDockWindows(parent
);
140 m_Image
= new QImage(512, 512, QImage::Format_RGB32
);
141 m_Pixmap
= new QPixmap(512, 512);
143 setMinimumWidth(512);
144 setMinimumHeight(512);
146 m_ColorThread
= new CColorThread();
147 m_ColorThread
->PanoplyPreview
= this;
148 m_Thread
= IThread::create(m_ColorThread
);
152 CPanoplyPreview::~CPanoplyPreview()
154 m_ColorThread
->SettingsMutex
.enter();
155 m_ColorThread
->BitmapMutex
.enter();
156 m_ColorThread
->Running
= false;
157 m_ColorThread
->BitmapMutex
.leave();
158 m_ColorThread
->SettingsMutex
.leave();
161 delete m_ColorThread
;
164 void CPanoplyPreview::paintEvent(QPaintEvent
* e
)
166 QPainter
painter(this);
167 painter
.drawPixmap(0, 0, *m_Pixmap
);
170 void CPanoplyPreview::displayBitmap(const CBitmap
&bitmap
) // Called from thread!
172 // nldebug("received bitmap");
174 m_ColorThread
->BitmapMutex
.enter();
175 m_ImageMutex
.enter();
177 const char *buffer
= (const char *)&bitmap
.getPixels()[0];
179 if (bitmap
.getWidth() != m_Image
->width() || bitmap
.getHeight() != m_Image
->height())
181 QImage
*image
= m_Image
;
182 m_Image
= new QImage(bitmap
.getWidth(), bitmap
.getHeight(), QImage::Format_RGB32
);
186 for (uint32 y
= 0; y
< bitmap
.getHeight(); ++y
)
188 uint8
*dst
= (uint8
*)m_Image
->scanLine(y
);
189 const uint8
*src
= (const uint8
*)&buffer
[y
* bitmap
.getWidth() * sizeof(uint32
)];
190 for (uint32 x
= 0; x
< bitmap
.getWidth(); ++x
)
193 dst
[xb
+ 0] = src
[xb
+ 2];
194 dst
[xb
+ 1] = src
[xb
+ 1];
195 dst
[xb
+ 2] = src
[xb
+ 0];
196 dst
[xb
+ 3] = src
[xb
+ 3];
199 //memcpy(m_Image->scanLine(y), &buffer[y * bitmap.getWidth() * sizeof(uint32)], sizeof(uint32) * bitmap.getWidth());
202 m_ImageMutex
.leave();
203 m_ColorThread
->BitmapMutex
.leave();
208 void CPanoplyPreview::tSlotBitmap()
210 // nldebug("display bitmap");
212 m_ImageMutex
.enter();
214 if (m_Image
->width() != m_Pixmap
->width()
215 || m_Image
->height() != m_Pixmap
->height())
217 QPixmap
*pixmap
= m_Pixmap
;
218 m_Pixmap
= new QPixmap(m_Image
->width(), m_Image
->height());
219 setMinimumWidth(m_Pixmap
->width());
220 setMinimumHeight(m_Pixmap
->height());
223 m_Pixmap
->convertFromImage(*m_Image
);
226 m_ImageMutex
.leave();
230 void CPanoplyPreview::colorEdited(const QString
&text
)
235 void CPanoplyPreview::maskEdited(const QString
&text
)
240 void CPanoplyPreview::goPushed(bool)
242 // nldebug("push bitmaps");
243 m_ColorThread
->SettingsMutex
.enter();
244 m_ColorThread
->BitmapMutex
.enter();
245 m_ColorThread
->BitmapsOk
= false;
251 if (!is
.open(m_ColorFile
.toLocal8Bit().data()))
252 throw NLMISC::Exception("Cannot open file '%s'", m_ColorFile
.toLocal8Bit().data());
253 uint32 depth
= m_ColorThread
->ColorBitmap
.load(is
);
254 if (depth
== 0 || m_ColorThread
->ColorBitmap
.getPixels().empty())
255 throw NLMISC::Exception("Failed to load bitmap '%s'", m_ColorFile
.toLocal8Bit().data());
256 if (m_ColorThread
->ColorBitmap
.PixelFormat
!= NLMISC::CBitmap::RGBA
)
257 m_ColorThread
->ColorBitmap
.convertToType(NLMISC::CBitmap::RGBA
);
261 if (!is
.open(m_MaskFile
.toLocal8Bit().data()))
262 throw NLMISC::Exception("Cannot open file '%s'", m_MaskFile
.toLocal8Bit().data());
263 uint32 depth
= m_ColorThread
->MaskBitmap
.load(is
);
264 if (depth
== 0 || m_ColorThread
->MaskBitmap
.getPixels().empty())
265 throw NLMISC::Exception("Failed to load bitmap '%s'", m_MaskFile
.toLocal8Bit().data());
266 if (m_ColorThread
->MaskBitmap
.PixelFormat
!= NLMISC::CBitmap::Luminance
)
267 m_ColorThread
->MaskBitmap
.convertToType(NLMISC::CBitmap::Luminance
);
270 m_ColorThread
->BitmapsOk
= true;
271 m_ColorThread
->Process
= true;
274 catch (const NLMISC::Exception
&e
)
276 nlwarning("Exception: '%s'", e
.what());
279 m_ColorThread
->BitmapMutex
.leave();
280 m_ColorThread
->SettingsMutex
.leave();
281 // nldebug("done pushing butmaps");
284 void CPanoplyPreview::hueChanged(int value
)
286 float v
= (float)value
;
287 m_ColorThread
->SettingsMutex
.enter();
288 m_ColorThread
->Hue
= v
;
289 m_ColorThread
->Process
= true;
290 m_ColorThread
->SettingsMutex
.leave();
293 void CPanoplyPreview::lightnessChanged(int value
)
295 float v
= (float)value
* 0.01f
;
296 m_ColorThread
->SettingsMutex
.enter();
297 m_ColorThread
->Lightness
= v
;
298 m_ColorThread
->Process
= true;
299 m_ColorThread
->SettingsMutex
.leave();
302 void CPanoplyPreview::saturationChanged(int value
)
304 float v
= (float)value
* 0.01f
;
305 m_ColorThread
->SettingsMutex
.enter();
306 m_ColorThread
->Saturation
= v
;
307 m_ColorThread
->Process
= true;
308 m_ColorThread
->SettingsMutex
.leave();
311 void CPanoplyPreview::luminosityChanged(int value
)
313 float v
= (float)value
;
314 m_ColorThread
->SettingsMutex
.enter();
315 m_ColorThread
->Luminosity
= v
;
316 m_ColorThread
->Process
= true;
317 m_ColorThread
->SettingsMutex
.leave();
320 void CPanoplyPreview::contrastChanged(int value
)
322 float v
= (float)value
;
323 m_ColorThread
->SettingsMutex
.enter();
324 m_ColorThread
->Contrast
= v
;
325 m_ColorThread
->Process
= true;
326 m_ColorThread
->SettingsMutex
.leave();
329 // *****************************************************************
331 CSliderTextEdit::CSliderTextEdit(QWidget
*parent
, QLineEdit
*lineEdit
, float scale
) : QSlider(Qt::Horizontal
, parent
), m_LineEdit(lineEdit
), m_Updating(false), m_Scale(scale
)
333 connect(this, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
334 connect(lineEdit
, SIGNAL(textEdited(const QString
&)), this, SLOT(lineEditTextEdited(const QString
&)));
337 CSliderTextEdit::~CSliderTextEdit()
342 void CSliderTextEdit::lineEditTextEdited(const QString
&text
)
347 setValue((int)(text
.toFloat() * m_Scale
));
352 void CSliderTextEdit::sliderValueChanged(int value
)
357 m_LineEdit
->setText(QString::number((double)value
/ (double)m_Scale
));
362 // *****************************************************************
364 void CPanoplyPreview::createDockWindows(CMainWindow
*mainWindow
)
366 nlassert(mainWindow
);
370 QDockWidget
*dockWidget
= new QDockWidget(mainWindow
);
371 nlassert(dockWidget
);
372 dockWidget
->setWindowTitle(tr("Color Modifier"));
373 dockWidget
->setAllowedAreas(Qt::LeftDockWidgetArea
| Qt::RightDockWidgetArea
);
374 QScrollArea
*scrollArea
= new QScrollArea(dockWidget
);
375 scrollArea
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
376 scrollArea
->setWidgetResizable(true);
377 QWidget
*widget
= new QWidget(scrollArea
);
378 QVBoxLayout
*vboxLayout
= new QVBoxLayout(widget
);
382 QGroupBox
*groupBox
= new QGroupBox(widget
);
383 groupBox
->setTitle(tr("Input File Paths"));
384 QGridLayout
*groupLayout
= new QGridLayout(groupBox
);
386 QLabel
*colorLabel
= new QLabel(groupBox
);
387 colorLabel
->setText(tr("Color: "));
388 groupLayout
->addWidget(colorLabel
, 0, 0);
390 QLineEdit
*colorFile
= new QLineEdit(groupBox
);
391 colorFile
->setText("W:\\database\\stuff\\fyros\\agents\\_textures\\actors\\fy_hof_armor00_arm01_c1.png");
392 groupLayout
->addWidget(colorFile
, 0, 1);
394 m_ColorFile
= colorFile
->text();
395 connect(colorFile
, SIGNAL(textEdited(const QString
&)), this, SLOT(colorEdited(const QString
&)));
397 QLabel
*maskLabel
= new QLabel(groupBox
);
398 maskLabel
->setText(tr("Mask: "));
399 groupLayout
->addWidget(maskLabel
, 1, 0);
401 QLineEdit
*maskFile
= new QLineEdit(groupBox
);
402 maskFile
->setText("W:\\database\\stuff\\fyros\\agents\\_textures\\actors\\mask\\fy_hof_armor00_arm01_c1_skin.png");
403 groupLayout
->addWidget(maskFile
, 1, 1);
405 m_MaskFile
= maskFile
->text();
406 connect(maskFile
, SIGNAL(textEdited(const QString
&)), this, SLOT(maskEdited(const QString
&)));
408 QPushButton
*go
= new QPushButton(groupBox
);
409 go
->setText(tr("Go"));
410 groupLayout
->addWidget(go
, 2, 0, 1, 2);
412 connect(go
, SIGNAL(clicked(bool)), this, SLOT(goPushed(bool)));
414 groupBox
->setLayout(groupLayout
);
415 vboxLayout
->addWidget(groupBox
);
420 QGroupBox
*groupBox
= new QGroupBox(widget
);
421 groupBox
->setTitle(tr("Color Modifier"));
422 QGridLayout
*groupLayout
= new QGridLayout(groupBox
);
426 CSliderTextEdit
*slider
;
428 label
= new QLabel(groupBox
);
429 label
->setText(tr("Hue [0, 360]: "));
430 groupLayout
->addWidget(label
, 0, 0);
432 edit
= new QLineEdit(groupBox
);
434 groupLayout
->addWidget(edit
, 0, 1);
436 slider
= new CSliderTextEdit(groupBox
, edit
, 1.0f
);
437 slider
->setMinimum(0);
438 slider
->setMaximum(360);
440 groupLayout
->addWidget(slider
, 1, 0, 1, 2);
442 connect(slider
, SIGNAL(valueChanged(int)), this, SLOT(hueChanged(int)));
444 label
= new QLabel(groupBox
);
445 label
->setText(tr("Lightness [-1, 1]: "));
446 groupLayout
->addWidget(label
, 2, 0);
448 edit
= new QLineEdit(groupBox
);
450 groupLayout
->addWidget(edit
, 2, 1);
452 slider
= new CSliderTextEdit(groupBox
, edit
, 100.0f
);
453 slider
->setMinimum(-100);
454 slider
->setMaximum(100);
456 groupLayout
->addWidget(slider
, 3, 0, 1, 2);
458 connect(slider
, SIGNAL(valueChanged(int)), this, SLOT(lightnessChanged(int)));
460 label
= new QLabel(groupBox
);
461 label
->setText(tr("Saturation [-1, 1]: "));
462 groupLayout
->addWidget(label
, 4, 0);
464 edit
= new QLineEdit(groupBox
);
466 groupLayout
->addWidget(edit
, 4, 1);
468 slider
= new CSliderTextEdit(groupBox
, edit
, 100.0f
);
469 slider
->setMinimum(-100);
470 slider
->setMaximum(100);
472 groupLayout
->addWidget(slider
, 5, 0, 1, 2);
474 connect(slider
, SIGNAL(valueChanged(int)), this, SLOT(saturationChanged(int)));
476 label
= new QLabel(groupBox
);
477 label
->setText(tr("Luminosity [-100, 100]: "));
478 groupLayout
->addWidget(label
, 6, 0);
480 edit
= new QLineEdit(groupBox
);
482 groupLayout
->addWidget(edit
, 6, 1);
484 slider
= new CSliderTextEdit(groupBox
, edit
, 1.0f
);
485 slider
->setMinimum(-100);
486 slider
->setMaximum(100);
488 groupLayout
->addWidget(slider
, 7, 0, 1, 2);
490 connect(slider
, SIGNAL(valueChanged(int)), this, SLOT(luminosityChanged(int)));
492 label
= new QLabel(groupBox
);
493 label
->setText(tr("Contrast [-100, 100]: "));
494 groupLayout
->addWidget(label
, 8, 0);
496 edit
= new QLineEdit(groupBox
);
498 groupLayout
->addWidget(edit
, 8, 1);
500 slider
= new CSliderTextEdit(groupBox
, edit
, 1.0f
);
501 slider
->setMinimum(-100);
502 slider
->setMaximum(100);
504 groupLayout
->addWidget(slider
, 9, 0, 1, 2);
506 connect(slider
, SIGNAL(valueChanged(int)), this, SLOT(contrastChanged(int)));
508 groupBox
->setLayout(groupLayout
);
509 vboxLayout
->addWidget(groupBox
);
512 vboxLayout
->addStretch();
513 widget
->setLayout(vboxLayout
);
514 scrollArea
->setWidget(widget
);
515 dockWidget
->setWidget(scrollArea
);
516 mainWindow
->addDockWidget(Qt::LeftDockWidgetArea
, dockWidget
);
517 mainWindow
->widgetsMenu()->addAction(dockWidget
->toggleViewAction());
521 } /* namespace NLTOOLS */