From 39eb314cb922b805e9126d5f0352f31c2f84f151 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 29 Jan 2014 20:01:30 +1100 Subject: [PATCH] UI: Refactor timecode functions into BLI_timecode - deduplicate timecode_simple_string from image.c - replace V2D_UNIT_SECONDSSEQ with V2D_UNIT_SECONDS - avoid possible buffer overflow bugs (sprintf -> BLI_snprintf) - remove option not to use timecode and split into 2 functions Patch D227 by Andrew Buttery with own refactoring. --- source/blender/blenkernel/intern/image.c | 30 +-- source/blender/blenlib/BLI_timecode.h | 41 ++++ source/blender/blenlib/CMakeLists.txt | 2 + source/blender/blenlib/intern/timecode.c | 218 ++++++++++++++++++ source/blender/editors/animation/anim_draw.c | 130 +---------- source/blender/editors/include/ED_anim_api.h | 3 - source/blender/editors/include/UI_view2d.h | 1 - source/blender/editors/interface/view2d.c | 20 +- .../editors/space_sequencer/sequencer_draw.c | 2 +- 9 files changed, 283 insertions(+), 164 deletions(-) create mode 100644 source/blender/blenlib/BLI_timecode.h create mode 100644 source/blender/blenlib/intern/timecode.c diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 336656eed19..3e93ab0cbcc 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -68,6 +68,7 @@ #include "BLI_blenlib.h" #include "BLI_threads.h" +#include "BLI_timecode.h" /* for stamp timecode format */ #include "BLI_utildefines.h" #include "BKE_bmfont.h" @@ -1520,30 +1521,6 @@ void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const ImBuf *i } -static void timecode_simple_string(char *text, size_t text_size, const int cfra, int const frs_sec) -{ - int f = (int)(cfra % frs_sec); - int s = (int)(cfra / frs_sec); - int h = 0; - int m = 0; - - if (s) { - m = (int)(s / 60); - s %= 60; - - if (m) { - h = (int)(m / 60); - m %= 60; - } - } - - if (frs_sec < 100) { - BLI_snprintf(text, text_size, "%02d:%02d:%02d.%02d", h, m, s, f); - } - else { - BLI_snprintf(text, text_size, "%02d:%02d:%02d.%03d", h, m, s, f); - } -} #define STAMP_NAME_SIZE ((MAX_ID_NAME - 2) + 16) /* could allow access externally - 512 is for long names, @@ -1607,8 +1584,9 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d } if (scene->r.stamp & R_STAMP_TIME) { - timecode_simple_string(text, sizeof(text), scene->r.cfra, scene->r.frs_sec); - BLI_snprintf(stamp_data->time, sizeof(stamp_data->time), do_prefix ? "Time %s" : "%s", text); + const short timecode_style = USER_TIMECODE_SMPTE_FULL; + BLI_timecode_string_from_time(text, sizeof(text), 0, FRA2TIME(scene->r.cfra), FPS, timecode_style); + BLI_snprintf(stamp_data->time, sizeof(stamp_data->time), do_prefix ? "Timecode %s" : "%s", text); } else { stamp_data->time[0] = '\0'; diff --git a/source/blender/blenlib/BLI_timecode.h b/source/blender/blenlib/BLI_timecode.h new file mode 100644 index 00000000000..b6cd0117bf5 --- /dev/null +++ b/source/blender/blenlib/BLI_timecode.h @@ -0,0 +1,41 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_TIMECODE_H__ +#define __BLI_TIMECODE_H__ + +/** \file BLI_timecode.h + * \ingroup BLI + */ + +size_t BLI_timecode_string_from_time( + char *str, const size_t len, const int power, const float time_seconds, + const double scene_fps, const short timecode_style); + +size_t BLI_timecode_string_from_time_simple( + char *str, const size_t len, const int power, const float time_seconds); + +#endif /* __BLI_TIMECODE_H__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 2a920a2e5fd..80e62929079 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -98,6 +98,7 @@ set(SRC intern/task.c intern/threads.c intern/time.c + intern/timecode.c intern/uvproject.c intern/voronoi.c intern/voxel.c @@ -166,6 +167,7 @@ set(SRC BLI_sys_types.h BLI_task.h BLI_threads.h + BLI_timecode.h BLI_utildefines.h BLI_uvproject.h BLI_vfontdata.h diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c new file mode 100644 index 00000000000..cd703741831 --- /dev/null +++ b/source/blender/blenlib/intern/timecode.c @@ -0,0 +1,218 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blendlib/intern/timecode.c + * \ingroup blendlib + * + * Time-Code string formatting + */ + +#include + + +#include "BLI_string.h" +#include "BLI_math.h" + +#include "BLI_timecode.h" /* own include */ + +#include "BLI_strict_flags.h" + +#include "DNA_userdef_types.h" /* for eTimecodeStyles only */ + + +/** + * Generate timecode/frame number string and store in \a str + * + * \param str destination string + * \param maxncpy maximum number of characters to copy ``sizeof(str)`` + * \param power special setting for #View2D grid drawing, + * used to specify how detailed we need to be + * \param time_seconds time total time in seconds + * \param fps frames per second, typically from the #FPS macro + * \param timecode_style enum from eTimecodeStyles + * \return length of \a str + */ + +size_t BLI_timecode_string_from_time( + char *str, const size_t maxncpy, const int power, const float time_seconds, + const double fps, const short timecode_style) +{ + int hours = 0, minutes = 0, seconds = 0, frames = 0; + float time = time_seconds; + char neg[2] = {'\0'}; + size_t rlen; + + /* get cframes */ + if (time < 0) { + /* correction for negative cfraues */ + neg[0] = '-'; + time = -time; + } + + if (time >= 3600) { + /* hours */ + /* XXX should we only display a single digit for hours since clips are + * VERY UNLIKELY to be more than 1-2 hours max? However, that would + * go against conventions... + */ + hours = (int)time / 3600; + time = (float)fmod(time, 3600); + } + + if (time >= 60) { + /* minutes */ + minutes = (int)time / 60; + time = (float)fmod(time, 60); + } + + if (power <= 0) { + /* seconds + frames + * Frames are derived from 'fraction' of second. We need to perform some additional rounding + * to cope with 'half' frames, etc., which should be fine in most cases + */ + seconds = (int)time; + frames = iroundf((float)(((double)time - (double)seconds) * fps)); + } + else { + /* seconds (with pixel offset rounding) */ + seconds = iroundf(time); + } + + switch (timecode_style) { + case USER_TIMECODE_MINIMAL: + { + /* - In general, minutes and seconds should be shown, as most clips will be + * within this length. Hours will only be included if relevant. + * - Only show frames when zoomed in enough for them to be relevant + * (using separator of '+' for frames). + * When showing frames, use slightly different display to avoid confusion with mm:ss format + */ + if (power <= 0) { + /* include "frames" in display */ + if (hours) { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d+%02d", neg, hours, minutes, seconds, frames); + } + else if (minutes) { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d+%02d", neg, minutes, seconds, frames); + } + else { + rlen = BLI_snprintf(str, maxncpy, "%s%d+%02d", neg, seconds, frames); + } + } + else { + /* don't include 'frames' in display */ + if (hours) { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d", neg, hours, minutes, seconds); + } + else { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d", neg, minutes, seconds); + } + } + break; + } + case USER_TIMECODE_SMPTE_MSF: + { + /* reduced SMPTE format that always shows minutes, seconds, frames. Hours only shown as needed. */ + if (hours) { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames); + } + else { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d", neg, minutes, seconds, frames); + } + break; + } + case USER_TIMECODE_MILLISECONDS: + { + /* reduced SMPTE. Instead of frames, milliseconds are shown */ + + /* precision of decimal part */ + const int ms_dp = (power <= 0) ? (1 - power) : 1; + + /* to get 2 digit whole-number part for seconds display + * (i.e. 3 is for 2 digits + radix, on top of full length) */ + const int s_pad = ms_dp + 3; + + if (hours) { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%0*.*f", neg, hours, minutes, s_pad, ms_dp, time); + } + else { + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%0*.*f", neg, minutes, s_pad, ms_dp, time); + } + break; + } + case USER_TIMECODE_SECONDS_ONLY: + { + /* only show the original seconds display */ + /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */ + if (power <= 0) { + rlen = BLI_snprintf(str, maxncpy, "%.*f", 1 - power, time_seconds); + } + else { + rlen = BLI_snprintf(str, maxncpy, "%d", iroundf(time_seconds)); + } + break; + } + case USER_TIMECODE_SMPTE_FULL: + default: + { + /* full SMPTE format */ + rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames); + break; + } + } + + return rlen; +} + + +/** + * Generate time string and store in \a str + * + * \param str destination string + * \param maxncpy maximum number of characters to copy ``sizeof(str)`` + * \param power special setting for #View2D grid drawing, + * used to specify how detailed we need to be + * \param time_seconds time total time in seconds + * \param seconds time in seconds. + * \return length of \a str + * + * \note in some cases this is used to print non-seconds values. + */ +size_t BLI_timecode_string_from_time_simple( + char *str, const size_t maxncpy, const int power, const float time_seconds) +{ + size_t rlen; + + /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */ + if (power <= 0) { + rlen = BLI_snprintf(str, maxncpy, "%.*f", 1 - power, time_seconds); + } + else { + rlen = BLI_snprintf(str, maxncpy, "%d", iroundf(time_seconds)); + } + + return rlen; +} diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 25fcd76b513..b1789d25b88 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -37,6 +37,7 @@ #include "DNA_userdef_types.h" #include "BLI_math.h" +#include "BLI_timecode.h" #include "BKE_context.h" #include "BKE_blender.h" @@ -55,126 +56,11 @@ #include "UI_resources.h" #include "UI_view2d.h" -/* *************************************************** */ -/* TIME CODE FORMATTING */ - -/* Generate timecode/frame number string and store in the supplied string - * - buffer: must be at least 13 chars long - * - power: special setting for View2D grid drawing, - * used to specify how detailed we need to be - * - timecodes: boolean specifying whether timecodes or - * frame numbers get drawn - * - cfra: time in frames or seconds, consistent with the values shown by timecodes - */ -// TODO: have this in kernel instead under scene? -void ANIM_timecode_string_from_frame(char *str, Scene *scene, int power, short timecodes, float cfra) -{ - if (timecodes) { - int hours = 0, minutes = 0, seconds = 0, frames = 0; - float raw_seconds = cfra; - char neg[2] = {'\0'}; - - /* get cframes */ - if (cfra < 0) { - /* correction for negative cfraues */ - neg[0] = '-'; - cfra = -cfra; - } - if (cfra >= 3600) { - /* hours */ - /* XXX should we only display a single digit for hours since clips are - * VERY UNLIKELY to be more than 1-2 hours max? However, that would - * go against conventions... - */ - hours = (int)cfra / 3600; - cfra = (float)fmod(cfra, 3600); - } - if (cfra >= 60) { - /* minutes */ - minutes = (int)cfra / 60; - cfra = (float)fmod(cfra, 60); - } - if (power <= 0) { - /* seconds + frames - * Frames are derived from 'fraction' of second. We need to perform some additional rounding - * to cope with 'half' frames, etc., which should be fine in most cases - */ - seconds = (int)cfra; - frames = (int)floor( (((double)cfra - (double)seconds) * FPS) + 0.5); - } - else { - /* seconds (with pixel offset rounding) */ - seconds = (int)floor(cfra + GLA_PIXEL_OFS); - } - - switch (U.timecode_style) { - case USER_TIMECODE_MINIMAL: - { - /* - In general, minutes and seconds should be shown, as most clips will be - * within this length. Hours will only be included if relevant. - * - Only show frames when zoomed in enough for them to be relevant - * (using separator of '+' for frames). - * When showing frames, use slightly different display to avoid confusion with mm:ss format - */ - if (power <= 0) { - /* include "frames" in display */ - if (hours) sprintf(str, "%s%02d:%02d:%02d+%02d", neg, hours, minutes, seconds, frames); - else if (minutes) sprintf(str, "%s%02d:%02d+%02d", neg, minutes, seconds, frames); - else sprintf(str, "%s%d+%02d", neg, seconds, frames); - } - else { - /* don't include 'frames' in display */ - if (hours) sprintf(str, "%s%02d:%02d:%02d", neg, hours, minutes, seconds); - else sprintf(str, "%s%02d:%02d", neg, minutes, seconds); - } - break; - } - case USER_TIMECODE_SMPTE_MSF: - { - /* reduced SMPTE format that always shows minutes, seconds, frames. Hours only shown as needed. */ - if (hours) sprintf(str, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames); - else sprintf(str, "%s%02d:%02d:%02d", neg, minutes, seconds, frames); - break; - } - case USER_TIMECODE_MILLISECONDS: - { - /* reduced SMPTE. Instead of frames, milliseconds are shown */ - int ms_dp = (power <= 0) ? (1 - power) : 1; /* precision of decimal part */ - int s_pad = ms_dp + 3; /* to get 2 digit whole-number part for seconds display (i.e. 3 is for 2 digits + radix, on top of full length) */ - - if (hours) sprintf(str, "%s%02d:%02d:%0*.*f", neg, hours, minutes, s_pad, ms_dp, cfra); - else sprintf(str, "%s%02d:%0*.*f", neg, minutes, s_pad, ms_dp, cfra); - break; - } - case USER_TIMECODE_SECONDS_ONLY: - { - /* only show the original seconds display */ - /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */ - if (power <= 0) sprintf(str, "%.*f", 1 - power, raw_seconds); - else sprintf(str, "%d", (int)floor(raw_seconds + GLA_PIXEL_OFS)); - break; - } - case USER_TIMECODE_SMPTE_FULL: - default: - { - /* full SMPTE format */ - sprintf(str, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames); - break; - } - } - } - else { - /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */ - if (power <= 0) sprintf(str, "%.*f", 1 - power, cfra); - else sprintf(str, "%d", (int)floor(cfra + GLA_PIXEL_OFS)); - } -} - /* *************************************************** */ /* CURRENT FRAME DRAWING */ /* Draw current frame number in a little green box beside the current frame indicator */ -static void draw_cfra_number(Scene *scene, View2D *v2d, float cfra, short time) +static void draw_cfra_number(Scene *scene, View2D *v2d, const float cfra, const bool time) { float xscale, yscale, x, y; char numstr[32] = " t"; /* t is the character to start replacing from */ @@ -189,10 +75,12 @@ static void draw_cfra_number(Scene *scene, View2D *v2d, float cfra, short time) * - power = 0, gives 'standard' behavior for time * but power = 1 is required for frames (to get integer frames) */ - if (time) - ANIM_timecode_string_from_frame(&numstr[4], scene, 0, time, FRA2TIME(cfra)); - else - ANIM_timecode_string_from_frame(&numstr[4], scene, 1, time, cfra); + if (time) { + BLI_timecode_string_from_time(&numstr[4], sizeof(numstr) - 4, 0, FRA2TIME(cfra), FPS, U.timecode_style); + } + else { + BLI_timecode_string_from_time_simple(&numstr[4], sizeof(numstr) - 4, 1, cfra); + } slen = (short)UI_GetStringWidth(numstr) - 1; /* get starting coordinates for drawing */ @@ -239,7 +127,7 @@ void ANIM_draw_cfra(const bContext *C, View2D *v2d, short flag) /* Draw current frame number in a little box */ if (flag & DRAWCFRA_SHOW_NUMBOX) { UI_view2d_view_orthoSpecial(CTX_wm_region(C), v2d, 1); - draw_cfra_number(scene, v2d, vec[0], (flag & DRAWCFRA_UNIT_SECONDS)); + draw_cfra_number(scene, v2d, vec[0], (flag & DRAWCFRA_UNIT_SECONDS) != 0); } } diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index dff5069d991..c13d0c45f21 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -471,9 +471,6 @@ void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, struct AnimData *adt, st /* DRAWING API */ /* anim_draw.c */ -/* Get string representing the given frame number as an appropriately represented frame or timecode */ -void ANIM_timecode_string_from_frame(char *str, struct Scene *scene, int power, short timecodes, float cfra); - /* ---------- Current Frame Drawing ---------------- */ /* flags for Current Frame Drawing */ diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 0d5198ea338..3bae255f297 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -78,7 +78,6 @@ enum eView2D_Units { V2D_UNIT_VALUES, V2D_UNIT_DEGREES, V2D_UNIT_TIME, - V2D_UNIT_SECONDSSEQ }; /* clamping of grid values to whole numbers */ diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index aadb1b30f5b..ea350fd08df 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -41,6 +41,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_timecode.h" #include "BKE_context.h" #include "BKE_screen.h" @@ -1613,7 +1614,13 @@ static void scroll_printstr(Scene *scene, float x, float y, float val, int power } /* get string to print */ - ANIM_timecode_string_from_frame(timecode_str, scene, power, (unit == V2D_UNIT_SECONDS), val); + if (unit == V2D_UNIT_SECONDS) { + /* not neces*/ + BLI_timecode_string_from_time(timecode_str, sizeof(timecode_str), power, val, FPS, U.timecode_style); + } + else { + BLI_timecode_string_from_time_simple(timecode_str, sizeof(timecode_str), power, val); + } /* get length of string, and adjust printing location to fit it into the horizontal scrollbar */ len = strlen(timecode_str); @@ -1737,17 +1744,6 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v scroll_printstr(scene, fac, h, fac2, grid->powerx, V2D_UNIT_SECONDS, 'h'); break; - case V2D_UNIT_SECONDSSEQ: /* seconds with special calculations (only used for sequencer only) */ - { - float time; - - fac2 = val / (float)FPS; - time = (float)floor(fac2); - fac2 = fac2 - time; - - scroll_printstr(scene, fac, h, time + (float)FPS * fac2 / 100.0f, grid->powerx, V2D_UNIT_SECONDSSEQ, 'h'); - break; - } case V2D_UNIT_DEGREES: /* Graph Editor for rotation Drivers */ /* HACK: although we're drawing horizontal, we make this draw as 'vertical', just to get degree signs */ scroll_printstr(scene, fac, h, val, grid->powerx, V2D_UNIT_DEGREES, 'v'); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 42f7e1b45b2..caade2dc59f 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1483,7 +1483,7 @@ void draw_timeline_seq(const bContext *C, ARegion *ar) UI_view2d_view_restore(C); /* scrollers */ - unit = (sseq->flag & SEQ_DRAWFRAMES) ? V2D_UNIT_FRAMES : V2D_UNIT_SECONDSSEQ; + unit = (sseq->flag & SEQ_DRAWFRAMES) ? V2D_UNIT_FRAMES : V2D_UNIT_SECONDS; scrollers = UI_view2d_scrollers_calc(C, v2d, unit, V2D_GRID_CLAMP, V2D_UNIT_VALUES, V2D_GRID_CLAMP); UI_view2d_scrollers_draw(C, v2d, scrollers); UI_view2d_scrollers_free(scrollers);