diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 7d881948466..0171fa902db 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -949,54 +949,28 @@ class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel): row.prop(strip, "mute", toggle=True, icon_only=True, emboss=False) -class SEQUENCER_PT_adjust_transform_offset(SequencerButtonsPanel, Panel): - bl_label = "Offset" - bl_parent_id = "SEQUENCER_PT_adjust_transform" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Strip" - - @classmethod - def poll(cls, context): - strip = act_strip(context) - return strip.type != 'SOUND' - - def draw_header(self, context): - strip = act_strip(context) - self.layout.prop(strip, "use_translation", text="") - - def draw(self, context): - strip = act_strip(context) - layout = self.layout - layout.use_property_split = True - - layout.active = strip.use_translation and (not strip.mute) - - col = layout.column(align=True) - col.prop(strip.transform, "offset_x", text="Position X") - col.prop(strip.transform, "offset_y", text="Y") - - -class SEQUENCER_PT_adjust_transform_crop(SequencerButtonsPanel, Panel): +class SEQUENCER_PT_adjust_crop(SequencerButtonsPanel, Panel): bl_label = "Crop" - bl_parent_id = "SEQUENCER_PT_adjust_transform" bl_options = {'DEFAULT_CLOSED'} bl_category = "Strip" @classmethod def poll(cls, context): + if not cls.has_sequencer(context): + return False + + strip = act_strip(context) + if not strip: + return False + strip = act_strip(context) return strip.type != 'SOUND' - def draw_header(self, context): - strip = act_strip(context) - self.layout.prop(strip, "use_crop", text="") - def draw(self, context): strip = act_strip(context) layout = self.layout layout.use_property_split = True - - layout.active = strip.use_crop and (not strip.mute) + layout.active = not strip.mute col = layout.column(align=True) col.prop(strip.crop, "min_x") @@ -1590,21 +1564,19 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel): split.label(text="%d-%d (%d)" % (sta, end, end - sta + 1), translate=False) -class SEQUENCER_PT_adjust(SequencerButtonsPanel, Panel): - bl_label = "Adjust" - bl_category = "Strip" - - def draw(self, context): - pass - - class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): bl_label = "Sound" - bl_parent_id = "SEQUENCER_PT_adjust" bl_category = "Strip" @classmethod def poll(cls, context): + if not cls.has_sequencer(context): + return False + + strip = act_strip(context) + if not strip: + return False + strip = act_strip(context) return strip.type == 'SOUND' @@ -1636,11 +1608,17 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): class SEQUENCER_PT_adjust_comp(SequencerButtonsPanel, Panel): bl_label = "Compositing" - bl_parent_id = "SEQUENCER_PT_adjust" bl_category = "Strip" @classmethod def poll(cls, context): + if not cls.has_sequencer(context): + return False + + strip = act_strip(context) + if not strip: + return False + strip = act_strip(context) return strip.type != 'SOUND' @@ -1659,8 +1637,8 @@ class SEQUENCER_PT_adjust_comp(SequencerButtonsPanel, Panel): class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): bl_label = "Transform" - bl_parent_id = "SEQUENCER_PT_adjust" bl_category = "Strip" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -1671,23 +1649,26 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): if not strip: return False - return strip.type in { - 'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK', - 'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'TEXT', - 'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', - 'OVER_DROP', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', - 'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX' - } + strip = act_strip(context) + return strip.type != 'SOUND' def draw(self, context): - layout = self.layout strip = act_strip(context) - + layout = self.layout layout.use_property_split = True - layout.use_property_decorate = False - layout.active = not strip.mute + col = layout.column(align=True) + col.prop(strip.transform, "offset_x", text="Position X") + col.prop(strip.transform, "offset_y", text="Y") + + col = layout.column(align=True) + col.prop(strip.transform, "scale_x", text="Scale X") + col.prop(strip.transform, "scale_y", text="Y") + + col = layout.column(align=True) + col.prop(strip.transform, "rotation", text="Rotation") + row = layout.row(heading="Mirror") sub = row.row(align=True) sub.prop(strip, "use_flip_x", text="X", toggle=True) @@ -1696,7 +1677,6 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): class SEQUENCER_PT_adjust_video(SequencerButtonsPanel, Panel): bl_label = "Video" - bl_parent_id = "SEQUENCER_PT_adjust" bl_options = {'DEFAULT_CLOSED'} bl_category = "Strip" @@ -1745,7 +1725,6 @@ class SEQUENCER_PT_adjust_video(SequencerButtonsPanel, Panel): class SEQUENCER_PT_adjust_color(SequencerButtonsPanel, Panel): bl_label = "Color" - bl_parent_id = "SEQUENCER_PT_adjust" bl_options = {'DEFAULT_CLOSED'} bl_category = "Strip" @@ -2234,11 +2213,9 @@ classes = ( SEQUENCER_PT_effect_text_style, SEQUENCER_PT_effect_text_layout, - SEQUENCER_PT_adjust, SEQUENCER_PT_adjust_comp, SEQUENCER_PT_adjust_transform, - SEQUENCER_PT_adjust_transform_offset, - SEQUENCER_PT_adjust_transform_crop, + SEQUENCER_PT_adjust_crop, SEQUENCER_PT_adjust_video, SEQUENCER_PT_adjust_color, SEQUENCER_PT_adjust_sound, 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], @@ -2484,6 +2522,21 @@ bool equals_m4m4(const float mat1[4][4], const float mat2[4][4]) equals_v4v4(mat1[2], mat2[2]) && equals_v4v4(mat1[3], mat2[3])); } +/** + * 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); + /* Change original image pointer to avoid another duplication in SEQ_USE_TRANSFORM. */ + preprocessed_ibuf = IMB_makeSingleUser(ibuf); + ibuf = preprocessed_ibuf; + + IMB_filtery(preprocessed_ibuf); } - if (seq->flag & (SEQ_USE_CROP | SEQ_USE_TRANSFORM)) { - StripCrop c = {0}; - StripTransform t = {0}; + /* 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); - 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; - } + /* 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); + } - /* 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 (sequencer_use_crop(seq)) { + /* Change original image pointer to avoid another duplication in SEQ_USE_TRANSFORM. */ + preprocessed_ibuf = IMB_makeSingleUser(ibuf); + ibuf = preprocessed_ibuf; - 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); - } - } + const int width = ibuf->x; + const int height = ibuf->y; + const StripCrop *c = seq->strip->crop; - t.xofs *= scale_factor; - t.yofs *= scale_factor; - c.left *= scale_factor; - c.right *= scale_factor; - c.top *= scale_factor; - c.bottom *= scale_factor; + 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}; - int sx, sy, dx, dy; - sx = ibuf->x - c.left - c.right; - sy = ibuf->y - c.top - c.bottom; + /* 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 (seq->flag & SEQ_USE_TRANSFORM) { - dx = context->rectx; - dy = context->recty; - } - else { - dx = sx; - dy = sy; - } + 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); - if (c.top + c.bottom >= ibuf->y || c.left + c.right >= ibuf->x || t.xofs >= dx || - t.yofs >= dy) { - return NULL; - } - - 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); + 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); + IMB_saturation(preprocessed_ibuf, seq->sat); } - mul = seq->mul; + if (seq->flag & SEQ_MAKE_FLOAT) { + if (!preprocessed_ibuf->rect_float) { + BKE_sequencer_imbuf_to_sequencer_space(scene, preprocessed_ibuf, true); + } + if (preprocessed_ibuf->rect) { + imb_freerectImBuf(preprocessed_ibuf); + } + } + + float mul = seq->mul; if (seq->blend_mode == SEQ_BLEND_REPLACE) { mul *= seq->blend_opacity / 100.0f; } - if (seq->flag & SEQ_MAKE_FLOAT) { - if (!ibuf->rect_float) { - BKE_sequencer_imbuf_to_sequencer_space(scene, ibuf, true); - } - - if (ibuf->rect) { - imb_freerectImBuf(ibuf); - } - } - if (mul != 1.0f) { - multibuf(ibuf, mul); - } - - 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); - } + 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"); }