Blender V4.3
wm_playanim.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
15#include <cerrno>
16#include <cmath>
17#include <cstdlib>
18#include <cstring>
19#include <fcntl.h>
20#include <sys/types.h>
21
22#ifndef WIN32
23# include <sys/times.h>
24# include <sys/wait.h>
25# include <unistd.h>
26#else
27# include <io.h>
28#endif
29#include "MEM_guardedalloc.h"
30
31#include "CLG_log.h"
32
33#include "BLI_fileops.h"
34#include "BLI_listbase.h"
36#include "BLI_path_utils.hh"
37#include "BLI_rect.h"
38#include "BLI_string.h"
39#include "BLI_system.h"
40#include "BLI_time.h"
41#include "BLI_utildefines.h"
42
44#include "IMB_imbuf.hh"
45#include "IMB_imbuf_types.hh"
46
47#include "BKE_image.hh"
48
49#include "BIF_glutil.hh"
50
51#include "GPU_context.hh"
52#include "GPU_framebuffer.hh"
53#include "GPU_immediate.hh"
54#include "GPU_immediate_util.hh"
55#include "GPU_init_exit.hh"
56#include "GPU_matrix.hh"
57#include "GPU_state.hh"
58
59#include "BLF_api.hh"
60#include "DNA_scene_types.h"
61#include "GHOST_C-api.h"
62
63#include "DEG_depsgraph.hh"
64
65#include "wm_window_private.hh"
66
67#include "WM_api.hh" /* Only for #WM_main_playanim. */
68
69#ifdef WITH_AUDASPACE
70# include <AUD_Device.h>
71# include <AUD_Handle.h>
72# include <AUD_Sound.h>
73# include <AUD_Special.h>
74
75static struct {
76 AUD_Sound *source;
77 AUD_Handle *playback_handle;
78 AUD_Handle *scrub_handle;
79 AUD_Device *audio_device;
80} g_audaspace = {nullptr};
81#endif
82
83/* Simple limiter to avoid flooding memory. */
84#define USE_FRAME_CACHE_LIMIT
85#ifdef USE_FRAME_CACHE_LIMIT
86# define PLAY_FRAME_CACHE_MAX 30
87#endif
88
89static CLG_LogRef LOG = {"wm.playanim"};
90
91struct PlayState;
92static void playanim_window_zoom(PlayState &ps, const float zoom_offset);
94
95/* -------------------------------------------------------------------- */
104static bool buffer_from_filepath(const char *filepath,
105 void **r_mem,
106 size_t *r_size,
107 char **r_error_message)
108{
109 errno = 0;
110 const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
111 if (UNLIKELY(file == -1)) {
112 *r_error_message = BLI_sprintfN("failure '%s' to open file", strerror(errno));
113 return false;
114 }
115
116 bool success = false;
117 uchar *mem = nullptr;
118 const size_t size = BLI_file_descriptor_size(file);
119 int64_t size_read;
120 if (UNLIKELY(size == size_t(-1))) {
121 *r_error_message = BLI_sprintfN("failure '%s' to access size", strerror(errno));
122 }
123 else if (r_mem && UNLIKELY(!(mem = static_cast<uchar *>(MEM_mallocN(size, __func__))))) {
124 *r_error_message = BLI_sprintfN("error allocating buffer %" PRIu64 " size", uint64_t(size));
125 }
126 else if (r_mem && UNLIKELY((size_read = BLI_read(file, mem, size)) != size)) {
127 *r_error_message = BLI_sprintfN(
128 "error '%s' reading file "
129 "(expected %" PRIu64 ", was %" PRId64 ")",
130 strerror(errno),
131 uint64_t(size),
132 size_read);
133 }
134 else {
135 close(file);
136 *r_size = size;
137 if (r_mem) {
138 *r_mem = mem;
139 mem = nullptr; /* `r_mem` owns, don't free on exit. */
140 }
141 success = true;
142 }
143
144 MEM_SAFE_FREE(mem);
145 close(file);
146 return success;
147}
148
153 WS_QUAL_LSHIFT = (1 << 0),
154 WS_QUAL_RSHIFT = (1 << 1),
155#define WS_QUAL_SHIFT (WS_QUAL_LSHIFT | WS_QUAL_RSHIFT)
156 WS_QUAL_LALT = (1 << 2),
157 WS_QUAL_RALT = (1 << 3),
158#define WS_QUAL_ALT (WS_QUAL_LALT | WS_QUAL_RALT)
159 WS_QUAL_LCTRL = (1 << 4),
160 WS_QUAL_RCTRL = (1 << 5),
161#define WS_QUAL_CTRL (WS_QUAL_LCTRL | WS_QUAL_RCTRL)
162 WS_QUAL_LMOUSE = (1 << 16),
163 WS_QUAL_MMOUSE = (1 << 17),
164 WS_QUAL_RMOUSE = (1 << 18),
166};
168
169struct GhostData {
170 GHOST_SystemHandle system;
171 GHOST_WindowHandle window;
172
175
178};
179
180struct PlayArgs {
181 int argc;
182 char **argv;
183};
184
197
269
270/* For debugging. */
271#if 0
272static void print_ps(const PlayState &ps)
273{
274 printf("ps:\n");
275 printf(" direction=%d,\n", int(ps.direction));
276 printf(" once=%d,\n", ps.once);
277 printf(" pingpong=%d,\n", ps.pingpong);
278 printf(" no_frame_skip=%d,\n", ps.no_frame_skip);
279 printf(" single_step=%d,\n", ps.single_step);
280 printf(" wait=%d,\n", ps.wait);
281 printf(" stopped=%d,\n", ps.stopped);
282 printf(" go=%d,\n\n", ps.go);
283 fflush(stdout);
284}
285#endif
286
287static blender::int2 playanim_window_size_get(GHOST_WindowHandle ghost_window)
288{
289 ;
290 GHOST_RectangleHandle bounds = GHOST_GetClientBounds(ghost_window);
291 const float native_pixel_size = GHOST_GetNativePixelSize(ghost_window);
292 const blender::int2 window_size = {
293 int(GHOST_GetWidthRectangle(bounds) * native_pixel_size),
294 int(GHOST_GetHeightRectangle(bounds) * native_pixel_size),
295 };
297 return window_size;
298}
299
301{
302 /* Unified matrix, note it affects offset for drawing. */
303 /* NOTE: cannot use GPU_matrix_ortho_2d_set here because shader ignores. */
304 GPU_matrix_ortho_set(0.0f, 1.0f, 0.0f, 1.0f, -1.0, 1.0f);
305}
306
307/* Implementation. */
308static void playanim_event_qual_update(GhostData &ghost_data)
309{
310 bool val;
311
312 /* Shift. */
314 SET_FLAG_FROM_TEST(ghost_data.qual, val, WS_QUAL_LSHIFT);
315
317 SET_FLAG_FROM_TEST(ghost_data.qual, val, WS_QUAL_RSHIFT);
318
319 /* Control. */
321 SET_FLAG_FROM_TEST(ghost_data.qual, val, WS_QUAL_LCTRL);
322
324 SET_FLAG_FROM_TEST(ghost_data.qual, val, WS_QUAL_RCTRL);
325
326 /* Alt. */
328 SET_FLAG_FROM_TEST(ghost_data.qual, val, WS_QUAL_LALT);
329
331 SET_FLAG_FROM_TEST(ghost_data.qual, val, WS_QUAL_RALT);
332}
333
337 size_t size;
339 const char *filepath;
344 int frame;
346
347#ifdef USE_FRAME_CACHE_LIMIT
351#endif
352};
353
359static struct {
361 double swap_time;
363#ifdef WITH_AUDASPACE
364 double fps_movie;
365#endif
366} g_playanim = {
367 /*from_disk*/ false,
368 /*swap_time*/ 0.04,
369 /*total_time*/ 0.0,
370#ifdef WITH_AUDASPACE
371 /*fps_movie*/ 0.0,
372#endif
374
375#ifdef USE_FRAME_CACHE_LIMIT
376static struct {
385} g_frame_cache = {
386 /*pics*/ {nullptr, nullptr},
387 /*pics_len*/ 0,
388 /*pics_size_in_memory*/ 0,
389 /*memory_limit*/ 0,
391
393{
396 g_frame_cache.pics_len++;
397
398 if (g_frame_cache.memory_limit != 0) {
399 BLI_assert(pic->size_in_memory == 0);
401 g_frame_cache.pics_size_in_memory += pic->size_in_memory;
402 }
403}
404
406{
407 LinkData *node = pic->frame_cache_node;
408 IMB_freeImBuf(pic->ibuf);
409 if (g_frame_cache.memory_limit != 0) {
410 BLI_assert(pic->size_in_memory != 0);
411 g_frame_cache.pics_size_in_memory -= pic->size_in_memory;
412 pic->size_in_memory = 0;
413 }
414 pic->ibuf = nullptr;
415 pic->frame_cache_node = nullptr;
416 BLI_freelinkN(&g_frame_cache.pics, node);
417 g_frame_cache.pics_len--;
418}
419
420/* Don't free the current frame by moving it to the head of the list. */
427
429{
430 return g_frame_cache.memory_limit ?
431 (g_frame_cache.pics_size_in_memory > g_frame_cache.memory_limit) :
433}
434
435static void frame_cache_limit_apply(ImBuf *ibuf_keep)
436{
437 /* Really basic memory conservation scheme. Keep frames in a FIFO queue. */
438 LinkData *node = static_cast<LinkData *>(g_frame_cache.pics.last);
439 while (node && frame_cache_limit_exceeded()) {
440 PlayAnimPict *pic = static_cast<PlayAnimPict *>(node->data);
441 BLI_assert(pic->frame_cache_node == node);
442
443 node = node->prev;
444 if (pic->ibuf && pic->ibuf != ibuf_keep) {
446 }
447 }
448}
449
450#endif /* USE_FRAME_CACHE_LIMIT */
451
453{
454 ImBuf *ibuf = nullptr;
455
456 if (pic->ibuf) {
457 ibuf = pic->ibuf;
458 }
459 else if (pic->anim) {
461 }
462 else if (pic->mem) {
463 /* Use correct color-space here. */
464 ibuf = IMB_ibImageFromMemory(pic->mem, pic->size, pic->IB_flags, nullptr, pic->filepath);
465 }
466 else {
467 /* Use correct color-space here. */
468 ibuf = IMB_loadiffname(pic->filepath, pic->IB_flags, nullptr);
469 }
470
471 return ibuf;
472}
473
474static PlayAnimPict *playanim_step(PlayAnimPict *playanim, int step)
475{
476 if (step > 0) {
477 while (step-- && playanim) {
478 playanim = playanim->next;
479 }
480 }
481 else if (step < 0) {
482 while (step++ && playanim) {
483 playanim = playanim->prev;
484 }
485 }
486 return playanim;
487}
488
489static int pupdate_time()
490{
491 static double time_last;
492
493 double time = BLI_time_now_seconds();
494
495 g_playanim.total_time += (time - time_last);
496 time_last = time;
497 return (g_playanim.total_time < 0.0);
498}
499
500static void *ocio_transform_ibuf(const PlayDisplayContext &display_ctx,
501 ImBuf *ibuf,
502 bool *r_glsl_used,
503 eGPUTextureFormat *r_format,
504 eGPUDataFormat *r_data,
505 void **r_buffer_cache_handle)
506{
507 void *display_buffer;
508 bool force_fallback = false;
509 *r_glsl_used = false;
510 force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
511 force_fallback |= (ibuf->dither != 0.0f);
512
513 /* Default. */
514 *r_format = GPU_RGBA8;
515 *r_data = GPU_DATA_UBYTE;
516
517 /* Fallback to CPU based color space conversion. */
518 if (force_fallback) {
519 *r_glsl_used = false;
520 display_buffer = nullptr;
521 }
522 else if (ibuf->float_buffer.data) {
523 display_buffer = ibuf->float_buffer.data;
524
525 *r_data = GPU_DATA_FLOAT;
526 if (ibuf->channels == 4) {
527 *r_format = GPU_RGBA16F;
528 }
529 else if (ibuf->channels == 3) {
530 /* Alpha is implicitly 1. */
531 *r_format = GPU_RGB16F;
532 }
533
534 if (ibuf->float_buffer.colorspace) {
536 &display_ctx.display_settings,
538 ibuf->dither,
539 false,
540 false);
541 }
542 else {
544 &display_ctx.view_settings, &display_ctx.display_settings, ibuf->dither, false);
545 }
546 }
547 else if (ibuf->byte_buffer.data) {
548 display_buffer = ibuf->byte_buffer.data;
550 &display_ctx.display_settings,
552 ibuf->dither,
553 false,
554 false);
555 }
556 else {
557 display_buffer = nullptr;
558 }
559
560 /* There is data to be displayed, but GLSL is not initialized
561 * properly, in this case we fallback to CPU-based display transform. */
562 if ((ibuf->byte_buffer.data || ibuf->float_buffer.data) && !*r_glsl_used) {
563 display_buffer = IMB_display_buffer_acquire(
564 ibuf, &display_ctx.view_settings, &display_ctx.display_settings, r_buffer_cache_handle);
565 *r_format = GPU_RGBA8;
566 *r_data = GPU_DATA_UBYTE;
567 }
568
569 return display_buffer;
570}
571
572static void draw_display_buffer(const PlayDisplayContext &display_ctx,
573 ImBuf *ibuf,
574 const rctf *canvas,
575 const bool draw_flip[2])
576{
577 /* Format needs to be created prior to any #immBindShader call.
578 * Do it here because OCIO binds its own shader. */
581 bool glsl_used = false;
582 GPUVertFormat *imm_format = immVertexFormat();
584 uint texCoord = GPU_vertformat_attr_add(
585 imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
586
587 void *buffer_cache_handle = nullptr;
588 void *display_buffer = ocio_transform_ibuf(
589 display_ctx, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
590
591 /* NOTE: This may fail, especially for large images that exceed the GPU's texture size limit.
592 * Large images could be supported although this isn't so common for animation playback. */
593 GPUTexture *texture = GPU_texture_create_2d(
594 "display_buf", ibuf->x, ibuf->y, 1, format, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
595
596 if (texture) {
597 GPU_texture_update(texture, data, display_buffer);
598 GPU_texture_filter_mode(texture, false);
599
600 GPU_texture_bind(texture, 0);
601 }
602
603 if (!glsl_used) {
605 immUniformColor3f(1.0f, 1.0f, 1.0f);
606 }
607
609
610 rctf preview;
611 BLI_rctf_init(&preview, 0.0f, 1.0f, 0.0f, 1.0f);
612 if (draw_flip[0]) {
613 std::swap(preview.xmin, preview.xmax);
614 }
615 if (draw_flip[1]) {
616 std::swap(preview.ymin, preview.ymax);
617 }
618
619 immAttr2f(texCoord, preview.xmin, preview.ymin);
620 immVertex2f(pos, canvas->xmin, canvas->ymin);
621
622 immAttr2f(texCoord, preview.xmin, preview.ymax);
623 immVertex2f(pos, canvas->xmin, canvas->ymax);
624
625 immAttr2f(texCoord, preview.xmax, preview.ymax);
626 immVertex2f(pos, canvas->xmax, canvas->ymax);
627
628 immAttr2f(texCoord, preview.xmax, preview.ymin);
629 immVertex2f(pos, canvas->xmax, canvas->ymin);
630
631 immEnd();
632
633 if (texture) {
634 GPU_texture_unbind(texture);
635 GPU_texture_free(texture);
636 }
637
638 if (!glsl_used) {
640 }
641 else {
643 }
644
645 if (buffer_cache_handle) {
646 IMB_display_buffer_release(buffer_cache_handle);
647 }
648}
649
657static void playanim_toscreen_ex(GhostData &ghost_data,
658 const PlayDisplayContext &display_ctx,
659 const PlayAnimPict *picture,
660 ImBuf *ibuf,
661 /* Run-time drawing arguments (not used on-load). */
662 const int font_id,
663 const int frame_step,
664 const float draw_zoom,
665 const bool draw_flip[2],
666 const float frame_indicator_factor)
667{
670
671 GPUContext *restore_context = GPU_context_active_get();
673
674 GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f);
675
676 /* A null `ibuf` is an exceptional case and should almost never happen.
677 * if it does, this function displays a warning along with the file-path that failed. */
678 if (ibuf) {
679 /* Size within window. */
680 float span_x = (draw_zoom * ibuf->x) / float(display_ctx.size[0]);
681 float span_y = (draw_zoom * ibuf->y) / float(display_ctx.size[1]);
682
683 /* Offset within window. */
684 float offs_x = 0.5f * (1.0f - span_x);
685 float offs_y = 0.5f * (1.0f - span_y);
686
687 CLAMP(offs_x, 0.0f, 1.0f);
688 CLAMP(offs_y, 0.0f, 1.0f);
689
690 /* Checkerboard for case alpha. */
691 if (ibuf->planes == 32) {
693
695 offs_y,
696 offs_x + span_x,
697 offs_y + span_y,
698 blender::float4{0.15, 0.15, 0.15, 1.0},
699 blender::float4{0.20, 0.20, 0.20, 1.0},
700 8);
701 }
702 rctf canvas;
703 BLI_rctf_init(&canvas, offs_x, offs_x + span_x, offs_y, offs_y + span_y);
704
705 draw_display_buffer(display_ctx, ibuf, &canvas, draw_flip);
706
708 }
709
710 pupdate_time();
711
712 if ((font_id != -1) && picture) {
713 const int font_margin = int(10 * display_ctx.ui_scale);
714 float fsizex_inv, fsizey_inv;
715 char label[32 + FILE_MAX];
716 if (ibuf) {
717 SNPRINTF(label, "%s | %.2f frames/s", picture->filepath, frame_step / g_playanim.swap_time);
718 }
719 else {
721 "%s | %s",
722 picture->filepath,
723 picture->error_message ? picture->error_message : "<unknown error>");
724 }
725
726 const blender::int2 window_size = playanim_window_size_get(ghost_data.window);
727 fsizex_inv = 1.0f / window_size[0];
728 fsizey_inv = 1.0f / window_size[1];
729
730 BLF_color4f(font_id, 1.0, 1.0, 1.0, 1.0);
731
732 /* FIXME(@ideasman42): Font positioning doesn't work because the aspect causes the position
733 * to be rounded to zero, investigate making BLF support this,
734 * for now use GPU matrix API to adjust the text position. */
735#if 0
736 BLF_enable(font_id, BLF_ASPECT);
737 BLF_aspect(font_id, fsizex_inv, fsizey_inv, 1.0f);
738 BLF_position(font_id, font_margin * fsizex_inv, font_margin * fsizey_inv, 0.0f);
739 BLF_draw(font_id, label, sizeof(label));
740#else
742 GPU_matrix_scale_2f(fsizex_inv, fsizey_inv);
743 GPU_matrix_translate_2f(font_margin, font_margin);
744 BLF_position(font_id, 0, 0, 0.0f);
745 BLF_draw(font_id, label, sizeof(label));
747#endif
748 }
749
750 if (frame_indicator_factor != -1.0f) {
751 float fac = frame_indicator_factor;
752 fac = 2.0f * fac - 1.0f;
757
759
761 immUniformColor3ub(0, 255, 0);
762
764 immVertex2f(pos, fac, -1.0f);
765 immVertex2f(pos, fac, 1.0f);
766 immEnd();
767
769
772 }
773
776 GPU_flush();
777 }
778
780 GPU_context_active_set(restore_context);
782}
783
784static void playanim_toscreen_on_load(GhostData &ghost_data,
785 const PlayDisplayContext &display_ctx,
786 const PlayAnimPict *picture,
787 ImBuf *ibuf)
788{
789 const int font_id = -1; /* Don't draw text. */
790 const int frame_step = -1;
791 const float zoom = 1.0f;
792 const float frame_indicator_factor = -1.0f;
793 const bool draw_flip[2] = {false, false};
794
795 playanim_toscreen_ex(ghost_data,
796 display_ctx,
797 picture,
798 ibuf,
799 font_id,
800 frame_step,
801 zoom,
802 draw_flip,
803 frame_indicator_factor);
804}
805
806static void playanim_toscreen(PlayState &ps, const PlayAnimPict *picture, ImBuf *ibuf)
807{
808 float frame_indicator_factor = -1.0f;
809 if (ps.show_frame_indicator) {
810 const int frame_range = static_cast<const PlayAnimPict *>(ps.picsbase.last)->frame -
811 static_cast<const PlayAnimPict *>(ps.picsbase.first)->frame;
812 if (frame_range > 0) {
813 frame_indicator_factor = float(double(picture->frame) / double(frame_range));
814 }
815 else {
817 "Multiple frames without a valid range!");
818 }
819 }
820
821 int font_id = -1;
823 /* Always inform the user of an error, this should be an exceptional case. */
824 (ibuf == nullptr))
825 {
826 font_id = ps.font_id;
827 }
828
829 BLI_assert(ps.loading == false);
831 ps.display_ctx,
832 picture,
833 ibuf,
834 font_id,
835 ps.frame_step,
836 ps.zoom,
837 ps.draw_flip,
838 frame_indicator_factor);
839}
840
842 GhostData &ghost_data,
843 const PlayDisplayContext &display_ctx,
844 const char *filepath_first,
845 const int frame_offset)
846{
847 /* OCIO_TODO: support different input color space. */
848 ImBufAnim *anim = IMB_open_anim(filepath_first, IB_rect, 0, nullptr);
849 if (anim == nullptr) {
850 CLOG_WARN(&LOG, "couldn't open anim '%s'", filepath_first);
851 return;
852 }
853
855 if (ibuf) {
856 playanim_toscreen_on_load(ghost_data, display_ctx, nullptr, ibuf);
857 IMB_freeImBuf(ibuf);
858 }
859
860 for (int pic = 0; pic < IMB_anim_get_duration(anim, IMB_TC_NONE); pic++) {
861 PlayAnimPict *picture = static_cast<PlayAnimPict *>(MEM_callocN(sizeof(PlayAnimPict), "Pict"));
862 picture->anim = anim;
863 picture->frame = pic + frame_offset;
864 picture->IB_flags = IB_rect;
865 picture->filepath = BLI_sprintfN("%s : %4.d", filepath_first, pic + 1);
866 BLI_addtail(&picsbase, picture);
867 }
868
869 const PlayAnimPict *picture = static_cast<const PlayAnimPict *>(picsbase.last);
870 if (!(picture && picture->anim == anim)) {
871 IMB_close_anim(anim);
872 CLOG_WARN(&LOG, "no frames added for: '%s'", filepath_first);
873 }
874}
875
877 GhostData &ghost_data,
878 const PlayDisplayContext &display_ctx,
879 const char *filepath_first,
880 const int frame_offset,
881 const int totframes,
882 const int frame_step,
883 const bool *loading_p)
884{
885 /* Load images into cache until the cache is full,
886 * this resolves choppiness for images that are slow to load, see: #81751. */
887 bool fill_cache = (
888#ifdef USE_FRAME_CACHE_LIMIT
889 true
890#else
891 false
892#endif
893 );
894
895 int fp_framenr;
896 struct {
897 char head[FILE_MAX], tail[FILE_MAX];
898 ushort digits;
899 } fp_decoded;
900
901 char filepath[FILE_MAX];
902 STRNCPY(filepath, filepath_first);
903 fp_framenr = BLI_path_sequence_decode(filepath,
904 fp_decoded.head,
905 sizeof(fp_decoded.head),
906 fp_decoded.tail,
907 sizeof(fp_decoded.tail),
908 &fp_decoded.digits);
909
910 pupdate_time();
911 g_playanim.total_time = 1.0;
912
913 for (int pic = 0; pic < totframes; pic++) {
914 if (!IMB_ispic(filepath)) {
915 break;
916 }
917
918 bool has_error = false;
919 char *error_message = nullptr;
920 void *mem = nullptr;
921 size_t size = -1;
923 filepath, g_playanim.from_disk ? nullptr : &mem, &size, &error_message))
924 {
925 has_error = true;
926 size = 0;
927 }
928
929 PlayAnimPict *picture = static_cast<PlayAnimPict *>(
930 MEM_callocN(sizeof(PlayAnimPict), "picture"));
931 picture->size = size;
932 picture->IB_flags = IB_rect;
933 picture->mem = static_cast<uchar *>(mem);
934 picture->filepath = BLI_strdup(filepath);
935 picture->error_message = error_message;
936 picture->frame = pic + frame_offset;
937 BLI_addtail(&picsbase, picture);
938
939 pupdate_time();
940
941 const bool display_imbuf = g_playanim.total_time > 1.0;
942
943 if (has_error) {
944 CLOG_WARN(&LOG,
945 "Picture %s failed: %s",
946 filepath,
947 error_message ? error_message : "<unknown error>");
948 }
949 else if (display_imbuf || fill_cache) {
950 /* OCIO_TODO: support different input color space. */
951 ImBuf *ibuf = ibuf_from_picture(picture);
952
953 if (ibuf) {
954 if (display_imbuf) {
955 playanim_toscreen_on_load(ghost_data, display_ctx, picture, ibuf);
956 }
957#ifdef USE_FRAME_CACHE_LIMIT
958 if (fill_cache) {
959 picture->ibuf = ibuf;
960 frame_cache_add(picture);
961 fill_cache = !frame_cache_limit_exceeded();
962 }
963 else
964#endif
965 {
966 IMB_freeImBuf(ibuf);
967 }
968 }
969
970 if (display_imbuf) {
971 pupdate_time();
972 g_playanim.total_time = 0.0;
973 }
974 }
975
976 /* Create a new file-path each time. */
977 fp_framenr += frame_step;
979 sizeof(filepath),
980 fp_decoded.head,
981 fp_decoded.tail,
982 fp_decoded.digits,
983 fp_framenr);
984
985 while (GHOST_ProcessEvents(ghost_data.system, false)) {
986 GHOST_DispatchEvents(ghost_data.system);
987 if (*loading_p == false) {
988 break;
989 }
990 }
991 }
992}
993
994static void build_pict_list(ListBase &picsbase,
995 GhostData &ghost_data,
996 const PlayDisplayContext &display_ctx,
997 const char *filepath_first,
998 const int totframes,
999 const int frame_step,
1000 bool *loading_p)
1001{
1002 *loading_p = true;
1003
1004 /* NOTE(@ideasman42): When loading many files (expanded from shell globing for e.g.)
1005 * it's important the frame number increases each time. Otherwise playing `*.png`
1006 * in a directory will expand into many arguments, each calling this function adding
1007 * a frame that's set to zero. */
1008 const PlayAnimPict *picture_last = static_cast<PlayAnimPict *>(picsbase.last);
1009 const int frame_offset = picture_last ? (picture_last->frame + 1) : 0;
1010
1011 bool do_image_load = false;
1012 if (IMB_isanim(filepath_first)) {
1013 build_pict_list_from_anim(picsbase, ghost_data, display_ctx, filepath_first, frame_offset);
1014
1015 if (picsbase.last == picture_last) {
1016 /* FFMPEG detected JPEG2000 as a video which would load with zero duration.
1017 * Resolve this by using images as a fallback when a video file has no frames to display. */
1018 do_image_load = true;
1019 }
1020 }
1021 else {
1022 do_image_load = true;
1023 }
1024
1025 if (do_image_load) {
1027 ghost_data,
1028 display_ctx,
1029 filepath_first,
1030 frame_offset,
1031 totframes,
1032 frame_step,
1033 loading_p);
1034 }
1035
1036 *loading_p = false;
1037}
1038
1039static void update_sound_fps()
1040{
1041#ifdef WITH_AUDASPACE
1042 if (g_audaspace.playback_handle) {
1043 /* Swap-time stores the 1.0/fps ratio. */
1044 double speed = 1.0 / (g_playanim.swap_time * g_playanim.fps_movie);
1045
1046 AUD_Handle_setPitch(g_audaspace.playback_handle, speed);
1047 }
1048#endif
1049}
1050
1051static void playanim_change_frame_tag(PlayState &ps, int cx)
1052{
1053 ps.need_frame_update = true;
1054 ps.frame_cursor_x = cx;
1055}
1056
1058{
1059 if (!ps.need_frame_update) {
1060 return;
1061 }
1063 return;
1064 }
1065
1067 const int i_last = static_cast<PlayAnimPict *>(ps.picsbase.last)->frame;
1068 /* Without this the frame-indicator location isn't closest to the cursor. */
1069 const int correct_rounding = (window_size[0] / (i_last + 1)) / 2;
1070 const int i = clamp_i(
1071 (i_last * (ps.frame_cursor_x + correct_rounding)) / window_size[0], 0, i_last);
1072
1073#ifdef WITH_AUDASPACE
1074 if (g_audaspace.scrub_handle) {
1075 AUD_Handle_stop(g_audaspace.scrub_handle);
1076 g_audaspace.scrub_handle = nullptr;
1077 }
1078
1079 if (g_audaspace.playback_handle) {
1080 AUD_Status status = AUD_Handle_getStatus(g_audaspace.playback_handle);
1081 if (status != AUD_STATUS_PLAYING) {
1082 AUD_Handle_stop(g_audaspace.playback_handle);
1083 g_audaspace.playback_handle = AUD_Device_play(
1084 g_audaspace.audio_device, g_audaspace.source, 1);
1085 if (g_audaspace.playback_handle) {
1086 AUD_Handle_setPosition(g_audaspace.playback_handle, i / g_playanim.fps_movie);
1087 g_audaspace.scrub_handle = AUD_pauseAfter(g_audaspace.playback_handle,
1088 1.0 / g_playanim.fps_movie);
1089 }
1091 }
1092 else {
1093 AUD_Handle_setPosition(g_audaspace.playback_handle, i / g_playanim.fps_movie);
1094 g_audaspace.scrub_handle = AUD_pauseAfter(g_audaspace.playback_handle,
1095 1.0 / g_playanim.fps_movie);
1096 }
1097 }
1098 else if (g_audaspace.source) {
1099 g_audaspace.playback_handle = AUD_Device_play(g_audaspace.audio_device, g_audaspace.source, 1);
1100 if (g_audaspace.playback_handle) {
1101 AUD_Handle_setPosition(g_audaspace.playback_handle, i / g_playanim.fps_movie);
1102 g_audaspace.scrub_handle = AUD_pauseAfter(g_audaspace.playback_handle,
1103 1.0 / g_playanim.fps_movie);
1104 }
1106 }
1107#endif
1108
1109 ps.picture = static_cast<PlayAnimPict *>(BLI_findlink(&ps.picsbase, i));
1110 BLI_assert(ps.picture != nullptr);
1111
1112 ps.single_step = true;
1113 ps.wait = false;
1114 ps.next_frame = 0;
1115
1116 ps.need_frame_update = false;
1117}
1118
1120{
1121#ifdef WITH_AUDASPACE
1122 /* TODO: store in ps direct? */
1123 const int i = BLI_findindex(&ps.picsbase, ps.picture);
1124 if (g_audaspace.playback_handle) {
1125 AUD_Handle_stop(g_audaspace.playback_handle);
1126 }
1127 g_audaspace.playback_handle = AUD_Device_play(g_audaspace.audio_device, g_audaspace.source, 1);
1128 if (g_audaspace.playback_handle) {
1129 AUD_Handle_setPosition(g_audaspace.playback_handle, i / g_playanim.fps_movie);
1130 }
1132#else
1133 UNUSED_VARS(ps);
1134#endif
1135}
1136
1137static void playanim_audio_stop(PlayState & /*ps*/)
1138{
1139#ifdef WITH_AUDASPACE
1140 if (g_audaspace.playback_handle) {
1141 AUD_Handle_stop(g_audaspace.playback_handle);
1142 g_audaspace.playback_handle = nullptr;
1143 }
1144#endif
1145}
1146
1147static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr ps_void_ptr)
1148{
1149 PlayState &ps = *static_cast<PlayState *>(ps_void_ptr);
1150 const GHOST_TEventType type = GHOST_GetEventType(ghost_event);
1151 GHOST_TEventDataPtr data = GHOST_GetEventData(ghost_event);
1152 /* Convert ghost event into value keyboard or mouse. */
1153 const int val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
1154 GHOST_SystemHandle ghost_system = ps.ghost_data.system;
1155 GHOST_WindowHandle ghost_window = ps.ghost_data.window;
1156
1157 // print_ps(ps);
1158
1160
1161 /* First check if we're busy loading files. */
1162 if (ps.loading) {
1163 switch (type) {
1165 case GHOST_kEventKeyUp: {
1166 const GHOST_TEventKeyData *key_data = static_cast<const GHOST_TEventKeyData *>(data);
1167 switch (key_data->key) {
1168 case GHOST_kKeyEsc:
1169 ps.loading = false;
1170 break;
1171 default:
1172 break;
1173 }
1174 break;
1175 }
1176 default:
1177 break;
1178 }
1179 return true;
1180 }
1181
1182 if (ps.wait && ps.stopped == false) {
1183 ps.stopped = true;
1184 }
1185
1186 if (ps.wait) {
1187 pupdate_time();
1188 g_playanim.total_time = 0.0;
1189 }
1190
1191 switch (type) {
1193 case GHOST_kEventKeyUp: {
1194 const GHOST_TEventKeyData *key_data = static_cast<const GHOST_TEventKeyData *>(data);
1195 switch (key_data->key) {
1196 case GHOST_kKeyA:
1197 if (val) {
1199 }
1200 break;
1201 case GHOST_kKeyI:
1202 if (val) {
1204 }
1205 break;
1206 case GHOST_kKeyP:
1207 if (val) {
1208 ps.pingpong = !ps.pingpong;
1209 }
1210 break;
1211 case GHOST_kKeyF: {
1212 if (val) {
1213 int axis = (ps.ghost_data.qual & WS_QUAL_SHIFT) ? 1 : 0;
1214 ps.draw_flip[axis] = !ps.draw_flip[axis];
1215 }
1216 break;
1217 }
1218 case GHOST_kKey1:
1219 case GHOST_kKeyNumpad1:
1220 if (val) {
1221 g_playanim.swap_time = ps.frame_step / 60.0;
1223 }
1224 break;
1225 case GHOST_kKey2:
1226 case GHOST_kKeyNumpad2:
1227 if (val) {
1228 g_playanim.swap_time = ps.frame_step / 50.0;
1230 }
1231 break;
1232 case GHOST_kKey3:
1233 case GHOST_kKeyNumpad3:
1234 if (val) {
1235 g_playanim.swap_time = ps.frame_step / 30.0;
1237 }
1238 break;
1239 case GHOST_kKey4:
1240 case GHOST_kKeyNumpad4:
1241 if (ps.ghost_data.qual & WS_QUAL_SHIFT) {
1242 g_playanim.swap_time = ps.frame_step / 24.0;
1244 }
1245 else {
1246 g_playanim.swap_time = ps.frame_step / 25.0;
1248 }
1249 break;
1250 case GHOST_kKey5:
1251 case GHOST_kKeyNumpad5:
1252 if (val) {
1253 g_playanim.swap_time = ps.frame_step / 20.0;
1255 }
1256 break;
1257 case GHOST_kKey6:
1258 case GHOST_kKeyNumpad6:
1259 if (val) {
1260 g_playanim.swap_time = ps.frame_step / 15.0;
1262 }
1263 break;
1264 case GHOST_kKey7:
1265 case GHOST_kKeyNumpad7:
1266 if (val) {
1267 g_playanim.swap_time = ps.frame_step / 12.0;
1269 }
1270 break;
1271 case GHOST_kKey8:
1272 case GHOST_kKeyNumpad8:
1273 if (val) {
1274 g_playanim.swap_time = ps.frame_step / 10.0;
1276 }
1277 break;
1278 case GHOST_kKey9:
1279 case GHOST_kKeyNumpad9:
1280 if (val) {
1281 g_playanim.swap_time = ps.frame_step / 6.0;
1283 }
1284 break;
1286 if (val) {
1287 ps.single_step = true;
1288 ps.wait = false;
1290
1291 if (ps.ghost_data.qual & WS_QUAL_SHIFT) {
1292 ps.picture = static_cast<PlayAnimPict *>(ps.picsbase.first);
1293 ps.next_frame = 0;
1294 }
1295 else {
1296 ps.next_frame = -1;
1297 }
1298 }
1299 break;
1301 if (val) {
1302 ps.wait = false;
1304
1305 if (ps.ghost_data.qual & WS_QUAL_SHIFT) {
1306 ps.next_frame = ps.direction = -1;
1307 }
1308 else {
1309 ps.next_frame = -10;
1310 ps.single_step = true;
1311 }
1312 }
1313 break;
1315 if (val) {
1316 ps.single_step = true;
1317 ps.wait = false;
1319
1320 if (ps.ghost_data.qual & WS_QUAL_SHIFT) {
1321 ps.picture = static_cast<PlayAnimPict *>(ps.picsbase.last);
1322 ps.next_frame = 0;
1323 }
1324 else {
1325 ps.next_frame = 1;
1326 }
1327 }
1328 break;
1329 case GHOST_kKeyUpArrow:
1330 if (val) {
1331 ps.wait = false;
1332 if (ps.ghost_data.qual & WS_QUAL_SHIFT) {
1333 ps.next_frame = ps.direction = 1;
1334 if (ps.single_step == false) {
1336 }
1337 }
1338 else {
1339 ps.next_frame = 10;
1340 ps.single_step = true;
1342 }
1343 }
1344 break;
1345
1346 case GHOST_kKeySlash:
1348 if (val) {
1349 if (ps.ghost_data.qual & WS_QUAL_SHIFT) {
1350 if (ps.picture && ps.picture->ibuf) {
1351 printf(" Name: %s | Speed: %.2f frames/s\n",
1352 ps.picture->ibuf->filepath,
1353 ps.frame_step / g_playanim.swap_time);
1354 }
1355 }
1356 else {
1357 g_playanim.swap_time = ps.frame_step / 5.0;
1359 }
1360 }
1361 break;
1362 case GHOST_kKey0:
1363 case GHOST_kKeyNumpad0:
1364 if (val) {
1365 if (ps.once) {
1366 ps.once = ps.wait = false;
1367 }
1368 else {
1369 ps.picture = nullptr;
1370 ps.once = true;
1371 ps.wait = false;
1372 }
1373 }
1374 break;
1375
1376 case GHOST_kKeySpace:
1377 if (val) {
1378 if (ps.wait || ps.single_step) {
1379 ps.wait = ps.single_step = false;
1381 }
1382 else {
1383 ps.single_step = true;
1384 ps.wait = true;
1386 }
1387 }
1388 break;
1389 case GHOST_kKeyEnter:
1391 if (val) {
1392 ps.wait = ps.single_step = false;
1394 }
1395 break;
1396 case GHOST_kKeyPeriod:
1398 if (val) {
1399 if (ps.single_step) {
1400 ps.wait = false;
1401 }
1402 else {
1403 ps.single_step = true;
1404 ps.wait = !ps.wait;
1406 }
1407 }
1408 break;
1409 case GHOST_kKeyEqual:
1410 case GHOST_kKeyPlus:
1411 case GHOST_kKeyNumpadPlus: {
1412 if (val == 0) {
1413 break;
1414 }
1415 if (ps.ghost_data.qual & WS_QUAL_CTRL) {
1416 playanim_window_zoom(ps, 0.1f);
1417 }
1418 else {
1419 if (g_playanim.swap_time > ps.frame_step / 60.0) {
1420 g_playanim.swap_time /= 1.1;
1422 }
1423 }
1424 break;
1425 }
1426 case GHOST_kKeyMinus:
1427 case GHOST_kKeyNumpadMinus: {
1428 if (val == 0) {
1429 break;
1430 }
1431 if (ps.ghost_data.qual & WS_QUAL_CTRL) {
1432 playanim_window_zoom(ps, -0.1f);
1433 }
1434 else {
1435 if (g_playanim.swap_time < ps.frame_step / 5.0) {
1436 g_playanim.swap_time *= 1.1;
1438 }
1439 }
1440 break;
1441 }
1442 case GHOST_kKeyEsc:
1443 ps.go = false;
1444 break;
1445 default:
1446 break;
1447 }
1448 break;
1449 }
1451 case GHOST_kEventButtonUp: {
1452 const GHOST_TEventButtonData *bd = static_cast<const GHOST_TEventButtonData *>(data);
1453 int cx, cy;
1454 const blender::int2 window_size = playanim_window_size_get(ghost_window);
1455
1456 const bool inside_window = (GHOST_GetCursorPosition(ghost_system, ghost_window, &cx, &cy) ==
1457 GHOST_kSuccess) &&
1458 (cx >= 0 && cx < window_size[0] && cy >= 0 &&
1459 cy <= window_size[1]);
1460
1461 if (bd->button == GHOST_kButtonMaskLeft) {
1462 if (type == GHOST_kEventButtonDown) {
1463 if (inside_window) {
1466 }
1467 }
1468 else {
1469 ps.ghost_data.qual &= ~WS_QUAL_LMOUSE;
1470 }
1471 }
1472 else if (bd->button == GHOST_kButtonMaskMiddle) {
1473 if (type == GHOST_kEventButtonDown) {
1474 if (inside_window) {
1476 }
1477 }
1478 else {
1479 ps.ghost_data.qual &= ~WS_QUAL_MMOUSE;
1480 }
1481 }
1482 else if (bd->button == GHOST_kButtonMaskRight) {
1483 if (type == GHOST_kEventButtonDown) {
1484 if (inside_window) {
1486 }
1487 }
1488 else {
1489 ps.ghost_data.qual &= ~WS_QUAL_RMOUSE;
1490 }
1491 }
1492 break;
1493 }
1495 if (ps.ghost_data.qual & WS_QUAL_LMOUSE) {
1496 const GHOST_TEventCursorData *cd = static_cast<const GHOST_TEventCursorData *>(data);
1497 int cx, cy;
1498
1499 /* Ignore 'in-between' events, since they can make scrubbing lag.
1500 *
1501 * Ideally we would keep into the event queue and see if this is the last motion event.
1502 * however the API currently doesn't support this. */
1503 {
1504 int x_test, y_test;
1505 if (GHOST_GetCursorPosition(ghost_system, ghost_window, &cx, &cy) == GHOST_kSuccess) {
1506 GHOST_ScreenToClient(ghost_window, cd->x, cd->y, &x_test, &y_test);
1507 if (cx != x_test || cy != y_test) {
1508 /* We're not the last event... skipping. */
1509 break;
1510 }
1511 }
1512 }
1513
1515 }
1516 break;
1517 }
1520 ps.ghost_data.qual &= ~WS_QUAL_MOUSE;
1521 break;
1522 }
1525 float zoomx, zoomy;
1526
1527 ps.display_ctx.size = playanim_window_size_get(ghost_window);
1529
1530 zoomx = float(ps.display_ctx.size[0]) / ps.ibuf_size[0];
1531 zoomy = float(ps.display_ctx.size[1]) / ps.ibuf_size[1];
1532
1533 /* Zoom always show entire image. */
1534 ps.zoom = std::min(zoomx, zoomy);
1535
1536 GPU_viewport(0, 0, ps.display_ctx.size[0], ps.display_ctx.size[1]);
1537 GPU_scissor(0, 0, ps.display_ctx.size[0], ps.display_ctx.size[1]);
1538
1540
1541 g_playanim.total_time = 0.0;
1542
1543 playanim_toscreen(ps, ps.picture, ps.picture ? ps.picture->ibuf : nullptr);
1544
1545 break;
1546 }
1549 ps.go = false;
1550 break;
1551 }
1553 /* Rely on frame-change to redraw. */
1555 break;
1556 }
1558 const GHOST_TEventDragnDropData *ddd = static_cast<const GHOST_TEventDragnDropData *>(data);
1559
1561 const GHOST_TStringArray *stra = static_cast<const GHOST_TStringArray *>(ddd->data);
1562 ps.argc_next = stra->count;
1563 ps.argv_next = static_cast<char **>(MEM_mallocN(sizeof(char **) * ps.argc_next, __func__));
1564 for (int i = 0; i < stra->count; i++) {
1565 ps.argv_next[i] = BLI_strdup(reinterpret_cast<const char *>(stra->strings[i]));
1566 }
1567 ps.go = false;
1568 printf("dropped %s, %d file(s)\n", ps.argv_next[0], ps.argc_next);
1569 }
1570 break;
1571 }
1572 default:
1573 /* Quiet warnings. */
1574 break;
1575 }
1576
1577 return true;
1578}
1579
1580static GHOST_WindowHandle playanim_window_open(
1581 GHOST_SystemHandle ghost_system, const char *title, int posx, int posy, int sizex, int sizey)
1582{
1583 GHOST_GPUSettings gpusettings = {0};
1584 const eGPUBackendType gpu_backend = GPU_backend_type_selection_get();
1585 gpusettings.context_type = wm_ghost_drawing_context_type(gpu_backend);
1586 gpusettings.preferred_device.index = U.gpu_preferred_index;
1587 gpusettings.preferred_device.vendor_id = U.gpu_preferred_vendor_id;
1588 gpusettings.preferred_device.device_id = U.gpu_preferred_device_id;
1589
1590 {
1591 bool screen_size_valid = false;
1592 uint32_t screen_size[2];
1593 if ((GHOST_GetMainDisplayDimensions(ghost_system, &screen_size[0], &screen_size[1]) ==
1594 GHOST_kSuccess) &&
1595 (screen_size[0] > 0) && (screen_size[1] > 0))
1596 {
1597 screen_size_valid = true;
1598 }
1599 else {
1600 /* Unlikely the screen size fails to access,
1601 * if this happens it's still important to clamp the window size by *something*. */
1602 screen_size[0] = 1024;
1603 screen_size[1] = 1024;
1604 }
1605
1606 if (screen_size_valid) {
1608 posy = (screen_size[1] - posy - sizey);
1609 }
1610 }
1611 else {
1612 posx = 0;
1613 posy = 0;
1614 }
1615
1616 /* NOTE: ideally the GPU could be queried for the maximum supported window size,
1617 * this isn't so simple as the GPU back-end's capabilities are initialized *after* the window
1618 * has been created. Further, it's quite unlikely the users main monitor size is larger
1619 * than the maximum window size supported by the GPU. */
1620
1621 /* Clamp the size so very large requests aren't rejected by the GPU.
1622 * Halve until a usable range is reached instead of scaling down to meet the screen size
1623 * since fractional scaling tends not to look so nice. */
1624 while (sizex >= int(screen_size[0]) || sizey >= int(screen_size[1])) {
1625 sizex /= 2;
1626 sizey /= 2;
1627 }
1628 /* Unlikely but ensure the size is *never* zero. */
1629 CLAMP_MIN(sizex, 1);
1630 CLAMP_MIN(sizey, 1);
1631 }
1632
1634 nullptr,
1635 title,
1636 posx,
1637 posy,
1638 sizex,
1639 sizey,
1640 /* Could optionally start full-screen. */
1642 false,
1643 gpusettings);
1644}
1645
1646static void playanim_window_zoom(PlayState &ps, const float zoom_offset)
1647{
1649 // blender::int2 ofs; /* UNUSED. */
1650
1651 if (ps.zoom + zoom_offset > 0.0f) {
1652 ps.zoom += zoom_offset;
1653 }
1654
1655 // playanim_window_get_position(&ofs[0], &ofs[1]);
1656 // size = playanim_window_size_get(ps.ghost_data.window);
1657 // ofs[0] += size[0] / 2; /* UNUSED. */
1658 // ofs[1] += size[1] / 2; /* UNUSED. */
1659 size[0] = ps.zoom * ps.ibuf_size[0];
1660 size[1] = ps.zoom * ps.ibuf_size[1];
1661 // ofs[0] -= size[0] / 2; /* UNUSED. */
1662 // ofs[1] -= size[1] / 2; /* UNUSED. */
1663 // window_set_position(ps.ghost_data.window, size[0], size[1]);
1664 GHOST_SetClientSize(ps.ghost_data.window, size[0], size[1]);
1665}
1666
1668{
1669 const float scale = (GHOST_GetDPIHint(ps.ghost_data.window) / 96.0f);
1670 const float font_size_base = 11.0f; /* Font size un-scaled. */
1671 const int font_size = int((font_size_base * scale) + 0.5f);
1672 bool changed = false;
1673 if (ps.font_size != font_size) {
1674 BLF_size(ps.font_id, font_size);
1675 ps.font_size = font_size;
1676 changed = true;
1677 }
1678 if (ps.display_ctx.ui_scale != scale) {
1679 ps.display_ctx.ui_scale = scale;
1680 }
1681 return changed;
1682}
1683
1688static bool wm_main_playanim_intern(int argc, const char **argv, PlayArgs *args_next)
1689{
1690 ImBuf *ibuf = nullptr;
1691 blender::int2 window_pos = {0, 0};
1692 int frame_start = -1;
1693 int frame_end = -1;
1694
1695 PlayState ps{};
1696
1697 ps.go = true;
1698 ps.direction = true;
1699 ps.next_frame = 1;
1700 ps.once = false;
1701 ps.pingpong = false;
1702 ps.no_frame_skip = false;
1703 ps.single_step = false;
1704 ps.wait = false;
1705 ps.stopped = false;
1706 ps.loading = false;
1707 ps.picture = nullptr;
1708 ps.show_frame_indicator = false;
1709 ps.argc_next = 0;
1710 ps.argv_next = nullptr;
1711 ps.zoom = 1.0f;
1712 ps.draw_flip[0] = false;
1713 ps.draw_flip[1] = false;
1714
1715 ps.frame_step = 1;
1716
1717 ps.font_id = -1;
1718
1719 IMB_init();
1720#ifdef WITH_FFMPEG
1722#endif
1723
1724 STRNCPY(ps.display_ctx.display_settings.display_device,
1726 IMB_colormanagement_init_default_view_settings(&ps.display_ctx.view_settings,
1727 &ps.display_ctx.display_settings);
1728 ps.display_ctx.ui_scale = 1.0f;
1729
1730 while ((argc > 0) && (argv[0][0] == '-')) {
1731 switch (argv[0][1]) {
1732 case 'm': {
1733 g_playanim.from_disk = true;
1734 break;
1735 }
1736 case 'p': {
1737 if (argc > 2) {
1738 window_pos[0] = atoi(argv[1]);
1739 window_pos[1] = atoi(argv[2]);
1740 argc -= 2;
1741 argv += 2;
1742 }
1743 else {
1744 printf("too few arguments for -p (need 2): skipping\n");
1745 }
1746 break;
1747 }
1748 case 'f': {
1749 if (argc > 2) {
1750 double fps = atof(argv[1]);
1751 double fps_base = atof(argv[2]);
1752 if (fps == 0.0) {
1753 fps = 1;
1754 printf("invalid fps, forcing 1\n");
1755 }
1756 g_playanim.swap_time = fps_base / fps;
1757 argc -= 2;
1758 argv += 2;
1759 }
1760 else {
1761 printf("too few arguments for -f (need 2): skipping\n");
1762 }
1763 break;
1764 }
1765 case 's': {
1766 frame_start = atoi(argv[1]);
1767 CLAMP(frame_start, 1, MAXFRAME);
1768 argc--;
1769 argv++;
1770 break;
1771 }
1772 case 'e': {
1773 frame_end = atoi(argv[1]);
1774 CLAMP(frame_end, 1, MAXFRAME);
1775 argc--;
1776 argv++;
1777 break;
1778 }
1779 case 'j': {
1780 ps.frame_step = atoi(argv[1]);
1781 CLAMP(ps.frame_step, 1, MAXFRAME);
1782 g_playanim.swap_time *= ps.frame_step;
1783 argc--;
1784 argv++;
1785 break;
1786 }
1787 case 'c': {
1788#ifdef USE_FRAME_CACHE_LIMIT
1789 const int memory_in_mb = max_ii(0, atoi(argv[1]));
1790 g_frame_cache.memory_limit = size_t(memory_in_mb) * (1024 * 1024);
1791#endif
1792 argc--;
1793 argv++;
1794 break;
1795 }
1796 default: {
1797 printf("unknown option '%c': skipping\n", argv[0][1]);
1798 break;
1799 }
1800 }
1801 argc--;
1802 argv++;
1803 }
1804
1805 if (argc == 0) {
1806 printf("%s: no filepath argument given\n", __func__);
1807 exit(EXIT_FAILURE);
1808 }
1809
1810 const char *filepath = argv[0];
1811
1812 if (IMB_isanim(filepath)) {
1813 /* OCIO_TODO: support different input color spaces. */
1814 ImBufAnim *anim = IMB_open_anim(filepath, IB_rect, 0, nullptr);
1815 if (anim) {
1817 IMB_close_anim(anim);
1818 anim = nullptr;
1819 }
1820 }
1821 else if (!IMB_ispic(filepath)) {
1822 printf("%s: '%s' not an image file\n", __func__, filepath);
1823 exit(EXIT_FAILURE);
1824 }
1825
1826 if (ibuf == nullptr) {
1827 /* OCIO_TODO: support different input color space. */
1828 ibuf = IMB_loadiffname(filepath, IB_rect, nullptr);
1829 }
1830
1831 if (ibuf == nullptr) {
1832 printf("%s: '%s' couldn't open\n", __func__, filepath);
1833 exit(EXIT_FAILURE);
1834 }
1835
1836 /* Select GPU backend. */
1838
1839 /* Init GHOST and open window. */
1840 GHOST_EventConsumerHandle ghost_event_consumer = nullptr;
1841 {
1842 ghost_event_consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps);
1843
1845
1846 ps.ghost_data.system = GHOST_CreateSystem();
1847 GPU_backend_ghost_system_set(ps.ghost_data.system);
1848
1849 if (UNLIKELY(ps.ghost_data.system == nullptr)) {
1850 /* GHOST will have reported the back-ends that failed to load. */
1851 CLOG_WARN(&LOG, "GHOST: unable to initialize, exiting!");
1852 /* This will leak memory, it's preferable to crashing. */
1853 exit(EXIT_FAILURE);
1854 }
1855
1856 GHOST_AddEventConsumer(ps.ghost_data.system, ghost_event_consumer);
1857
1859
1860 ps.ghost_data.window = playanim_window_open(ps.ghost_data.system,
1861 "Blender Animation Player",
1862 window_pos[0],
1863 window_pos[1],
1864 ibuf->x,
1865 ibuf->y);
1866 }
1867
1868 // GHOST_ActivateWindowDrawingContext(ps.ghost_data.window);
1869
1870 /* Init Blender GPU context. */
1871 ps.ghost_data.gpu_context = GPU_context_create(ps.ghost_data.window, nullptr);
1872 GPU_init();
1873
1874 /* Initialize the font. */
1875 BLF_init();
1876 ps.font_id = BLF_load_mono_default(false);
1877
1878 ps.font_size = -1; /* Force update. */
1880
1881 ps.ibuf_size[0] = ibuf->x;
1882 ps.ibuf_size[1] = ibuf->y;
1883
1884 ps.display_ctx.size = ps.ibuf_size;
1885
1888 GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f);
1889
1890 {
1891 const blender::int2 window_size = playanim_window_size_get(ps.ghost_data.window);
1892 GPU_viewport(0, 0, window_size[0], window_size[1]);
1893 GPU_scissor(0, 0, window_size[0], window_size[1]);
1895 }
1896
1897 GHOST_SwapWindowBuffers(ps.ghost_data.window);
1899
1900 /* One of the frames was invalid or not passed in. */
1901 if (frame_start == -1 || frame_end == -1) {
1902 frame_start = 1;
1903 if (argc == 1) {
1904 /* A single file was passed in, attempt to load all images from an image sequence.
1905 * (if it is an image sequence). */
1906 frame_end = MAXFRAME;
1907 }
1908 else {
1909 /* Multiple files passed in, show each file without expanding image sequences.
1910 * This occurs when dropping multiple files. */
1911 frame_end = 1;
1912 }
1913 }
1914
1915 build_pict_list(ps.picsbase,
1916 ps.ghost_data,
1917 ps.display_ctx,
1918 filepath,
1919 (frame_end - frame_start) + 1,
1920 ps.frame_step,
1921 &ps.loading);
1922
1923#ifdef WITH_AUDASPACE
1924 g_audaspace.source = AUD_Sound_file(filepath);
1925 if (!BLI_listbase_is_empty(&ps.picsbase)) {
1926 const ImBufAnim *anim_movie = static_cast<PlayAnimPict *>(ps.picsbase.first)->anim;
1927 if (anim_movie) {
1928 short frs_sec = 25;
1929 float frs_sec_base = 1.0;
1930
1931 IMB_anim_get_fps(anim_movie, true, &frs_sec, &frs_sec_base);
1932
1933 g_playanim.fps_movie = double(frs_sec) / double(frs_sec_base);
1934 /* Enforce same fps for movie as sound. */
1935 g_playanim.swap_time = ps.frame_step / g_playanim.fps_movie;
1936 }
1937 }
1938#endif
1939
1940 for (int i = 1; i < argc; i++) {
1941 filepath = argv[i];
1942 build_pict_list(ps.picsbase,
1943 ps.ghost_data,
1944 ps.display_ctx,
1945 filepath,
1946 (frame_end - frame_start) + 1,
1947 ps.frame_step,
1948 &ps.loading);
1949 }
1950
1951 IMB_freeImBuf(ibuf);
1952 ibuf = nullptr;
1953
1954 pupdate_time();
1955 g_playanim.total_time = 0.0;
1956
1957/* Newly added in 2.6x, without this images never get freed. */
1958#define USE_IMB_CACHE
1959
1960 while (ps.go) {
1961 if (ps.pingpong) {
1962 ps.direction = -ps.direction;
1963 }
1964
1965 if (ps.direction == 1) {
1966 ps.picture = static_cast<PlayAnimPict *>(ps.picsbase.first);
1967 }
1968 else {
1969 ps.picture = static_cast<PlayAnimPict *>(ps.picsbase.last);
1970 }
1971
1972 if (ps.picture == nullptr) {
1973 printf("couldn't find pictures\n");
1974 ps.go = false;
1975 }
1976 if (ps.pingpong) {
1977 if (ps.direction == 1) {
1978 ps.picture = ps.picture->next;
1979 }
1980 else {
1981 ps.picture = ps.picture->prev;
1982 }
1983 }
1984 if (g_playanim.total_time > 0.0) {
1985 g_playanim.total_time = 0.0;
1986 }
1987
1988#ifdef WITH_AUDASPACE
1989 if (g_audaspace.playback_handle) {
1990 AUD_Handle_stop(g_audaspace.playback_handle);
1991 }
1992 g_audaspace.playback_handle = AUD_Device_play(g_audaspace.audio_device, g_audaspace.source, 1);
1994#endif
1995
1996 while (ps.picture) {
1997 bool has_event;
1998#ifndef USE_IMB_CACHE
1999 if (ibuf != nullptr && ibuf->ftype == IMB_FTYPE_NONE) {
2000 IMB_freeImBuf(ibuf);
2001 }
2002#endif
2003
2004 ibuf = ibuf_from_picture(ps.picture);
2005
2006 {
2007#ifdef USE_IMB_CACHE
2008 ps.picture->ibuf = ibuf;
2009#endif
2010 if (ibuf) {
2011#ifdef USE_FRAME_CACHE_LIMIT
2012 if (ps.picture->frame_cache_node == nullptr) {
2013 frame_cache_add(ps.picture);
2014 }
2015 else {
2016 frame_cache_touch(ps.picture);
2017 }
2019#endif /* USE_FRAME_CACHE_LIMIT */
2020
2021 STRNCPY(ibuf->filepath, ps.picture->filepath);
2022 }
2023
2024 while (pupdate_time()) {
2026 }
2027 g_playanim.total_time -= g_playanim.swap_time;
2028 playanim_toscreen(ps, ps.picture, ibuf);
2029 }
2030
2031 if (ps.once) {
2032 if (ps.picture->next == nullptr) {
2033 ps.wait = true;
2034 }
2035 else if (ps.picture->prev == nullptr) {
2036 ps.wait = true;
2037 }
2038 }
2039
2040 ps.next_frame = ps.direction;
2041
2043 GPUContext *restore_context = GPU_context_active_get();
2044 GPU_context_active_set(ps.ghost_data.gpu_context);
2045 while ((has_event = GHOST_ProcessEvents(ps.ghost_data.system, false))) {
2046 GHOST_DispatchEvents(ps.ghost_data.system);
2047 }
2049 GPU_context_active_set(restore_context);
2050
2051 if (ps.go == false) {
2052 break;
2053 }
2055 if (!has_event) {
2057 }
2058 if (ps.wait) {
2059 continue;
2060 }
2061
2062 ps.wait = ps.single_step;
2063
2064 if (ps.wait == false && ps.stopped) {
2065 ps.stopped = false;
2066 }
2067
2068 pupdate_time();
2069
2070 if (ps.picture && ps.next_frame) {
2071 /* Advance to the next frame, always at least set one step.
2072 * Implement frame-skipping when enabled and playback is not fast enough. */
2073 while (ps.picture) {
2074 ps.picture = playanim_step(ps.picture, ps.next_frame);
2075
2076 if (ps.once && ps.picture != nullptr) {
2077 if (ps.picture->next == nullptr) {
2078 ps.wait = true;
2079 }
2080 else if (ps.picture->prev == nullptr) {
2081 ps.wait = true;
2082 }
2083 }
2084
2085 if (ps.wait || g_playanim.total_time < g_playanim.swap_time || ps.no_frame_skip) {
2086 break;
2087 }
2088 g_playanim.total_time -= g_playanim.swap_time;
2089 }
2090 if (ps.picture == nullptr && ps.single_step) {
2091 ps.picture = playanim_step(ps.picture, ps.next_frame);
2092 }
2093 }
2094 if (ps.go == false) {
2095 break;
2096 }
2097 }
2098 }
2099 while ((ps.picture = static_cast<PlayAnimPict *>(BLI_pophead(&ps.picsbase)))) {
2100 if (ps.picture->anim) {
2101 if ((ps.picture->next == nullptr) || (ps.picture->next->anim != ps.picture->anim)) {
2102 IMB_close_anim(ps.picture->anim);
2103 }
2104 }
2105
2106 if (ps.picture->ibuf) {
2107 IMB_freeImBuf(ps.picture->ibuf);
2108 }
2109 if (ps.picture->mem) {
2110 MEM_freeN(ps.picture->mem);
2111 }
2112 if (ps.picture->error_message) {
2113 MEM_freeN(static_cast<void *>(ps.picture->error_message));
2114 }
2115 MEM_freeN(const_cast<char *>(ps.picture->filepath));
2116 MEM_freeN(ps.picture);
2117 }
2118
2119/* Cleanup. */
2120#ifndef USE_IMB_CACHE
2121 if (ibuf) {
2122 IMB_freeImBuf(ibuf);
2123 }
2124#endif
2125
2126#ifdef USE_FRAME_CACHE_LIMIT
2128 g_frame_cache.pics_len = 0;
2129 g_frame_cache.pics_size_in_memory = 0;
2130#endif
2131
2132#ifdef WITH_AUDASPACE
2133 if (g_audaspace.playback_handle) {
2134 AUD_Handle_stop(g_audaspace.playback_handle);
2135 g_audaspace.playback_handle = nullptr;
2136 }
2137 if (g_audaspace.scrub_handle) {
2138 AUD_Handle_stop(g_audaspace.scrub_handle);
2139 g_audaspace.scrub_handle = nullptr;
2140 }
2141 AUD_Sound_free(g_audaspace.source);
2142 g_audaspace.source = nullptr;
2143#endif
2144
2145 /* We still miss freeing a lot!
2146 * But many areas could skip initialization too for anim play. */
2147
2149
2150 BLF_exit();
2151
2152 /* NOTE: Must happen before GPU Context destruction as GPU resources are released via
2153 * Color Management module.
2154 * NOTE: there is no #IMB_ffmpeg_exit. */
2155 IMB_exit();
2156
2157 if (ps.ghost_data.gpu_context) {
2158 GPU_context_active_set(ps.ghost_data.gpu_context);
2159 GPU_exit();
2160 GPU_context_discard(ps.ghost_data.gpu_context);
2161 ps.ghost_data.gpu_context = nullptr;
2162 }
2163 GHOST_RemoveEventConsumer(ps.ghost_data.system, ghost_event_consumer);
2164 GHOST_DisposeEventConsumer(ghost_event_consumer);
2165
2166 GHOST_DisposeWindow(ps.ghost_data.system, ps.ghost_data.window);
2167
2168 /* Early exit, IMB and BKE should be exited only in end. */
2169 if (ps.argv_next) {
2170 args_next->argc = ps.argc_next;
2171 args_next->argv = ps.argv_next;
2172 return true;
2173 }
2174
2175 GHOST_DisposeSystem(ps.ghost_data.system);
2176
2177#if 0
2179 if (totblock != 0) {
2180 /* Prints many `bAKey`, `bArgument` messages which are tricky to fix. */
2181 printf("Error Totblock: %d\n", totblock);
2183 }
2184#endif
2185
2186 return false;
2187}
2188
2189void WM_main_playanim(int argc, const char **argv)
2190{
2191#ifdef WITH_AUDASPACE
2192 {
2193 AUD_DeviceSpecs specs;
2194
2195 specs.rate = AUD_RATE_48000;
2196 specs.format = AUD_FORMAT_FLOAT32;
2197 specs.channels = AUD_CHANNELS_STEREO;
2198
2199 AUD_initOnce();
2200
2201 if (!(g_audaspace.audio_device = AUD_init(nullptr, specs, 1024, "Blender"))) {
2202 g_audaspace.audio_device = AUD_init("None", specs, 0, "Blender");
2203 }
2204 }
2205#endif
2206
2207 PlayArgs args_next = {0};
2208 do {
2209 PlayArgs args_free = args_next;
2210 args_next = {0};
2211
2212 if (wm_main_playanim_intern(argc, argv, &args_next)) {
2213 argc = args_next.argc;
2214 argv = const_cast<const char **>(args_next.argv);
2215 }
2216 else {
2217 argc = 0;
2218 argv = nullptr;
2219 }
2220
2221 if (args_free.argv) {
2222 for (int i = 0; i < args_free.argc; i++) {
2223 MEM_freeN(args_free.argv[i]);
2224 }
2225 MEM_freeN(args_free.argv);
2226 }
2227 } while (argv != nullptr);
2228
2229#ifdef WITH_AUDASPACE
2230 AUD_exit(g_audaspace.audio_device);
2231 AUD_exitOnce();
2232#endif
2233}
int ED_draw_imbuf_method(const ImBuf *ibuf)
Definition glutil.cc:609
void BLF_size(int fontid, float size)
Definition blf.cc:426
void BLF_aspect(int fontid, float x, float y, float z)
Definition blf.cc:360
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:568
int BLF_load_mono_default(bool unique)
void BLF_exit()
Definition blf.cc:71
void BLF_enable(int fontid, int option)
Definition blf.cc:312
int BLF_init()
Definition blf.cc:62
void BLF_color4f(int fontid, float r, float g, float b, float a)
Definition blf.cc:497
@ BLF_ASPECT
Definition BLF_api.hh:366
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
File and directory operations.
#define O_BINARY
size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT
Definition storage.cc:206
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int64_t BLI_read(int fd, void *buf, size_t nbytes)
Definition fileops_c.cc:96
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void void BLI_INLINE bool BLI_listbase_is_single(const struct ListBase *lb)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
struct LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:909
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:251
MINLINE int max_ii(int a, int b)
MINLINE int clamp_i(int value, int min, int max)
#define FILE_MAX
int BLI_path_sequence_decode(const char *path, char *head, size_t head_maxncpy, char *tail, size_t tail_maxncpy, unsigned short *r_digits_len)
Definition path_utils.cc:57
void BLI_path_sequence_encode(char *path, size_t path_maxncpy, const char *head, const char *tail, unsigned short numlen, int pic)
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.c:408
char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
unsigned char uchar
unsigned short ushort
unsigned int uint
void BLI_system_backtrace(FILE *fp)
Definition system.c:63
Platform independent time functions.
void BLI_time_sleep_ms(int ms)
Definition time.c:85
double BLI_time_now_seconds(void)
Definition time.c:65
#define CLAMP(a, b, c)
#define UNUSED_VARS(...)
#define ENUM_OPERATORS(_type, _max)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define UNLIKELY(x)
#define ELEM(...)
#define CLAMP_MIN(a, b)
typedef double(DMatrix)[4][4]
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
void DEG_free_node_types()
#define MAXFRAME
@ IMAGE_DRAW_METHOD_GLSL
GHOST C-API function and type declarations.
int32_t GHOST_GetWidthRectangle(GHOST_RectangleHandle rectanglehandle)
GHOST_TSuccess GHOST_GetCursorPosition(const GHOST_SystemHandle systemhandle, const GHOST_WindowHandle windowhandle, int32_t *x, int32_t *y)
GHOST_TSuccess GHOST_SetClientSize(GHOST_WindowHandle windowhandle, uint32_t width, uint32_t height)
GHOST_TCapabilityFlag GHOST_GetCapabilities(void)
GHOST_EventConsumerHandle GHOST_CreateEventConsumer(GHOST_EventCallbackProcPtr eventCallback, GHOST_TUserDataPtr user_data)
GHOST_SystemHandle GHOST_CreateSystem(void)
GHOST_TSuccess GHOST_AddEventConsumer(GHOST_SystemHandle systemhandle, GHOST_EventConsumerHandle consumerhandle)
GHOST_TSuccess GHOST_RemoveEventConsumer(GHOST_SystemHandle systemhandle, GHOST_EventConsumerHandle consumerhandle)
uint16_t GHOST_GetDPIHint(GHOST_WindowHandle windowhandle)
void GHOST_DisposeRectangle(GHOST_RectangleHandle rectanglehandle)
bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent)
void GHOST_ScreenToClient(GHOST_WindowHandle windowhandle, int32_t inX, int32_t inY, int32_t *outX, int32_t *outY)
GHOST_TSuccess GHOST_DisposeEventConsumer(GHOST_EventConsumerHandle consumerhandle)
GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle)
void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn)
int32_t GHOST_GetHeightRectangle(GHOST_RectangleHandle rectanglehandle)
GHOST_TSuccess GHOST_SwapWindowBuffers(GHOST_WindowHandle windowhandle)
GHOST_TEventDataPtr GHOST_GetEventData(GHOST_EventHandle eventhandle)
GHOST_TSuccess GHOST_ActivateWindowDrawingContext(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle, GHOST_TModifierKey mask, bool *r_is_down)
GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle parent_windowhandle, const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, bool is_dialog, GHOST_GPUSettings gpuSettings)
float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle)
GHOST_TEventType GHOST_GetEventType(GHOST_EventHandle eventhandle)
bool GHOST_UseNativePixels(void)
void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
GHOST_RectangleHandle GHOST_GetClientBounds(GHOST_WindowHandle windowhandle)
GHOST_TSuccess GHOST_DisposeSystem(GHOST_SystemHandle systemhandle)
GHOST_TSuccess GHOST_GetMainDisplayDimensions(GHOST_SystemHandle systemhandle, uint32_t *r_width, uint32_t *r_height)
static GHOST_SystemCocoa * ghost_system
@ GHOST_kWindowStateNormal
void * GHOST_TUserDataPtr
Definition GHOST_Types.h:85
GHOST_TEventType
@ GHOST_kEventWindowClose
@ GHOST_kEventWindowMove
@ GHOST_kEventWindowSize
@ GHOST_kEventDraggingDropDone
@ GHOST_kEventCursorMove
@ GHOST_kEventButtonUp
@ GHOST_kEventWindowActivate
@ GHOST_kEventWindowDeactivate
@ GHOST_kEventButtonDown
@ GHOST_kEventKeyDown
@ GHOST_kEventWindowDPIHintChanged
@ GHOST_kEventKeyUp
@ GHOST_kEventQuitRequest
@ GHOST_kCapabilityWindowPosition
@ GHOST_kKey5
@ GHOST_kKey4
@ GHOST_kKeyNumpad3
@ GHOST_kKeyNumpad1
@ GHOST_kKey3
@ GHOST_kKeyI
@ GHOST_kKeyEnter
@ GHOST_kKeyP
@ GHOST_kKeyNumpadSlash
@ GHOST_kKeyRightArrow
@ GHOST_kKeyNumpad4
@ GHOST_kKeyMinus
@ GHOST_kKey6
@ GHOST_kKey0
@ GHOST_kKeyDownArrow
@ GHOST_kKeyNumpadPeriod
@ GHOST_kKeyF
@ GHOST_kKey1
@ GHOST_kKey8
@ GHOST_kKeyNumpad2
@ GHOST_kKeyPeriod
@ GHOST_kKeyNumpadPlus
@ GHOST_kKey9
@ GHOST_kKeyNumpad5
@ GHOST_kKeyLeftArrow
@ GHOST_kKeyEqual
@ GHOST_kKey7
@ GHOST_kKeyNumpad6
@ GHOST_kKeyNumpad8
@ GHOST_kKeyNumpad9
@ GHOST_kKeyUpArrow
@ GHOST_kKeyNumpad0
@ GHOST_kKeyA
@ GHOST_kKey2
@ GHOST_kKeyNumpad7
@ GHOST_kKeyEsc
@ GHOST_kKeyPlus
@ GHOST_kKeySlash
@ GHOST_kKeyNumpadEnter
@ GHOST_kKeyNumpadMinus
@ GHOST_kKeySpace
const void * GHOST_TEventDataPtr
@ GHOST_kModifierKeyRightControl
@ GHOST_kModifierKeyLeftControl
@ GHOST_kModifierKeyRightAlt
@ GHOST_kModifierKeyRightShift
@ GHOST_kModifierKeyLeftAlt
@ GHOST_kModifierKeyLeftShift
@ GHOST_kSuccess
Definition GHOST_Types.h:87
void(* GHOST_TBacktraceFn)(void *file_handle)
Definition GHOST_Types.h:63
@ GHOST_kDragnDropTypeFilenames
@ GHOST_kButtonMaskRight
@ GHOST_kButtonMaskLeft
@ GHOST_kButtonMaskMiddle
void GPU_render_end()
GPUContext * GPU_context_create(void *ghost_window, void *ghost_context)
void GPU_render_begin()
GPUContext * GPU_context_active_get()
eGPUBackendType GPU_backend_type_selection_get()
void GPU_render_step()
void GPU_context_discard(GPUContext *)
bool GPU_backend_type_selection_detect()
void GPU_context_active_set(GPUContext *)
eGPUBackendType GPU_backend_get_type()
void GPU_backend_ghost_system_set(void *ghost_system_handle)
void GPU_clear_color(float red, float green, float blue, float alpha)
void immEnd()
void immUnbindProgram()
void immUniformColor3ub(unsigned char r, unsigned char g, unsigned char b)
void immVertex2f(uint attr_id, float x, float y)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immUniformColor3f(float r, float g, float b)
void immAttr2f(uint attr_id, float x, float y)
void immBegin(GPUPrimType, uint vertex_len)
void imm_draw_box_checker_2d_ex(float x1, float y1, float x2, float y2, const float color_primary[4], const float color_secondary[4], int checker_size)
void GPU_init()
void GPU_exit()
void GPU_matrix_identity_projection_set()
void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far)
void GPU_matrix_identity_set()
void GPU_matrix_scale_2f(float x, float y)
void GPU_matrix_push()
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
void GPU_matrix_pop()
void GPU_matrix_translate_2f(float x, float y)
@ GPU_PRIM_TRI_FAN
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_SHADER_3D_IMAGE_COLOR
void GPU_flush()
Definition gpu_state.cc:294
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_scissor(int x, int y, int width, int height)
Definition gpu_state.cc:188
void GPU_viewport(int x, int y, int width, int height)
Definition gpu_state.cc:194
void GPU_texture_bind(GPUTexture *texture, int unit)
GPUTexture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_free(GPUTexture *texture)
void GPU_texture_unbind(GPUTexture *texture)
eGPUDataFormat
@ GPU_DATA_UBYTE
@ GPU_DATA_FLOAT
@ GPU_TEXTURE_USAGE_SHADER_READ
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
eGPUTextureFormat
@ GPU_RGB16F
void GPU_texture_update(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
unsigned char * IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, void **cache_handle)
bool IMB_colormanagement_setup_glsl_draw(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, float dither, bool predivide)
void IMB_colormanagement_init_default_view_settings(ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
void IMB_display_buffer_release(void *cache_handle)
const char * IMB_colormanagement_role_colorspace_name_get(int role)
void IMB_colormanagement_finish_glsl_draw()
@ COLOR_ROLE_DEFAULT_BYTE
bool IMB_colormanagement_setup_glsl_draw_from_space(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, ColorSpace *from_colorspace, float dither, bool predivide, bool do_overlay_merge)
void IMB_exit()
Definition module.cc:26
ImBuf * IMB_anim_absolute(ImBufAnim *anim, int position, IMB_Timecode_Type tc, IMB_Proxy_Size preview_size)
bool IMB_anim_get_fps(const ImBufAnim *anim, bool no_av_base, short *r_frs_sec, float *r_frs_sec_base)
ImBuf * IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE])
Definition readimage.cc:146
void IMB_ffmpeg_init()
void IMB_init()
Definition module.cc:18
bool IMB_isanim(const char *filepath)
ImBufAnim * IMB_open_anim(const char *filepath, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE])
void IMB_close_anim(ImBufAnim *anim)
Definition anim_movie.cc:76
bool IMB_ispic(const char *filepath)
size_t IMB_get_size_in_memory(ImBuf *ibuf)
int IMB_anim_get_duration(ImBufAnim *anim, IMB_Timecode_Type tc)
@ IMB_FTYPE_NONE
@ IMB_PROXY_NONE
@ IMB_TC_NONE
Contains defines and structs used throughout the imbuf module.
@ IB_rect
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
struct GPUContext GPUContext
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
unsigned int U
Definition btGjkEpa3.h:78
output_img define("CONVERT_EXPRESSION(value)", "value") .do_static_compilation(true)
#define printf
double time
const char * label
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
RAYTRACE_GROUP_SIZE additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "eevee_sampling_data", "eevee_utility_texture", "eevee_hiz_data", "draw_view") .specialization_constant(Type RAYTRACE_GROUP_SIZE in_sh_0_tx in_sh_2_tx screen_normal_tx GPU_RGBA8
struct ImBuf * IMB_ibImageFromMemory(const unsigned char *, size_t, int, char[IM_MAX_SPACE], const char *)
void IMB_freeImBuf(ImBuf *)
#define PRIu64
Definition inttypes.h:132
#define PRId64
Definition inttypes.h:78
format
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
uint(* MEM_get_memory_blocks_in_use)(void)
Definition mallocn.cc:63
void(* MEM_printmemlist)(void)
Definition mallocn.cc:56
static uint totblock
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
GHOST_TDrawingContextType context_type
GHOST_GPUDevice preferred_device
GHOST_TDragnDropTypes dataType
GHOST_TDragnDropDataPtr data
eWS_Qual qual
GHOST_SystemHandle system
GPUContext * gpu_context
GHOST_WindowHandle window
ColorSpace * colorspace
ColorSpace * colorspace
char filepath[IMB_FILEPATH_SIZE]
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
unsigned char planes
enum eImbFileType ftype
void * data
void * last
void * first
const char * filepath
PlayAnimPict * prev
char * error_message
size_t size_in_memory
LinkData * frame_cache_node
PlayAnimPict * next
ImBufAnim * anim
char ** argv
ColorManagedViewSettings view_settings
ColorManagedDisplaySettings display_settings
blender::int2 size
short next_frame
blender::int2 ibuf_size
bool show_frame_indicator
PlayDisplayContext display_ctx
bool no_frame_skip
char ** argv_next
struct PlayAnimPict * picture
bool draw_flip[2]
ListBase picsbase
short direction
GhostData ghost_data
bool single_step
bool need_frame_update
int frame_cursor_x
float xmax
float xmin
float ymax
float ymin
static blender::int2 playanim_window_size_get(GHOST_WindowHandle ghost_window)
static void playanim_audio_stop(PlayState &)
static void build_pict_list_from_image_sequence(ListBase &picsbase, GhostData &ghost_data, const PlayDisplayContext &display_ctx, const char *filepath_first, const int frame_offset, const int totframes, const int frame_step, const bool *loading_p)
static void playanim_audio_resume(PlayState &ps)
static PlayAnimPict * playanim_step(PlayAnimPict *playanim, int step)
static bool playanim_window_font_scale_from_dpi(PlayState &ps)
void WM_main_playanim(int argc, const char **argv)
size_t memory_limit
static void frame_cache_remove(PlayAnimPict *pic)
static struct @1386 g_playanim
static bool wm_main_playanim_intern(int argc, const char **argv, PlayArgs *args_next)
static void * ocio_transform_ibuf(const PlayDisplayContext &display_ctx, ImBuf *ibuf, bool *r_glsl_used, eGPUTextureFormat *r_format, eGPUDataFormat *r_data, void **r_buffer_cache_handle)
static void frame_cache_touch(PlayAnimPict *pic)
static struct @1387 g_frame_cache
static void frame_cache_add(PlayAnimPict *pic)
static void playanim_toscreen_ex(GhostData &ghost_data, const PlayDisplayContext &display_ctx, const PlayAnimPict *picture, ImBuf *ibuf, const int font_id, const int frame_step, const float draw_zoom, const bool draw_flip[2], const float frame_indicator_factor)
static bool buffer_from_filepath(const char *filepath, void **r_mem, size_t *r_size, char **r_error_message)
static void playanim_event_qual_update(GhostData &ghost_data)
bool from_disk
static void playanim_window_zoom(PlayState &ps, const float zoom_offset)
static int pupdate_time()
static void frame_cache_limit_apply(ImBuf *ibuf_keep)
size_t pics_size_in_memory
static void playanim_change_frame(PlayState &ps)
static void update_sound_fps()
int pics_len
static void playanim_gpu_matrix()
static void playanim_toscreen_on_load(GhostData &ghost_data, const PlayDisplayContext &display_ctx, const PlayAnimPict *picture, ImBuf *ibuf)
static void playanim_toscreen(PlayState &ps, const PlayAnimPict *picture, ImBuf *ibuf)
static void playanim_change_frame_tag(PlayState &ps, int cx)
eWS_Qual
@ WS_QUAL_LCTRL
@ WS_QUAL_RMOUSE
@ WS_QUAL_RALT
@ WS_QUAL_MMOUSE
@ WS_QUAL_LMOUSE
@ WS_QUAL_LALT
@ WS_QUAL_RCTRL
@ WS_QUAL_LSHIFT
@ WS_QUAL_RSHIFT
#define WS_QUAL_SHIFT
#define PLAY_FRAME_CACHE_MAX
#define WS_QUAL_CTRL
double total_time
static GHOST_WindowHandle playanim_window_open(GHOST_SystemHandle ghost_system, const char *title, int posx, int posy, int sizex, int sizey)
static CLG_LogRef LOG
static void draw_display_buffer(const PlayDisplayContext &display_ctx, ImBuf *ibuf, const rctf *canvas, const bool draw_flip[2])
static bool frame_cache_limit_exceeded()
static ImBuf * ibuf_from_picture(PlayAnimPict *pic)
ListBase pics
static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr ps_void_ptr)
static void build_pict_list_from_anim(ListBase &picsbase, GhostData &ghost_data, const PlayDisplayContext &display_ctx, const char *filepath_first, const int frame_offset)
double swap_time
static void build_pict_list(ListBase &picsbase, GhostData &ghost_data, const PlayDisplayContext &display_ctx, const char *filepath_first, const int totframes, const int frame_step, bool *loading_p)
#define WS_QUAL_MOUSE
GHOST_TDrawingContextType wm_ghost_drawing_context_type(const eGPUBackendType gpu_backend)