Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/conan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v3
- name: install conan
uses: turtlebrowser/get-conan@main

- name: create package
run: |
conan create -pr:a conan/windows_release . --build=missing
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Conan
run: python -m pip install "conan>=2.10,<3"
- name: Create package
run: conan create -pr:a conan/windows_release . --build=missing
28 changes: 17 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
cmake_minimum_required(VERSION 3.11)
cmake_minimum_required(VERSION 3.16)
project(uca C)

include(CTest)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

# Prefer a Python 3 interpreter; keep compatibility with older CMake versions.
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.12")
find_package(Python3 COMPONENTS Interpreter REQUIRED)
Expand All @@ -13,7 +19,7 @@ endif()
option(WITH_PYTHON_MULTITHREADING "Enable Python multithreading support" ON)
option(WITH_GIR "Build introspection files" ON)
option(WITH_TIFF "Support loading tiff files" ON)
option(WITH_GUI "Build the GUI tools" ON)
option(WITH_GUI "Build the deprecated GTK 2 GUI tools" OFF)
option(WITH_TOOLS "Build the CLI tools" ON)
option(USE_FIND_PACKAGE_FOR_GLIB "Use find_package instead of pkg-config to find GLib dependencies" OFF)

Expand All @@ -25,10 +31,10 @@ else()
message(STATUS "Using pkg-config for GLib")
find_package(PkgConfig)
find_program(GLIB2_MKENUMS glib-mkenums REQUIRED)
pkg_check_modules(GLIB2 glib-2.0>=2.38 REQUIRED)
pkg_check_modules(GOBJECT2 gobject-2.0>=2.38 REQUIRED)
pkg_check_modules(GMODULE2 gmodule-2.0>=2.38 REQUIRED)
pkg_check_modules(GIO2 gio-2.0>=2.38 REQUIRED)
pkg_check_modules(GLIB2 glib-2.0>=2.56 REQUIRED)
pkg_check_modules(GOBJECT2 gobject-2.0>=2.56 REQUIRED)
pkg_check_modules(GMODULE2 gmodule-2.0>=2.56 REQUIRED)
pkg_check_modules(GIO2 gio-2.0>=2.56 REQUIRED)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(PkgConfigVars)
Expand All @@ -50,7 +56,7 @@ else()
endif()

if (WITH_TIFF)
find_package(TIFF REQUIRED)
find_package(TIFF 4.3 REQUIRED)
set(HAVE_LIBTIFF "1")
endif()

Expand Down Expand Up @@ -118,15 +124,15 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/package.sh.in
#{{{ Common dependencies
find_program(GLIB2_MKENUMS glib-mkenums REQUIRED)

set(GLIB_VERSION_MIN_REQUIRED "GLIB_VERSION_2_38")
set(GLIB_VERSION_MAX_ALLOWED "GLIB_VERSION_2_38")
set(GLIB_VERSION_MIN_REQUIRED "GLIB_VERSION_2_56")
set(GLIB_VERSION_MAX_ALLOWED "GLIB_VERSION_2_56")

link_directories(${GLIB2_LIBRARY_DIRS})
#}}}

#{{{ Common definitions
if (GNU)
add_definitions("-std=c99 -Wall -fPIC")
if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(-Wall -Wextra)
endif()
#}}}
#{{{ Subdirectories
Expand Down
3 changes: 2 additions & 1 deletion bin/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
find_package(PkgConfig)
message(DEPRECATION "The GTK 2 camera-control GUI is deprecated and disabled by default")
find_program(GLIB_COMPILE_RESOURCES glib-compile-resources REQUIRED)

pkg_check_modules(GTK2 gtk+-2.0>=2.22)
pkg_check_modules(GTK2 gtk+-2.0>=2.24)
pkg_check_modules(GTHREAD2 gthread-2.0)

if (GTK2_FOUND)
Expand Down
3 changes: 2 additions & 1 deletion bin/gui/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
cc = meson.get_compiler('c')

libm = cc.find_library('m')
gtk_dep = dependency('gtk+-2.0', required: false)
warning('The GTK 2 camera-control GUI is deprecated and disabled by default')
gtk_dep = dependency('gtk+-2.0', version: '>= 2.24', required: get_option('gui'))
gthread_dep = dependency('gthread-2.0')

if gtk_dep.found()
Expand Down
4 changes: 3 additions & 1 deletion bin/meson.build
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
subdir('gui')
if not get_option('gui').disabled()
subdir('gui')
endif
subdir('tools')
5 changes: 4 additions & 1 deletion bin/tools/CMakeLists.txt

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows building libuca and its command-line tools without TIFF support—for example, for acquisition where images are transferred or processed directly instead of saved as TIFF. This is a general build fix discovered during the work, not a PCO requirement. It can be reviewed and merged separately—or left out of the initial PCO contribution.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ set(BINARIES "benchmark" "gen-doc" "info" "grab")

foreach (BINARY ${BINARIES})
add_executable(uca-${BINARY} ${BINARY}.c common.c)
target_link_libraries(uca-${BINARY} PUBLIC uca TIFF::TIFF)
target_link_libraries(uca-${BINARY} PUBLIC uca)
if (WITH_TIFF)
target_link_libraries(uca-${BINARY} PUBLIC TIFF::TIFF)
endif ()
endforeach ()

if (GNU)
Expand Down
10 changes: 6 additions & 4 deletions bin/tools/grab.c

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rational behind these changes:

If we left the code unchanged, the camera could acquire correct images, but uca-grab could save them in a TIFF file that some programs read incorrectly or reject.

  1. Calculating strip size before setting image dimensions.
    TIFF stores an image in blocks called strips. Choosing their size requires knowing how many bytes one image row occupies. At that point, the old code hadn’t supplied the width or pixel format. Depending on libtiff’s behavior, this could produce warnings or an unsuitable strip size. It does not necessarily corrupt pixels, but relies on fallback behavior.
  2. Setting the page-type tag only for the first image.
    Finishing a TIFF page clears its settings. Consequently, subsequent images weren’t explicitly marked as pages of a multi-page document. Many readers still find them through TIFF’s directory chain, so this does not mean later frames disappear. However, the file is inconsistently described, and software relying on that tag may handle later pages differently.
  3. Not declaring how grayscale values should be displayed.
    The missing tag tells the reader that zero means black and larger values mean brighter pixels. Without it, readers may assume a default, display the image incorrectly, warn about the missing information, or reject it. Adding PHOTOMETRIC_MINISBLACK makes the interpretation explicit.

I asked to find failure scenarious:

Image

So it seems the firs one is serious. And the 2 and 3rd issues which may arise in other programs or apps during reading.

Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,27 @@ write_tiff (UcaRingBuffer *buffer,

tif = TIFFOpen (opts->filename, "w");
n_frames = uca_ring_buffer_get_num_blocks (buffer);
rows_per_strip = TIFFDefaultStripSize (tif, (guint32) - 1);
bytes_per_pixel = get_bytes_per_pixel (bits_per_pixel);
bits_per_sample = bits_per_pixel > 8 ? 16 : 8;

/* Write multi page TIFF file */
TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);

/* Write multi page TIFF file. libtiff needs the image geometry before it
* can calculate a valid default strip size, and TIFFWriteDirectory resets
* the directory fields for the following page. */
for (guint i = 0; i < n_frames; i++) {
gpointer data;
gsize offset = 0;

data = uca_ring_buffer_get_read_pointer (buffer);

TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField (tif, TIFFTAG_IMAGELENGTH, height);
TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
rows_per_strip = TIFFDefaultStripSize (tif, (guint32) -1);
TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rows_per_strip);
TIFFSetField (tif, TIFFTAG_PAGENUMBER, i, n_frames);

Expand Down
2 changes: 1 addition & 1 deletion bin/tools/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cc = meson.get_compiler('c')
libm = cc.find_library('m')

tiff_dep = dependency('libtiff-4', required: false)
tiff_dep = dependency('libtiff-4', version: '>= 4.3', required: false)

config = configuration_data()
config.set('HAVE_LIBTIFF', tiff_dep.found())
Expand Down
37 changes: 37 additions & 0 deletions conan.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"version": "0.5",
"requires": [
"zstd/1.5.7#b68ca8e3de04ba5957761751d1d661f4%1760955092.069",
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
"xz_utils/5.8.3#a8432fead347c69d8b2737c35f936132%1775752656.4",
"pcre2/10.42#a0b25dffca18f5987304619e9c93fb44%1783075188.143",
"libtiff/4.7.2#7283595b6a38d0781953fa63881e0e92%1783597163.469",
"libselinux/3.6#5a78ff6ae5034eeaac8da723361a8ce4%1717655459.344",
"libmount/2.39.2#64850679fa9d5ead878e5076b0cab2a8%1705591482.625",
"libjpeg/9f#8b1b89da851ad9172565c35f14e23c4c%1780476337.068",
"libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1774021608.288",
"libgettext/0.22#b09eea019e19b9b9c46d8f1da7d75444%1765809130.834",
"libffi/3.4.8#a045c00fb26779635e3bed40e80c5254%1753360042.396",
"glib/2.86.5#2886b0810a99a7d54b313db5d214f221%1783335578.429",
"elfutils/0.190#8e8c47bd9b2578e29a0341b09b977be8%1739188192.515",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1762886692.465"
],
"build_requires": [
"pkgconf/2.5.1#93c2051284cba1279494a43a4fcfeae2%1757684701.089",
"pkgconf/2.2.0#4ac315b50ef734072b00ff3aacbf52bf%1755505628.021",
"ninja/1.13.2#c8c5dc2a52ed6e4e42a66d75b4717ceb%1764096931.974",
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"meson/1.10.2#9d2d10681fe7fe61c788c58626c89b25%1775558003.754",
"m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259",
"libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1774021608.288",
"gnu-config/cci.20210814#466e9d4d7779e1c142443f7ea44b4284%1762363589.329",
"gettext/0.22.5#2f6edba611e8db1a8a18488b116e8c2a%1765299121.036",
"flex/2.6.4#efa781fc5088b47c895bd4eef6911f2e%1761560242.855",
"cmake/4.4.0#a5dd5d73bd9fc56b0fc3163a04573dc8%1784051561.926",
"bison/3.8.2#c3490cbe0078b6fd3eb4cf5ed64144dc%1755688988.644",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86"
],
"python_requires": [],
"config_requires": []
}
33 changes: 23 additions & 10 deletions conanfile.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import os

from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMakeToolchain, CMake
from conan.tools.microsoft.visual import is_msvc
from conan.errors import ConanInvalidConfiguration

class UcaConan(ConanFile):
name = "libuca"
version = "2.3.0"
license = "LGPL-2.1"
version = "2.4.0"
license = "LGPL-2.1-or-later"
author = "Marius Elvert [email protected]"
url = "https://github.com/ufo-kit/libuca"
description = "GLib-based C library for unified camera access ."
topics = ("utilities",)
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False]}
default_options = {"shared": True}
options = {
"shared": [True, False],
"with_tiff": [True, False],
}
default_options = {
"shared": True,
"with_tiff": True,
}
generators = "CMakeDeps"
exports_sources = "src/*", "include/*", "test/*", "bin/*", "plugins/*", "CMakeLists.txt", "package.sh.in"
tool_requires = "glib/2.81.0",

exports_sources = "src/*", "test/*", "bin/*", "plugins/*", "cmake/*", "CMakeLists.txt", "package.sh.in", "COPYING"
def configure(self):
# These are not applicable for C libraries
self.settings.rm_safe("compiler.libcxx")
Expand All @@ -26,16 +32,23 @@ def configure(self):
self.options["glib"].shared = True

def requirements(self):
self.requires("libtiff/4.4.0")
self.requires("glib/2.81.0", transitive_headers=True)
if self.options.with_tiff:
self.requires("libtiff/4.7.2")
self.requires("glib/2.86.5", transitive_headers=True)

def generate(self):
tc = CMakeToolchain(self)
# glib-mkenums is a Python script from the GLib library package.
# Host dependencies are not necessarily on the build PATH (Windows).
# CMake invokes this script using the build machine's Python interpreter.
tc.variables["GLIB2_MKENUMS"] = os.path.join(
self.dependencies["glib"].package_folder, "bin", "glib-mkenums"
).replace("\\", "/")
tc.variables["WITH_PYTHON_MULTITHREADING"] = False
tc.variables["WITH_GIR"] = False
tc.variables["WITH_GUI"] = False
tc.variables["WITH_TOOLS"] = True
tc.variables["WITH_TIFF"] = True
tc.variables["WITH_TIFF"] = bool(self.options.with_tiff)
tc.variables["USE_FIND_PACKAGE_FOR_GLIB"] = True
tc.generate()

Expand Down
11 changes: 6 additions & 5 deletions meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
project('libuca', 'c',
version: '2.4.0'
version: '2.4.0',
default_options: ['c_std=c11', 'warning_level=2']
)

version = meson.project_version()
Expand All @@ -10,10 +11,10 @@ version_patch = components[2]

gnome = import('gnome')

glib_dep = dependency('glib-2.0', version: '>= 2.38')
gio_dep = dependency('gio-2.0', version: '>= 2.38')
gobject_dep = dependency('gobject-2.0', version: '>= 2.38')
gmodule_dep = dependency('gmodule-2.0', version: '>= 2.38')
glib_dep = dependency('glib-2.0', version: '>= 2.56')
gio_dep = dependency('gio-2.0', version: '>= 2.56')
gobject_dep = dependency('gobject-2.0', version: '>= 2.56')
gmodule_dep = dependency('gmodule-2.0', version: '>= 2.56')

deps = [glib_dep, gio_dep, gobject_dep, gmodule_dep]

Expand Down
4 changes: 4 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ option('python',
type : 'string',
value : 'python3',
description : 'Name of python executable')

option('gui',
type : 'feature', value : 'disabled',
description : 'Build the deprecated GTK 2 GUI tools')
2 changes: 1 addition & 1 deletion plugins/file/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
tiff_dep = dependency('libtiff-4', required: false)
tiff_dep = dependency('libtiff-4', version: '>= 4.3', required: false)

if tiff_dep.found()
shared_library('ucafile',
Expand Down
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ set(CPACK_PACKAGE_VERSION_MINOR ${UCA_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${UCA_VERSION_PATCH})
set(VERSION ${UCA_VERSION_STRING})

set(CPACK_GENERATOR "RPM;")
set(CPACK_GENERATOR "TGZ" CACHE STRING
"Binary package generator(s), for example TGZ, DEB, or RPM")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)

set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_SOURCE_IGNORE_FILES ".git" "tags" ".bzr" ".swp")
Expand Down
4 changes: 2 additions & 2 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ plugindir = '@0@/@1@/uca'.format(get_option('prefix'), get_option('libdir'))

conf = configuration_data()
conf.set_quoted('UCA_PLUGINDIR', plugindir)
conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_38')
conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_38')
conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_56')
conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_56')

if python_dep.found()
conf.set('WITH_PYTHON_MULTITHREADING', get_option('with_python_multithreading'))
Expand Down
16 changes: 11 additions & 5 deletions src/uca-plugin-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ struct _UcaPluginManagerPrivate {

#ifdef _WIN32

static const gchar *MODULE_SEARCH_PATTERN = "uca([A-Za-z0-9]+).dll";
static const gchar *MODULE_SEARCH_PATTERN = "^uca([A-Za-z0-9_-]+)\\.dll$";
static const gchar *MODULE_PRINT_PATTERN = "uca%s.dll";

#else

static const gchar *MODULE_SEARCH_PATTERN = "libuca([A-Za-z0-9]+)";
static const gchar *MODULE_SEARCH_PATTERN =
"^libuca([A-Za-z0-9_-]+)\\.so(?:\\.[0-9]+)*$";
static const gchar *MODULE_PRINT_PATTERN = "libuca%s.so";

#endif
Expand Down Expand Up @@ -166,12 +167,16 @@ transform_camera_module_path_to_name (gchar *path, GList **result)
{
GRegex *pattern;
GMatchInfo *match_info;
gchar *basename;

pattern = g_regex_new (MODULE_SEARCH_PATTERN, 0, 0, NULL);
g_regex_match (pattern, path, 0, &match_info);
basename = g_path_get_basename (path);
g_regex_match (pattern, basename, 0, &match_info);

*result = g_list_append (*result, g_match_info_fetch (match_info, 1));
if (g_match_info_matches (match_info))
*result = g_list_append (*result, g_match_info_fetch (match_info, 1));
g_match_info_free (match_info);
g_free (basename);
g_regex_unref (pattern);
}

Expand Down Expand Up @@ -203,7 +208,7 @@ uca_plugin_manager_get_available_cameras (UcaPluginManager *manager)
}

unsigned int get_num_net_cameras(UcaPluginManager *manager) {
const GList *camera_names = uca_plugin_manager_get_available_cameras(manager);
GList *camera_names = uca_plugin_manager_get_available_cameras(manager);
unsigned int count = 0;

for (const GList *it = camera_names; it != NULL; it = g_list_next(it)) {
Expand All @@ -213,6 +218,7 @@ unsigned int get_num_net_cameras(UcaPluginManager *manager) {
}
}

g_list_free_full (camera_names, g_free);
return count;
}

Expand Down
Loading