I'm trying to port the C openGL texture loading code found here:
http://www.nullterminator.net/gltexture.html
to C++. In particular I'm trying to deal with reading some textures in from a file, what is the best way of rewriting the following code in an idiomatic and portable manner:
GLuint texture;
int width = 256, height = 256;
BYTE * data;
FILE * file;
// open texture data
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
// allocate buffer
data = malloc( width * height * 3 );
// read texture data
fread( data, width * height * 3, 1, file );
fclose( file );
In particular what is the best way of replacing the BYTE macro in a c++ way that is portable?
EDIT: BYTE macro is not defined in the current environment I am working in. I was trying to figure out what the underlying type of this is on other systems so that I can typedef for the correct type.
Assuming the original code is portable, you can just leave it. Just make sure you pull in the definition of BYTE as is. C++ compilers are backwards compatible to C, so the corresponding headers are still there.
(If BYTE is really a macro, I'd perhaps typedef it.)
The C code should work just fine when compiled as C++.
Rather than use the BYTE type, just use the OpenGL-defined type GLbyte, which is the actual type the APIs take anyway. It is defined in gl.h thus:
typedef signed char GLbyte;
A very quick (untested!) translation of the above code into C++ would be something like:
GLuint texture;
unsigned width = 256, height = 256;
unsigned buffer_size = width * height * 3;
GLbyte * data;
std::ifstream file;
// open texture data
file.open(filename, ios_base::in | ios_base::binary);
if (!file) return 0;
// allocate buffer
data = new BYTE[buffer_size];
// read texture data
file.read(data, buffer_size);
file.close();
// Process data...
// ...
// Don't forget to release it when you're done!
delete [] data;
BYTE* in this case seems to be just a macro for char* or unsigned char*. I could be wrong but I doubt it. So using char* or unsigned char* in your program would be equivalent. However if you are porting from C to C++ you might want to consider using ifstream (in binary mode) from the C++ standard library.
Use unsigned char instead of BYTE - should work as expected (you might have to cast the return value of malloc().
Related
I did a sample project to read a file into a buffer.
When I use the tellg() function it gives me a larger value than the
read function is actually read from the file. I think that there is a bug.
here is my code:
EDIT:
void read_file (const char* name, int *size , char*& buffer)
{
ifstream file;
file.open(name,ios::in|ios::binary);
*size = 0;
if (file.is_open())
{
// get length of file
file.seekg(0,std::ios_base::end);
int length = *size = file.tellg();
file.seekg(0,std::ios_base::beg);
// allocate buffer in size of file
buffer = new char[length];
// read
file.read(buffer,length);
cout << file.gcount() << endl;
}
file.close();
}
main:
void main()
{
int size = 0;
char* buffer = NULL;
read_file("File.txt",&size,buffer);
for (int i = 0; i < size; i++)
cout << buffer[i];
cout << endl;
}
tellg does not report the size of the file, nor the offset
from the beginning in bytes. It reports a token value which can
later be used to seek to the same place, and nothing more.
(It's not even guaranteed that you can convert the type to an
integral type.)
At least according to the language specification: in practice,
on Unix systems, the value returned will be the offset in bytes
from the beginning of the file, and under Windows, it will be
the offset from the beginning of the file for files opened in
binary mode. For Windows (and most non-Unix systems), in text
mode, there is no direct and immediate mapping between what
tellg returns and the number of bytes you must read to get to
that position. Under Windows, all you can really count on is
that the value will be no less than the number of bytes you have
to read (and in most real cases, won't be too much greater,
although it can be up to two times more).
If it is important to know exactly how many bytes you can read,
the only way of reliably doing so is by reading. You should be
able to do this with something like:
#include <limits>
file.ignore( std::numeric_limits<std::streamsize>::max() );
std::streamsize length = file.gcount();
file.clear(); // Since ignore will have set eof.
file.seekg( 0, std::ios_base::beg );
Finally, two other remarks concerning your code:
First, the line:
*buffer = new char[length];
shouldn't compile: you have declared buffer to be a char*,
so *buffer has type char, and is not a pointer. Given what
you seem to be doing, you probably want to declare buffer as
a char**. But a much better solution would be to declare it
as a std::vector<char>& or a std::string&. (That way, you
don't have to return the size as well, and you won't leak memory
if there is an exception.)
Second, the loop condition at the end is wrong. If you really
want to read one character at a time,
while ( file.get( buffer[i] ) ) {
++ i;
}
should do the trick. A better solution would probably be to
read blocks of data:
while ( file.read( buffer + i, N ) || file.gcount() != 0 ) {
i += file.gcount();
}
or even:
file.read( buffer, size );
size = file.gcount();
EDIT: I just noticed a third error: if you fail to open the
file, you don't tell the caller. At the very least, you should
set the size to 0 (but some sort of more precise error
handling is probably better).
In C++17 there are std::filesystem file_size methods and functions, so that can streamline the whole task.
std::filesystem::file_size - cppreference.com
std::filesystem::directory_entry::file_size - cppreference.com
With those functions/methods there's a chance not to open a file, but read cached data (especially with the std::filesystem::directory_entry::file_size method)
Those functions also require only directory read permissions and not file read permission (as tellg() does)
void read_file (int *size, char* name,char* buffer)
*buffer = new char[length];
These lines do look like a bug: you create an char array and save to buffer[0] char. Then you read a file to buffer, which is still uninitialized.
You need to pass buffer by pointer:
void read_file (int *size, char* name,char** buffer)
*buffer = new char[length];
Or by reference, which is the c++ way and is less error prone:
void read_file (int *size, char* name,char*& buffer)
buffer = new char[length];
...
fseek(fptr, 0L, SEEK_END);
filesz = ftell(fptr);
will do the file if file opened through fopen
using ifstream,
in.seekg(0,ifstream::end);
dilesz = in.tellg();
would do similar
I have an unsigned char array in c++, and I want to save it's bits into a file.
So for example if I have the array below
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
I want to save this into the file so that the binary data of the file looks like this (without the spaces).
00000000 00000001 00000010
I have tried this code below but it doesn't seem to work. I give the program a blank file that is 0 bytes in size, and after the program runs the file remains 0 bytes in size (nothing gets written to it).
// Create unsigned char array
unsigned char *arrayToWrite= (unsigned char*)malloc(sizeof(unsigned char)*720);
// Populate it with some test values
arrayToWrite[0] = 0;
arrayToWrite[1] = 1;
arrayToWrite[2] = 2;
// Open the file I want to write to
FILE *dat;
dat = fopen("filePath.bin", "wb");
// Write array to file
fwrite(&arrayToWrite, sizeof(char)*720, 1, dat);
fclose(dat);
I would expect after this program runs that the file "filePath.bin" would be 720 bytes long filled with mostly 0's except for the first and second position I filled with test data.
What am I doing wrong? Any help would be much appreciated! Thank you!
The basic issue there is that you're passing the pointer to your arrayToWrite variable, rather than to the array itself. Change fwrite(&arrayToWrite... to fwrite(arrayToWrite....
BTW, malloc() does NOT promise to give you zeroed memory. For that, use calloc() to allocate and zero memory or memset() to zero already-allocated memory. (Though all that is more a C thing; for C++ you'd be better off using something like std::vector instead of raw C arrays.)
C++ code is written different way:
std::vector<unsigned char> vectorToWrite = { 0, 1, 2 };
vectorToWrite.resize( 720 );
std::ofstream file( "filePath.bin", std::ios::out | std::ios::binary );
if( !file ) {
// error handling
}
file.write( vectorToWrite.data(), vectorToWrite.size() * sizeof( decltype(vectorToWrite)::value_type ) );
Note: I put sizeof( decltype(vectorToWrite)::value_type ) there so if you later change vector data it will still properly work without further change, but in case of char it can be omitted completely as sizeof(char) is always equal to 1
I am trying to get sound from simple tapping keyboard. Looks like a little drum machine.
If DirectSound is not a proper way to do this, please suggest something else.
In my code I don't know what's wrong. Here it is without error checking and with translations:
//Declaring the IDirectSound object
IDirectSound* device;
DirectSoundCreate(NULL, &device, NULL);
device->SetCooperativeLevel(hWnd, DSSCL_NORMAL );
/* Declaring secondary buffers */
IDirectSoundBuffer* kickbuf;
IDirectSoundBuffer* snarebuf;
/* Declaring .wav files pointers
And to structures for reading the information int the begining of the .wav file */
FILE* fkick;
FILE* fsnare;
sWaveHeader kickHdr;
sWaveHeader snareHdr;
The structure sWaveHeader is declared this way:
typedef struct sWaveHeader
{
char RiffSig[4]; // 'RIFF'
unsigned long WaveformChunkSize; // 8
char WaveSig[4]; // 'WAVE'
char FormatSig[4]; // 'fmt '
unsigned long FormatChunkSize; // 16
unsigned short FormatTag; // WAVE_FORMAT_PCM
unsigned short Channels; // Channels
unsigned long SampleRate;
unsigned long BytesPerSec;
unsigned short BlockAlign;
unsigned short BitsPerSample;
char DataSig[4]; // 'data'
unsigned long DataSize;
} sWaveHeader;
The .wav file opening
#define KICK "D:/muzic/kick.wav"
#define SNARE "D:/muzic/snare.wav"
fkick = fopen(KICK, "rb")
fsnare = fopen(SNARE, "rb")
Here I make a function that does the common work for snarebuf* and **kickbuf
int read_wav_to_WaveHeader (sWaveHeader* , FILE* , IDirectSoundBuffer* ); // The declaring
But I wil not write this function, just show the way it works with kickbuf, for instance.
fseek(fkick, 0, SEEK_SET); // Zero the position in file
fread(&kickHdr, 1, sizeof(sWaveHeader), fkick); // reading the sWaveHeader structure from file
Here goes a checking for fitting if sWaveHeader structure:
if(memcmp(pwvHdr.RiffSig, "RIFF", 4) ||
memcmp(pwvHdr.WaveSig, "WAVE", 4) ||
memcmp(pwvHdr.FormatSig, "fmt ", 4) ||
memcmp(pwvHdr.DataSig, "data", 4))
return 1;
Declaring the format and descriptor for a buffer and filling them:
DSBUFFERDESC bufDesc;
WAVEFORMATEX wvFormat;
ZeroMemory(&wvFormat, sizeof(WAVEFORMATEX));
wvFormat.wFormatTag = WAVE_FORMAT_PCM;
wvFormat.nChannels = kickHdr.Channels;
wvFormat.nSamplesPerSec = kickHdr.SampleRate;
wvFormat.wBitsPerSample = kickHdr.BitsPerSample;
wvFormat.nBlockAlign = wvFormat.wBitsPerSample / 8 * wvFormat.nChannels;
ZeroMemory(&bufDesc, sizeof(DSBUFFERDESC));
bufDesc.dwSize = sizeof(DSBUFFERDESC);
bufDesc.dwFlags = DSBCAPS_CTRLVOLUME |
DSBCAPS_CTRLPAN |
DSBCAPS_CTRLFREQUENCY;
bufDesc.dwBufferBytes = kickHdr.DataSize;
bufDesc.lpwfxFormat = &wvFormat;
Well, the creating of a buffer:
device->CreateSoundBuffer(&bufDesc, &kickbuf, NULL); // Any mistakes by this point?
Now locking the buffer and loading some data to it.
This data starts after sizeof(sWaveHeader) bytes in a WAVE file, am I wrong?
LPVOID Ptr1; // pointer on a pointer on a First block of data
LPVOID Ptr2; // pointer on a pointer on a Second block of data
DWORD Size1, Size2; // their sizes
Now calling the Lock() method:
kickbuf->Lock((DWORD)LockPos, (DWORD)Size,
&Ptr1, &Size1,
&Ptr2, &Size2, 0);
Loading data (is it ok?):
fseek(fkick, sizeof(sWaveHeader), SEEK_SET);
fread(Ptr1, 1, Size1, fkick);
if(Ptr2 != NULL)
fread(Ptr2, 1, Size2, fkick);
Unlocking the buffer:
kickbuf->Unlock(Ptr1, Size1, Ptr2, Size2);
Setting the volume:
kickbuf->SetVolume(-2500);
Then I make a wile(1) looping:
1. ask for a key pressing
2. if it is pressed:
kickbuf->SetCurrentPosition(0)
kickbuf->Play(0,0,0);
But there's no sound playing, please say, what is not proper in my code or maybe in the whole concept. Thank you.
When you initialize the WAVEFORMATEX, your are forgetting to set the nAvgBytesPerSec member. Add this line after the initialization of wvFormat.nBlockAlign:
wvFormat.nAvgBytesPerSec = wvFormat.nSamplesPerSec * wvFormat.nBlockAlign;
Also, I suspect this could be a problem:
kickbuf->SetVolume(-2500);
I suspect that will just attenuate your sample to absolute silence. Try taking that call out so that it plays at full volume.
But more likely, none of you sample code above shows validation of the return values from any of the DirectSound APIs, nor any of the file I/O values. Have you validated the HRESULTs returned by all the DSound APIs are returning S_OK? Have you tried printing or using OutputDebugString to print the values you computed for the members of WAVEFORMATEX?
Have you debugging the fread calls to validate that you are getting valid data into your buffers?
Hope this helps.
I am experimenting with reading the width and height of a PNG file.
This is my code:
struct TImageSize {
int width;
int height;
};
bool getPngSize(const char *fileName, TImageSize &is) {
std::ifstream file(fileName, std::ios_base::binary | std::ios_base::in);
if (!file.is_open() || !file) {
file.close();
return false;
}
// Skip PNG file signature
file.seekg(9, std::ios_base::cur);
// First chunk: IHDR image header
// Skip Chunk Length
file.seekg(4, std::ios_base::cur);
// Skip Chunk Type
file.seekg(4, std::ios_base::cur);
__int32 width, height;
file.read((char*)&width, 4);
file.read((char*)&height, 4);
std::cout << file.tellg();
is.width = width;
is.height = height;
file.close();
return true;
}
If I try to read for example from this image from Wikipedia, I'm getting these wrong values:
252097920 (should be 800)
139985408 (should be 600)
Note that the function is not returning false so the contents of the width and height variables must come from the file.
It looks like you're off by a byte:
// Skip PNG file signature
file.seekg(9, std::ios_base::cur);
The PNG Specification says the header is 8 bytes long, so you want that "9" to be an "8" instead. Positions start at 0.
Also note that the spec says that integers are in network (big-endian) order, so you may want or need to use ntohl() or otherwise convert byte order if you're on a little-endian system.
It's probably worth using libpng or stb_image or something similar rather than attempting to parse the png yourself, though -- unless you're doing this to learn.
When you look at Portable Network Graphics Technical details, it says the signature is 8 bytes not 9.
Plus, are you sure your system has the same byte order as the PNG standard? ntohl(3) will ensure the correct byte order. It's available for windows also.
How can I write only every third item in a char buffer to file quickly in C++?
I get a three-channel image from my camera, but each channel contains the same info (the image is grayscale). I'd like to write only one channel to disk to save space and make the writes faster, since this is part of a real-time, data collection system.
C++'s ofstream::write command seems to only write contiguous blocks of binary data, so my current code writes all three channels and runs too slowly:
char * data = getDataFromCamera();
int dataSize = imageWidth * imageHeight * imageChannels;
std::ofstream output;
output.open( fileName, std::ios::out | std::ios::binary );
output.write( data, dataSize );
I'd love to be able to replace the last line with a call like:
int skipSize = imageChannels;
output.write( data, dataSize, skipSize );
where skipSize would cause write to put only every third into the output file. However, I haven't been able to find any function that does this.
I'd love to hear any ideas for getting a single channel written to disk quickly.
Thanks.
You'll probably have to copy every third element into a buffer, then write that buffer out to disk.
You can use a codecvt facet on a local to filter out part of the output.
Once created you can imbue any stream with the appropraite local and it will only see every third character on the input.
#include <locale>
#include <fstream>
#include <iostream>
class Filter: public std::codecvt<char,char,mbstate_t>
{
public:
typedef std::codecvt<char,char,mbstate_t> MyType;
typedef MyType::state_type state_type;
typedef MyType::result result;
// This indicates that we are converting the input.
// Thus forcing a call to do_out()
virtual bool do_always_noconv() const throw() {return false;}
// Reads from -> from_end
// Writes to -> to_end
virtual result do_out(state_type &state,
const char *from, const char *from_end, const char* &from_next,
char *to, char *to_limit, char* &to_next) const
{
// Notice the increment of from
for(;(from < from_end) && (to < to_limit);from += 3,to += 1)
{
(*to) = (*from);
}
from_next = from;
to_next = to;
return((to > to_limit)?partial:ok);
}
};
Once you have the facet all you need is to know how to use it:
int main(int argc,char* argv[])
{
// construct a custom filter locale and add it to a local.
const std::locale filterLocale(std::cout.getloc(), new Filter());
// Create a stream and imbue it with the locale
std::ofstream saveFile;
saveFile.imbue(filterLocale);
// Now the stream is imbued we can open it.
// NB If you open the file stream first.
// Any attempt to imbue it with a local will silently fail.
saveFile.open("Test");
saveFile << "123123123123123123123123123123123123123123123123123123";
std::vector<char> data[1000];
saveFile.write( &data[0], data.length() /* The filter implements the skipSize */ );
// With a tinay amount of extra work
// You can make filter take a filter size
// parameter.
return(0);
}
Let's say your buffer is 24-bit RGB, and you're using a 32-bit processor (so that operations on 32-bit entities are the most efficient).
For the most speed, let's work with a 12-byte chunk at a time. In twelve bytes, we'll have 4 pixels, like so:
AAABBBCCCDDD
Which is 3 32-bit values:
AAAB
BBCC
CDDD
We want to turn this into ABCD (a single 32-bit value).
We can create ABCD by applying a mask to each input and ORing.
ABCD = A000 | 0BC0 | 000D
In C++, with a little-endian processor, I think it would be:
unsigned int turn12grayBytesInto4ColorBytes( unsigned int buf[3] )
{
return (buf[0]&0x000000FF) // mask seems reversed because of little-endianness
| (buf[1]&0x00FFFF00)
| (buf[2]&0xFF000000);
}
It's probably fastest to do this another conversion to another buffer and THEN dump to disk, instead of going directly to disk.
There is no such a functionality in the standardlibrary afaik. Jerry Coffin's solution will work best. I wrote a simple snippet which should do the trick:
const char * data = getDataFromCamera();
const int channelNum = 0;
const int channelSize = imageWidth * imageHeight;
const int dataSize = channelSize * imageChannels;
char * singleChannelData = new char[channelSize];
for(int i=0; i<channelSize ++i)
singleChannelData[i] = data[i*imageChannels];
try {
std::ofstream output;
output.open( fileName, std::ios::out | std::ios::binary );
output.write( singleChannelData, channelSize );
}
catch(const std::ios_base::failure& output_error) {
delete [] channelSize;
throw;
}
delete [] singleChannelData;
EDIT: i added try..catch. Of course you could aswell use a std::vector for nicer code, but it might be a tiny bit slower.
First, I'd mention that to maximize writing speed, you should write buffers that are multiples of the sector size (eg. 64KB or 256KB)
To answer your question, you're going to have to copy every 3rd element from your source data into another buffer, and then write that to the stream.
If I recall correctly Intel Performance Primitives has functions for copying buffers, skipping a certain number of elements. Using IPP will probably have faster results than your own copy routine.
I'm tempted to say that you should read your data into a struct and then overload the insertion operator.
ostream& operator<< (ostream& out, struct data * s) {
out.write(s->first);
}