Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/kaa/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ set(CYTHON_FILES
shaders.pxi
materials.pxi
statistics.pxi
capture.pxi

kaacore/__init__.pxd
kaacore/engine.pxd
Expand Down Expand Up @@ -62,6 +63,7 @@ set(CYTHON_FILES
kaacore/resources.pxd
kaacore/textures.pxd
kaacore/statistics.pxd
kaacore/capture.pxd

extra/include/pythonic_callback.h
extra/include/python_exceptions_wrapper.h
Expand Down
1 change: 1 addition & 0 deletions src/kaa/_kaa.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ include "window.pxi"
include "audio.pxi"
include "timers.pxi"
include "statistics.pxi"
include "capture.pxi"
include "engine.pxi"
include "shaders.pxi"
include "materials.pxi"
66 changes: 66 additions & 0 deletions src/kaa/capture.pxi
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from libcpp.vector cimport vector
from cython.view cimport array as cvarray

from .kaacore.capture cimport CCapturedFrames

import base64
from io import BytesIO


cdef class CapturedFrames:
cdef CCapturedFrames c_captured_frames

cdef void c_attach_captured_frames(self, CCapturedFrames& c_captured_frames):
self.c_captured_frames = c_captured_frames

@property
def memoryviews(self):
cdef cvarray image_array
cdef uint8_t* frame_data
cdef tuple dimensions = self.dimensions
cdef list collected_memoryviews = []

for frame_data in self.c_captured_frames.raw_ptr_frames_uint8():
image_array = cvarray(
shape=dimensions,
itemsize=4, format='I', mode='c', allocate_buffer=False,
)
image_array.data = <char*>frame_data
collected_memoryviews.append(image_array.get_memview())
return collected_memoryviews

@property
def dimensions(self):
return (self.c_captured_frames.width, self.c_captured_frames.height)


class HTMLBase64Image:
def __init__(self, bytes content, str image_type):
self.content_encoded = base64.b64encode(content).decode('ascii')
self.image_type = image_type

def _repr_html_(self):
return '<img src="data:{};base64,{}" />'.format(
self.image_type, self.content_encoded,
)


def generate_gif(CapturedFrames captured_frames, *, duration=33):
from PIL import Image

cdef object bytes_buffer = BytesIO()
cdef list images = [Image.frombuffer('RGBA', memview.shape, memview)
for memview in captured_frames.memoryviews]

images[0].save(
bytes_buffer, format='gif', save_all=True,
append_images=images[1:], duration=duration, loop=0,
optimize=False,
)

bytes_buffer.seek(0)
return HTMLBase64Image(bytes_buffer.read(), 'image/gif')


def generate_auto(CapturedFrames captured_frames):
return generate_gif(captured_frames)
26 changes: 26 additions & 0 deletions src/kaa/engine.pxi
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import atexit
import math
from enum import IntEnum
from contextlib import contextmanager

Expand All @@ -12,11 +13,16 @@ from .kaacore.engine cimport (
CVirtualResolutionMode
)
from .kaacore.display cimport CDisplay
from .kaacore.capture cimport CCapturedFrames
from .kaacore.log cimport c_emit_log_dynamic, CLogLevel, _log_category_wrapper
from .kaacore.clock cimport CDuration

from . import __version__


cdef double DEFAULT_FIXED_FRAMETIME = 1. / 30.


def _clean_up():
# force _c_engine_instance deletion,
# so we are sure that kaacore dies before the python process
Expand Down Expand Up @@ -99,6 +105,26 @@ cdef class _Engine:
with nogil:
c_engine.run(c_scene)

def run_capture(
self, Scene scene not None, uint32_t frames_limit,
*, double fixed_dt=DEFAULT_FIXED_FRAMETIME,
frames_preview_generator=None,
):
cdef:
CScene* c_scene = scene._c_scene.get()
CEngine* c_engine = get_c_engine()
CDuration c_duration = CDuration(fixed_dt)
CCapturedFrames c_captured_frames
CapturedFrames captured_frames = CapturedFrames.__new__(CapturedFrames)
with nogil:
c_captured_frames = c_engine.run_capture(c_scene, frames_limit, c_duration)

if frames_preview_generator is None:
frames_preview_generator = generate_auto

captured_frames.c_attach_captured_frames(c_captured_frames)
return frames_preview_generator(captured_frames)

def quit(self):
get_c_engine().quit()

Expand Down
13 changes: 13 additions & 0 deletions src/kaa/kaacore/capture.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from libcpp.memory cimport shared_ptr
from libcpp.vector cimport vector
from libc.stdint cimport uint8_t, uint32_t

from .exceptions cimport raise_py_error


cdef extern from "kaacore/capture.h" namespace "kaacore" nogil:
cdef cppclass CCapturedFrames "kaacore::CapturedFrames":
uint32_t width
uint32_t height

vector[uint8_t*] raw_ptr_frames_uint8() except +raise_py_error
5 changes: 4 additions & 1 deletion src/kaa/kaacore/engine.pxd
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from libcpp.string cimport string
from libcpp.memory cimport unique_ptr
from libcpp.vector cimport vector
from libc.stdint cimport int32_t, uint64_t
from libc.stdint cimport uint32_t, uint64_t

from .clock cimport CDuration
from .capture cimport CCapturedFrames
from .display cimport CDisplay
from .scenes cimport CScene
from .window cimport CWindow
Expand Down Expand Up @@ -47,6 +48,8 @@ cdef extern from "kaacore/engine.h" namespace "kaacore" nogil:
double get_fps() except +raise_py_error

void run(CScene* c_scene) except +raise_py_error
CCapturedFrames run_capture(CScene* c_scene, uint32_t frames_limit,
CDuration fixed_dt) except +raise_py_error
void change_scene(CScene* c_scene) except +raise_py_error
void quit() except +raise_py_error

Expand Down