Saving a TPaintBox

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

Post Reply
smd
BCBJ Guru
BCBJ Guru
Posts: 139
Joined: Sat Nov 29, 2014 8:02 pm
Location: Las Vegas
Contact:

Saving a TPaintBox

Post by smd »

FMX CPP Using Rad Studio 10.2.2

I am having trouble figuring out how to save the contents of a TPaintBox. I wrote a simple test program to make my base image. This is the first time I am using bitmaps and canvases directly so I made a clunky test program to figure out how to do this. After working through the various example code, I have a simple routine that creates:
Screenshot
Screenshot
screenshot1.png (9.67 KiB) Viewed 10038 times
The TPaintBox is 500x150 pixels.

Attached is the project code. This is just experimenting code so it is not optimized or pretty. It is just for figuring out how stuff works.

I want to save the image of the paintbox as a PNG file. Button 4 is my latest attempt to save the image. The resulting PNG is just a blank white rectangle that is 500x150.I cannot figure out how to save the image as a PNG.

Anyone know how to do this?

Scott
Attachments
Canvas.zip
FMX RAD Project Files
(12.45 KiB) Downloaded 523 times
-----------------------------
Scott
rlebeau
BCBJ Author
BCBJ Author
Posts: 1726
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: Saving a TPaintBox

Post by rlebeau »

smd wrote:I am having trouble figuring out how to save the contents of a TPaintBox.
That is because you can't save it, because there is nothing available to save. TPaintBox is not designed or intended for that purpose. Its drawing is temporary, it only exists for the time period that the OS is rendering the control onscreen.

The correct way to handle this situation is to either:

- do all of your rendering to a persistent TBitmap that exists for the lifetime of your TForm. Whenever the contents of the TBitmap changes, invalidate the TPaintBox via its InvalidateRect() method to trigger a repaint. Whenever the TPaintBox::OnPaint event is fired, draw the current TBitmap onto the TPaintBox::Canvas. Then you can save the TBitmap to file/stream whenever needed.

- don't use a persistent TBitmap. Move your drawing code to its own function that takes a TCanvas as input. Have your TPaintBox::OnPaint handler call that function with the TPaintBox::Canvas as input. And have your TButton::OnClick handler create a TBitmap, call that function with the TBitmap::Canvas as input, and then save the TBitmap as needed.
Remy Lebeau (TeamB)
Lebeau Software
smd
BCBJ Guru
BCBJ Guru
Posts: 139
Joined: Sat Nov 29, 2014 8:02 pm
Location: Las Vegas
Contact:

Re: Saving a TPaintBox

Post by smd »

- do all of your rendering to a persistent TBitmap that exists for the lifetime of your TForm. Whenever the contents of the TBitmap changes, invalidate the TPaintBox via its InvalidateRect() method to trigger a repaint. Whenever the TPaintBox::OnPaint event is fired, draw the current TBitmap onto the TPaintBox::Canvas. Then you can save the TBitmap to file/stream whenever needed.
I think this is what I am already doing.

Code: Select all


/* ******************* */
/* does a scope redraw */
/* ******************* */

void ScopeUpdate(void)
  {
  int ofstx;
  int ofsty;
  TRect px;
  int i;

  ofstx = (int)MainForm->Scope1->Position->X;
  ofsty = (int)MainForm->Scope1->Position->Y;

  MainForm->Scope1->Canvas->BeginScene();

  MainForm->Scope1->Canvas->Fill->Color = scopestats.bckcolor;
  MainForm->Scope1->Canvas->FillRect(scopestats.bckgnd, 0.0, 0.0, AllCorners, 1.0);

  MainForm->Scope1->Canvas->Fill->Color = scopestats.gridcolor;
  MainForm->Scope1->Canvas->FillRect(scopestats.gh0, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gh1, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gh2, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gh3, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gh4, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gh5, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gh6, 0.0, 0.0, AllCorners, 0.5);

  MainForm->Scope1->Canvas->FillRect(scopestats.gv00, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv01, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv02, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv03, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv04, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv05, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv06, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv07, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv08, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv09, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv10, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv11, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv12, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv13, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv14, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv15, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv16, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv17, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv18, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv19, 0.0, 0.0, AllCorners, 0.5);
  MainForm->Scope1->Canvas->FillRect(scopestats.gv20, 0.0, 0.0, AllCorners, 0.5);

  for (i = 0; i < scopestats.ttl; ++i)
    {
    if (scopestats.white[i] >= 0)
      {
      px.left = i;
      px.top = SCOPEYMAX - scopestats.white[i];
      px.right = i + 1;
      px.bottom = (SCOPEYMAX - scopestats.white[i]) + 1;
      px.Offset(ofstx, ofsty);
      MainForm->Scope1->Canvas->Fill->Color = scopestats.px1color;
      MainForm->Scope1->Canvas->FillRect(px, 0.0, 0.0, AllCorners, 1.0);
      }
    if (scopestats.red[i] >= 0)
      {
      px.left = i;
      px.top = SCOPEYMAX - scopestats.red[i];
      px.right = i + 1;
      px.bottom = (SCOPEYMAX - scopestats.red[i]) + 1;
      px.Offset(ofstx, ofsty);
      MainForm->Scope1->Canvas->Fill->Color = scopestats.px2color;
      MainForm->Scope1->Canvas->FillRect(px, 0.0, 0.0, AllCorners, 1.0);
      }
    if (scopestats.blue[i] >= 0)
      {
      px.left = i;
      px.top = SCOPEYMAX - scopestats.blue[i];
      px.right = i + 1;
      px.bottom = (SCOPEYMAX - scopestats.blue[i]) + 1;
      px.Offset(ofstx, ofsty);
      MainForm->Scope1->Canvas->Fill->Color = scopestats.px3color;
      MainForm->Scope1->Canvas->FillRect(px, 0.0, 0.0, AllCorners, 1.0);
      }
    }

  MainForm->Scope1->Canvas->EndScene();
  }

Scope1 is the name of the PaintBox.

Writing to its canvas. I tried saving the bitmap using the Scope1->bitmap->SaveToFile but that is what gives me a blank png file. The dimensions of the png are correct but it is blank. The canvas drawing functions obviously work since it is rendering correctly on the screen.

PaintBox->MakeScreenshot() does not seem to work.

Should I declare an external object like

TBitmap *mybitmap;

Or place a TImage instead of PaintBox?
-----------------------------
Scott
smd
BCBJ Guru
BCBJ Guru
Posts: 139
Joined: Sat Nov 29, 2014 8:02 pm
Location: Las Vegas
Contact:

Re: Saving a TPaintBox

Post by smd »

Got it to work.

Use a TImage instead of the TPaintBox.

Place a TImage.

I named it Scope2

In the OnFormCreate

Code: Select all

  
Scope2->Bitmap = new TBitmap(500, 150);
In the body of the button click function, or wherever:

Code: Select all

  TBitmapCodecSaveParams *t;
  TRect r;

  r.left = 10;
  r.top = 10;
  r.right = 30;
  r.bottom = 30;

  t->Quality = 100;

  Scope2->Bitmap->Canvas->BeginScene();
  Scope2->Bitmap->Canvas->Clear(0xff000000);
  Scope2->Bitmap->Canvas->Fill->Color = claBlue;
  Scope2->Bitmap->Canvas->FillRect(r, 0.0, 0.0, AllCorners, 1.0);
  Scope2->Bitmap->Canvas->EndScene();

  Scope2->Bitmap->SaveToFile("test2.png", t);
Saves a png file as test2.png which is a black rectangle wiith a 10x10 blue rectangle on it. The file opens properly in various programs, so YAY it works.
-----------------------------
Scott
rlebeau
BCBJ Author
BCBJ Author
Posts: 1726
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: Saving a TPaintBox

Post by rlebeau »

smd wrote:I think this is what I am already doing.
No, you are not. ScopeUpdate() is drawing directly on to the TPaintBox's Canvas, and is not trying to preserve that drawing across multiple paint events. Drawing directly on a TPaintBox is valid only while the TPaintBox is actively being painted by the OS (ie, in the OnPaint event). Once a TPaintBox has been draw onscreen, any drawing on it is discarded for the next paint event to draw.
smd wrote:I tried saving the bitmap using the Scope1->bitmap->SaveToFile but that is what gives me a blank png file.
I think you mean Scope1->Canvas->Bitmap->..., but that still won't work. For what I have described, you must use a separate TBitmap.
smd wrote:The dimensions of the png are correct but it is blank.
Correct, because the pixel data from your drawing is gone by that time.
smd wrote:The canvas drawing functions obviously work since it is rendering correctly on the screen.
Only during an active paint event, yes. Not outside of a paint event, no.
smd wrote:PaintBox->MakeScreenshot() does not seem to work.
Nope.
smd wrote:Should I declare an external object like

TBitmap *mybitmap;
Yes. That is exactly what I told you earlier to do. For example:

Code: Select all

TBitmap *mybitmap;

__fastcall TMainForm::TMainForm(TComponent *Owner)
    : TForm(Owner)
{
    mybitmap = new TBitmap(Scope1->Width, Scope1->Height);
}

__fastcall TMainForm::~TMainForm()
{
    delete mybitmap;
}

void __fastcall TMainForm::Button1Click(TObject *Sender)
{
  mybitmap->SaveToFile(...);
}

void __fastcall TMainForm::Scope1Paint(TObject *Sender)
{
  TRectF r = mybitmap->BoundsF;
  Scope1->Canvas->DrawBitmap(mybitmap, r, r, 1.0f);
}

...

void ScopeUpdate(void)
{
  int ofstx;
  int ofsty;
  TRect px;
  int i;

  ofstx = (int)MainForm->Scope1->Position->X;
  ofsty = (int)MainForm->Scope1->Position->Y;

  mybitmap->Canvas->BeginScene();

  mybitmap->Canvas->Fill->Color = scopestats.bckcolor;
  mybitmap->Canvas->FillRect(scopestats.bckgnd, 0.0, 0.0, AllCorners, 1.0);

  mybitmap->Canvas->Fill->Color = scopestats.gridcolor;
  mybitmap->Canvas->FillRect(scopestats.gh0, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gh1, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gh2, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gh3, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gh4, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gh5, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gh6, 0.0, 0.0, AllCorners, 0.5);

  mybitmap->Canvas->FillRect(scopestats.gv00, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv01, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv02, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv03, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv04, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv05, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv06, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv07, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv08, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv09, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv10, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv11, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv12, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv13, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv14, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv15, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv16, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv17, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv18, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv19, 0.0, 0.0, AllCorners, 0.5);
  mybitmap->Canvas->FillRect(scopestats.gv20, 0.0, 0.0, AllCorners, 0.5);

  for (i = 0; i < scopestats.ttl; ++i)
  {
    if (scopestats.white[i] >= 0)
    {
      px.left = i;
      px.top = SCOPEYMAX - scopestats.white[i];
      px.right = i + 1;
      px.bottom = (SCOPEYMAX - scopestats.white[i]) + 1;
      px.Offset(ofstx, ofsty);
      mybitmap->Canvas->Fill->Color = scopestats.px1color;
      mybitmap->Canvas->FillRect(px, 0.0, 0.0, AllCorners, 1.0);
    }
    if (scopestats.red[i] >= 0)
    {
      px.left = i;
      px.top = SCOPEYMAX - scopestats.red[i];
      px.right = i + 1;
      px.bottom = (SCOPEYMAX - scopestats.red[i]) + 1;
      px.Offset(ofstx, ofsty);
      mybitmap->Canvas->Fill->Color = scopestats.px2color;
      mybitmap->Canvas->FillRect(px, 0.0, 0.0, AllCorners, 1.0);
    }
    if (scopestats.blue[i] >= 0)
    {
      px.left = i;
      px.top = SCOPEYMAX - scopestats.blue[i];
      px.right = i + 1;
      px.bottom = (SCOPEYMAX - scopestats.blue[i]) + 1;
      px.Offset(ofstx, ofsty);
      mybitmap->Canvas->Fill->Color = scopestats.px3color;
      mybitmap->Canvas->FillRect(px, 0.0, 0.0, AllCorners, 1.0);
    }
  }

  mybitmap->Canvas->EndScene();
  MainForm->Scope1->InvalidateRect(MainForm->Scope1->LocalRect);
}
smd wrote:Or place a TImage instead of PaintBox?
That cold be another alternative, yes. That would handle a persistent TBItmap for you.
Last edited by rlebeau on Tue Jun 09, 2020 12:44 pm, edited 1 time in total.
Remy Lebeau (TeamB)
Lebeau Software
rlebeau
BCBJ Author
BCBJ Author
Posts: 1726
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: Saving a TPaintBox

Post by rlebeau »

smd wrote:In the OnFormCreate

Code: Select all

Scope2->Bitmap = new TBitmap(500, 150);
That is a memory leak. The TImage::Bitmap property will make a copy of the TBitmap you pass to it. You are still responsible for the original. In this case, you shouldn't need to size the TImage::Bitmap manually, it will match the TImage's own size automatically.
Remy Lebeau (TeamB)
Lebeau Software
smd
BCBJ Guru
BCBJ Guru
Posts: 139
Joined: Sat Nov 29, 2014 8:02 pm
Location: Las Vegas
Contact:

Re: Saving a TPaintBox

Post by smd »

The TImage documentation example code uses at OnFormCreate

Code: Select all

Scope2->Bitmap = new TBitmap(500, 150);
(with different width and height of course).

I tried commenting out that code. did not work.
I tried just removing the width/height numbers. did not work.
Seems that the TImage Bitmap is just a pointer that needs to be allocated to use.
The position of the TImage on the form does work, so it draws at the location of the TImage on the form.

I tried using just the TBitmap without using TImage.

Code: Select all


TBitmap *Scope3;

void __fastcall TMainForm::FormCreate(TObject *Sender)
  {
  Scope3 = new TBitmap(500, 150);
  }

void __fastcall TMainForm::FormDestroy(TObject *Sender)
  {
  delete Scope3;
  }

void __fastcall TMainForm::Button5Click(TObject *Sender)
  {
  TBitmapCodecSaveParams *t;
  TRect r;

  t->Quality = 100;

  r.left = 10;
  r.top = 10;
  r.right = 30;
  r.bottom = 30;

  Scope3->Canvas->BeginScene();
  Scope3->Canvas->Clear(0xff000000);
  Scope3->Canvas->Fill->Color = claYellow;
  Scope3->Canvas->FillRect(r, 0.0, 0.0, AllCorners, 1.0);
  Scope3->Canvas->EndScene();

  Scope3->SaveToFile("test3.png", t);
  }
The bitmap image was created and saved to test3.png, however the image is not displaying on the form.
-----------------------------
Scott
rlebeau
BCBJ Author
BCBJ Author
Posts: 1726
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: Saving a TPaintBox

Post by rlebeau »

smd wrote:The TImage documentation example code uses at OnFormCreate

Code: Select all

Scope2->Bitmap = new TBitmap(500, 150);
Then the documentation is wrong. I checked the FMX source code. The TImage::Bitmap property setter makes a copy of the input TBitmap, it does not take ownership.

You could try this instead:

Code: Select all

Scope2->Bitmap->Resize(500, 150);
smd wrote:Seems that the TImage Bitmap is just a pointer that needs to be allocated to use.
No, it is not. The TImage constructor creates a TBitmap object for you. However, looking at the FMX source code, that bitmap appears to be set to (0,0) by default.
smd wrote:

Code: Select all

void __fastcall TMainForm::Button5Click(TObject *Sender)
  {
  TBitmapCodecSaveParams *t;
  ...

  t->Quality = 100;
  ...

  Scope3->SaveToFile("test3.png", t);
  }
You are not allocating any memory for your TBitmapCodecSaveParams* pointer to point at before accessing the members of the TBitmapCodecSaveParams. You would need to use the 'new' operator to make that work properly, eg:

Code: Select all

void __fastcall TMainForm::Button5Click(TObject *Sender)
  {
  TBitmapCodecSaveParams *t = new TBitmapCodecSaveParams;
  ...

  t->Quality = 100;
  ...

  Scope3->SaveToFile("test3.png", t);
  delete t;
  }
Alternatively:

Code: Select all

#include <memory>

void __fastcall TMainForm::Button5Click(TObject *Sender)
  {
  std::unique_ptr<TBitmapCodecSaveParams> t(new TBitmapCodecSaveParams);
  // or: auto t = std::make_unique<TBitmapCodecSaveParams>();
  ...

  t->Quality = 100;
  ...

  Scope3->SaveToFile("test3.png", t,get());
  }
However, you really should not be using a pointer at all. Use a local TBitmapCodecSaveParams instance instance and pass it to SaveToFile() using the '&' address operator, eg:

Code: Select all

void __fastcall TMainForm::Button5Click(TObject *Sender)
  {
  TBitmapCodecSaveParams t;  // <-- NOT A POINTER
  ...

  t.Quality = 100; // <-- use the '.' operator, NOT the '->' operator
  ...

  Scope3->SaveToFile("test3.png", &t); // <-- use the '&' operator
  }
smd wrote:The bitmap image was created and saved to test3.png, however the image is not displaying on the form.
Of course not, because in this example, you are not drawing the TBitmap anywhere on your Form, such as in a TPaintBox, like I showed you earlier, or even in the Form's own OnPaint event.

Also, you are lucky your code didn't just crash outright because of the above bug.
Remy Lebeau (TeamB)
Lebeau Software
smd
BCBJ Guru
BCBJ Guru
Posts: 139
Joined: Sat Nov 29, 2014 8:02 pm
Location: Las Vegas
Contact:

Re: Saving a TPaintBox

Post by smd »

OMG, The Horror, An error in the well crafted (auto-crafted?) Rad Documentation. Unheard of, never happens!
/sarc

I decided to use the TImage instead of the straight TBitmap declaration since it simplifies position and sizing. I changed in the CreateForm to using the resize

Scope2->Bitmap->Resize(500,150);

(Scope2 is the name of the placed TImage on the form)

It works. Just for the record, below is a screenshot from the documentation page.
Rad Documentation Screenshot
Rad Documentation Screenshot
screenshot2.PNG (32.57 KiB) Viewed 9792 times
Note that a verbatim use of that code results in an error popup on the Clear statement that states it cannot not be zero. Originally I could not figure out what zero it meant, so I deleted the Clear and the error went away. But apparently it is still wrong to do it the documentations way.

I also changed the TBitmapCodecSaveParams to the non-pointer way. The save works properly, but it also worked with the pointer declaration.

In the C language, if you declare a pointer, with the exception of void, storage for the value is also allocated at time of the declaration. So it seems that

TBitmapCodecSaveParams *t;
t->Quality=100;

and

TBitmapCodecSaveParams t;
t.Quality=100;

should both work without having to do a new.
-----------------------------
Scott
rlebeau
BCBJ Author
BCBJ Author
Posts: 1726
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: Saving a TPaintBox

Post by rlebeau »

smd wrote:Note that a verbatim use of that code results in an error popup on the Clear statement that states it cannot not be zero. Originally I could not figure out what zero it meant, so I deleted the Clear and the error went away.
It is referring to the fact that the TImage::Bitmap is initialized as (0,0) in size.
smd wrote:I also changed the TBitmapCodecSaveParams to the non-pointer way. The save works properly, but it also worked with the pointer declaration.
The pointer was uninitialized, so the memory address it contained was indeterminate, formed by whatever random data happened to be present in the memory block that the pointer was occupying. You were then writing your TBitmapCodecSaveParams values to a random memory address. Your code didn't crash only because the memory being pointed at by the pointer just happened to be read/write accessible within your process, but that doesn't change the fact that you were writing to random memory, corrupting the contents of that memory.
smd wrote:In the C language, if you declare a pointer, with the exception of void, storage for the value is also allocated at time of the declaration.
Yes, storage for the pointer itself it allocated. But the value stored inside that pointer is indeterminate until you assign a valid memory address to it, such as the result of malloc()/new, or operator&/std::addressof(), etc.
smd wrote:So it seems that

TBitmapCodecSaveParams *t;
t->Quality=100;

and

TBitmapCodecSaveParams t;
t.Quality=100;

should both work without having to do a new.
Not true. Using an uninitialized pointer like this is undefined behavior, in both C and C++. I think you need to brush up on your pointer fundamentals.
Remy Lebeau (TeamB)
Lebeau Software
Post Reply