An exercise in frustration with TImage :-(

An exercise in frustration with TImage :-(

Post by Ted Byer » Fri, 04 May 2001 23:38:42



Please look at the following three functions.  Using the first version of
OtherGraphicsFiles1Click (which loads a graphics file) and
OtherGraphicsFiles2Click (which saves the current image TO a graphics file),
I can load graphics files  in any of the formats supported by TImage and
save the contents of TImage in any format supported by TImage (e.g., I can
load an image as a gif and save it as a jpg using this code).  MyImage is a
TImage placed at design time on a TScrollBox.

The ONLY problem with this code is that TImage will allow me to draw on it
ONLY if it contains a bitmap.

Therefore, I have been struggling with the code in what is labelled as the
second version.  theMap is a TBitmap, backgroundImage is a std::string that
holds the name of the map used.  There are comments in the body of the
second version which describe some of the puzzles that are plaguing me.  As
currently shown, I see the bitmap image change to the correct shape and size
(I am testing with a bitmap that is rectangular while the default is square
with a slightly larger area), but the image shown is all white.  The actual
map is not shown.  I do not understand this.  When I loaded the map directly
into MyImage, it displayed properly.  I just wasn't able to drawn on it when
it wasn't a bitmap.  The procedure I am using for theTmpImage is the same as
I was using to load the map into MyImage.  And the procedure for copying the
TImage into theMap is the same as that which I used to be able to change the
TImage into any of the supported graphics file formats.  So I do not
understand why I get a solid white background, albeit of the right shape and
size, instead of the map.

NB: I MUST use TImage to load the graphics file because it is the only one
that knows about TBitmap, TGIFImage, and TJPEGImage!  I can not know at
compile time what kind of image the user is selecting.  And I must use a
TBitmap as the picture in MyImage so that I can draw on it!

Can anyone see my error?  (Yes, I know, I am probably leaking theTmpImage,
and should probably wrap it in an std::auto_ptr, but I'll worry about that
after I get the main problem fixed: perhaps I can get rid of it altogether
if I can change the kind of image TImage holds: i.e. load a gif into
MyImage, copy it to theMap, and then make MyImage hold a TBitmap copy of
theMap instead of a TGIFImage copy of it - but TImge doesn't want me to
alter it if it holds anything other than a TBitmap).  Or am I destined to be
bald by the end of the week?  :-(

Any help would be greatly appreciated!

BTW: I am deprecating the use of gifs, for the usual reasons, and have added
support for PNG, but I have not yet found a way to integrate png support
into TImage, so I have separate functions for loading and unloading png
files.

Thanks,

Ted

R.E. Byers

==========the code====================
//--------------second version----------------------------------
void __fastcall TMyMainForm::OtherGraphicsFiles1Click(TObject *Sender)
{  // body of function to load files
  if ( MyOpenPictureDialog->Execute() ) {
    theMap = new Graphics::TBitmap();
    TImage *theTmpImage = new TImage(this);
    TRect theRect;
    theRect.Left = 0;
    theRect.Top = 0;
    theTmpImage->AutoSize = true;
    backgroundImage = MyOpenPictureDialog->FileName.c_str();
// the next line fails if the image loaded it NOT a bitmap
// - no surprise here since theMap IS a TBitmap!
    //theMap->LoadFromFile(MyOpenPictureDialog->FileName);
    theTmpImage->Picture->LoadFromFile(MyOpenPictureDialog->FileName);
    bmpWidth = theTmpImage->Width;
    bmpHeight = theTmpImage->Height;
    zoom = 1.0;
    zooming = false;
    theRect.Right = bmpWidth;
    theRect.Bottom = bmpHeight;
    theMap->Width = bmpWidth;
    theMap->Height = bmpHeight;
// these statements fail with an exception complaining that a TImage can
// only be modified if it holds a TBitmap!  WHY?!?!?!  I am merely trying
// to copy it to a TBitmap!!!!!!! which in turn would be copied to MyImage!
    //theMap->Canvas->CopyRect(theRect,theTmpImage->Canvas,theRect);
    //BitBlt(theMap->Canvas->Handle,0,0,bmpWidth,bmpHeight,
    //       theTmpImage->Canvas->Handle,0,0,
    //       SRCCOPY);
    theMap->Canvas->Draw( 0, 0,theTmpImage->Picture->Graphic);
    MyImage->AutoSize = true;
    MyImage->Stretch = false;
    MyImage->Picture->Bitmap = theMap;
    MyImage->Canvas->Draw( 0, 0,theTmpImage->Picture->Graphic);
    MyImage->AutoSize = false;
    MyImage->Stretch = true;
    MyImage->Top = 0;
    MyImage->Left = 0;
    createBackgrounds();
    FormPaint(Sender);
  };

Quote:}

//--------------first, and current,
version-------------------------------------------
void __fastcall TMyMainForm::OtherGraphicsFiles2Click(TObject *Sender)
{
  //this function uses the picture save dialog to save a graphics file
  if ( MySavePictureDialog->Execute() ) {
    AnsiString fname =MySavePictureDialog->FileName;
    AnsiString fext = ExtractFileExt(fname).LowerCase();
    if (fext.IsEmpty() ) {
      ShowMessage("You must enter a name with a standard graphics file
extension");
      return;
    }
    if (fext == ".bmp") {
      //ShowMessage("A bitmap name was selected");
      std::auto_ptr<Graphics::TBitmap> Bitmap(new Graphics::TBitmap());
      Bitmap->Width = MyImage->Width;
      Bitmap->Height = MyImage->Height;
      Bitmap->Canvas->Draw( 0, 0,MyImage->Picture->Graphic);
      Bitmap->SaveToFile(fname);
      return;
    }
    if ((fext == ".jpg") || (fext == ".jpeg") ) {
      //ShowMessage("A bitmap name was selected");
      std::auto_ptr<TJPEGImage> Bitmap(new TJPEGImage());
      //
      //An ugly hack since TJPEGImage doesn't like TGIFImage
      //
      std::auto_ptr<Graphics::TBitmap> TmpBitmap(new Graphics::TBitmap());
      TmpBitmap->Width = MyImage->Width;
      TmpBitmap->Height = MyImage->Height;
      TmpBitmap->Canvas->Draw( 0, 0,MyImage->Picture->Graphic);
      //Bitmap->Width = MyImage->Width;
      //Bitmap->Height = MyImage->Height;
      Bitmap->Assign(TmpBitmap.get());
      Bitmap->SaveToFile(fname);
      return;
    }
    if (fext == ".gif") {
      //ShowMessage("A bitmap name was selected");
      std::auto_ptr<TGIFImage> Bitmap(new TGIFImage());
      Bitmap->Width = MyImage->Width;
      Bitmap->Height = MyImage->Height;
      Bitmap->Assign(MyImage->Picture->Graphic);
      Bitmap->SaveToFile(fname);
      return;
    }
    ShowMessage("Sorry!  I do not recognise that graphics file extension.");
  }
Quote:}

//--------------first version-------------------------------------------
void __fastcall TMyMainForm::OtherGraphicsFiles1Click(TObject *Sender)
{
  //This function uses the picture load dialog to open a graphics file
  if ( MyOpenPictureDialog->Execute() ) {
    MyImage->AutoSize = true;
    MyImage->Stretch = false;
    MyImage->Picture->LoadFromFile(MyOpenPictureDialog->FileName);
    MyStatusBar->Panels->Items[2]->Text = "World = (" +
IntToStr(MyImage->Width)
                                      + ", " + IntToStr(MyImage->Height) +
")";
    MyImage->AutoSize = false;
    MyImage->Stretch = true;
    MyImage->Visible = true;
    MyImage->Top = 0;
    MyImage->Left = 0;
  }
Quote:}

//--------------------------------------------------------------------------
-
 
 
 

An exercise in frustration with TImage :-(

Post by J.A. Bijsterbosc » Mon, 07 May 2001 18:25:49


Hello Ted,



[ snip ]

Quote:> The ONLY problem with this code is that TImage will allow me to draw on it
> ONLY if it contains a bitmap.

[ snip ]

Quote:> NB: I MUST use TImage to load the graphics file because it is the only one
> that knows about TBitmap, TGIFImage, and TJPEGImage!  I can not know at
> compile time what kind of image the user is selecting.  And I must use a
> TBitmap as the picture in MyImage so that I can draw on it!

Hmm, you have to rethink the way you use the TImage in combination with the
TOpenDialog where the user makes his/her choice IHMO.
Easiest is to use the Filter and FilterIndex properties to check what kind
of graphic format is selected. After that it's quite simple to use the
FilterIndex to load the graphic in the correct format and assign that to a
TImage.
The only caveat is that the Filter property of a TOpenDialog most of the
time contains an entry All Files (*.*) too, so you will have to cater for
that with a function that checkes for an allowed graphic extension. Set up
some constants first

const int TARGA_IMG = 1;    // corresponds with TOpenDialog::FilterIndex
const int JPEG_IMG  = 2;
const int BMP_IMG   = 3;
const int GIF_IMG   = 4;
const int UNDEF_IMG = 5;    // All Files (*.*) index in
TOpenDialog::FilterIndex

int __fastcall TMainForm::FindImageType(String Name)
{
  int result = UNDEF_IMG;

  if( LowerCase(ExtractFileExt(Name)) == ".tga" )
    result = TARGA_IMG;
  else if( LowerCase(ExtractFileExt(Name)) == ".jpg" )
    result = JPEG_IMG;
  else if( LowerCase(ExtractFileExt(Name)) == ".jpeg" )
    result = JPEG_IMG;
  else if( LowerCase(ExtractFileExt(Name)) == ".bmp" )
    result = BMP_IMG;
  else if( LowerCase(ExtractFileExt(Name)) == ".gif" )
    result = GIF_IMG;

  return result;

Quote:}

Now in some event do something like:

if( OpenDialog->Execute() ) {
    int filterIndex = OpenDialog->FilterIndex;
    if( filterIndex == UNDEF_IMG ) {
        filterIndex = FindImageType(OpenDialog->FileName);
        if( filterIndex == UNDEF_IMG ) {
            ShowMessage("Not a supported graphics file format...");
            return;
        }
    }
    switch( filterIndex ) {
        case BMP_IMG: {    // .bmp
            Graphics::TBitmap *bmp = new Graphics::TBitmap;
            bmp->LoadFromFile(OpenDialog->FileName);
            Image->Picture->Bitmap->Assign(bmp);
            delete bmp;
        } break;
        case JPEG_IMG: {      // .jpeg, .jpg
            TJPEGImage *jpg = new TJPEGImage;
            jpg->LoadFromFile(OpenDialog->FileName);
            Image->Picture->Bitmap->Assign(jpg);
            delete jpg;
        } break;
        ...     // and so on
    }

Quote:}
> Any help would be greatly appreciated!

Hope this gets you going...;-))

Quote:> Ted

--
Greetings from sunny Amsterdam

         Jan


http://home.worldonline.nl/~bijster

 
 
 

An exercise in frustration with TImage :-(

Post by Ted Byer » Tue, 08 May 2001 14:38:22


Thanks Jan,

I'll do a bit of experimenting and see how I do.

But one question.  In the switch you show, does "Image" already have an
instance of a bitmap, or do I need to create a blank bitmap and give that to
it before doing the Assign(graphic)?

Quote:>     switch( filterIndex ) {
>         case BMP_IMG: {    // .bmp
>             Graphics::TBitmap *bmp = new Graphics::TBitmap;
>             bmp->LoadFromFile(OpenDialog->FileName);
>             Image->Picture->Bitmap->Assign(bmp);
>             delete bmp;
>         } break;
>         case JPEG_IMG: {      // .jpeg, .jpg
>             TJPEGImage *jpg = new TJPEGImage;
>             jpg->LoadFromFile(OpenDialog->FileName);
>             Image->Picture->Bitmap->Assign(jpg);
>             delete jpg;
>         } break;
>         ...     // and so on
>     }
> }

It seems to me that if Image->Picture->Bitmap does not already have a value,
I'd get an access violation when trying to do the Assign.  I do not know why
it would have a default bitmap when it could as readily can a metafile or
gif or jpeg.

Thanks again,

Ted

 
 
 

An exercise in frustration with TImage :-(

Post by J.A. Bijsterbosc » Thu, 10 May 2001 16:42:42


Hello Ted,



Quote:> Thanks Jan,

> I'll do a bit of experimenting and see how I do.

> But one question.  In the switch you show, does "Image" already have an
> instance of a bitmap, or do I need to create a blank bitmap and give that
to
> it before doing the Assign(graphic)?

Well TPicture has in fact and yes you can use it directly. You should
realise that all graphics formats used in bcb and in windows for that
matter, with the exception of metafiles, are translated to the bitmap format
internally. This means that you don't lose anything when doing this directly
by hand instead of letting TPicture handle this. As an added bonus you can
then use the TImage::Canvas or TImage->Picture->Bitmap::Canvas to do
additional drawing, like for instance selection rectangles and such.
In principle properties in the VCL behave the same all along the way, so
assigning a graphic to a Graphics::TBitmap works the same as using
TImage_>Picture->Bitmap.
The only exception to this I have found is the Glyph property of a TBitBtn.
This is a TBitmap too, but using this property at runtime as a TBitmap will
give strange errors. I think this is a flaw in its implementation, but it
could well be uncle Bill's code responsibility underneath<g> (as it's
obviously a wrapper for winapi code)

Quote:> >     switch( filterIndex ) {
> >         case JPEG_IMG: {      // .jpeg, .jpg
> >             TJPEGImage *jpg = new TJPEGImage;
> >             jpg->LoadFromFile(OpenDialog->FileName);
> >             Image->Picture->Bitmap->Assign(jpg);
> >             delete jpg;
> >         } break;
> >         ...     // and so on
> >     }
> > }

> It seems to me that if Image->Picture->Bitmap does not already have a
value,
> I'd get an access violation when trying to do the Assign.

Never had that problem, and I use the above construct quite a lot when using
graphics...

Quote:> I do not know why
> it would have a default bitmap when it could as readily can a metafile or
> gif or jpeg.

See above.
Btw, I didn't look very close to the code in your initial post, but I
noticed the FormPaint() call at the bottom. If you use an OnPaint event to
show your graphic you shouldn't use a TImage. In that case you should use a
TBitmap directly. Saves quite a lot of overhead...

Quote:> Thanks again,

Hope this gets you going...;-))

Quote:> Ted

--
Greetings from sunny Amsterdam

         Jan


http://home.worldonline.nl/~bijster

 
 
 

An exercise in frustration with TImage :-(

Post by Ted Byer » Thu, 10 May 2001 23:04:53


Thanks Jan,

Ted