diff --git a/stuff/config/current.txt b/stuff/config/current.txt index d42fcf2..26a6afd 100644 --- a/stuff/config/current.txt +++ b/stuff/config/current.txt @@ -1224,6 +1224,16 @@ "STD_iwa_BokehFx.distance5" "Layer5 Distance" "STD_iwa_BokehFx.bokeh_adjustment5" "Layer5 Bokeh Adjustment" + "STD_iwa_TimeCodeFx" "TimeCode Iwa" + "STD_iwa_TimeCodeFx.displayType" "Display Type" + "STD_iwa_TimeCodeFx.frameRate" "Frame Rate" + "STD_iwa_TimeCodeFx.startFrame" "Start Frame" + "STD_iwa_TimeCodeFx.position" "Text Position" + "STD_iwa_TimeCodeFx.size" "Text Size" + "STD_iwa_TimeCodeFx.textColor" "Text Color" + "STD_iwa_TimeCodeFx.showBox" "Show Box" + "STD_iwa_TimeCodeFx.boxColor" "Box Color" + STD_iwa_TiledParticlesFx "Tiled Particles Iwa" diff --git a/stuff/profiles/layouts/fxs/STD_iwa_TimeCodeFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_TimeCodeFx.xml new file mode 100644 index 0000000..87d3662 --- /dev/null +++ b/stuff/profiles/layouts/fxs/STD_iwa_TimeCodeFx.xml @@ -0,0 +1,14 @@ + + + + displayType + frameRate + startFrame + position + size + textColor + showBox + boxColor + + + diff --git a/stuff/profiles/layouts/fxs/fxs.lst b/stuff/profiles/layouts/fxs/fxs.lst index db7accc..3c42800 100644 --- a/stuff/profiles/layouts/fxs/fxs.lst +++ b/stuff/profiles/layouts/fxs/fxs.lst @@ -144,6 +144,7 @@ STD_inopnCloudsFx STD_particlesFx STD_iwa_TiledParticlesFx + STD_iwa_TimeCodeFx STD_colorEmbossFx diff --git a/toonz/sources/stdfx/CMakeLists.txt b/toonz/sources/stdfx/CMakeLists.txt index d2b8fab..aa6bad6 100644 --- a/toonz/sources/stdfx/CMakeLists.txt +++ b/toonz/sources/stdfx/CMakeLists.txt @@ -73,6 +73,7 @@ set(HEADERS iwa_pnperspectivefx.h iwa_soapbubblefx.h iwa_bokehfx.h + iwa_timecodefx.h ) set(SOURCES @@ -250,6 +251,7 @@ set(SOURCES ${SDKROOT}/kiss_fft130/kiss_fft.c ${SDKROOT}/kiss_fft130/tools/kiss_fftnd.c iwa_bokehfx.cpp + iwa_timecodefx.cpp ) set(OBJCSOURCES diff --git a/toonz/sources/stdfx/iwa_timecodefx.cpp b/toonz/sources/stdfx/iwa_timecodefx.cpp new file mode 100644 index 0000000..21f7dc5 --- /dev/null +++ b/toonz/sources/stdfx/iwa_timecodefx.cpp @@ -0,0 +1,178 @@ +#include "iwa_timecodefx.h" + +#include "tparamuiconcept.h" + +#include +#include +#include +#include +//------------------------------------------------------------------ + +Iwa_TimeCodeFx::Iwa_TimeCodeFx() + : m_displayType(new TIntEnumParam(TYPE_HHMMSSFF, "HH;MM;SS;FF")) + , m_frameRate(24) + , m_startFrame(0) + , m_position(TPointD(0.0, 0.0)) + , m_size(25) + , m_textColor(TPixel32::White) + , m_showBox(true) + , m_boxColor(TPixel32::Black) { + m_displayType->addItem(TYPE_FRAME, "Frame Number"); + m_position->getX()->setMeasureName("fxLength"); + m_position->getY()->setMeasureName("fxLength"); + m_size->setMeasureName("fxLength"); + m_size->setValueRange(0.1, (std::numeric_limits::max)()); + + bindParam(this, "displayType", m_displayType); + bindParam(this, "frameRate", m_frameRate); + bindParam(this, "startFrame", m_startFrame); + bindParam(this, "position", m_position); + bindParam(this, "size", m_size); + bindParam(this, "textColor", m_textColor); + bindParam(this, "showBox", m_showBox); + bindParam(this, "boxColor", m_boxColor); +} + +//------------------------------------------------------------------ + +bool Iwa_TimeCodeFx::doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &ri) { + bBox = TConsts::infiniteRectD; + return true; +} + +//------------------------------------------------------------------ + +void Iwa_TimeCodeFx::doCompute(TTile &tile, double frame, + const TRenderSettings &ri) { + double fac = sqrt(fabs(ri.m_affine.det())); + int size = (int)(fac * fabs(m_size->getValue(frame))); + TPoint point = convert( + fac * m_position->getValue(frame) - + (tile.m_pos + tile.getRaster()->getCenterD()) + + TPointD(ri.m_cameraBox.getLx() / 2.0, ri.m_cameraBox.getLy() / 2.0)); +#ifdef _WIN32 + QFont font("Arial", size); +#else + QFont font("Helvetica", size); +#endif + font.setWeight(QFont::Normal); + QFontMetrics fm(font); + QString timeCodeStr = getTimeCodeStr(frame, ri); + int width = fm.width(timeCodeStr); + int height = fm.height(); + + QImage img(width, height, QImage::Format_ARGB32); + + if (m_showBox->getValue()) { + TPixel32 boxColor = m_boxColor->getValue(frame); + img.fill(QColor((int)boxColor.r, (int)boxColor.g, (int)boxColor.b, + (int)boxColor.m)); + } else + img.fill(Qt::transparent); + + QPainter painter(&img); + TPixel32 color = m_textColor->getValue(frame); + painter.setPen( + QColor((int)color.r, (int)color.g, (int)color.b, (int)color.m)); + painter.setFont(font); + painter.drawText(QPoint(0, fm.ascent()), timeCodeStr); + + tile.getRaster()->clear(); + TRaster32P ras32 = (TRaster32P)tile.getRaster(); + TRaster64P ras64 = (TRaster64P)tile.getRaster(); + if (ras32) + putTimeCodeImage(ras32, point, img); + else if (ras64) + putTimeCodeImage(ras64, point, img); + else + throw TException("Iwa_TimeCodeFx: unsupported Pixel Type"); +} + +//------------------------------------------------------------------ + +void Iwa_TimeCodeFx::getParamUIs(TParamUIConcept *&concepts, int &length) { + concepts = new TParamUIConcept[length = 1]; + + concepts[0].m_type = TParamUIConcept::POINT; + concepts[0].m_label = "Position"; + concepts[0].m_params.push_back(m_position); +} + +//------------------------------------------------------------------ + +std::string Iwa_TimeCodeFx::getAlias(double frame, + const TRenderSettings &info) const { + std::string alias = getFxType(); + alias += "["; + + std::string paramalias(""); + for (int i = 0; i < getParams()->getParamCount(); ++i) { + TParam *param = getParams()->getParam(i); + paramalias += param->getName() + "=" + param->getValueAlias(frame, 3); + } + + return alias + std::to_string(frame) + "," + std::to_string(getIdentifier()) + + paramalias + "]"; +} + +//------------------------------------------------------------------ + +QString Iwa_TimeCodeFx::getTimeCodeStr(double frame, + const TRenderSettings &ri) { + int f = (int)frame + m_startFrame->getValue(); + + if (m_displayType->getValue() == TYPE_HHMMSSFF) { + bool neg = (f < 0); + f = abs(f); + int fps = m_frameRate->getValue(); + int hh = f / (fps * 60 * 60); + f -= hh * fps * 60 * 60; + int mm = f / (fps * 60); + f -= mm * fps * 60; + int ss = f / fps; + int ff = f % fps; + return QString((neg) ? "-" : "") + + QString::number(hh).rightJustified(2, '0') + QString(";") + + QString::number(mm).rightJustified(2, '0') + QString(";") + + QString::number(ss).rightJustified(2, '0') + QString(";") + + QString::number(ff).rightJustified(2, '0'); + } else { // TYPE_FRAMENUMBER + f += 1; // starting from "000001" with no frame offset. + return QString((f < 0) ? "-" : "") + + QString::number(abs(f)).rightJustified(6, '0'); + } +} + +//------------------------------------------------------------------ + +template +void Iwa_TimeCodeFx::putTimeCodeImage(const RASTER srcRas, TPoint &pos, + QImage &img) { + for (int j = 0; j < img.height(); j++) { + int rasY = pos.y + j; + if (rasY < 0) continue; + if (srcRas->getLy() <= rasY) break; + + PIXEL *pix = srcRas->pixels(rasY); + QRgb *img_p = (QRgb *)img.scanLine(img.height() - j - 1); + for (int i = 0; i < img.width(); i++, img_p++) { + int rasX = pos.x + i; + if (rasX < 0) continue; + if (srcRas->getLx() <= rasX) break; + + pix[rasX].r = (typename PIXEL::Channel)( + qRed(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX); + pix[rasX].g = (typename PIXEL::Channel)( + qGreen(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX); + pix[rasX].b = (typename PIXEL::Channel)( + qBlue(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX); + pix[rasX].m = (typename PIXEL::Channel)( + qAlpha(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX); + } + } +} + +//============================================================================== + +FX_PLUGIN_IDENTIFIER(Iwa_TimeCodeFx, "iwa_TimeCodeFx"); diff --git a/toonz/sources/stdfx/iwa_timecodefx.h b/toonz/sources/stdfx/iwa_timecodefx.h new file mode 100644 index 0000000..a62c08b --- /dev/null +++ b/toonz/sources/stdfx/iwa_timecodefx.h @@ -0,0 +1,53 @@ +#pragma once + +#ifndef IWA_TIMECODEFX_H +#define IWA_TIMECODEFX_H + +#include "stdfx.h" +#include "tfxparam.h" +#include "tparamset.h" + +//****************************************************************** +// Iwa_TimeCode Fx class +//****************************************************************** + +class Iwa_TimeCodeFx final : public TStandardZeraryFx { + FX_PLUGIN_DECLARATION(Iwa_TimeCodeFx) + +public: + TIntEnumParamP m_displayType; // - SMPTE HH;MM;SS;FF + // - FrameNumber [######] + TIntParamP m_frameRate; + TIntParamP m_startFrame; + TPointParamP m_position; + TDoubleParamP m_size; + TPixelParamP m_textColor; + TBoolParamP m_showBox; + TPixelParamP m_boxColor; + + QString getTimeCodeStr(double frame, const TRenderSettings &ri); + + template + void putTimeCodeImage(const RASTER srcRas, TPoint &pos, QImage &img); + +public: + enum { TYPE_HHMMSSFF, TYPE_FRAME }; + + Iwa_TimeCodeFx(); + + bool isZerary() const override { return true; } + + bool canHandle(const TRenderSettings &info, double frame) override { + return true; + } + + bool doGetBBox(double frame, TRectD &bBox, + const TRenderSettings &ri) override; + void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override; + void getParamUIs(TParamUIConcept *&concepts, int &length) override; + + std::string getAlias(double frame, + const TRenderSettings &info) const override; +}; + +#endif \ No newline at end of file