Something to allow C++ program to output PNG file
I'm using GCC/G++, but I have absolutely no clue how to use the libpng library. I read there was a C++ wrapper for it as well, but I have no idea how to work that either.
Has anyone used C++ to output a PNG file? I'm looking for a library that's easy to set up and get cracking on in C++.
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Well, tried to get libpng to compile as library in NetBeans using GCC. That didn't work at all, all sorts of missing header files and stuff from the source code I downloaded. Oy vey.
I might just as well learn the format of PNG and create my own class to edit it! I don't need the program to display it, only to output PNG files!
EDIT: You know, why don't I just create 24-bit bitmaps instead? I've done that before and they're a lot simpler. I can use paint to convert them to PNG.
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Alright, got the class going. BMP's remind me of the fun of padding rows so they are divisible by 4 in length:
struct b24_rgb {
unsigned char red;
unsigned char green;
unsigned char blue;
};
class bmp_24 {
public:
//destructor
~bmp_24() {
delete []image;
}
//constructor that sets up size and background color
bmp_24(unsigned long width, unsigned long height, b24_rgb background_color) :
width(width), height(height), padding((4-width*3%4)%4), size_pixels(width*height),
size_bytes((width*3+padding)*height), image(new unsigned char[size_bytes]) {
for(unsigned long i = 0; i < size_bytes; ++i)
image[i] = 0;
for(unsigned long x = 0; x < width; ++x)
for(unsigned long y = 0; y < height; ++y)
plot(x, y, background_color);
}
//plots a single pixel of color specified by b24_rgb object
void plot(unsigned long x, unsigned long y, b24_rgb rgb) {
unsigned long pos = (height - y - 1) * (width * 3 + padding) + x * 3;
image[pos] = rgb.blue;
image[pos+1] = rgb.green;
image[pos+2] = rgb.red;
}
//draws rectangle with fill (no stroke)
void rect(unsigned long x, unsigned long y, unsigned long wid, unsigned long hgt,
b24_rgb rgb) {
if(x + wid > width) wid = width - x;
if(y + hgt > height) hgt = height - y;
for(unsigned i = x; i < wid + x; ++i)
for(unsigned j = y; j < hgt + y; ++j)
plot(i, j, rgb);
}
//saves 24-bit bitmap to a file that can be read by MSPaint
//or Microsoft Photo Viewer, as well as other programs.
bool save(const char* filename) {
unsigned short magic = 0x4d42;
unsigned long blank32 = 0;
unsigned long offset = 0x36;
unsigned long header = 0x28;
unsigned long file_size = offset + size_bytes;
unsigned short planes = 0x1;
unsigned short bitcnt = 0x18;
std::fstream of;
try {
of.open(filename, std::ios::binary | std::ios::out);
if(!of.good()) throw(0);
of.write((char*) &magic, sizeof(magic));
of.write((char*) &file_size, sizeof(file_size));
of.write((char*) &blank32, sizeof(file_size));
of.write((char*) &offset, sizeof(file_size));
of.flush();
if(!of.good()) throw(1);
of.write((char*) &header, sizeof(header));
of.write((char*) &width, sizeof(width));
of.write((char*) &height, sizeof(height));
of.write((char*) &planes, sizeof(planes));
of.write((char*) &bitcnt, sizeof(bitcnt));
of.write((char*) &blank32, sizeof(blank32));
of.write((char*) &blank32, sizeof(blank32));
of.write((char*) &blank32, sizeof(blank32));
of.write((char*) &blank32, sizeof(blank32));
of.write((char*) &blank32, sizeof(blank32));
of.write((char*) &blank32, sizeof(blank32));
of.flush();
if(!of.good()) throw(1);
of.write((char*) image, size_bytes);
of.flush();
if(!of.good()) throw(1);
}
catch(int val) {
if(val) {
of.close();
}
return false;
}
of.close();
return true;
}
private:
unsigned long width;
unsigned long height;
unsigned long padding;
unsigned long size_pixels;
unsigned long size_bytes;
unsigned char* image;
};
EDIT: Improved by adding POD-struct b24_rgb which is passed instead of individual colors. Can do b24_rgb{red, green, blue} when calling constructor or plot.
I plan to use this to demonstrate range_map ranges, sub_ranges, etc., by visual impact. To get an idea of their construction. First, though, I should create a way to make a rectangle object.
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Last edited by beneficii on 25 May 2014, 11:29 pm, edited 3 times in total.
For less platform-dependent programming, this has also gotten me to create a little_endian class. It is designed to work with <fstream> to read basic data types from and/or write basic data types to binary files whose format calls for all data to be stored as little-endian. This is designed so that even on systems that store things like int in a big-endian way, you can still easily read/write little-endian in binary files that need to be stored in little-endian. Can work with up to 64-bit data types (like unsigned long long).
Here is the class (not tested yet). UPDATE: write() works with fstream type on little-endian system.
#include <cstring>
#if __cplusplus >= 201103L
#include <utility>
#else
#include <algorithm>
#endif
class little_endian {
public:
//constructor tests if system is little-endian or not; stores result
little_endian() {
unsigned short tester = 1;
std::strcpy(conversion, (char *) &tester);
if(conversion[0])
is_sys_le = true;
else
is_sys_le = false;
}
//writes to "of" a basic data type T in little-endian
//"of" can also be pointer to either ofstream or fstream (derived classes)
template<class T>
void write(std::ostream* of, T out) {
std::size_t size = sizeof(T);
if(size > 8) size = 8;
std::memcpy((void*) conversion, (void*) &out, size);
conversion_func(size);
of->write(conversion, size);
}
//reads little-endian from "inf" and stores basic data type T
//which is returned
//"inf" can also be pointer to either ifstream or fstream (derived classes)
template<class T>
T read(std::istream* inf) {
std::size_t size = sizeof(T);
if(size > 8) size = 8;
inf->read(conversion, size);
conversion_func(size);
T ret;
std::memcpy((void*) &ret, (void*) conversion, size);
return ret;
}
private:
//reverses order of bytes if system is big-endian for conversion
void conversion_func(std::size_t size) {
if(!is_sys_le) {
for(int i = 0; i < size/2; ++i)
std::swap(conversion[i], conversion[size-i-1]);
}
}
bool is_sys_le;
char conversion[8];
};
I wonder if I should give it a whirl on the bmp_24 class. Of course, true testing would require a big-endian system.
EDIT: Just need to change <algorithm> to <cstring>, since that's where strcpy() and memcpy() are declared.
EDIT 2: Made slight fix to remove warnings (added & to integer).
EDIT 3: Change [size-i] to [size-i-1] (d'oh!).
EDIT 4: Made sure to also include header that has swap (utility in C++11, algorithm otherwise)
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Last edited by beneficii on 25 May 2014, 1:55 pm, edited 5 times in total.
Here is the bitmap class using the little_endian class. It actually makes the save function easier to read (and it works!), showing that at least the write() method of little_endian works on a little_endian system:
struct b24_rgb {
unsigned char red;
unsigned char green;
unsigned char blue;
};
class bmp_24 {
public:
~bmp_24() {
delete []image;
}
bmp_24(unsigned long width, unsigned long height, b24_rgb background_color) :
width(width), height(height), padding((4-width*3%4)%4), size_pixels(width*height),
size_bytes((width*3+padding)*height), image(new unsigned char[size_bytes]) {
for(unsigned long i = 0; i < size_bytes; ++i)
image[i] = 0;
for(unsigned long x = 0; x < width; ++x)
for(unsigned long y = 0; y < height; ++y)
plot(x, y, background_color);
}
void plot(unsigned long x, unsigned long y, b24_rgb rgb) {
unsigned long pos = (height - y - 1) * (width * 3 + padding) + x * 3;
image[pos] = rgb.blue;
image[pos+1] = rgb.green;
image[pos+2] = rgb.red;
}
void rect(unsigned long x, unsigned long y, unsigned long wid, unsigned long hgt,
b24_rgb rgb) {
if(x + wid > width) wid = width - x;
if(y + hgt > height) hgt = height - y;
for(unsigned i = x; i < wid + x; ++i)
for(unsigned j = y; j < hgt + y; ++j)
plot(i, j, rgb);
}
bool save(const char* filename) {
unsigned short magic = 0x4d42;
unsigned long blank32 = 0;
unsigned long offset = 0x36;
unsigned long header = 0x28;
unsigned long file_size = offset + size_bytes;
unsigned short planes = 0x1;
unsigned short bitcount = 0x18;
std::fstream of;
little_endian le;
try {
of.open(filename, std::ios::binary | std::ios::out);
if(!of.good()) throw(0);
le.write(&of, magic);
le.write(&of, file_size);
le.write(&of, blank32);
le.write(&of, offset);
of.flush();
if(!of.good()) throw(1);
le.write(&of, header);
le.write(&of, width);
le.write(&of, height);
le.write(&of, planes);
le.write(&of, bitcount);
le.write(&of, blank32);
le.write(&of, blank32);
le.write(&of, blank32);
le.write(&of, blank32);
le.write(&of, blank32);
le.write(&of, blank32);
of.flush();
if(!of.good()) throw(1);
of.write((char*) image, size_bytes);
of.flush();
if(!of.good()) throw(1);
}
catch(int val) {
if(val) {
of.close();
}
return false;
}
of.close();
return true;
}
private:
unsigned long width;
unsigned long height;
unsigned long padding;
unsigned long size_pixels;
unsigned long size_bytes;
unsigned char* image;
};
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Last edited by beneficii on 25 May 2014, 11:29 pm, edited 3 times in total.
Another thing, if you wanted to create a big_endian class, for cases where the data stored in a binary file type has to be big-endian, not much change would be needed. Only these 3 changes would be needed:
1. Change name of class to big_endian.
2. Change name of constructor to big_endian().
3. Remove NOT (!) operator from is_sys_le in conversion_func() method.
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Alright, got the rectangle going! Now to take C++11's <random> and to start producing visual depictions of the ranges to help increase their understanding!
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Made adjustments so that position (0, 0) on the bitmap is at the top left instead of the top right.
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
A result from the range map thread! (Original BMP converted to PNG):
[img][800:1000]http://i58.tinypic.com/a4yfxk.png[/img]
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Here is the bmp_24 class:
http://www.beneficii.net/bmp_24.zip
All code is implemented in the 2 header files. Simply put in your project's folder and include "bmp_24.hpp" and you can use the POD-struct b24_rgb to set up the colors and the class bmp_24 to implement the bitmap.
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Because I have to fill the space up. It's the part of the header that tells the program reading the bitmap that it's compressed. In the Windows specification, you have to use all 40 bytes of the header, so I just put all 0's because I didn't use compression.
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
Updated range_map, with increased allocator support. The pointer your allocator returns can now be a wrapper class for the memory used by the range_map as well as for storing handles to memory where the actual range and associated objects of the range_map's node object are stored.
New file name here:
http://beneficii.net/range_map.zip
Remember, this library is header only. For now, place it in the same folder as your product files and use the following code to include the range_map class container template:
_________________
"You have a responsibility to consider all sides of a problem and a responsibility to make a judgment and a responsibility to care for all involved." --Ian Danskin
