Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
sebastiankrzyszkowiak
megapixels
Commits
a9d01640
Unverified
Commit
a9d01640
authored
Feb 19, 2021
by
Benjamin Schaaf
Committed by
Martijn Braam
Feb 21, 2021
Browse files
Add bar code scanning using zbar
Fixes #41
parent
8867b41a
Changes
6
Hide whitespace changes
Inline
Side-by-side
main.c
View file @
a9d01640
...
...
@@ -17,6 +17,7 @@
#include
<wordexp.h>
#include
<gtk/gtk.h>
#include
<locale.h>
#include
<zbar.h>
#include
"camera_config.h"
#include
"quickpreview.h"
#include
"io_pipeline.h"
...
...
@@ -44,6 +45,8 @@ static cairo_surface_t *surface = NULL;
static
cairo_surface_t
*
status_surface
=
NULL
;
static
char
last_path
[
260
]
=
""
;
static
MPZBarScanResult
*
zbar_result
=
NULL
;
static
int
burst_length
=
3
;
static
enum
user_control
current_control
;
...
...
@@ -130,6 +133,28 @@ mp_main_update_state(const struct mp_main_state *state)
(
GSourceFunc
)
update_state
,
state_copy
,
free
);
}
static
bool
set_zbar_result
(
MPZBarScanResult
*
result
)
{
if
(
zbar_result
)
{
for
(
uint8_t
i
=
0
;
i
<
zbar_result
->
size
;
++
i
)
{
free
(
zbar_result
->
codes
[
i
].
data
);
}
free
(
zbar_result
);
}
zbar_result
=
result
;
gtk_widget_queue_draw
(
preview
);
return
false
;
}
void
mp_main_set_zbar_result
(
MPZBarScanResult
*
result
)
{
g_main_context_invoke_full
(
g_main_context_default
(),
G_PRIORITY_DEFAULT_IDLE
,
(
GSourceFunc
)
set_zbar_result
,
result
,
NULL
);
}
static
bool
set_preview
(
cairo_surface_t
*
image
)
{
...
...
@@ -148,20 +173,26 @@ mp_main_set_preview(cairo_surface_t *image)
(
GSourceFunc
)
set_preview
,
image
,
NULL
);
}
static
void
transform_centered
(
cairo_t
*
cr
,
uint32_t
dst_width
,
uint32_t
dst_height
,
int
src_width
,
int
src_height
)
{
cairo_translate
(
cr
,
dst_width
/
2
,
dst_height
/
2
);
double
scale
=
MIN
(
dst_width
/
(
double
)
src_width
,
dst_height
/
(
double
)
src_height
);
cairo_scale
(
cr
,
scale
,
scale
);
cairo_translate
(
cr
,
-
src_width
/
2
,
-
src_height
/
2
);
}
void
draw_surface_scaled_centered
(
cairo_t
*
cr
,
uint32_t
dst_width
,
uint32_t
dst_height
,
cairo_surface_t
*
surface
)
{
cairo_save
(
cr
);
cairo_translate
(
cr
,
dst_width
/
2
,
dst_height
/
2
);
int
width
=
cairo_image_surface_get_width
(
surface
);
int
height
=
cairo_image_surface_get_height
(
surface
);
double
scale
=
MIN
(
dst_width
/
(
double
)
width
,
dst_height
/
(
double
)
height
);
cairo_scale
(
cr
,
scale
,
scale
);
cairo_translate
(
cr
,
-
width
/
2
,
-
height
/
2
);
transform_centered
(
cr
,
dst_width
,
dst_height
,
width
,
height
);
cairo_set_source_surface
(
cr
,
surface
,
0
,
0
);
cairo_paint
(
cr
);
...
...
@@ -296,10 +327,39 @@ preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
// Clear preview area with black
cairo_paint
(
cr
);
// Draw camera preview
if
(
surface
)
{
draw_surface_scaled_centered
(
cr
,
preview_width
,
preview_height
,
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_source_rgba
(
cr
,
1
,
1
,
1
,
0
.
5
);
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
);
}
}
cairo_restore
(
cr
);
}
// Draw control overlay
...
...
@@ -360,6 +420,85 @@ on_shutter_clicked(GtkWidget *widget, gpointer user_data)
mp_io_pipeline_capture
();
}
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
=
strncmp
(
code
->
data
,
"http://"
,
7
)
==
0
||
strncmp
(
code
->
data
,
"https://"
,
8
)
==
0
;
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
(
code
->
data
,
NULL
,
&
error
))
{
g_printerr
(
"Could not launch browser: %s
\n
"
,
error
->
message
);
}
case
GTK_RESPONSE_ACCEPT
:
gtk_clipboard_set_text
(
gtk_clipboard_get
(
GDK_SELECTION_PRIMARY
),
code
->
data
,
-
1
);
case
GTK_RESPONSE_CANCEL
:
break
;
}
gtk_widget_destroy
(
dialog
);
}
void
on_preview_tap
(
GtkWidget
*
widget
,
GdkEventButton
*
event
,
gpointer
user_data
)
{
...
...
@@ -399,6 +538,25 @@ on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
return
;
}
// 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
;
}
}
}
// Tapped preview image itself, try focussing
if
(
has_auto_focus_start
)
{
mp_io_pipeline_focus
();
...
...
main.h
View file @
a9d01640
#pragma once
#include
"camera_config.h"
#include
"zbar_pipeline.h"
#include
"gtk/gtk.h"
#define MP_MAIN_THUMB_SIZE 24
...
...
@@ -25,6 +26,8 @@ void mp_main_update_state(const struct mp_main_state *state);
void
mp_main_set_preview
(
cairo_surface_t
*
image
);
void
mp_main_capture_completed
(
cairo_surface_t
*
thumb
,
const
char
*
fname
);
void
mp_main_set_zbar_result
(
MPZBarScanResult
*
result
);
int
remap
(
int
value
,
int
input_min
,
int
input_max
,
int
output_min
,
int
output_max
);
void
draw_surface_scaled_centered
(
cairo_t
*
cr
,
uint32_t
dst_width
,
uint32_t
dst_height
,
...
...
meson.build
View file @
a9d01640
...
...
@@ -2,6 +2,7 @@ project('megapixels', 'c')
gnome = import('gnome')
gtkdep = dependency('gtk+-3.0')
tiff = dependency('libtiff-4')
zbar = dependency('zbar')
threads = dependency('threads')
cc = meson.get_compiler('c')
...
...
@@ -26,7 +27,21 @@ if get_option('tiffcfapattern')
add_global_arguments('-DLIBTIFF_CFA_PATTERN', language: 'c')
endif
executable('megapixels', 'main.c', 'ini.c', 'quickpreview.c', 'camera.c', 'device.c', 'pipeline.c', 'camera_config.c', 'io_pipeline.c', 'process_pipeline.c', 'matrix.c', resources, dependencies : [gtkdep, libm, tiff, threads], install : true)
executable('megapixels',
'main.c',
'ini.c',
'quickpreview.c',
'camera.c',
'device.c',
'pipeline.c',
'camera_config.c',
'io_pipeline.c',
'process_pipeline.c',
'zbar_pipeline.c',
'matrix.c',
resources,
dependencies : [gtkdep, libm, tiff, zbar, threads],
install : true)
install_data(['data/org.postmarketos.Megapixels.desktop'],
install_dir : get_option('datadir') / 'applications')
...
...
process_pipeline.c
View file @
a9d01640
#include
"process_pipeline.h"
#include
"pipeline.h"
#include
"zbar_pipeline.h"
#include
"main.h"
#include
"config.h"
#include
"quickpreview.h"
...
...
@@ -120,12 +121,17 @@ mp_process_pipeline_start()
pipeline
=
mp_pipeline_new
();
mp_pipeline_invoke
(
pipeline
,
setup
,
NULL
,
0
);
mp_zbar_pipeline_start
();
}
void
mp_process_pipeline_stop
()
{
mp_pipeline_free
(
pipeline
);
mp_zbar_pipeline_stop
();
}
static
cairo_surface_t
*
...
...
@@ -160,7 +166,8 @@ process_image_for_preview(const MPImage *image)
cairo_destroy
(
cr
);
}
// Pass processed preview to main
// Pass processed preview to main and zbar
mp_zbar_pipeline_process_image
(
cairo_surface_reference
(
surface
));
mp_main_set_preview
(
surface
);
return
thumb
;
...
...
zbar_pipeline.c
0 → 100644
View file @
a9d01640
#include
"zbar_pipeline.h"
#include
"pipeline.h"
#include
"main.h"
#include
"io_pipeline.h"
#include
<zbar.h>
#include
<assert.h>
static
MPPipeline
*
pipeline
;
static
volatile
int
frames_processed
=
0
;
static
volatile
int
frames_received
=
0
;
static
zbar_image_scanner_t
*
scanner
;
static
void
setup
(
MPPipeline
*
pipeline
,
const
void
*
data
)
{
scanner
=
zbar_image_scanner_create
();
zbar_image_scanner_set_config
(
scanner
,
0
,
ZBAR_CFG_ENABLE
,
1
);
}
void
mp_zbar_pipeline_start
()
{
pipeline
=
mp_pipeline_new
();
mp_pipeline_invoke
(
pipeline
,
setup
,
NULL
,
0
);
}
void
mp_zbar_pipeline_stop
()
{
mp_pipeline_free
(
pipeline
);
}
static
bool
is_3d_code
(
zbar_symbol_type_t
type
)
{
switch
(
type
)
{
case
ZBAR_EAN2
:
case
ZBAR_EAN5
:
case
ZBAR_EAN8
:
case
ZBAR_UPCE
:
case
ZBAR_ISBN10
:
case
ZBAR_UPCA
:
case
ZBAR_EAN13
:
case
ZBAR_ISBN13
:
case
ZBAR_I25
:
case
ZBAR_DATABAR
:
case
ZBAR_DATABAR_EXP
:
case
ZBAR_CODABAR
:
case
ZBAR_CODE39
:
case
ZBAR_CODE93
:
case
ZBAR_CODE128
:
return
false
;
case
ZBAR_COMPOSITE
:
case
ZBAR_PDF417
:
case
ZBAR_QRCODE
:
case
ZBAR_SQCODE
:
return
true
;
default:
return
false
;
}
}
static
MPZBarCode
process_symbol
(
const
zbar_symbol_t
*
symbol
)
{
MPZBarCode
code
;
unsigned
loc_size
=
zbar_symbol_get_loc_size
(
symbol
);
assert
(
loc_size
>
0
);
zbar_symbol_type_t
type
=
zbar_symbol_get_type
(
symbol
);
if
(
is_3d_code
(
type
)
&&
loc_size
==
4
)
{
for
(
unsigned
i
=
0
;
i
<
loc_size
;
++
i
)
{
code
.
bounds_x
[
i
]
=
zbar_symbol_get_loc_x
(
symbol
,
i
);
code
.
bounds_y
[
i
]
=
zbar_symbol_get_loc_y
(
symbol
,
i
);
}
}
else
{
int
min_x
=
zbar_symbol_get_loc_x
(
symbol
,
0
);
int
min_y
=
zbar_symbol_get_loc_y
(
symbol
,
0
);
int
max_x
=
min_x
,
max_y
=
min_y
;
for
(
unsigned
i
=
1
;
i
<
loc_size
;
++
i
)
{
int
x
=
zbar_symbol_get_loc_x
(
symbol
,
i
);
int
y
=
zbar_symbol_get_loc_y
(
symbol
,
i
);
min_x
=
MIN
(
min_x
,
x
);
min_y
=
MIN
(
min_y
,
y
);
max_x
=
MAX
(
max_x
,
x
);
max_y
=
MAX
(
max_y
,
y
);
}
code
.
bounds_x
[
0
]
=
min_x
;
code
.
bounds_y
[
0
]
=
min_y
;
code
.
bounds_x
[
1
]
=
max_x
;
code
.
bounds_y
[
1
]
=
min_y
;
code
.
bounds_x
[
2
]
=
max_x
;
code
.
bounds_y
[
2
]
=
max_y
;
code
.
bounds_x
[
3
]
=
min_x
;
code
.
bounds_y
[
3
]
=
max_y
;
}
const
char
*
data
=
zbar_symbol_get_data
(
symbol
);
unsigned
int
data_size
=
zbar_symbol_get_data_length
(
symbol
);
code
.
data
=
strndup
(
data
,
data_size
);
code
.
type
=
zbar_get_symbol_name
(
type
);
return
code
;
}
static
void
process_surface
(
MPPipeline
*
pipeline
,
cairo_surface_t
**
_surface
)
{
cairo_surface_t
*
surface
=
*
_surface
;
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
);
// 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
;
}
// Create image for zbar
zbar_image_t
*
zbar_image
=
zbar_image_create
();
zbar_image_set_format
(
zbar_image
,
zbar_fourcc
(
'Y'
,
'8'
,
'0'
,
'0'
));
zbar_image_set_size
(
zbar_image
,
width
,
height
);
zbar_image_set_data
(
zbar_image
,
data
,
width
*
height
*
sizeof
(
uint8_t
),
zbar_image_free_data
);
int
res
=
zbar_scan_image
(
scanner
,
zbar_image
);
assert
(
res
>=
0
);
if
(
res
>
0
)
{
MPZBarScanResult
*
result
=
malloc
(
sizeof
(
MPZBarScanResult
));
result
->
size
=
res
;
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
);
symbol
=
zbar_symbol_next
(
symbol
);
}
mp_main_set_zbar_result
(
result
);
}
else
{
mp_main_set_zbar_result
(
NULL
);
}
zbar_image_destroy
(
zbar_image
);
cairo_surface_destroy
(
surface
);
++
frames_processed
;
}
void
mp_zbar_pipeline_process_image
(
cairo_surface_t
*
surface
)
{
// If we haven't processed the previous frame yet, drop this one
if
(
frames_received
!=
frames_processed
)
{
return
;
}
++
frames_received
;
mp_pipeline_invoke
(
pipeline
,
(
MPPipelineCallback
)
process_surface
,
&
surface
,
sizeof
(
cairo_surface_t
*
));
}
zbar_pipeline.h
0 → 100644
View file @
a9d01640
#pragma once
#include
"camera_config.h"
typedef
struct
_cairo_surface
cairo_surface_t
;
typedef
struct
{
int
bounds_x
[
4
];
int
bounds_y
[
4
];
char
*
data
;
const
char
*
type
;
}
MPZBarCode
;
typedef
struct
{
MPZBarCode
codes
[
8
];
uint8_t
size
;
}
MPZBarScanResult
;
void
mp_zbar_pipeline_start
();
void
mp_zbar_pipeline_stop
();
void
mp_zbar_pipeline_process_image
(
cairo_surface_t
*
surface
);
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment