Writing PNG file from pixel data using libpng - c++

I am trying to write PNG file (saving an image in png format) from pixel data read by glReadPixels in openGL using following piece of code (copied from here):
bool writePNGFileFromBuffer(const char *filename, unsigned char *pixels, int w, int h)
{
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);//8
if (!png)
return false;
png_infop info = png_create_info_struct(png);//7
if (!info) {
png_destroy_write_struct(&png, &info);//
return false;
}
FILE *fp = fopen(filename, "wb");
if (!fp) {
png_destroy_write_struct(&png, &info);//
return false;
}
png_init_io(png, fp);//9
png_set_IHDR(png, info, w, h, 8 /* depth */, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);//10
png_colorp palette = (png_colorp)png_malloc(png, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));//4
if (!palette) {
fclose(fp);
png_destroy_write_struct(&png, &info);//
return false;
}
png_set_PLTE(png, info, palette, PNG_MAX_PALETTE_LENGTH);//12
png_write_info(png, info);//1
png_set_packing(png);//5
png_bytepp rows = (png_bytepp)png_malloc(png, h * sizeof(png_bytep));//
for (int i = 0; i < h; ++i)
rows[i] = (png_bytep)(pixels + (h - i) * w * 3);
png_write_image(png, rows);//2
png_write_end(png, info);//6
png_free(png, palette);//11
png_destroy_write_struct(&png, &info);//3
fclose(fp);
delete[] rows;
return true;
}
I linked the libpng and zlib libraries in the additional dependencies. But when I compile the code it gives following error:
Error 77 error LNK2001: unresolved external symbol png_write_info
It gives this error at about 12 instances marked in the code snippet above. I am clear that it is not able to find the definitions of the functions but I do not know why?
Any help would be appreciated. Thanks
Edit: I looked into the header file "png.h" and it contains declarations for all the functions. Functions are defined in "pngwrite.c" and I think it should take it from the linked library.
Edit2: After hitting around for couple of days, I found that when I change the output type of my project (in which I am using libpng.lib) to Static Lib (.lib), everything works and the code compiles well but with .dll output it throws the above specified error.
And my problem is, I have to use .dll output because that I use in another huge project which I cant change to accept .lib.
Any help on this?

You may be missing "pnglibconf.h" which you must copy from
scripts/pnglibconf.h.prebuilt to pnglibconf.h in your source directory.

Related

C++ Tesseract OCR: Getting ObjectCache(): WARNING! LEAK! object still has count 1

I installed libtesseract-dev (v4.1.1) on Ubuntu 20.04 & I am trying out a C++ code to OCR an image to searchable PDF.
My code is somewhat modified than the C++ API example code provided at official website:
/home/test/Desktop/Example2/testexample2.cpp:
#include <leptonica/allheaders.h>
#include <tesseract/baseapi.h>
#include <tesseract/renderer.h>
int main()
{
//const char* input_image = "/usr/src/tesseract-oc/testing/phototest.tif";
//const char* output_base = "my_first_tesseract_pdf";
//const char* datapath = "/Projects/OCR/tesseract/tessdata";
const char* input_image = "001.jpg";
const char* output_base = "001";
const char* datapath = ".";
int timeout_ms = 5000;
const char* retry_config = nullptr;
bool textonly = false;
int jpg_quality = 92;
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
if (api->Init(datapath, "eng")) {
fprintf(stderr, "Could not initialize tesseract.\n");
exit(1);
}
/*
tesseract::TessPDFRenderer *renderer = new tesseract::TessPDFRenderer(
output_base, api->GetDatapath(), textonly, jpg_quality);
*/
tesseract::TessPDFRenderer *renderer = new tesseract::TessPDFRenderer(
output_base, api->GetDatapath(), textonly);
bool succeed = api->ProcessPages(input_image, retry_config, timeout_ms, renderer);
if (!succeed) {
fprintf(stderr, "Error during processing.\n");
return EXIT_FAILURE;
}
api->End();
return EXIT_SUCCESS;
}
I also followed https://stackoverflow.com/a/59382664 as follows:
cd /home/test/Desktop/Example2
wget https://github.com/tesseract-ocr/tessdata/raw/master/eng.traineddata
wget https://github.com/tesseract-ocr/tesseract/blob/master/tessdata/pdf.ttf
export TESSDATA_PREFIX=$(pwd)
gedit config
(In the config file, entered the contents:
tessedit_create_pdf 1 Write .pdf output file
tessedit_create txt 1 Write .txt output file
)
g++ testexample2.cpp -o testexample2 -ltesseract
./testexample2
But on execution, it displays the errors as follows:
Warning: Invalid resolution 0 dpi. Using 70 instead.
Error during processing.
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c5241a0 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatapunc-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c506770 still has count 1 (id /home/test/Desktop/Example2/eng.traineddataword-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c9a4a70 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatanumber-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5c9a4980 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatabigram-dawg)
ObjectCache(0x7f1b096669c0)::~ObjectCache(): WARNING! LEAK! object 0x55af5d7d5170 still has count 1 (id /home/test/Desktop/Example2/eng.traineddatafreq-dawg)
My directory structure is:
Example2
|------->001.jpg
|------->config
|------->eng.traineddata
|------->pdf.ttf
|------->testexample2
|------->testexample2.cpp
I have searched about this on multiple sources, but could not find any fix for this.
Further, I would like to know if there is someway I can build a binary using C++ compilation from this code + libtesseract such that my binary becomes a standalone portable binary, running which on other Ubuntu systems would not require reinstalling tesseract libraries & their dependencies
tesseract API examples is show case for using tesseract features without covering all specifics of programming language of your choice (c++ in your case).
Just looking at your code even without trying it: you dynamically allocates memory 2x but you did not deallocate them. Try to fix these issues.
You must free use dynamic memory for your class "api"
Use:
... you code...
if (renderer) delete renderer;
if (api) delete api;

Properly setting up tinycc with visual studio 2019, library libtcc1-32.a not found

I'm using tcclib to compile and run C code on the fly in my C++ project.
I'm using the binaries provided here https://bellard.org/tcc/
I then open a vs2019 developer prompt and run both those command
lib /def:libtcc\libtcc.def /out:libtcc.lib
cl /MD examples/libtcc_test.c -I libtcc libtcc.lib
My code builds fine, I'm using this code. This code is similar to the one found in the tcclib example, which is this one : https://repo.or.cz/tinycc.git/blob/HEAD:/tests/libtcc_test.c (this is another repo, but it's the same code.
The code I run is this one. This is inside an extern "C" {}.
int tcc_stuff(int argc, const char** argv) {
TCCState* s;
int i;
int (*func)(int);
s = tcc_new();
if (!s) {
fprintf(stderr, "Could not create tcc state\n");
exit(1);
}
/* if tcclib.h and libtcc1.a are not installed, where can we find them */
for (i = 1; i < argc; ++i) {
const char* a = argv[i];
if (a[0] == '-') {
if (a[1] == 'B')
tcc_set_lib_path(s, a + 2);
else if (a[1] == 'I')
tcc_add_include_path(s, a + 2);
else if (a[1] == 'L')
tcc_add_library_path(s, a + 2);
}
}
/* MUST BE CALLED before any compilation */
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
{
const char* other_file = ReadFile2(argv[1]);
if (other_file == NULL)
{
printf("invalid filename %s\n", argv[1]);
return 1;
}
if (tcc_compile_string(s, other_file) == -1)
return 1;
}
/* as a test, we add symbols that the compiled program can use.
You may also open a dll with tcc_add_dll() and use symbols from that */
tcc_add_symbol(s, "add", add);
tcc_add_symbol(s, "hello", hello);
/* relocate the code */
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0)
return 1;
/* get entry symbol */
func = (int(*)(int))tcc_get_symbol(s, "foo");
if (!func)
return 1;
/* run the code */
msg(func(32));
//msg(func2(4));
/* delete the state */
tcc_delete(s);
return 0;
}
When running my code, TCC had the error
tcc: error: library 'libtcc1-32.a' not found
I fixed it by placing this file in the lib/ directory next to my .exe
I also copied the include/ folder to include stdio.h etc.
My question is: why does it need this file in a lib/ folder, instead of the provided tcclib.dll file? Is it possible to "ship" certain headers like stdio.h?
The question has no answer but 360 views, so I thought I'd reply.
The library doesn't necessarily need to be in that folder. To quote the author's command line docs, which still apply to the library,
-Ldir
Specify an additional static library path for the -l option. The default library paths are /usr/local/lib, /usr/lib and /lib.
I inferred your program to be a modified main() of libtcc_test.c & fixed it to the point of functioning. Then I used VS2022 to retrace your steps, put the .a files into the same folder as my new tests_libtcc_test.exe, then I ran this:
tests_libtcc_test c:/lang/tcc/examples/fib.c -Ic:/lang/tcc/include -L.
The library issue appears if I don't -L anything, and disappears if I include at least the ".".
And of course, you can drop the include folder into your redistributable and include it by default right from the code.
Because the tcc DLL is just another interface to the same compiler, it needs the same things tcc.exe would to build an executable; in this case, it needs the same libraries.

Graphviz as library in windows

I am contacting you regarding Graphviz libraries. I have a bug that I observed when using graphviz as a library within c++ code and qt creator on windows.
Based on the example from the Documentation, I used the following function to convert a dot file in png file:
bool saveImageGV(){
GVC_t *gvc= gvContext();
gvAddLibrary(gvc, &gvplugin_dot_layout_LTX_library);
gvAddLibrary(gvc, &gvplugin_core_LTX_library);
gvAddLibrary(gvc, &gvplugin_gd_LTX_library);
FILE *fp = fopen((pathTmp + ".dot").c_str(), "r");
Agraph_t *g = agread(fp,0);
gvLayout(gvc, g, "dot");
FILE *fp2 = fopen((pathTmp + ".png").c_str(), "w");
gvRender(gvc, g, "png", fp2);
gvFreeLayout(gvc, g);
agclose(g); fclose(fp); fclose(fp2);
return (gvFreeContext(gvc));
}
It appears that on linux, this function works very well with graphviz 2.30 and I succeed to convert my dot file into a png file.
However, when I use the windows libraries, I got a segmentation fault with the same function.
My investigations let me think the problems could come from the .dll libraries as, on linux, it worked with the 2.30 version but not with the 2.38 version of the libraries. With windows, 2.30 and 2.38 lead to the same error of segmentation fault.
Is it a well known bug and it exists another way to convert a dot file into png file on windows? I link the libraries from the "Graphviz2.38\lib\release\lib" folder and take the dlls from "Graphviz2.38\lib\release\dll" folder.
I thank you in advance for your response and if you need more information.
graphviz FAQ
maybe it's because of a wrong version of stdio
i had segmentation fault happened in visual studio and here is the solution (using /std:c++latest so the syntax does not conform to the standard, the code is only for understanding)
Agiodisc_t my_iodisc = {
.afread = [](void* chan, char* buf, int bufsize)->int
{
return fread(buf, 1, bufsize, (FILE*)chan);
},
.putstr = [](void* chan,const char* buf)->int
{
return fwrite(buf, 1, strlen(buf), (FILE*)chan);
},
.flush = [](void* chan)->int
{
return fflush((FILE*)chan);
}
};
Agdisc_t my_disc = {
.mem = NULL, // use system default
.id = NULL, // use system default
.io = &my_iodisc
};
FILE* p_file = fopen("1.gv", "r");
Agraph_t* g = agread(p_file, &my_disc);
fclose(p_file);

Small C program compiles, but tries to import functions from wrong DLLs

I have a small C program that uses 3 DLLs. It uses 8 functions from the first DLL. 5 functions from the second DLL. And 2 functions from the 3rd DLL.
Problem 1) When I open the outputed executable with Dependency Walker, the executable only tries to import 2 functions from each DLL. That can't be right.
Problem 2) What's worse, it tries to import the same 2 functions from each DLL. But only one of the DLLs has these 2 functions it's even trying to import. So the executable throws an error saying it can't find a function. But of course it can't, because it's looking inside DLLs that clearly don't have the function.
Question) How do I fix this? I want to open the executable with Dependency Walker and see that it tries to import 8 functions from the first DLL. 5 functions from the second DLL. And 2 functions from the 3rd DLL. And I want to see that it tries to import the right functions from the right DLLs.
Hopefully someone can help me based on just the information above. But if not, hopefully I can edit this question, and I will include all the details including the source code of the program (it's one file and short). Screenshots of Dependency Walker. Links to the .h, .lib, and .dll files (they are a part of libx264). And screenshots of all the settings in Visual Studio that are needed to get the program to build without any errors.
Aditional Information...
Sorry I'm new to C programming but I will try to provide enough information.
The following is a C program that uses libx264 (or maybe an ffmpeg API that uses libx264 behind the scenes) that manually draws raw RGB images (as uint8_t arrays), feeds them to the libx264 encoder, receives individual x264 frames from the encoder, and writes the x264 frames to a file (as uint8_t arrays). These x264 frames can then be fed to a decoder. But this program just encodes.
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#pragma warning(disable : 4996)
static AVCodecContext *c = NULL;
static AVFrame *frame;
static AVPacket pkt;
static FILE *file;
struct SwsContext *sws_context = NULL;
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) {
const int in_linesize[1] = { 3 * c->width };
sws_context = sws_getCachedContext(sws_context,
c->width, c->height, AV_PIX_FMT_RGB24,
c->width, c->height, AV_PIX_FMT_YUV420P,
0, 0, 0, 0);
sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0,
c->height, frame->data, frame->linesize);
}
uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb) {
int x, y, cur;
rgb = (uint8_t*)realloc(rgb, 3 * sizeof(uint8_t) * height * width);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
cur = 3 * (y * width + x);
rgb[cur + 0] = 0;
rgb[cur + 1] = 0;
rgb[cur + 2] = 0;
if ((frame->pts / 25) % 2 == 0) {
if (y < height / 2) {
if (x < width / 2) {
/* Black. */
}
else {
rgb[cur + 0] = 255;
}
}
else {
if (x < width / 2) {
rgb[cur + 1] = 255;
}
else {
rgb[cur + 2] = 255;
}
}
}
else {
if (y < height / 2) {
rgb[cur + 0] = 255;
if (x < width / 2) {
rgb[cur + 1] = 255;
}
else {
rgb[cur + 2] = 255;
}
}
else {
if (x < width / 2) {
rgb[cur + 1] = 255;
rgb[cur + 2] = 255;
}
else {
rgb[cur + 0] = 255;
rgb[cur + 1] = 255;
rgb[cur + 2] = 255;
}
}
}
}
}
return rgb;
}
/* Allocate resources and write header data to the output file. */
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) {
AVCodec *codec;
int ret;
codec = avcodec_find_encoder(codec_id);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
c->bit_rate = 400000;
c->width = width;
c->height = height;
c->time_base.num = 1;
c->time_base.den = fps;
c->keyint_min = 600;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
file = fopen(filename, "wb");
if (!file) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw picture buffer\n");
exit(1);
}
}
/*
Write trailing data to the output file
and free resources allocated by ffmpeg_encoder_start.
*/
void ffmpeg_encoder_finish(void) {
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
int got_output, ret;
do {
fflush(stdout);
ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
fwrite(pkt.data, 1, pkt.size, file);
av_packet_unref(&pkt);
}
} while (got_output);
fwrite(endcode, 1, sizeof(endcode), file);
fclose(file);
avcodec_close(c);
av_free(c);
av_freep(&frame->data[0]);
av_frame_free(&frame);
}
/*
Encode one frame from an RGB24 input and save it to the output file.
Must be called after ffmpeg_encoder_start, and ffmpeg_encoder_finish
must be called after the last call to this function.
*/
void ffmpeg_encoder_encode_frame(uint8_t *rgb) {
int ret, got_output;
ffmpeg_encoder_set_frame_yuv_from_rgb(rgb);
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
if (frame->pts == 1) {
frame->key_frame = 1;
frame->pict_type = AV_PICTURE_TYPE_I;
}
else {
frame->key_frame = 0;
frame->pict_type = AV_PICTURE_TYPE_P;
}
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
fwrite(pkt.data, 1, pkt.size, file);
av_packet_unref(&pkt);
}
}
/* Represents the main loop of an application which generates one frame per loop. */
static void encode_example(const char *filename, int codec_id) {
int pts;
int width = 320;
int height = 240;
uint8_t *rgb = NULL;
ffmpeg_encoder_start(filename, codec_id, 25, width, height);
for (pts = 0; pts < 100; pts++) {
frame->pts = pts;
rgb = generate_rgb(width, height, pts, rgb);
ffmpeg_encoder_encode_frame(rgb);
}
ffmpeg_encoder_finish();
}
int main(void) {
avcodec_register_all();
encode_example("tmp.h264", AV_CODEC_ID_H264);
encode_example("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO);
return 0;
}
The line #pragma warning(disable : 4996) ignores depreciation warnings that are treated as errors. You can see that some of the functions I'm using are marked as depreciated in the header files. I'll worry about that later, right now I just need something that runs.
The .h, .lib, and .dll files can be found here: https://ffmpeg.zeranoe.com/builds/
There are some download options on that site: Screenshot of Download Options
I'm using that 64-bit beta version of the .h, .lib, and .dll files. You also have to select if you want to download the "Static", "Shared", or "Dev" version of the files. The "Shared" version has the .dll files. The "Dev" version has the .h and .lib files. So I needed both of those.
I was advised to not post screenshots of settings in Visual Studio but I'll summary of what I did in Visual Studio to get the program to build. I had to make sure the project was set to 64-bit output (it was 32-bit by default). I had to tell Visual Studio to compile and link C not C++ code (maybe I did this by renaming the source code file extension to .c instead of .cpp, I can't remember). I put the folders with the .lib files and .h files in a logical place inside my project folder and added the folder paths to the places Visual Studio will look for .lib files and .h files. I also had to specifically name the .lib files I was using. After building I put the .dll files in the same folder as the compiled executable. There might have been something else but I can't remember.
I was also advised not to make screenshots of Dependency Walker, and instead paste output from the command:
dumpbin /imports LibLinkingTest.exe
LibLinkingTest.exe is the name of the executable that was compiled from the C source code above, based on the name I gave to my project. Here is the output from that command above:
Microsoft (R) COFF/PE Dumper Version 14.12.25835.0 Copyright (C)
Microsoft Corporation. All rights reserved.
Dump of file LibLinkingTest.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
avcodec-58.dll
1400221D8 Import Address Table
1400226D8 Import Name Table
0 time date stamp
0 Index of first forwarder reference
C sws_getCachedContext
1B sws_scale
avutil-56.dll
1400221D8 Import Address Table
1400226D8 Import Name Table
0 time date stamp
0 Index of first forwarder reference
C sws_getCachedContext
1B sws_scale
swscale-5.dll
1400221D8 Import Address Table
1400226D8 Import Name Table
0 time date stamp
0 Index of first forwarder reference
C sws_getCachedContext
1B sws_scale
VCRUNTIME140D.dll
140022150 Import Address Table
140022650 Import Name Table
0 time date stamp
0 Index of first forwarder reference
31 __vcrt_LoadLibraryExW
2F __vcrt_GetModuleHandleW
25 __std_type_info_destroy_list
8 __C_specific_handler
2E __vcrt_GetModuleFileNameW
ucrtbased.dll
140022270 Import Address Table
140022770 Import Name Table
0 time date stamp
0 Index of first forwarder reference
4D __p__commode
52B strcpy_s
527 strcat_s
68 __stdio_common_vsprintf_s
2C1 _seh_filter_dll
172 _initialize_onexit_table
2B4 _register_onexit_function
E5 _execute_onexit_table
C2 _crt_atexit
C1 _crt_at_quick_exit
54A terminate
39B _wmakepath_s
3B7 _wsplitpath_s
563 wcscpy_s
B5 _configthreadlocale
2CA _set_fmode
2B5 _register_thread_local_exe_atexit_callback
175 _initterm_e
174 _initterm
13D _get_initial_narrow_environment
171 _initialize_narrow_environment
B6 _configure_narrow_argv
5B __setusermatherr
2C5 _set_app_type
2C2 _seh_filter_exe
5 _CrtDbgReportW
4 _CrtDbgReport
44F exit
504 realloc
5C __stdio_common_vfprintf
48A fwrite
47B fopen
468 fflush
459 fclose
35 __acrt_iob_func
2CD _set_new_mode
A4 _cexit
9F _c_exit
EA _exit
4A __p___argv
49 __p___argc
KERNEL32.dll
140022000 Import Address Table
140022500 Import Name Table
0 time date stamp
0 Index of first forwarder reference
37D IsDebuggerPresent
45F RaiseException
3EB MultiByteToWideChar
605 WideCharToMultiByte
4CB RtlCaptureContext
4D2 RtlLookupFunctionEntry
4D9 RtlVirtualUnwind
5B4 UnhandledExceptionFilter
573 SetUnhandledExceptionFilter
2B1 GetProcAddress
1AF FreeLibrary
5D5 VirtualQuery
21B GetCurrentProcess
592 TerminateProcess
2B7 GetProcessHeap
34E HeapFree
384 IsProcessorFeaturePresent
34A HeapAlloc
449 QueryPerformanceCounter
21C GetCurrentProcessId
220 GetCurrentThreadId
2EC GetSystemTimeAsFileTime
367 InitializeSListHead
2D3 GetStartupInfoW
27A GetModuleHandleW
263 GetLastError
Summary
1000 .00cfg
1000 .data
2000 .idata
3000 .pdata
3000 .rdata
1000 .reloc
1000 .rsrc
A000 .text
10000 .textbss
It's associating the functions sws_getCachedContext and sws_scale which are used in the C source code above with 3 DLLs: avcodec-58.dll, avutil-56.dll, and swscale-5.dll
But the functions sws_getCachedContext and sws_scale are only present in swscale-5.dll.
And there is no mention of the 13 other functions in the C source code above that should be loaded from these DLLs.
The functions avcodec_find_encoder, avcodec_alloc_context3, avcodec_encode_video2, avcodec_open2, avcodec_close, avcodec_register_all, av_packet_unref, and av_init_packet from the C source code above should be loaded from avcodec-58.dll.
The functions av_opt_set, av_image_alloc, av_free, av_freep, and av_frame_free from the C source code above should be loaded from avutil-56.dll.
The functions sws_getCachedContext and sws_scale from the C source code above should be loaded from swscale-5.dll (so it looks like it actually got that right).
These function are definitely present in these DLLs because I can see them inside the DLLs using Dependency Walker. I'm pretty sure that none of these functions are statically embedded inside the final executable. All of these functions should be loaded from the DLLs.
And I was advised to post the build log. Here is the build log:
Microsoft (R) C/C++ Optimizing Compiler Version 19.12.25835 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
cl /c /ZI /W3 /WX- /diagnostics:classic /sdl /Od /D _DEBUG /D _CONSOLE
/D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise
/permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"x64\Debug\"
/Fd"x64\Debug\vc141.pdb" /Gd /TC /errorReport:prompt LibLinkingTest.c
LibLinkingTest.c
Microsoft (R) Incremental Linker Version 14.12.25835.0
Copyright (C) Microsoft Corporation. All rights reserved.
"/OUT:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.exe"
/INCREMENTAL avcodec.lib avdevice.lib avfilter.lib avformat.lib
avutil.lib postproc.lib swresample.lib swscale.lib kernel32.lib
user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib
shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
/MANIFEST "/MANIFESTUAC:level='asInvoker' uiAccess='false'"
/manifest:embed /DEBUG:FASTLINK
"/PDB:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.pdb"
/SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT
"/IMPLIB:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.lib"
/MACHINE:X64 x64\Debug\LibLinkingTest.obj x64\Debug\stdafx.obj
LibLinkingTest.vcxproj ->
C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.exe

Runtime error on Windows when trying to load image with libpng

I am using pHash and that library uses libpng. I am having issues running my program because libpng fails loading a PNG file.
Version of libpng: 1.4.19
Platform: Windows 10
Environment: Visual Studio 2015
Trivial
Just if you came up with the following questions...
Is the path to image correct? Yes
Is the image a valid PNG file? Yes
Code details
Library pHash uses CImg, the version of CImg they are using is a bit old I think:
#define cimg_version 148 // In CImg.h
I have debugged the library and the problems occurs in CImg.h (contained in the pHash VC++ project):
CImg<T>& _load_png(std::FILE *const file, const char *const filename) {
if (!file && !filename)
throw CImgArgumentException(_cimg_instance
"load_png() : Specified filename is (null).",
cimg_instance);
// Open file and check for PNG validity
if (Buffer) strcat(Buffer, "Checking PNG availability\r\n");
const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
unsigned char pngCheck[8] = { 0 };
cimg::fread(pngCheck,8,(std::FILE*)nfile);
if (png_sig_cmp(pngCheck,0,8)) {
if (!file) cimg::fclose(nfile);
throw CImgIOException(_cimg_instance
"load_png() : Invalid PNG file '%s'.",
cimg_instance,
nfilename?nfilename:"(FILE*)");
}
// Setup PNG structures for read
png_voidp user_error_ptr = 0;
png_error_ptr user_error_fn = 0, user_warning_fn = 0;
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
if (!png_ptr) { // <-- PROBLEM HERE
if (!file) cimg::fclose(nfile);
throw CImgIOException(_cimg_instance
"load_png() : Failed to initialize 'png_ptr' structure for file '%s'.",
cimg_instance,
nfilename?nfilename:"(FILE*)");
...
}
The snippet shows the first part of CImg<T>& _load_png(std::FILE *const file, const char *const filename) which is called by the CImg library used by pHash.
Runtime issue
The code compiles fine but I get this error at runtime which I can see in the debugger:
CImgIOException: Failed to initialize 'png_ptr'...
In the point indicated in the code. I don't know why, it fails loading the image. The failure occurs when calling png_create_read_struct in CImg.h. That code is a bit obscure as defined through preprocessor directives. It is not clear why it is failing.
Any ideas?
Either if you are including libpng yourself or if another library is including and using libpng there are a few things to be aware of.
Which ever version of Visual Studio you are using, the libpng (dll or lib) files must be built from the same version of Visual Studio that your solution is linking against.
The platform you are using 32bit or 64bit is of concern.
Project settings when building the png library must match the build types of your current project. (Code Generation -> Runtime Library) must match. Your character set should match as well.
It is a little to difficult to tell what exactly is causing the problem but these are a few things to have a look at.
One thing I would suggest is to go to the website that provides the newest version of libpng and download it. Set a folder on your computer and create "system environment variable through windows" to point to your library. Open the solution to this library in the current version of VS you are using, build it out for both a static lib and dynamic lib (two different solutions) and build them both out for 32 bit and 64 bit saving the generated files into separated folders. Then go into the other library that depends on this and try to switch the dlls or libs and link against the new ones if possible. Also the other 3rd party library you should try to open its solution in the same version of VS and try to do a clean build from there. Then make sure you link everything properly. You may have to also modify the props file.
EDIT
I am not familiar with pHash or CImg, but I am familiar with libpng.
Here is a function in one of my projects to load in a png into a texture structure. Now this is a part of a class object that relies on many other classes, but you should be able to see from this snippet that I am successfully using libpng.
// ----------------------------------------------------------------------------
// loadPng()
bool TextureFileReader::loadPng( Texture* pTexture ) {
struct PngFile {
FILE* fp;
png_struct* pStruct;
png_info* pInfo;
// --------------------------------------------------------------------
PngFile() :
fp( NULL ),
pStruct( NULL ),
pInfo( NULL )
{} // PngFile
// --------------------------------------------------------------------
~PngFile() {
if ( NULL != fp ) {
fclose( fp );
}
if ( NULL != pStruct ) {
if ( NULL != pInfo ) {
png_destroy_read_struct( &pStruct, &pInfo, NULL );
} else {
png_destroy_read_struct( &pStruct, NULL, NULL );
}
}
} // ~PngFile
} png;
// Error Message Handling
std::ostringstream strStream;
strStream << __FUNCTION__ << " ";
if ( fopen_s( &png.fp, m_strFilenameWithPath.c_str(), "rb" ) != 0 ) {
strStream << "can not open file for reading";
throwError( strStream );
}
// Test If File Is Actually A PNG Image
const int NUM_HEADER_BYTES = 8;
png_byte headerBytes[NUM_HEADER_BYTES];
// Read The File Header
if ( fread( headerBytes, 1, NUM_HEADER_BYTES, png.fp ) != NUM_HEADER_BYTES ) {
strStream << "error reading header";
return false;
}
// Test Header
if ( png_sig_cmp( headerBytes, 0, NUM_HEADER_BYTES ) != 0 ) {
return false; // Not A PNG FILE
}
// Init PNG Read Structure - Test PNG Version Compatibility
png.pStruct = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
if ( NULL == png.pStruct ) {
strStream << "can not create struct for PNG file";
throwError( strStream );
}
// Init PNG Info Structure - Allocate Memory For Image Info
png.pInfo = png_create_info_struct( png.pStruct );
if ( NULL == png.pInfo ) {
strStream << "can not create info for PNG file";
throwError( strStream );
}
// Prepare For Error Handling
if ( setjmp( png_jmpbuf( png.pStruct ) ) ) {
strStream << "can not init error handling for PNG file";
throwError( strStream );
}
// Tell libPng Where The File Data Is
png_init_io( png.pStruct, png.fp );
// Tell libPng That You Have Already Read The Header Bytes
png_set_sig_bytes( png.pStruct, NUM_HEADER_BYTES );
// Read Image Data From The File
png_read_png( png.pStruct, png.pInfo, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_GRAY_TO_RGB, NULL );
// Show Image Attributes
png_byte colorType = png_get_color_type( png.pStruct, png.pInfo );
switch( colorType ) {
case PNG_COLOR_TYPE_RGB:
case PNG_COLOR_TYPE_RGBA: {
break;
}
default: {
strStream << "PNG is saved in an unsupported color type (" << colorType << ")";
throwError( strStream );
}
}
unsigned uHeight = png_get_image_height( png.pStruct, png.pInfo );
unsigned uBytesPerRow = png_get_rowbytes( png.pStruct, png.pInfo );
if ( 0 == uHeight || 0 == uBytesPerRow ) {
strStream << "invalid image size. Height(" << uHeight << "), Bytes per row(" << uBytesPerRow << ")";
throwError( strStream );
}
// Make Room For All Pixel Data
unsigned uTotalNumBytes = uHeight * uBytesPerRow;
pTexture->vPixelData.resize( uTotalNumBytes );
// Get All Pixel Data From PNG Image
png_bytepp ppPixelRow = png_get_rows( png.pStruct, png.pInfo );
for ( unsigned int r = 0; r < uHeight; ++r ) {
memcpy( &pTexture->vPixelData[ uBytesPerRow * ( uHeight - 1 - r ) ], ppPixelRow[r], uBytesPerRow );
}
// Store Other Values In Texture
pTexture->uWidth = png_get_image_width( png.pStruct, png.pInfo );
pTexture->uHeight = uHeight;
pTexture->hasAlphaChannel = ( colorType == PNG_COLOR_TYPE_RGBA );
return true;
} // loadPng
Looking through the source code for png_create_read_struct_2(), there are only 2 failure modes: inability to allocate memory, which is unlikely to be the problem, and a library version conflict.
If you are using a precompiled build of the pHash library, you must ensure that the copy of the libpng DLL that gets linked dynamically at runtime is the same version of the library that pHash was compiled against. The latest Windows build on pHash.org ships with libpng12.dll in the "Release" subdirectory, which is probably incompatible the version that you mentioned in the question, namely 1.4.19.
If you are building pHash from source, make sure that the libpng include files that are being used in your build process match the version being loaded at runtime.
If you're unsure exactly which DLLs are being loaded at runtime, the surest way I know to determine it would be to use Process Monitor.