diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f452f5bbc4..5dee725158 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_BLACKBOX_TESTS=ON -DBUILD_UNIT_TESTS=ON -DBUILD_PYTHON_MODULE=ON + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_BLACKBOX_TESTS=ON -DBUILD_UNIT_TESTS=ON -DBUILD_PYTHON_MODULE=ON -DBUILD_C_API=ON - name: Build working-directory: ${{runner.workspace}}/build @@ -64,7 +64,7 @@ jobs: run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure - run: cmake -S $GITHUB_WORKSPACE -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_BLACKBOX_TESTS=ON -DBUILD_UNIT_TESTS=ON -DBUILD_PYTHON_MODULE=OFF -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS="-march=native -fsanitize=address,undefined -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer" + run: cmake -S $GITHUB_WORKSPACE -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_BLACKBOX_TESTS=ON -DBUILD_UNIT_TESTS=ON -DBUILD_PYTHON_MODULE=OFF -DBUILD_C_API=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS="-march=native -fsanitize=address,undefined -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer" - name: Build run: cmake --build ${{runner.workspace}}/build -j8 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6352a0bc92..299a72a01a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ option (BUILD_EXAMPLES "Build the example barcode reader/writer applications" ON option (BUILD_BLACKBOX_TESTS "Build the black box reader/writer tests" OFF) option (BUILD_UNIT_TESTS "Build the unit tests (don't enable for production builds)" OFF) option (BUILD_PYTHON_MODULE "Build the python module" OFF) +option (BUILD_C_API "Build the C-API" OFF) set(BUILD_DEPENDENCIES "AUTO" CACHE STRING "Fetch from github or use locally installed (AUTO/GITHUB/LOCAL)") if (WIN32) @@ -76,3 +77,7 @@ endif() if (BUILD_PYTHON_MODULE) add_subdirectory (wrappers/python) endif() + +if (BUILD_C_API) + add_subdirectory (wrappers/c) +endif() diff --git a/wrappers/c/CMakeLists.txt b/wrappers/c/CMakeLists.txt new file mode 100644 index 0000000000..38a817e79e --- /dev/null +++ b/wrappers/c/CMakeLists.txt @@ -0,0 +1,7 @@ +zxing_add_package_stb() + +if (BUILD_READERS) + add_executable (zxing-c-test zxing-c.cpp zxing-c-test.c) + target_link_libraries (zxing-c-test ZXing::ZXing stb::stb) + add_test(NAME zxing-c-test COMMAND zxing-c-test ${CMAKE_SOURCE_DIR}/test/samples/qrcode-1/1.png) +endif() diff --git a/wrappers/c/zxing-c-test.c b/wrappers/c/zxing-c-test.c new file mode 100644 index 0000000000..24d4457d66 --- /dev/null +++ b/wrappers/c/zxing-c-test.c @@ -0,0 +1,93 @@ +/* +* Copyright 2023 siiky +* Copyright 2023 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "zxing-c.h" + +#define STB_IMAGE_IMPLEMENTATION +#include + +int usage(char* pname) +{ + fprintf(stderr, "Usage: %s FILE [FORMATS]\n", pname); + return 1; +} + +bool parse_args(int argc, char** argv, char** filename, zxing_BarcodeFormats* formats) +{ + if (argc < 2) + return false; + *filename = argv[1]; + if (argc >= 3) { + *formats = zxing_BarcodeFormatsFromString(argv[2]); + if (*formats == zxing_BarcodeFormat_Invalid) { + fprintf(stderr, "Invalid barcode formats string '%s'\n", argv[2]); + return false; + } + } + return true; +} + +void printF(const char* fmt, char* text) +{ + if (!text) + return; + if (*text) + printf(fmt, text); + free(text); +} + +int main(int argc, char** argv) +{ + char* filename = NULL; + zxing_BarcodeFormats formats = zxing_BarcodeFormat_None; + + if (!parse_args(argc, argv, &filename, &formats)) + return usage(argv[0]); + + int width = 0; + int height = 0; + int channels = 0; + stbi_uc* data = stbi_load(filename, &width, &height, &channels, STBI_grey); + if (!data) + return 2; + + zxing_DecodeHints* hints = zxing_DecodeHints_new(); + zxing_DecodeHints_setTextMode(hints, zxing_TextMode_HRI); + zxing_DecodeHints_setEanAddOnSymbol(hints, zxing_EanAddOnSymbol_Ignore); + zxing_DecodeHints_setFormats(hints, formats); + zxing_DecodeHints_setReturnErrors(hints, true); + + zxing_ImageView* iv = zxing_ImageView_new(data, width, height, zxing_ImageFormat_Lum, 0, 0); + + zxing_Results* results = zxing_ReadBarcodes(iv, hints); + + if (results) { + for (int i = 0, n = zxing_Results_size(results); i < n; ++i) { + const zxing_Result* result = zxing_Results_at(results, i); + + printF("Text : %s\n", zxing_Result_text(result)); + printF("Format : %s\n", zxing_BarcodeFormatToString(zxing_Result_format(result))); + printF("Content : %s\n", zxing_ContentTypeToString(zxing_Result_contentType(result))); + printF("Identifier : %s\n", zxing_Result_symbologyIdentifier(result)); + printF("EC Level : %s\n", zxing_Result_ecLevel(result)); + printF("Error : %s\n", zxing_Result_errorMsg(result)); + printf("Rotation : %d\n", zxing_Result_orientation(result)); + + if (i < n-1) + printf("\n"); + } + + zxing_Results_delete(results); + } else { + printf("No barcode found\n"); + } + + zxing_ImageView_delete(iv); + zxing_DecodeHints_delete(hints); + stbi_image_free(data); + + return 0; +} diff --git a/wrappers/c/zxing-c.cpp b/wrappers/c/zxing-c.cpp new file mode 100644 index 0000000000..d368112638 --- /dev/null +++ b/wrappers/c/zxing-c.cpp @@ -0,0 +1,241 @@ +/* +* Copyright 2023 siiky +* Copyright 2023 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "zxing-c.h" + +#include "ReadBarcode.h" + +using namespace ZXing; + +char* copy(std::string_view sv) +{ + auto ret = (char*)malloc(sv.size() + 1); + if (ret) { + strncpy(ret, sv.data(), sv.size()); + ret[sv.size()] = '\0'; + } + return ret; +} + +extern "C" +{ + /* + * ZXing/ImageView.h + */ + + zxing_ImageView* zxing_ImageView_new(const uint8_t* data, int width, int height, zxing_ImageFormat format, int rowStride, + int pixStride) + { + ImageFormat cppformat = static_cast(format); + return new ImageView(data, width, height, cppformat, rowStride, pixStride); + } + + void zxing_ImageView_delete(zxing_ImageView* iv) + { + delete iv; + } + + /* + * ZXing/BarcodeFormat.h + */ + + zxing_BarcodeFormats zxing_BarcodeFormatsFromString(const char* str) + { + if (!str) + return {}; + try { + auto format = BarcodeFormatsFromString(str); + return static_cast(*reinterpret_cast(&format)); + } catch (...) { + return zxing_BarcodeFormat_Invalid; + } + } + + zxing_BarcodeFormat zxing_BarcodeFormatFromString(const char* str) + { + zxing_BarcodeFormat res = zxing_BarcodeFormatsFromString(str); + return BitHacks::CountBitsSet(res) == 1 ? res : zxing_BarcodeFormat_Invalid; + } + + char* zxing_BarcodeFormatToString(zxing_BarcodeFormat format) + { + return copy(ToString(static_cast(format))); + } + + /* + * ZXing/DecodeHints.h + */ + + zxing_DecodeHints* zxing_DecodeHints_new() + { + return new DecodeHints(); + } + + void zxing_DecodeHints_delete(zxing_DecodeHints* hints) + { + delete hints; + } + + void zxing_DecodeHints_setTryHarder(zxing_DecodeHints* hints, bool tryHarder) + { + hints->setTryHarder(tryHarder); + } + + void zxing_DecodeHints_setTryRotate(zxing_DecodeHints* hints, bool tryRotate) + { + hints->setTryRotate(tryRotate); + } + + void zxing_DecodeHints_setTryInvert(zxing_DecodeHints* hints, bool tryInvert) + { + hints->setTryInvert(tryInvert); + } + + void zxing_DecodeHints_setTryDownscale(zxing_DecodeHints* hints, bool tryDownscale) + { + hints->setTryDownscale(tryDownscale); + } + + void zxing_DecodeHints_setIsPure(zxing_DecodeHints* hints, bool isPure) + { + hints->setIsPure(isPure); + } + + void zxing_DecodeHints_setReturnErrors(zxing_DecodeHints* hints, bool returnErrors) + { + hints->setReturnErrors(returnErrors); + } + + void zxing_DecodeHints_setFormats(zxing_DecodeHints* hints, zxing_BarcodeFormats formats) + { + hints->setFormats(static_cast(formats)); + } + + void zxing_DecodeHints_setBinarizer(zxing_DecodeHints* hints, zxing_Binarizer binarizer) + { + hints->setBinarizer(static_cast(binarizer)); + } + + void zxing_DecodeHints_setEanAddOnSymbol(zxing_DecodeHints* hints, zxing_EanAddOnSymbol eanAddOnSymbol) + { + hints->setEanAddOnSymbol(static_cast(eanAddOnSymbol)); + } + + void zxing_DecodeHints_setTextMode(zxing_DecodeHints* hints, zxing_TextMode textMode) + { + hints->setTextMode(static_cast(textMode)); + } + + /* + * ZXing/Result.h + */ + + char* zxing_ContentTypeToString(zxing_ContentType type) + { + return copy(ToString(static_cast(type))); + } + + bool zxing_Result_isValid(const zxing_Result* result) + { + return result->isValid(); + } + + char* zxing_Result_errorMsg(const zxing_Result* result) + { + return copy(ToString(result->error())); + } + + zxing_BarcodeFormat zxing_Result_format(const zxing_Result* result) + { + return static_cast(result->format()); + } + + zxing_ContentType zxing_Result_contentType(const zxing_Result* result) + { + return static_cast(result->contentType()); + } + + uint8_t* zxing_Result_bytes(const zxing_Result* result, int* len) + { + *len = Size(result->bytes()); + + auto ret = (uint8_t*)malloc(*len + 1); + if (ret) + memcpy(ret, result->bytes().data(), *len); + else + *len = 0; + + return ret; + } + + char* zxing_Result_text(const zxing_Result* result) + { + return copy(result->text()); + } + + char* zxing_Result_ecLevel(const zxing_Result* result) + { + return copy(result->ecLevel()); + } + + char* zxing_Result_symbologyIdentifier(const zxing_Result* result) + { + return copy(result->symbologyIdentifier()); + } + + int zxing_Result_orientation(const zxing_Result* result) + { + return result->orientation(); + } + + bool zxing_Result_isInverted(const zxing_Result* result) + { + return result->isInverted(); + } + + bool zxing_Result_isMirrored(const zxing_Result* result) + { + return result->isMirrored(); + } + + /* + * ZXing/ReadBarcode.h + */ + + zxing_Result* zxing_ReadBarcode(const zxing_ImageView* iv, const zxing_DecodeHints* hints) + { + auto res = ReadBarcode(*iv, *hints); + return res.format() != BarcodeFormat::None ? new Result(std::move(res)) : NULL; + } + + zxing_Results* zxing_ReadBarcodes(const zxing_ImageView* iv, const zxing_DecodeHints* hints) + { + auto res = ReadBarcodes(*iv, *hints); + return !res.empty() ? new Results(std::move(res)) : NULL; + } + + void zxing_Result_delete(zxing_Result* result) + { + delete result; + } + + void zxing_Results_delete(zxing_Results* results) + { + delete results; + } + + int zxing_Results_size(const zxing_Results* results) + { + return Size(*results); + } + + const zxing_Result* zxing_Results_at(const zxing_Results* results, int i) + { + if (i < 0 || i >= Size(*results)) + return NULL; + return &(*results)[i]; + } +} diff --git a/wrappers/c/zxing-c.h b/wrappers/c/zxing-c.h new file mode 100644 index 0000000000..daded89240 --- /dev/null +++ b/wrappers/c/zxing-c.h @@ -0,0 +1,184 @@ +/* +* Copyright 2023 siiky +* Copyright 2023 Axel Waggershauser +*/ +// SPDX-License-Identifier: Apache-2.0 + +#ifndef _ZXING_C_H +#define _ZXING_C_H + +#include +#include + +#ifdef __cplusplus + +#include "DecodeHints.h" +#include "ImageView.h" +#include "Result.h" + +typedef ZXing::ImageView zxing_ImageView; +typedef ZXing::DecodeHints zxing_DecodeHints; +typedef ZXing::Result zxing_Result; +typedef ZXing::Results zxing_Results; + +extern "C" +{ +#else + +typedef struct zxing_ImageView zxing_ImageView; +typedef struct zxing_DecodeHints zxing_DecodeHints; +typedef struct zxing_Result zxing_Result; +typedef struct zxing_Results zxing_Results; + +#endif + + /* + * ZXing/ImageView.h + */ + + typedef enum + { + zxing_ImageFormat_None = 0, + zxing_ImageFormat_Lum = 0x01000000, + zxing_ImageFormat_RGB = 0x03000102, + zxing_ImageFormat_BGR = 0x03020100, + zxing_ImageFormat_RGBX = 0x04000102, + zxing_ImageFormat_XRGB = 0x04010203, + zxing_ImageFormat_BGRX = 0x04020100, + zxing_ImageFormat_XBGR = 0x04030201, + } zxing_ImageFormat; + + zxing_ImageView* zxing_ImageView_new(const uint8_t* data, int width, int height, zxing_ImageFormat format, int rowStride, + int pixStride); + void zxing_ImageView_delete(zxing_ImageView* iv); + + /* + * ZXing/BarcodeFormat.h + */ + + typedef enum + { + zxing_BarcodeFormat_None = 0, + zxing_BarcodeFormat_Aztec = (1 << 0), + zxing_BarcodeFormat_Codabar = (1 << 1), + zxing_BarcodeFormat_Code39 = (1 << 2), + zxing_BarcodeFormat_Code93 = (1 << 3), + zxing_BarcodeFormat_Code128 = (1 << 4), + zxing_BarcodeFormat_DataBar = (1 << 5), + zxing_BarcodeFormat_DataBarExpanded = (1 << 6), + zxing_BarcodeFormat_DataMatrix = (1 << 7), + zxing_BarcodeFormat_EAN8 = (1 << 8), + zxing_BarcodeFormat_EAN13 = (1 << 9), + zxing_BarcodeFormat_ITF = (1 << 10), + zxing_BarcodeFormat_MaxiCode = (1 << 11), + zxing_BarcodeFormat_PDF417 = (1 << 12), + zxing_BarcodeFormat_QRCode = (1 << 13), + zxing_BarcodeFormat_UPCA = (1 << 14), + zxing_BarcodeFormat_UPCE = (1 << 15), + zxing_BarcodeFormat_MicroQRCode = (1 << 16), + + zxing_BarcodeFormat_LinearCodes = zxing_BarcodeFormat_Codabar | zxing_BarcodeFormat_Code39 | zxing_BarcodeFormat_Code93 + | zxing_BarcodeFormat_Code128 | zxing_BarcodeFormat_EAN8 | zxing_BarcodeFormat_EAN13 + | zxing_BarcodeFormat_ITF | zxing_BarcodeFormat_DataBar | zxing_BarcodeFormat_DataBarExpanded + | zxing_BarcodeFormat_UPCA | zxing_BarcodeFormat_UPCE, + zxing_BarcodeFormat_MatrixCodes = zxing_BarcodeFormat_Aztec | zxing_BarcodeFormat_DataMatrix | zxing_BarcodeFormat_MaxiCode + | zxing_BarcodeFormat_PDF417 | zxing_BarcodeFormat_QRCode | zxing_BarcodeFormat_MicroQRCode, + zxing_BarcodeFormat_Any = zxing_BarcodeFormat_LinearCodes | zxing_BarcodeFormat_MatrixCodes, + + zxing_BarcodeFormat_Invalid = -1 /* return value when BarcodeFormatsFromString() throws */ + } zxing_BarcodeFormat; + + typedef zxing_BarcodeFormat zxing_BarcodeFormats; + + zxing_BarcodeFormats zxing_BarcodeFormatsFromString(const char* str); + zxing_BarcodeFormat zxing_BarcodeFormatFromString(const char* str); + char* zxing_BarcodeFormatToString(zxing_BarcodeFormat format); + + /* + * ZXing/DecodeHints.h + */ + + typedef enum + { + zxing_Binarizer_LocalAverage, + zxing_Binarizer_GlobalHistogram, + zxing_Binarizer_FixedThreshold, + zxing_Binarizer_BoolCast, + } zxing_Binarizer; + + typedef enum + { + zxing_EanAddOnSymbol_Ignore, + zxing_EanAddOnSymbol_Read, + zxing_EanAddOnSymbol_Require, + } zxing_EanAddOnSymbol; + + typedef enum + { + zxing_TextMode_Plain, + zxing_TextMode_ECI, + zxing_TextMode_HRI, + zxing_TextMode_Hex, + zxing_TextMode_Escaped, + } zxing_TextMode; + + zxing_DecodeHints* zxing_DecodeHints_new(); + void zxing_DecodeHints_delete(zxing_DecodeHints* hints); + + void zxing_DecodeHints_setTryHarder(zxing_DecodeHints* hints, bool tryHarder); + void zxing_DecodeHints_setTryRotate(zxing_DecodeHints* hints, bool tryRotate); + void zxing_DecodeHints_setTryInvert(zxing_DecodeHints* hints, bool tryInvert); + void zxing_DecodeHints_setTryDownscale(zxing_DecodeHints* hints, bool tryDownscale); + void zxing_DecodeHints_setIsPure(zxing_DecodeHints* hints, bool isPure); + void zxing_DecodeHints_setReturnErrors(zxing_DecodeHints* hints, bool returnErrors); + void zxing_DecodeHints_setFormats(zxing_DecodeHints* hints, zxing_BarcodeFormats formats); + void zxing_DecodeHints_setBinarizer(zxing_DecodeHints* hints, zxing_Binarizer binarizer); + void zxing_DecodeHints_setEanAddOnSymbol(zxing_DecodeHints* hints, zxing_EanAddOnSymbol eanAddOnSymbol); + void zxing_DecodeHints_setTextMode(zxing_DecodeHints* hints, zxing_TextMode textMode); + + /* + * ZXing/Result.h + */ + + typedef enum + { + zxing_ContentType_Text, + zxing_ContentType_Binary, + zxing_ContentType_Mixed, + zxing_ContentType_GS1, + zxing_ContentType_ISO15434, + zxing_ContentType_UnknownECI + } zxing_ContentType; + + char* zxing_ContentTypeToString(zxing_ContentType type); + + bool zxing_Result_isValid(const zxing_Result* result); + char* zxing_Result_errorMsg(const zxing_Result* result); + zxing_BarcodeFormat zxing_Result_format(const zxing_Result* result); + zxing_ContentType zxing_Result_contentType(const zxing_Result* result); + uint8_t* zxing_Result_bytes(const zxing_Result* result, int* len); + char* zxing_Result_text(const zxing_Result* result); + char* zxing_Result_ecLevel(const zxing_Result* result); + char* zxing_Result_symbologyIdentifier(const zxing_Result* result); + int zxing_Result_orientation(const zxing_Result* result); + bool zxing_Result_isInverted(const zxing_Result* result); + bool zxing_Result_isMirrored(const zxing_Result* result); + + /* + * ZXing/ReadBarcode.h + */ + + zxing_Result* zxing_ReadBarcode(const zxing_ImageView* iv, const zxing_DecodeHints* hints); + zxing_Results* zxing_ReadBarcodes(const zxing_ImageView* iv, const zxing_DecodeHints* hints); + + void zxing_Result_delete(zxing_Result* result); + void zxing_Results_delete(zxing_Results* results); + + int zxing_Results_size(const zxing_Results* results); + const zxing_Result* zxing_Results_at(const zxing_Results* results, int i); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZXING_C_H */ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy