Extract RGB from JPEG image, using libjpeg C++ Library - c++

I am using the libjpeg library to read and copy a jpeg into an editing program that I am writing in C++
I have a display buffer which is a vector of a datatype called ColorData
All ColorData consists of is 3 floats (RGB)
Here is my code that opens the jpeg files
PixelBuffer * IOManager::load_jpg_to_pixel_buffer(const char *file_name){
struct jpeg_decompress_struct cinfo;
FILE * infile;
JSAMPARRAY buffer;
if ((infile = fopen(file_name, "rb")) == NULL) {
std::cout << "Could not open the jpg file: " << file_name << std::endl;
return nullptr;
}
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
int width = static_cast<int>(cinfo.output_width);
int height = static_cast<int>(cinfo.output_height);
std::cout << typeid(cinfo.colormap).name() << std::endl;
std::cout << "Width: " << width << "Height: " << height << std::endl;
PixelBuffer * image_buffer = new PixelBuffer(width, height, ColorData());
std::cout << cinfo.output_components << std::endl;
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, cinfo.output_width * cinfo.output_components, 1);
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
/* Here we use the library's state variable cinfo.output_scanline as the
* loop counter, so that we don't have to keep track ourselves.
*/
while (cinfo.output_scanline < cinfo.output_height) {
/* jpeg_read_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could ask for
* more than one scanline at a time if that's more convenient.
*/
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
/* Assume put_scanline_someplace wants a pointer and sample count. */
}
return nullptr;
}
How can I get the RGB value from the jpeg using the libjpeg?

The RGB values are in buffer. It's actually an array of arrays, so you have to index buffer[0].
Something like this:
while (cinfo.output_scanline < cinfo.output_height)
{
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
// get the pointer to the row:
unsigned char* pixel_row = (unsigned char*)(buffer[0]);
// iterate over the pixels:
for(int i = 0; i < cinfo.output_width; i++)
{
// convert the RGB values to a float in the range 0 - 1
float red = (float)(*pixel_row++) / 255.0f;
float green = (float)(*pixel_row++) / 255.0f;
float blue = (float)(*pixel_row++) / 255.0f;
}
}
This is assuming cinfo.output_components is 3.

Related

Using libpng 1.2 to write RGB image buffer to PNG buffer in memory causing segmentation fault

My project need to receive a PNG file content over HTTP request, do something to the image and sending back the generated PNG back in the HTTP response. All code need to be done in C/C++.
I'm new to libpng. So I try to write a prototype, reading a PNG file into unsigned char buffer, get the RGB values out (ignore alpha), do no-op, create a new unsigned char buffer with PNG file content, write the new file to the disk and validate I "generate" the same image. I have referenced this question in StackOverflow
My code (with some unrelated function removed, full runnable code is here):
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <png.h>
using namespace std;
typedef struct
{
png_bytep data;
int size;
} ImageTarget;
int read_png(string file_path, unsigned char** buffer)
{
// ... ...
}
void write_png(string file_path, unsigned char* buffer, int length)
{
// ... ...
}
static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
// ... ...
}
void pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
cout << "- length ----------- " << length << endl;
ImageTarget * itarget = (ImageTarget*)png_get_io_ptr(png_ptr);
size_t nsize = itarget->size + length;
cout << "- nsize ----------- " << nsize << endl;
cout << "- data ------ " << (size_t) itarget->data << endl;
if(itarget->data != nullptr)
itarget->data = (unsigned char*)realloc(itarget->data, nsize);
else
itarget->data = (unsigned char*)malloc(nsize);
memcpy(itarget->data + itarget->size, data, length);
itarget->size += length;
}
int main()
{
const string Input_PNG = "pic/c.png";
const string Output_PNG = "output/output.png";
unsigned char* buffer = nullptr;
int length = read_png(Input_PNG, &buffer);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
png_infop info_ptr = png_create_info_struct(png_ptr);
ImageSource imgsource;
imgsource.data = buffer;
imgsource.size = length;
imgsource.offset = 0;
png_set_read_fn(png_ptr, &imgsource, pngReadCallback);
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_ALPHA, 0);
int w = png_get_image_width( png_ptr, info_ptr );
int h = png_get_image_height( png_ptr, info_ptr );
cout << "Image width (from PNG file): " << w << endl;
cout << "Image height (from PNG file): " << h << endl;
png_bytep* row_pointers = png_get_rows( png_ptr, info_ptr );
png_bytep raw_rgb = (png_bytep)malloc(w * h * 3);
int i = 0;
for(int y=0; y<h; ++y ) {
for(int x=0; x<w*3; ) {
raw_rgb[i++] = row_pointers[y][x++]; // red
raw_rgb[i++] = row_pointers[y][x++]; // green
raw_rgb[i++] = row_pointers[y][x++]; // blue
}
}
// Do Something
png_destroy_read_struct( &png_ptr, &info_ptr, 0);
// ---------------------------------
png_structp wpng_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop winfo_ptr = png_create_info_struct(wpng_ptr);
setjmp(png_jmpbuf(wpng_ptr));
png_set_IHDR(wpng_ptr, winfo_ptr, 720, 720, 8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_set_rows(wpng_ptr, winfo_ptr, &raw_rgb);
ImageTarget itarget;
itarget.data = nullptr;
itarget.size = 0;
png_set_write_fn(wpng_ptr, &itarget, pngWriteCallback, NULL);
png_write_png(wpng_ptr, winfo_ptr, PNG_TRANSFORM_IDENTITY, NULL);
cout << "Output file name: " << Output_PNG << endl;
write_png(Output_PNG, itarget.data, length);
return 0;
}
The makefile I use: here.
After compile and run my code, I see the output (I have verified the c.png I used is a PNG file by Irfanview):
Image width (from PNG file): 720
Image height (from PNG file): 720
- length ----------- 8
- nsize ----------- 8
- data ------ 0
- length ----------- 8
- nsize ----------- 16
- data ------ 22161152
- length ----------- 13
- nsize ----------- 29
- data ------ 22161152
- length ----------- 4
- nsize ----------- 33
- data ------ 22161152
[1] 6675 segmentation fault (core dumped) ./png_from_buffer
Check the core file with gdb, here is the output:
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:35
#1 0x00007f3c41b3caf0 in png_write_row () from /lib/x86_64-linux-gnu/libpng12.so.0
#2 0x00007f3c41b3cd78 in png_write_image () from /lib/x86_64-linux-gnu/libpng12.so.0
#3 0x00007f3c41b3d61b in png_write_png () from /lib/x86_64-linux-gnu/libpng12.so.0
#4 0x000000000040269c in main () at main.cpp:181
I tried using different buffer, convert to vector<unsigned char*> as the rows. No luck so far. Any idea will be appreciated.
My environment, if matters:
Ubuntu 16.04
libpng 1.2
libpng12-0/xenial-updates,xenial-security,now 1.2.54-1ubuntu1.1 amd64 [installed]
libpng12-dev/xenial-updates,xenial-security,now 1.2.54-1ubuntu1.1 amd64 [installed,automatic]
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
I figured out a solution: using libpng v1.6 and the problem could be fixed easily as below:
// READING....
png_image image;
memset(&image, 0, (sizeof image));
image.version = PNG_IMAGE_VERSION;
if (png_image_begin_read_from_memory(&image, file_buffer, length) == 0)
{
return -1;
}
png_bytep buffer;
image.format = PNG_FORMAT_BGR;
size_t input_data_length = PNG_IMAGE_SIZE(image);
buffer = (png_bytep)malloc(input_data_length);
memset(buffer, 0, input_data_length);
if (png_image_finish_read(&image, NULL, buffer, 0, NULL) == 0)
{
return -1;
}
Writing to a memory buffer is also quite easy:
// WRITING......
png_image wimage;
memset(&wimage, 0, (sizeof wimage));
wimage.version = PNG_IMAGE_VERSION;
wimage.format = PNG_FORMAT_BGR;
wimage.height = 720;
wimage.width = 720;
// Get memory size
bool wresult = png_image_write_to_memory(&wimage, nullptr, &wlength, 0, buffer, 0, nullptr);
if (!wresult)
{
cout << "Error: " << image.message << endl;
}
// Real write to memory
unsigned char* wbuffer = (unsigned char*)malloc(wlength);
wresult = png_image_write_to_memory(&wimage, wbuffer, &wlength, 0, buffer, 0, nullptr);
write_png(Output_PNG, wbuffer, wlength);
libpng v1.6 exist simple api.Both png_image_write_to_memory you use will consume double time.
I found the better sample in github:
https://gist.github.com/dobrokot/10486786
I hope that it helps.

Save RGB values to jpeg use libjpeg, getting weird results

I have a program that will load a jpeg image and allow the user to draw on the image and resave it.
I have loading and drawing working, but when I try to save the image I get this result
The result should have just been the image of the snail with the line.
Here is my code
bool IOManager::save_jpg_to_file(const char *file_name) {
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile;
JSAMPROW row_pointer[1];
int row;
JSAMPLE * image_buffer;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
if ((outfile = fopen(file_name_.c_str(), "wb")) == NULL) {
fprintf(stderr, "can't open %s\n", file_name_);
return false;
}
jpeg_stdio_dest(&cinfo, outfile);
int img_width = pixel_buffer_->width();
int img_height = pixel_buffer_->height();
cinfo.image_width = img_width;
cinfo.image_height = img_height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 100, TRUE);
jpeg_start_compress(&cinfo, TRUE);
unsigned char bytes[img_width * 3];
while (cinfo.next_scanline < cinfo.image_height) {
for (int i = 0; i < img_width; i+=3){
ColorData colorData = pixel_buffer_->get_pixel(i, cinfo
.next_scanline);
bytes[i] = static_cast<int>(colorData.red()*255) & 0xff;
bytes[i+1] = static_cast<int>(colorData.green()*255) & 0xff;
bytes[i+2] = static_cast<int>(colorData.blue()*255) & 0xff;
}
row_pointer[0] = bytes;
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
std::cout << "Done" << std::endl;
return true;
}
PixelBuffer is just a 2D array of RGB values.
I cannot figure out why it is generating those weird line, any help?
Just posting my comment as a separate answer. You seem to be accessing your pixel buffer data in a wrong way: you are jumping over 3 pixels in both source and destination buffer, while (given your code fragment) it looks like you need to process every pixel of your source. Make sure you increment index only by 1 not 3 when iterating pixel_buffer_

C++ libpng into GLFWimage into GLFWcursor causing distortion and intermittent behaviour

EDIT:
I am now using Brett's code to load a png and it works fine when rendered to a texture in opengl (as a software cursor) but every time I load as a GLFWcursor I get a distorted image (different each time) and there is a problem where the GLFWcursor will only use the last GLFWimage loaded. The pixels I put in are not the pixels I get out.
EDIT:
GLFWimage CursorManager::LoadImageFromFile(string filename)
{
FILE* file = fopen(filename.c_str(), "rb");
if (!file) {
//return NULL;
}
unsigned int width = 0;
unsigned int height = 0;
unsigned char* buffer = NULL;
int error = png_rgba_load(file
,&width
,&height
,&buffer);
if(error == 0)
{
GLFWimage image;
int w = 32; //32
int h = 32; //32
unsigned char pixels[w * h * 4];
memcpy(pixels, buffer, sizeof(pixels));
//for(int i=0;i<sizeof(pixels);i++)
// cout << pixels[i];
cout << "unsigned char pixels:" << endl;
for(int i=0;i<sizeof(pixels);i++)
cout << hex((int)pixels[i]);
cout << endl << "image.pixels:" << endl;
image.width = w;
image.height = h;
image.pixels = pixels;
for(int i=0;i<sizeof(image.pixels);i++)
cout << hex((int)image.pixels[i]);
return image;
}
else
{
cout << "ERROR # png_rgba_load" << endl;
//return NULL;
}
fclose(file);
//if (fclose(file) != 0) /* filesystem I/O error (?) */
// goto fail;
}
-
cout << "Loading GLimage " << m_sFileName_Arrow << endl;
m_oArrow = LoadImageFromFile(m_sFileName_Arrow);
cout << "Loading GLimage " << m_sFileName_Text << endl;
m_oText = LoadImageFromFile(m_sFileName_Text);
cout << "Loading GLimage " << m_sFileName_Crosshair << endl;
m_oCrosshair = LoadImageFromFile(m_sFileName_Crosshair);
cout << "Loading GLimage " << m_sFileName_Hand << endl;
m_oHand = LoadImageFromFile(m_sFileName_Hand);
cout << "Loading GLimage " << m_sFileName_Hresize << endl;
m_oHresize = LoadImageFromFile(m_sFileName_Hresize);
cout << "Loading GLimage " << m_sFileName_Vresize << endl;
m_oVresize = LoadImageFromFile(m_sFileName_Vresize);
-
cout << "Set cursor to Crosshair" << endl;
cur = glfwCreateCursor(&m_oCrosshair,0,0);
the cursor loads from the GLFWimage loaded from libpng, but (1) it has random pixels different each time. (2) Also, no matter what image I set as the new cursor, it always displays the last one loaded, which is m_oVresize (3) when printing image.pixels as hex after setting to pixels it does not match pixels
unsigned char pixels:
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FFD97400FF272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FFD97400FFD97400FFD97400FF272D70FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FFD97400FFD97400FFD97400FFD97400FFD97400FF272D70FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF272D70FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FFD97400FFD97400FFD97400FFD97400FFD97400FF272D70FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FFD97400FFD97400FFD97400FF272D70FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FFD97400FF272D70FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272D70FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
image.pixels: 0000000000000000
This is probably bordering on too much of a code dump for S.O., but it might be full of useful hints to others. It was torture trying to read through the PNG API and various examples, etc. In the end, this is the (relatively) simple interface I provided:
(as for 'copyright' - do whatever you want with it, as long as you leave me alone, etc.)
/*******************************************************************************
*
* png_rgba.h : PNG file I/O in (8) bits per channel RGBA format:
*
* Copyright (c) Brett Hale 2008, 2012. Public Domain.
*
*******************************************************************************/
#ifndef _PNG_RGBA_H
#define _PNG_RGBA_H
#if defined (__cplusplus) /* ISO C declaration scope: */
#define _PNG_RGBA_INIT_DECL extern "C" {
#define _PNG_RGBA_FINI_DECL }
#else
#define _PNG_RGBA_INIT_DECL
#define _PNG_RGBA_FINI_DECL
#endif /* defined (__cplusplus) */
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h> /* ISO C : standard I/O library. */
_PNG_RGBA_INIT_DECL
/******************************************************************************/
/* load a PNG image using an opened file stream. return the image data
* as a (malloc) allocated RGBA image buffer, with the width: (w), and
* height: (h). return (0) on success: */
/* if the operation fails, then the dimensions are set to (0), and the
* buffer is set to (NULL). */
/* the operation fails if the image has zero area, or if the number of
* pixels exceeds PNG_RGBA_PIXEL_LIMIT. */
/* asserts that 'unsigned int' has a width of at least 32 bits. */
#define PNG_RGBA_PIXEL_LIMIT (0x1000000)
int png_rgba_load (FILE *, unsigned *w, unsigned *h, unsigned char **);
/******************************************************************************/
/* save an RGBA image buffer, with the width: (w), and height: (h), as
* a PNG image, using an opened file stream. return (0) on success: */
/* the operation fails if the image has zero area, or if the number of
* pixels exceeds PNG_RGBA_PIXEL_LIMIT. */
/* asserts that 'unsigned int' has a width of at least 32 bits. */
int png_rgba_save (FILE *, unsigned w, unsigned h, const unsigned char *);
/******************************************************************************/
_PNG_RGBA_FINI_DECL
#endif /* _PNG_RGBA_H */
And the implementation:
/*******************************************************************************
*
* png_rgba.c : PNG file I/O in (8) bits per channel RGBA format:
*
* Copyright (c) Brett Hale 2008, 2012. Public Domain.
*
*******************************************************************************/
#include "png_rgba.h"
#include <png.h> /* PNG library. */
#define PNG_SIG_BYTES (8) /* bytes in the PNG file signature. */
/******************************************************************************/
static int
png_rgba_pixel_limit (png_uint_32 w, png_uint_32 h)
{
double da;
/* assert(w != 0 && h != 0); */
if (w > PNG_RGBA_PIXEL_LIMIT || h > PNG_RGBA_PIXEL_LIMIT)
return (1); /* since both (w) and (h) are non-zero. */
/* since an IEEE-754 double has a 53 bit mantissa, it can
* represent the maximum area: (w * h == 2^48) exactly. */
da = ((double) w) * ((double) h);
if (da > ((double) PNG_RGBA_PIXEL_LIMIT))
return (1);
return (0); /* the PNG image is within the pixel limit. */
}
/******************************************************************************/
int png_rgba_load
(
FILE *fp, unsigned *w, unsigned *h, unsigned char **buf)
{
png_byte magic[PNG_SIG_BYTES]; /* (signature byte buffer) */
png_structp png_ctx;
png_infop info_ctx;
png_uint_32 img_width, img_height, row;
png_byte img_depth, img_color_type;
/* 'volatile' qualifier forces reload in setjmp cleanup: */
png_byte *volatile img_data = NULL;
png_bytep *volatile row_data = NULL;
*w = 0, *h = 0, *buf = NULL;
/* it is assumed that 'longjmp' can be invoked within this
* code to efficiently unwind resources for *all* errors. */
/* PNG structures and resource unwinding: */
if ((png_ctx = png_create_read_struct(
PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
return (1); /* ENOMEM (?) */
if ((info_ctx = png_create_info_struct(png_ctx)) == NULL)
{
png_destroy_read_struct(& png_ctx, NULL, NULL);
return (1); /* ENOMEM (?) */
}
if (setjmp(png_jmpbuf(png_ctx)) != 0)
{
png_destroy_read_struct(& png_ctx, & info_ctx, NULL);
free(img_data); free(row_data);
return (1); /* libpng feedback (?) */
}
/* check PNG file signature: */
if (fread(magic, (1), PNG_SIG_BYTES, fp) != PNG_SIG_BYTES)
png_error(png_ctx, "invalid PNG file");
if (png_sig_cmp(magic, 0, PNG_SIG_BYTES))
png_error(png_ctx, "invalid PNG file");
/* set the input file stream and get the PNG image info: */
png_init_io(png_ctx, fp);
png_set_sig_bytes(png_ctx, PNG_SIG_BYTES);
png_read_info(png_ctx, info_ctx);
img_width = png_get_image_width(png_ctx, info_ctx);
img_height = png_get_image_height(png_ctx, info_ctx);
#if (1) /* PNG doesn't support zero area image? */
if (img_width == 0 || img_height == 0)
png_error(png_ctx, "zero area PNG image");
#endif
if (png_rgba_pixel_limit(img_width, img_height))
png_error(png_ctx, "PNG image exceeds pixel limits");
img_depth = png_get_bit_depth(png_ctx, info_ctx);
img_color_type = png_get_color_type(png_ctx, info_ctx);
/* ignored image interlacing, compression and filtering. */
/* force 8-bit color channels: */
if (img_depth == 16)
png_set_strip_16(png_ctx);
else if (img_depth < 8)
png_set_packing(png_ctx);
/* force formats to RGB: */
if (img_color_type != PNG_COLOR_TYPE_RGBA)
png_set_expand(png_ctx);
if (img_color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ctx);
if (img_color_type == PNG_COLOR_TYPE_GRAY)
png_set_gray_to_rgb(png_ctx);
if (img_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ctx);
/* add full opacity alpha channel if required: */
if (img_color_type != PNG_COLOR_TYPE_RGBA)
png_set_filler(png_ctx, 0xff, PNG_FILLER_AFTER);
/* apply the output transforms before reading image data: */
png_read_update_info(png_ctx, info_ctx);
/* allocate RGBA image data: */
img_data = (png_byte *)
malloc((size_t) (img_width * img_height * (4)));
if (img_data == NULL)
png_error(png_ctx, "error allocating image buffer");
/* allocate row pointers: */
row_data = (png_bytep *)
malloc((size_t) (img_height * sizeof(png_bytep)));
if (row_data == NULL)
png_error(png_ctx, "error allocating row pointers");
/* set the row pointers and read the RGBA image data: */
for (row = 0; row < img_height; row++)
row_data[row] = img_data +
(img_height - (row + 1)) * (img_width * (4));
png_read_image(png_ctx, row_data);
/* libpng and dynamic resource unwinding: */
png_read_end(png_ctx, NULL);
png_destroy_read_struct(& png_ctx, & info_ctx, NULL);
free(row_data);
*w = (unsigned) img_width, *h = (unsigned) img_height;
*buf = img_data; /* (asserts png_byte is an unsigned char) */
return (0);
}
/******************************************************************************/
int png_rgba_save
(
FILE *fp, unsigned w, unsigned h, const unsigned char *data)
{
png_structp png_ctx;
png_infop info_ctx;
png_uint_32 img_width, img_height, row;
img_width = (png_uint_32) w, img_height = (png_uint_32) h;
/* it is assumed that 'longjmp' can be invoked within this
* code to efficiently unwind resources for *all* errors. */
/* PNG structures and resource unwinding: */
if ((png_ctx = png_create_write_struct(
PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
return (1); /* ENOMEM (?) */
if ((info_ctx = png_create_info_struct(png_ctx)) == NULL)
{
png_destroy_write_struct(& png_ctx, NULL);
return (1); /* ENOMEM (?) */
}
if (setjmp(png_jmpbuf(png_ctx)) != 0)
{
png_destroy_write_struct(& png_ctx, & info_ctx);
return (1); /* libpng feedback (?) */
}
/* set the output file stream and set the PNG image HDR: */
png_init_io(png_ctx, fp);
if (png_rgba_pixel_limit(img_width, img_height))
png_error(png_ctx, "PNG image exceeds pixel limits");
png_set_IHDR(
png_ctx, info_ctx, img_width, img_height, (8),
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
/* write the RGBA image data from the bottom to top row: */
png_write_info(png_ctx, info_ctx);
for (row = img_height; row != 0; row--)
{
png_bytep row_data = (png_bytep)
(data + (row - 1) * (img_width * (4)));
png_write_row(png_ctx, row_data); /* non-interlaced. */
}
/* libpng and dynamic resource unwinding: */
png_write_end(png_ctx, NULL);
png_destroy_write_struct(& png_ctx, & info_ctx);
return (0); /* (much easier when the data format is known) */
}
/******************************************************************************/
#if (0)
int main (int argc, char **argv)
{
/* test the ability to read any PNG file and save as RGBA: */
FILE *ifile = NULL, *ofile = NULL;
int load_error, save_error;
unsigned int img_width, img_height;
unsigned char *img_data = NULL;
if (argc < 3)
{
fprintf(stderr, "png_rgba <infile> <outfile>\n");
return (1);
}
if ((ifile = fopen(argv[1], "rb")) == NULL)
goto fail;
load_error = png_rgba_load(
ifile, & img_width, & img_height, & img_data);
if (fclose(ifile) != 0) /* filesystem I/O error (?) */
goto fail;
if (load_error)
{
fprintf(stderr, "could not load '%s'\n", argv[1]);
return (1);
}
if ((ofile = fopen(argv[2], "wb")) == NULL)
goto fail;
save_error = png_rgba_save(
ofile, img_width, img_height, img_data);
if (fclose(ofile) != 0) /* filesystem I/O error (?) */
goto fail;
if (save_error)
{
fprintf(stderr, "could not save '%s'\n", argv[2]);
return (1);
}
fprintf(stdout, "%u x %u image\n", img_width, img_height);
return (0);
fail:
perror("png_rgba"); /* prepend to the system error message. */
return (1);
}
#endif
/******************************************************************************/
Most implementations of malloc will yield 8 byte alignment at a minimum - and usually 16 byte alignment on platforms like x86[-64]. For OpenGL, it's therefore safe to assume that rows are 4 byte (RGBA) aligned, in practice. This is the default for glPixelStore - if in doubt, use:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); prior to glTexImage2D calls.
You can also get the pixel data of a PNG with Soil.
I used it for setting up a window icon, but according to the cursor specification, this method should work there also.
GLFWimage icons[1];
icons[0].pixels = SOIL_load_image("icon.png", &icons[0].width, &icons[0].height, 0, SOIL_LOAD_RGBA);
glfwSetWindowIcon(window.window, 1, icons);
SOIL_free_image_data(icons[0].pixels);
Try putting this just prior to png_read_image():
png_set_expand(png_ptr); // expand to RGB or RGBA
png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER);
//"filler" contains the alpha value to assign to each pixel.
It's safe to do this even when your image is already RGB or RGBA.
Use filler=255 if you are writing an 8-bit-per-sample image and
you want the pixels to be opaque.
If loading png images into textures is the only thing you need to do, I suggest maybe ditching libpng and using stb_image instead. It's a very simple to use, header-only library that can give you raw RGB data from PNGs.

FreeImage delete[] bitmap data fails

Please help me with cleaning up my heap after loading bitmap with FreeImage.
Somehow
delete[] data;
causes _ASSERTE(_CrtIsValidHeapPointer(pUserData)) assertion, and I cannot found how to fix it other than commenting this line. Will there memory leak?
Any help and explanation will be appreciated!
Full code at pastebin: http://pastebin.com/dWxz0tjM
Visual Studio 2012 solution (with huge FreeImage static lib): http://rghost.ru/40322357 (15.7 Mbytes!)
Full code here:
#include <iostream>
// FreeImage static linkage
#define FREEIMAGE_LIB
#include "FreeImage/FreeImage.h"
#include "FreeImage/Utilities.h"
#pragma comment(lib, "FreeImage/FreeImaged.lib")
using namespace std;
static const wchar_t* sk_Filename = L"Test.tga";
// Error handler to use in callback
void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *msg)
{
char buf[1024];
sprintf_s(buf, 1024, "Error: %s", FreeImage_GetFormatFromFIF(fif));
cout << buf;
}
// Bitmap loader from FreeImage samples
FIBITMAP* GenericLoaderU(const wchar_t* lpszPathName, int flag)
{
FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
fif = FreeImage_GetFileTypeU(lpszPathName, 0);
if(fif == FIF_UNKNOWN)
{
fif = FreeImage_GetFIFFromFilenameU(lpszPathName);
}
if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif))
{
FIBITMAP *dib = FreeImage_LoadU(fif, lpszPathName, flag);
return dib;
}
return NULL;
}
// Function gets filename and returns bitmap data array, its size and bits per pixel
void GetData(const wchar_t* szFilename, unsigned char* data, unsigned int& width, unsigned int& height, unsigned int& bpp)
{
FIBITMAP* src = GenericLoaderU(szFilename, 0);
if(src == 0)
return;
FIBITMAP* src32 = FreeImage_ConvertTo32Bits(src);
FreeImage_Unload(src);
// Get picture info
width = FreeImage_GetWidth(src32);
height = FreeImage_GetHeight(src32);
bpp = FreeImage_GetBPP(src32);
unsigned int scan_width = width * bpp/8;
if((width == 0) || (height == 0) || (bpp == 0))
return;
memset(data, 0, height * scan_width);
SwapRedBlue32(src32); // Convert BGR to RGB
// Get bitmap data
FreeImage_ConvertToRawBits(data, src32, scan_width, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, TRUE);
FreeImage_Unload(src32);
return;
}
int main()
{
FreeImage_Initialise();
FreeImage_SetOutputMessage(FreeImageErrorHandler);
//Creating bitmap data array (size is unknown here)
unsigned char* data = new unsigned char[];
unsigned int width(0), height(0), bpp(0);
// Loading data here
GetData(sk_Filename, data, width, height, bpp);
//Using data here
cout << width << "x" << height << "x" << bpp << endl;
for (unsigned int i = 0; i < width * height * bpp/8; )
{
cout << "("
<< (unsigned int)data[i] << ", "
<< (unsigned int)data[i+1] << ", "
<< (unsigned int)data[i+2] << ", "
<< (unsigned int)data[i+3] << ")"
<< endl;
i += 4;
}
cout << endl;
//Cleanup
delete[] data; // <-- Breaks with _ASSERTE(_CrtIsValidHeapPointer(pUserData));
// What's wrong here?
system("pause");
return 0;
}
---EDIT--------------------------------
Okay, first possible solution is to use std::vector.
It has nothing to do with the delete.
The thing is that Debug Crt Runtime can check the memory integrity only during calls to memory API like: malloc, free, realloc, new, delete.
You have a memory overrun that is detected by the Crt.
Obviously, new unsigned char[] does not allocate enough bytes for you.
Move the allocation into the GetData() proc and call it like:
unsigned char* data = GetData(sk_Filename, width, height, bpp);
Write a function that calculates the size of data based on the image.
Then allocate data with that size
e.g.
size_z GetDataSize(const wchar_t* szFilename)
It's easy to calculate the required size inside your GetData function, so allocate the array there and return it instead.
You would have
unsigned char* GetData(const wchar_t* szFilename,
unsigned int& width,
unsigned int& height,
unsigned int& bpp);
which contains
unsigned char* data = new unsigned char[height * scan_width];
// Do the conversion...
return data;
and main would say
unsigned char* data = GetData(sk_Filename, width, height, bpp);

Examples or tutorials of using libjpeg-turbo's TurboJPEG

The instructions for libjpeg-turbo here describes the TurboJPEG API thus: "This API wraps libjpeg-turbo and provides an easy-to-use interface for compressing and decompressing JPEG images in memory". Great, but are there some solid examples of using this API available? Just looking to decompress a fairly vanilla jpeg in memory.
I've found a few bits such as https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c, which appears to be using the TurboJPEG API, but are there any more solid/varied examples?
The source for libjpeg-turbo is well documented, so that does help.
Ok, I know that you did already solve your problem, but as some people, just like me, could be searching some simple example I will share what I created.
It is an example, compressing and decompressing an RGB image. Otherwise I think that the API documentation of TurboJPEG is quite easy to understand!
Compression:
#include <turbojpeg.h>
const int JPEG_QUALITY = 75;
const int COLOR_COMPONENTS = 3;
int _width = 1920;
int _height = 1080;
long unsigned int _jpegSize = 0;
unsigned char* _compressedImage = NULL; //!< Memory is allocated by tjCompress2 if _jpegSize == 0
unsigned char buffer[_width*_height*COLOR_COMPONENTS]; //!< Contains the uncompressed image
tjhandle _jpegCompressor = tjInitCompress();
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
tjDestroy(_jpegCompressor);
//to free the memory allocated by TurboJPEG (either by tjAlloc(),
//or by the Compress/Decompress) after you are done working on it:
tjFree(&_compressedImage);
After that you have the compressed image in _compressedImage.
To decompress you have to do the following:
Decompression:
#include <turbojpeg.h>
long unsigned int _jpegSize; //!< _jpegSize from above
unsigned char* _compressedImage; //!< _compressedImage from above
int jpegSubsamp, width, height;
unsigned char buffer[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image
tjhandle _jpegDecompressor = tjInitDecompress();
tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, &height, &jpegSubsamp);
tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, buffer, width, 0/*pitch*/, height, TJPF_RGB, TJFLAG_FASTDCT);
tjDestroy(_jpegDecompressor);
Some random thoughts:
I just came back over this as I am writing my bachelor thesis, and I noticed that if you run the compression in a loop it is preferable to store the biggest size of the JPEG buffer to not have to allocate a new one every turn. Basically, instead of doing:
long unsigned int _jpegSize = 0;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
we would add an object variable, holding the size of the allocated memory long unsigned int _jpegBufferSize = 0; and before every compression round we would set the jpegSize back to that value:
long unsigned int jpegSize = _jpegBufferSize;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
_jpegBufferSize = _jpegBufferSize >= jpegSize? _jpegBufferSize : jpegSize;
after the compression one would compare the memory size with the actual jpegSize and set it to the jpegSize if it is higher than the previous memory size.
I ended up using below code as a working example for both JPEG encoding and decoding. Best example that I can find, it's self-contained that initializes a dummy image and output the encoded image to a local file.
Below code is NOT my own, credit goes to https://sourceforge.net/p/libjpeg-turbo/discussion/1086868/thread/e402d36f/#8722 . Posting it here again to help anyone finds it's difficult to get libjpeg turbo working.
#include "turbojpeg.h"
#include <iostream>
#include <string.h>
#include <errno.h>
using namespace std;
int main(void)
{
unsigned char *srcBuf; //passed in as a param containing pixel data in RGB pixel interleaved format
tjhandle handle = tjInitCompress();
if(handle == NULL)
{
const char *err = (const char *) tjGetErrorStr();
cerr << "TJ Error: " << err << " UNABLE TO INIT TJ Compressor Object\n";
return -1;
}
int jpegQual =92;
int width = 128;
int height = 128;
int nbands = 3;
int flags = 0;
unsigned char* jpegBuf = NULL;
int pitch = width * nbands;
int pixelFormat = TJPF_GRAY;
int jpegSubsamp = TJSAMP_GRAY;
if(nbands == 3)
{
pixelFormat = TJPF_RGB;
jpegSubsamp = TJSAMP_411;
}
unsigned long jpegSize = 0;
srcBuf = new unsigned char[width * height * nbands];
for(int j = 0; j < height; j++)
{
for(int i = 0; i < width; i++)
{
srcBuf[(j * width + i) * nbands + 0] = (i) % 256;
srcBuf[(j * width + i) * nbands + 1] = (j) % 256;
srcBuf[(j * width + i) * nbands + 2] = (j + i) % 256;
}
}
int tj_stat = tjCompress2( handle, srcBuf, width, pitch, height,
pixelFormat, &(jpegBuf), &jpegSize, jpegSubsamp, jpegQual, flags);
if(tj_stat != 0)
{
const char *err = (const char *) tjGetErrorStr();
cerr << "TurboJPEG Error: " << err << " UNABLE TO COMPRESS JPEG IMAGE\n";
tjDestroy(handle);
handle = NULL;
return -1;
}
FILE *file = fopen("out.jpg", "wb");
if (!file) {
cerr << "Could not open JPEG file: " << strerror(errno);
return -1;
}
if (fwrite(jpegBuf, jpegSize, 1, file) < 1) {
cerr << "Could not write JPEG file: " << strerror(errno);
return -1;
}
fclose(file);
//write out the compress date to the image file
//cleanup
int tjstat = tjDestroy(handle); //should deallocate data buffer
handle = 0;
}
In the end I used a combination of random code found on the internet (e.g. https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c) and the .c and header files for libjeg-turbo, which are well documented.
This official API is a good information source aswell.
Here's a fragment of code what I use to load jpeg's from memory. Maybe it will require a bit of fixing, because I extracted it from different files in my project. It will load both - grayscale and rgb images (bpp will be set either to 1 or to 3).
struct Image
{
int bpp;
int width;
int height;
unsigned char* data;
};
struct jerror_mgr
{
jpeg_error_mgr base;
jmp_buf jmp;
};
METHODDEF(void) jerror_exit(j_common_ptr jinfo)
{
jerror_mgr* err = (jerror_mgr*)jinfo->err;
longjmp(err->jmp, 1);
}
METHODDEF(void) joutput_message(j_common_ptr)
{
}
bool Image_LoadJpeg(Image* image, unsigned char* img_data, unsigned int img_size)
{
jpeg_decompress_struct jinfo;
jerror_mgr jerr;
jinfo.err = jpeg_std_error(&jerr.base);
jerr.base.error_exit = jerror_exit;
jerr.base.output_message = joutput_message;
jpeg_create_decompress(&jinfo);
image->data = NULL;
if (setjmp(jerr.jmp)) goto bail;
jpeg_mem_src(&jinfo, img_data, img_size);
if (jpeg_read_header(&jinfo, TRUE) != JPEG_HEADER_OK) goto bail;
jinfo.dct_method = JDCT_FLOAT; // change this to JDCT_ISLOW on Android/iOS
if (!jpeg_start_decompress(&jinfo)) goto bail;
if (jinfo.num_components != 1 && jinfo.num_components != 3) goto bail;
image->data = new (std::nothrow) unsigned char [jinfo.output_width * jinfo.output_height * jinfo.output_components];
if (!image->data) goto bail;
{
JSAMPROW ptr = image->data;
while (jinfo.output_scanline < jinfo.output_height)
{
if (jpeg_read_scanlines(&jinfo, &ptr, 1) != 1) goto bail;
ptr += jinfo.output_width * jinfo.output_components;
}
}
if (!jpeg_finish_decompress(&jinfo)) goto bail;
image->bpp = jinfo.output_components;
image->width = jinfo.output_width;
image->height = jinfo.output_height;
jpeg_destroy_decompress(&jinfo);
return true;
bail:
jpeg_destroy_decompress(&jinfo);
if (image->data) delete [] data;
return false;
}