-
-
Notifications
You must be signed in to change notification settings - Fork 464
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
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
eccf639
Start implementing C wrapper
siiky a6e43f5
Don't use `void*`
siiky 9ca0e25
Remove unnecessary `ifdef`s
siiky 1a3d636
Use `std::move` to create new copies
siiky 1e154b7
Change destructor names from `_free` to `_delete`
siiky 8f0f551
Simplify wrapper code
siiky b120f2d
Correctly dereference `ImageView`/`DecodeHints` pointers
siiky 4c2dae6
Don't hide pointers behind `typedef`s
siiky 1111825
Use `reinterpret_cast` instead of C-style casts
siiky de82da9
Make getters const
siiky bca97fa
Don't unnecessarily assign to variable
siiky 7a18c30
Use `static_cast` instead of C-style cast for enums
siiky ced3330
Remove unnecessary variable assignments
siiky b3ce198
Format code with `clang-format`
siiky 423c22a
Add C wrapper example program
siiky 1124709
include stb_image.h
axxel 81d245a
stbi_load related cosmetic (variable names + STBI_grey)
axxel 5b3bcdb
Add `zxing_Result_bytes()`
siiky 6be805b
c-API: rename zxing.* to zxing-c.* and add CMake build support
axxel 4c69b14
c-API: add more properties + ctest case + comment cleanup
axxel 7242bdf
c-API: WIP
axxel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.