From d2c9f912da389c23ed27e81e940be3540f64fc59 Mon Sep 17 00:00:00 2001 From: Dom Cobley Date: Thu, 28 Nov 2013 22:22:10 +0000 Subject: [PATCH] RaspiStill example code for YUV fast paths Update RaspiStill framework to provide access to the new EGL image targets that provide a fastpath for mapping the individual YUV planes to one byte per pixel GL_LUMINANCE buffers. These are still accessed as OES textures so this is effectively a greyscale image created from a single plane of a MMAL buffer. Also added some example code (yuv, sobel) that demonstrates how to use the new EGL image targets. Also, add -gc to the command line to capture the GL frame-buffer as a TGA file. Thanks to Tim Gover --- CMakeLists.txt | 6 +- helpers/vc_image/vc_image.h | 2 + .../linux/apps/raspicam/CMakeLists.txt | 28 +- .../linux/apps/raspicam/RaspiStill.c | 86 +++-- .../linux/apps/raspicam/RaspiTex.c | 217 ++++++++++-- .../linux/apps/raspicam/RaspiTex.h | 73 +++- .../linux/apps/raspicam/RaspiTexUtil.c | 314 ++++++++++++++++-- .../linux/apps/raspicam/RaspiTexUtil.h | 49 ++- .../{ => gl_scenes}/cube_texture_and_coords.h | 0 .../linux/apps/raspicam/gl_scenes/mirror.c | 123 +++++++ .../apps/raspicam/{ => gl_scenes}/mirror.h | 0 .../apps/raspicam/{ => gl_scenes}/models.c | 0 .../apps/raspicam/{ => gl_scenes}/models.h | 0 .../linux/apps/raspicam/gl_scenes/sobel.c | 192 +++++++++++ .../linux/apps/raspicam/gl_scenes/sobel.h | 37 +++ .../apps/raspicam/{ => gl_scenes}/square.c | 1 + .../apps/raspicam/{ => gl_scenes}/square.h | 0 .../apps/raspicam/{ => gl_scenes}/teapot.c | 17 +- .../apps/raspicam/{ => gl_scenes}/teapot.h | 0 .../linux/apps/raspicam/gl_scenes/yuv.c | 145 ++++++++ .../linux/apps/raspicam/gl_scenes/yuv.h | 37 +++ .../linux/apps/raspicam/mirror.c | 251 -------------- host_applications/linux/apps/raspicam/tga.c | 113 +++++++ host_applications/linux/apps/raspicam/tga.h | 73 ++++ interface/khronos/ext/egl_khr_image_client.c | 15 + interface/khronos/include/EGL/eglext_brcm.h | 7 + interface/vctypes/vc_image_types.h | 3 + middleware/khronos/ext/egl_khr_image.h | 3 + 28 files changed, 1442 insertions(+), 350 deletions(-) rename host_applications/linux/apps/raspicam/{ => gl_scenes}/cube_texture_and_coords.h (100%) create mode 100644 host_applications/linux/apps/raspicam/gl_scenes/mirror.c rename host_applications/linux/apps/raspicam/{ => gl_scenes}/mirror.h (100%) rename host_applications/linux/apps/raspicam/{ => gl_scenes}/models.c (100%) rename host_applications/linux/apps/raspicam/{ => gl_scenes}/models.h (100%) create mode 100644 host_applications/linux/apps/raspicam/gl_scenes/sobel.c create mode 100644 host_applications/linux/apps/raspicam/gl_scenes/sobel.h rename host_applications/linux/apps/raspicam/{ => gl_scenes}/square.c (98%) rename host_applications/linux/apps/raspicam/{ => gl_scenes}/square.h (100%) rename host_applications/linux/apps/raspicam/{ => gl_scenes}/teapot.c (95%) rename host_applications/linux/apps/raspicam/{ => gl_scenes}/teapot.h (100%) create mode 100644 host_applications/linux/apps/raspicam/gl_scenes/yuv.c create mode 100644 host_applications/linux/apps/raspicam/gl_scenes/yuv.h delete mode 100644 host_applications/linux/apps/raspicam/mirror.c create mode 100644 host_applications/linux/apps/raspicam/tga.c create mode 100644 host_applications/linux/apps/raspicam/tga.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 32cde59..6345900 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 2.8) project(vmcs_host_apps) set(BUILD_MMAL TRUE) -set(BUILD_MMAL_COMPONENTS FALSE) if (ALL_APPS) set(BUILD_MMAL_APPS TRUE) else() @@ -70,6 +69,11 @@ if(BUILD_MMAL_APPS) add_subdirectory(containers) endif() +# VidTex supports Android and Linux +if(BUILD_MMAL_COMPONENTS) +add_subdirectory(host_applications/android/apps/vidtex) +endif(BUILD_MMAL_COMPONENTS) + add_subdirectory(middleware/openmaxil) # 3d demo code diff --git a/helpers/vc_image/vc_image.h b/helpers/vc_image/vc_image.h index 5928669..8ccd7cc 100644 --- a/helpers/vc_image/vc_image.h +++ b/helpers/vc_image/vc_image.h @@ -246,6 +246,8 @@ extern "C" { case VC_IMAGE_TF_PAL4: \ case VC_IMAGE_TF_ETC1: \ case VC_IMAGE_TF_Y8: \ + case VC_IMAGE_TF_U8: \ + case VC_IMAGE_TF_V8: \ case VC_IMAGE_TF_A8: \ case VC_IMAGE_TF_SHORT: \ case VC_IMAGE_TF_1BPP diff --git a/host_applications/linux/apps/raspicam/CMakeLists.txt b/host_applications/linux/apps/raspicam/CMakeLists.txt index 37aae4c..590586d 100644 --- a/host_applications/linux/apps/raspicam/CMakeLists.txt +++ b/host_applications/linux/apps/raspicam/CMakeLists.txt @@ -4,13 +4,29 @@ SET(COMPILE_DEFINITIONS -Werror) include_directories(${CMAKE_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) +include_directories(${CMAKE_SOURCE_DIR}/host_applications/linux/apps/raspicam/) -add_executable(raspistill RaspiCamControl.c RaspiCLI.c RaspiPreview.c RaspiStill.c RaspiTex.c RaspiTexUtil.c teapot.c models.c square.c mirror.c) -add_executable(raspiyuv RaspiCamControl.c RaspiCLI.c RaspiPreview.c RaspiStillYUV.c) -add_executable(raspivid RaspiCamControl.c RaspiCLI.c RaspiPreview.c RaspiVid.c) +set (GL_SCENE_SOURCES + gl_scenes/models.c + gl_scenes/mirror.c + gl_scenes/yuv.c + gl_scenes/sobel.c + gl_scenes/square.c + gl_scenes/teapot.c) -target_link_libraries(raspistill mmal_core mmal_util mmal_vc_client vcos bcm_host GLESv2 EGL m) -target_link_libraries(raspiyuv mmal_core mmal_util mmal_vc_client vcos bcm_host) -target_link_libraries(raspivid mmal_core mmal_util mmal_vc_client vcos bcm_host) +set (COMMON_SOURCES + RaspiCamControl.c + RaspiCLI.c + RaspiPreview.c) + +add_executable(raspistill ${COMMON_SOURCES} RaspiStill.c RaspiTex.c RaspiTexUtil.c tga.c ${GL_SCENE_SOURCES}) +add_executable(raspiyuv ${COMMON_SOURCES} RaspiStillYUV.c) +add_executable(raspivid ${COMMON_SOURCES} RaspiVid.c) + +set (MMAL_LIBS mmal_core mmal_util mmal_vc_client) + +target_link_libraries(raspistill ${MMAL_LIBS} vcos bcm_host GLESv2 EGL m) +target_link_libraries(raspiyuv ${MMAL_LIBS} vcos bcm_host) +target_link_libraries(raspivid ${MMAL_LIBS} vcos bcm_host) install(TARGETS raspistill raspiyuv raspivid RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/raspicam/RaspiStill.c b/host_applications/linux/apps/raspicam/RaspiStill.c index b549a8a..1c5eccb 100644 --- a/host_applications/linux/apps/raspicam/RaspiStill.c +++ b/host_applications/linux/apps/raspicam/RaspiStill.c @@ -134,6 +134,7 @@ typedef struct int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps. int frameNextMethod; /// Which method to use to advance to next frame int useGL; /// Render preview using OpenGL + int glCapture; /// Save the GL frame-buffer instead of camera output RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters @@ -181,6 +182,7 @@ static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag); #define CommandKeypress 15 #define CommandSignal 16 #define CommandGL 17 +#define CommandGLCapture 18 static COMMAND_LIST cmdline_commands[] = { @@ -202,6 +204,7 @@ static COMMAND_LIST cmdline_commands[] = { CommandKeypress,"-keypress", "k", "Wait between captures for a ENTER, X then ENTER to exit", 0}, { CommandSignal, "-signal", "s", "Wait between captures for a SIGUSR1 from another process", 0}, { CommandGL, "-gl", "g", "Draw preview to texture instead of using video render component", 0}, + { CommandGLCapture, "-glcapture","gc", "Capture the GL frame-buffer instead of the camera image", 0}, }; static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); @@ -276,6 +279,7 @@ static void default_status(RASPISTILL_STATE *state) state->fullResPreview = 0; state->frameNextMethod = FRAME_NEXT_SINGLE; state->useGL = 0; + state->glCapture = 0; // Setup preview window defaults raspipreview_set_defaults(&state->preview_parameters); @@ -584,6 +588,10 @@ static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) state->useGL = 1; break; + case CommandGLCapture: + state->glCapture = 1; + break; + default: { // Try parsing for any image specific parameters @@ -619,8 +627,14 @@ static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) state->raspitex_state.width = state->preview_parameters.previewWindow.width; state->raspitex_state.height = state->preview_parameters.previewWindow.height; } - state->raspitex_state.opacity = state->preview_parameters.opacity; - state->raspitex_state.verbose = state->verbose; + /* Also pass the preview information through so GL renderer can determine + * the real resolution of the multi-media image */ + state->raspitex_state.preview_x = state->preview_parameters.previewWindow.x; + state->raspitex_state.preview_y = state->preview_parameters.previewWindow.y; + state->raspitex_state.preview_width = state->preview_parameters.previewWindow.width; + state->raspitex_state.preview_height = state->preview_parameters.previewWindow.height; + state->raspitex_state.opacity = state->preview_parameters.opacity; + state->raspitex_state.verbose = state->verbose; if (!valid) { @@ -1449,7 +1463,37 @@ static int wait_for_next_frame(RASPISTILL_STATE *state, int *frame) return keep_running; } +static void rename_file(RASPISTILL_STATE *state, FILE *output_file, + const char *final_filename, const char *use_filename, int frame) +{ + MMAL_STATUS_T status; + fclose(output_file); + vcos_assert(use_filename != NULL && final_filename != NULL); + if (0 != rename(use_filename, final_filename)) + { + vcos_log_error("Could not rename temp file to: %s; %s", + final_filename,strerror(errno)); + } + if (state->linkname) + { + char *use_link; + char *final_link; + status = create_filenames(&final_link, &use_link, state->linkname, frame); + + // Create hard link if possible, symlink otherwise + if (status != MMAL_SUCCESS + || (0 != link(final_filename, use_link) + && 0 != symlink(final_filename, use_link)) + || 0 != rename(use_link, final_link)) + { + vcos_log_error("Could not link as filename: %s; %s", + state->linkname,strerror(errno)); + } + if (use_link) free(use_link); + if (final_link) free(final_link); + } +} /** * main @@ -1649,7 +1693,15 @@ int main(int argc, const char **argv) } // We only capture if a filename was specified and it opened - if (output_file) + if (state.useGL && state.glCapture && output_file) + { + /* Save the next GL framebuffer as the next camera still */ + int rc = raspitex_capture(&state.raspitex_state, output_file); + if (rc != 0) + vcos_log_error("Failed to capture GL preview"); + rename_file(&state, output_file, final_filename, use_filename, frame); + } + else if (output_file) { int num, q; @@ -1725,32 +1777,8 @@ int main(int argc, const char **argv) if (output_file != stdout) { - fclose(output_file); - vcos_assert(use_filename != NULL && final_filename != NULL); - if (0 != rename(use_filename, final_filename)) - { - vcos_log_error("Could not rename temp file to: %s; %s", - final_filename,strerror(errno)); - } - if (state.linkname) - { - char *use_link; - char *final_link; - status = create_filenames(&final_link, &use_link, state.linkname, frame); - - // Create hard link if possible, symlink otherwise - if (status != MMAL_SUCCESS - || (0 != link(final_filename, use_link) - && 0 != symlink(final_filename, use_link)) - || 0 != rename(use_link, final_link)) - { - vcos_log_error("Could not link as filename: %s; %s", - state.linkname,strerror(errno)); - } - if (use_link) free(use_link); - if (final_link) free(final_link); - } - } + rename_file(&state, output_file, final_filename, use_filename, frame); + } // Disable encoder output port status = mmal_port_disable(encoder_output_port); } diff --git a/host_applications/linux/apps/raspicam/RaspiTex.c b/host_applications/linux/apps/raspicam/RaspiTex.c index f9c1401..c8af796 100644 --- a/host_applications/linux/apps/raspicam/RaspiTex.c +++ b/host_applications/linux/apps/raspicam/RaspiTex.c @@ -42,10 +42,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "interface/mmal/mmal_buffer.h" #include "interface/mmal/util/mmal_util.h" #include "interface/mmal/util/mmal_util_params.h" +#include "tga.h" -#include "mirror.h" -#include "square.h" -#include "teapot.h" +#include "gl_scenes/mirror.h" +#include "gl_scenes/sobel.h" +#include "gl_scenes/square.h" +#include "gl_scenes/teapot.h" +#include "gl_scenes/yuv.h" /** * \file RaspiTex.c @@ -94,7 +97,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static COMMAND_LIST cmdline_commands[] = { - { CommandGLScene, "-glscene", "gs", "GL scene square,teapot,mirror", 1 }, + { CommandGLScene, "-glscene", "gs", "GL scene square,teapot,mirror,yuv,sobel", 1 }, { CommandGLWin, "-glwin", "gw", "GL window settings <'x,y,w,h'>", 1 }, }; @@ -152,6 +155,12 @@ int raspitex_parse_cmdline(RASPITEX_STATE *state, state->scene_id = RASPITEX_SCENE_TEAPOT; else if (strcmp(arg2, "mirror") == 0) state->scene_id = RASPITEX_SCENE_MIRROR; + else if (strcmp(arg2, "yuv") == 0) + state->scene_id = RASPITEX_SCENE_YUV; + else if (strcmp(arg2, "sobel") == 0) + state->scene_id = RASPITEX_SCENE_SOBEL; + else + vcos_log_error("Unknown scene %s", arg2); used = 2; break; @@ -195,6 +204,51 @@ static void update_fps() } } +/** + * Captures the frame-buffer if requested. + * @param state RASPITEX STATE + * @return Zero if successful. + */ +static void raspitex_do_capture(RASPITEX_STATE *state) +{ + uint8_t *buffer = NULL; + size_t size = 0; + + if (state->capture.request) + { + if (state->ops.capture(state, &buffer, &size) == 0) + { + /* Pass ownership of buffer to main thread via capture state */ + state->capture.buffer = buffer; + state->capture.size = size; + } + else + { + state->capture.buffer = NULL; // Null indicates an error + state->capture.size = 0; + } + + state->capture.request = 0; // Always clear request and post sem + vcos_semaphore_post(&state->capture.completed_sem); + } +} + +/** + * Checks if there is at least one valid EGL image. + * @param state RASPITEX STATE + * @return Zero if successful. + */ +static int check_egl_image(RASPITEX_STATE *state) +{ + if (state->egl_image == EGL_NO_IMAGE_KHR && + state->y_egl_image == EGL_NO_IMAGE_KHR && + state->u_egl_image == EGL_NO_IMAGE_KHR && + state->v_egl_image == EGL_NO_IMAGE_KHR) + return -1; + else + return 0; +} + /** * Draws the next preview frame. If a new preview buffer is available then the * preview texture is updated first. @@ -217,15 +271,48 @@ static int raspitex_draw(RASPITEX_STATE *state, MMAL_BUFFER_HEADER_T *buf) if (buf) { /* Update the texture to the new viewfinder image which should */ - rc = state->ops.update_texture(state, (EGLClientBuffer) buf->data); - if (rc != 0) + if (state->ops.update_texture) { - vcos_log_error("%s: Failed to update texture", VCOS_FUNCTION); - goto end; + rc = state->ops.update_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update RGBX texture %d", + VCOS_FUNCTION, rc); + goto end; + } } - /* Now return the PREVIOUS MMAL buffer header back to the camera preview. - */ + if (state->ops.update_y_texture) + { + rc = state->ops.update_y_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update Y' plane texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_u_texture) + { + rc = state->ops.update_u_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update U plane texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_v_texture) + { + rc = state->ops.update_v_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update V texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + /* Now return the PREVIOUS MMAL buffer header back to the camera preview. */ if (state->preview_buf) mmal_buffer_header_release(state->preview_buf); @@ -233,7 +320,7 @@ static int raspitex_draw(RASPITEX_STATE *state, MMAL_BUFFER_HEADER_T *buf) } /* Do the drawing */ - if (state->preview_egl_image != EGL_NO_IMAGE_KHR) + if (check_egl_image(state) == 0) { rc = state->ops.update_model(state); if (rc != 0) @@ -243,9 +330,15 @@ static int raspitex_draw(RASPITEX_STATE *state, MMAL_BUFFER_HEADER_T *buf) if (rc != 0) goto end; + raspitex_do_capture(state); + eglSwapBuffers(state->display, state->surface); update_fps(); } + else + { + // vcos_log_trace("%s: No preview image", VCOS_FUNCTION); + } end: return rc; @@ -354,13 +447,13 @@ static void preview_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) if (buf->length == 0) { - vcos_log_info("%s: zero-length buffer => EOS", port->name); + vcos_log_trace("%s: zero-length buffer => EOS", port->name); state->preview_stop = 1; mmal_buffer_header_release(buf); } else if (buf->data == NULL) { - vcos_log_info("%s: zero buffer handle", port->name); + vcos_log_trace("%s: zero buffer handle", port->name); mmal_buffer_header_release(buf); } else @@ -450,13 +543,13 @@ end: return (status == MMAL_SUCCESS ? 0 : -1); } - /* Initialises GL preview state and creates the dispmanx native window. * @param state Pointer to the GL preview state. * @return Zero if successful. */ int raspitex_init(RASPITEX_STATE *state) { + VCOS_STATUS_T status; int rc; vcos_init(); @@ -465,6 +558,16 @@ int raspitex_init(RASPITEX_STATE *state) state->verbose ? VCOS_LOG_INFO : VCOS_LOG_WARN); vcos_log_trace("%s", VCOS_FUNCTION); + status = vcos_semaphore_create(&state->capture.start_sem, + "glcap_start_sem", 1); + if (status != VCOS_SUCCESS) + goto error; + + status = vcos_semaphore_create(&state->capture.completed_sem, + "glcap_completed_sem", 0); + if (status != VCOS_SUCCESS) + goto error; + switch (state->scene_id) { case RASPITEX_SCENE_SQUARE: @@ -476,14 +579,24 @@ int raspitex_init(RASPITEX_STATE *state) case RASPITEX_SCENE_TEAPOT: rc = teapot_open(state); break; + case RASPITEX_SCENE_YUV: + rc = yuv_open(state); + break; + case RASPITEX_SCENE_SOBEL: + rc = sobel_open(state); + break; default: rc = -1; break; } if (rc != 0) - return rc; + goto error; - return rc; + return 0; + +error: + vcos_log_error("%s: failed", VCOS_FUNCTION); + return -1; } /* Destroys the pools of buffers used by the GL renderer. @@ -503,8 +616,15 @@ void raspitex_destroy(RASPITEX_STATE *state) mmal_queue_destroy(state->preview_queue); state->preview_queue = NULL; } - state->ops.destroy_native_window(state); - state->ops.close(state); + + if (state->ops.destroy_native_window) + state->ops.destroy_native_window(state); + + if (state->ops.close) + state->ops.close(state); + + vcos_semaphore_delete(&state->capture.start_sem); + vcos_semaphore_delete(&state->capture.completed_sem); } /* Initialise the GL / window state to sensible defaults. @@ -516,10 +636,15 @@ void raspitex_destroy(RASPITEX_STATE *state) void raspitex_set_defaults(RASPITEX_STATE *state) { memset(state, 0, sizeof(*state)); + state->version_major = RASPITEX_VERSION_MAJOR; + state->version_minor = RASPITEX_VERSION_MINOR; state->display = EGL_NO_DISPLAY; state->surface = EGL_NO_SURFACE; state->context = EGL_NO_CONTEXT; - state->preview_egl_image = EGL_NO_IMAGE_KHR; + state->egl_image = EGL_NO_IMAGE_KHR; + state->y_egl_image = EGL_NO_IMAGE_KHR; + state->u_egl_image = EGL_NO_IMAGE_KHR; + state->v_egl_image = EGL_NO_IMAGE_KHR; state->opacity = 255; state->width = DEFAULT_WIDTH; state->height = DEFAULT_HEIGHT; @@ -527,9 +652,9 @@ void raspitex_set_defaults(RASPITEX_STATE *state) state->ops.create_native_window = raspitexutil_create_native_window; state->ops.gl_init = raspitexutil_gl_init_1_0; - state->ops.update_texture = raspitexutil_update_texture; state->ops.update_model = raspitexutil_update_model; state->ops.redraw = raspitexutil_redraw; + state->ops.capture = raspitexutil_capture_bgra; state->ops.gl_term = raspitexutil_gl_term; state->ops.destroy_native_window = raspitexutil_destroy_native_window; state->ops.close = raspitexutil_close; @@ -569,3 +694,55 @@ int raspitex_start(RASPITEX_STATE *state) return (status == VCOS_SUCCESS ? 0 : -1); } + +/** + * Writes the next GL frame-buffer to a RAW .ppm formatted file + * using the specified file-handle. + * @param state Pointer to the GL preview state. + * @param outpt_file Output file handle for the ppm image. + * @return Zero on success. + */ +int raspitex_capture(RASPITEX_STATE *state, FILE *output_file) +{ + int rc = 0; + uint8_t *buffer = NULL; + size_t size = 0; + + vcos_log_trace("%s: state %p file %p", VCOS_FUNCTION, + state, output_file); + + if (state && output_file) + { + /* Only request one capture at a time */ + vcos_semaphore_wait(&state->capture.start_sem); + state->capture.request = 1; + + /* Wait for capture to start */ + vcos_semaphore_wait(&state->capture.completed_sem); + + /* Take ownership of the captured buffer */ + buffer = state->capture.buffer; + size = state->capture.size; + + state->capture.request = 0; + state->capture.buffer = 0; + state->capture.size = 0; + + /* Allow another capture to be requested */ + vcos_semaphore_post(&state->capture.start_sem); + } + if (size == 0 || ! buffer) + { + vcos_log_error("%s: capture failed", VCOS_FUNCTION); + rc = -1; + goto end; + } + + raspitexutil_brga_to_rgba(buffer, size); + rc = write_tga(output_file, state->width, state->height, buffer, size); + fflush(output_file); + +end: + free(buffer); + return rc; +} diff --git a/host_applications/linux/apps/raspicam/RaspiTex.h b/host_applications/linux/apps/raspicam/RaspiTex.h index 89cf31d..b177417 100644 --- a/host_applications/linux/apps/raspicam/RaspiTex.h +++ b/host_applications/linux/apps/raspicam/RaspiTex.h @@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef RASPITEX_H_ #define RASPITEX_H_ +#include #include #include #include @@ -36,10 +37,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "interface/khronos/include/EGL/eglext_brcm.h" #include "interface/mmal/mmal.h" +#define RASPITEX_VERSION_MAJOR 1 +#define RASPITEX_VERSION_MINOR 0 + typedef enum { RASPITEX_SCENE_SQUARE = 0, RASPITEX_SCENE_MIRROR, - RASPITEX_SCENE_TEAPOT + RASPITEX_SCENE_TEAPOT, + RASPITEX_SCENE_YUV, + RASPITEX_SCENE_SOBEL, } RASPITEX_SCENE_T; @@ -54,15 +60,33 @@ typedef struct RASPITEX_SCENE_OPS /// Creates EGL surface for native window int (*gl_init)(struct RASPITEX_STATE *state); - /// Advance to the next animation step + /// Updates the RGBX texture from the next MMAL buffer + /// Set to null if this texture type is not required int (*update_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + /// Updates the Y' plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_y_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the U plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_u_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the V plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_v_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + /// Advance to the next animation step int (*update_model)(struct RASPITEX_STATE *state); /// Draw the scene - called after update_model int (*redraw)(struct RASPITEX_STATE *state); + /// Allocates a buffer and copies the pixels from the current + /// frame-buffer into it. + int (*capture)(struct RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size); + /// Creates EGL surface for native window void (*gl_term)(struct RASPITEX_STATE *state); @@ -73,18 +97,45 @@ typedef struct RASPITEX_SCENE_OPS void (*close)(struct RASPITEX_STATE *state); } RASPITEX_SCENE_OPS; +typedef struct RASPITEX_CAPTURE +{ + /// Wait for previous capture to complete + VCOS_SEMAPHORE_T start_sem; + + /// Posted once the capture is complete + VCOS_SEMAPHORE_T completed_sem; + + /// The RGB capture buffer + uint8_t *buffer; + + /// Size of the captured buffer in bytes + size_t size; + + /// Frame-buffer capture has been requested. Could use + /// a queue instead here to allow multiple capture requests. + int request; +} RASPITEX_CAPTURE; + /** * Contains the internal state and configuration for the GL rendered * preview window. */ typedef struct RASPITEX_STATE { + int version_major; /// For binary compatibility + int version_minor; /// Incremented for new features MMAL_PORT_T *preview_port; /// Source port for preview opaque buffers MMAL_POOL_T *preview_pool; /// Pool for storing opaque buffer handles MMAL_QUEUE_T *preview_queue; /// Queue preview buffers to display in order VCOS_THREAD_T preview_thread; /// Preview worker / GL rendering thread uint32_t preview_stop; /// If zero the worker can continue + /* Copy of preview window params */ + int32_t preview_x; /// x-offset of preview window + int32_t preview_y; /// y-offset of preview window + int32_t preview_width; /// preview y-plane width in pixels + int32_t preview_height; /// preview y-plane height in pixels + /* Display rectangle for the native window */ int32_t x; /// x-offset in pixels int32_t y; /// y-offset in pixels @@ -102,16 +153,27 @@ typedef struct RASPITEX_STATE EGLDisplay display; /// The current EGL display EGLSurface surface; /// The current EGL surface EGLContext context; /// The current EGL context + const EGLint *egl_config_attribs; /// GL scenes preferred EGL configuration - GLuint texture; /// Texture name for the preview texture - EGLImageKHR preview_egl_image; /// The current preview EGL image - MMAL_BUFFER_HEADER_T *preview_buf; /// MMAL buffer currently bound to texture + GLuint texture; /// Name for the preview texture + EGLImageKHR egl_image; /// The current preview EGL image + + GLuint y_texture; /// The Y plane texture + EGLImageKHR y_egl_image; /// EGL image for Y plane texture + GLuint u_texture; /// The U plane texture + EGLImageKHR u_egl_image; /// EGL image for U plane texture + GLuint v_texture; /// The V plane texture + EGLImageKHR v_egl_image; /// EGL image for V plane texture + + MMAL_BUFFER_HEADER_T *preview_buf; /// MMAL buffer currently bound to texture(s) RASPITEX_SCENE_T scene_id; /// Id of the scene to load RASPITEX_SCENE_OPS ops; /// The interface for the current scene void *scene_state; /// Pointer to scene specific data int verbose; /// Log FPS + RASPITEX_CAPTURE capture; /// Frame-buffer capture state + } RASPITEX_STATE; int raspitex_init(RASPITEX_STATE *state); @@ -124,5 +186,6 @@ int raspitex_configure_preview_port(RASPITEX_STATE *state, void raspitex_display_help(); int raspitex_parse_cmdline(RASPITEX_STATE *state, const char *arg1, const char *arg2); +int raspitex_capture(RASPITEX_STATE *state, FILE* output_file); #endif /* RASPITEX_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.c b/host_applications/linux/apps/raspicam/RaspiTexUtil.c index 9f8b43b..120fef6 100644 --- a/host_applications/linux/apps/raspicam/RaspiTexUtil.c +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.c @@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "RaspiTexUtil.h" #include "RaspiTex.h" #include +#include VCOS_LOG_CAT_T raspitex_log_category; @@ -47,10 +48,22 @@ void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state) { vcos_log_trace("%s", VCOS_FUNCTION); - /* Delete texture name */ + /* Delete OES textures */ glDeleteTextures(1, &raspitex_state->texture); - eglDestroyImageKHR(raspitex_state->display, raspitex_state->preview_egl_image); - raspitex_state->preview_egl_image = EGL_NO_IMAGE_KHR; + eglDestroyImageKHR(raspitex_state->display, raspitex_state->egl_image); + raspitex_state->egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->y_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->y_egl_image); + raspitex_state->y_egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->u_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->u_egl_image); + raspitex_state->u_egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->v_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->v_egl_image); + raspitex_state->u_egl_image = EGL_NO_IMAGE_KHR; /* Terminate EGL */ eglMakeCurrent(raspitex_state->display, EGL_NO_SURFACE, @@ -80,13 +93,13 @@ int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state) dest_rect.width = raspitex_state->width; dest_rect.height = raspitex_state->height; - src_rect.width = dest_rect.width << 16; - src_rect.height = dest_rect.height << 16; - vcos_log_trace("%s: %d,%d,%d,%d %d,%d,0x%x,0x%x", VCOS_FUNCTION, src_rect.x, src_rect.y, src_rect.width, src_rect.height, dest_rect.x, dest_rect.y, dest_rect.width, dest_rect.height); + src_rect.width = dest_rect.width << 16; + src_rect.height = dest_rect.height << 16; + raspitex_state->disp = vc_dispmanx_display_open(disp_num); if (raspitex_state->disp == DISPMANX_NO_HANDLE) { @@ -208,6 +221,19 @@ error: return -1; } +/* Creates the RGBA and luma textures with some default parameters + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_create_textures(RASPITEX_STATE *raspitex_state) +{ + GLCHK(glGenTextures(1, &raspitex_state->texture)); + GLCHK(glGenTextures(1, &raspitex_state->y_texture)); + GLCHK(glGenTextures(1, &raspitex_state->u_texture)); + GLCHK(glGenTextures(1, &raspitex_state->v_texture)); + return 0; +} + /** * Creates an OpenGL ES 1.X context. * @param raspitex_state A pointer to the GL preview state. @@ -216,8 +242,9 @@ error: int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state) { int rc; + const EGLint* attribs = raspitex_state->egl_config_attribs; - const EGLint attribs[] = + const EGLint default_attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, @@ -234,13 +261,15 @@ int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state) EGL_NONE }; - vcos_log_trace("%s", VCOS_FUNCTION); + if (! attribs) + attribs = default_attribs; + rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs); if (rc != 0) goto end; GLCHK(glEnable(GL_TEXTURE_EXTERNAL_OES)); - GLCHK(glGenTextures(1, &raspitex_state->texture)); + rc = raspitexutil_create_textures(raspitex_state); end: return rc; @@ -254,14 +283,16 @@ end: int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state) { int rc; - const EGLint attribs[] = + const EGLint* attribs = raspitex_state->egl_config_attribs;; + + const EGLint default_attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; @@ -271,44 +302,102 @@ int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state) EGL_NONE }; + if (! attribs) + attribs = default_attribs; + vcos_log_trace("%s", VCOS_FUNCTION); rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs); if (rc != 0) goto end; - GLCHK(glGenTextures(1, &raspitex_state->texture)); + rc = raspitexutil_create_textures(raspitex_state); end: return rc; } /** - * Updates the viewfinder texture whenever a new viewfinder MMAL buffer - * is available. + * Advances the texture and EGL image to the next MMAL buffer. + * + * @param display The EGL display. + * @param target The EGL image target e.g. EGL_IMAGE_BRCM_MULTIMEDIA + * @param mm_buf The EGL client buffer (mmal opaque buffer) that is used to + * create the EGL Image for the preview texture. + * @param egl_image Pointer to the EGL image to update with mm_buf. + * @param texture Pointer to the texture to update from EGL image. + * @return Zero if successful. + */ +int raspitexutil_do_update_texture(EGLDisplay display, EGLenum target, + EGLClientBuffer mm_buf, GLuint *texture, EGLImageKHR *egl_image) +{ + vcos_log_trace("%s: mm_buf %u", VCOS_FUNCTION, (unsigned) mm_buf); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, *texture)); + if (*egl_image != EGL_NO_IMAGE_KHR) + { + /* Discard the EGL image for the preview frame */ + eglDestroyImageKHR(display, *egl_image); + *egl_image = EGL_NO_IMAGE_KHR; + } + + *egl_image = eglCreateImageKHR(display, EGL_NO_CONTEXT, target, mm_buf, NULL); + GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, *egl_image)); + + return 0; +} + +/** + * Updates the RGBX texture to the specified MMAL buffer. * @param raspitex_state A pointer to the GL preview state. - * @param mm_buf The EGL client buffer (mmal opaque buffer) that is - * used to create the EGL Image for the preview texture. + * @param mm_buf The MMAL buffer. * @return Zero if successful. */ int raspitexutil_update_texture(RASPITEX_STATE *raspitex_state, EGLClientBuffer mm_buf) { - // vcos_log_trace("%s: mm_buf %u", VCOS_FUNCTION, (unsigned) mm_buf); - if (raspitex_state->preview_egl_image != EGL_NO_IMAGE_KHR) - { - /* Discard the EGL image for the preview frame */ - eglDestroyImageKHR(raspitex_state->display, - raspitex_state->preview_egl_image); - raspitex_state->preview_egl_image = EGL_NO_IMAGE_KHR; - } + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA, mm_buf, + &raspitex_state->texture, &raspitex_state->egl_image); +} - raspitex_state->preview_egl_image = eglCreateImageKHR(raspitex_state->display, - EGL_NO_CONTEXT, EGL_IMAGE_BRCM_MULTIMEDIA, mm_buf, NULL); +/** + * Updates the Y plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_y_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_Y, mm_buf, + &raspitex_state->y_texture, &raspitex_state->y_egl_image); +} - GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture)); - GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, - raspitex_state->preview_egl_image)); +/** + * Updates the U plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_u_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_U, mm_buf, + &raspitex_state->u_texture, &raspitex_state->u_egl_image); +} - return 0; +/** + * Updates the V plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_v_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_V, mm_buf, + &raspitex_state->v_texture, &raspitex_state->v_egl_image); } /** @@ -341,3 +430,168 @@ void raspitexutil_close(RASPITEX_STATE* raspitex_state) { (void) raspitex_state; } + +/** + * Performs an in-place byte swap from BGRA to RGBA. + * @param buffer The buffer to modify. + * @param size Size of the buffer in bytes. + */ +void raspitexutil_brga_to_rgba(uint8_t *buffer, size_t size) +{ + uint8_t* out = buffer; + uint8_t* end = buffer + size; + + while (out < end) + { + uint8_t tmp = out[0]; + out[0] = out[2]; + out[2] = tmp; + out += 4; + } +} + +/** + * Uses glReadPixels to grab the current frame-buffer contents + * and returns the result in a newly allocate buffer along with + * the its size. + * Data is returned in BGRA format for TGA output. PPM output doesn't + * require the channel order swap but would require a vflip. The TGA + * format also supports alpha. The byte swap is not done in this function + * to avoid blocking the GL rendering thread. + * @param state Pointer to the GL preview state. + * @param buffer Address of pointer to set to pointer to new buffer. + * @param buffer_size The size of the new buffer in bytes (out param) + * @return Zero if successful. + */ +int raspitexutil_capture_bgra(RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size) +{ + const int bytes_per_pixel = 4; + + vcos_log_trace("%s: %dx%d %d", VCOS_FUNCTION, + state->width, state->height, bytes_per_pixel); + + *buffer_size = state->width * state->height * bytes_per_pixel; + *buffer = calloc(*buffer_size, 1); + if (! *buffer) + goto error; + + glReadPixels(0, 0, state->width, state->height, GL_RGBA, + GL_UNSIGNED_BYTE, *buffer); + if (glGetError() != GL_NO_ERROR) + goto error; + + return 0; + +error: + *buffer_size = 0; + if (*buffer) + free(*buffer); + *buffer = NULL; + return -1; +} + + +/** + * Takes a description of shader program, compiles it and gets the locations + * of uniforms and attributes. + * + * @param p The shader program state. + * @return Zero if successful. + */ +int raspitexutil_build_shader_program(RASPITEXUTIL_SHADER_PROGRAM_T *p) +{ + GLint status; + int i = 0; + char log[1024]; + int logLen = 0; + vcos_assert(p); + vcos_assert(p->vertex_source); + vcos_assert(p->fragment_source); + + if (! (p && p->vertex_source && p->fragment_source)) + goto fail; + + p->vs = p->fs = 0; + + p->vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(p->vs, 1, &p->vertex_source, NULL); + glCompileShader(p->vs); + glGetShaderiv(p->vs, GL_COMPILE_STATUS, &status); + if (! status) { + glGetShaderInfoLog(p->vs, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + p->fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(p->fs, 1, &p->fragment_source, NULL); + glCompileShader(p->fs); + + glGetShaderiv(p->fs, GL_COMPILE_STATUS, &status); + if (! status) { + glGetShaderInfoLog(p->fs, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + p->program = glCreateProgram(); + glAttachShader(p->program, p->vs); + glAttachShader(p->program, p->fs); + glLinkProgram(p->program); + glGetProgramiv(p->program, GL_LINK_STATUS, &status); + if (! status) + { + vcos_log_error("Failed to link shader program"); + glGetProgramInfoLog(p->program, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + for (i = 0; i < SHADER_MAX_ATTRIBUTES; ++i) + { + if (! p->attribute_names[i]) + break; + p->attribute_locations[i] = glGetAttribLocation(p->program, p->attribute_names[i]); + if (p->attribute_locations[i] == -1) + { + vcos_log_error("Failed to get location for attribute %s", + p->attribute_names[i]); + goto fail; + } + else { + vcos_log_trace("Attribute for %s is %d", + p->attribute_names[i], p->attribute_locations[i]); + } + } + + for (i = 0; i < SHADER_MAX_UNIFORMS; ++i) + { + if (! p->uniform_names[i]) + break; + p->uniform_locations[i] = glGetUniformLocation(p->program, p->uniform_names[i]); + if (p->uniform_locations[i] == -1) + { + vcos_log_error("Failed to get location for uniform %s", + p->uniform_names[i]); + goto fail; + } + else { + vcos_log_trace("Uniform for %s is %d", + p->uniform_names[i], p->uniform_locations[i]); + } + } + + return 0; + +fail: + vcos_log_error("%s: Failed to build shader program", VCOS_FUNCTION); + if (p) + { + glDeleteProgram(p->program); + glDeleteShader(p->fs); + glDeleteShader(p->vs); + } + return -1; +} + diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.h b/host_applications/linux/apps/raspicam/RaspiTexUtil.h index f73666e..cf5dfb3 100644 --- a/host_applications/linux/apps/raspicam/RaspiTexUtil.h +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.h @@ -26,8 +26,8 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RASPITEX_UTIL_H -#define RASPITEX_UTIL_H +#ifndef RASPITEX_UTIL_H_ +#define RASPITEX_UTIL_H_ #define VCOS_LOG_CATEGORY (&raspitex_log_category) #include @@ -41,6 +41,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. extern VCOS_LOG_CAT_T raspitex_log_category; +#define SHADER_MAX_ATTRIBUTES 16 +#define SHADER_MAX_UNIFORMS 16 +/** + * Container for a simple shader program. The uniform and attribute locations + * are automatically setup by raspitex_build_shader_program. + */ +typedef struct RASPITEXUTIL_SHADER_PROGRAM_T +{ + const char *vertex_source; /// Pointer to vertex shader source + const char *fragment_source; /// Pointer to fragment shader source + + /// Array of uniform names for raspitex_build_shader_program to process + const char *uniform_names[SHADER_MAX_UNIFORMS]; + /// Array of attribute names for raspitex_build_shader_program to process + const char *attribute_names[SHADER_MAX_ATTRIBUTES]; + + GLint vs; /// Vertex shader handle + GLint fs; /// Fragment shader handle + GLint program; /// Shader program handle + + /// The locations for uniforms defined in uniform_names + GLint uniform_locations[SHADER_MAX_UNIFORMS]; + + /// The locations for attributes defined in attribute_names + GLint attribute_locations[SHADER_MAX_ATTRIBUTES]; +} RASPITEXUTIL_SHADER_PROGRAM_T; + + /* Uncomment to enable extra GL error checking */ //#define CHECK_GL_ERRORS #if defined(CHECK_GL_ERRORS) @@ -50,7 +78,7 @@ do { \ X; \ while ((err = glGetError())) \ { \ - vcos_log_trace("GL error 0x%x in " #X "file %s line %d", err, __FILE__,__LINE__); \ + vcos_log_error("GL error 0x%x in " #X "file %s line %d", err, __FILE__,__LINE__); \ vcos_assert(err == GL_NO_ERROR); \ exit(err); \ } \ @@ -68,8 +96,21 @@ int raspitexutil_update_model(RASPITEX_STATE* raspitex_state); int raspitexutil_redraw(RASPITEX_STATE* raspitex_state); void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state); void raspitexutil_destroy_native_window(RASPITEX_STATE *raspitex_state); +int raspitexutil_create_textures(RASPITEX_STATE *raspitex_state); int raspitexutil_update_texture(RASPITEX_STATE *raspitex_state, EGLClientBuffer mm_buf); +int raspitexutil_update_y_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_u_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_v_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_capture_bgra(struct RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size); void raspitexutil_close(RASPITEX_STATE* raspitex_state); -#endif /* RASPITEX_UTIL_H */ +/* Utility functions */ +int raspitexutil_build_shader_program(RASPITEXUTIL_SHADER_PROGRAM_T *p); +void raspitexutil_brga_to_rgba(uint8_t *buffer, size_t size); + +#endif /* RASPITEX_UTIL_H_ */ diff --git a/host_applications/linux/apps/raspicam/cube_texture_and_coords.h b/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h similarity index 100% rename from host_applications/linux/apps/raspicam/cube_texture_and_coords.h rename to host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h diff --git a/host_applications/linux/apps/raspicam/gl_scenes/mirror.c b/host_applications/linux/apps/raspicam/gl_scenes/mirror.c new file mode 100644 index 0000000..fe14096 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/mirror.c @@ -0,0 +1,123 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mirror.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/** + * Draws an external EGL image and applies a sine wave distortion to create + * a hall of mirrors effect. + */ +static RASPITEXUTIL_SHADER_PROGRAM_T mirror_shader = { + .vertex_source = + "attribute vec2 vertex;\n" + "varying vec2 texcoord;" + "void main(void) {\n" + " texcoord = 0.5 * (vertex + 1.0);\n" + " gl_Position = vec4(vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "uniform float offset;\n" + "const float waves = 2.0;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " float x = texcoord.x + 0.05 * sin(offset + (texcoord.y * waves * 2.0 * 3.141592));\n" + " float y = texcoord.y + 0.05 * sin(offset + (texcoord.x * waves * 2.0 * 3.141592));\n" + " if (y < 1.0 && y > 0.0 && x < 1.0 && x > 0.0) {\n" + " vec2 pos = vec2(x, y);\n" + " gl_FragColor = texture2D(tex, pos);\n" + " }\n" + " else {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n", + .uniform_names = {"tex", "offset"}, + .attribute_names = {"vertex"}, +}; + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int mirror_init(RASPITEX_STATE *state) +{ + int rc = raspitexutil_gl_init_2_0(state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&mirror_shader); +end: + return rc; +} + +static int mirror_redraw(RASPITEX_STATE *raspitex_state) { + static float offset = 0.0; + + // Start with a clear screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Bind the OES texture which is used to render the camera preview + glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture); + + offset += 0.05; + GLCHK(glUseProgram(mirror_shader.program)); + GLCHK(glEnableVertexAttribArray(mirror_shader.attribute_locations[0])); + GLfloat varray[] = { + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f, + + -1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + }; + GLCHK(glVertexAttribPointer(mirror_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, varray)); + GLCHK(glUniform1f(mirror_shader.uniform_locations[1], offset)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(mirror_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + return 0; +} + +int mirror_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = mirror_init; + state->ops.redraw = mirror_redraw; + state->ops.update_texture = raspitexutil_update_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/mirror.h b/host_applications/linux/apps/raspicam/gl_scenes/mirror.h similarity index 100% rename from host_applications/linux/apps/raspicam/mirror.h rename to host_applications/linux/apps/raspicam/gl_scenes/mirror.h diff --git a/host_applications/linux/apps/raspicam/models.c b/host_applications/linux/apps/raspicam/gl_scenes/models.c similarity index 100% rename from host_applications/linux/apps/raspicam/models.c rename to host_applications/linux/apps/raspicam/gl_scenes/models.c diff --git a/host_applications/linux/apps/raspicam/models.h b/host_applications/linux/apps/raspicam/gl_scenes/models.h similarity index 100% rename from host_applications/linux/apps/raspicam/models.h rename to host_applications/linux/apps/raspicam/gl_scenes/models.h diff --git a/host_applications/linux/apps/raspicam/gl_scenes/sobel.c b/host_applications/linux/apps/raspicam/gl_scenes/sobel.c new file mode 100644 index 0000000..ad2c8d9 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/sobel.c @@ -0,0 +1,192 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "sobel.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/* \file sobel.c + * Example code for implementing Sobel filter as GLSL shaders. + * The input image is a greyscale texture from the MMAL buffer Y plane. + */ + +#define SOBEL_VSHADER_SOURCE \ + "attribute vec2 vertex;\n" \ + "varying vec2 texcoord;\n" \ + "void main(void) {\n" \ + " texcoord = 0.5 * (vertex + 1.0);\n" \ + " gl_Position = vec4(vertex, 0.0, 1.0);\n" \ + "}\n" + +/* Example Sobel edge detct shader. The texture format for + * EGL_IMAGE_BRCM_MULTIMEDIA_Y is a one byte per pixel greyscale GL_LUMINANCE. + * If the output is to be fed into another image processing shader then it may + * be worth changing this code to take 4 input Y pixels and pack the result + * into a 32bpp RGBA pixel. + */ +#define SOBEL_FSHADER_SOURCE \ + "#extension GL_OES_EGL_image_external : require\n" \ + "uniform samplerExternalOES tex;\n" \ + "varying vec2 texcoord;\n" \ + "uniform vec2 tex_unit;\n" \ + "void main(void) {\n" \ + " float x = texcoord.x;\n" \ + " float y = texcoord.y;\n" \ + " float x1 = x - tex_unit.x;\n" \ + " float y1 = y - tex_unit.y;\n" \ + " float x2 = x + tex_unit.x;\n" \ + " float y2 = y + tex_unit.y;\n" \ + " vec4 p0 = texture2D(tex, vec2(x1, y1));\n" \ + " vec4 p1 = texture2D(tex, vec2(x, y1));\n" \ + " vec4 p2 = texture2D(tex, vec2(x2, y1));\n" \ + " vec4 p3 = texture2D(tex, vec2(x1, y));\n" \ + " /* vec4 p4 = texture2D(tex, vec2(x, y)); */\n" \ + " vec4 p5 = texture2D(tex, vec2(x2, y));\n" \ + " vec4 p6 = texture2D(tex, vec2(x1, y2));\n" \ + " vec4 p7 = texture2D(tex, vec2(x, y2));\n" \ + " vec4 p8 = texture2D(tex, vec2(x2, y2));\n" \ + "\n" \ + " vec4 v = p0 + (2.0 * p1) + p3 -p6 + (-2.0 * p7) + -p8;\n" \ + " vec4 h = p0 + (2.0 * p3) + p7 -p2 + (-2.0 * p5) + -p8;\n" \ + " gl_FragColor = sqrt(h*h + v*v);\n" \ + " gl_FragColor.a = 1.0;\n" \ + "}\n" + +static GLfloat quad_varray[] = { + -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, +}; + +static GLuint quad_vbo; + +static RASPITEXUTIL_SHADER_PROGRAM_T sobel_shader = +{ + .vertex_source = SOBEL_VSHADER_SOURCE, + .fragment_source = SOBEL_FSHADER_SOURCE, + .uniform_names = {"tex", "tex_unit"}, + .attribute_names = {"vertex"}, +}; + +static const EGLint sobel_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + + +/** + * Initialisation of shader uniforms. + * + * @param width Width of the EGL image. + * @param width Height of the EGL image. + */ +static int shader_set_uniforms(RASPITEXUTIL_SHADER_PROGRAM_T *shader, + int width, int height) +{ + GLCHK(glUseProgram(shader->program)); + GLCHK(glUniform1i(shader->uniform_locations[0], 0)); // Texture unit + + /* Dimensions of a single pixel in texture co-ordinates */ + GLCHK(glUniform2f(shader->uniform_locations[1], + 1.0 / (float) width, 1.0 / (float) height)); + + /* Enable attrib 0 as vertex array */ + GLCHK(glEnableVertexAttribArray(shader->attribute_locations[0])); + return 0; +} + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int sobel_init(RASPITEX_STATE *raspitex_state) +{ + int rc = 0; + int width = raspitex_state->width; + int height = raspitex_state->height; + + vcos_log_trace("%s", VCOS_FUNCTION); + raspitex_state->egl_config_attribs = sobel_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(raspitex_state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&sobel_shader); + if (rc != 0) + goto end; + + rc = shader_set_uniforms(&sobel_shader, width, height); + if (rc != 0) + goto end; + + GLCHK(glGenBuffers(1, &quad_vbo)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, sizeof(quad_varray), quad_varray, GL_STATIC_DRAW)); + GLCHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + +end: + return rc; +} + +/* Redraws the scene with the latest luma buffer. + * + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int sobel_redraw(RASPITEX_STATE* state) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLCHK(glUseProgram(sobel_shader.program)); + + /* Bind the Y plane texture */ + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, state->y_texture)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glEnableVertexAttribArray(sobel_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(sobel_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + return 0; +} + +int sobel_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = sobel_init; + state->ops.redraw = sobel_redraw; + state->ops.update_y_texture = raspitexutil_update_y_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/sobel.h b/host_applications/linux/apps/raspicam/gl_scenes/sobel.h new file mode 100644 index 0000000..14ab13a --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/sobel.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SOBEL_H +#define SOBEL_H + +#include "RaspiTex.h" + +int sobel_open(RASPITEX_STATE *state); + +#endif /* SOBEL_H */ diff --git a/host_applications/linux/apps/raspicam/square.c b/host_applications/linux/apps/raspicam/gl_scenes/square.c similarity index 98% rename from host_applications/linux/apps/raspicam/square.c rename to host_applications/linux/apps/raspicam/gl_scenes/square.c index dfe2c89..a1028a1 100644 --- a/host_applications/linux/apps/raspicam/square.c +++ b/host_applications/linux/apps/raspicam/gl_scenes/square.c @@ -116,5 +116,6 @@ int square_open(RASPITEX_STATE *state) state->ops.gl_init = square_init; state->ops.update_model = square_update_model; state->ops.redraw = square_redraw; + state->ops.update_texture = raspitexutil_update_texture; return 0; } diff --git a/host_applications/linux/apps/raspicam/square.h b/host_applications/linux/apps/raspicam/gl_scenes/square.h similarity index 100% rename from host_applications/linux/apps/raspicam/square.h rename to host_applications/linux/apps/raspicam/gl_scenes/square.h diff --git a/host_applications/linux/apps/raspicam/teapot.c b/host_applications/linux/apps/raspicam/gl_scenes/teapot.c similarity index 95% rename from host_applications/linux/apps/raspicam/teapot.c rename to host_applications/linux/apps/raspicam/gl_scenes/teapot.c index 2c9adda..eac4bcb 100644 --- a/host_applications/linux/apps/raspicam/teapot.c +++ b/host_applications/linux/apps/raspicam/gl_scenes/teapot.c @@ -305,11 +305,19 @@ end: return rc; } -static void teapot_gl_term(RASPITEX_STATE *state) +static void teapot_gl_term(RASPITEX_STATE *raspitex_state) { - raspitexutil_gl_term(state); - free(state->scene_state); - state->scene_state = NULL; + vcos_log_trace("%s:", VCOS_FUNCTION); + + TEAPOT_STATE_T *state = raspitex_state->scene_state; + if (state) + { + if (state->model) + unload_wavefront(state->model); + raspitexutil_gl_term(raspitex_state); + free(raspitex_state->scene_state); + raspitex_state->scene_state = NULL; + } } int teapot_open(RASPITEX_STATE *raspitex_state) @@ -318,6 +326,7 @@ int teapot_open(RASPITEX_STATE *raspitex_state) raspitex_state->ops.update_model = teapot_update_model; raspitex_state->ops.redraw = teapot_redraw; raspitex_state->ops.gl_term = teapot_gl_term; + raspitex_state->ops.update_texture = raspitexutil_update_texture; return 0; } diff --git a/host_applications/linux/apps/raspicam/teapot.h b/host_applications/linux/apps/raspicam/gl_scenes/teapot.h similarity index 100% rename from host_applications/linux/apps/raspicam/teapot.h rename to host_applications/linux/apps/raspicam/gl_scenes/teapot.h diff --git a/host_applications/linux/apps/raspicam/gl_scenes/yuv.c b/host_applications/linux/apps/raspicam/gl_scenes/yuv.c new file mode 100644 index 0000000..89fd6ab --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/yuv.c @@ -0,0 +1,145 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "yuv.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/* Draw a scaled quad showing the the entire texture with the + * origin defined as an attribute */ +static RASPITEXUTIL_SHADER_PROGRAM_T yuv_shader = +{ + .vertex_source = + "attribute vec2 vertex;\n" + "attribute vec2 top_left;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " texcoord = vertex + vec2(0.0, 1.0);\n" + " gl_Position = vec4(top_left + vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, texcoord);\n" + "}\n", + .uniform_names = {"tex"}, + .attribute_names = {"vertex", "top_left"}, +}; + +static GLfloat varray[] = +{ + 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, +}; + +static const EGLint yuv_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int yuv_init(RASPITEX_STATE *state) +{ + int rc; + state->egl_config_attribs = yuv_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&yuv_shader); + GLCHK(glUseProgram(yuv_shader.program)); + GLCHK(glUniform1i(yuv_shader.uniform_locations[0], 0)); // tex unit +end: + return rc; +} + +/** + * Draws a 2x2 grid with each shell showing the entire MMAL buffer from a + * different EGL image target. + */ +static int yuv_redraw(RASPITEX_STATE *raspitex_state) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + GLCHK(glUseProgram(yuv_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glEnableVertexAttribArray(yuv_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(yuv_shader.attribute_locations[0], + 2, GL_FLOAT, GL_FALSE, 0, varray)); + + // Y plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], -1.0f, 1.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // U plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->u_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], 0.0f, 1.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // V plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->v_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], 0.0f, 0.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // RGB plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], -1.0f, 0.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(yuv_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + return 0; +} + +int yuv_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = yuv_init; + state->ops.redraw = yuv_redraw; + state->ops.update_texture = raspitexutil_update_texture; + state->ops.update_y_texture = raspitexutil_update_y_texture; + state->ops.update_u_texture = raspitexutil_update_u_texture; + state->ops.update_v_texture = raspitexutil_update_v_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/yuv.h b/host_applications/linux/apps/raspicam/gl_scenes/yuv.h new file mode 100644 index 0000000..b0e3a91 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/yuv.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef YUV_H +#define YUV_H + +#include "RaspiTex.h" + +int yuv_open(RASPITEX_STATE *state); + +#endif /* YUV_H */ diff --git a/host_applications/linux/apps/raspicam/mirror.c b/host_applications/linux/apps/raspicam/mirror.c deleted file mode 100644 index f0732cf..0000000 --- a/host_applications/linux/apps/raspicam/mirror.c +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright (c) 2013, Broadcom Europe Ltd -Copyright (c) 2013, Tim Gover -All rights reserved. - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the copyright holder nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "RaspiTex.h" -#include "RaspiTexUtil.h" -#include -#include -#include - -#define SHADER_MAX_ATTRIBUTES 16 -#define SHADER_MAX_UNIFORMS 16 - -/** - * Container for a GL texture. - */ -struct TEXTURE_T { - GLuint name; - GLuint width; - GLuint height; -}; - -/** - * Container for a simple vertex, fragment share with the names and locations. - */ -struct SHADER_PROGRAM_T { - const char *vertex_source; - const char *fragment_source; - const char *uniform_names[SHADER_MAX_UNIFORMS]; - const char *attribute_names[SHADER_MAX_ATTRIBUTES]; - GLint vs; - GLint fs; - GLint program; - - /// The locations for uniforms defined in uniform_names - GLint uniform_locations[SHADER_MAX_UNIFORMS]; - - /// The locations for attributes defined in attribute_names - GLint attribute_locations[SHADER_MAX_ATTRIBUTES]; - - /// Optional texture information - struct TEXTURE_T tex; -}; - -/** - * Draws an external EGL image and applies a sine wave distortion to create - * a hall of mirrors effect. - */ -struct SHADER_PROGRAM_T picture_shader = { - .vertex_source = - "attribute vec2 vertex;\n" - "varying vec2 texcoord;" - "void main(void) {\n" - " texcoord = 0.5 * (vertex + 1.0);\n" - " gl_Position = vec4(vertex, 0.0, 1.0);\n" - "}\n", - - .fragment_source = - "#extension GL_OES_EGL_image_external : require\n" - "uniform samplerExternalOES tex;\n" - "uniform float offset;\n" - "const float waves = 2.0;\n" - "varying vec2 texcoord;\n" - "void main(void) {\n" - " float x = texcoord.x + 0.05 * sin(offset + (texcoord.y * waves * 2.0 * 3.141592));\n" - " float y = texcoord.y + 0.05 * sin(offset + (texcoord.x * waves * 2.0 * 3.141592));\n" - " if (y < 1.0 && y > 0.0 && x < 1.0 && x > 0.0) {\n" - " vec2 pos = vec2(x, y);\n" - " gl_FragColor = texture2D(tex, pos);\n" - " }\n" - " else {\n" - " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" - " }\n" - "}\n", - .uniform_names = {"tex", "offset"}, - .attribute_names = {"vertex"}, - -}; - -/** - * Utility for building shaders and configuring the attribute and locations. - * @return Zero if successful. - */ -static int buildShaderProgram(struct SHADER_PROGRAM_T *p) -{ - GLint status; - int i = 0; - char log[1024]; - int logLen = 0; - assert(p); - assert(p->vertex_source); - assert(p->fragment_source); - - if (! (p && p->vertex_source && p->fragment_source)) - goto fail; - - p->vs = p->fs = 0; - - GLCHK(p->vs = glCreateShader(GL_VERTEX_SHADER)); - GLCHK(glShaderSource(p->vs, 1, &p->vertex_source, NULL)); - GLCHK(glCompileShader(p->vs)); - GLCHK(glGetShaderiv(p->vs, GL_COMPILE_STATUS, &status)); - if (! status) { - glGetShaderInfoLog(p->vs, sizeof(log), &logLen, log); - vcos_log_trace("Program info log %s", log); - goto fail; - } - - GLCHK(p->fs = glCreateShader(GL_FRAGMENT_SHADER)); - GLCHK(glShaderSource(p->fs, 1, &p->fragment_source, NULL)); - GLCHK(glCompileShader(p->fs)); - GLCHK(glGetShaderiv(p->fs, GL_COMPILE_STATUS, &status)); - if (! status) { - glGetShaderInfoLog(p->fs, sizeof(log), &logLen, log); - vcos_log_trace("Program info log %s", log); - goto fail; - } - - GLCHK(p->program = glCreateProgram()); - GLCHK(glAttachShader(p->program, p->vs)); - GLCHK(glAttachShader(p->program, p->fs)); - GLCHK(glLinkProgram(p->program)); - - GLCHK(glGetProgramiv(p->program, GL_LINK_STATUS, &status)); - if (! status) - { - vcos_log_trace("Failed to link shader program"); - glGetProgramInfoLog(p->program, sizeof(log), &logLen, log); - vcos_log_trace("Program info log %s", log); - goto fail; - } - - for (i = 0; i < SHADER_MAX_ATTRIBUTES; ++i) - { - if (! p->attribute_names[i]) - break; - GLCHK(p->attribute_locations[i] = glGetAttribLocation(p->program, p->attribute_names[i])); - if (p->attribute_locations[i] == -1) - { - vcos_log_trace("Failed to get location for attribute %s", p->attribute_names[i]); - goto fail; - } - else { - vcos_log_trace("Attribute for %s is %d", p->attribute_names[i], p->attribute_locations[i]); - } - } - - for (i = 0; i < SHADER_MAX_UNIFORMS; ++i) - { - if (! p->uniform_names[i]) - break; - GLCHK(p->uniform_locations[i] = glGetUniformLocation(p->program, p->uniform_names[i])); - if (p->uniform_locations[i] == -1) - { - vcos_log_trace("Failed to get location for uniform %s", p->uniform_names[i]); - goto fail; - } - else { - vcos_log_trace("Uniform for %s is %d", p->uniform_names[i], p->uniform_locations[i]); - } - } - - return 0; -fail: - vcos_log_trace("%s: Failed to build shader program", __func__); - if (p) - { - glDeleteProgram(p->program); - glDeleteShader(p->fs); - glDeleteShader(p->vs); - } - return -1; -} - -/** - * Creates the OpenGL ES 2.X context and builds the shaders. - * @param raspitex_state A pointer to the GL preview state. - * @return Zero if successful. - */ -static int mirror_init(RASPITEX_STATE *state) -{ - int rc = raspitexutil_gl_init_2_0(state); - if (rc != 0) - goto end; - - rc = buildShaderProgram(&picture_shader); -end: - return rc; -} - -static int mirror_redraw(RASPITEX_STATE *raspitex_state) { - static float offset = 0.0; - - // Start with a clear screen - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Bind the OES texture which is used to render the camera preview - glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture); - - offset += 0.05; - GLCHK(glUseProgram(picture_shader.program)); - GLCHK(glEnableVertexAttribArray(picture_shader.attribute_locations[0])); - GLfloat varray[] = { - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, -1.0f, - - -1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - }; - GLCHK(glVertexAttribPointer(picture_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, varray)); - GLCHK(glUniform1f(picture_shader.uniform_locations[1], offset)); - GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); - - GLCHK(glDisableVertexAttribArray(picture_shader.attribute_locations[0])); - GLCHK(glUseProgram(0)); - return 0; -} - -int mirror_open(RASPITEX_STATE *state) -{ - state->ops.gl_init = mirror_init; - state->ops.redraw = mirror_redraw; - return 0; -} diff --git a/host_applications/linux/apps/raspicam/tga.c b/host_applications/linux/apps/raspicam/tga.c new file mode 100644 index 0000000..7e64ae4 --- /dev/null +++ b/host_applications/linux/apps/raspicam/tga.c @@ -0,0 +1,113 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "tga.h" +#include + +#define TGA_WRITE(FP, F) \ + if (fwrite((&F), sizeof(F), 1, (FP)) != 1) goto write_fail +int write_tga(FILE *fp, int width, int height, + uint8_t *buffer, size_t buffer_size) +{ + struct tga_header header; + memset(&header, 0, sizeof(header)); + header.image_type = tga_type_true_color; + header.image_info.width = width; + header.image_info.y_origin = height; + header.image_info.height = height; + header.image_info.bpp = 32; + + TGA_WRITE(fp, header.id_length); + TGA_WRITE(fp, header.color_map_type); + TGA_WRITE(fp, header.image_type); + TGA_WRITE(fp, header.colormap_info.offset); + TGA_WRITE(fp, header.colormap_info.length); + TGA_WRITE(fp, header.colormap_info.bpp); + TGA_WRITE(fp, header.image_info.x_origin); + TGA_WRITE(fp, header.image_info.y_origin); + TGA_WRITE(fp, header.image_info.width); + TGA_WRITE(fp, header.image_info.height); + TGA_WRITE(fp, header.image_info.bpp); + TGA_WRITE(fp, header.image_info.descriptor); + + if (fwrite(buffer, 1, buffer_size, fp) != buffer_size) + goto write_fail; + + return 0; +write_fail: + return -1; +} + +#define TGA_READ(FP, F) if (fread((&F), sizeof(F), 1, (FP)) != 1) goto read_fail + +static int read_header(FILE *fp, struct tga_header *header) { + TGA_READ(fp, header->id_length); + TGA_READ(fp, header->color_map_type); + TGA_READ(fp, header->image_type); + TGA_READ(fp, header->colormap_info.offset); + TGA_READ(fp, header->colormap_info.length); + TGA_READ(fp, header->colormap_info.bpp); + TGA_READ(fp, header->image_info.x_origin); + TGA_READ(fp, header->image_info.y_origin); + TGA_READ(fp, header->image_info.width); + TGA_READ(fp, header->image_info.height); + TGA_READ(fp, header->image_info.bpp); + TGA_READ(fp, header->image_info.descriptor); + + return 0; + +read_fail: + return -1; +} + +unsigned char *load_tga(const char *filename, struct tga_header *header) { + unsigned char *image = NULL; + FILE *fp = fopen(filename, "r"); + if (fp) { + if(read_header(fp, header) == 0) { + if (header->image_type == tga_type_true_color && + (header->image_info.bpp == 24 || + header->image_info.bpp == 32)) { + int buflen = header->image_info.width * + header->image_info.height * (header->image_info.bpp / 8); + image = malloc(buflen); + if (image) { + if (header->id_length) + fseek(fp, SEEK_CUR, header->id_length); + + if (fread(image, 1, buflen, fp) != buflen) { + free(image); + image = NULL; + } + } + } + } + fclose(fp); + } + return image; +} diff --git a/host_applications/linux/apps/raspicam/tga.h b/host_applications/linux/apps/raspicam/tga.h new file mode 100644 index 0000000..eb9a16c --- /dev/null +++ b/host_applications/linux/apps/raspicam/tga.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef TGA_H +#define TGA_H + +#include +#include +#include + +typedef enum { + tga_type_null = 0, + tga_type_color_map = 1, + tga_type_true_color = 2, + tga_type_grayscale = 3, + tga_type_rle_color_map = 9, + tga_type_rle_true_color = 10, + tga_type_rle_grayscale = 11, + +} tga_image_type; + +struct tga_colormap_info { + unsigned short offset; + unsigned short length; + unsigned char bpp; +}; + +struct tga_image_info { + unsigned short x_origin; + unsigned short y_origin; + unsigned short width; + unsigned short height; + unsigned char bpp; + unsigned char descriptor; +}; + +struct tga_header { + unsigned char id_length; + unsigned char color_map_type; + unsigned char image_type; + struct tga_colormap_info colormap_info; + struct tga_image_info image_info; +}; + +int write_tga(FILE* fp, int width, int height, uint8_t *buffer, size_t buffer_size); +unsigned char *load_tga(const char *filename, struct tga_header *header); + +#endif /* TGA_H */ diff --git a/interface/khronos/ext/egl_khr_image_client.c b/interface/khronos/ext/egl_khr_image_client.c index 6c8a5ee..4cb9db9 100644 --- a/interface/khronos/ext/egl_khr_image_client.c +++ b/interface/khronos/ext/egl_khr_image_client.c @@ -81,6 +81,9 @@ EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx || target == EGL_IMAGE_BRCM_RAW_PIXELS #endif || target == EGL_IMAGE_BRCM_MULTIMEDIA + || target == EGL_IMAGE_BRCM_MULTIMEDIA_Y + || target == EGL_IMAGE_BRCM_MULTIMEDIA_U + || target == EGL_IMAGE_BRCM_MULTIMEDIA_V || target == EGL_IMAGE_BRCM_DUPLICATE ) { context = NULL; @@ -241,6 +244,18 @@ EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx buf[0] = (uint32_t)buffer; vcos_log_trace("%s: converting buffer handle %u to EGL_IMAGE_BRCM_MULTIMEDIA", __FUNCTION__, buf[0]); + } else if (target == EGL_IMAGE_BRCM_MULTIMEDIA_Y) { + buf[0] = (uint32_t)buffer; + vcos_log_trace("%s: converting buffer handle %u to EGL_IMAGE_BRCM_MULTIMEDIA_Y", + __FUNCTION__, buf[0]); + } else if (target == EGL_IMAGE_BRCM_MULTIMEDIA_U) { + buf[0] = (uint32_t)buffer; + vcos_log_trace("%s: converting buffer handle %u to EGL_IMAGE_BRCM_MULTIMEDIA_U", + __FUNCTION__, buf[0]); + } else if (target == EGL_IMAGE_BRCM_MULTIMEDIA_V) { + buf[0] = (uint32_t)buffer; + vcos_log_trace("%s: converting buffer handle %u to EGL_IMAGE_BRCM_MULTIMEDIA_V", + __FUNCTION__, buf[0]); #endif } else { vcos_log_trace("%s:target type %x buffer %p handled on server", __FUNCTION__, target, buffer); diff --git a/interface/khronos/include/EGL/eglext_brcm.h b/interface/khronos/include/EGL/eglext_brcm.h index 65f7478..eb5119f 100644 --- a/interface/khronos/include/EGL/eglext_brcm.h +++ b/interface/khronos/include/EGL/eglext_brcm.h @@ -38,6 +38,13 @@ extern "C" { #define EGL_IMAGE_BRCM_MULTIMEDIA 0x99930B2 #define EGL_IMAGE_BRCM_DUPLICATE 0x99930B3 /* a new EGL Image pointing at the same underlying object */ #define EGL_IMAGE_BRCM_RAW_PIXELS 0x99930B4 /* Raw YUV multimedia pixels */ + +/* Fastpath for creating greyscale textures from a single plane of a + * MMAL opaque buffers. */ +#define EGL_IMAGE_BRCM_MULTIMEDIA_Y 0x99930C0 +#define EGL_IMAGE_BRCM_MULTIMEDIA_U 0x99930C1 +#define EGL_IMAGE_BRCM_MULTIMEDIA_V 0x99930C2 + #ifndef EGL_BRCM_sane_choose_config #define EGL_BRCM_sane_choose_config 1 #endif diff --git a/interface/vctypes/vc_image_types.h b/interface/vctypes/vc_image_types.h index 3072a9a..b0feeb4 100644 --- a/interface/vctypes/vc_image_types.h +++ b/interface/vctypes/vc_image_types.h @@ -112,6 +112,9 @@ typedef enum VC_IMAGE_YUV420SP, /* Y as a plane, then UV byte interleaved in plane with with same pitch, half height */ VC_IMAGE_YUV444PLANAR, /* Y, U, & V planes separately 4:4:4 */ + + VC_IMAGE_TF_U8, /* T-format 8-bit U - same as TF_Y8 buf from U plane */ + VC_IMAGE_TF_V8, /* T-format 8-bit U - same as TF_Y8 buf from V plane */ VC_IMAGE_MAX, //bounds for error checking VC_IMAGE_FORCE_ENUM_16BIT = 0xffff, diff --git a/middleware/khronos/ext/egl_khr_image.h b/middleware/khronos/ext/egl_khr_image.h index d08c211..5a073fe 100644 --- a/middleware/khronos/ext/egl_khr_image.h +++ b/middleware/khronos/ext/egl_khr_image.h @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "interface/khronos/include/EGL/eglext.h" #include "middleware/khronos/egl/egl_server.h" #include "middleware/imageconv/imageconv.h" +#include "vcinclude/vc_image_types.h" typedef struct EGL_IMAGE_T { @@ -56,6 +57,8 @@ typedef struct EGL_IMAGE_T { */ MEM_HANDLE_T src; const IMAGE_CONVERT_CLASS_T *convert; + KHRN_IMAGE_FORMAT_T conv_khrn_format; + VC_IMAGE_TYPE_T conv_vc_format; uint32_t src_updated; uint32_t src_converted; } external;