1 #include "toonz/boardsettings.h"
2
3 // TnzLib includes
4 #include "toonz/toonzscene.h"
5 #include "toonz/tproject.h"
6 #include "toonz/sceneproperties.h"
7 #include "toonz/toonzfolders.h"
8 #include "toutputproperties.h"
9 #include "tsystem.h"
10
11 #include <QImage>
12 #include <QDate>
13 #include <QDateTime>
14 #include <QFontMetricsF>
15 #include <QMap>
16
17 namespace {
18 QMap<BoardItem::Type, std::wstring> strs = {
19 {BoardItem::FreeText, L"FreeText"},
20 {BoardItem::ProjectName, L"ProjectName"},
21 {BoardItem::SceneName, L"SceneName"},
22 {BoardItem::Duration_Frame, L"Duration_Frame"},
23 {BoardItem::Duration_SecFrame, L"Duration_SecFrame"},
24 {BoardItem::Duration_HHMMSSFF, L"Duration_HHMMSSFF"},
25 {BoardItem::CurrentDate, L"CurrentDate"},
26 {BoardItem::CurrentDateTime, L"CurrentDateTime"},
27 {BoardItem::UserName, L"UserName"},
28 {BoardItem::ScenePath_Aliased, L"ScenePath_Aliased"},
29 {BoardItem::ScenePath_Full, L"ScenePath_Full"},
30 {BoardItem::MoviePath_Aliased, L"MoviePath_Aliased"},
31 {BoardItem::MoviePath_Full, L"MoviePath_Full"},
32 {BoardItem::Image, L"Image"}};
33
type2String(BoardItem::Type type)34 std::wstring type2String(BoardItem::Type type) { return strs.value(type, L""); }
string2Type(std::wstring str)35 BoardItem::Type string2Type(std::wstring str) {
36 return strs.key(str, BoardItem::TypeCount);
37 }
38 }; // namespace
39
BoardItem()40 BoardItem::BoardItem() {
41 m_name = "Item";
42 m_type = ProjectName;
43 m_rect = QRectF(0.1, 0.1, 0.8, 0.8);
44 m_maximumFontSize = 300;
45 m_color = Qt::black;
46 }
47
getContentText(ToonzScene * scene)48 QString BoardItem::getContentText(ToonzScene *scene) {
49 auto getDuration = [&]() {
50 TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
51 int from, to, step;
52 if (oprop->getRange(from, to, step))
53 return to - from + 1;
54 else
55 return scene->getFrameCount();
56 };
57
58 switch (m_type) {
59 case FreeText:
60 return m_text;
61 break;
62 case ProjectName:
63 return scene->getProject()->getName().getQString();
64 break;
65 case SceneName:
66 return QString::fromStdWString(scene->getSceneName());
67 break;
68 case Duration_Frame:
69 return QString::number(getDuration());
70 break;
71 case Duration_SecFrame: {
72 TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
73 int fps = (int)oprop->getFrameRate();
74 int frame = getDuration();
75 return QString("%1 + %2").arg(QString::number(frame / fps),
76 QString::number(frame % fps));
77 } break;
78 case Duration_HHMMSSFF: {
79 TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
80 int fps = (int)oprop->getFrameRate();
81 int frame = getDuration();
82 int hh = frame / (fps * 60 * 60);
83 frame -= hh * fps * 60 * 60;
84 int mm = frame / (fps * 60);
85 frame -= mm * fps * 60;
86 int ss = frame / fps;
87 int ff = frame % fps;
88 return QString::number(hh).rightJustified(2, '0') + ":" +
89 QString::number(mm).rightJustified(2, '0') + ":" +
90 QString::number(ss).rightJustified(2, '0') + ":" +
91 QString::number(ff).rightJustified(2, '0');
92 } break;
93 case CurrentDate:
94 return QDate::currentDate().toString(Qt::DefaultLocaleLongDate);
95 break;
96 case CurrentDateTime:
97 return QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate);
98 break;
99 case UserName:
100 return TSystem::getUserName();
101 break;
102 case ScenePath_Aliased:
103 return scene->codeFilePath(scene->getScenePath()).getQString();
104 break;
105 case ScenePath_Full:
106 return scene->decodeFilePath(scene->getScenePath()).getQString();
107 break;
108 case MoviePath_Aliased: {
109 TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
110 return scene->codeFilePath(oprop->getPath()).getQString();
111 } break;
112 case MoviePath_Full: {
113 TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
114 return scene->decodeFilePath(oprop->getPath()).getQString();
115 } break;
116 default:
117 break;
118 }
119 return QString();
120 }
121
getItemRect(QSize imgSize)122 QRectF BoardItem::getItemRect(QSize imgSize) {
123 QSizeF imgSizeF(imgSize);
124 return QRectF(
125 imgSizeF.width() * m_rect.left(), imgSizeF.height() * m_rect.top(),
126 imgSizeF.width() * m_rect.width(), imgSizeF.height() * m_rect.height());
127 }
128
drawItem(QPainter & p,QSize imgSize,int shrink,ToonzScene * scene)129 void BoardItem::drawItem(QPainter &p, QSize imgSize, int shrink,
130 ToonzScene *scene) {
131 QRectF itemRect = getItemRect(imgSize);
132
133 if (m_type == Image) {
134 if (m_imgPath.isEmpty()) return;
135 TFilePath decodedPath = scene->decodeFilePath(m_imgPath);
136 QImage img(decodedPath.getQString());
137 if (m_imgARMode == Qt::KeepAspectRatio) {
138 float ratio = std::min((float)itemRect.width() / (float)img.width(),
139 (float)itemRect.height() / (float)img.height());
140 QSizeF imgSize((float)img.width() * ratio, (float)img.height() * ratio);
141 QPointF imgTopLeft =
142 itemRect.topLeft() +
143 QPointF((itemRect.width() - imgSize.width()) * 0.5f,
144 (itemRect.height() - imgSize.height()) * 0.5f);
145
146 p.drawImage(QRectF(imgTopLeft, imgSize), img);
147 } else if (m_imgARMode == Qt::IgnoreAspectRatio)
148 p.drawImage(itemRect, img);
149 return;
150 }
151
152 QString contentText = getContentText(scene);
153
154 QFont tmpFont(m_font);
155 tmpFont.setPixelSize(100);
156 QFontMetricsF tmpFm(tmpFont);
157 QRectF tmpBounding =
158 tmpFm.boundingRect(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
159
160 float ratio = std::min(itemRect.width() / tmpBounding.width(),
161 itemRect.height() / tmpBounding.height());
162
163 // compute the font size which will just fit the item region
164 int fontSize = (int)(100.0f * ratio);
165 tmpFont.setPixelSize(fontSize);
166 tmpFm = QFontMetricsF(tmpFont);
167 tmpBounding =
168 tmpFm.boundingRect(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
169 bool isInRect;
170 if (itemRect.width() >= tmpBounding.width() &&
171 itemRect.height() >= tmpBounding.height())
172 isInRect = true;
173 else
174 isInRect = false;
175 while (1) {
176 fontSize += (isInRect) ? 1 : -1;
177 if (fontSize <= 0) // cannot draw
178 return;
179 tmpFont.setPixelSize(fontSize);
180 tmpFm = QFontMetricsF(tmpFont);
181 tmpBounding =
182 tmpFm.boundingRect(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
183
184 bool newIsInRect = (itemRect.width() >= tmpBounding.width() &&
185 itemRect.height() >= tmpBounding.height());
186 if (isInRect != newIsInRect) {
187 if (isInRect) fontSize--;
188 break;
189 }
190 }
191
192 //----
193 fontSize = std::min(fontSize, m_maximumFontSize / shrink);
194
195 QFont font(m_font);
196 font.setPixelSize(fontSize);
197
198 p.setFont(font);
199 p.setPen(m_color);
200
201 if (m_type == FreeText)
202 p.drawText(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
203 else
204 p.drawText(itemRect, Qt::AlignCenter, contentText);
205 }
206
saveData(TOStream & os)207 void BoardItem::saveData(TOStream &os) {
208 os.child("type") << type2String(m_type);
209 os.child("name") << m_name;
210 os.child("rect") << m_rect.x() << m_rect.y() << m_rect.width()
211 << m_rect.height();
212
213 if (m_type == Image) {
214 // if the path is in library folder, then save the relative path
215 TFilePath libFp = ToonzFolder::getLibraryFolder();
216 if (libFp.isAncestorOf(m_imgPath))
217 os.child("imgPath") << 1 << m_imgPath - libFp;
218 else
219 os.child("imgPath") << 0 << m_imgPath;
220 os.child("imgARMode") << (int)m_imgARMode;
221 } else {
222 if (m_type == FreeText) os.child("text") << m_text;
223
224 os.child("maximumFontSize") << m_maximumFontSize;
225 os.child("color") << m_color.red() << m_color.green() << m_color.blue()
226 << m_color.alpha();
227 os.child("font") << m_font.family() << (int)(m_font.bold() ? 1 : 0)
228 << (int)(m_font.italic() ? 1 : 0);
229 }
230 }
231
loadData(TIStream & is)232 void BoardItem::loadData(TIStream &is) {
233 std::string tagName;
234 while (is.matchTag(tagName)) {
235 if (tagName == "type") {
236 std::wstring typeStr;
237 is >> typeStr;
238 m_type = string2Type(typeStr);
239 } else if (tagName == "name") {
240 std::wstring str;
241 is >> str;
242 m_name = QString::fromStdWString(str);
243 } else if (tagName == "rect") {
244 double x, y, width, height;
245 is >> x >> y >> width >> height;
246 m_rect = QRectF(x, y, width, height);
247 } else if (tagName == "imgPath") {
248 int isInLibrary;
249 TFilePath fp;
250 is >> isInLibrary >> fp;
251 if (isInLibrary == 1)
252 m_imgPath = ToonzFolder::getLibraryFolder() + fp;
253 else
254 m_imgPath = fp;
255 } else if (tagName == "imgARMode") {
256 int mode;
257 is >> mode;
258 m_imgARMode = (Qt::AspectRatioMode)mode;
259 } else if (tagName == "text") {
260 std::wstring str;
261 is >> str;
262 m_text = QString::fromStdWString(str);
263 } else if (tagName == "maximumFontSize") {
264 is >> m_maximumFontSize;
265 } else if (tagName == "color") {
266 int r, g, b, a;
267 is >> r >> g >> b >> a;
268 m_color = QColor(r, g, b, a);
269 } else if (tagName == "font") {
270 QString family;
271 int isBold, isItalic;
272 is >> family >> isBold >> isItalic;
273 m_font.setFamily(family);
274 m_font.setBold(isBold == 1);
275 m_font.setItalic(isItalic == 1);
276 } else
277 throw TException("unexpected tag: " + tagName);
278 is.closeChild();
279 }
280 }
281
282 //======================================================================================
283
BoardSettings()284 BoardSettings::BoardSettings() {
285 // add one item as an example
286 m_items.push_back(BoardItem());
287 }
288
getBoardImage(TDimension & dim,int shrink,ToonzScene * scene)289 QImage BoardSettings::getBoardImage(TDimension &dim, int shrink,
290 ToonzScene *scene) {
291 QImage img(dim.lx, dim.ly, QImage::Format_ARGB32);
292
293 QPainter painter(&img);
294
295 painter.fillRect(img.rect(), Qt::white);
296
297 // draw each item
298 for (int i = m_items.size() - 1; i >= 0; i--)
299 m_items[i].drawItem(painter, img.rect().size(), shrink, scene);
300
301 painter.end();
302
303 return img;
304 }
305
getBoardRaster(TDimension & dim,int shrink,ToonzScene * scene)306 TRaster32P BoardSettings::getBoardRaster(TDimension &dim, int shrink,
307 ToonzScene *scene) {
308 QImage img = getBoardImage(dim, shrink, scene);
309
310 // convert QImage to TRaster
311 TRaster32P boardRas(dim);
312 int img_y = img.height() - 1;
313 for (int j = 0; j < dim.ly; j++, img_y--) {
314 TPixel32 *pix = boardRas->pixels(j);
315 QRgb *img_p = (QRgb *)img.scanLine(img_y);
316 for (int i = 0; i < dim.lx; i++, pix++, img_p++) {
317 (*pix).r = (typename TPixel32::Channel)(qRed(*img_p));
318 (*pix).g = (typename TPixel32::Channel)(qGreen(*img_p));
319 (*pix).b = (typename TPixel32::Channel)(qBlue(*img_p));
320 (*pix).m = (typename TPixel32::Channel)(qAlpha(*img_p));
321 }
322 }
323 return boardRas;
324 }
325
addNewItem(int insertAt)326 void BoardSettings::addNewItem(int insertAt) {
327 m_items.insert(insertAt, BoardItem());
328 }
329
removeItem(int index)330 void BoardSettings::removeItem(int index) {
331 if (index < 0 || index >= m_items.size()) return;
332 m_items.removeAt(index);
333 }
334
swapItems(int i,int j)335 void BoardSettings::swapItems(int i, int j) { m_items.swap(i, j); }
336
saveData(TOStream & os,bool forPreset)337 void BoardSettings::saveData(TOStream &os, bool forPreset) {
338 if (!forPreset) os.child("active") << (int)((m_active) ? 1 : 0);
339 os.child("duration") << m_duration;
340 if (!m_items.isEmpty()) {
341 os.openChild("boardItems");
342 for (int i = 0; i < getItemCount(); i++) {
343 os.openChild("item");
344 m_items[i].saveData(os);
345 os.closeChild();
346 }
347 os.closeChild();
348 }
349 }
350
loadData(TIStream & is)351 void BoardSettings::loadData(TIStream &is) {
352 std::string tagName;
353 while (is.matchTag(tagName)) {
354 if (tagName == "active") {
355 int val;
356 is >> val;
357 setActive(val == 1);
358 } else if (tagName == "duration") {
359 is >> m_duration;
360 } else if (tagName == "boardItems") {
361 m_items.clear();
362 while (is.matchTag(tagName)) {
363 if (tagName == "item") {
364 BoardItem item;
365 item.loadData(is);
366 m_items.append(item);
367 } else
368 throw TException("unexpected tag: " + tagName);
369 is.closeChild();
370 }
371 } else
372 throw TException("unexpected tag: " + tagName);
373 is.closeChild();
374 }
375 }