Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Antalik <richardantalik@gmail.com>2020-11-03 00:15:52 +0300
committerRichard Antalik <richardantalik@gmail.com>2020-11-03 00:15:52 +0300
commite1665c3d31906aed195bfdc10a4dd7582f31b1f5 (patch)
tree98da1c84116144388be1beb81e762ccaee3b8aab /source/blender
parent6b3eca661dc2d0791ecf07b94ef51f13c3aa6d25 (diff)
VSE: Media transform redesign
This patch changes behavior of strip transform and crop feature. Purpose of this change is to allow display arbitrary portion of input image, simplify user interface and workflow. Offset and Crop values in old files are converted in versioning. Offset animation is also converted. Crop animation and animation of crop or offset enable properties is not taken into account Changes in behavior and interface: - If image is added to timeline it is scaled to fit inside preview area while maintaining aspect ratio. Image is centered. This is considered as a baseline for further transformation. - Scale and rotation was added, so it is possible to transform image at it's original resolution. - Crop will not affect image transformation (does not move image). - Values of Crop and Transform Position are in pixels, these values are corrected if preview is fraction of project resolution. - Transform and Mirror panel has been removed and new Transform panel and Crop panel is moved to Adjust panel. Mirror is now part of new Transform panel. Technical changes: - Preprocessing stage must work on duplicated image, because original is cached. Previously Crop and Offset could run at once and required only one duplication of image. This is not the case with new algorithms, so duplication on demand is implemented. Transformation can read original image and will output new image that is safe to modify. It should be possible to add crop step to transform algorithm, so that Crop won't require previous duplication though. - Use Crop and Use Translation checkboxes were removed. Individual values are compared to default values to check if image needs to be processed. In case of transform this will be done also if resolution of source. Reviewed By: sergey Differential Revision: https://developer.blender.org/D8393
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h8
-rw-r--r--source/blender/blenlib/intern/math_matrix.c53
-rw-r--r--source/blender/blenloader/intern/versioning_290.c169
-rw-r--r--source/blender/imbuf/IMB_imbuf.h2
-rw-r--r--source/blender/imbuf/intern/rectop.c77
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h7
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c69
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h2
-rw-r--r--source/blender/sequencer/intern/sequencer.c296
10 files changed, 533 insertions, 152 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 59e6b5629f0..289caae7f94 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 1
+#define BLENDER_FILE_SUBVERSION 2
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 9f6c56d698a..d971f48c4cf 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -322,9 +322,13 @@ void mat4_to_size(float size[3], const float M[4][4]);
void mat4_to_size_fix_shear(float size[3], const float M[4][4]);
+void translate_m3(float mat[3][3], float tx, float ty);
void translate_m4(float mat[4][4], float tx, float ty, float tz);
+void rotate_m3(float mat[3][3], const float angle);
void rotate_m4(float mat[4][4], const char axis, const float angle);
+void rescale_m3(float mat[3][3], const float scale[2]);
void rescale_m4(float mat[4][4], const float scale[3]);
+void transform_pivot_set_m3(float mat[3][3], const float pivot[2]);
void transform_pivot_set_m4(float mat[4][4], const float pivot[3]);
void mat3_to_rot_size(float rot[3][3], float size[3], const float mat3[3][3]);
@@ -334,6 +338,10 @@ void mat4_decompose(float loc[3], float quat[4], float size[3], const float wmat
void mat3_polar_decompose(const float mat3[3][3], float r_U[3][3], float r_P[3][3]);
+void loc_rot_size_to_mat3(float R[3][3],
+ const float loc[2],
+ const float angle,
+ const float size[2]);
void loc_rot_size_to_mat4(float R[4][4],
const float loc[3],
const float rot[3][3],
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index cf6945529f2..9769d2baf9b 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -2223,6 +2223,12 @@ void scale_m4_fl(float R[4][4], float scale)
R[3][0] = R[3][1] = R[3][2] = 0.0;
}
+void translate_m3(float mat[3][3], float tx, float ty)
+{
+ mat[2][0] += (tx * mat[0][0] + ty * mat[1][0]);
+ mat[2][1] += (tx * mat[0][1] + ty * mat[1][1]);
+}
+
void translate_m4(float mat[4][4], float Tx, float Ty, float Tz)
{
mat[3][0] += (Tx * mat[0][0] + Ty * mat[1][0] + Tz * mat[2][0]);
@@ -2230,6 +2236,18 @@ void translate_m4(float mat[4][4], float Tx, float Ty, float Tz)
mat[3][2] += (Tx * mat[0][2] + Ty * mat[1][2] + Tz * mat[2][2]);
}
+void rotate_m3(float mat[3][3], const float angle)
+{
+ const float angle_cos = cosf(angle);
+ const float angle_sin = sinf(angle);
+
+ for (int col = 0; col < 3; col++) {
+ float temp = angle_cos * mat[0][col] + angle_sin * mat[1][col];
+ mat[1][col] = -angle_sin * mat[0][col] + angle_cos * mat[1][col];
+ mat[0][col] = temp;
+ }
+}
+
/* TODO: enum for axis? */
/**
* Rotate a matrix in-place.
@@ -2275,6 +2293,12 @@ void rotate_m4(float mat[4][4], const char axis, const float angle)
}
}
+void rescale_m3(float mat[3][3], const float scale[2])
+{
+ mul_v3_fl(mat[0], scale[0]);
+ mul_v3_fl(mat[1], scale[1]);
+}
+
/** Scale a matrix in-place. */
void rescale_m4(float mat[4][4], const float scale[3])
{
@@ -2305,6 +2329,20 @@ void transform_pivot_set_m4(float mat[4][4], const float pivot[3])
mul_m4_m4m4(mat, mat, tmat);
}
+void transform_pivot_set_m3(float mat[3][3], const float pivot[2])
+{
+ float tmat[3][3];
+
+ unit_m3(tmat);
+
+ copy_v2_v2(tmat[2], pivot);
+ mul_m3_m3m3(mat, tmat, mat);
+
+ /* invert the matrix */
+ negate_v2(tmat[2]);
+ mul_m3_m3m3(mat, mat, tmat);
+}
+
void blend_m3_m3m3(float out[3][3],
const float dst[3][3],
const float src[3][3],
@@ -2485,6 +2523,21 @@ bool equals_m4m4(const float mat1[4][4], const float mat2[4][4])
}
/**
+ * Make a 3x3 matrix out of 3 transform components.
+ * Matrices are made in the order: `loc * rot * scale`
+ */
+void loc_rot_size_to_mat3(float R[3][3],
+ const float loc[2],
+ const float angle,
+ const float size[2])
+{
+ unit_m3(R);
+ translate_m3(R, loc[0], loc[1]);
+ rotate_m3(R, angle);
+ rescale_m3(R, size);
+}
+
+/**
* Make a 4x4 matrix out of 3 transform components.
* Matrices are made in the order: `scale * rot * loc`
*/
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 37987b2c31d..d3d6eeeb3dd 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -44,12 +44,14 @@
#include "DNA_rigidbody_types.h"
#include "DNA_screen_types.h"
#include "DNA_shader_fx_types.h"
+#include "DNA_space_types.h"
#include "DNA_tracking_types.h"
#include "DNA_workspace_types.h"
#include "BKE_animsys.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
+#include "BKE_fcurve.h"
#include "BKE_gpencil.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -59,12 +61,154 @@
#include "MEM_guardedalloc.h"
+#include "RNA_access.h"
+
+#include "SEQ_sequencer.h"
+
#include "BLO_readfile.h"
#include "readfile.h"
/* Make preferences read-only, use versioning_userdef.c. */
#define U (*((const UserDef *)&U))
+/* image_size is width or height depending what RNA property is converted - X or Y. */
+static void seq_convert_transform_animation(const Scene *scene,
+ const char *path,
+ const int image_size)
+{
+ if (scene->adt == NULL || scene->adt->action == NULL) {
+ return;
+ }
+
+ FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0);
+ if (fcu != NULL && !BKE_fcurve_is_empty(fcu)) {
+ BezTriple *bezt = fcu->bezt;
+ for (int i = 0; i < fcu->totvert; i++, bezt++) {
+ /* Same math as with old_image_center_*, but simplified. */
+ bezt->vec[1][1] = image_size / 2 + bezt->vec[1][1] - scene->r.xsch / 2;
+ }
+ }
+}
+
+static void seq_convert_transform_crop(const Scene *scene,
+ Sequence *seq,
+ const eSpaceSeq_Proxy_RenderSize render_size)
+{
+ StripCrop *c = seq->strip->crop;
+ StripTransform *t = seq->strip->transform;
+ int old_image_center_x = scene->r.xsch / 2;
+ int old_image_center_y = scene->r.ysch / 2;
+ int image_size_x = scene->r.xsch;
+ int image_size_y = scene->r.ysch;
+
+ /* Hardcoded legacy bit-flags which has been removed. */
+ const uint32_t use_transform_flag = (1 << 16);
+ const uint32_t use_crop_flag = (1 << 17);
+
+ const StripElem *s_elem = BKE_sequencer_give_stripelem(seq, seq->start);
+ if (s_elem != NULL) {
+ image_size_x = s_elem->orig_width;
+ image_size_y = s_elem->orig_height;
+
+ if (SEQ_can_use_proxy(seq, SEQ_rendersize_to_proxysize(render_size))) {
+ image_size_x /= BKE_sequencer_rendersize_to_scale_factor(render_size);
+ image_size_y /= BKE_sequencer_rendersize_to_scale_factor(render_size);
+ }
+ }
+
+ /* Default scale. */
+ if (t->scale_x == 0.0f && t->scale_y == 0.0f) {
+ t->scale_x = 1.0f;
+ t->scale_y = 1.0f;
+ }
+
+ /* Clear crop if it was unused. This must happen before converting values. */
+ if ((seq->flag & use_crop_flag) == 0) {
+ c->bottom = c->top = c->left = c->right = 0;
+ }
+
+ if ((seq->flag & use_transform_flag) == 0) {
+ t->xofs = t->yofs = 0;
+
+ /* Reverse scale to fit for strips not using offset. */
+ float project_aspect = (float)scene->r.xsch / (float)scene->r.ysch;
+ float image_aspect = (float)image_size_x / (float)image_size_y;
+ if (project_aspect > image_aspect) {
+ t->scale_x = project_aspect / image_aspect;
+ }
+ else {
+ t->scale_y = image_aspect / project_aspect;
+ }
+ }
+
+ if ((seq->flag & use_crop_flag) != 0 && (seq->flag & use_transform_flag) == 0) {
+ /* Calculate image offset. */
+ float s_x = scene->r.xsch / image_size_x;
+ float s_y = scene->r.ysch / image_size_y;
+ old_image_center_x += c->right * s_x - c->left * s_x;
+ old_image_center_y += c->top * s_y - c->bottom * s_y;
+
+ /* Convert crop to scale. */
+ int cropped_image_size_x = image_size_x - c->right - c->left;
+ int cropped_image_size_y = image_size_y - c->top - c->bottom;
+ c->bottom = c->top = c->left = c->right = 0;
+ t->scale_x *= (float)image_size_x / (float)cropped_image_size_x;
+ t->scale_y *= (float)image_size_y / (float)cropped_image_size_y;
+ }
+
+ if ((seq->flag & use_transform_flag) != 0) {
+ /* Convert image offset. */
+ old_image_center_x = image_size_x / 2 - c->left + t->xofs;
+ old_image_center_y = image_size_y / 2 - c->bottom + t->yofs;
+
+ /* Preserve original image size. */
+ t->scale_x = t->scale_y = MAX2((float)image_size_x / (float)scene->r.xsch,
+ (float)image_size_y / (float)scene->r.ysch);
+
+ /* Convert crop. */
+ if ((seq->flag & use_crop_flag) != 0) {
+ c->top /= t->scale_x;
+ c->bottom /= t->scale_x;
+ c->left /= t->scale_x;
+ c->right /= t->scale_x;
+ }
+ }
+
+ t->xofs = old_image_center_x - scene->r.xsch / 2;
+ t->yofs = old_image_center_y - scene->r.ysch / 2;
+
+ /* Convert offset animation, but only if crop is not used. */
+ if ((seq->flag & use_transform_flag) != 0 && (seq->flag & use_crop_flag) == 0) {
+ char name_esc[(sizeof(seq->name) - 2) * 2], *path;
+ BLI_strescape(name_esc, seq->name + 2, sizeof(name_esc));
+
+ path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_x", name_esc);
+ seq_convert_transform_animation(scene, path, image_size_x);
+ MEM_freeN(path);
+ path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_y", name_esc);
+ seq_convert_transform_animation(scene, path, image_size_y);
+ MEM_freeN(path);
+ }
+
+ seq->flag &= ~use_transform_flag;
+ seq->flag &= ~use_crop_flag;
+}
+
+static void seq_convert_transform_crop_lb(const Scene *scene,
+ const ListBase *lb,
+ const eSpaceSeq_Proxy_RenderSize render_size)
+{
+
+ LISTBASE_FOREACH (Sequence *, seq, lb) {
+ if (seq->type != SEQ_TYPE_SOUND_RAM) {
+ seq_convert_transform_crop(scene, seq, render_size);
+ }
+ if (seq->type == SEQ_TYPE_META) {
+ seq_convert_transform_crop_lb(scene, &seq->seqbase, render_size);
+ }
+ }
+}
+
void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
{
if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) {
@@ -292,6 +436,31 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 292, 2)) {
+
+ eSpaceSeq_Proxy_RenderSize render_size = 100;
+
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ switch (sl->spacetype) {
+ case SPACE_SEQ: {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ render_size = sseq->render_size;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ seq_convert_transform_crop_lb(scene, &scene->ed->seqbase, render_size);
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 839b0b12b83..2f848b5be08 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -684,6 +684,8 @@ void IMB_rectfill_area(struct ImBuf *ibuf,
int x2,
int y2,
struct ColorManagedDisplay *display);
+void IMB_rectfill_area_replace(
+ const struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2);
void IMB_rectfill_alpha(struct ImBuf *ibuf, const float value);
/* This should not be here, really,
diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c
index 8b4f33bb306..8e348ead756 100644
--- a/source/blender/imbuf/intern/rectop.c
+++ b/source/blender/imbuf/intern/rectop.c
@@ -1069,8 +1069,11 @@ void IMB_rectblend_threaded(ImBuf *dbuf,
}
}
-/* fill */
-
+/**
+ * Replace pixels of entire image with solid color.
+ * \param ibuf an image to be filled with color. It must be 4 channel image.
+ * \param col RGBA color, which is assigned directly to both byte (via scaling) and float buffers.
+ */
void IMB_rectfill(ImBuf *drect, const float col[4])
{
int num;
@@ -1103,6 +1106,61 @@ void IMB_rectfill(ImBuf *drect, const float col[4])
}
}
+/**
+ * Replace pixels of image area with solid color.
+ * \param ibuf an image to be filled with color. It must be 4 channel image.
+ * \param col RGBA color, which is assigned directly to both byte (via scaling) and float buffers.
+ * \param x1, y1, x2, y2 (x1, y1) defines starting point of the rectangular area to be filled,
+ * (x2, y2) is the end point. Note that values are allowed to be loosely ordered, which means that
+ * x2 is allowed to be lower than x1, as well as y2 is allowed to be lower than y1. No matter the
+ * order the area between x1 and x2, and y1 and y2 is filled.
+ */
+void IMB_rectfill_area_replace(
+ const ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2)
+{
+ /* Sanity checks. */
+ BLI_assert(ibuf->channels == 4);
+
+ if (ibuf->channels != 4) {
+ return;
+ }
+
+ int width = ibuf->x;
+ int height = ibuf->y;
+ CLAMP(x1, 0, width);
+ CLAMP(x2, 0, width);
+ CLAMP(y1, 0, height);
+ CLAMP(y2, 0, height);
+
+ if (x1 > x2) {
+ SWAP(int, x1, x2);
+ }
+ if (y1 > y2) {
+ SWAP(int, y1, y2);
+ }
+ if (x1 == x2 || y1 == y2) {
+ return;
+ }
+
+ unsigned char col_char[4] = {col[0] * 255, col[1] * 255, col[2] * 255, col[3] * 255};
+
+ for (int y = y1; y < y2; y++) {
+ for (int x = x1; x < x2; x++) {
+ size_t offset = ((size_t)ibuf->x) * y * 4 + 4 * x;
+
+ if (ibuf->rect) {
+ unsigned char *rrect = (unsigned char *)ibuf->rect + offset;
+ memcpy(rrect, &col_char, sizeof(unsigned char) * 4);
+ }
+
+ if (ibuf->rect_float) {
+ float *rrectf = ibuf->rect_float + offset;
+ memcpy(rrectf, &col, sizeof(float) * 4);
+ }
+ }
+ }
+}
+
void buf_rectfill_area(unsigned char *rect,
float *rectf,
int width,
@@ -1214,6 +1272,21 @@ void buf_rectfill_area(unsigned char *rect,
}
}
+/**
+ * Blend pixels of image area with solid color.
+ *
+ * For images with uchar buffer use color matching image colorspace.
+ * For images with float buffer use color display colorspace.
+ * If display colorspace can not be referenced, use color in SRGB colorspace.
+ *
+ * \param ibuf an image to be filled with color. It must be 4 channel image.
+ * \param col RGBA color.
+ * \param x1, y1, x2, y2 (x1, y1) defines starting point of the rectangular area to be filled,
+ * (x2, y2) is the end point. Note that values are allowed to be loosely ordered, which means that
+ * x2 is allowed to be lower than x1, as well as y2 is allowed to be lower than y1. No matter the
+ * order the area between x1 and x2, and y1 and y2 is filled.
+ * \param display colorspace reference for display space.
+ */
void IMB_rectfill_area(ImBuf *ibuf,
const float col[4],
int x1,
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 1d89657faf6..1847fbfa986 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -68,6 +68,9 @@ typedef struct StripCrop {
typedef struct StripTransform {
int xofs;
int yofs;
+ float scale_x;
+ float scale_y;
+ float rotation;
} StripTransform;
typedef struct StripColorBalance {
@@ -494,8 +497,8 @@ enum {
SEQ_MAKE_FLOAT = (1 << 13),
SEQ_LOCK = (1 << 14),
SEQ_USE_PROXY = (1 << 15),
- SEQ_USE_TRANSFORM = (1 << 16),
- SEQ_USE_CROP = (1 << 17),
+ SEQ_FLAG_UNUSED_23 = (1 << 16), /* cleared */
+ SEQ_FLAG_UNUSED_22 = (1 << 17), /* cleared */
SEQ_FLAG_UNUSED_18 = (1 << 18), /* cleared */
SEQ_FLAG_UNUSED_19 = (1 << 19), /* cleared */
SEQ_FLAG_UNUSED_21 = (1 << 21), /* cleared */
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 8da2d762c94..eed30b05c82 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -473,34 +473,6 @@ static void rna_Sequence_use_proxy_set(PointerRNA *ptr, bool value)
BKE_sequencer_proxy_set(seq, value != 0);
}
-static void rna_Sequence_use_translation_set(PointerRNA *ptr, bool value)
-{
- Sequence *seq = (Sequence *)ptr->data;
- if (value) {
- seq->flag |= SEQ_USE_TRANSFORM;
- if (seq->strip->transform == NULL) {
- seq->strip->transform = MEM_callocN(sizeof(struct StripTransform), "StripTransform");
- }
- }
- else {
- seq->flag &= ~SEQ_USE_TRANSFORM;
- }
-}
-
-static void rna_Sequence_use_crop_set(PointerRNA *ptr, bool value)
-{
- Sequence *seq = (Sequence *)ptr->data;
- if (value) {
- seq->flag |= SEQ_USE_CROP;
- if (seq->strip->crop == NULL) {
- seq->strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop");
- }
- }
- else {
- seq->flag &= ~SEQ_USE_CROP;
- }
-}
-
static int transform_seq_cmp_fn(Sequence *seq, void *arg_pt)
{
SequenceSearchData *data = arg_pt;
@@ -1409,18 +1381,35 @@ static void rna_def_strip_transform(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Sequence Transform", "Transform parameters for a sequence strip");
RNA_def_struct_sdna(srna, "StripTransform");
+ prop = RNA_def_property(srna, "scale_x", PROP_FLOAT, PROP_UNSIGNED);
+ RNA_def_property_float_sdna(prop, NULL, "scale_x");
+ RNA_def_property_ui_text(prop, "Scale X", "Scale along X axis");
+ RNA_def_property_ui_range(prop, 0, FLT_MAX, 3, 3);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
+
+ prop = RNA_def_property(srna, "scale_y", PROP_FLOAT, PROP_UNSIGNED);
+ RNA_def_property_float_sdna(prop, NULL, "scale_y");
+ RNA_def_property_ui_text(prop, "Scale Y", "Scale along Y axis");
+ RNA_def_property_ui_range(prop, 0, FLT_MAX, 3, 3);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
+
prop = RNA_def_property(srna, "offset_x", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "xofs");
- RNA_def_property_ui_text(
- prop, "Offset X", "Amount to move the input on the X axis within its boundaries");
- RNA_def_property_ui_range(prop, -4096, 4096, 1, -1);
+ RNA_def_property_ui_text(prop, "Translate X", "Move along X axis");
+ RNA_def_property_ui_range(prop, INT_MIN, INT_MAX, 1, 6);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
prop = RNA_def_property(srna, "offset_y", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "yofs");
- RNA_def_property_ui_text(
- prop, "Offset Y", "Amount to move the input on the Y axis within its boundaries");
- RNA_def_property_ui_range(prop, -4096, 4096, 1, -1);
+ RNA_def_property_ui_text(prop, "Translate Y", "Move along Y axis");
+ RNA_def_property_ui_range(prop, INT_MIN, INT_MAX, 1, 6);
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
+
+ prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "rotation");
+ RNA_def_property_ui_text(prop, "Rotation", "Rotate around image centr");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update");
RNA_def_struct_path_func(srna, "rna_SequenceTransform_path");
@@ -2175,22 +2164,10 @@ static void rna_def_filter_video(StructRNA *srna)
RNA_def_property_ui_text(prop, "Strobe", "Only display every nth frame");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
- prop = RNA_def_property(srna, "use_translation", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_TRANSFORM);
- RNA_def_property_ui_text(prop, "Use Translation", "Translate image before processing");
- RNA_def_property_boolean_funcs(prop, NULL, "rna_Sequence_use_translation_set");
- RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
-
prop = RNA_def_property(srna, "transform", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "strip->transform");
RNA_def_property_ui_text(prop, "Transform", "");
- prop = RNA_def_property(srna, "use_crop", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_CROP);
- RNA_def_property_ui_text(prop, "Use Crop", "Crop image before processing");
- RNA_def_property_boolean_funcs(prop, NULL, "rna_Sequence_use_crop_set");
- RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
-
prop = RNA_def_property(srna, "crop", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "strip->crop");
RNA_def_property_ui_text(prop, "Crop", "");
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 90b1d611842..a154b5d908b 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -156,6 +156,8 @@ void BKE_sequencer_new_render_data(struct Main *bmain,
int preview_render_size,
int for_render,
SeqRenderData *r_context);
+bool SEQ_can_use_proxy(struct Sequence *seq, int psize);
+int SEQ_rendersize_to_proxysize(int render_size);
/* **********************************************************************
* sequencer.c
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index 42134b38666..4dd737cdbd2 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -1631,7 +1631,7 @@ typedef struct SeqIndexBuildContext {
#define PROXY_MAXFILE (2 * FILE_MAXDIR + FILE_MAXFILE)
-static IMB_Proxy_Size seq_rendersize_to_proxysize(int render_size)
+int SEQ_rendersize_to_proxysize(int render_size)
{
switch (render_size) {
case SEQ_RENDER_SIZE_PROXY_25:
@@ -1904,7 +1904,7 @@ static bool seq_proxy_get_fname(Editing *ed,
return true;
}
-static bool seq_can_use_proxy(Sequence *seq, IMB_Proxy_Size psize)
+bool SEQ_can_use_proxy(Sequence *seq, int psize)
{
if (seq->strip->proxy == NULL) {
return false;
@@ -1922,7 +1922,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c
StripAnim *sanim;
/* only use proxies, if they are enabled (even if present!) */
- if (!seq_can_use_proxy(seq, seq_rendersize_to_proxysize(psize))) {
+ if (!SEQ_can_use_proxy(seq, SEQ_rendersize_to_proxysize(psize))) {
return NULL;
}
@@ -2653,6 +2653,28 @@ void BKE_sequencer_color_balance_apply(
* - Premultiply
*/
+static bool sequencer_use_transform(const Sequence *seq)
+{
+ const StripTransform *transform = seq->strip->transform;
+
+ if (transform->xofs != 0 || transform->yofs != 0 || transform->scale_x != 1 ||
+ transform->scale_y != 1 || transform->rotation != 0) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool sequencer_use_crop(const Sequence *seq)
+{
+ const StripCrop *crop = seq->strip->crop;
+ if (crop->left > 0 || crop->right > 0 || crop->top > 0 || crop->bottom > 0) {
+ return true;
+ }
+
+ return false;
+}
+
bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context,
Sequence *seq,
float UNUSED(cfra))
@@ -2663,8 +2685,8 @@ bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context,
return false;
}
- if (seq->flag &
- (SEQ_FILTERY | SEQ_USE_CROP | SEQ_USE_TRANSFORM | SEQ_FLIPX | SEQ_FLIPY | SEQ_MAKE_FLOAT)) {
+ if ((seq->flag & (SEQ_FILTERY | SEQ_FLIPX | SEQ_FLIPY | SEQ_MAKE_FLOAT)) ||
+ sequencer_use_crop(seq) || sequencer_use_transform(seq)) {
return true;
}
@@ -2689,6 +2711,83 @@ bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context,
return false;
}
+typedef struct ImageTransformThreadInitData {
+ ImBuf *ibuf_source;
+ ImBuf *ibuf_out;
+ StripTransform *transform;
+ float scale_to_fit;
+ float image_scale_factor;
+ bool for_render;
+} ImageTransformThreadInitData;
+
+typedef struct ImageTransformThreadData {
+ ImBuf *ibuf_source;
+ ImBuf *ibuf_out;
+ StripTransform *transform;
+ float scale_to_fit;
+ float image_scale_factor;
+ bool for_render;
+ int start_line;
+ int tot_line;
+} ImageTransformThreadData;
+
+static void sequencer_image_transform_init(void *handle_v,
+ int start_line,
+ int tot_line,
+ void *init_data_v)
+{
+ ImageTransformThreadData *handle = (ImageTransformThreadData *)handle_v;
+ const ImageTransformThreadInitData *init_data = (ImageTransformThreadInitData *)init_data_v;
+
+ handle->ibuf_source = init_data->ibuf_source;
+ handle->ibuf_out = init_data->ibuf_out;
+ handle->transform = init_data->transform;
+ handle->scale_to_fit = init_data->scale_to_fit;
+ handle->image_scale_factor = init_data->image_scale_factor;
+ handle->for_render = init_data->for_render;
+
+ handle->start_line = start_line;
+ handle->tot_line = tot_line;
+}
+
+static void *sequencer_image_transform_do_thread(void *data_v)
+{
+ const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v;
+ const StripTransform *transform = data->transform;
+ const float scale_x = transform->scale_x * data->scale_to_fit;
+ const float scale_y = transform->scale_y * data->scale_to_fit;
+ const float scale_to_fit_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2;
+ const float scale_to_fit_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2;
+ const float translate_x = transform->xofs * data->image_scale_factor + scale_to_fit_offs_x;
+ const float translate_y = transform->yofs * data->image_scale_factor + scale_to_fit_offs_y;
+ const int width = data->ibuf_out->x;
+ const int height = data->ibuf_out->y;
+ const float pivot[2] = {width / 2 - scale_to_fit_offs_x, height / 2 - scale_to_fit_offs_y};
+ float transform_matrix[3][3];
+ loc_rot_size_to_mat3(transform_matrix,
+ (const float[]){translate_x, translate_y},
+ transform->rotation,
+ (const float[]){scale_x, scale_y});
+ invert_m3(transform_matrix);
+ transform_pivot_set_m3(transform_matrix, pivot);
+
+ for (int yi = data->start_line; yi < data->start_line + data->tot_line; yi++) {
+ for (int xi = 0; xi < width; xi++) {
+ float uv[2] = {xi, yi};
+ mul_v2_m3v2(uv, transform_matrix, uv);
+
+ if (data->for_render) {
+ bilinear_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi);
+ }
+ else {
+ nearest_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi);
+ }
+ }
+ }
+
+ return NULL;
+}
+
static ImBuf *input_preprocess(const SeqRenderData *context,
Sequence *seq,
float cfra,
@@ -2696,131 +2795,124 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
const bool is_proxy_image)
{
Scene *scene = context->scene;
- float mul;
-
- ibuf = IMB_makeSingleUser(ibuf);
+ ImBuf *preprocessed_ibuf = NULL;
+ /* Deinterlace. */
if ((seq->flag & SEQ_FILTERY) && !ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_MOVIECLIP)) {
- IMB_filtery(ibuf);
- }
-
- if (seq->flag & (SEQ_USE_CROP | SEQ_USE_TRANSFORM)) {
- StripCrop c = {0};
- StripTransform t = {0};
+ /* Change original image pointer to avoid another duplication in SEQ_USE_TRANSFORM. */
+ preprocessed_ibuf = IMB_makeSingleUser(ibuf);
+ ibuf = preprocessed_ibuf;
- if (seq->flag & SEQ_USE_CROP && seq->strip->crop) {
- c = *seq->strip->crop;
- }
- if (seq->flag & SEQ_USE_TRANSFORM && seq->strip->transform) {
- t = *seq->strip->transform;
- }
-
- /* Calculate scale factor for current image if needed. */
- double scale_factor, image_scale_factor = 1.0;
- if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) {
- scale_factor = image_scale_factor = (double)scene->r.size / 100;
- }
- else {
- scale_factor = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size);
- if (!is_proxy_image) {
- image_scale_factor = scale_factor;
- }
- }
-
- if (image_scale_factor != 1.0) {
- if (context->for_render) {
- IMB_scaleImBuf(ibuf, ibuf->x * image_scale_factor, ibuf->y * image_scale_factor);
- }
- else {
- IMB_scalefastImBuf(ibuf, ibuf->x * image_scale_factor, ibuf->y * image_scale_factor);
- }
- }
-
- t.xofs *= scale_factor;
- t.yofs *= scale_factor;
- c.left *= scale_factor;
- c.right *= scale_factor;
- c.top *= scale_factor;
- c.bottom *= scale_factor;
-
- int sx, sy, dx, dy;
- sx = ibuf->x - c.left - c.right;
- sy = ibuf->y - c.top - c.bottom;
-
- if (seq->flag & SEQ_USE_TRANSFORM) {
- dx = context->rectx;
- dy = context->recty;
- }
- else {
- dx = sx;
- dy = sy;
- }
+ IMB_filtery(preprocessed_ibuf);
+ }
- if (c.top + c.bottom >= ibuf->y || c.left + c.right >= ibuf->x || t.xofs >= dx ||
- t.yofs >= dy) {
- return NULL;
- }
+ /* Calculate scale factor, so image fits in preview area with original aspect ratio. */
+ const float scale_to_fit_factor = MIN2((float)context->rectx / (float)ibuf->x,
+ (float)context->recty / (float)ibuf->y);
- ImBuf *i = IMB_allocImBuf(dx, dy, 32, ibuf->rect_float ? IB_rectfloat : IB_rect);
- IMB_rectcpy(i, ibuf, t.xofs, t.yofs, c.left, c.bottom, sx, sy);
- sequencer_imbuf_assign_spaces(scene, i);
- IMB_metadata_copy(i, ibuf);
+ /* Get scale factor if preview resolution doesn't match project resolution. */
+ float preview_scale_factor;
+ if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) {
+ preview_scale_factor = (float)scene->r.size / 100;
+ }
+ else {
+ preview_scale_factor = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size);
+ }
+
+ if (sequencer_use_crop(seq)) {
+ /* Change original image pointer to avoid another duplication in SEQ_USE_TRANSFORM. */
+ preprocessed_ibuf = IMB_makeSingleUser(ibuf);
+ ibuf = preprocessed_ibuf;
+
+ const int width = ibuf->x;
+ const int height = ibuf->y;
+ const StripCrop *c = seq->strip->crop;
+
+ const int left = c->left / scale_to_fit_factor * preview_scale_factor;
+ const int right = c->right / scale_to_fit_factor * preview_scale_factor;
+ const int top = c->top / scale_to_fit_factor * preview_scale_factor;
+ const int bottom = c->bottom / scale_to_fit_factor * preview_scale_factor;
+ const float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ /* Left. */
+ IMB_rectfill_area_replace(preprocessed_ibuf, col, 0, 0, left, height);
+ /* Bottom. */
+ IMB_rectfill_area_replace(preprocessed_ibuf, col, left, 0, width, bottom);
+ /* Right. */
+ IMB_rectfill_area_replace(preprocessed_ibuf, col, width - right, bottom, width, height);
+ /* Top. */
+ IMB_rectfill_area_replace(preprocessed_ibuf, col, left, height - top, width - right, height);
+ }
+
+ if (sequencer_use_transform(seq) || context->rectx != ibuf->x || context->recty != ibuf->y) {
+ const int x = context->rectx;
+ const int y = context->recty;
+ preprocessed_ibuf = IMB_allocImBuf(x, y, 32, ibuf->rect_float ? IB_rectfloat : IB_rect);
+
+ ImageTransformThreadInitData init_data = {NULL};
+ init_data.ibuf_source = ibuf;
+ init_data.ibuf_out = preprocessed_ibuf;
+ init_data.transform = seq->strip->transform;
+ init_data.scale_to_fit = scale_to_fit_factor;
+ init_data.image_scale_factor = preview_scale_factor;
+ init_data.for_render = context->for_render;
+ IMB_processor_apply_threaded(context->recty,
+ sizeof(ImageTransformThreadData),
+ &init_data,
+ sequencer_image_transform_init,
+ sequencer_image_transform_do_thread);
+ sequencer_imbuf_assign_spaces(scene, preprocessed_ibuf);
+ IMB_metadata_copy(preprocessed_ibuf, ibuf);
IMB_freeImBuf(ibuf);
- ibuf = i;
+ }
+
+ /* Duplicate ibuf if we still have original. */
+ if (preprocessed_ibuf == NULL) {
+ preprocessed_ibuf = IMB_makeSingleUser(ibuf);
}
if (seq->flag & SEQ_FLIPX) {
- IMB_flipx(ibuf);
+ IMB_flipx(preprocessed_ibuf);
}
if (seq->flag & SEQ_FLIPY) {
- IMB_flipy(ibuf);
+ IMB_flipy(preprocessed_ibuf);
}
if (seq->sat != 1.0f) {
- IMB_saturation(ibuf, seq->sat);
- }
-
- mul = seq->mul;
-
- if (seq->blend_mode == SEQ_BLEND_REPLACE) {
- mul *= seq->blend_opacity / 100.0f;
+ IMB_saturation(preprocessed_ibuf, seq->sat);
}
if (seq->flag & SEQ_MAKE_FLOAT) {
- if (!ibuf->rect_float) {
- BKE_sequencer_imbuf_to_sequencer_space(scene, ibuf, true);
+ if (!preprocessed_ibuf->rect_float) {
+ BKE_sequencer_imbuf_to_sequencer_space(scene, preprocessed_ibuf, true);
}
- if (ibuf->rect) {
- imb_freerectImBuf(ibuf);
+ if (preprocessed_ibuf->rect) {
+ imb_freerectImBuf(preprocessed_ibuf);
}
}
- if (mul != 1.0f) {
- multibuf(ibuf, mul);
+ float mul = seq->mul;
+ if (seq->blend_mode == SEQ_BLEND_REPLACE) {
+ mul *= seq->blend_opacity / 100.0f;
}
- if (ibuf->x != context->rectx || ibuf->y != context->recty) {
- if (context->for_render) {
- IMB_scaleImBuf(ibuf, (short)context->rectx, (short)context->recty);
- }
- else {
- IMB_scalefastImBuf(ibuf, (short)context->rectx, (short)context->recty);
- }
+ if (mul != 1.0f) {
+ multibuf(preprocessed_ibuf, mul);
}
if (seq->modifiers.first) {
- ImBuf *ibuf_new = BKE_sequence_modifier_apply_stack(context, seq, ibuf, cfra);
+ ImBuf *ibuf_new = BKE_sequence_modifier_apply_stack(context, seq, preprocessed_ibuf, cfra);
- if (ibuf_new != ibuf) {
- IMB_metadata_copy(ibuf_new, ibuf);
- IMB_freeImBuf(ibuf);
- ibuf = ibuf_new;
+ if (ibuf_new != preprocessed_ibuf) {
+ IMB_metadata_copy(ibuf_new, preprocessed_ibuf);
+ IMB_freeImBuf(preprocessed_ibuf);
+ preprocessed_ibuf = ibuf_new;
}
}
- return ibuf;
+ return preprocessed_ibuf;
}
/*********************** strip rendering functions *************************/
@@ -3190,11 +3282,11 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
bool *r_is_proxy_image)
{
ImBuf *ibuf = NULL;
- IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size);
+ IMB_Proxy_Size psize = SEQ_rendersize_to_proxysize(context->preview_render_size);
IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
- if (seq_can_use_proxy(seq, psize)) {
+ if (SEQ_can_use_proxy(seq, psize)) {
/* Try to get a proxy image.
* Movie proxies are handled by ImBuf module with exception of `custom file` setting. */
if (context->scene->ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE &&
@@ -3326,7 +3418,7 @@ static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context,
{
ImBuf *ibuf = NULL;
MovieClipUser user;
- IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size);
+ IMB_Proxy_Size psize = SEQ_rendersize_to_proxysize(context->preview_render_size);
if (!seq->clip) {
return NULL;
@@ -5347,6 +5439,8 @@ static Strip *seq_strip_alloc(int type)
if (ELEM(type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD) == 0) {
strip->transform = MEM_callocN(sizeof(struct StripTransform), "StripTransform");
+ strip->transform->scale_x = 1;
+ strip->transform->scale_y = 1;
strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop");
}