From 0d486513f56453f8203c270fcad0b04f8e4f1fee Mon Sep 17 00:00:00 2001 From: Dom Cobley Date: Sun, 10 Nov 2013 17:47:38 +0000 Subject: [PATCH] RaspiStill: Add option to render preview via GL. Add RaspiTex.c which demonstrates how the camera preview buffers can be bound to EGL Images and rendered via Open GLES. To enable GL rendering add --gl to the command line and select a GL scene * square - A simple rotating square * teapot - The classic teapot demo * mirror - OpenGL ES 2.X Hall of mirrors example --- .../linux/apps/raspicam/CMakeLists.txt | 4 +- .../linux/apps/raspicam/RaspiStill.c | 86 ++- .../linux/apps/raspicam/RaspiTex.c | 571 ++++++++++++++++++ .../linux/apps/raspicam/RaspiTex.h | 128 ++++ .../linux/apps/raspicam/RaspiTexUtil.c | 343 +++++++++++ .../linux/apps/raspicam/RaspiTexUtil.h | 75 +++ .../apps/raspicam/cube_texture_and_coords.h | 100 +++ .../linux/apps/raspicam/mirror.c | 251 ++++++++ .../linux/apps/raspicam/mirror.h | 37 ++ .../linux/apps/raspicam/models.c | 521 ++++++++++++++++ .../linux/apps/raspicam/models.h | 36 ++ .../linux/apps/raspicam/square.c | 120 ++++ .../linux/apps/raspicam/square.h | 37 ++ .../linux/apps/raspicam/teapot.c | 323 ++++++++++ .../linux/apps/raspicam/teapot.h | 35 ++ interface/khronos/ext/egl_khr_image_client.c | 7 +- .../khronos/ext/gl_oes_egl_image_client.c | 1 - middleware/imageconv/imageconv.h | 153 ++++- middleware/khronos/ext/egl_khr_image.h | 3 - 19 files changed, 2779 insertions(+), 52 deletions(-) mode change 100755 => 100644 host_applications/linux/apps/raspicam/RaspiStill.c create mode 100644 host_applications/linux/apps/raspicam/RaspiTex.c create mode 100644 host_applications/linux/apps/raspicam/RaspiTex.h create mode 100644 host_applications/linux/apps/raspicam/RaspiTexUtil.c create mode 100644 host_applications/linux/apps/raspicam/RaspiTexUtil.h create mode 100644 host_applications/linux/apps/raspicam/cube_texture_and_coords.h create mode 100644 host_applications/linux/apps/raspicam/mirror.c create mode 100644 host_applications/linux/apps/raspicam/mirror.h create mode 100644 host_applications/linux/apps/raspicam/models.c create mode 100644 host_applications/linux/apps/raspicam/models.h create mode 100644 host_applications/linux/apps/raspicam/square.c create mode 100644 host_applications/linux/apps/raspicam/square.h create mode 100644 host_applications/linux/apps/raspicam/teapot.c create mode 100644 host_applications/linux/apps/raspicam/teapot.h diff --git a/host_applications/linux/apps/raspicam/CMakeLists.txt b/host_applications/linux/apps/raspicam/CMakeLists.txt index 2345154..37aae4c 100644 --- a/host_applications/linux/apps/raspicam/CMakeLists.txt +++ b/host_applications/linux/apps/raspicam/CMakeLists.txt @@ -5,11 +5,11 @@ SET(COMPILE_DEFINITIONS -Werror) include_directories(${CMAKE_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) -add_executable(raspistill RaspiCamControl.c RaspiCLI.c RaspiPreview.c RaspiStill.c) +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) -target_link_libraries(raspistill mmal_core mmal_util mmal_vc_client vcos bcm_host) +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) diff --git a/host_applications/linux/apps/raspicam/RaspiStill.c b/host_applications/linux/apps/raspicam/RaspiStill.c old mode 100755 new mode 100644 index d74baa6..b549a8a --- a/host_applications/linux/apps/raspicam/RaspiStill.c +++ b/host_applications/linux/apps/raspicam/RaspiStill.c @@ -74,6 +74,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "RaspiCamControl.h" #include "RaspiPreview.h" #include "RaspiCLI.h" +#include "RaspiTex.h" #include @@ -132,6 +133,7 @@ typedef struct int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse 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 RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters @@ -144,6 +146,8 @@ typedef struct MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port + RASPITEX_STATE raspitex_state; /// GL renderer state and parameters + } RASPISTILL_STATE; /** Struct used to pass information in encoder port userdata to callback @@ -176,6 +180,7 @@ static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag); #define CommandLink 14 #define CommandKeypress 15 #define CommandSignal 16 +#define CommandGL 17 static COMMAND_LIST cmdline_commands[] = { @@ -196,6 +201,7 @@ static COMMAND_LIST cmdline_commands[] = { CommandFullResPreview,"-fullpreview","fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0}, { 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}, }; static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); @@ -269,12 +275,16 @@ static void default_status(RASPISTILL_STATE *state) state->timelapse = 0; state->fullResPreview = 0; state->frameNextMethod = FRAME_NEXT_SINGLE; + state->useGL = 0; // Setup preview window defaults raspipreview_set_defaults(&state->preview_parameters); // Set up the camera_parameters to default raspicamcontrol_set_defaults(&state->camera_parameters); + + // Set initial GL preview state + raspitex_set_defaults(&state->raspitex_state); } /** @@ -570,6 +580,10 @@ static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) signal(SIGUSR1, signal_handler); break; + case CommandGL: + state->useGL = 1; + break; + default: { // Try parsing for any image specific parameters @@ -582,6 +596,10 @@ static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) if (!parms_used) parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + // Still unused, try GL preview options + if (!parms_used) + parms_used = raspitex_parse_cmdline(&state->raspitex_state, &argv[i][1], second_arg); + // If no parms were used, this must be a bad parameters if (!parms_used) valid = 0; @@ -593,6 +611,17 @@ static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) } } + /* GL preview parameters use preview parameters as defaults unless overriden */ + if (! state->raspitex_state.gl_win_defined) + { + state->raspitex_state.x = state->preview_parameters.previewWindow.x; + state->raspitex_state.y = state->preview_parameters.previewWindow.y; + 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; + if (!valid) { fprintf(stderr, "Invalid command line option (%s)\n", argv[i]); @@ -622,6 +651,9 @@ static void display_valid_parameters(char *app_name) // Now display any help information from the camcontrol code raspicamcontrol_display_help(); + // Now display GL preview help + raspitex_display_help(); + fprintf(stderr, "\n"); return; @@ -714,10 +746,8 @@ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf if (complete) vcos_semaphore_post(&(pData->complete_semaphore)); - } - /** * Create the camera component, set up its ports * @@ -793,7 +823,6 @@ static MMAL_STATUS_T create_camera_component(RASPISTILL_STATE *state) // Now set up the port formats format = preview_port->format; - format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; @@ -824,7 +853,6 @@ static MMAL_STATUS_T create_camera_component(RASPISTILL_STATE *state) } status = mmal_port_format_commit(preview_port); - if (status != MMAL_SUCCESS) { vcos_log_error("camera viewfinder format couldn't be set"); @@ -880,6 +908,16 @@ static MMAL_STATUS_T create_camera_component(RASPISTILL_STATE *state) goto error; } + if (state->useGL) + { + status = raspitex_configure_preview_port(&state->raspitex_state, preview_port); + if (status != MMAL_SUCCESS) + { + fprintf(stderr, "Failed to configure preview port for GL rendering"); + goto error; + } + } + state->camera_component = camera; if (state->verbose) @@ -910,8 +948,6 @@ static void destroy_camera_component(RASPISTILL_STATE *state) } } - - /** * Create the encoder component, set up its ports * @@ -1466,6 +1502,9 @@ int main(int argc, const char **argv) dump_status(&state); } + if (state.useGL) + raspitex_init(&state.raspitex_state); + // OK, we have a nice set of parameters. Now set up our components // We have three components. Camera, Preview and encoder. // Camera and encoder are different in stills/video, but preview @@ -1476,7 +1515,7 @@ int main(int argc, const char **argv) vcos_log_error("%s: Failed to create camera component", __func__); exit_code = EX_SOFTWARE; } - else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + else if ((!state.useGL) && (status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) { vcos_log_error("%s: Failed to create preview component", __func__); destroy_camera_component(&state); @@ -1502,12 +1541,18 @@ int main(int argc, const char **argv) encoder_input_port = state.encoder_component->input[0]; encoder_output_port = state.encoder_component->output[0]; - // Note we are lucky that the preview and null sink components use the same input port - // so we can simple do this without conditionals - preview_input_port = state.preview_parameters.preview_component->input[0]; + if (! state.useGL) + { + if (state.verbose) + fprintf(stderr, "Connecting camera preview port to video render.\n"); - // Connect camera to preview (which might be a null_sink if no preview required) - status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + // Note we are lucky that the preview and null sink components use the same input port + // so we can simple do this without conditionals + preview_input_port = state.preview_parameters.preview_component->input[0]; + + // Connect camera to preview (which might be a null_sink if no preview required) + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + } if (status == MMAL_SUCCESS) { @@ -1533,6 +1578,10 @@ int main(int argc, const char **argv) vcos_assert(vcos_status == VCOS_SUCCESS); + /* If GL preview is requested then start the GL threads */ + if (state.useGL && (raspitex_start(&state.raspitex_state) != 0)) + goto error; + if (status != MMAL_SUCCESS) { vcos_log_error("Failed to setup encoder output"); @@ -1734,13 +1783,22 @@ error: if (state.verbose) fprintf(stderr, "Closing down\n"); + if (state.useGL) + { + raspitex_stop(&state.raspitex_state); + raspitex_destroy(&state.raspitex_state); + } + // Disable all our ports that are not handled by connections check_disable_port(camera_video_port); check_disable_port(encoder_output_port); - mmal_connection_destroy(state.preview_connection); + if (state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + if (state.encoder_connection) + mmal_connection_destroy(state.encoder_connection); - mmal_connection_destroy(state.encoder_connection); /* Disable components */ if (state.encoder_component) diff --git a/host_applications/linux/apps/raspicam/RaspiTex.c b/host_applications/linux/apps/raspicam/RaspiTex.c new file mode 100644 index 0000000..f9c1401 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTex.c @@ -0,0 +1,571 @@ +/* +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 "RaspiCLI.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RaspiTexUtil.h" +#include "interface/vcos/vcos.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" + +#include "mirror.h" +#include "square.h" +#include "teapot.h" + +/** + * \file RaspiTex.c + * + * A simple framework for extending a MMAL application to render buffers via + * OpenGL. + * + * MMAL buffers are often in YUV colour space and in either a planar or + * tile format which is not supported directly by V3D. Instead of copying + * the buffer from the GPU and doing a colour space / pixel format conversion + * the GL_OES_EGL_image_external is used. This allows an EGL image to be + * created from GPU buffer handle (MMAL opaque buffer handle). The EGL image + * may then be used to create a texture (glEGLImageTargetTexture2DOES) and + * drawn by either OpenGL ES 1.0 or 2.0 contexts. + * + * Notes: + * 1) GL_OES_EGL_image_external textures always return pixels in RGBA format. + * This is also the case when used from a fragment shader. + * + * 2) The driver implementation creates a new RGB_565 buffer and does the color + * space conversion from YUV. This happens in GPU memory using the vector + * processor. + * + * 3) Each EGL external image in use will consume GPU memory for the RGB 565 + * buffer. In addition, the GL pipeline might require more than one EGL image + * to be retained in GPU memory until the drawing commands are flushed. + * + * Typically 128 MB of GPU memory is sufficient for 720p viewfinder and 720p + * GL surface. If both the viewfinder and the GL surface are 1080p then + * 256MB of GPU memory is recommended, otherwise, for non-trivial scenes + * the system can run out of GPU memory whilst the camera is running. + * + * 4) It is important to make sure that the MMAL opaque buffer is not returned + * to MMAL before the GL driver has completed the asynchronous call to + * glEGLImageTargetTexture2DOES. Deferring destruction of the EGL image and + * the buffer return to MMAL until after eglSwapBuffers is the recommended. + * + * See also: http://www.khronos.org/registry/gles/extensions/OES/OES_EGL_image_external.txt + */ + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +#define CommandGLScene 1 +#define CommandGLWin 2 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandGLScene, "-glscene", "gs", "GL scene square,teapot,mirror", 1 }, + { CommandGLWin, "-glwin", "gw", "GL window settings <'x,y,w,h'>", 1 }, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspitex_parse_cmdline(RASPITEX_STATE *state, + const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, + cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandGLWin: // Allows a GL window to be different to preview-res + { + int tmp; + tmp = sscanf(arg2, "%d,%d,%d,%d", + &state->x, &state->y, &state->width, &state->height); + if (tmp != 4) + { + // Default to safe size on parse error + state->x = state->y = 0; + state->width = DEFAULT_WIDTH; + state->height = DEFAULT_HEIGHT; + } + else + { + state->gl_win_defined = 1; + } + + used = 2; + break; + } + + case CommandGLScene: // Selects the GL scene + { + if (strcmp(arg2, "square") == 0) + state->scene_id = RASPITEX_SCENE_SQUARE; + else if (strcmp(arg2, "teapot") == 0) + state->scene_id = RASPITEX_SCENE_TEAPOT; + else if (strcmp(arg2, "mirror") == 0) + state->scene_id = RASPITEX_SCENE_MIRROR; + + used = 2; + break; + } + } + return used; +} + +/** + * Display help for command line options + */ +void raspitex_display_help() +{ + fprintf(stderr, "\nPreview parameter commands\n\n"); + raspicli_display_help(cmdline_commands, cmdline_commands_size); +} + +static void update_fps() +{ + static int frame_count = 0; + static long long time_start = 0; + long long time_now; + struct timeval te; + float fps; + + frame_count++; + + gettimeofday(&te, NULL); + time_now = te.tv_sec * 1000LL + te.tv_usec / 1000; + + if (time_start == 0) + { + time_start = time_now; + } + else if (time_now - time_start > 5000) + { + fps = (float) frame_count / ((time_now - time_start) / 1000.0); + frame_count = 0; + time_start = time_now; + vcos_log_info("%3.2f FPS", fps); + } +} + +/** + * Draws the next preview frame. If a new preview buffer is available then the + * preview texture is updated first. + * + * @param state RASPITEX STATE + * @param video_frame MMAL buffer header containing the opaque buffer handle. + * @return Zero if successful. + */ +static int raspitex_draw(RASPITEX_STATE *state, MMAL_BUFFER_HEADER_T *buf) +{ + int rc = 0; + + /* If buf is non-NULL then there is a new viewfinder frame available + * from the camera so the texture should be updated. + * + * Although it's possible to have multiple textures mapped to different + * viewfinder frames this can consume a lot of GPU memory for high-resolution + * viewfinders. + */ + if (buf) + { + /* Update the texture to the new viewfinder image which should */ + rc = state->ops.update_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update texture", VCOS_FUNCTION); + 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); + + state->preview_buf = buf; + } + + /* Do the drawing */ + if (state->preview_egl_image != EGL_NO_IMAGE_KHR) + { + rc = state->ops.update_model(state); + if (rc != 0) + goto end; + + rc = state->ops.redraw(state); + if (rc != 0) + goto end; + + eglSwapBuffers(state->display, state->surface); + update_fps(); + } + +end: + return rc; +} + +/** + * Process preview buffers. + * + * Dequeue each available preview buffer in order and call current redraw + * function. If no new buffers are available then the render function is + * invoked anyway. + * @param state The GL preview window state. + * @return Zero if successful. + */ +static int preview_process_returned_bufs(RASPITEX_STATE* state) +{ + MMAL_BUFFER_HEADER_T *buf; + int new_frame = 0; + int rc = 0; + + while ((buf = mmal_queue_get(state->preview_queue)) != NULL) + { + if (state->preview_stop == 0) + { + new_frame = 1; + rc = raspitex_draw(state, buf); + if (rc != 0) + { + vcos_log_error("%s: Error drawing frame. Stopping.", VCOS_FUNCTION); + state->preview_stop = 1; + return rc; + } + } + } + + /* If there were no new frames then redraw the scene again with the previous + * texture. Otherwise, go round the loop again to see if any new buffers + * are returned. + */ + if (! new_frame) + rc = raspitex_draw(state, NULL); + return rc; +} + +/** Preview worker thread. + * Ensures camera preview is supplied with buffers and sends preview frames to GL. + * @param arg Pointer to state. + * @return NULL always. + */ +static void *preview_worker(void *arg) +{ + RASPITEX_STATE* state = arg; + MMAL_PORT_T *preview_port = state->preview_port; + MMAL_BUFFER_HEADER_T *buf; + MMAL_STATUS_T st; + int rc; + + vcos_log_trace("%s: port %p", VCOS_FUNCTION, preview_port); + + rc = state->ops.create_native_window(state); + if (rc != 0) + goto end; + + rc = state->ops.gl_init(state); + if (rc != 0) + goto end; + + while (state->preview_stop == 0) + { + /* Send empty buffers to camera preview port */ + while ((buf = mmal_queue_get(state->preview_pool->queue)) != NULL) + { + st = mmal_port_send_buffer(preview_port, buf); + if (st != MMAL_SUCCESS) + { + vcos_log_error("Failed to send buffer to %s", preview_port->name); + } + } + /* Process returned buffers */ + if (preview_process_returned_bufs(state) != 0) + { + vcos_log_error("Preview error. Exiting."); + state->preview_stop = 1; + } + } + +end: + /* Make sure all buffers are returned on exit */ + while ((buf = mmal_queue_get(state->preview_queue)) != NULL) + mmal_buffer_header_release(buf); + + /* Tear down GL */ + state->ops.gl_term(state); + vcos_log_trace("Exiting preview worker"); + return NULL; +} + +/** + * MMAL Callback from camera preview output port. + * @param port The camera preview port. + * @param buf The new preview buffer. + **/ +static void preview_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + RASPITEX_STATE *state = (RASPITEX_STATE*) port->userdata; + + if (buf->length == 0) + { + vcos_log_info("%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); + mmal_buffer_header_release(buf); + } + else + { + /* Enqueue the preview frame for rendering and return to + * avoid blocking MMAL core. + */ + mmal_queue_put(state->preview_queue, buf); + } +} + +/* Registers a callback on the camera preview port to receive + * notifications of new frames. + * This must be called before rapitex_start and may not be called again + * without calling raspitex_destroy first. + * + * @param state Pointer to the GL preview state. + * @param port Pointer to the camera preview port + * @return Zero if successful. + */ +int raspitex_configure_preview_port(RASPITEX_STATE *state, + MMAL_PORT_T *preview_port) +{ + MMAL_STATUS_T status; + vcos_log_trace("%s port %p", VCOS_FUNCTION, preview_port); + + /* Enable ZERO_COPY mode on the preview port which instructs MMAL to only + * pass the 4-byte opaque buffer handle instead of the contents of the opaque + * buffer. + * The opaque handle is resolved on VideoCore by the GL driver when the EGL + * image is created. + */ + status = mmal_port_parameter_set_boolean(preview_port, + MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to enable zero copy on camera preview port"); + goto end; + } + + status = mmal_port_format_commit(preview_port); + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto end; + } + + /* For GL a pool of opaque buffer handles must be allocated in the client. + * These buffers are used to create the EGL images. + */ + state->preview_port = preview_port; + preview_port->buffer_num = preview_port->buffer_num_recommended; + preview_port->buffer_size = preview_port->buffer_size_recommended; + + vcos_log_trace("Creating buffer pool for GL renderer num %d size %d", + preview_port->buffer_num, preview_port->buffer_size); + + /* Pool + queue to hold preview frames */ + state->preview_pool = mmal_port_pool_create(preview_port, + preview_port->buffer_num, preview_port->buffer_size); + + if (! state->preview_pool) + { + vcos_log_error("Error allocating pool"); + status = MMAL_ENOMEM; + goto end; + } + + /* Place filled buffers from the preview port in a queue to render */ + state->preview_queue = mmal_queue_create(); + if (! state->preview_queue) + { + vcos_log_error("Error allocating queue"); + status = MMAL_ENOMEM; + goto end; + } + + /* Enable preview port callback */ + preview_port->userdata = (struct MMAL_PORT_USERDATA_T*) state; + status = mmal_port_enable(preview_port, preview_output_cb); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to camera preview port"); + goto end; + } +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) +{ + int rc; + vcos_init(); + + vcos_log_register("RaspiTex", VCOS_LOG_CATEGORY); + vcos_log_set_level(VCOS_LOG_CATEGORY, + state->verbose ? VCOS_LOG_INFO : VCOS_LOG_WARN); + vcos_log_trace("%s", VCOS_FUNCTION); + + switch (state->scene_id) + { + case RASPITEX_SCENE_SQUARE: + rc = square_open(state); + break; + case RASPITEX_SCENE_MIRROR: + rc = mirror_open(state); + break; + case RASPITEX_SCENE_TEAPOT: + rc = teapot_open(state); + break; + default: + rc = -1; + break; + } + if (rc != 0) + return rc; + + return rc; +} + +/* Destroys the pools of buffers used by the GL renderer. + * @param state Pointer to the GL preview state. + */ +void raspitex_destroy(RASPITEX_STATE *state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + if (state->preview_pool) + { + mmal_pool_destroy(state->preview_pool); + state->preview_pool = NULL; + } + + if (state->preview_queue) + { + mmal_queue_destroy(state->preview_queue); + state->preview_queue = NULL; + } + state->ops.destroy_native_window(state); + state->ops.close(state); +} + +/* Initialise the GL / window state to sensible defaults. + * Also initialise any rendering parameters e.g. the scene + * + * @param state Pointer to the GL preview state. + * @return Zero if successful. + */ +void raspitex_set_defaults(RASPITEX_STATE *state) +{ + memset(state, 0, sizeof(*state)); + state->display = EGL_NO_DISPLAY; + state->surface = EGL_NO_SURFACE; + state->context = EGL_NO_CONTEXT; + state->preview_egl_image = EGL_NO_IMAGE_KHR; + state->opacity = 255; + state->width = DEFAULT_WIDTH; + state->height = DEFAULT_HEIGHT; + state->scene_id = RASPITEX_SCENE_SQUARE; + + 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.gl_term = raspitexutil_gl_term; + state->ops.destroy_native_window = raspitexutil_destroy_native_window; + state->ops.close = raspitexutil_close; +} + +/* Stops the rendering loop and destroys MMAL resources + * @param state Pointer to the GL preview state. + */ +void raspitex_stop(RASPITEX_STATE *state) +{ + if (! state->preview_stop) + { + vcos_log_trace("Stopping GL preview"); + state->preview_stop = 1; + vcos_thread_join(&state->preview_thread, NULL); + } +} + +/** + * Starts the worker / GL renderer thread. + * @pre raspitex_init was successful + * @pre raspitex_configure_preview_port was successful + * @param state Pointer to the GL preview state. + * @return Zero on success, otherwise, -1 is returned + * */ +int raspitex_start(RASPITEX_STATE *state) +{ + VCOS_STATUS_T status; + + vcos_log_trace("%s", VCOS_FUNCTION); + status = vcos_thread_create(&state->preview_thread, "preview-worker", + NULL, preview_worker, state); + + if (status != VCOS_SUCCESS) + vcos_log_error("%s: Failed to start worker thread %d", + VCOS_FUNCTION, status); + + return (status == VCOS_SUCCESS ? 0 : -1); +} diff --git a/host_applications/linux/apps/raspicam/RaspiTex.h b/host_applications/linux/apps/raspicam/RaspiTex.h new file mode 100644 index 0000000..89cf31d --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTex.h @@ -0,0 +1,128 @@ +/* +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 RASPITEX_H_ +#define RASPITEX_H_ + +#include +#include +#include +#include +#include "interface/khronos/include/EGL/eglext_brcm.h" +#include "interface/mmal/mmal.h" + +typedef enum { + RASPITEX_SCENE_SQUARE = 0, + RASPITEX_SCENE_MIRROR, + RASPITEX_SCENE_TEAPOT + +} RASPITEX_SCENE_T; + +struct RASPITEX_STATE; + +typedef struct RASPITEX_SCENE_OPS +{ + /// Creates a native window that will be used by egl_init + /// to create a window surface. + int (*create_native_window)(struct RASPITEX_STATE *state); + + /// Creates EGL surface for native window + int (*gl_init)(struct RASPITEX_STATE *state); + + /// Advance to the next animation step + int (*update_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); + + /// Creates EGL surface for native window + void (*gl_term)(struct RASPITEX_STATE *state); + + /// Destroys the native window + void (*destroy_native_window)(struct RASPITEX_STATE *state); + + /// Called when the scene is unloaded + void (*close)(struct RASPITEX_STATE *state); +} RASPITEX_SCENE_OPS; + +/** + * Contains the internal state and configuration for the GL rendered + * preview window. + */ +typedef struct RASPITEX_STATE +{ + 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 + + /* Display rectangle for the native window */ + int32_t x; /// x-offset in pixels + int32_t y; /// y-offset in pixels + int32_t width; /// width in pixels + int32_t height; /// height in pixels + int opacity; /// Alpha value for display element + int gl_win_defined; /// Use rect from --glwin instead of preview + + /* DispmanX info. This might be unused if a custom create_native_window + * does something else. */ + DISPMANX_DISPLAY_HANDLE_T disp; /// Dispmanx display for GL preview + EGL_DISPMANX_WINDOW_T win; /// Dispmanx handle for preview surface + + EGLNativeWindowType* native_window; /// Native window used for EGL surface + EGLDisplay display; /// The current EGL display + EGLSurface surface; /// The current EGL surface + EGLContext context; /// The current EGL context + + 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 + + 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_STATE; + +int raspitex_init(RASPITEX_STATE *state); +void raspitex_destroy(RASPITEX_STATE *state); +int raspitex_start(RASPITEX_STATE *state); +void raspitex_stop(RASPITEX_STATE *state); +void raspitex_set_defaults(RASPITEX_STATE *state); +int raspitex_configure_preview_port(RASPITEX_STATE *state, + MMAL_PORT_T *preview_port); +void raspitex_display_help(); +int raspitex_parse_cmdline(RASPITEX_STATE *state, + const char *arg1, const char *arg2); + +#endif /* RASPITEX_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.c b/host_applications/linux/apps/raspicam/RaspiTexUtil.c new file mode 100644 index 0000000..9f8b43b --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.c @@ -0,0 +1,343 @@ +/* +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 "RaspiTexUtil.h" +#include "RaspiTex.h" +#include + +VCOS_LOG_CAT_T raspitex_log_category; + +/** + * \file RaspiTexUtil.c + * + * Provides default implementations for the raspitex_scene_ops functions + * and general utility functions. + */ + +/** + * Deletes textures and EGL surfaces and context. + * @param raspitex_state Pointer to the Raspi + */ +void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + + /* Delete texture name */ + glDeleteTextures(1, &raspitex_state->texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->preview_egl_image); + raspitex_state->preview_egl_image = EGL_NO_IMAGE_KHR; + + /* Terminate EGL */ + eglMakeCurrent(raspitex_state->display, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(raspitex_state->display, raspitex_state->context); + eglDestroySurface(raspitex_state->display, raspitex_state->surface); + eglTerminate(raspitex_state->display); +} + +/** Creates a native window for the GL surface using dispmanx + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful, otherwise, -1 is returned. + */ +int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state) +{ + VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0}; + VC_RECT_T src_rect = {0}; + VC_RECT_T dest_rect = {0}; + uint32_t disp_num = 0; // Primary + uint32_t layer_num = 0; + DISPMANX_ELEMENT_HANDLE_T elem; + DISPMANX_UPDATE_HANDLE_T update; + + alpha.opacity = raspitex_state->opacity; + dest_rect.x = raspitex_state->x; + dest_rect.y = raspitex_state->y; + 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); + + raspitex_state->disp = vc_dispmanx_display_open(disp_num); + if (raspitex_state->disp == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to open display handle"); + goto error; + } + + update = vc_dispmanx_update_start(0); + if (update == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to open update handle"); + goto error; + } + + elem = vc_dispmanx_element_add(update, raspitex_state->disp, layer_num, + &dest_rect, 0, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, + DISPMANX_NO_ROTATE); + if (elem == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to create element handle"); + goto error; + } + + raspitex_state->win.element = elem; + raspitex_state->win.width = raspitex_state->width; + raspitex_state->win.height = raspitex_state->height; + vc_dispmanx_update_submit_sync(update); + + raspitex_state->native_window = (EGLNativeWindowType*) &raspitex_state->win; + + return 0; +error: + return -1; +} + +/** Destroys the pools of buffers used by the GL renderer. + * @param raspitex_state A pointer to the GL preview state. + */ +void raspitexutil_destroy_native_window(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + if (raspitex_state->disp != DISPMANX_NO_HANDLE) + { + vc_dispmanx_display_close(raspitex_state->disp); + raspitex_state->disp = DISPMANX_NO_HANDLE; + } +} + +/** Creates the EGL context and window surface for the native window + * using specified arguments. + * @param raspitex_state A pointer to the GL preview state. This contains + * the native_window pointer. + * @param attribs The config attributes. + * @param context_attribs The context attributes. + * @return Zero if successful. + */ +static int raspitexutil_gl_common(RASPITEX_STATE *raspitex_state, + const EGLint attribs[], const EGLint context_attribs[]) +{ + EGLConfig config; + EGLint num_configs; + + vcos_log_trace("%s", VCOS_FUNCTION); + + if (raspitex_state->native_window == NULL) + { + vcos_log_error("%s: No native window", VCOS_FUNCTION); + goto error; + } + + raspitex_state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (raspitex_state->display == EGL_NO_DISPLAY) + { + vcos_log_error("%s: Failed to get EGL display", VCOS_FUNCTION); + goto error; + } + + if (! eglInitialize(raspitex_state->display, 0, 0)) + { + vcos_log_error("%s: eglInitialize failed", VCOS_FUNCTION); + goto error; + } + + if (! eglChooseConfig(raspitex_state->display, attribs, &config, + 1, &num_configs)) + { + vcos_log_error("%s: eglChooseConfig failed", VCOS_FUNCTION); + goto error; + } + + raspitex_state->surface = eglCreateWindowSurface(raspitex_state->display, + config, raspitex_state->native_window, NULL); + if (raspitex_state->surface == EGL_NO_SURFACE) + { + vcos_log_error("%s: eglCreateWindowSurface failed", VCOS_FUNCTION); + goto error; + } + + raspitex_state->context = eglCreateContext(raspitex_state->display, + config, EGL_NO_CONTEXT, context_attribs); + if (raspitex_state->context == EGL_NO_CONTEXT) + { + vcos_log_error("%s: eglCreateContext failed", VCOS_FUNCTION); + goto error; + } + + if (!eglMakeCurrent(raspitex_state->display, raspitex_state->surface, + raspitex_state->surface, raspitex_state->context)) + { + vcos_log_error("%s: Failed to activate EGL context", VCOS_FUNCTION); + goto error; + } + + return 0; + +error: + vcos_log_error("%s: EGL error 0x%08x", VCOS_FUNCTION, eglGetError()); + raspitex_state->ops.gl_term(raspitex_state); + return -1; +} + +/** + * Creates an OpenGL ES 1.X context. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state) +{ + int rc; + + const EGLint 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_NONE + }; + + const EGLint context_attribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 1, + EGL_NONE + }; + + vcos_log_trace("%s", VCOS_FUNCTION); + 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)); + +end: + return rc; +} + +/** + * Creates an OpenGL ES 2.X context. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state) +{ + int rc; + const EGLint 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_NONE + }; + + const EGLint context_attribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + 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)); + +end: + return rc; +} + +/** + * Updates the viewfinder texture whenever a new viewfinder MMAL buffer + * is available. + * @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. + * @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; + } + + raspitex_state->preview_egl_image = eglCreateImageKHR(raspitex_state->display, + EGL_NO_CONTEXT, EGL_IMAGE_BRCM_MULTIMEDIA, mm_buf, NULL); + + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture)); + GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, + raspitex_state->preview_egl_image)); + + return 0; +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + * @return Zero. + */ +int raspitexutil_update_model(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; + return 0; +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + * @return Zero. + */ +int raspitexutil_redraw(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; + return 0; +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + */ +void raspitexutil_close(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; +} diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.h b/host_applications/linux/apps/raspicam/RaspiTexUtil.h new file mode 100644 index 0000000..f73666e --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.h @@ -0,0 +1,75 @@ +/* +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 RASPITEX_UTIL_H +#define RASPITEX_UTIL_H + +#define VCOS_LOG_CATEGORY (&raspitex_log_category) +#include +#include +#include +#include +#include +#include +#include "RaspiTex.h" +#include "interface/vcos/vcos.h" + +extern VCOS_LOG_CAT_T raspitex_log_category; + +/* Uncomment to enable extra GL error checking */ +//#define CHECK_GL_ERRORS +#if defined(CHECK_GL_ERRORS) +#define GLCHK(X) \ +do { \ + GLenum err = GL_NO_ERROR; \ + X; \ + while ((err = glGetError())) \ + { \ + vcos_log_trace("GL error 0x%x in " #X "file %s line %d", err, __FILE__,__LINE__); \ + vcos_assert(err == GL_NO_ERROR); \ + exit(err); \ + } \ +} \ +while(0) +#else +#define GLCHK(X) X +#endif /* CHECK_GL_ERRORS */ + +/* Default GL scene ops functions */ +int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state); +int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state); +int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state); +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_update_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +void raspitexutil_close(RASPITEX_STATE* raspitex_state); + +#endif /* RASPITEX_UTIL_H */ diff --git a/host_applications/linux/apps/raspicam/cube_texture_and_coords.h b/host_applications/linux/apps/raspicam/cube_texture_and_coords.h new file mode 100644 index 0000000..663e23b --- /dev/null +++ b/host_applications/linux/apps/raspicam/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +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. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, +}; + diff --git a/host_applications/linux/apps/raspicam/mirror.c b/host_applications/linux/apps/raspicam/mirror.c new file mode 100644 index 0000000..f0732cf --- /dev/null +++ b/host_applications/linux/apps/raspicam/mirror.c @@ -0,0 +1,251 @@ +/* +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/mirror.h b/host_applications/linux/apps/raspicam/mirror.h new file mode 100644 index 0000000..8437728 --- /dev/null +++ b/host_applications/linux/apps/raspicam/mirror.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 MIRROR_H +#define MIRROR_H + +#include "RaspiTex.h" + +int mirror_open(RASPITEX_STATE *state); + +#endif /* MIRROR_H */ diff --git a/host_applications/linux/apps/raspicam/models.c b/host_applications/linux/apps/raspicam/models.c new file mode 100644 index 0000000..434bb2f --- /dev/null +++ b/host_applications/linux/apps/raspicam/models.c @@ -0,0 +1,521 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +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 +#include +#include + +#include "GLES/gl.h" +#include +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "models.h" + +#define VMCS_RESOURCE(a,b) (b) + +/****************************************************************************** +Private typedefs, macros and constants +******************************************************************************/ + +enum {VBO_VERTEX, VBO_NORMAL, VBO_TEXTURE, VBO_MAX}; +#define MAX_MATERIALS 4 +#define MAX_MATERIAL_NAME 32 + +typedef struct wavefront_material_s { + GLuint vbo[VBO_MAX]; + int numverts; + char name[MAX_MATERIAL_NAME]; + GLuint texture; +} WAVEFRONT_MATERIAL_T; + +typedef struct wavefront_model_s { + WAVEFRONT_MATERIAL_T material[MAX_MATERIALS]; + int num_materials; + GLuint texture; +} WAVEFRONT_MODEL_T; + + +/****************************************************************************** +Static Data +******************************************************************************/ + +/****************************************************************************** +Static Function Declarations +******************************************************************************/ + +/****************************************************************************** +Static Function Definitions +******************************************************************************/ + +static void create_vbo(GLenum type, GLuint *vbo, int size, void *data) +{ + glGenBuffers(1, vbo); + vc_assert(*vbo); + glBindBuffer(type, *vbo); + glBufferData(type, size, data, GL_STATIC_DRAW); + glBindBuffer(type, 0); +} + + +static void destroy_vbo(GLuint *vbo) +{ + glDeleteBuffers(1, vbo); + *vbo = 0; +} + +#define MAX_VERTICES 100000 +static void *allocbuffer(int size) +{ + return malloc(size); +} + +static void freebuffer(void *p) +{ + free (p); +} + +static void centre_and_rescale(float *verts, int numvertices) +{ + float cx=0.0f, cy=0.0f, cz=0.0f, scale=0.0f; + float minx=0.0f, miny=0.0f, minz=0.0f; + float maxx=0.0f, maxy=0.0f, maxz=0.0f; + int i; + float *v = verts; + minx = maxx = verts[0]; + miny = maxy = verts[1]; + minz = maxz = verts[2]; + for (i=0; i= 3) *dst++ = src[ind + 2]; + indexes += 3; + } +} + +int draw_wavefront(MODEL_T m, GLuint texture) +{ + int i; + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->texture == -1) continue; + + if (mat->texture) + glBindTexture(GL_TEXTURE_2D, mat->texture); + else + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); + + if (mat->vbo[VBO_VERTEX]) { + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_VERTEX]); + glVertexPointer(3, GL_FLOAT, 0, NULL); + } + if (mat->vbo[VBO_NORMAL]) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_NORMAL]); + glNormalPointer(GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_NORMAL_ARRAY); + } + if (mat->vbo[VBO_TEXTURE]) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_TEXTURE]); + glTexCoordPointer(2, GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + glDrawArrays(GL_TRIANGLES, 0, mat->numverts); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + return 0; +} + +struct wavefront_model_loading_s { + unsigned short material_index[MAX_MATERIALS]; + int num_materials; + int numv, numt, numn, numf; + unsigned int data[0]; +}; + +static int load_wavefront_obj(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + char line[256+1]; + unsigned short pp[54+1]; + FILE *fp; + int i, valid; + float *qv = (float *)m->data; + float *qt = (float *)m->data + 3 * MAX_VERTICES; + float *qn = (float *)m->data + (3+2) * MAX_VERTICES; + unsigned short *qf = (unsigned short *)((float *)m->data + (3+2+3) * MAX_VERTICES); + float *pv = qv, *pt = qt, *pn = qn; + unsigned short *pf = qf; + fp = fopen(modelname, "r"); + if (!fp) return -1; + + m->num_materials = 0; + m->material_index[0] = 0; + + valid = fread(line, 1, sizeof(line)-1, fp); + + while (valid > 0) { + char *s, *end = line; + + while((end-line < valid) && *end != '\n' && *end != '\r') + end++; + *end++ = 0; + + if((end-line < valid) && *end != '\n' && *end != '\r') + *end++ = 0; + + s = line; + + if (s[strlen(s)-1] == 10) s[strlen(s)-1]=0; + switch (s[0]) { + case '#': break; // comment + case '\r': case '\n': case '\0': break; // blank line + case 'm': vc_assert(strncmp(s, "mtllib", sizeof "mtllib"-1)==0); break; + case 'o': break; + case 'u': + if (sscanf(s, "usemtl %s", /*MAX_MATERIAL_NAME-1, */model->material[m->num_materials].name) == 1) { + if (m->num_materials < MAX_MATERIALS) { + if (m->num_materials > 0 && ((pf-qf)/3 == m->material_index[m->num_materials-1] || strcmp(model->material[m->num_materials-1].name, model->material[m->num_materials].name)==0)) { + strcpy(model->material[m->num_materials-1].name, model->material[m->num_materials].name); + m->num_materials--; + } else + m->material_index[m->num_materials] = (pf-qf)/3; + m->num_materials++; + } + } else { printf(s); vc_assert(0); } + break; + case 'g': vc_assert(strncmp(s, "g ", sizeof "g "-1)==0); break; + case 's': vc_assert(strncmp(s, "s ", sizeof "s "-1)==0); break; + case 'v': case 'f': + if (sscanf(s, "v %f %f %f", pv+0, pv+1, pv+2) == 3) { + pv += 3; + } else if (sscanf(s, "vt %f %f", pt+0, pt+1) == 2) { + pt += 2; + } else if (sscanf(s, "vn %f %f %f", pn+0, pn+1, pn+2) == 3) { + pn += 3; + } else if (i = sscanf(s, "f"" %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu", + pp+ 0, pp+ 1, pp+ 2, pp+ 3, pp+ 4, pp+ 5, pp+ 6, pp+ 7, pp+ 8, pp+ 9, pp+10, pp+11, + pp+12, pp+13, pp+14, pp+15, pp+16, pp+17, pp+18, pp+19, pp+20, pp+21, pp+22, pp+23, + pp+24, pp+25, pp+26, pp+27, pp+28, pp+29, pp+30, pp+32, pp+32, pp+33, pp+34, pp+35, pp+36), i >= 6) { + int poly = i/2; + //vc_assert(i < countof(pp)); // may need to increment poly count and pp array + for (i=1; i= 6) { + int poly = i/2; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i= 9) { + int poly = i/3; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i valid ? valid : end-line; + memmove(line, end, valid - i); + valid -= i; + valid += fread(line+valid, 1, sizeof(line)-1-valid, fp); + } + fclose(fp); + + if (m->num_materials==0) m->material_index[m->num_materials++] = 0; + + centre_and_rescale(qv, (pv-qv)/3); + renormalise(qn, (pn-qn)/3); + //centre_and_rescale2(qt, (pt-qt)/2); + + m->numv = pv-qv; + m->numt = pt-qt; + m->numn = pn-qn; + m->numf = pf-qf; + + // compress array + //memcpy((float *)m->data, (float *)m->data, m->numv * sizeof *qv); - nop + memcpy((float *)m->data + m->numv, (float *)m->data + 3 * MAX_VERTICES, m->numt * sizeof *qt); + memcpy((float *)m->data + m->numv + m->numt,(float *) m->data + (3 + 2) * MAX_VERTICES, m->numn * sizeof *qn); + memcpy((float *)m->data + m->numv + m->numt + m->numn, (float *)m->data + (3 + 2 + 3) * MAX_VERTICES, m->numf * sizeof *qf); + + return 0; +} + +static int load_wavefront_dat(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + FILE *fp; + int s; + const int size = sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES; //each face has 9 vertices + + fp = fopen(modelname, "r"); + if (!fp) return -1; + s = fread(m, 1, size, fp); + if (s < 0) return -1; + fclose(fp); + return 0; +} + +MODEL_T load_wavefront(const char *modelname, const char *texturename) +{ + WAVEFRONT_MODEL_T *model; + float *temp, *qv, *qt, *qn; + unsigned short *qf; + int i; + int numverts = 0, offset = 0; + struct wavefront_model_loading_s *m; + int s=-1; + char modelname_obj[128]; + model = malloc(sizeof *model); + if (!model || !modelname) return NULL; + memset (model, 0, sizeof *model); + model->texture = 0; //load_texture(texturename); + m = allocbuffer(sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES); //each face has 9 vertices + if (!m) return 0; + + if (strlen(modelname) + 5 <= sizeof modelname_obj) { + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + s = load_wavefront_dat(modelname_obj, model, m); + } + if (s==0) {} + else if (strncmp(modelname + strlen(modelname) - 4, ".obj", 4) == 0) { + #ifdef DUMP_OBJ_DAT + int size; + FILE *fp; + #endif + s = load_wavefront_obj(modelname, model, m); + #ifdef DUMP_OBJ_DAT + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + size = sizeof *m + + sizeof(float)*(3*m->numv+2*m->numt+3*m->numn) + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*m->numf; //each face has 9 vertices + fp = host_file_open(modelname_obj, "w"); + fwrite(m, 1, size, fp); + fclose(fp); + #endif + } else if (strncmp(modelname + strlen(modelname) - 4, ".dat", 4) == 0) { + s = load_wavefront_dat(modelname, model, m); + } + if (s != 0) return 0; + + qv = (float *)(m->data); + qt = (float *)(m->data + m->numv); + qn = (float *)(m->data + m->numv + m->numt); + qf = (unsigned short *)(m->data + m->numv + m->numt + m->numn); + + numverts = m->numf/3; + vc_assert(numverts <= MAX_VERTICES); + + temp = allocbuffer(3*numverts*sizeof *temp); + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + mat->numverts = i < m->num_materials-1 ? m->material_index[i+1]-m->material_index[i] : numverts - m->material_index[i]; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + offset += mat->numverts; + mat->texture = model->texture; + } + model->num_materials = m->num_materials; + vc_assert(offset == numverts); + freebuffer(temp); + freebuffer(m); + return (MODEL_T)model; +} + +void unload_wavefront(MODEL_T m) +{ + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + int i; + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->vbo[VBO_VERTEX]) + destroy_vbo(mat->vbo+VBO_VERTEX); + if (mat->vbo[VBO_TEXTURE]) + destroy_vbo(mat->vbo+VBO_TEXTURE); + if (mat->vbo[VBO_NORMAL]) + destroy_vbo(mat->vbo+VBO_NORMAL); + } +} + +// create a cube model that looks like a wavefront model, +MODEL_T cube_wavefront(void) +{ + static const float qv[] = { + -0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, -0.5f, + }; + + static const float qn[] = { + 0.0f, -1.0f, -0.0f, + 0.0f, 1.0f, -0.0f, + 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, -0.0f, + 0.0f, 0.0f, -1.0f, + -1.0f, 0.0f, -0.0f, + }; + + static const float qt[] = { + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + }; + + static const unsigned short qf[] = { + 1,1,1, 2,2,1, 3,3,1, + 3,3,1, 4,4,1, 1,1,1, + 5,4,2, 6,1,2, 7,2,2, + 7,2,2, 8,3,2, 5,4,2, + 1,4,3, 4,1,3, 6,2,3, + 6,2,3, 5,3,3, 1,4,3, + 4,4,4, 3,1,4, 7,2,4, + 7,2,4, 6,3,4, 4,4,4, + 3,4,5, 2,1,5, 8,2,5, + 8,2,5, 7,3,5, 3,4,5, + 2,4,6, 1,1,6, 5,2,6, + 5,2,6, 8,3,6, 2,4,6, + }; + WAVEFRONT_MODEL_T *model = malloc(sizeof *model); + if (model) { + WAVEFRONT_MATERIAL_T *mat = model->material; + float *temp; + const int offset = 0; + memset(model, 0, sizeof *model); + + temp = allocbuffer(3*MAX_VERTICES*sizeof *temp); + mat->numverts = countof(qf)/3; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + + freebuffer(temp); + model->num_materials = 1; + } + return (MODEL_T)model; +} + + diff --git a/host_applications/linux/apps/raspicam/models.h b/host_applications/linux/apps/raspicam/models.h new file mode 100644 index 0000000..4a6cbd0 --- /dev/null +++ b/host_applications/linux/apps/raspicam/models.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +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 MODELS_T +#define MODELS_T +typedef struct opqaue_model_s * MODEL_T; + +MODEL_T load_wavefront(const char *modelname, const char *texturename); +MODEL_T cube_wavefront(void); +void unload_wavefront(MODEL_T m); +int draw_wavefront(MODEL_T m, GLuint texture); +#endif diff --git a/host_applications/linux/apps/raspicam/square.c b/host_applications/linux/apps/raspicam/square.c new file mode 100644 index 0000000..dfe2c89 --- /dev/null +++ b/host_applications/linux/apps/raspicam/square.c @@ -0,0 +1,120 @@ +/* +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 +#include +#include +#include +#include "RaspiTexUtil.h" + +/* Vertex co-ordinates: + * + * v0----v1 + * | | + * | | + * | | + * v3----v2 + */ + +static const GLfloat vertices[] = +{ +#define V0 -0.8, 0.8, 0.8, +#define V1 0.8, 0.8, 0.8, +#define V2 0.8, -0.8, 0.8, +#define V3 -0.8, -0.8, 0.8, + V0 V3 V2 V2 V1 V0 +}; + +/* Texture co-ordinates: + * + * (0,0) b--c + * | | + * a--d + * + * b,a,d d,c,b + */ +static const GLfloat tex_coords[] = +{ + 0, 0, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 0 +}; + +static GLfloat angle; +static uint32_t anim_step; + +static int square_init(RASPITEX_STATE *state) +{ + int rc = raspitexutil_gl_init_1_0(state); + + if (rc != 0) + goto end; + + angle = 0.0f; + anim_step = 0; + + glClearColor(0, 0, 0, 0); + glClearDepthf(1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + +end: + return rc; +} + +static int square_update_model(RASPITEX_STATE *state) +{ + int frames_per_rev = 30 * 15; + (void) state; + angle = (anim_step * 360) / (GLfloat) frames_per_rev; + anim_step = (anim_step + 1) % frames_per_rev; + + return 0; +} + +static int square_redraw(RASPITEX_STATE *state) +{ + /* Bind the OES texture which is used to render the camera preview */ + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, state->texture)); + glLoadIdentity(); + glRotatef(angle, 0.0, 0.0, 1.0); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vertices); + glDisableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, tex_coords); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, vcos_countof(tex_coords) / 2)); + return 0; +} + +int square_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = square_init; + state->ops.update_model = square_update_model; + state->ops.redraw = square_redraw; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/square.h b/host_applications/linux/apps/raspicam/square.h new file mode 100644 index 0000000..ef4741a --- /dev/null +++ b/host_applications/linux/apps/raspicam/square.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 SQUARE_H +#define SQUARE_H + +#include "RaspiTex.h" + +int square_open(RASPITEX_STATE *state); + +#endif /* SQUARE_H */ diff --git a/host_applications/linux/apps/raspicam/teapot.c b/host_applications/linux/apps/raspicam/teapot.c new file mode 100644 index 0000000..2c9adda --- /dev/null +++ b/host_applications/linux/apps/raspicam/teapot.c @@ -0,0 +1,323 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +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. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cube_texture_and_coords.h" +#include "models.h" +#include "teapot.h" + +#include "RaspiTex.h" +#include "RaspiTexUtil.h" + +#define PATH "./" + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; + GLuint tex; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; + MODEL_T model; +} TEAPOT_STATE_T; + +static void init_ogl(TEAPOT_STATE_T *state); +static void init_model_proj(TEAPOT_STATE_T *state); +static void reset_model(TEAPOT_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(TEAPOT_STATE_T *state) +{ + // Set background color and clear buffers + glClearColor((0.3922f+7*0.5f)/8, (0.1176f+7*0.5f)/8, (0.5882f+7*0.5f)/8, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glClearDepthf(1.0); + glDepthFunc(GL_LEQUAL); + + float noAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glLightfv(GL_LIGHT0, GL_AMBIENT, noAmbient); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHTING); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(TEAPOT_STATE_T *state) +{ + float nearp = 0.1f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(TEAPOT_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 0.8f*1.5f; +} + +/*********************************************************** + * Name: teapot_update_model + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static int teapot_update_model(RASPITEX_STATE *raspitex_state) +{ + TEAPOT_STATE_T *state = (TEAPOT_STATE_T *) raspitex_state->scene_state; + + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); + + return 0; +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 10.0f) + distance = 10.f; + else if (distance <= 1.0f) + distance = 1.0f; + + return distance; +} + +/*********************************************************** + * Name: teapot_redraw + * + * Arguments: + * RASPITEX_STATE_T *state - holds OGLES model info + * + * Description: Draws the model + * + * Returns: void + * + ***********************************************************/ +static int teapot_redraw(RASPITEX_STATE *raspitex_state) +{ + TEAPOT_STATE_T *state = (TEAPOT_STATE_T *) raspitex_state->scene_state; + + // 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); + draw_wavefront(state->model, raspitex_state->texture); + return 0; +} + +//============================================================================== + +static int teapot_gl_init(RASPITEX_STATE *raspitex_state) +{ + const char *model_path = "/opt/vc/src/hello_pi/hello_teapot/teapot.obj.dat"; + TEAPOT_STATE_T *state = NULL; + int rc = 0; + + // Clear scene state + state = calloc(1, sizeof(TEAPOT_STATE_T)); + raspitex_state->scene_state = state; + state->screen_width = raspitex_state->width; + state->screen_height = raspitex_state->height; + + rc = raspitexutil_gl_init_1_0(raspitex_state); + if (rc != 0) + goto end; + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + state->model = load_wavefront(model_path, NULL); + + if (! state->model) + { + vcos_log_error("Failed to load model from %s\n", model_path); + rc = -1; + } + +end: + return rc; +} + +static void teapot_gl_term(RASPITEX_STATE *state) +{ + raspitexutil_gl_term(state); + free(state->scene_state); + state->scene_state = NULL; +} + +int teapot_open(RASPITEX_STATE *raspitex_state) +{ + raspitex_state->ops.gl_init = teapot_gl_init; + raspitex_state->ops.update_model = teapot_update_model; + raspitex_state->ops.redraw = teapot_redraw; + raspitex_state->ops.gl_term = teapot_gl_term; + return 0; +} + diff --git a/host_applications/linux/apps/raspicam/teapot.h b/host_applications/linux/apps/raspicam/teapot.h new file mode 100644 index 0000000..c8d5162 --- /dev/null +++ b/host_applications/linux/apps/raspicam/teapot.h @@ -0,0 +1,35 @@ +/* +Copyright (c) 2012-2013, Broadcom Europe Ltd +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 TEAPOT_H +#define TEAPOT_H + +#include "RaspiTex.h" + +int teapot_open(RASPITEX_STATE *raspitex_state); + +#endif /* TEAPOT_H */ diff --git a/interface/khronos/ext/egl_khr_image_client.c b/interface/khronos/ext/egl_khr_image_client.c index fb46584..6c8a5ee 100644 --- a/interface/khronos/ext/egl_khr_image_client.c +++ b/interface/khronos/ext/egl_khr_image_client.c @@ -78,9 +78,9 @@ EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx #endif #if EGL_ANDROID_image_native_buffer || target == EGL_NATIVE_BUFFER_ANDROID - || target == EGL_IMAGE_BRCM_MULTIMEDIA || target == EGL_IMAGE_BRCM_RAW_PIXELS #endif + || target == EGL_IMAGE_BRCM_MULTIMEDIA || target == EGL_IMAGE_BRCM_DUPLICATE ) { context = NULL; @@ -236,6 +236,11 @@ EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx vcos_log_error("%s: unknown gralloc resource type %x", __FUNCTION__, res_type); } #endif +#else /* Not Android */ + } else if (target == EGL_IMAGE_BRCM_MULTIMEDIA) { + buf[0] = (uint32_t)buffer; + vcos_log_trace("%s: converting buffer handle %u to EGL_IMAGE_BRCM_MULTIMEDIA", + __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/ext/gl_oes_egl_image_client.c b/interface/khronos/ext/gl_oes_egl_image_client.c index 6251115..f9b7287 100644 --- a/interface/khronos/ext/gl_oes_egl_image_client.c +++ b/interface/khronos/ext/gl_oes_egl_image_client.c @@ -102,7 +102,6 @@ GL_API void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageO } } else { #endif - vcos_log_trace("[%s] target 0x%x image %d", __FUNCTION__, target, (uint32_t)image); RPC_CALL2(glEGLImageTargetTexture2DOES_impl, thread, GLEGLIMAGETARGETTEXTURE2DOES_ID, diff --git a/middleware/imageconv/imageconv.h b/middleware/imageconv/imageconv.h index e1d76b3..f7a9a0e 100644 --- a/middleware/imageconv/imageconv.h +++ b/middleware/imageconv/imageconv.h @@ -69,8 +69,7 @@ typedef struct IMAGE_CONVERT_CLASS_T IMAGE_CONVERT_CLASS_T; * The initial reference count is 1. * @param data Image data. Not all fields are used by all convert classes. 'data' must be * filled in for all classes. - * @param handle On success, this points to the newly created image handle. The actual type - * depends on the convert class, but is probably a MEM_HANDLE_T in most cases. + * @param handle On success, this points to the newly created image handle. * @return 0 on success; -1 on failure. */ typedef int (*IMAGECONV_CREATE)(const IMAGE_CONVERT_CLASS_T *converter, @@ -103,7 +102,9 @@ typedef int (*IMAGECONV_GET_CONVERTED_SIZE)( * * May be NULL if the converter does not support or require this. */ -typedef void (*IMAGECONV_UNGET_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T handle); +typedef void (*IMAGECONV_UNGET_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, + MEM_HANDLE_T src, + IMAGECONV_DRIVER_IMAGE_T *image); /* * Add a reference count to an image, preventing it from being returned to its @@ -111,8 +112,8 @@ typedef void (*IMAGECONV_UNGET_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, ME * * May be NULL if the converter does not support or require this. */ -typedef void (*IMAGECONV_ACQUIRE_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, - IMAGECONV_DRIVER_IMAGE_T *image); +typedef int (*IMAGECONV_ACQUIRE_IMAGE)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); /* * Remove a reference count to an image, releasing it back to its @@ -155,6 +156,42 @@ typedef void (*IMAGECONV_SET_SRC_DESC)(const IMAGE_CONVERT_CLASS_T *converter, M */ typedef const char *(*IMAGECONV_GET_SRC_DESC)(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src); +/* Callback made in response to IMAGECONV_NOTIFY + * + * A status of 0 indicates that a reference to the image can now be acquired + * via imageconv_acquire(_image). + * + * Any other value indicates that the request timed out. + */ +typedef void (*IMAGECONV_NOTIFY_CALLBACK)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image, + void *context, + int status); + +/* Request notification when a reference to the image can be acquired. + * + * May be NULL if the converter does not support or require this. + */ +typedef int (*IMAGECONV_NOTIFY)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image, + uint32_t timeout, + IMAGECONV_NOTIFY_CALLBACK callback, + void *context); + +/* Cancel an outstanding IMAGECONV_NOTIFY request. +*/ +typedef void (*IMAGECONV_CANCEL_NOTIFY)(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); + +typedef enum IMAGECONV_ID_T { + IMAGECONV_ID_MMAL, /**< MMAL opaque buffer to Khronos */ + IMAGECONV_ID_KHRONOS, /**< YV12 to Khronos */ + IMAGECONV_ID_EGL_VC, /**< EGL image to VC_IMAGE_T */ + IMAGECONV_ID_GENERIC, + IMAGECONV_ID_KHRONOS_VC, /**< Khronos to VC_IMAGE_T */ + IMAGECONV_ID_MAX +} IMAGECONV_ID_T; + /* Do not call these directly! */ struct IMAGE_CONVERT_CLASS_T { IMAGECONV_CREATE create; @@ -169,15 +206,18 @@ struct IMAGE_CONVERT_CLASS_T { IMAGECONV_CONVERT convert; IMAGECONV_SET_SRC_DESC set_src_desc; IMAGECONV_GET_SRC_DESC get_src_desc; + IMAGECONV_NOTIFY notify; + IMAGECONV_CANCEL_NOTIFY cancel_notify; + IMAGECONV_ID_T id; }; -typedef enum IMAGECONV_ID_T { - IMAGECONV_ID_MMAL, /**< MMAL opaque buffer to Khronos */ - IMAGECONV_ID_KHRONOS, /**< YV12 to Khronos */ - IMAGECONV_ID_EGL_VC, /**< EGL image to VC_IMAGE_T */ - IMAGECONV_ID_GENERIC, - IMAGECONV_ID_MAX -} IMAGECONV_ID_T; +typedef enum IMAGECONV_ERR_T { + IMAGECONV_ERR_NONE = 0, + IMAGECONV_ERR_GENERAL = -1, + IMAGECONV_ERR_NOT_SUPPORTED = -2, + IMAGECONV_ERR_NOT_READY = -3, + IMAGECONV_ERR_ALREADY = -4, +} IMAGECONV_ERR_T; /** Initialise the library */ @@ -189,7 +229,8 @@ void imageconv_init(void); * @param converter_id type of image to support * @param converter function pointers for this image type * - * @return 0 on success or -1 if image type unknown. + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_NOT_SUPPORTED if image type unknown. */ int imageconv_get_convert_class( IMAGECONV_ID_T converter_id, @@ -200,21 +241,24 @@ int imageconv_get_convert_class( * * @param converter_class type of image to support * @param converter function pointers for this image type - * - * @return 0 on success or -1 if image type unknown. */ -int imageconv_set_convert_class(IMAGECONV_ID_T converter_id, +void imageconv_set_convert_class(IMAGECONV_ID_T converter_id, const IMAGE_CONVERT_CLASS_T *converter); /** * Create an image. + * * The initial reference count is 1. + * * @param converter Convert class. * @param data Image data. Not all fields are used by all convert classes. 'data' must be * filled in for all classes. * @param handle On success, this points to the newly created image handle. The actual type * depends on the convert class, but is probably a MEM_HANDLE_T in most cases. - * @return 0 on success; -1 on failure. + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL on failure */ int imageconv_create(const IMAGE_CONVERT_CLASS_T *converter, const IMAGECONV_IMAGE_DATA_T *data, MEM_HANDLE_T *handle); @@ -228,13 +272,19 @@ int imageconv_create(const IMAGE_CONVERT_CLASS_T *converter, const IMAGECONV_IMA * @param pitch pitch of image in bytes * @param type type of image * - * @return 0 on success or -1 if image handle is invalid. + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid. */ int imageconv_get_size(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T self, uint32_t *width, uint32_t *height, uint32_t *pitch, VC_IMAGE_TYPE_T *type); /** Converts the image writing the result to the memory specified at * dst, dst_offset. + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL on failure */ int imageconv_convert(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T dest, uint32_t dest_offset, @@ -249,7 +299,8 @@ int imageconv_convert(const IMAGE_CONVERT_CLASS_T *converter, * @param src MEM_HANDLE_T to image structure * @param image Return image pointer. * - * @return 0 on success or -1 if image handle is invalid. + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid. */ int imageconv_get(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src, @@ -266,17 +317,22 @@ int imageconv_get(const IMAGE_CONVERT_CLASS_T *converter, * the image storage is not modified; all other fields * will be filled in. * - * @return 0 on success, otherwise an error value is returned. + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid. */ int imageconv_get_converted_size(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src, VC_IMAGE_TYPE_T type, IMAGECONV_IMAGE_DATA_T *dest_info); /** Release an image that was previously taken with imageconv_get(). + * * @param converter converter functions for this image * @param src MEM_HANDLE_T to image structure + * @param image image obtained from imageconv_get */ void imageconv_unget(const IMAGE_CONVERT_CLASS_T *converter, - MEM_HANDLE_T src); + MEM_HANDLE_T src, + IMAGECONV_DRIVER_IMAGE_T *image); /** Acquire a lock on an image to prevent it being recycled into its * image pool. @@ -284,7 +340,10 @@ void imageconv_unget(const IMAGE_CONVERT_CLASS_T *converter, * @param converter converter functions for this image * @param src MEM_HANDLE_T to image structure * - * @return 0 on success or -1 if image handle is invalid. + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid + * @return IMAGECONV_ERR_NOT_READY if reference cannot be currently acquired */ int imageconv_acquire(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src); @@ -296,7 +355,10 @@ int imageconv_acquire(const IMAGE_CONVERT_CLASS_T *converter, * @param converter converter functions for this image * @param image image pointer from imageconv_get * - * @return 0 on success or -1 if image handle is invalid. + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid + * @return IMAGECONV_ERR_NOT_READY if reference cannot be currently acquired */ int imageconv_acquire_image(const IMAGE_CONVERT_CLASS_T *converter, IMAGECONV_DRIVER_IMAGE_T *image); @@ -307,7 +369,9 @@ int imageconv_acquire_image(const IMAGE_CONVERT_CLASS_T *converter, * @param converter converter functions for this image * @param image image pointer from imageconv_get * - * @return 0 on success or -1 if image handle is invalid. + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_NONE on success + * @return IMAGECONV_ERR_GENERAL if image handle is invalid */ int imageconv_release_image(const IMAGE_CONVERT_CLASS_T *converter, IMAGECONV_DRIVER_IMAGE_T *image); @@ -344,14 +408,41 @@ void imageconv_set_src_desc_printf(const IMAGE_CONVERT_CLASS_T *converter, MEM_H */ const char *imageconv_get_src_desc(const IMAGE_CONVERT_CLASS_T *converter, MEM_HANDLE_T src); -int imageconv_get_converted(const IMAGE_CONVERT_CLASS_T *converter, - MEM_HANDLE_T src, - VC_IMAGE_TYPE_T type, - IMAGECONV_IMAGE_DATA_T *image, - MEM_HANDLE_T *h); +/* Request notification when a reference to the image can be acquired. + * + * If the reference can already be acquired at the time when the request is made, + * this is indicated via the return code, and no callback will be made. + * + * Otherwise the callback is made when the reference can be acquired, or when + * the specified time period has elapsed. + * + * The thread context in which callbacks are executed depends upon the converter + * class implementation. The callback function should therefore do as little + * work as possible, deferring to a known thread context for any further + * processing. + * + * @param converter converter functions for this image + * @param image image pointer + * @param timeout time to wait in milliseconds; 0 means wait indefinitely + * @param callback callback to be invoked when image is valid, or timeout expires + * + * @return IMAGECONV_ERR_NOT_SUPPORTED if not implemented by the converter class + * @return IMAGECONV_ERR_ALREADY if reference can be acquired immediately + * @return IMAGECONV_ERR_NONE if reference cannot be acquire immediately; callback will be made + */ +int imageconv_notify(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image, + uint32_t timeout, + IMAGECONV_NOTIFY_CALLBACK callback, + void *context); -int imageconv_request_preconvert(const IMAGE_CONVERT_CLASS_T *converter, - MEM_HANDLE_T src, VC_IMAGE_TYPE_T type); +/* Cancel an outstanding IMAGECONV_NOTIFY request. + * + * @param converter converter functions for this image + * @param image return image pointer + */ +void imageconv_cancel_notify(const IMAGE_CONVERT_CLASS_T *converter, + IMAGECONV_DRIVER_IMAGE_T *image); #endif diff --git a/middleware/khronos/ext/egl_khr_image.h b/middleware/khronos/ext/egl_khr_image.h index 6e66342..d08c211 100644 --- a/middleware/khronos/ext/egl_khr_image.h +++ b/middleware/khronos/ext/egl_khr_image.h @@ -58,9 +58,6 @@ typedef struct EGL_IMAGE_T { const IMAGE_CONVERT_CLASS_T *convert; uint32_t src_updated; uint32_t src_converted; - - /* Indicates whether the external source image has been acquired */ - IMAGECONV_DRIVER_IMAGE_T *acquired_image; } external; } EGL_IMAGE_T;