feat: add deterministic raw development
This commit is contained in:
parent
3bfbfc0534
commit
c8fb2e4094
7 changed files with 896 additions and 61 deletions
|
|
@ -16,6 +16,16 @@ typedef enum {
|
|||
LARDON3D_IMAGE_CATALOG_DB_ERROR
|
||||
} Lardon3DImageCatalogImportResult;
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED = 0,
|
||||
LARDON3D_IMAGE_CATALOG_ASSET_INVALID_ARGUMENT,
|
||||
LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_ERROR,
|
||||
LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_CHANGED,
|
||||
LARDON3D_IMAGE_CATALOG_ASSET_TOO_LARGE,
|
||||
LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR,
|
||||
LARDON3D_IMAGE_CATALOG_ASSET_DB_ERROR
|
||||
} Lardon3DImageCatalogAssetPublishResult;
|
||||
|
||||
bool lardon3d_image_catalog_create_scanset(
|
||||
Lardon3DAppState *state, const char *name,
|
||||
Lardon3DProjectDbScanSet *scanset
|
||||
|
|
@ -25,6 +35,12 @@ Lardon3DImageCatalogImportResult lardon3d_image_catalog_import_file(
|
|||
uint64_t producer_task_id, Lardon3DProjectDbImage *image,
|
||||
Lardon3DProjectDbImageAsset *asset
|
||||
);
|
||||
/* Publishes immutable bytes to the managed asset store without creating an image. */
|
||||
Lardon3DImageCatalogAssetPublishResult lardon3d_image_catalog_publish_asset_file(
|
||||
Lardon3DAppState *state, const char *source_path, int64_t created_at,
|
||||
uint64_t max_source_bytes,
|
||||
Lardon3DProjectDbImageAsset *asset
|
||||
);
|
||||
Lardon3DProjectDbResult lardon3d_image_catalog_list(
|
||||
Lardon3DAppState *state, uint64_t scanset_id, uint64_t after_image_id,
|
||||
Lardon3DProjectDbImage *images, Lardon3DProjectDbImageAsset *assets,
|
||||
|
|
|
|||
57
include/lardon3d/raw_development.h
Normal file
57
include/lardon3d/raw_development.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef LARDON3D_RAW_DEVELOPMENT_H
|
||||
#define LARDON3D_RAW_DEVELOPMENT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <lardon3d/app_state.h>
|
||||
#include <lardon3d/project_db.h>
|
||||
|
||||
typedef enum {
|
||||
LARDON3D_RAW_DEVELOPMENT_OK = 0,
|
||||
LARDON3D_RAW_DEVELOPMENT_INVALID_ARGUMENT,
|
||||
LARDON3D_RAW_DEVELOPMENT_SOURCE_NOT_FOUND,
|
||||
LARDON3D_RAW_DEVELOPMENT_SOURCE_CHANGED,
|
||||
LARDON3D_RAW_DEVELOPMENT_UNSUPPORTED_RAW,
|
||||
LARDON3D_RAW_DEVELOPMENT_CORRUPT_RAW,
|
||||
LARDON3D_RAW_DEVELOPMENT_DECODER_FAILURE,
|
||||
LARDON3D_RAW_DEVELOPMENT_OUT_OF_MEMORY,
|
||||
LARDON3D_RAW_DEVELOPMENT_OUTPUT_LIMIT_EXCEEDED,
|
||||
LARDON3D_RAW_DEVELOPMENT_IO_ERROR,
|
||||
LARDON3D_RAW_DEVELOPMENT_DB_ERROR,
|
||||
LARDON3D_RAW_DEVELOPMENT_CONSTRAINT,
|
||||
LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR
|
||||
} Lardon3DRawDevelopmentResult;
|
||||
|
||||
typedef struct {
|
||||
Lardon3DProjectDbImageAsset source_asset;
|
||||
Lardon3DProjectDbImageAsset derived_asset;
|
||||
Lardon3DProjectDbImage image;
|
||||
unsigned char parameter_fingerprint[LARDON3D_PROJECT_DB_SHA256_SIZE];
|
||||
float resolved_camera_wb[4];
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
char libraw_version[64];
|
||||
char png_encoder_version[64];
|
||||
} Lardon3DRawDevelopmentOutput;
|
||||
|
||||
/* Computes the fixed RAW policy v1 fingerprint for explicitly resolved camera WB. */
|
||||
Lardon3DRawDevelopmentResult lardon3d_raw_development_policy_fingerprint(
|
||||
const float resolved_camera_wb[4],
|
||||
unsigned char fingerprint[LARDON3D_PROJECT_DB_SHA256_SIZE]
|
||||
);
|
||||
|
||||
/* Publishes a managed ARW source and a derived PNG into an existing Capture. */
|
||||
Lardon3DRawDevelopmentResult lardon3d_raw_develop_to_capture(
|
||||
Lardon3DAppState *state, uint64_t capture_id, const char *source_arw_path,
|
||||
uint64_t producer_task_id, int64_t created_at, Lardon3DRawDevelopmentOutput *output
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
26
meson.build
26
meson.build
|
|
@ -36,6 +36,9 @@ opencv = dependency(
|
|||
include_type: 'system',
|
||||
modules: ['opencv_core', 'opencv_imgcodecs', 'opencv_features2d', 'opencv_geometry'],
|
||||
)
|
||||
libraw = dependency('libraw', required: true)
|
||||
libpng = dependency('libpng', required: true)
|
||||
libdeflate = dependency('libdeflate', required: true)
|
||||
opencv_benchmark = dependency(
|
||||
'opencv5',
|
||||
required: true,
|
||||
|
|
@ -144,6 +147,7 @@ executable(
|
|||
'src/import_task.c',
|
||||
'src/image_catalog.c',
|
||||
'src/image_catalog_persistent.c',
|
||||
'src/raw_development.cpp',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
'src/feature_store.c',
|
||||
'src/feature_extractor_opencv.cpp',
|
||||
|
|
@ -193,6 +197,9 @@ executable(
|
|||
threads,
|
||||
sqlite3,
|
||||
openssl,
|
||||
libraw,
|
||||
libpng,
|
||||
libdeflate,
|
||||
opencv,
|
||||
opencv_geometry,
|
||||
opencv_dense_mvs,
|
||||
|
|
@ -294,6 +301,25 @@ persistent_image_catalog_test = executable(
|
|||
|
||||
test('persistent-image-catalog', persistent_image_catalog_test, timeout: 60)
|
||||
|
||||
raw_development_test = executable(
|
||||
'test-raw-development',
|
||||
sources: [
|
||||
'tests/test_raw_development.cpp',
|
||||
'src/app_state.c',
|
||||
'src/image_catalog_persistent.c',
|
||||
'src/raw_development.cpp',
|
||||
'src/project_db.c', 'src/project_db_sparse_sfm.c',
|
||||
'src/task.c',
|
||||
'src/resource_governor.c',
|
||||
'src/resource_snapshot.c',
|
||||
],
|
||||
include_directories: include_directories('include'),
|
||||
cpp_args: ['-DLARDON3D_RAW_DEVELOPMENT_TESTING'],
|
||||
dependencies: [threads, sqlite3, openssl, opencv, libraw, libpng, libdeflate],
|
||||
)
|
||||
|
||||
test('raw-development', raw_development_test, timeout: 30)
|
||||
|
||||
feature_store_test = executable(
|
||||
'test-feature-store',
|
||||
sources: [
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@
|
|||
|
||||
enum { COPY_BUFFER_SIZE = 64 * 1024 };
|
||||
|
||||
typedef enum {
|
||||
COPY_OK = 0,
|
||||
COPY_SOURCE_ERROR,
|
||||
COPY_DESTINATION_ERROR
|
||||
} CopyResult;
|
||||
|
||||
static bool
|
||||
join_path(char output[PATH_MAX], const char *parent, const char *child)
|
||||
{
|
||||
|
|
@ -46,34 +52,44 @@ write_all(int descriptor, const unsigned char *data, size_t size)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
static CopyResult
|
||||
hash_stream(int input, int output,
|
||||
unsigned char hash[LARDON3D_PROJECT_DB_SHA256_SIZE], uint64_t *size)
|
||||
{
|
||||
EVP_MD_CTX *context = EVP_MD_CTX_new();
|
||||
if (!context) return false;
|
||||
if (!context) return COPY_SOURCE_ERROR;
|
||||
bool success = EVP_DigestInit_ex(context, EVP_sha256(), NULL) == 1;
|
||||
CopyResult result = COPY_OK;
|
||||
unsigned char buffer[COPY_BUFFER_SIZE];
|
||||
uint64_t total = 0;
|
||||
while (success) {
|
||||
ssize_t count = read(input, buffer, sizeof(buffer));
|
||||
if (count < 0 && errno == EINTR) continue;
|
||||
if (count < 0) { success = false; break; }
|
||||
if (count < 0) { success = false; result = COPY_SOURCE_ERROR; break; }
|
||||
if (count == 0) break;
|
||||
size_t bytes = (size_t)count;
|
||||
if (total > (uint64_t)INT64_MAX - bytes
|
||||
|| EVP_DigestUpdate(context, buffer, bytes) != 1
|
||||
|| (output >= 0 && !write_all(output, buffer, bytes))) {
|
||||
success = false; break;
|
||||
|| EVP_DigestUpdate(context, buffer, bytes) != 1) {
|
||||
success = false;
|
||||
result = COPY_SOURCE_ERROR;
|
||||
break;
|
||||
}
|
||||
if (output >= 0 && !write_all(output, buffer, bytes)) {
|
||||
success = false;
|
||||
result = COPY_DESTINATION_ERROR;
|
||||
break;
|
||||
}
|
||||
total += bytes;
|
||||
}
|
||||
unsigned int hash_size = 0;
|
||||
if (!success || EVP_DigestFinal_ex(context, hash, &hash_size) != 1
|
||||
|| hash_size != LARDON3D_PROJECT_DB_SHA256_SIZE) success = false;
|
||||
|| hash_size != LARDON3D_PROJECT_DB_SHA256_SIZE) {
|
||||
success = false;
|
||||
if (result == COPY_OK) result = COPY_SOURCE_ERROR;
|
||||
}
|
||||
EVP_MD_CTX_free(context);
|
||||
if (success) *size = total;
|
||||
return success;
|
||||
return success ? COPY_OK : result;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -100,7 +116,7 @@ same_published_asset(const char *path,
|
|||
uint64_t size = 0;
|
||||
bool success = fstat(descriptor, &information) == 0
|
||||
&& S_ISREG(information.st_mode)
|
||||
&& hash_stream(descriptor, -1, actual, &size)
|
||||
&& hash_stream(descriptor, -1, actual, &size) == COPY_OK
|
||||
&& size == expected_size
|
||||
&& memcmp(actual, expected, sizeof(actual)) == 0;
|
||||
(void)close(descriptor);
|
||||
|
|
@ -117,6 +133,114 @@ sync_directory(const char *path)
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool
|
||||
same_source_snapshot(const struct stat *before, const struct stat *after)
|
||||
{
|
||||
return before->st_dev == after->st_dev && before->st_ino == after->st_ino
|
||||
&& before->st_size == after->st_size
|
||||
&& before->st_mtim.tv_sec == after->st_mtim.tv_sec
|
||||
&& before->st_mtim.tv_nsec == after->st_mtim.tv_nsec
|
||||
&& before->st_ctim.tv_sec == after->st_ctim.tv_sec
|
||||
&& before->st_ctim.tv_nsec == after->st_ctim.tv_nsec;
|
||||
}
|
||||
|
||||
Lardon3DImageCatalogAssetPublishResult
|
||||
lardon3d_image_catalog_publish_asset_file(Lardon3DAppState *state,
|
||||
const char *source_path, int64_t created_at, uint64_t max_source_bytes,
|
||||
Lardon3DProjectDbImageAsset *asset)
|
||||
{
|
||||
if (!state || !state->project_loaded || !state->project_db || !source_path
|
||||
|| !source_path[0] || created_at < 0 || max_source_bytes == 0 || !asset)
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_INVALID_ARGUMENT;
|
||||
memset(asset, 0, sizeof(*asset));
|
||||
int input = open(source_path, O_RDONLY | O_NOFOLLOW);
|
||||
if (input < 0) return LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_ERROR;
|
||||
struct stat source_before;
|
||||
if (fstat(input, &source_before) != 0 || !S_ISREG(source_before.st_mode)
|
||||
|| source_before.st_size < 0) {
|
||||
(void)close(input);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_ERROR;
|
||||
}
|
||||
if ((uint64_t)source_before.st_size > max_source_bytes) {
|
||||
(void)close(input);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_TOO_LARGE;
|
||||
}
|
||||
char assets[PATH_MAX], images[PATH_MAX], temporary[PATH_MAX];
|
||||
if (!join_path(assets, state->project_path, "assets")
|
||||
|| !ensure_directory(assets) || !sync_directory(state->project_path)
|
||||
|| !join_path(images, assets, "images") || !ensure_directory(images)
|
||||
|| !sync_directory(assets)
|
||||
|| snprintf(temporary, sizeof(temporary), "%s/.asset.tmp.XXXXXX", images) <= 0) {
|
||||
(void)close(input);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR;
|
||||
}
|
||||
int output = mkstemp(temporary);
|
||||
if (output < 0) {
|
||||
(void)close(input);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR;
|
||||
}
|
||||
unsigned char hash[LARDON3D_PROJECT_DB_SHA256_SIZE];
|
||||
uint64_t size = 0;
|
||||
struct stat source_after;
|
||||
CopyResult copy = hash_stream(input, output, hash, &size);
|
||||
bool copied = copy == COPY_OK;
|
||||
bool source_changed = copied && (fstat(input, &source_after) != 0
|
||||
|| !same_source_snapshot(&source_before, &source_after));
|
||||
bool source_close_ok = close(input) == 0;
|
||||
bool destination_sync_ok = copied && !source_changed && source_close_ok
|
||||
&& fsync(output) == 0;
|
||||
int destination_close_result = close(output);
|
||||
if (copy == COPY_DESTINATION_ERROR || (copied && !source_changed && source_close_ok
|
||||
&& (!destination_sync_ok || destination_close_result != 0))) {
|
||||
(void)unlink(temporary);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR;
|
||||
}
|
||||
if (source_changed) {
|
||||
(void)unlink(temporary);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_CHANGED;
|
||||
}
|
||||
if (copy != COPY_OK || !source_close_ok) {
|
||||
(void)unlink(temporary);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_ERROR;
|
||||
}
|
||||
char hex[65], prefix[3], prefix_path[PATH_MAX], final[PATH_MAX];
|
||||
hash_hex(hash, hex);
|
||||
prefix[0] = hex[0]; prefix[1] = hex[1]; prefix[2] = '\0';
|
||||
if (!join_path(prefix_path, images, prefix) || !ensure_directory(prefix_path)
|
||||
|| !sync_directory(images) || !join_path(final, prefix_path, hex)) {
|
||||
(void)unlink(temporary);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR;
|
||||
}
|
||||
bool published = false;
|
||||
if (link(temporary, final) == 0) {
|
||||
published = unlink(temporary) == 0 && sync_directory(prefix_path);
|
||||
} else if (errno == EEXIST && same_published_asset(final, hash, size)) {
|
||||
published = unlink(temporary) == 0;
|
||||
}
|
||||
if (!published) {
|
||||
(void)unlink(temporary);
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR;
|
||||
}
|
||||
char relative[LARDON3D_PROJECT_DB_PATH_CAPACITY];
|
||||
int relative_size = snprintf(relative, sizeof(relative), "assets/images/%s/%s", prefix, hex);
|
||||
if (relative_size <= 0 || (size_t)relative_size >= sizeof(relative)
|
||||
|| lardon3d_project_db_register_image_asset(state->project_db, hash, relative,
|
||||
size, created_at, asset) != LARDON3D_PROJECT_DB_OK)
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_DB_ERROR;
|
||||
return LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED;
|
||||
}
|
||||
|
||||
Lardon3DImageCatalogAssetPublishResult
|
||||
lardon3d_image_catalog_test_copy_hash(int input, int output)
|
||||
{
|
||||
unsigned char hash[LARDON3D_PROJECT_DB_SHA256_SIZE];
|
||||
uint64_t size = 0;
|
||||
CopyResult result = hash_stream(input, output, hash, &size);
|
||||
return result == COPY_OK ? LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED
|
||||
: result == COPY_DESTINATION_ERROR ? LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR
|
||||
: LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_ERROR;
|
||||
}
|
||||
|
||||
bool
|
||||
lardon3d_image_catalog_create_scanset(Lardon3DAppState *state,
|
||||
const char *name, Lardon3DProjectDbScanSet *scanset)
|
||||
|
|
@ -144,59 +268,22 @@ lardon3d_image_catalog_import_file(Lardon3DAppState *state,
|
|||
Lardon3DProjectDbScanSet scanset;
|
||||
if (lardon3d_project_db_load_scanset(state->project_db, scanset_id, &scanset)
|
||||
!= LARDON3D_PROJECT_DB_OK) return LARDON3D_IMAGE_CATALOG_INVALID_ARGUMENT;
|
||||
int input = open(source_path, O_RDONLY | O_NOFOLLOW);
|
||||
if (input < 0) return LARDON3D_IMAGE_CATALOG_SOURCE_ERROR;
|
||||
struct stat source_information;
|
||||
if (fstat(input, &source_information) != 0
|
||||
|| !S_ISREG(source_information.st_mode) || source_information.st_size < 0) {
|
||||
(void)close(input); return LARDON3D_IMAGE_CATALOG_SOURCE_ERROR;
|
||||
}
|
||||
char assets[PATH_MAX], images[PATH_MAX], temporary[PATH_MAX];
|
||||
if (!join_path(assets, state->project_path, "assets")
|
||||
|| !ensure_directory(assets) || !sync_directory(state->project_path)
|
||||
|| !join_path(images, assets, "images") || !ensure_directory(images)
|
||||
|| !sync_directory(assets)
|
||||
|| snprintf(temporary, sizeof(temporary), "%s/.asset.tmp.XXXXXX", images) <= 0) {
|
||||
(void)close(input); return LARDON3D_IMAGE_CATALOG_PUBLICATION_ERROR;
|
||||
}
|
||||
int output = mkstemp(temporary);
|
||||
if (output < 0) { (void)close(input); return LARDON3D_IMAGE_CATALOG_PUBLICATION_ERROR; }
|
||||
unsigned char hash[LARDON3D_PROJECT_DB_SHA256_SIZE];
|
||||
uint64_t size = 0;
|
||||
bool copied = hash_stream(input, output, hash, &size);
|
||||
if (close(input) != 0) copied = false;
|
||||
if (copied && fsync(output) != 0) copied = false;
|
||||
if (close(output) != 0) copied = false;
|
||||
if (!copied) {
|
||||
(void)unlink(temporary); return LARDON3D_IMAGE_CATALOG_SOURCE_ERROR;
|
||||
}
|
||||
char hex[65], prefix[3], prefix_path[PATH_MAX], final[PATH_MAX];
|
||||
hash_hex(hash, hex); prefix[0] = hex[0]; prefix[1] = hex[1]; prefix[2] = '\0';
|
||||
if (!join_path(prefix_path, images, prefix) || !ensure_directory(prefix_path)
|
||||
|| !sync_directory(images)
|
||||
|| !join_path(final, prefix_path, hex)) {
|
||||
(void)unlink(temporary); return LARDON3D_IMAGE_CATALOG_PUBLICATION_ERROR;
|
||||
}
|
||||
bool published = false;
|
||||
if (link(temporary, final) == 0) {
|
||||
bool temporary_removed = unlink(temporary) == 0;
|
||||
bool directory_synced = sync_directory(prefix_path);
|
||||
published = temporary_removed && directory_synced;
|
||||
} else if (errno == EEXIST && same_published_asset(final, hash, size)) {
|
||||
published = unlink(temporary) == 0;
|
||||
}
|
||||
if (!published) {
|
||||
(void)unlink(temporary); return LARDON3D_IMAGE_CATALOG_PUBLICATION_ERROR;
|
||||
}
|
||||
char relative[LARDON3D_PROJECT_DB_PATH_CAPACITY];
|
||||
int relative_size = snprintf(relative, sizeof(relative),
|
||||
"assets/images/%s/%s", prefix, hex);
|
||||
if (relative_size <= 0 || (size_t)relative_size >= sizeof(relative))
|
||||
return LARDON3D_IMAGE_CATALOG_PUBLICATION_ERROR;
|
||||
Lardon3DProjectDbImageRegisterStatus status;
|
||||
int64_t imported_at = (int64_t)time(NULL);
|
||||
if (imported_at < 0) return LARDON3D_IMAGE_CATALOG_PUBLICATION_ERROR;
|
||||
Lardon3DImageCatalogAssetPublishResult published =
|
||||
lardon3d_image_catalog_publish_asset_file(state, source_path, imported_at,
|
||||
UINT64_MAX, asset);
|
||||
if (published == LARDON3D_IMAGE_CATALOG_ASSET_INVALID_ARGUMENT)
|
||||
return LARDON3D_IMAGE_CATALOG_INVALID_ARGUMENT;
|
||||
if (published == LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_ERROR)
|
||||
return LARDON3D_IMAGE_CATALOG_SOURCE_ERROR;
|
||||
if (published == LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR)
|
||||
return LARDON3D_IMAGE_CATALOG_PUBLICATION_ERROR;
|
||||
if (published != LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED)
|
||||
return LARDON3D_IMAGE_CATALOG_DB_ERROR;
|
||||
Lardon3DProjectDbImageRegisterStatus status;
|
||||
if (imported_at < 0 || lardon3d_project_db_register_image(state->project_db,
|
||||
scanset_id, hash, relative, size, basename, source_path,
|
||||
scanset_id, asset->sha256, asset->path, asset->size_bytes, basename, source_path,
|
||||
producer_task_id, imported_at, &status, image)
|
||||
!= LARDON3D_PROJECT_DB_OK) return LARDON3D_IMAGE_CATALOG_DB_ERROR;
|
||||
if (lardon3d_project_db_load_image(state->project_db, image->image_id,
|
||||
|
|
|
|||
331
src/raw_development.cpp
Normal file
331
src/raw_development.cpp
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
#include <lardon3d/raw_development.h>
|
||||
extern "C" {
|
||||
#include <lardon3d/image_catalog.h>
|
||||
}
|
||||
|
||||
#include <libraw/libraw.h>
|
||||
#include <libdeflate.h>
|
||||
#include <opencv2/core/version.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <openssl/evp.h>
|
||||
#include <png.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
constexpr uint32_t kMaxDimension = 16384;
|
||||
constexpr uint64_t kMaxPixels = 40000000;
|
||||
constexpr uint64_t kMaxSourceBytes = UINT64_C(1024) * 1024U * 1024U;
|
||||
constexpr int kPngCompression = 6;
|
||||
constexpr int kPngStrategy = cv::IMWRITE_PNG_STRATEGY_DEFAULT;
|
||||
constexpr uint32_t kBackendKindLibRaw = 1;
|
||||
constexpr uint32_t kEncoderKindOpenCvPng = 1;
|
||||
constexpr size_t kRawFingerprintPayloadSize = 200;
|
||||
|
||||
bool valid_wb(const float wb[4]) {
|
||||
for (size_t i = 0; i < 4; ++i) if (!std::isfinite(wb[i]) || wb[i] <= 0.0F) return false;
|
||||
return true;
|
||||
}
|
||||
void put_u32(std::vector<unsigned char> *bytes, uint32_t value) {
|
||||
for (unsigned i = 0; i < 4; ++i) bytes->push_back((unsigned char)(value >> (8U * i)));
|
||||
}
|
||||
void put_float(std::vector<unsigned char> *bytes, float value) {
|
||||
uint32_t bits = 0; static_assert(sizeof(bits) == sizeof(value), "float width");
|
||||
std::memcpy(&bits, &value, sizeof(bits)); put_u32(bytes, bits);
|
||||
}
|
||||
bool sha256(const std::vector<unsigned char> &bytes, unsigned char out[32]) {
|
||||
unsigned int size = 0;
|
||||
return EVP_Digest(bytes.data(), bytes.size(), out, &size, EVP_sha256(), nullptr) == 1 && size == 32;
|
||||
}
|
||||
bool checked_rgb_size(uint32_t width, uint32_t height, size_t *bytes) {
|
||||
if (width == 0 || height == 0 || width > kMaxDimension || height > kMaxDimension) return false;
|
||||
uint64_t pixels = (uint64_t)width * height;
|
||||
if (pixels > kMaxPixels || pixels > SIZE_MAX / 3U) return false;
|
||||
*bytes = (size_t)pixels * 3U; return true;
|
||||
}
|
||||
Lardon3DRawDevelopmentResult catalog_result(Lardon3DImageCatalogAssetPublishResult result) {
|
||||
switch (result) {
|
||||
case LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED: return LARDON3D_RAW_DEVELOPMENT_OK;
|
||||
case LARDON3D_IMAGE_CATALOG_ASSET_INVALID_ARGUMENT: return LARDON3D_RAW_DEVELOPMENT_INVALID_ARGUMENT;
|
||||
case LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_ERROR: return LARDON3D_RAW_DEVELOPMENT_SOURCE_NOT_FOUND;
|
||||
case LARDON3D_IMAGE_CATALOG_ASSET_SOURCE_CHANGED: return LARDON3D_RAW_DEVELOPMENT_SOURCE_CHANGED;
|
||||
case LARDON3D_IMAGE_CATALOG_ASSET_TOO_LARGE: return LARDON3D_RAW_DEVELOPMENT_OUTPUT_LIMIT_EXCEEDED;
|
||||
case LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR: return LARDON3D_RAW_DEVELOPMENT_IO_ERROR;
|
||||
case LARDON3D_IMAGE_CATALOG_ASSET_DB_ERROR: return LARDON3D_RAW_DEVELOPMENT_DB_ERROR;
|
||||
}
|
||||
return LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR;
|
||||
}
|
||||
Lardon3DRawDevelopmentResult db_result(Lardon3DProjectDbResult result) {
|
||||
if (result == LARDON3D_PROJECT_DB_OK) return LARDON3D_RAW_DEVELOPMENT_OK;
|
||||
if (result == LARDON3D_PROJECT_DB_CONSTRAINT) return LARDON3D_RAW_DEVELOPMENT_CONSTRAINT;
|
||||
if (result == LARDON3D_PROJECT_DB_NOT_FOUND) return LARDON3D_RAW_DEVELOPMENT_SOURCE_NOT_FOUND;
|
||||
return LARDON3D_RAW_DEVELOPMENT_DB_ERROR;
|
||||
}
|
||||
bool same_scientific_derivation(const Lardon3DProjectDbAssetDerivation &stored,
|
||||
const Lardon3DProjectDbAssetDerivation &expected) {
|
||||
return stored.parent_asset_id == expected.parent_asset_id
|
||||
&& stored.child_asset_id == expected.child_asset_id && stored.kind == expected.kind
|
||||
&& stored.version == expected.version
|
||||
&& std::memcmp(stored.parameter_fingerprint, expected.parameter_fingerprint,
|
||||
sizeof(expected.parameter_fingerprint)) == 0;
|
||||
}
|
||||
Lardon3DRawDevelopmentResult ensure_derivation(Lardon3DProjectDb *database,
|
||||
const Lardon3DProjectDbAssetDerivation &expected) {
|
||||
Lardon3DProjectDbAssetDerivation stored = {};
|
||||
Lardon3DProjectDbResult load = lardon3d_project_db_load_asset_derivation(
|
||||
database, expected.child_asset_id, &stored);
|
||||
if (load == LARDON3D_PROJECT_DB_NOT_FOUND)
|
||||
return db_result(lardon3d_project_db_record_asset_derivation(database, &expected));
|
||||
if (load != LARDON3D_PROJECT_DB_OK) return LARDON3D_RAW_DEVELOPMENT_DB_ERROR;
|
||||
return same_scientific_derivation(stored, expected) ? LARDON3D_RAW_DEVELOPMENT_OK
|
||||
: LARDON3D_RAW_DEVELOPMENT_CONSTRAINT;
|
||||
}
|
||||
Lardon3DRawDevelopmentResult ensure_capture_asset_role(
|
||||
Lardon3DProjectDb *database, uint64_t capture_id, uint64_t asset_id,
|
||||
Lardon3DProjectDbCaptureAssetRole expected_role) {
|
||||
Lardon3DProjectDbResult attach = lardon3d_project_db_attach_capture_asset(
|
||||
database, capture_id, asset_id, expected_role);
|
||||
if (attach == LARDON3D_PROJECT_DB_OK) return LARDON3D_RAW_DEVELOPMENT_OK;
|
||||
if (attach != LARDON3D_PROJECT_DB_CONSTRAINT) return db_result(attach);
|
||||
|
||||
Lardon3DProjectDbCaptureAsset page[LARDON3D_PROJECT_DB_CATALOG_PAGE_MAX];
|
||||
uint64_t after_asset_id = 0;
|
||||
for (;;) {
|
||||
size_t count = 0;
|
||||
Lardon3DProjectDbResult listed = lardon3d_project_db_list_capture_assets(
|
||||
database, capture_id, after_asset_id, page,
|
||||
LARDON3D_PROJECT_DB_CATALOG_PAGE_MAX, &count);
|
||||
if (listed != LARDON3D_PROJECT_DB_OK) return LARDON3D_RAW_DEVELOPMENT_DB_ERROR;
|
||||
for (size_t index = 0; index < count; ++index) {
|
||||
if (page[index].asset_id == asset_id)
|
||||
return page[index].role == expected_role ? LARDON3D_RAW_DEVELOPMENT_OK
|
||||
: LARDON3D_RAW_DEVELOPMENT_CONSTRAINT;
|
||||
if (page[index].asset_id > asset_id) return LARDON3D_RAW_DEVELOPMENT_CONSTRAINT;
|
||||
}
|
||||
if (count < LARDON3D_PROJECT_DB_CATALOG_PAGE_MAX) return LARDON3D_RAW_DEVELOPMENT_CONSTRAINT;
|
||||
after_asset_id = page[count - 1].asset_id;
|
||||
}
|
||||
}
|
||||
Lardon3DRawDevelopmentResult libraw_result(int code) {
|
||||
if (code == LIBRAW_FILE_UNSUPPORTED) return LARDON3D_RAW_DEVELOPMENT_UNSUPPORTED_RAW;
|
||||
if (code == LIBRAW_UNSUFFICIENT_MEMORY) return LARDON3D_RAW_DEVELOPMENT_OUT_OF_MEMORY;
|
||||
if (code == LIBRAW_DATA_ERROR || code == LIBRAW_IO_ERROR) return LARDON3D_RAW_DEVELOPMENT_CORRUPT_RAW;
|
||||
return LARDON3D_RAW_DEVELOPMENT_DECODER_FAILURE;
|
||||
}
|
||||
bool write_all(int fd, const unsigned char *data, size_t size) {
|
||||
size_t offset = 0;
|
||||
while (offset < size) {
|
||||
ssize_t written = write(fd, data + offset, size - offset);
|
||||
if (written < 0 && errno == EINTR) continue;
|
||||
if (written <= 0) return false;
|
||||
offset += (size_t)written;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool asset_file_path(const Lardon3DAppState *state, const Lardon3DProjectDbImageAsset &asset,
|
||||
char path[LARDON3D_APP_STATE_PATH_CAPACITY]) {
|
||||
int count = std::snprintf(path, LARDON3D_APP_STATE_PATH_CAPACITY, "%s/%s",
|
||||
state->project_path, asset.path);
|
||||
return count > 0 && count < LARDON3D_APP_STATE_PATH_CAPACITY;
|
||||
}
|
||||
bool verify_managed_asset(const char *path, const unsigned char expected[32]) {
|
||||
int fd = open(path, O_RDONLY | O_NOFOLLOW);
|
||||
if (fd < 0) return false;
|
||||
struct stat info; EVP_MD_CTX *context = EVP_MD_CTX_new();
|
||||
bool ok = fstat(fd, &info) == 0 && S_ISREG(info.st_mode) && context
|
||||
&& EVP_DigestInit_ex(context, EVP_sha256(), nullptr) == 1;
|
||||
unsigned char buffer[65536];
|
||||
while (ok) {
|
||||
ssize_t count = read(fd, buffer, sizeof(buffer));
|
||||
if (count < 0 && errno == EINTR) continue;
|
||||
if (count < 0) { ok = false; break; }
|
||||
if (count == 0) break;
|
||||
ok = EVP_DigestUpdate(context, buffer, (size_t)count) == 1;
|
||||
}
|
||||
unsigned char actual[32]; unsigned int length = 0;
|
||||
if (ok) ok = EVP_DigestFinal_ex(context, actual, &length) == 1 && length == sizeof(actual)
|
||||
&& std::memcmp(actual, expected, sizeof(actual)) == 0;
|
||||
EVP_MD_CTX_free(context); (void)close(fd); return ok;
|
||||
}
|
||||
bool valid_name(const char *path, char name[LARDON3D_PROJECT_DB_IMAGE_NAME_CAPACITY]) {
|
||||
const char *base = std::strrchr(path, '/'); base = base ? base + 1 : path;
|
||||
size_t size = strnlen(base, LARDON3D_PROJECT_DB_IMAGE_NAME_CAPACITY);
|
||||
if (size == 0 || size >= LARDON3D_PROJECT_DB_IMAGE_NAME_CAPACITY || std::strchr(base, '\\')
|
||||
|| std::strchr(base, '\t') || std::strchr(base, '\r') || std::strchr(base, '\n')) return false;
|
||||
std::memcpy(name, base, size + 1); return true;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" Lardon3DRawDevelopmentResult lardon3d_raw_development_policy_fingerprint(
|
||||
const float wb[4], unsigned char fingerprint[32]) {
|
||||
if (!wb || !fingerprint || !valid_wb(wb)) return LARDON3D_RAW_DEVELOPMENT_INVALID_ARGUMENT;
|
||||
try {
|
||||
if (std::strcmp(png_get_libpng_ver(nullptr), PNG_LIBPNG_VER_STRING) != 0)
|
||||
return LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR;
|
||||
std::vector<unsigned char> bytes;
|
||||
bytes.reserve(kRawFingerprintPayloadSize);
|
||||
const char magic[] = "L3DRAWD1";
|
||||
bytes.insert(bytes.end(), magic, magic + sizeof(magic) - 1);
|
||||
put_u32(&bytes, 1); put_u32(&bytes, kBackendKindLibRaw);
|
||||
put_u32(&bytes, LIBRAW_VERSION); put_u32(&bytes, libraw_versionNumber());
|
||||
put_u32(&bytes, 3); put_u32(&bytes, 0); put_u32(&bytes, 0);
|
||||
for (size_t i = 0; i < 4; ++i) put_float(&bytes, wb[i]);
|
||||
put_u32(&bytes, 0); put_u32(&bytes, 0); put_u32(&bytes, 0); put_u32(&bytes, 1);
|
||||
put_float(&bytes, 1.0F / 2.4F); put_float(&bytes, 12.92F);
|
||||
put_u32(&bytes, 1); put_float(&bytes, 1.0F); put_float(&bytes, 0.0F);
|
||||
put_u32(&bytes, 0); put_float(&bytes, 1.0F); put_float(&bytes, 0.0F);
|
||||
put_u32(&bytes, 0); put_float(&bytes, 0.0F); put_u32(&bytes, 0); put_u32(&bytes, 0);
|
||||
put_u32(&bytes, 0); put_u32(&bytes, 0); put_u32(&bytes, 0); put_u32(&bytes, 0);
|
||||
put_u32(&bytes, 8); put_u32(&bytes, 3); put_u32(&bytes, kEncoderKindOpenCvPng);
|
||||
put_u32(&bytes, kPngCompression);
|
||||
put_u32(&bytes, kPngStrategy); put_u32(&bytes, 0); put_u32(&bytes, kMaxDimension);
|
||||
put_u32(&bytes, (uint32_t)kMaxPixels);
|
||||
put_u32(&bytes, (uint32_t)(kMaxSourceBytes >> 20)); put_u32(&bytes, CV_VERSION_MAJOR);
|
||||
put_u32(&bytes, CV_VERSION_MINOR); put_u32(&bytes, CV_VERSION_REVISION);
|
||||
put_u32(&bytes, PNG_LIBPNG_VER_MAJOR); put_u32(&bytes, PNG_LIBPNG_VER_MINOR);
|
||||
put_u32(&bytes, PNG_LIBPNG_VER_RELEASE);
|
||||
put_u32(&bytes, LIBDEFLATE_VERSION_MAJOR); put_u32(&bytes, LIBDEFLATE_VERSION_MINOR);
|
||||
if (bytes.size() != kRawFingerprintPayloadSize)
|
||||
return LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR;
|
||||
return sha256(bytes, fingerprint) ? LARDON3D_RAW_DEVELOPMENT_OK
|
||||
: LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR;
|
||||
} catch (const std::bad_alloc &) {
|
||||
return LARDON3D_RAW_DEVELOPMENT_OUT_OF_MEMORY;
|
||||
} catch (...) {
|
||||
return LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LARDON3D_RAW_DEVELOPMENT_TESTING
|
||||
extern "C" size_t lardon3d_raw_development_test_policy_payload_size(void) {
|
||||
return kRawFingerprintPayloadSize;
|
||||
}
|
||||
extern "C" Lardon3DRawDevelopmentResult
|
||||
lardon3d_raw_development_test_ensure_derivation(
|
||||
Lardon3DProjectDb *database, const Lardon3DProjectDbAssetDerivation *expected) {
|
||||
return !database || !expected ? LARDON3D_RAW_DEVELOPMENT_INVALID_ARGUMENT
|
||||
: ensure_derivation(database, *expected);
|
||||
}
|
||||
extern "C" Lardon3DRawDevelopmentResult
|
||||
lardon3d_raw_development_test_ensure_capture_asset_role(
|
||||
Lardon3DProjectDb *database, uint64_t capture_id, uint64_t asset_id,
|
||||
Lardon3DProjectDbCaptureAssetRole expected_role) {
|
||||
return ensure_capture_asset_role(database, capture_id, asset_id, expected_role);
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" Lardon3DRawDevelopmentResult lardon3d_raw_develop_to_capture(
|
||||
Lardon3DAppState *state, uint64_t capture_id, const char *source_arw_path,
|
||||
uint64_t producer_task_id, int64_t created_at, Lardon3DRawDevelopmentOutput *output) {
|
||||
if (!state || !state->project_loaded || !state->project_db || capture_id == 0 || !source_arw_path
|
||||
|| !source_arw_path[0] || created_at < 0 || !output) return LARDON3D_RAW_DEVELOPMENT_INVALID_ARGUMENT;
|
||||
std::memset(output, 0, sizeof(*output));
|
||||
try {
|
||||
Lardon3DProjectDbCapture capture;
|
||||
Lardon3DRawDevelopmentResult result = db_result(lardon3d_project_db_load_capture(
|
||||
state->project_db, capture_id, &capture));
|
||||
if (result != LARDON3D_RAW_DEVELOPMENT_OK) return result;
|
||||
result = catalog_result(lardon3d_image_catalog_publish_asset_file(
|
||||
state, source_arw_path, created_at, kMaxSourceBytes, &output->source_asset));
|
||||
if (result != LARDON3D_RAW_DEVELOPMENT_OK) return result;
|
||||
result = ensure_capture_asset_role(state->project_db, capture_id,
|
||||
output->source_asset.asset_id, LARDON3D_DB_CAPTURE_ASSET_SOURCE);
|
||||
if (result != LARDON3D_RAW_DEVELOPMENT_OK) return result;
|
||||
char managed_source[LARDON3D_APP_STATE_PATH_CAPACITY];
|
||||
if (!asset_file_path(state, output->source_asset, managed_source)) return LARDON3D_RAW_DEVELOPMENT_IO_ERROR;
|
||||
if (!verify_managed_asset(managed_source, output->source_asset.sha256)) return LARDON3D_RAW_DEVELOPMENT_SOURCE_CHANGED;
|
||||
LibRaw raw;
|
||||
raw.imgdata.params.user_qual = 3; raw.imgdata.params.half_size = 0;
|
||||
raw.imgdata.params.user_flip = 0; raw.imgdata.params.use_auto_wb = 0;
|
||||
raw.imgdata.params.use_camera_wb = 0; raw.imgdata.params.use_camera_matrix = 0;
|
||||
raw.imgdata.params.output_profile = nullptr; raw.imgdata.params.camera_profile = nullptr;
|
||||
raw.imgdata.params.output_color = 1; raw.imgdata.params.gamm[0] = 1.0 / 2.4;
|
||||
raw.imgdata.params.gamm[1] = 12.92; raw.imgdata.params.no_auto_bright = 1;
|
||||
raw.imgdata.params.bright = 1.0F; raw.imgdata.params.adjust_maximum_thr = 0.0F;
|
||||
raw.imgdata.params.exp_correc = 0; raw.imgdata.params.exp_shift = 1.0F;
|
||||
raw.imgdata.params.exp_preser = 0.0F; raw.imgdata.params.highlight = 0;
|
||||
raw.imgdata.params.threshold = 0.0F; raw.imgdata.params.fbdd_noiserd = 0;
|
||||
raw.imgdata.params.med_passes = 0; raw.imgdata.params.output_bps = 8;
|
||||
raw.imgdata.params.output_tiff = 0; raw.imgdata.params.user_black = 0;
|
||||
raw.imgdata.params.user_sat = 0; raw.imgdata.params.no_auto_scale = 0;
|
||||
raw.imgdata.params.no_interpolation = 0;
|
||||
for (size_t i = 0; i < 4; ++i) raw.imgdata.params.aber[i] = 1.0;
|
||||
int code = raw.open_file(managed_source);
|
||||
if (code != LIBRAW_SUCCESS) return libraw_result(code);
|
||||
size_t decoded_size = 0;
|
||||
if (!checked_rgb_size(raw.imgdata.sizes.iwidth, raw.imgdata.sizes.iheight, &decoded_size))
|
||||
return LARDON3D_RAW_DEVELOPMENT_OUTPUT_LIMIT_EXCEEDED;
|
||||
code = raw.unpack(); if (code != LIBRAW_SUCCESS) return libraw_result(code);
|
||||
for (size_t i = 0; i < 4; ++i) output->resolved_camera_wb[i] = raw.imgdata.color.cam_mul[i];
|
||||
if (!valid_wb(output->resolved_camera_wb)) return LARDON3D_RAW_DEVELOPMENT_UNSUPPORTED_RAW;
|
||||
for (size_t i = 0; i < 4; ++i) raw.imgdata.params.user_mul[i] = output->resolved_camera_wb[i];
|
||||
result = lardon3d_raw_development_policy_fingerprint(output->resolved_camera_wb,
|
||||
output->parameter_fingerprint);
|
||||
if (result != LARDON3D_RAW_DEVELOPMENT_OK) return result;
|
||||
code = raw.dcraw_process(); if (code != LIBRAW_SUCCESS) return libraw_result(code);
|
||||
int image_error = LIBRAW_SUCCESS;
|
||||
libraw_processed_image_t *processed = raw.dcraw_make_mem_image(&image_error);
|
||||
if (!processed) return libraw_result(image_error);
|
||||
output->width = processed->width; output->height = processed->height;
|
||||
size_t rgb_size = 0;
|
||||
if (processed->type != LIBRAW_IMAGE_BITMAP || processed->colors != 3
|
||||
|| !checked_rgb_size(output->width, output->height, &rgb_size)
|
||||
|| processed->data_size != rgb_size) { LibRaw::dcraw_clear_mem(processed); return LARDON3D_RAW_DEVELOPMENT_OUTPUT_LIMIT_EXCEEDED; }
|
||||
std::vector<unsigned char> bgr(rgb_size);
|
||||
for (size_t i = 0; i < rgb_size; i += 3) { bgr[i] = processed->data[i + 2]; bgr[i + 1] = processed->data[i + 1]; bgr[i + 2] = processed->data[i]; }
|
||||
LibRaw::dcraw_clear_mem(processed);
|
||||
cv::Mat image((int)output->height, (int)output->width, CV_8UC3, bgr.data());
|
||||
std::vector<unsigned char> png;
|
||||
std::vector<int> parameters = {cv::IMWRITE_PNG_COMPRESSION, kPngCompression,
|
||||
cv::IMWRITE_PNG_STRATEGY, kPngStrategy};
|
||||
if (!cv::imencode(".png", image, png, parameters) || png.empty()) return LARDON3D_RAW_DEVELOPMENT_IO_ERROR;
|
||||
cv::Mat validated = cv::imdecode(png, cv::IMREAD_UNCHANGED);
|
||||
if (validated.empty() || validated.cols != (int)output->width
|
||||
|| validated.rows != (int)output->height || validated.type() != CV_8UC3)
|
||||
return LARDON3D_RAW_DEVELOPMENT_IO_ERROR;
|
||||
char staging[LARDON3D_APP_STATE_PATH_CAPACITY];
|
||||
int written = std::snprintf(staging, sizeof(staging), "%s/assets/images/.raw-png.tmp.XXXXXX", state->project_path);
|
||||
if (written <= 0 || written >= (int)sizeof(staging)) return LARDON3D_RAW_DEVELOPMENT_IO_ERROR;
|
||||
int fd = mkstemp(staging); if (fd < 0) return LARDON3D_RAW_DEVELOPMENT_IO_ERROR;
|
||||
bool write_ok = write_all(fd, png.data(), png.size());
|
||||
bool sync_ok = write_ok && fsync(fd) == 0;
|
||||
int close_result = close(fd);
|
||||
if (!write_ok || !sync_ok || close_result != 0) {
|
||||
(void)unlink(staging);
|
||||
return LARDON3D_RAW_DEVELOPMENT_IO_ERROR;
|
||||
}
|
||||
result = catalog_result(lardon3d_image_catalog_publish_asset_file(state, staging, created_at,
|
||||
kMaxSourceBytes, &output->derived_asset));
|
||||
(void)unlink(staging);
|
||||
if (result != LARDON3D_RAW_DEVELOPMENT_OK) return result;
|
||||
Lardon3DProjectDbAssetDerivation derivation = {};
|
||||
derivation.parent_asset_id = output->source_asset.asset_id; derivation.child_asset_id = output->derived_asset.asset_id;
|
||||
derivation.kind = LARDON3D_DB_ASSET_DERIVATION_GENERIC_VERSIONED; derivation.version = 1;
|
||||
std::memcpy(derivation.parameter_fingerprint, output->parameter_fingerprint, sizeof(derivation.parameter_fingerprint));
|
||||
derivation.has_producer_task = producer_task_id != 0; derivation.producer_task_id = producer_task_id;
|
||||
derivation.created_at = created_at;
|
||||
result = ensure_derivation(state->project_db, derivation);
|
||||
if (result != LARDON3D_RAW_DEVELOPMENT_OK) return result;
|
||||
char name[LARDON3D_PROJECT_DB_IMAGE_NAME_CAPACITY];
|
||||
if (!valid_name(source_arw_path, name)) return LARDON3D_RAW_DEVELOPMENT_INVALID_ARGUMENT;
|
||||
Lardon3DProjectDbImageRegisterStatus status;
|
||||
result = db_result(lardon3d_project_db_publish_derived_capture_image(state->project_db, capture_id,
|
||||
output->derived_asset.asset_id, name, source_arw_path, producer_task_id, created_at, &status, &output->image));
|
||||
if (result != LARDON3D_RAW_DEVELOPMENT_OK) return result;
|
||||
std::snprintf(output->libraw_version, sizeof(output->libraw_version), "%s", libraw_version());
|
||||
std::snprintf(output->png_encoder_version, sizeof(output->png_encoder_version), "%s", CV_VERSION);
|
||||
return LARDON3D_RAW_DEVELOPMENT_OK;
|
||||
} catch (const std::bad_alloc &) { return LARDON3D_RAW_DEVELOPMENT_OUT_OF_MEMORY; }
|
||||
catch (...) { return LARDON3D_RAW_DEVELOPMENT_INTERNAL_ERROR; }
|
||||
}
|
||||
|
|
@ -12,6 +12,9 @@
|
|||
|
||||
#include <lardon3d/image_catalog.h>
|
||||
|
||||
Lardon3DImageCatalogAssetPublishResult lardon3d_image_catalog_test_copy_hash(
|
||||
int input, int output);
|
||||
|
||||
#define CHECK(condition) do { if (!(condition)) { \
|
||||
(void)fprintf(stderr, "Échec ligne %d : %s\n", __LINE__, #condition); return false; \
|
||||
} } while (0)
|
||||
|
|
@ -139,12 +142,27 @@ run_test(void)
|
|||
{
|
||||
char root[] = "/tmp/lardon3d-catalog-v1-XXXXXX";
|
||||
CHECK(mkdtemp(root));
|
||||
char database_path[PATH_MAX], source_a[PATH_MAX], source_b[PATH_MAX];
|
||||
char database_path[PATH_MAX], source_a[PATH_MAX], source_b[PATH_MAX], copied_path[PATH_MAX];
|
||||
CHECK(join_path(database_path, root, "project.db"));
|
||||
CHECK(join_path(source_a, root, "photo001.jpg"));
|
||||
CHECK(join_path(source_b, root, "other-name.jpg"));
|
||||
CHECK(join_path(copied_path, root, "copy.bin"));
|
||||
CHECK(write_file(source_a, "same-image-content"));
|
||||
CHECK(write_file(source_b, "same-image-content"));
|
||||
int copy_source = open(source_a, O_RDONLY);
|
||||
int copy_destination = open(copied_path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
CHECK(copy_source >= 0 && copy_destination >= 0);
|
||||
CHECK(lardon3d_image_catalog_test_copy_hash(copy_source, copy_destination)
|
||||
== LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED);
|
||||
CHECK(close(copy_source) == 0 && close(copy_destination) == 0);
|
||||
struct stat full_information;
|
||||
CHECK(stat("/dev/full", &full_information) == 0 && S_ISCHR(full_information.st_mode));
|
||||
copy_source = open(source_a, O_RDONLY);
|
||||
copy_destination = open("/dev/full", O_WRONLY);
|
||||
CHECK(copy_source >= 0 && copy_destination >= 0);
|
||||
CHECK(lardon3d_image_catalog_test_copy_hash(copy_source, copy_destination)
|
||||
== LARDON3D_IMAGE_CATALOG_ASSET_PUBLICATION_ERROR);
|
||||
CHECK(close(copy_source) == 0 && close(copy_destination) == 0);
|
||||
Lardon3DProjectDb *database = NULL;
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
CHECK(lardon3d_project_db_open(database_path, &database, error)
|
||||
|
|
|
|||
300
tests/test_raw_development.cpp
Normal file
300
tests/test_raw_development.cpp
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" {
|
||||
#include <lardon3d/image_catalog.h>
|
||||
}
|
||||
#include <lardon3d/raw_development.h>
|
||||
|
||||
extern "C" size_t lardon3d_raw_development_test_policy_payload_size(void);
|
||||
extern "C" Lardon3DRawDevelopmentResult lardon3d_raw_development_test_ensure_derivation(
|
||||
Lardon3DProjectDb *database, const Lardon3DProjectDbAssetDerivation *expected);
|
||||
extern "C" Lardon3DRawDevelopmentResult
|
||||
lardon3d_raw_development_test_ensure_capture_asset_role(
|
||||
Lardon3DProjectDb *database, uint64_t capture_id, uint64_t asset_id,
|
||||
Lardon3DProjectDbCaptureAssetRole expected_role);
|
||||
|
||||
#define CHECK(condition) do { if (!(condition)) { \
|
||||
std::fprintf(stderr, "Failure line %d: %s\\n", __LINE__, #condition); return false; \
|
||||
} } while (0)
|
||||
|
||||
static bool write_file(const char *path, const char *text) {
|
||||
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) return false;
|
||||
size_t size = std::strlen(text);
|
||||
bool ok = write(fd, text, size) == (ssize_t)size;
|
||||
return close(fd) == 0 && ok;
|
||||
}
|
||||
|
||||
static bool remove_tree(const char *path) {
|
||||
struct stat info;
|
||||
if (lstat(path, &info) != 0) return errno == ENOENT;
|
||||
if (!S_ISDIR(info.st_mode)) return unlink(path) == 0;
|
||||
DIR *directory = opendir(path);
|
||||
if (!directory) return false;
|
||||
bool ok = true;
|
||||
for (dirent *entry = readdir(directory); entry; entry = readdir(directory)) {
|
||||
if (std::strcmp(entry->d_name, ".") == 0 || std::strcmp(entry->d_name, "..") == 0) continue;
|
||||
char child[4096];
|
||||
int count = std::snprintf(child, sizeof(child), "%s/%s", path, entry->d_name);
|
||||
if (count <= 0 || count >= (int)sizeof(child) || !remove_tree(child)) ok = false;
|
||||
}
|
||||
return closedir(directory) == 0 && rmdir(path) == 0 && ok;
|
||||
}
|
||||
|
||||
static void print_hex(const char *key, const unsigned char value[32]) {
|
||||
static const char digits[] = "0123456789abcdef";
|
||||
char text[65];
|
||||
for (size_t index = 0; index < 32; ++index) {
|
||||
text[index * 2] = digits[value[index] >> 4];
|
||||
text[index * 2 + 1] = digits[value[index] & 15U];
|
||||
}
|
||||
text[64] = '\0';
|
||||
std::printf("%s=%s\n", key, text);
|
||||
}
|
||||
|
||||
static bool capture_has_asset(Lardon3DProjectDb *db, uint64_t capture_id,
|
||||
uint64_t asset_id, Lardon3DProjectDbCaptureAssetRole role) {
|
||||
Lardon3DProjectDbCaptureAsset assets[4];
|
||||
size_t count = 0;
|
||||
if (lardon3d_project_db_list_capture_assets(db, capture_id, 0, assets, 4, &count)
|
||||
!= LARDON3D_PROJECT_DB_OK) return false;
|
||||
for (size_t index = 0; index < count; ++index) {
|
||||
if (assets[index].asset_id == asset_id && assets[index].role == role) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool run_golden(Lardon3DAppState *state, Lardon3DProjectDb *db,
|
||||
const char *golden_path, bool keep, const char *root) {
|
||||
Lardon3DProjectDbScanSet scanset;
|
||||
CHECK(lardon3d_project_db_create_scanset(db, "A6000 golden", &scanset)
|
||||
== LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DProjectDbCapture capture;
|
||||
CHECK(lardon3d_project_db_create_capture(db, scanset.scanset_id, 2, &capture)
|
||||
== LARDON3D_PROJECT_DB_OK);
|
||||
|
||||
Lardon3DRawDevelopmentOutput first;
|
||||
Lardon3DRawDevelopmentResult first_result = lardon3d_raw_develop_to_capture(
|
||||
state, capture.capture_id, golden_path, 0, 2, &first);
|
||||
std::fprintf(stderr, "GOLDEN_FIRST_RESULT=%d\n", (int)first_result);
|
||||
CHECK(first_result == LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(first.source_asset.asset_id != 0 && first.derived_asset.asset_id != 0
|
||||
&& first.image.image_id != 0);
|
||||
CHECK(first.source_asset.asset_id != first.derived_asset.asset_id);
|
||||
Lardon3DProjectDbCapture image_capture;
|
||||
CHECK(lardon3d_project_db_find_capture_for_image(db, first.image.image_id, &image_capture)
|
||||
== LARDON3D_PROJECT_DB_OK && image_capture.capture_id == capture.capture_id);
|
||||
CHECK(capture_has_asset(db, capture.capture_id, first.source_asset.asset_id,
|
||||
LARDON3D_DB_CAPTURE_ASSET_SOURCE));
|
||||
CHECK(capture_has_asset(db, capture.capture_id, first.derived_asset.asset_id,
|
||||
LARDON3D_DB_CAPTURE_ASSET_DERIVED));
|
||||
Lardon3DProjectDbAssetDerivation derivation;
|
||||
CHECK(lardon3d_project_db_load_asset_derivation(db, first.derived_asset.asset_id, &derivation)
|
||||
== LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(derivation.parent_asset_id == first.source_asset.asset_id
|
||||
&& derivation.child_asset_id == first.derived_asset.asset_id);
|
||||
CHECK(std::memcmp(derivation.parameter_fingerprint, first.parameter_fingerprint,
|
||||
sizeof(first.parameter_fingerprint)) == 0);
|
||||
uint64_t selected = 0;
|
||||
CHECK(lardon3d_project_db_get_selected_capture_image(db, capture.capture_id, &selected)
|
||||
== LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
CHECK(first.width > 0 && first.height > 0 && first.width <= 16384 && first.height <= 16384
|
||||
&& (uint64_t)first.width * first.height <= 40000000U);
|
||||
for (size_t index = 0; index < 4; ++index)
|
||||
CHECK(std::isfinite(first.resolved_camera_wb[index]) && first.resolved_camera_wb[index] > 0.0F);
|
||||
|
||||
char png_path[4096];
|
||||
CHECK(std::snprintf(png_path, sizeof(png_path), "%s/%s", state->project_path,
|
||||
first.derived_asset.path) > 0);
|
||||
struct stat png_info;
|
||||
CHECK(stat(png_path, &png_info) == 0 && S_ISREG(png_info.st_mode) && png_info.st_size > 0);
|
||||
|
||||
std::printf("GOLDEN_WIDTH=%u\n", first.width);
|
||||
std::printf("GOLDEN_HEIGHT=%u\n", first.height);
|
||||
for (size_t index = 0; index < 4; ++index)
|
||||
std::printf("GOLDEN_WB_%zu=%.9g\n", index, first.resolved_camera_wb[index]);
|
||||
print_hex("GOLDEN_SOURCE_SHA256", first.source_asset.sha256);
|
||||
print_hex("GOLDEN_DERIVED_SHA256", first.derived_asset.sha256);
|
||||
print_hex("GOLDEN_PARAMETER_FINGERPRINT", first.parameter_fingerprint);
|
||||
std::printf("GOLDEN_SOURCE_ASSET_ID=%llu\n",
|
||||
(unsigned long long)first.source_asset.asset_id);
|
||||
std::printf("GOLDEN_DERIVED_ASSET_ID=%llu\n",
|
||||
(unsigned long long)first.derived_asset.asset_id);
|
||||
std::printf("GOLDEN_IMAGE_ID=%llu\n", (unsigned long long)first.image.image_id);
|
||||
std::printf("GOLDEN_LIBRAW=%s\n", first.libraw_version);
|
||||
std::printf("GOLDEN_PNG_ENCODER=%s\n", first.png_encoder_version);
|
||||
std::printf("GOLDEN_PNG_PATH=%s\n", png_path);
|
||||
std::printf("GOLDEN_PNG_BYTES=%llu\n", (unsigned long long)png_info.st_size);
|
||||
|
||||
Lardon3DRawDevelopmentOutput second;
|
||||
Lardon3DRawDevelopmentResult second_result = lardon3d_raw_develop_to_capture(
|
||||
state, capture.capture_id, golden_path, 0, 2, &second);
|
||||
std::fprintf(stderr, "GOLDEN_SECOND_RESULT=%d\n", (int)second_result);
|
||||
CHECK(second_result == LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(second.source_asset.asset_id == first.source_asset.asset_id
|
||||
&& second.derived_asset.asset_id == first.derived_asset.asset_id
|
||||
&& second.image.image_id == first.image.image_id
|
||||
&& std::memcmp(second.source_asset.sha256, first.source_asset.sha256, 32) == 0
|
||||
&& std::memcmp(second.derived_asset.sha256, first.derived_asset.sha256, 32) == 0
|
||||
&& std::memcmp(second.parameter_fingerprint, first.parameter_fingerprint, 32) == 0
|
||||
&& second.width == first.width && second.height == first.height
|
||||
&& std::memcmp(second.resolved_camera_wb, first.resolved_camera_wb,
|
||||
sizeof(first.resolved_camera_wb)) == 0);
|
||||
uint64_t image_count = 0;
|
||||
CHECK(lardon3d_project_db_count_images(db, scanset.scanset_id, &image_count)
|
||||
== LARDON3D_PROJECT_DB_OK && image_count == 1);
|
||||
CHECK(lardon3d_project_db_get_selected_capture_image(db, capture.capture_id, &selected)
|
||||
== LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
if (keep) std::printf("GOLDEN_PROJECT_PATH=%s\n", root);
|
||||
std::printf("GOLDEN_A6000_RESULT=PASS\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool run_test() {
|
||||
float wb_a[4] = {2.0F, 1.0F, 1.0F, 1.5F};
|
||||
float wb_b[4] = {2.1F, 1.0F, 1.0F, 1.5F};
|
||||
unsigned char first[32], second[32], changed[32];
|
||||
CHECK(lardon3d_raw_development_policy_fingerprint(wb_a, first) == LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(lardon3d_raw_development_policy_fingerprint(wb_a, second) == LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(lardon3d_raw_development_policy_fingerprint(wb_b, changed) == LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(std::memcmp(first, second, sizeof(first)) == 0);
|
||||
CHECK(std::memcmp(first, changed, sizeof(first)) != 0);
|
||||
CHECK(lardon3d_raw_development_test_policy_payload_size() == 200);
|
||||
CHECK(lardon3d_raw_development_policy_fingerprint(nullptr, first) == LARDON3D_RAW_DEVELOPMENT_INVALID_ARGUMENT);
|
||||
|
||||
char root[] = "/tmp/lardon3d-raw-XXXXXX";
|
||||
CHECK(mkdtemp(root) != nullptr);
|
||||
char database_path[4096], corrupt_path[4096], link_path[4096], parent_path[4096], child_path[4096],
|
||||
other_parent_path[4096];
|
||||
CHECK(std::snprintf(database_path, sizeof(database_path), "%s/project.db", root) > 0);
|
||||
CHECK(std::snprintf(corrupt_path, sizeof(corrupt_path), "%s/not-arw.bin", root) > 0);
|
||||
CHECK(std::snprintf(link_path, sizeof(link_path), "%s/link.arw", root) > 0);
|
||||
CHECK(std::snprintf(parent_path, sizeof(parent_path), "%s/parent.bin", root) > 0);
|
||||
CHECK(std::snprintf(child_path, sizeof(child_path), "%s/child.bin", root) > 0);
|
||||
CHECK(std::snprintf(other_parent_path, sizeof(other_parent_path), "%s/other.bin", root) > 0);
|
||||
CHECK(write_file(corrupt_path, "not a raw image"));
|
||||
CHECK(write_file(parent_path, "parent"));
|
||||
CHECK(write_file(child_path, "child"));
|
||||
CHECK(write_file(other_parent_path, "other"));
|
||||
Lardon3DProjectDb *db = nullptr;
|
||||
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
|
||||
CHECK(lardon3d_project_db_open(database_path, &db, error) == LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DAppState state; lardon3d_app_state_init(&state);
|
||||
state.project_loaded = true; state.project_db = db;
|
||||
CHECK(std::snprintf(state.project_path, sizeof(state.project_path), "%s", root) > 0);
|
||||
Lardon3DProjectDbImageAsset parent_asset, child_asset, other_parent_asset;
|
||||
CHECK(lardon3d_image_catalog_publish_asset_file(&state, parent_path, 1, UINT64_MAX,
|
||||
&parent_asset) == LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED);
|
||||
CHECK(lardon3d_image_catalog_publish_asset_file(&state, child_path, 1, UINT64_MAX,
|
||||
&child_asset) == LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED);
|
||||
CHECK(lardon3d_image_catalog_publish_asset_file(&state, other_parent_path, 1, UINT64_MAX,
|
||||
&other_parent_asset) == LARDON3D_IMAGE_CATALOG_ASSET_PUBLISHED);
|
||||
Lardon3DProjectDbAssetDerivation expected = {};
|
||||
expected.parent_asset_id = parent_asset.asset_id;
|
||||
expected.child_asset_id = child_asset.asset_id;
|
||||
expected.kind = LARDON3D_DB_ASSET_DERIVATION_GENERIC_VERSIONED;
|
||||
expected.version = 1;
|
||||
std::memset(expected.parameter_fingerprint, 0x5a, sizeof(expected.parameter_fingerprint));
|
||||
expected.created_at = 5;
|
||||
Lardon3DProjectDbAssetDerivation stored;
|
||||
CHECK(lardon3d_project_db_load_asset_derivation(db, child_asset.asset_id, &stored)
|
||||
== LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
CHECK(lardon3d_raw_development_test_ensure_derivation(db, &expected)
|
||||
== LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(lardon3d_project_db_load_asset_derivation(db, child_asset.asset_id, &stored)
|
||||
== LARDON3D_PROJECT_DB_OK && stored.parent_asset_id == parent_asset.asset_id
|
||||
&& stored.child_asset_id == child_asset.asset_id && stored.kind == expected.kind
|
||||
&& stored.version == expected.version && !stored.has_producer_task
|
||||
&& stored.created_at == expected.created_at
|
||||
&& std::memcmp(stored.parameter_fingerprint, expected.parameter_fingerprint, 32) == 0);
|
||||
Lardon3DProjectDbAssetDerivation retry = expected;
|
||||
retry.has_producer_task = true;
|
||||
retry.producer_task_id = 1;
|
||||
retry.created_at = 9;
|
||||
CHECK(lardon3d_raw_development_test_ensure_derivation(db, &retry)
|
||||
== LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(lardon3d_project_db_load_asset_derivation(db, child_asset.asset_id, &stored)
|
||||
== LARDON3D_PROJECT_DB_OK && !stored.has_producer_task && stored.created_at == 5);
|
||||
retry.parent_asset_id = other_parent_asset.asset_id;
|
||||
CHECK(lardon3d_raw_development_test_ensure_derivation(db, &retry)
|
||||
== LARDON3D_RAW_DEVELOPMENT_CONSTRAINT);
|
||||
retry.parent_asset_id = parent_asset.asset_id;
|
||||
retry.parameter_fingerprint[0] ^= 1U;
|
||||
CHECK(lardon3d_raw_development_test_ensure_derivation(db, &retry)
|
||||
== LARDON3D_RAW_DEVELOPMENT_CONSTRAINT);
|
||||
CHECK(lardon3d_project_db_load_asset_derivation(db, child_asset.asset_id, &stored)
|
||||
== LARDON3D_PROJECT_DB_OK && stored.parent_asset_id == parent_asset.asset_id
|
||||
&& std::memcmp(stored.parameter_fingerprint, expected.parameter_fingerprint, 32) == 0);
|
||||
Lardon3DProjectDbScanSet attachment_scanset;
|
||||
CHECK(lardon3d_project_db_create_scanset(db, "Attachment", &attachment_scanset)
|
||||
== LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DProjectDbCapture attachment_capture;
|
||||
CHECK(lardon3d_project_db_create_capture(db, attachment_scanset.scanset_id, 1,
|
||||
&attachment_capture) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_raw_development_test_ensure_capture_asset_role(db,
|
||||
attachment_capture.capture_id, parent_asset.asset_id, LARDON3D_DB_CAPTURE_ASSET_SOURCE)
|
||||
== LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(capture_has_asset(db, attachment_capture.capture_id, parent_asset.asset_id,
|
||||
LARDON3D_DB_CAPTURE_ASSET_SOURCE));
|
||||
CHECK(lardon3d_raw_development_test_ensure_capture_asset_role(db,
|
||||
attachment_capture.capture_id, parent_asset.asset_id, LARDON3D_DB_CAPTURE_ASSET_SOURCE)
|
||||
== LARDON3D_RAW_DEVELOPMENT_OK);
|
||||
CHECK(lardon3d_project_db_attach_capture_asset(db, attachment_capture.capture_id,
|
||||
other_parent_asset.asset_id, LARDON3D_DB_CAPTURE_ASSET_SOURCE) == LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DProjectDbCaptureAsset attachment_page[4];
|
||||
size_t attachment_count = 0;
|
||||
CHECK(lardon3d_project_db_list_capture_assets(db, attachment_capture.capture_id, 0,
|
||||
attachment_page, 4, &attachment_count) == LARDON3D_PROJECT_DB_OK
|
||||
&& attachment_count == 2);
|
||||
Lardon3DProjectDbCapture conflict_capture;
|
||||
CHECK(lardon3d_project_db_create_capture(db, attachment_scanset.scanset_id, 2,
|
||||
&conflict_capture) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_project_db_attach_capture_asset(db, conflict_capture.capture_id,
|
||||
child_asset.asset_id, LARDON3D_DB_CAPTURE_ASSET_DERIVED) == LARDON3D_PROJECT_DB_OK);
|
||||
CHECK(lardon3d_raw_development_test_ensure_capture_asset_role(db,
|
||||
conflict_capture.capture_id, child_asset.asset_id, LARDON3D_DB_CAPTURE_ASSET_SOURCE)
|
||||
== LARDON3D_RAW_DEVELOPMENT_CONSTRAINT);
|
||||
CHECK(capture_has_asset(db, conflict_capture.capture_id, child_asset.asset_id,
|
||||
LARDON3D_DB_CAPTURE_ASSET_DERIVED));
|
||||
Lardon3DProjectDbImageAsset rejected_asset;
|
||||
CHECK(lardon3d_image_catalog_publish_asset_file(&state, corrupt_path, 1, 1,
|
||||
&rejected_asset) == LARDON3D_IMAGE_CATALOG_ASSET_TOO_LARGE);
|
||||
CHECK(rejected_asset.asset_id == 0);
|
||||
Lardon3DProjectDbScanSet scanset;
|
||||
CHECK(lardon3d_project_db_create_scanset(db, "RAW", &scanset) == LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DProjectDbCapture capture;
|
||||
CHECK(lardon3d_project_db_create_capture(db, scanset.scanset_id, 1, &capture) == LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DRawDevelopmentOutput output;
|
||||
CHECK(lardon3d_raw_develop_to_capture(&state, capture.capture_id, "/missing/file.arw", 0, 1, &output)
|
||||
== LARDON3D_RAW_DEVELOPMENT_SOURCE_NOT_FOUND);
|
||||
CHECK(symlink(corrupt_path, link_path) == 0);
|
||||
CHECK(lardon3d_raw_develop_to_capture(&state, capture.capture_id, link_path, 0, 1, &output)
|
||||
== LARDON3D_RAW_DEVELOPMENT_SOURCE_NOT_FOUND);
|
||||
Lardon3DRawDevelopmentResult corrupt = lardon3d_raw_develop_to_capture(
|
||||
&state, capture.capture_id, corrupt_path, 0, 1, &output);
|
||||
CHECK(corrupt == LARDON3D_RAW_DEVELOPMENT_UNSUPPORTED_RAW
|
||||
|| corrupt == LARDON3D_RAW_DEVELOPMENT_CORRUPT_RAW);
|
||||
uint64_t selected = 0;
|
||||
CHECK(lardon3d_project_db_get_selected_capture_image(db, capture.capture_id, &selected)
|
||||
== LARDON3D_PROJECT_DB_NOT_FOUND);
|
||||
uint64_t count = 1;
|
||||
CHECK(lardon3d_project_db_count_images(db, scanset.scanset_id, &count) == LARDON3D_PROJECT_DB_OK && count == 0);
|
||||
const char *golden_path = std::getenv("LARDON3D_RAW_GOLDEN");
|
||||
bool golden = golden_path && golden_path[0];
|
||||
const char *keep_value = std::getenv("LARDON3D_RAW_GOLDEN_KEEP");
|
||||
bool keep = golden && keep_value && std::strcmp(keep_value, "1") == 0;
|
||||
if (golden) CHECK(run_golden(&state, db, golden_path, keep, root));
|
||||
lardon3d_project_db_close(db);
|
||||
return keep || remove_tree(root);
|
||||
}
|
||||
|
||||
int main() { return run_test() ? 0 : 1; }
|
||||
Loading…
Reference in a new issue