Related
I have code that are to paint a BMP image on a TPaintBox on a VCL form in a C++ application.
Everything works fine as long as I only have one image to paint, on one form. When I create a second form, I get sporadic access violations.
The code is called from a thread and I'm using the Synchronize function in order to synchronize with the main VCL thread as this
void TCameraForm::loadImage(FramePtr frame)
{
syncing s;
s.aFrame = frame;
s.theForm = this;
//Synchronize with UI thread
TThread::Synchronize(0, &s.fn);
}
In the code, a FramePtr is a shared pointer to one individual 'frame', holding a device dependent bitmap.
The syncing variable is a structure, holding the code for the actual painting:
//This is a trick to use VCL's TThread::Synchronize function "with parameters"
//Thanks to Mr. R. Lebeau for sharing this trick.
struct syncing
{
FramePtr aFrame;
TCameraForm* theForm;
int tag;
void __fastcall fn()
{
try
{
//Create a device dependent bitmap
BitMap aBitMap(aFrame);
//Get the bitmap memory into a TMemoryStream
TMemoryStream* ms = new TMemoryStream();
int bytes = ms->Write(aBitmap.getBuffer()->mMemoryBuffer, aBitmap.getBuffer()->mBufferSize);
ms->Position = 0;
//Create a TPicture object that will be used for drawing on the paintbox
TBitmap* tbm = new TBitmap();
tbm->LoadFromStream(ms);
TRect stretchedRect(getStretchedDimensions(tbm->Width, tbm->Height, theForm->PaintBox1->Width, theForm->PaintBox1->Height));
theForm->PaintBox1->Canvas->StretchDraw(stretchedRect, tbm);
delete ms;
delete tbm;
}
catch(...)
{
Log(lError) << "Exception occured in the CameraFrame sync function";
}
}
};
The debugger mainly stops on creation of the bitmap.
I'm using BCC builder 10.3.2 and the classic compiler.
The bitmap class looks like this:
Header
class BitMap
{
public:
BitMap(FramePtr aFrame);
BitMap(unsigned long width, unsigned long height, ColorCode c, ImageMemoryBuffer& buf);
ImageMemoryBuffer* getBuffer();
~BitMap();
bool write(const string& file);
protected:
unsigned int mWidth;
unsigned int mHeight;
ColorCode mColorCode;
ImageMemoryBuffer mImageMemoryBuffer;
bool create();
bool release();
};
And CPP:
enum { THREE_CHANNEL = 0xC,};
enum { BMP_HEADER_SIZE = 54, };
enum { ALIGNMENT_SIZE = 4, };
namespace ai
{
BitMap::BitMap(FramePtr aFrame)
:
mWidth(0),
mHeight(0),
mColorCode(ColorCodeMono8),
mImageMemoryBuffer()
{
aFrame->GetImageSize(mImageMemoryBuffer.mBufferSize);
aFrame->GetWidth(mWidth);
aFrame->GetHeight(mHeight);
VmbPixelFormatType ePixelFormat = VmbPixelFormatMono8;
aFrame->GetPixelFormat(ePixelFormat);
if((ePixelFormat != VmbPixelFormatMono8) && (ePixelFormat != VmbPixelFormatRgb8))
{
throw(MVRException("Invalid pixel format: " + toString(ePixelFormat)));
}
mColorCode = (ePixelFormat == VmbPixelFormatRgb8) ? ColorCodeRGB24 : ColorCodeMono8;
VmbUchar_t *pImage = NULL;
if (aFrame->GetImage(pImage) != VmbErrorSuccess)
{
throw(MVRException("Failed \"getting\" image"));
}
mImageMemoryBuffer.mMemoryBuffer = (unsigned char*) pImage;
if(!create())
{
Log(lError) << "There was an error creating the bitmap";
throw(MVRException("Failed creating Bitmap"));
}
}
BitMap::BitMap(unsigned long width, unsigned long height, ColorCode c, ImageMemoryBuffer& buf)
:
mWidth(width),
mHeight(height),
mColorCode(c),
mImageMemoryBuffer(buf)
{
if(!create())
{
Log(lError) << "There was an error creating the bitmap";
throw(MVRException("Failed creating bitmap"));
}
}
BitMap::~BitMap()
{
if(!release())
{
Log(lError) << "There was an error releasing the bitmap";
}
}
ImageMemoryBuffer* BitMap::getBuffer()
{
return &mImageMemoryBuffer;
}
bool BitMap::create()
{
try
{
unsigned char nNumColors; // Number of colors of our image
unsigned char nPadLength; // The padding we need to align the bitmap ALIGNMENT_SIZE
unsigned long nPaletteSize = 0; // The size of the bitmap's palette
unsigned long nHeaderSize; // The size of the bitmap's header
unsigned long nFileSize; // The size of the bitmap file
unsigned char* pBitmapBuffer; // A buffer we use for creating the bitmap
unsigned char* pCurBitmapBuf; // A cursor to move over "pBitmapBuffer"
unsigned char* pCurSrc; // A cursor to move over the given buffer "pBuffer"
unsigned long px; // A single pixel for storing transformed color information
unsigned long x; // The horizontal position within our image
unsigned long y; // The vertical position within our image
unsigned long i; // Counter for some iteration
// The bitmap header
char fileHeader[14] = { 'B','M', // Default
0,0,0,0, // File size
0,0,0,0, // Reserved
0,0,0,0 }; // Offset to image content
char infoHeader[40] = { 40,0,0,0, // Size of info header
0,0,0,0, // Width
0,0,0,0, // Height
1,0, // Default
0, 0 }; // bpp
if ( 0 == mImageMemoryBuffer.mBufferSize || 0 == mWidth || 0 == mHeight )
{
Log(lError) << "Zero bitmap buffer, width ot height in Bitmap constructor";
return false;
}
if ( mColorCode == (mColorCode & THREE_CHANNEL) )
{
nNumColors = 3;
}
else
{
nNumColors = 1;
}
// Bitmap padding always is a multiple of four Bytes. If data is not we need to pad with zeros.
nPadLength = (mWidth * nNumColors) % ALIGNMENT_SIZE;
if ( 0 != nPadLength )
{
nPadLength = ALIGNMENT_SIZE - nPadLength;
}
if ( ColorCodeRGB24 != mColorCode )
{
nPaletteSize = 256;
}
nHeaderSize = BMP_HEADER_SIZE + nPaletteSize * 4;
pBitmapBuffer = (unsigned char*)malloc( nHeaderSize + mImageMemoryBuffer.mBufferSize + (nPadLength * mHeight) );
nFileSize = nHeaderSize + mImageMemoryBuffer.mBufferSize + (nPadLength * mHeight);
// File size
fileHeader[ 2] = (char)(nFileSize);
fileHeader[ 3] = (char)(nFileSize >> 8);
fileHeader[ 4] = (char)(nFileSize >> 16);
fileHeader[ 5] = (char)(nFileSize >> 24);
// Offset to image content
fileHeader[10] = (char)(nHeaderSize);
fileHeader[11] = (char)(nHeaderSize >> 8);
fileHeader[12] = (char)(nHeaderSize >> 16);
fileHeader[13] = (char)(nHeaderSize >> 24);
// Width
infoHeader[ 4] = (char)(mWidth);
infoHeader[ 5] = (char)(mWidth >> 8);
infoHeader[ 6] = (char)(mWidth >> 16);
infoHeader[ 7] = (char)(mWidth >> 24);
// Height (has to be negative for a top down image)
infoHeader[ 8] = (char)(-(long)mHeight);
infoHeader[ 9] = (char)(-(long)mHeight >> 8);
infoHeader[10] = (char)(-(long)mHeight >> 16);
infoHeader[11] = (char)(-(long)mHeight >> 24);
// bpp
infoHeader[14] = 8 * nNumColors;
// Image size
infoHeader[20] = (char)(mImageMemoryBuffer.mBufferSize);
infoHeader[21] = (char)(mImageMemoryBuffer.mBufferSize >> 8);
infoHeader[22] = (char)(mImageMemoryBuffer.mBufferSize >> 16);
infoHeader[23] = (char)(mImageMemoryBuffer.mBufferSize >> 24);
// Palette size
infoHeader[32] = (char)(nPaletteSize);
infoHeader[33] = (char)(nPaletteSize >> 8);
infoHeader[34] = (char)(nPaletteSize >> 16);
infoHeader[35] = (char)(nPaletteSize >> 24);
// Used colors
infoHeader[36] = (char)(nPaletteSize);
infoHeader[37] = (char)(nPaletteSize >> 8);
infoHeader[38] = (char)(nPaletteSize >> 16);
infoHeader[39] = (char)(nPaletteSize >> 24);
// Write header
pCurBitmapBuf = pBitmapBuffer;
memcpy(pCurBitmapBuf, fileHeader, 14);
pCurBitmapBuf += 14;
memcpy(pCurBitmapBuf, infoHeader, 40);
pCurBitmapBuf += 40;
for(i = 0; i < nPaletteSize; ++i)
{
pCurBitmapBuf[0] = (char)(i);
pCurBitmapBuf[1] = (char)(i);
pCurBitmapBuf[2] = (char)(i);
pCurBitmapBuf[3] = 0;
pCurBitmapBuf += 4;
}
// RGB -> BGR (a Windows bitmap is BGR)
if(mColorCode == ColorCodeRGB24)
{
pCurSrc = (unsigned char*) mImageMemoryBuffer.mMemoryBuffer;
for(y=0; y < mHeight; ++y, pCurBitmapBuf += nPadLength )
{
for (x = 0; x < mWidth; ++x, pCurSrc += 3, pCurBitmapBuf += 3)
{
px = 0;
// Create a 4 Byte structure to store ARGB (we don't use A)
px = px | (pCurSrc[0] << 16) | (pCurSrc[1] << 8) | pCurSrc[2];
// Due to endianess ARGB is stored as BGRA
// and we only have to write the first three Bytes
memcpy( pCurBitmapBuf, &px, 3 );
}
// Add padding at the end of each row
memset( pCurBitmapBuf, 0, nPadLength );
}
mColorCode = ColorCodeBGR24;
}
// Mono8
else
{
if(nPadLength == 0)
{
memcpy( pCurBitmapBuf, mImageMemoryBuffer.mMemoryBuffer, mImageMemoryBuffer.mBufferSize );
}
else
{
pCurSrc = (unsigned char*)mImageMemoryBuffer.mMemoryBuffer;
for (y=0; y < mHeight; ++y, pCurSrc += mWidth * nNumColors)
{
// Write a single row of colored pixels
memcpy( pCurBitmapBuf, pCurSrc, mWidth * nNumColors );
pCurBitmapBuf += mWidth * nNumColors;
// Write padding pixels
memset(pCurBitmapBuf, 0, nPadLength);
pCurBitmapBuf += nPadLength;
}
}
}
mImageMemoryBuffer.mMemoryBuffer = pBitmapBuffer;
mImageMemoryBuffer.mBufferSize = nFileSize;
return true;
}
catch(...)
{
Log(lError) << "Exception in creation of bitmap create function";
return false;
}
}
bool BitMap::release()
{
try
{
if (mImageMemoryBuffer.mMemoryBuffer != NULL && mImageMemoryBuffer.mBufferSize > 0)
{
free(mImageMemoryBuffer.mMemoryBuffer);
mImageMemoryBuffer.mMemoryBuffer = NULL;
}
return true;
}
catch(...)
{
return false;
}
}
bool BitMap::write(const string& fName)
{
if (mImageMemoryBuffer.mMemoryBuffer == NULL)
{
return false;
}
FILE *file = fopen(fName.c_str(), "wb");
if(!file)
{
Log(lError) << "Failed opening file: " << fName;
return false;
}
fwrite(mImageMemoryBuffer.mMemoryBuffer, 1, mImageMemoryBuffer.mBufferSize, file );
fclose(file);
return true;
}
}
UPDATE: The above code works fine when executed by one thread. The problem occurs when several threads are involved. I have located the problem happening when the memcpy function is called in the creation of the bitmap (not the TBitmap), supporting this. So main problem is that the same memory is being manipulated by two or more threads at the same time.
For the above code, where would it be appropriate to incorporate a Mutex, in order to prevent the memory corruption? Or could one use another technique?
Hi i am trying to record from a board and i have successfully record 4 seconds. Problem is when i try to record for more time, i got an error telling me that there not enough memory. my target is to record a 5 minutes file. Until now i have create a buffer named snIn[256] where are the samples. i send it to a big buffer of [16K * 4sec] and when it is full, i create the wav file.
#include "SAI_InOut.hpp"
#include "F746_GUI.hpp"
#include "Delay.hpp"
#include "WaveformDisplay.hpp"
#include "SDFileSystem.h"
#include "wavfile.h"
using namespace Mikami;
#define RES_STR_SIZE 0x20
#define WAVFILE_SAMPLES_PER_SECOND 16000
#define REC_TIME 4
//Create an SDFileSystem object
SDFileSystem sd("sd");
bool flag = 1;
int count = 0;
char *res_buf;
int rp = 0;
const int NUM_SAMPLES = WAVFILE_SAMPLES_PER_SECOND * REC_TIME;
Array<int16_t> my_buffer(NUM_SAMPLES);
int j = 0;
static const char *target_filename = "/sd/rectest.wav";
const int SEG_SIZE = 256;
int sent_array = 0;
int rec(const char *filename, Array<int16_t> my_buffer)
{
j = 0;
flag = 0;
sent_array = 0;
WavFileResult result;
wavfile_info_t info;
wavfile_data_t data;
WAVFILE_INFO_AUDIO_FORMAT(&info) = 1;
WAVFILE_INFO_NUM_CHANNELS(&info) = 1;
WAVFILE_INFO_SAMPLE_RATE(&info) = WAVFILE_SAMPLES_PER_SECOND;
WAVFILE_INFO_BITS_PER_SAMPLE(&info) = 16;
WAVFILE_INFO_BYTE_RATE(&info) = WAVFILE_INFO_NUM_CHANNELS(&info) * WAVFILE_INFO_SAMPLE_RATE(&info) * (WAVFILE_INFO_BITS_PER_SAMPLE(&info) / 8);
WAVFILE_INFO_BLOCK_ALIGN(&info) = 2;
WAVFILE *wf = wavfile_open(filename, WavFileModeWrite, &result);
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf, RES_STR_SIZE);
printf("%s", res_buf);
return result;
} else printf ("Open file success \r\n");
rp = 0;
WAVFILE_DATA_NUM_CHANNELS(&data) = 1;
result = wavfile_write_info(wf, &info);
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf, RES_STR_SIZE);
printf("%s", res_buf);
return result; } else printf ("Write info success \r\n");
while ( rp < NUM_SAMPLES ) {
WAVFILE_DATA_CHANNEL_DATA(&data, 0) = my_buffer[rp];
result = wavfile_write_data(wf, &data);
rp += 1;
}
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf, RES_STR_SIZE);
printf("%s", res_buf);
return result; } else printf ("Write Data file success \r\n");
result = wavfile_close(wf);
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf , RES_STR_SIZE);
printf("%s", res_buf);
return result; } else printf ("Close file success \r\n");
//UnMount the filesystem
sd.unmount();
printf("Success rec !\r\n");
return 0;
}
int main()
{
//Mount the filesystem
sd.mount();
const float MAX_DELAY = 0.5f; // 最大遅延,単位:秒
const int FS = I2S_AUDIOFREQ_16K; // 標本化周波数: 16 kHz
const uint32_t MAX_ARRAY_SIZE = (uint32_t)(MAX_DELAY*FS);
SaiIO mySai(SaiIO::BOTH, 256, FS, INPUT_DEVICE_DIGITAL_MICROPHONE_2);
Label myLabel(185, 10, "Delay System", Label::CENTER, Font16);
// ButtonGroup: "ON", "OFF"
const uint16_t BG_LEFT = 370;
const uint16_t BG_WIDTH = 100;
const uint16_t BG_HEIGHT = 45;
ButtonGroup onOff(BG_LEFT, 40, BG_WIDTH/2, BG_HEIGHT,
2, (string[]){"ON", "OFF"}, 0, 0, 2, 1);
const uint16_t SB_LEFT = BG_LEFT - 320;
const uint16_t SB_WIDTH = 270;
const uint16_t SB_Y0 = 240;
char str[20];
sprintf(str, " %3.1f [s]", MAX_DELAY);
SeekBar barDelay(SB_LEFT, SB_Y0, SB_WIDTH,
0, MAX_ARRAY_SIZE, 0, "0", "", str);
NumericLabel<float> labelDelay(SB_LEFT+SB_WIDTH/2, SB_Y0-40, "DELEY: %4.2f", 0, Label::CENTER);
DelaySystem delaySystem(MAX_ARRAY_SIZE);
WaveformDisplay displayIn(*GuiBase::GetLcdPtr(), SB_LEFT+7, 70, 256, 9,LCD_COLOR_WHITE, LCD_COLOR_CYAN,GuiBase::ENUM_BACK);
Label inLabel(SB_LEFT-30, 65, "IN");
WaveformDisplay displayOut(*GuiBase::GetLcdPtr(), SB_LEFT+7, 130, 256, 9,LCD_COLOR_WHITE, LCD_COLOR_CYAN,GuiBase::ENUM_BACK);
Label outLabel(SB_LEFT-30, 125, "OUT");
int runStop = 1;
Array<int16_t> snIn(mySai.GetLength());
Array<int16_t> snOut(mySai.GetLength());
mySai.RecordIn();
mySai.PlayOut();
mySai.PauseOut();
while (true)
{
// On/OFF
int num;
if (onOff.GetTouchedNumber(num))
if (runStop != num)
{
if (num == 0) mySai.ResumeOut();
else mySai.PauseOut();
runStop = num;
}
if (mySai.IsCompleted())
{
for (int n=0; n<mySai.GetLength() ; n++)
{
int16_t xL, xR;
mySai.Input(xL,xR);
int16_t xn = xL + xR;
snIn[n] = xn;
my_buffer[j] = xn;
j++;
if (j == NUM_SAMPLES && flag == 1) {
rec (target_filename , my_buffer); }
int16_t yn = delaySystem.Execute(xn);
mySai.Output(yn, yn);
snOut[n] = yn;
}
mySai.Reset();
displayIn.Execute(snIn);
}
}
}
I thought about a possible solution, to fill directly the "data field" of the wavefile with the snIn[256] buffer (instead of using my_buffer) again and again and at the end close the wavfile. Please let me know what you think about that and other solutions
things to note: 1) while a write operation is being performed, more data is still coming in.
At the very least I would double buffer that data, so can be writing one buffer while the other one fills.
Usually this means using an interrupt to collect the samples (into which ever buffer is currently being filed.)
the foreground program waits for the current buffer to be 'full', then initiates write operation.,
then waits again for a buffer to be 'full'
The interrupt function tracks which buffer is being filled and the current index into that buffer. When a buffer is full, set a 'global' status to let the foreground program know which buffer is ready to be written.
The foreground program writes the buffer, then resets the status for that buffer.
I am making a program in C++ that uses SDL to display text boxes. I've made the basic functions of my text boxes like typing text, limitations, copy/paste, etc. but I am stuck at make it able to highlight text.
Here's how I draw textboxes:
void draw_text_box(SDL_Surface *screan, char *message, struct textbox *text_box, int r, int g, int b)
{
int temp_width;
int temp_frame;
SDL_Surface *text;
SDL_Color textcolor = {0,0,0,0};
SDL_Rect temp_location, clr_box;
temp_width = text_box->location.w; //preserve the correct width
temp_location = text_box->location;
temp_location.y += 4;
temp_location.h = 12;
clr_box = text_box->location;
//colors!
textcolor.r = r;
textcolor.g = g;
textcolor.b = b;
//text = TTF_RenderText_Solid(ttf_font, text_box->prev_message, textcolor);
//SDL_BlitSurface( text, NULL, screan, &text_box->location);//<-changes "location" width to width of text
//clear
SDL_FillRect(screan, &clr_box, SDL_MapRGB(screen->format, 0, 0, 0));
//strcpy(text_box->prev_message, message);
//set the text
text = TTF_RenderText_Solid(ttf_font, message, textcolor);
if(!text) return;
//set the offset
temp_location.x = text_box->last_shift;
temp_location.y = 0;
temp_location.h = text->h;
if(text_box->cursor_loc - text_box->last_shift > temp_location.w)
temp_location.x = text_box->cursor_loc - temp_location.w;
if(text_box->cursor_loc - 3 < text_box->last_shift)
temp_location.x = text_box->cursor_loc - 3;
if(temp_location.x < 0) temp_location.x = 0;
text_box->last_shift = temp_location.x;
//draw
SDL_BlitSurface( text, &temp_location, screan, &text_box->location);//<-changes "location" width to width of text
text_box->location.w = temp_width;
}
Setting the text:
void set_text_box_text(SDL_Surface *screan, struct textbox *text_box, char *message)
{
if(text_box->message != message)
strcpy(text_box->message, message);
text_box->char_amt = strlen(message);
text_box->cursor = text_box->char_amt;
text_box->last_shift = 0;
if (screan) //if not NULL
{
if (text_box->selected)
select_text_box(screan,text_box);
else
unselect_text_box(screan,text_box);
}
else
{
;//text_box->prev_message[0] = '\0';
}
}
Select and deselect the textbox:
void select_text_box(SDL_Surface *screan, struct textbox *text_box)
{
char temp_message[405] = "";
char temp_left[405];
int i;
text_box->selected = 1;
if (text_box->pass_char == '\0')
{
if (text_box->cursor != 0)
{
left(temp_left, text_box->message, text_box->cursor);
strcat(temp_message, temp_left);
}
temp_message[text_box->cursor] = '|';
temp_message[text_box->cursor + 1] = '\0';
right(temp_left, text_box->message, text_box->cursor);
strcat(temp_message, temp_left);
}
else
{
for(i=0;i<text_box->cursor;i++)
temp_message[i] = text_box->pass_char;
temp_message[i] = '|';
for(i++;i<text_box->char_amt + 1;i++)
temp_message[i] = text_box->pass_char;
temp_message[i] = '\0';
}
draw_text_box(screan, temp_message, text_box, 250, 250, 250);
}
void unselect_text_box(SDL_Surface *screan, struct textbox *text_box)
{
char temp_message[405];
int i;
text_box->selected = 0;
if (text_box->pass_char == '\0')
draw_text_box(screan, text_box->message, text_box, 250, 250, 250);
else
{
for(i=0;i<text_box->char_amt;i++)
temp_message[i] = text_box->pass_char;
temp_message[i] = '\0';
draw_text_box(screan, temp_message, text_box, 250, 250, 250);
}
}
So I am having a problem figuring out exactly what is going wrong with trying to read any 24bpp bitmap image and re-create it in the same folder. It works with one image, but not two others that I have tested it with. When reading from the bitmap, I use the information found in the header itself. It could be said I have three questions. 1) Am I reading from bitmap correctly? 2) Am I calculating/using/writing the padding correctly? 3) Am I outputting correctly?.
Third is confirmed no with this image and its output.
Also the reason for allocating an 2d array for the Images is so that I can latter try to rotate bitmaps by 90 degrees.
Unfortunately I cannot post images, the image taken is from here, the rgb_24bpp.bmp
http://pyglet.googlecode.com/svn/trunk/tests/image/
Here is the code used for reading from the image and to calculate the padding.
ifstream myBitmap("rgb_24bpp.bmp", ios::binary | ios::beg);
// Get the total file size in bytes, testing file access
begin = myBitmap.tellg();
myBitmap.seekg(0, ios::end);
end = myBitmap.tellg();
// Actually reading image file
myBitmap.seekg( 0, ios::beg);
myBitmap.read((char*)FileHeader, sizeof(BITMAPFILEHEADER));
myBitmap.read((char*)InfoHeader, sizeof(BITMAPINFOHEADER));
test = myBitmap.tellg();
RGBQUAD ** Image = new RGBQUAD*[InfoHeader->biWidth];
for (int i = 0; i < InfoHeader->biWidth; ++i) {
Image[i] = new RGBQUAD[InfoHeader->biHeight];
}
int pitch = InfoHeader->biWidth * 3;
if (pitch % 4 != 0)
{
pitch += 4 - (pitch % 4);
}
int padding = pitch - (InfoHeader->biWidth * 3);
cout << "padding: " << padding << endl;
myBitmap.seekg(FileHeader->bfOffBits, ios::beg);
for (int i = InfoHeader->biHeight; i > 0; --i) {
for (int j = 0; j < InfoHeader->biWidth; ++j) {
myBitmap.read((char*)&Image[j][i], sizeof(RGBQUAD));
}
if (padding != 0) myBitmap.read(PadBuffer, padding);
}
myBitmap.close();
begin/end/test are all of streampos and printed on console for debugging.
And this is the code used to output/recreate the image.
ofstream BitmapOut("Output.bmp");
BitmapOut.write((char*)FileHeader, sizeof(BITMAPFILEHEADER));
BitmapOut.write((char*)InfoHeader, sizeof(BITMAPINFOHEADER));
for (int i = InfoHeader->biHeight; i > 0; --i) {
for (int j = 0; j < InfoHeader->biWidth; ++j) {
BitmapOut.write((char*)&Image[j][i], sizeof(RGBQUAD));
}
if (padding != 0) BitmapOut.write("\0\0\0\0\0\0\0", padding);
}
BitmapOut.close();
I have confirmed that both headers are indeed correct and can pull data from them properly in 3 different tests.
Utilizing this guys code (sorry, this project is non-commercial and self-study only).
reading a .bmp file in c++
With the exception of commenting out the reserved in the RGBQUAD and making effectively a RGBTRI instead.
You can do it like this.. Also, if you don't want to make a temporary array to copy the pixels, you can easily read, seek, read, seek, etc.. OR you can just read all at once. There are so many ways to read a bitmap and be efficient/inefficient. It's up to you how you want to do it. Another efficient way to do it is to SAVE the BitmapInfoHeader and BitmapFileHeader. Then when you decide to write the bitmap to the disk, just write them headers first then the pixels. WAY faster and easier.. I did NOT do that in this example. I'll leave that up to you to figure out.
Here is a sample code I wrote for answering your question. I prefer to use 1-dimensional arrays.
#include <fstream>
#include <cstring>
#include <windows.h>
typedef struct
{
unsigned int width, height;
unsigned char* pixels;
} Bitmap;
void InitBitmap(Bitmap* bmp)
{
if (bmp)
{
bmp->width = 0;
bmp->height = 0;
bmp->pixels = NULL;
}
}
void FreeBitmap(Bitmap* bmp)
{
if (bmp && bmp->pixels)
{
bmp->width = 0;
bmp->height = 0;
delete[] bmp->pixels;
bmp->pixels = NULL;
}
}
bool ReadBitmap(const char* FilePath, Bitmap* bmp)
{
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!bmp || !hFile.is_open())
return false;
BITMAPINFO Info;
BITMAPFILEHEADER Header;
memset(&Info, 0, sizeof(Info));
memset(&Header, 0, sizeof(Header));
hFile.read((char*)&Header, sizeof(Header));
hFile.read((char*)&Info.bmiHeader, sizeof(Info.bmiHeader));
bmp->width = Info.bmiHeader.biWidth;
bmp->height = Info.bmiHeader.biHeight < 0 ? -Info.bmiHeader.biHeight : Info.bmiHeader.biHeight;
size_t size = Info.bmiHeader.biSizeImage;
bmp->pixels = new unsigned char[size];
hFile.seekg(Header.bfOffBits, std::ios::beg);
hFile.read((char*)bmp->pixels, size);
hFile.close();
return true;
}
bool WriteBitmap(const char* FilePath, Bitmap* bmp)
{
std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
if (!bmp || !hFile)
return false;
BITMAPINFO Info;
BITMAPFILEHEADER Header;
memset(&Info, 0, sizeof(Info));
memset(&Header, 0, sizeof(Header));
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = bmp->width;
Info.bmiHeader.biHeight = bmp->height;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = 24;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = 0;
Header.bfType = 0x4D42;
Header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
size_t size = (((24 * bmp->width + 31) & ~31) / 8) * bmp->height;
hFile.write((char*)&Header, sizeof(Header));
hFile.write((char*)&Info.bmiHeader, sizeof(Info.bmiHeader));
hFile.write((char*)bmp->pixels, size);
hFile.close();
return true;
}
int main()
{
Bitmap bmp;
InitBitmap(&bmp);
ReadBitmap("C:/Users/Brandon/Desktop/foo.bmp", &bmp);
WriteBitmap("C:/Users/Brandon/Desktop/foo2.bmp", &bmp);
FreeBitmap(&bmp);
}
I'm having problems preserving newslines from a RichEdit control inside strings.
What I'm doing is:
Get text from RichEdit control
Split everything delimited by a space
Add some RTF formatting
"Fuse" words back together
Send text to control
I'm not sure what part causes this so here's the most relevant bits:
int RichEdit::GetTextLength() const
{
GETTEXTLENGTHEX len;
len.codepage = 1200;
len.flags = GTL_NUMBYTES;
return (int)SendMessage(this->handle, EM_GETTEXTLENGTHEX, (WPARAM)&len, 0) + 1;
}
tstring RichEdit::GetText() const
{
auto len = this->GetTextLength();
GETTEXTEX str;
TCHAR* tmp = new TCHAR[len];
str.cb = len;
str.flags = GT_USECRLF;
str.codepage = 1200;
str.lpDefaultChar = NULL;
str.lpUsedDefChar = NULL;
(void)SendMessage(this->handle, EM_GETTEXTEX, (WPARAM)&str, (LPARAM)tmp);
tstring ret(tmp);
delete[] tmp;
return ret;
}
void RichEdit::SetRtfText(const tstring& text, int flags)
{
DWORD WideLength = text.length();
DWORD Length = WideLength * 4;
PSTR Utf8 = (PSTR)malloc(Length);
int ReturnedLength = WideCharToMultiByte(CP_UTF8,
0,
text.c_str(),
WideLength-1,
Utf8,
Length-1,
NULL,
NULL);
if (ReturnedLength)
Utf8[ReturnedLength] = 0;
SETTEXTEX st = {0};
st.flags = flags;
st.codepage = CP_UTF8;
(void)SendMessage(this->handle, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)Utf8 );
free(Utf8);
}
void split ( tstring input , tstring split_id, std::vector<std::pair<tstring,bool>>& res ) {
std::vector<std::pair<tstring,bool>> result;
int i = 0;
bool add;
tstring temp;
std::wstringstream ss;
size_t found;
tstring real;
int r = 0;
while ( i != input.length() )
{
add = false;
ss << input.at(i);
temp = ss.str();
found = temp.find(split_id);
if ( found != tstring::npos )
{
add = true;
real.append ( temp , 0 , found );
} else if ( r > 0 && ( i+1 ) == input.length() )
{
add = true;
real.append ( temp , 0 , found );
}
if ( add )
{
result.emplace_back(std::make_pair(real,false));
ss.str(tstring());
ss.clear();
temp.clear();
real.clear();
r = 0;
}
i++;
r++;
}
res = result;
}
ps: tstring is just a typedef for std::wstring/std::string
How can I preserve the newlines?
There are quite a few problems with your code.
Your code is TCHAR based, but you are not actually retrieving/setting the RTF data using TCHAR correctly.
When retreiving the text, you are normalizing line breaks to CRLF, but you are not doing that same normalizing when retreiving the text length, so they are going to be out of sync with each other.
You are writing data to the RichEdit using UTF-8, but RTF is an ASCII-based format that uses escape sequences for Unicode data. If you are going to retrieve data as Unicode, you may as well write it using Unicode as well, and make sure you are doing all of that correctly to begin with. Let the RichEdit control handle the Unicode for you.
Your use of WideCharToMultiByte() is wrong. You should not be subtracting -1 from the string lengths at all. You are likely trying to account for null terminators, but the length values do not include null terminators to begin with. If you are going to stick with UTF-8 then you should be using WideCharToMultiByte() to calculate the correct UTF-8 length instead of hard-coding it.
int Length = WideCharToMultiByte(CP_UTF8, 0, text.c_str(), text.length(), NULL, 0, NULL, NULL);
char Utf8 = new char[Length+1];
WideCharToMultiByte(CP_UTF8, 0, text.c_str(), text.length(), Utf8, Length, NULL, NULL);
Utf8[Length] = 0;
...
delete[] Utf8;
With that said, if you are going to stick with TCHAR then try this:
#ifdef UNICODE
#define RTFCodePage 1200
#else
#define RTFCodePage CP_ACP
#endif
int RichEdit::GetTextLength() const
{
GETTEXTLENGTHEX len = {0};
len.codepage = RTFCodePage;
len.flags = GTL_NUMCHARS | GTL_USECRLF;
return SendMessage(this->handle, EM_GETTEXTLENGTHEX, (WPARAM)&len, 0);
}
tstring RichEdit::GetText() const
{
int len = this->GetTextLength() + 1;
GETTEXTEX str = {0};
str.cb = len * sizeof(TCHAR);
str.flags = GT_USECRLF;
str.codepage = RTFCodePage;
vector<TCHAR> tmp(len);
len = SendMessage(this->handle, EM_GETTEXTEX, (WPARAM)&str, (LPARAM)&tmp[0]);
return tstring(&tmp[0], len-1);
}
void RichEdit::SetRtfText(const tstring& text, int flags)
{
SETTEXTEX st = {0};
st.flags = flags;
st.codepage = RTFCodePage;
#ifdef UNICODE
st.flags |= ST_UNICODE;
#endif
SendMessage(this->handle, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text.c_str());
}
It would be better to drop TCHAR and just use Unicode for everything:
int RichEdit::GetTextLength() const
{
GETTEXTLENGTHEX len = {0};
len.codepage = 1200;
len.flags = GTL_NUMCHARS | GTL_USECRLF;
return SendMessage(this->handle, EM_GETTEXTLENGTHEX, (WPARAM)&len, 0);
}
wstring RichEdit::GetText() const
{
int len = this->GetTextLength() + 1;
GETTEXTEX str = {0};
str.cb = len * sizeof(WCHAR);
str.flags = GT_USECRLF;
str.codepage = 1200;
vector<WCHAR> tmp(len);
len = SendMessage(this->handle, EM_GETTEXTEX, (WPARAM)&str, (LPARAM)&tmp[0]);
return wstring(tmp, len-1);
}
void RichEdit::SetRtfText(const wstring& text, int flags)
{
SETTEXTEX st = {0};
st.flags = flags | ST_UNICODE;
st.codepage = 1200;
SendMessage(this->handle, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text.c_str());
}
Update: if you have to go back to UTF-8 for the EM_SETTEXTEX message then try this:
void RichEdit::SetRtfText(const tstring& text, int flags)
{
string Utf8;
int Length;
#ifdef UNICODE
Length = WideCharToMultiByte(CP_UTF8, 0, text.c_str(), text.length(), NULL, 0, NULL, NULL);
if (Length > 0)
{
Utf8.resize(Length);
WideCharToMultiByte(CP_UTF8, 0, text.c_str(), text.length(), &Utf8[0], Length, NULL, NULL);
}
#else
Length = MultiByteToWideChar(CP_ACP, 0, text.c_str(), text.length(), NULL, 0);
if (Length > 0)
{
vector<WCHAR> tmp(Length);
MultiByteToWideChar(CP_ACP, 0, text.c_str(), text.length(), &tmp[0], Length);
Length = WideCharToMultiByte(CP_UTF8, 0, tmp.c_str(), tmp.length(), NULL, 0, NULL, NULL);
if (Length > 0)
{
Utf8.resize(Length);
WideCharToMultiByte(CP_UTF8, 0, tmp.c_str(), tmp.length(), &Utf8[0], Length, NULL, NULL);
}
}
#endif
SETTEXTEX st = {0};
st.flags = flags & ~ST_UNICODE;
st.codepage = CP_UTF8;
SendMessage(this->handle, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)Utf8.c_str());
}