Skip to content

Start implementing C wrapper #553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jul 1, 2023
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -76,3 +77,7 @@ endif()
if (BUILD_PYTHON_MODULE)
add_subdirectory (wrappers/python)
endif()

if (BUILD_C_API)
add_subdirectory (wrappers/c)
endif()
7 changes: 7 additions & 0 deletions wrappers/c/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
93 changes: 93 additions & 0 deletions wrappers/c/zxing-c-test.c
Original file line number Diff line number Diff line change
@@ -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 <stb_image.h>

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;
}
241 changes: 241 additions & 0 deletions wrappers/c/zxing-c.cpp
Original file line number Diff line number Diff line change
@@ -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<ImageFormat>(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<zxing_BarcodeFormats>(*reinterpret_cast<BarcodeFormat*>(&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<BarcodeFormat>(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<BarcodeFormat>(formats));
}

void zxing_DecodeHints_setBinarizer(zxing_DecodeHints* hints, zxing_Binarizer binarizer)
{
hints->setBinarizer(static_cast<Binarizer>(binarizer));
}

void zxing_DecodeHints_setEanAddOnSymbol(zxing_DecodeHints* hints, zxing_EanAddOnSymbol eanAddOnSymbol)
{
hints->setEanAddOnSymbol(static_cast<EanAddOnSymbol>(eanAddOnSymbol));
}

void zxing_DecodeHints_setTextMode(zxing_DecodeHints* hints, zxing_TextMode textMode)
{
hints->setTextMode(static_cast<TextMode>(textMode));
}

/*
* ZXing/Result.h
*/

char* zxing_ContentTypeToString(zxing_ContentType type)
{
return copy(ToString(static_cast<ContentType>(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<zxing_BarcodeFormat>(result->format());
}

zxing_ContentType zxing_Result_contentType(const zxing_Result* result)
{
return static_cast<zxing_ContentType>(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];
}
}
Loading
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