48 : x(-0.5), y(-0.5), brightness(1.0), size(1.0), spread(1.0),
49 color(
Color(
"#ffffff"))
51 init_effect_details();
63 : x(xPos), y(yPos), brightness(intensity), size(scale),
64 spread(spreadVal), color(tint)
66 init_effect_details();
73 void LensFlare::init_effect_details()
78 info.
description =
"Simulate sunlight hitting a lens with flares and spectral colors.";
91 static inline QRgb blendAdd(QRgb dst,
const QColor &c,
float p)
93 int dr = (255 - qRed(dst)) * p * c.redF();
94 int dg = (255 - qGreen(dst)) * p * c.greenF();
95 int db = (255 - qBlue(dst)) * p * c.blueF();
96 int da = (255 - qAlpha(dst)) * p * c.alphaF();
98 std::clamp(qRed(dst) + dr, 0, 255),
99 std::clamp(qGreen(dst) + dg, 0, 255),
100 std::clamp(qBlue(dst) + db, 0, 255),
101 std::clamp(qAlpha(dst) + da, 0, 255)
106 static QColor shifted_hsv(
const QColor &base,
float h_shift,
107 float s_scale,
float v_scale,
108 float a_scale = 1.0f)
111 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
116 base.getHsvF(&h, &s, &v, &a);
119 h =
static_cast<decltype(h)
>(std::fmod(
static_cast<double>(h + h_shift + 1.0), 1.0));
120 s = std::clamp(s * s_scale,
static_cast<decltype(s)
>(0.0),
static_cast<decltype(s)
>(1.0));
121 v = std::clamp(v * v_scale,
static_cast<decltype(v)
>(0.0),
static_cast<decltype(v)
>(1.0));
122 a = std::clamp(a * a_scale,
static_cast<decltype(a)
>(0.0),
static_cast<decltype(a)
>(1.0));
125 out.setHsvF(h, s, v, a);
130 static void init_reflectors(std::vector<Reflect> &refs,
float DX,
float DY,
131 int width,
int height,
const QColor &tint,
134 float halfW = width * 0.5f;
135 float halfH = height * 0.5f;
138 struct Rdef {
int type;
float fx, fy, fsize, r, g, b; };
140 {1, 0.6699f, 0.6699f, 0.027f, 0.0f, 14/255.0f, 113/255.0f},
141 {1, 0.2692f, 0.2692f, 0.010f, 90/255.0f, 181/255.0f, 142/255.0f},
142 {1, -0.0112f, -0.0112f, 0.005f, 56/255.0f, 140/255.0f, 106/255.0f},
143 {2, 0.6490f, 0.6490f, 0.031f, 9/255.0f, 29/255.0f, 19/255.0f},
144 {2, 0.4696f, 0.4696f, 0.015f, 24/255.0f, 14/255.0f, 0.0f},
145 {2, 0.4087f, 0.4087f, 0.037f, 24/255.0f, 14/255.0f, 0.0f},
146 {2, -0.2003f, -0.2003f, 0.022f, 42/255.0f, 19/255.0f, 0.0f},
147 {2, -0.4103f, -0.4103f, 0.025f, 0.0f, 9/255.0f, 17/255.0f},
148 {2, -0.4503f, -0.4503f, 0.058f, 10/255.0f, 4/255.0f, 0.0f},
149 {2, -0.5112f, -0.5112f, 0.017f, 5/255.0f, 5/255.0f, 14/255.0f},
150 {2, -1.4960f, -1.4960f, 0.20f, 9/255.0f, 4/255.0f, 0.0f},
151 {2, -1.4960f, -1.4960f, 0.50f, 9/255.0f, 4/255.0f, 0.0f},
152 {3, 0.4487f, 0.4487f, 0.075f, 34/255.0f, 19/255.0f, 0.0f},
153 {3, 1.0000f, 1.0000f, 0.10f, 14/255.0f, 26/255.0f, 0.0f},
154 {3, -1.3010f, -1.3010f, 0.039f, 10/255.0f, 25/255.0f, 13/255.0f},
155 {4, 1.3090f, 1.3090f, 0.19f, 9/255.0f, 0.0f, 17/255.0f},
156 {4, 1.3090f, 1.3090f, 0.195f, 9/255.0f, 16/255.0f, 5/255.0f},
157 {4, 1.3090f, 1.3090f, 0.20f, 17/255.0f, 4/255.0f, 0.0f},
158 {4, -1.3010f, -1.3010f, 0.038f, 17/255.0f, 4/255.0f, 0.0f}
162 refs.reserve(std::size(defs));
163 bool whiteTint = (tint.saturationF() < 0.01f);
165 for (
auto &d : defs) {
168 r.
size = d.fsize * matt * S;
169 r.
xp = halfW + d.fx * DX;
170 r.
yp = halfH + d.fy * DY;
172 QColor base = QColor::fromRgbF(d.r, d.g, d.b, 1.0f);
173 r.
col = whiteTint ? base
184 static void apply_reflector(QRgb &pxl,
const Reflect &r,
int cx,
int cy)
186 float d = std::hypot(r.
xp - cx, r.
yp - cy);
194 pxl = blendAdd(pxl, r.
col, p);
198 p = (r.
size - d) / (r.
size * 0.15f);
200 p = std::min(p, 1.0f);
201 pxl = blendAdd(pxl, r.
col, p);
205 p = (r.
size - d) / (r.
size * 0.12f);
207 p = std::min(p, 1.0f);
208 p = 1.0f - (p * 0.12f);
209 pxl = blendAdd(pxl, r.
col, p);
213 p = std::abs((d - r.
size) / (r.
size * 0.04f));
215 pxl = blendAdd(pxl, r.
col, 1.0f - p);
222 std::shared_ptr<openshot::Frame>
225 auto img = frame->GetImage();
226 int w = img->width();
227 int h = img->height();
237 float halfW = w * 0.5f, halfH = h * 0.5f;
238 float px = (X * 0.5f + 0.5f) * w;
239 float py = (Y * 0.5f + 0.5f) * h;
240 float DX = (halfW - px) * SP;
241 float DY = (halfH - py) * SP;
244 QColor tint = QColor::fromRgbF(
253 float scolor = matt * 0.0375f * S;
254 float sglow = matt * 0.078125f * S;
255 float sinner = matt * 0.1796875f * S;
256 float souter = matt * 0.3359375f * S;
257 float shalo = matt * 0.084375f * S;
260 auto tintify = [&](
float br,
float bg,
float bb) {
261 return QColor::fromRgbF(
269 QColor c_color = tintify(239/255.0f, 239/255.0f, 239/255.0f);
270 QColor c_glow = tintify(245/255.0f, 245/255.0f, 245/255.0f);
271 QColor c_inner = tintify(1.0f, 38/255.0f, 43/255.0f);
272 QColor c_outer = tintify(69/255.0f, 59/255.0f, 64/255.0f);
273 QColor c_halo = tintify(80/255.0f, 15/255.0f, 4/255.0f);
276 std::vector<Reflect> refs;
277 init_reflectors(refs, DX, DY, w, h, tint, S);
280 QImage overlay(w, h, QImage::Format_ARGB32);
281 overlay.fill(Qt::transparent);
283 #pragma omp parallel for schedule(dynamic)
284 for (
int yy = 0; yy < h; ++yy) {
285 QRgb *scan =
reinterpret_cast<QRgb*
>(overlay.scanLine(yy));
286 for (
int xx = 0; xx < w; ++xx) {
289 float d = std::hypot(xx - px, yy - py);
293 float p = (scolor - d)/scolor; p*=p;
294 QRgb tmp = blendAdd(qRgba(r,g,b,0), c_color, p);
295 r = qRed(tmp); g = qGreen(tmp); b = qBlue(tmp);
299 float p = (sglow - d)/sglow; p*=p;
300 QRgb tmp = blendAdd(qRgba(r,g,b,0), c_glow, p);
301 r = qRed(tmp); g = qGreen(tmp); b = qBlue(tmp);
305 float p = (sinner - d)/sinner; p*=p;
306 QRgb tmp = blendAdd(qRgba(r,g,b,0), c_inner, p);
307 r = qRed(tmp); g = qGreen(tmp); b = qBlue(tmp);
311 float p = (souter - d)/souter;
312 QRgb tmp = blendAdd(qRgba(r,g,b,0), c_outer, p);
313 r = qRed(tmp); g = qGreen(tmp); b = qBlue(tmp);
317 float p = std::abs((d - shalo)/(shalo*0.07f));
319 QRgb tmp = blendAdd(qRgba(r,g,b,0), c_halo, 1.0f-p);
320 r = qRed(tmp); g = qGreen(tmp); b = qBlue(tmp);
324 for (
auto &rf : refs) {
325 QRgb tmp = qRgba(r,g,b,0);
326 apply_reflector(tmp, rf, xx, yy);
327 r = qRed(tmp); g = qGreen(tmp); b = qBlue(tmp);
331 int a = std::max({r,g,b});
332 scan[xx] = qRgba(r,g,b,a);
337 QImage origAlpha = img->convertToFormat(QImage::Format_Alpha8);
340 QPainter p(img.get());
341 p.setCompositionMode(QPainter::CompositionMode_Plus);
343 p.drawImage(0, 0, overlay);
347 QImage finalA(w,h, QImage::Format_Alpha8);
348 auto overlayA = overlay.convertToFormat(QImage::Format_Alpha8);
350 for (
int yy=0; yy<h; ++yy) {
351 uchar *oL = origAlpha.scanLine(yy);
352 uchar *fL = overlayA.scanLine(yy);
353 uchar *nL = finalA.scanLine(yy);
354 for (
int xx=0; xx<w; ++xx) {
355 float oa = oL[xx]/255.0f;
356 float fa = (fL[xx]/255.0f)*I;
357 nL[xx] =
static_cast<uchar
>(std::clamp(std::max(oa,fa)*255.0f, 0.0f, 255.0f));
360 img->setAlphaChannel(finalA);
365 std::shared_ptr<openshot::Frame>
368 return GetFrame(std::make_shared<openshot::Frame>(), frame_number);
395 catch (...) {
throw InvalidJSON(
"LensFlare JSON"); }
424 return r.toStyledString();