diff --git a/data/blit.frag b/data/blit.frag index 1f1b314..58df011 100644 --- a/data/blit.frag +++ b/data/blit.frag @@ -6,8 +6,6 @@ uniform sampler2D texture; varying vec2 uv; -#define fetch(p) texture2D(texture, p).r - void main() { - gl_FragColor = texture2D(texture, uv); + gl_FragColor = vec4(texture2D(texture, uv).rgb, 1); } diff --git a/data/org.postmarketos.Megapixels.gresource.xml b/data/org.postmarketos.Megapixels.gresource.xml index c6b7076..0243024 100644 --- a/data/org.postmarketos.Megapixels.gresource.xml +++ b/data/org.postmarketos.Megapixels.gresource.xml @@ -11,6 +11,8 @@ shutter-symbolic.svg blit.vert blit.frag + solid.vert + solid.frag debayer.vert debayer.frag diff --git a/data/solid.frag b/data/solid.frag new file mode 100644 index 0000000..bb167f7 --- /dev/null +++ b/data/solid.frag @@ -0,0 +1,9 @@ +#ifdef GL_ES +precision mediump float; +#endif + +uniform vec4 color; + +void main() { + gl_FragColor = color; +} diff --git a/data/solid.vert b/data/solid.vert new file mode 100644 index 0000000..f578403 --- /dev/null +++ b/data/solid.vert @@ -0,0 +1,9 @@ +#ifdef GL_ES +precision mediump float; +#endif + +attribute vec2 vert; + +void main() { + gl_Position = vec4(vert, 0, 1); +} diff --git a/src/main.c b/src/main.c index 76aff6a..aeac5a0 100644 --- a/src/main.c +++ b/src/main.c @@ -54,7 +54,6 @@ static MPProcessPipelineBuffer *current_preview_buffer = NULL; static int preview_buffer_width = -1; static int preview_buffer_height = -1; -static cairo_surface_t *status_surface = NULL; static char last_path[260] = ""; static MPZBarScanResult *zbar_result = NULL; @@ -67,10 +66,7 @@ GtkWidget *main_stack; GtkWidget *open_last_stack; GtkWidget *thumb_last; GtkWidget *process_spinner; -GtkWidget *control_box; -GtkWidget *control_name; -GtkAdjustment *control_slider; -GtkWidget *control_auto; +GtkWidget *scanned_codes; int remap(int value, int input_min, int input_max, int output_min, int output_max) @@ -241,95 +237,10 @@ mp_main_capture_completed(cairo_surface_t *thumb, const char *fname) (GSourceFunc)capture_completed, args, free); } -static void -draw_controls() -{ - // cairo_t *cr; - char iso[6]; - int temp; - char shutterangle[6]; - - if (exposure_is_manual) { - temp = (int)((float)exposure / (float)camera->capture_mode.height * - 360); - sprintf(shutterangle, "%d\u00b0", temp); - } else { - sprintf(shutterangle, "auto"); - } - - if (gain_is_manual) { - temp = remap(gain - 1, 0, gain_max, camera->iso_min, - camera->iso_max); - sprintf(iso, "%d", temp); - } else { - sprintf(iso, "auto"); - } - - if (status_surface) - cairo_surface_destroy(status_surface); - - // Make a service to show status of controls, 32px high - // if (gtk_widget_get_window(preview) == NULL) { - // return; - // } - // status_surface = - // gdk_window_create_similar_surface(gtk_widget_get_window(preview), - // CAIRO_CONTENT_COLOR_ALPHA, - // preview_width, 32); - - // cr = cairo_create(status_surface); - // cairo_set_source_rgba(cr, 0, 0, 0, 0.0); - // cairo_paint(cr); - - // // Draw the outlines for the headings - // cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, - // CAIRO_FONT_WEIGHT_BOLD); - // cairo_set_font_size(cr, 9); - // cairo_set_source_rgba(cr, 0, 0, 0, 1); - - // cairo_move_to(cr, 16, 16); - // cairo_text_path(cr, "ISO"); - // cairo_stroke(cr); - - // cairo_move_to(cr, 60, 16); - // cairo_text_path(cr, "Shutter"); - // cairo_stroke(cr); - - // // Draw the fill for the headings - // cairo_set_source_rgba(cr, 1, 1, 1, 1); - // cairo_move_to(cr, 16, 16); - // cairo_show_text(cr, "ISO"); - // cairo_move_to(cr, 60, 16); - // cairo_show_text(cr, "Shutter"); - - // // Draw the outlines for the values - // cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, - // CAIRO_FONT_WEIGHT_NORMAL); - // cairo_set_font_size(cr, 11); - // cairo_set_source_rgba(cr, 0, 0, 0, 1); - - // cairo_move_to(cr, 16, 26); - // cairo_text_path(cr, iso); - // cairo_stroke(cr); - - // cairo_move_to(cr, 60, 26); - // cairo_text_path(cr, shutterangle); - // cairo_stroke(cr); - - // // Draw the fill for the values - // cairo_set_source_rgba(cr, 1, 1, 1, 1); - // cairo_move_to(cr, 16, 26); - // cairo_show_text(cr, iso); - // cairo_move_to(cr, 60, 26); - // cairo_show_text(cr, shutterangle); - - // cairo_destroy(cr); - - // gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32); -} - static GLuint blit_program; static GLuint blit_uniform_texture; +static GLuint solid_program; +static GLuint solid_uniform_color; static GLuint quad; static void @@ -361,9 +272,32 @@ preview_realize(GtkGLArea *area) blit_uniform_texture = glGetUniformLocation(blit_program, "texture"); + GLuint solid_shaders[] = { + gl_util_load_shader("/org/postmarketos/Megapixels/solid.vert", GL_VERTEX_SHADER, NULL, 0), + gl_util_load_shader("/org/postmarketos/Megapixels/solid.frag", GL_FRAGMENT_SHADER, NULL, 0), + }; + + solid_program = gl_util_link_program(solid_shaders, 2); + glBindAttribLocation(solid_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert"); + check_gl(); + + solid_uniform_color = glGetUniformLocation(solid_program, "color"); + quad = gl_util_new_quad(); } +static void +position_preview(float *offset_x, float *offset_y, float *size_x, float *size_y) +{ + double ratio = preview_buffer_height / (double)preview_buffer_width; + + *offset_x = 0; + *offset_y = 0; + *size_x = preview_width; + *size_y = preview_width * ratio; + +} + static gboolean preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) { @@ -382,11 +316,12 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); - double ratio = preview_buffer_height / (double)preview_buffer_width; - glViewport(0, - preview_height - preview_width * ratio, - preview_width, - preview_width * ratio); + float offset_x, offset_y, size_x, size_y; + position_preview(&offset_x, &offset_y, &size_x, &size_y); + glViewport(offset_x, + preview_height - size_y - offset_y, + size_x, + size_y); if (current_preview_buffer) { glUseProgram(blit_program); @@ -400,50 +335,56 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data) gl_util_draw_quad(quad); } - /* - // Clear preview area with black - cairo_paint(cr); - - if (surface) { - // Draw camera preview - cairo_save(cr); - - int width = cairo_image_surface_get_width(surface); - int height = cairo_image_surface_get_height(surface); - transform_centered(cr, preview_width, preview_height, width, height); - - cairo_set_source_surface(cr, surface, 0, 0); - cairo_paint(cr); - - // Draw zbar image - if (zbar_result) { - for (uint8_t i = 0; i < zbar_result->size; ++i) { - MPZBarCode *code = &zbar_result->codes[i]; - - cairo_set_line_width(cr, 3.0); - cairo_set_source_rgba(cr, 0, 0.5, 1, 0.75); - cairo_new_path(cr); - cairo_move_to(cr, code->bounds_x[0], code->bounds_y[0]); - for (uint8_t i = 0; i < 4; ++i) { - cairo_line_to(cr, code->bounds_x[i], code->bounds_y[i]); - } - cairo_close_path(cr); - cairo_stroke(cr); - - cairo_save(cr); - cairo_translate(cr, code->bounds_x[0], code->bounds_y[0]); - cairo_show_text(cr, code->data); - cairo_restore(cr); - } + if (zbar_result) { + GLuint buffer; + if (!gtk_gl_area_get_use_es(area)) { + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + check_gl(); } - cairo_restore(cr); - } + glUseProgram(solid_program); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // Draw control overlay - cairo_set_source_surface(cr, status_surface, 0, 0); - cairo_paint(cr); - */ + glUniform4f(solid_uniform_color, 1, 0, 0, 0.5); + + for (uint8_t i = 0; i < zbar_result->size; ++i) { + MPZBarCode *code = &zbar_result->codes[i]; + + GLfloat vertices[] = { + code->bounds_x[0], code->bounds_y[0], + code->bounds_x[1], code->bounds_y[1], + code->bounds_x[3], code->bounds_y[3], + code->bounds_x[2], code->bounds_y[2], + }; + + for (int i = 0; i < 4; ++i) { + vertices[i * 2] = 2 * vertices[i * 2] / preview_buffer_width - 1.0; + vertices[i * 2 + 1] = 1.0 - 2 * vertices[i * 2 + 1] / preview_buffer_height; + } + + if (gtk_gl_area_get_use_es(area)) { + glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, vertices); + check_gl(); + glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE); + check_gl(); + } else { + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + check_gl(); + + glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE); + check_gl(); + } + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + check_gl(); + } + + glDisable(GL_BLEND); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } glFlush(); @@ -463,8 +404,6 @@ preview_resize(GtkWidget *widget, int width, int height, gpointer data) update_io_pipeline(); } - draw_controls(); - return TRUE; } @@ -508,110 +447,124 @@ run_quit_action(GSimpleAction *action, GVariant *param, GApplication *app) g_application_quit(app); } -// static bool -// check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y) -// { -// bool right = false, left = false, top = false, bottom = false; +static bool +check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y) +{ + bool right = false, left = false, top = false, bottom = false; -// for (int i = 0; i < 4; ++i) { -// if (x <= bounds_x[i]) -// left = true; -// if (x >= bounds_x[i]) -// right = true; -// if (y <= bounds_y[i]) -// top = true; -// if (y >= bounds_y[i]) -// bottom = true; -// } + for (int i = 0; i < 4; ++i) { + if (x <= bounds_x[i]) + left = true; + if (x >= bounds_x[i]) + right = true; + if (y <= bounds_y[i]) + top = true; + if (y >= bounds_y[i]) + bottom = true; + } -// return right && left && top && bottom; -// } + return right && left && top && bottom; +} -// static void -// on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code) -// { -// GtkWidget *dialog; -// GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT; -// bool data_is_url = g_uri_is_valid( -// code->data, G_URI_FLAGS_PARSE_RELAXED, NULL); +static void +on_zbar_dialog_response(GtkDialog *dialog, int response, char *data) +{ + GError *error = NULL; + switch (response) { + case GTK_RESPONSE_YES: + if (!g_app_info_launch_default_for_uri(data, + NULL, &error)) { + g_printerr("Could not launch application: %s\n", + error->message); + } + case GTK_RESPONSE_ACCEPT: + { + GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(dialog)); + gdk_clipboard_set_text( + gdk_display_get_primary_clipboard(display), + data); + } + case GTK_RESPONSE_CANCEL: + break; + default: + g_printerr("Wrong dialog response: %d\n", response); + } -// char* data = strdup(code->data); + g_free(data); + gtk_window_destroy(GTK_WINDOW(dialog)); +} -// if (data_is_url) { -// dialog = gtk_message_dialog_new( -// GTK_WINDOW(gtk_widget_get_toplevel(widget)), -// flags, -// GTK_MESSAGE_QUESTION, -// GTK_BUTTONS_NONE, -// "Found a URL '%s' encoded in a %s code.", -// code->data, -// code->type); -// gtk_dialog_add_buttons( -// GTK_DIALOG(dialog), -// "_Open URL", -// GTK_RESPONSE_YES, -// NULL); -// } else { -// dialog = gtk_message_dialog_new( -// GTK_WINDOW(gtk_widget_get_toplevel(widget)), -// flags, -// GTK_MESSAGE_QUESTION, -// GTK_BUTTONS_NONE, -// "Found '%s' encoded in a %s code.", -// code->data, -// code->type); -// } -// gtk_dialog_add_buttons( -// GTK_DIALOG(dialog), -// "_Copy", -// GTK_RESPONSE_ACCEPT, -// "_Cancel", -// GTK_RESPONSE_CANCEL, -// NULL); +static void +on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code) +{ + GtkWidget *dialog; + GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT; + bool data_is_url = g_uri_is_valid( + code->data, G_URI_FLAGS_PARSE_RELAXED, NULL); -// int result = gtk_dialog_run(GTK_DIALOG(dialog)); + char* data = strdup(code->data); -// GError *error = NULL; -// switch (result) { -// case GTK_RESPONSE_YES: -// if (!g_app_info_launch_default_for_uri(data, -// NULL, &error)) { -// g_printerr("Could not launch application: %s\n", -// error->message); -// } -// case GTK_RESPONSE_ACCEPT: -// gtk_clipboard_set_text( -// gtk_clipboard_get(GDK_SELECTION_PRIMARY), -// data, -1); -// case GTK_RESPONSE_CANCEL: -// break; -// default: -// g_printerr("Wrong dialog result: %d\n", result); -// } -// gtk_widget_destroy(dialog); -// } + if (data_is_url) { + dialog = gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_root(widget)), + flags, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + "Found a URL '%s' encoded in a %s code.", + code->data, + code->type); + gtk_dialog_add_buttons( + GTK_DIALOG(dialog), + "_Open URL", + GTK_RESPONSE_YES, + NULL); + } else { + dialog = gtk_message_dialog_new( + GTK_WINDOW(gtk_widget_get_root(widget)), + flags, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + "Found '%s' encoded in a %s code.", + code->data, + code->type); + } + gtk_dialog_add_buttons( + GTK_DIALOG(dialog), + "_Copy", + GTK_RESPONSE_ACCEPT, + "_Cancel", + GTK_RESPONSE_CANCEL, + NULL); -void + g_signal_connect(dialog, "response", G_CALLBACK(on_zbar_dialog_response), data); + + gtk_widget_show(GTK_WIDGET(dialog)); +} + +static void preview_pressed(GtkGestureClick *gesture, int n_press, double x, double y) { + GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)); + // Tapped zbar result - // if (zbar_result) { - // // Transform the event coordinates to the image - // int width = cairo_image_surface_get_width(surface); - // int height = cairo_image_surface_get_height(surface); - // double scale = MIN(preview_width / (double)width, preview_height / (double)height); - // int x = (event->x - preview_width / 2) / scale + width / 2; - // int y = (event->y - preview_height / 2) / scale + height / 2; + if (zbar_result) { + // Transform the event coordinates to the image + float offset_x, offset_y, size_x, size_y; + position_preview(&offset_x, &offset_y, &size_x, &size_y); - // for (uint8_t i = 0; i < zbar_result->size; ++i) { - // MPZBarCode *code = &zbar_result->codes[i]; + double scale = preview_buffer_height / size_y; + int zbar_x = (x - offset_x) * scale; + int zbar_y = (y - offset_y) * scale; - // if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) { - // on_zbar_code_tapped(widget, code); - // return; - // } - // } - // } + for (uint8_t i = 0; i < zbar_result->size; ++i) { + MPZBarCode *code = &zbar_result->codes[i]; + + if (check_point_inside_bounds(zbar_x, zbar_y, code->bounds_x, code->bounds_y)) { + on_zbar_code_tapped(widget, code); + return; + } + } + } // Tapped preview image itself, try focussing if (has_auto_focus_start) { @@ -886,6 +839,7 @@ activate(GtkApplication *app, gpointer data) open_last_stack = GTK_WIDGET(gtk_builder_get_object(builder, "open_last_stack")); thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last")); process_spinner = GTK_WIDGET(gtk_builder_get_object(builder, "process_spinner")); + scanned_codes = GTK_WIDGET(gtk_builder_get_object(builder, "scanned-codes")); g_signal_connect(window, "realize", G_CALLBACK(on_realize), NULL); diff --git a/src/process_pipeline.c b/src/process_pipeline.c index b9aba03..18ffb80 100644 --- a/src/process_pipeline.c +++ b/src/process_pipeline.c @@ -608,6 +608,9 @@ process_image(MPPipeline *pipeline, const MPBuffer *buffer) memcpy(image, buffer->data, size); mp_io_pipeline_release_buffer(buffer->index); + MPZBarImage *zbar_image = mp_zbar_image_new(image, mode.pixel_format, mode.width, mode.height, camera->rotate, camera->mirrored); + mp_zbar_pipeline_process_image(mp_zbar_image_ref(zbar_image)); + cairo_surface_t *thumb = process_image_for_preview(image); if (captures_remaining > 0) { @@ -626,7 +629,7 @@ process_image(MPPipeline *pipeline, const MPBuffer *buffer) assert(!thumb); } - free(image); + mp_zbar_image_unref(zbar_image); ++frames_processed; if (captures_remaining == 0) { diff --git a/src/zbar_pipeline.c b/src/zbar_pipeline.c index e4c9257..ce86161 100644 --- a/src/zbar_pipeline.c +++ b/src/zbar_pipeline.c @@ -6,6 +6,17 @@ #include #include +struct _MPZBarImage { + uint8_t *data; + MPPixelFormat pixel_format; + int width; + int height; + int rotation; + bool mirrored; + + _Atomic int ref_count; +}; + static MPPipeline *pipeline; static volatile int frames_processed = 0; @@ -33,7 +44,8 @@ mp_zbar_pipeline_stop() mp_pipeline_free(pipeline); } -static bool is_3d_code(zbar_symbol_type_t type) +static bool +is_3d_code(zbar_symbol_type_t type) { switch (type) { case ZBAR_EAN2: @@ -62,9 +74,41 @@ static bool is_3d_code(zbar_symbol_type_t type) } } -static MPZBarCode -process_symbol(const zbar_symbol_t *symbol) +static inline void +map_coords(int *x, int *y, int width, int height, int rotation, bool mirrored) { + int x_r, y_r; + if (rotation == 0) { + x_r = *x; + y_r = *y; + } else if (rotation == 90) { + x_r = *y; + y_r = height - *x - 1; + } else if (rotation == 270) { + x_r = width - *y - 1; + y_r = *x; + } else { + x_r = width - *x - 1; + y_r = height - *y - 1; + } + + if (mirrored) { + x_r = width - x_r - 1; + } + + *x = x_r; + *y = y_r; +} + +static MPZBarCode +process_symbol(const MPZBarImage *image, int width, int height, const zbar_symbol_t *symbol) +{ + if (image->rotation == 90 || image->rotation == 270) { + int tmp = width; + width = height; + height = tmp; + } + MPZBarCode code; unsigned loc_size = zbar_symbol_get_loc_size(symbol); @@ -100,6 +144,10 @@ process_symbol(const zbar_symbol_t *symbol) code.bounds_y[3] = max_y; } + for (uint8_t i = 0; i < 4; ++i) { + map_coords(&code.bounds_x[i], &code.bounds_y[i], width, height, image->rotation, image->mirrored); + } + const char *data = zbar_symbol_get_data(symbol); unsigned int data_size = zbar_symbol_get_data_length(symbol); code.type = zbar_get_symbol_name(type); @@ -110,18 +158,26 @@ process_symbol(const zbar_symbol_t *symbol) } static void -process_surface(MPPipeline *pipeline, cairo_surface_t **_surface) +process_image(MPPipeline *pipeline, MPZBarImage **_image) { - cairo_surface_t *surface = *_surface; + MPZBarImage *image = *_image; - int width = cairo_image_surface_get_width(surface); - int height = cairo_image_surface_get_height(surface); - const uint32_t *surface_data = (const uint32_t *)cairo_image_surface_get_data(surface); + assert(image->pixel_format == MP_PIXEL_FMT_BGGR8 + || image->pixel_format == MP_PIXEL_FMT_GBRG8 + || image->pixel_format == MP_PIXEL_FMT_GRBG8 + || image->pixel_format == MP_PIXEL_FMT_RGGB8); + + // Create a grayscale image for scanning from the current preview. + // Rotate/mirror correctly. + int width = image->width / 2; + int height = image->height / 2; - // Create a grayscale image for scanning from the current preview uint8_t *data = malloc(width * height * sizeof(uint8_t)); - for (size_t i = 0; i < width * height; ++i) { - data[i] = (surface_data[i] >> 16) & 0xff; + size_t i = 0; + for (int y = 0; y < image->height; y += 2) { + for (int x = 0; x < image->width; x += 2) { + data[++i] = image->data[x + image->width * y]; + } } // Create image for zbar @@ -140,7 +196,7 @@ process_surface(MPPipeline *pipeline, cairo_surface_t **_surface) const zbar_symbol_t *symbol = zbar_image_first_symbol(zbar_image); for (int i = 0; i < MIN(res, 8); ++i) { assert(symbol != NULL); - result->codes[i] = process_symbol(symbol); + result->codes[i] = process_symbol(image, width, height, symbol); symbol = zbar_symbol_next(symbol); } @@ -150,22 +206,57 @@ process_surface(MPPipeline *pipeline, cairo_surface_t **_surface) } zbar_image_destroy(zbar_image); - cairo_surface_destroy(surface); + mp_zbar_image_unref(image); ++frames_processed; } void -mp_zbar_pipeline_process_image(cairo_surface_t *surface) +mp_zbar_pipeline_process_image(MPZBarImage *image) { // If we haven't processed the previous frame yet, drop this one if (frames_received != frames_processed) { - cairo_surface_destroy(surface); + mp_zbar_image_unref(image); return; } ++frames_received; - mp_pipeline_invoke(pipeline, (MPPipelineCallback)process_surface, &surface, - sizeof(cairo_surface_t *)); + mp_pipeline_invoke(pipeline, (MPPipelineCallback)process_image, &image, + sizeof(MPZBarImage *)); +} + +MPZBarImage * +mp_zbar_image_new(uint8_t *data, + MPPixelFormat pixel_format, + int width, + int height, + int rotation, + bool mirrored) +{ + MPZBarImage *image = malloc(sizeof(MPZBarImage)); + image->data = data; + image->pixel_format = pixel_format; + image->width = width; + image->height = height; + image->rotation = rotation; + image->mirrored = mirrored; + image->ref_count = 1; + return image; +} + +MPZBarImage * +mp_zbar_image_ref(MPZBarImage *image) +{ + ++image->ref_count; + return image; +} + +void +mp_zbar_image_unref(MPZBarImage *image) +{ + if (--image->ref_count == 0) { + free(image->data); + free(image); + } } diff --git a/src/zbar_pipeline.h b/src/zbar_pipeline.h index 150861a..410e906 100644 --- a/src/zbar_pipeline.h +++ b/src/zbar_pipeline.h @@ -2,7 +2,7 @@ #include "camera_config.h" -typedef struct _cairo_surface cairo_surface_t; +typedef struct _MPZBarImage MPZBarImage; typedef struct { int bounds_x[4]; @@ -19,4 +19,13 @@ typedef struct { void mp_zbar_pipeline_start(); void mp_zbar_pipeline_stop(); -void mp_zbar_pipeline_process_image(cairo_surface_t *surface); +void mp_zbar_pipeline_process_image(MPZBarImage *image); + +MPZBarImage *mp_zbar_image_new(uint8_t *data, + MPPixelFormat pixel_format, + int width, + int height, + int rotation, + bool mirrored); +MPZBarImage *mp_zbar_image_ref(MPZBarImage *image); +void mp_zbar_image_unref(MPZBarImage *image);