26 double clamp_margin(
double value) {
36 Blur::Blur() : horizontal_radius(6.0), vertical_radius(6.0), sigma(3.0), iterations(3.0),
37 left(0.0), top(0.0), right(0.0), bottom(0.0),
40 init_effect_details();
45 horizontal_radius(new_horizontal_radius), vertical_radius(new_vertical_radius),
46 sigma(new_sigma), iterations(new_iterations),
47 left(0.0), top(0.0), right(0.0), bottom(0.0),
51 init_effect_details();
57 horizontal_radius(new_horizontal_radius), vertical_radius(new_vertical_radius),
58 sigma(new_sigma), iterations(new_iterations),
59 left(new_left), top(new_top), right(new_right), bottom(new_bottom),
63 init_effect_details();
67 void Blur::init_effect_details()
82 std::shared_ptr<openshot::Frame>
Blur::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
85 std::shared_ptr<QImage> frame_image = frame->GetImage();
94 int w = frame_image->width();
95 int h = frame_image->height();
96 if (w <= 0 || h <= 0 || iteration_value <= 0)
100 QRect area(QPoint(0, 0), frame_image->size());
101 area = area.marginsRemoved({
107 area = area.intersected(QRect(QPoint(0, 0), frame_image->size()));
112 QImage image_copy = frame_image->copy(area);
113 std::shared_ptr<QImage> blur_image = std::make_shared<QImage>(image_copy);
114 std::shared_ptr<QImage> frame_image_2 = std::make_shared<QImage>(image_copy);
115 const int area_w = blur_image->width();
116 const int area_h = blur_image->height();
117 const int horizontal_area_radius = std::min(std::max(0, horizontal_radius_value), std::max(0, area_w - 1));
118 const int vertical_area_radius = std::min(std::max(0, vertical_radius_value), std::max(0, area_h - 1));
119 bool blurred =
false;
122 for (
int iteration = 0; iteration < iteration_value; ++iteration)
125 if (horizontal_area_radius > 0.0) {
127 boxBlurH(blur_image->bits(), frame_image_2->bits(), area_w, area_h, horizontal_area_radius);
130 blur_image.swap(frame_image_2);
135 if (vertical_area_radius > 0.0) {
137 boxBlurT(blur_image->bits(), frame_image_2->bits(), area_w, area_h, vertical_area_radius);
140 blur_image.swap(frame_image_2);
146 QPainter painter(frame_image.get());
147 painter.drawImage(area, *blur_image);
161 std::shared_ptr<QImage> mask_image, int64_t frame_number)
const {
163 if (!original_image || !effected_image || !mask_image)
165 if (original_image->size() != effected_image->size() || effected_image->size() != mask_image->size())
168 unsigned char* original_pixels =
reinterpret_cast<unsigned char*
>(original_image->bits());
169 unsigned char* effected_pixels =
reinterpret_cast<unsigned char*
>(effected_image->bits());
170 unsigned char* mask_pixels =
reinterpret_cast<unsigned char*
>(mask_image->bits());
171 const int pixel_count = effected_image->width() * effected_image->height();
173 #pragma omp parallel for schedule(static)
174 for (
int i = 0; i < pixel_count; ++i) {
175 const int idx = i * 4;
176 float factor =
static_cast<float>(qGray(mask_pixels[idx], mask_pixels[idx + 1], mask_pixels[idx + 2])) / 255.0f;
178 factor = 1.0f - factor;
180 factor = factor * factor;
181 const float inverse = 1.0f - factor;
184 effected_pixels[idx] =
static_cast<unsigned char>(
185 (original_pixels[idx] * inverse) + (effected_pixels[idx] * factor));
186 effected_pixels[idx + 1] =
static_cast<unsigned char>(
187 (original_pixels[idx + 1] * inverse) + (effected_pixels[idx + 1] * factor));
188 effected_pixels[idx + 2] =
static_cast<unsigned char>(
189 (original_pixels[idx + 2] * inverse) + (effected_pixels[idx + 2] * factor));
190 effected_pixels[idx + 3] = original_pixels[idx + 3];
196 void Blur::boxBlurH(
unsigned char *scl,
unsigned char *tcl,
int w,
int h,
int r) {
197 const float iarr = 1.0f / (r + r + 1);
199 #pragma omp parallel for shared(scl, tcl)
200 for (
int i = 0; i < h; ++i) {
201 const unsigned char* src = scl + i * w * 4;
202 unsigned char* dst = tcl + i * w * 4;
204 const unsigned char* first = src;
205 const unsigned char* last = src + (w - 1) * 4;
208 for (
int c = 0; c < 4; ++c)
209 val[c] = (r + 1) * first[c];
210 for (
int j = 0; j < r; ++j) {
211 const unsigned char* p = src + j * 4;
212 for (
int c = 0; c < 4; ++c)
217 for (
int j = 0; j <= r; ++j, ++ri) {
218 const unsigned char* add = src + ri * 4;
219 unsigned char* out = dst + j * 4;
220 for (
int c = 0; c < 4; ++c) {
221 val[c] += add[c] - first[c];
222 out[c] = (
unsigned char)(val[c] * iarr + 0.5f);
225 for (
int j = r + 1; j < w - r; ++j, ++li, ++ri) {
226 const unsigned char* add = src + ri * 4;
227 const unsigned char* sub = src + li * 4;
228 unsigned char* out = dst + j * 4;
229 for (
int c = 0; c < 4; ++c) {
230 val[c] += add[c] - sub[c];
231 out[c] = (
unsigned char)(val[c] * iarr + 0.5f);
234 for (
int j = w - r; j < w; ++j, ++li) {
235 const unsigned char* sub = src + li * 4;
236 unsigned char* out = dst + j * 4;
237 for (
int c = 0; c < 4; ++c) {
238 val[c] += last[c] - sub[c];
239 out[c] = (
unsigned char)(val[c] * iarr + 0.5f);
245 void Blur::boxBlurT(
unsigned char *scl,
unsigned char *tcl,
int w,
int h,
int r) {
246 const float iarr = 1.0f / (r + r + 1);
247 const int stride = w * 4;
249 #pragma omp parallel for shared(scl, tcl)
250 for (
int i = 0; i < w; ++i) {
251 const unsigned char* col_src = scl + i * 4;
252 unsigned char* col_dst = tcl + i * 4;
254 const unsigned char* first = col_src;
255 const unsigned char* last = col_src + (h - 1) * stride;
258 for (
int c = 0; c < 4; ++c)
259 val[c] = (r + 1) * first[c];
260 for (
int j = 0; j < r; ++j) {
261 const unsigned char* p = col_src + j * stride;
262 for (
int c = 0; c < 4; ++c)
267 for (
int j = 0; j <= r; ++j, ++ri) {
268 const unsigned char* add = col_src + ri * stride;
269 unsigned char* out = col_dst + j * stride;
270 for (
int c = 0; c < 4; ++c) {
271 val[c] += add[c] - first[c];
272 out[c] = (
unsigned char)(val[c] * iarr + 0.5f);
275 for (
int j = r + 1; j < h - r; ++j, ++li, ++ri) {
276 const unsigned char* add = col_src + ri * stride;
277 const unsigned char* sub = col_src + li * stride;
278 unsigned char* out = col_dst + j * stride;
279 for (
int c = 0; c < 4; ++c) {
280 val[c] += add[c] - sub[c];
281 out[c] = (
unsigned char)(val[c] * iarr + 0.5f);
284 for (
int j = h - r; j < h; ++j, ++li) {
285 const unsigned char* sub = col_src + li * stride;
286 unsigned char* out = col_dst + j * stride;
287 for (
int c = 0; c < 4; ++c) {
288 val[c] += last[c] - sub[c];
289 out[c] = (
unsigned char)(val[c] * iarr + 0.5f);
332 catch (
const std::exception& e)
335 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
346 if (!root[
"horizontal_radius"].isNull())
348 if (!root[
"vertical_radius"].isNull())
350 if (!root[
"sigma"].isNull())
352 if (!root[
"iterations"].isNull())
354 if (!root[
"left"].isNull())
356 if (!root[
"top"].isNull())
358 if (!root[
"right"].isNull())
360 if (!root[
"bottom"].isNull())
362 if (!root[
"mask_mode"].isNull())
386 return root.toStyledString();