Browse Source

Get zbar working again

Benjamin Schaaf 3 years ago
parent
commit
44fc390b99
8 changed files with 335 additions and 260 deletions
  1. 1 3
      data/blit.frag
  2. 2 0
      data/org.postmarketos.Megapixels.gresource.xml
  3. 9 0
      data/solid.frag
  4. 9 0
      data/solid.vert
  5. 192 238
      src/main.c
  6. 4 1
      src/process_pipeline.c
  7. 107 16
      src/zbar_pipeline.c
  8. 11 2
      src/zbar_pipeline.h

+ 1 - 3
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);
 }

+ 2 - 0
data/org.postmarketos.Megapixels.gresource.xml

@@ -11,6 +11,8 @@
     <file preprocess="xml-stripblanks">shutter-symbolic.svg</file>
     <file>blit.vert</file>
     <file>blit.frag</file>
+    <file>solid.vert</file>
+    <file>solid.frag</file>
     <file>debayer.vert</file>
     <file>debayer.frag</file>
   </gresource>

+ 9 - 0
data/solid.frag

@@ -0,0 +1,9 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+uniform vec4 color;
+
+void main() {
+	gl_FragColor = color;
+}

+ 9 - 0
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);
+}

+ 192 - 238
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,51 +335,57 @@ preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
 		gl_util_draw_quad(quad);
 	}
 
-	/*
-	// Clear preview area with black
-	cairo_paint(cr);
+	if (zbar_result) {
+		GLuint buffer;
+		if (!gtk_gl_area_get_use_es(area)) {
+			glGenBuffers(1, &buffer);
+			glBindBuffer(GL_ARRAY_BUFFER, buffer);
+			check_gl();
+		}
 
-	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);
+		glUseProgram(solid_program);
+		glEnable(GL_BLEND);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+		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();
 		}
 
-		cairo_restore(cr);
+		glDisable(GL_BLEND);
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
 	}
 
-	// Draw control overlay
-	cairo_set_source_surface(cr, status_surface, 0, 0);
-	cairo_paint(cr);
-	*/
-
 	glFlush();
 
 #ifdef RENDERDOC
@@ -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;
-
-// 	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;
-// }
-
-// 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);
-
-// 	char* data = strdup(code->data);
-
-// 	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);
-
-// 	int result = gtk_dialog_run(GTK_DIALOG(dialog));
-
-// 	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);
-// }
+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;
+	}
 
-void
+	return right && left && top && bottom;
+}
+
+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);
+	}
+
+	g_free(data);
+	gtk_window_destroy(GTK_WINDOW(dialog));
+}
+
+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);
+
+	char* data = strdup(code->data);
+
+	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);
+
+	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;
-
-	// 	for (uint8_t i = 0; i < zbar_result->size; ++i) {
-	// 		MPZBarCode *code = &zbar_result->codes[i];
-
-	// 		if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) {
-	// 			on_zbar_code_tapped(widget, code);
-	// 			return;
-	// 		}
-	// 	}
-	// }
+	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);
+
+		double scale = preview_buffer_height / size_y;
+		int zbar_x = (x - offset_x) * scale;
+		int zbar_y = (y - offset_y) * scale;
+
+		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);
 

+ 4 - 1
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) {

+ 107 - 16
src/zbar_pipeline.c

@@ -6,6 +6,17 @@
 #include <zbar.h>
 #include <assert.h>
 
+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 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 zbar_symbol_t *symbol)
+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);
+	}
 }

+ 11 - 2
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);