Saving a Bitmap

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Saving a Bitmap

Mikael Svane
Today I wanted to save a Bitmap to a .bmp file. I couldn't find anything
useful in the image, so I searched old posts using DSDN. Among others, I
found an article from Andy dated 3rd November 1998 where he wrote:

>Unfortunately there is no method to directly save a bitmap to a file (MS
>didn't make it that easy).
>You'll have to do this with a number of API calls.

Is this still true or is there another way to do it now?

Sincerely,

Mikael Svane
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Saving a Bitmap

David Simmons
"Mikael Svane" <[hidden email]> wrote in message
news:93899s$995vt$[hidden email]...
> Today I wanted to save a Bitmap to a .bmp file. I couldn't find anything
> useful in the image, so I searched old posts using DSDN. Among others, I
> found an article from Andy dated 3rd November 1998 where he wrote:
>
> >Unfortunately there is no method to directly save a bitmap to a file (MS
> >didn't make it that easy).
> >You'll have to do this with a number of API calls.
>
> Is this still true or is there another way to do it now?

I don't know if this will help you, but here is the method from QKS
Smalltalk for reading a DIB directly from a file. Obviously the reverse will
write it out to a file.
...

-- Dave Simmons [www.qks.com / www.smallscript.com]
  "Effectively solving a problem begins with how you express it."

>
> Sincerely,
>
> Mikael Svane
> [hidden email]
>

fromFile: aFileSpecifierOrPath

    <annotations>

       “Description: Return a new instance of the receiver based on the data
from the
        specified file.”

        Method Author:      'David Simmons'.
        Method Updated:     'Wed 07/30/1997 05:24:54 PM (PDT)'.
        Browse Category:    'instance creation'.

        Method Scope:        Default.
        Scope Access:       'public-extended'.
        Method Type:        'factory'.

        Method Interface:    none.
        Method Returns:      none.
        Method Throws:       none.

    </annotations>

    | specifier stream nBytes inst |

   “Coerce paths to specifiers”
    (specifier := (aFileSpecifierOrPath
        ? [^nil]) asSpecifier) ? [^nil].

   “Now attempt to open it”
    (stream := specifier asReadStream) ? [^nil].

   “Now allocate a new instance and read the value into the instance.”
    nBytes := stream seekLimit-14.
    stream position: 14;
        read: nBytes into: (inst := DIBitmap basicNewStorage: nBytes);
        close.

   “After closing the stream, return the configured instance”
   ^inst
/*
typedef struct tagBITMAPFILEHEADER { // bmfh
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER;
*/


Reply | Threaded
Open this post in threaded view
|

Re: Saving a Bitmap

Ian Bartholomew
In reply to this post by Mikael Svane
Mikael,

> Is this still true or is there another way to do it now?

I've got a Bitmap subclass that can be used as a normal Bitmap but can also
be saved as a file. There's a bit more to it than you might imagine, you
have to get involved in colour tables and bitmap structures, but it seems to
work as expected.  It is a bit more inefficient than the existing Bitmap
subclasses, memory wise, so I tend to only use it for bitmaps that I know
want to file out but _theoretically it could be used in place all the
Dolphin bitmaps.

It (the DeviceIndependentBitmap package) has been sitting on my system for
some time now waiting to be uploaded as a goodie, I just can't get round to
the documentation. I'll try to do it in the next few days but let me know if
you want a copy mailed to you before that.

Ian


Reply | Threaded
Open this post in threaded view
|

Re: Saving a Bitmap

Blair McGlashan
In reply to this post by Mikael Svane
Mikael

"Mikael Svane" <[hidden email]> wrote in message
news:93899s$995vt$[hidden email]...
> Today I wanted to save a Bitmap to a .bmp file. I couldn't find anything
> useful in the image, so I searched old posts using DSDN. Among others, I
> found an article from Andy dated 3rd November 1998 where he wrote:
>
> >Unfortunately there is no method to directly save a bitmap to a file (MS
> >didn't make it that easy).
> >You'll have to do this with a number of API calls.
>
> Is this still true or is there another way to do it now?

Loading bitmaps from files is of course very easy since there is a Win32 API
call to do it (LoadImage() passing the LR_LOADFROMFILE flag), and this is
what all the various Dolphin Image subclasses (Bitmap, Icon, Cursor, etc)
use to implement #loadFromFile:. Unfortunately Microsoft don't (or at least
did not, one would have to check MSDN for the current state of play) provide
a similar API call to save a BMP. It is relatively easy to do, however, (the
on-disk format is essentially the same as the in-memory representation of a
DIB, Device-Independent Bitmap). As Ian points out, he has code to do this
as well in his DIBitmap goodie, and one can also find example code on MSDN.

The preferred form of Bitmap these days is a DIBSection. This combines the
device-independence of the older DIB format, and the efficiency of the plain
on old device-dependent Bitmap. I'd imagine code to directly save these
could be found, and I'd start with checking MSDN.

An alternative is to make use of the services of OLEPicture. An example:
We'll start off with a JPEG, as OLEPicture can load and then save these in
bmp form.

    o := OLEPicture fromFile: 'Resources\Dolphin1.jpg'.

Test to see if it is what we are expecting:

    ImagePresenter show value: o.

Now save it to a file:

    s := IStream onHGLOBAL.
    o picture saveAsFile: s fSaveMemCopy: true.
    (FileStream write: 'c:\test.bmp' text: false) nextPutAll: s contents;
close.

(Hmmm, this looks like the makings of #saveAsFile: method for OLEPicture)

And then view that:

    ImagePresenter show value: (DIBSection fromFile: 'c:\test.bmp').

Note that this will be inefficient for large bitmaps because the bitmap is
written first to an in memory stream, before being written to disk as a byte
image. AFAIK Microsoft do not provide an implementation of IStream that sits
directly on top of a raw OS file (odd that). I have code on the shelf which
implements the COM interface <IStream> directly onto a Dolphin FileStream
though, if I could just find it. In the meantime a more efficient form would
be:

    s := IStream onHGLOBAL.
    byteSize := o picture saveAsFile: s fSaveMemCopy: true.
    hglobal := s handle.
    (File open: 'c:\test.bmp') write: (KernelLibrary default globalLock:
hglobal) count: byteSize; close.
    KernelLibrary default globalUnlock: hglobal

So the method I've attached uses (essentially) this messier form.

Regards

Blair

----------------------
!OLEPicture methodsFor!

saveAsFile: pathString
 "Save this picture to a file at the specified path, and existing file at
that
 path is overwritten."

 | stream hglobal byteSize pBytes |
 stream := IStream onHGLOBAL.
 byteSize := self picture saveAsFile: stream fSaveMemCopy: true.
 hglobal := stream handle.
 pBytes := KernelLibrary default globalLock: hglobal.
 [ (File open: pathString)
   write: pBytes count: byteSize;
   close
 ] ensure: [KernelLibrary default globalUnlock: hglobal]! !
!OLEPicture categoriesFor: #saveAsFile:!file operations!public! !


























begin 666 OLEPicture_saveAsFile.st
M(4],15!I8W1U<F4@;65T:&]D<T9O<B$-"@T*<V%V94%S1FEL93H@<&%T:%-T
M<FEN9PT*"2)3879E('1H:7,@<&EC='5R92!T;R!A(&9I;&4@870@=&AE('-P
M96-I9FEE9"!P871H+"!A;F0@97AI<W1I;F<@9FEL92!A="!T:&%T#0H)<&%T
M:"!I<R!O=F5R=W)I='1E;BXB#0H-"@E\('-T<F5A;2!H9VQO8F%L(&)Y=&53
M:7IE('!">71E<R!\#0H)<W1R96%M(#H]($E3=')E86T@;VY(1TQ/0D%,+@T*
M"6)Y=&53:7IE(#H]('-E;&8@<&EC='5R92!S879E07-&:6QE.B!S=')E86T@
M9E-A=F5-96U#;W!Y.B!T<G5E+@T*"6AG;&]B86P@.CT@<W1R96%M(&AA;F1L
M92X-"@EP0GET97,@.CT@2V5R;F5L3&EB<F%R>2!D969A=6QT(&=L;V)A;$QO
M8VLZ(&AG;&]B86PN#0H)6PDH1FEL92!O<&5N.B!P871H4W1R:6YG*2 -"@D)
M"7=R:71E.B!P0GET97,@8V]U;G0Z(&)Y=&53:7IE.R -"@D)"6-L;W-E#0H)
M72!E;G-U<F4Z(%M+97)N96Q,:6)R87)Y(&1E9F%U;'0@9VQO8F%L56YL;V-K
M.B!H9VQO8F%L72$@(0T*(4],15!I8W1U<F4@8V%T96=O<FEE<T9O<CH@(W-A
G=F5!<T9I;&4Z(69I;&4@;W!E<F%T:6]N<R%P=6)L:6,A("$-"@T*
`
end


Reply | Threaded
Open this post in threaded view
|

Re: Saving a Bitmap (and a Package Browser bug)

Mikael Svane
Blair,

Although Ians solution works fine, I wanted to try this one also. I filed in
the code attached and replaced every instance of Bitmap with OLEPicture.
Then I tried to save the package, but it complains about cyclic
prerequisites:

1) My package has two prerequisites, Dolphin and OLE COM.

2) OLE COM also has two prerequisites, Dolphin and OLE Structured Storage.

3) OLE Structured Storage has two prerequisites, Dolphin and OLE COM, due to
OLEPicture>>saveAsFile: that references the global IStream.

4) repeat from #2.

Also there seems to be a problem when using loose methods with the little
red arrow that indicates packages that were changed. Perhaps you are aware
of this, but otherwise this reproduces the problem:

1) create a new package.

2) create a new method in one of the available classes, for example
Model>>testMethod. This marks the package called Dolphin with a red arrow.

3) drag the method to the package you created. It appears in the Loose
methods, but the package is not marked and Dolphin remains marked.

4) remove the method from your package. It remains in Loose methods until
you select another package and then return to your package.

5) if you instead of number 3 above right click on the method in the CHB and
select Package from the context menu, it doesn't appear in Loose methods
until you select another package and then return. Your package still isn't
marked with the arrow, but Dolphin, in this case, is.

Since the arrow is so useful, it would be nice if it worked for loose
methods too.


Regards
Mikael
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Saving a Bitmap (and a Package Browser bug)

Blair McGlashan
Mikael

"Mikael Svane" <[hidden email]> wrote in message
news:93ambb$9g3ea$[hidden email]...
>...
> Then I tried to save the package, but it complains about cyclic
> prerequisites:
>
> 1) My package has two prerequisites, Dolphin and OLE COM.
>
> 2) OLE COM also has two prerequisites, Dolphin and OLE Structured Storage.
>
> 3) OLE Structured Storage has two prerequisites, Dolphin and OLE COM, due
to
> OLEPicture>>saveAsFile: that references the global IStream.
>
> 4) repeat from #2.

Yes, that is true. I should have mentioned that it is necessary to make
OLEPicture>>saveAsFile: a loose method in in your own package. I assumed you
would do this because otherwise you would lose the method if you saved your
package and reloaded it into a fresh image, but I should have mentioned it
instead of making assumptions. Sorry.

> Also there seems to be a problem when using loose methods with the little
> red arrow that indicates packages that were changed. Perhaps you are aware
> of this, but otherwise this reproduces the problem:

Thanks. We are aware of a bug that that Bob Jarvis (?) reported recently to
do with Dolphin not updating the package change markers correctly when
moving methods to become loose methods. If this is not the same thing, it is
certainly related, so we will bear it in mind when looking into that. I'll
just comment on one point to clear up a misunderstanding:

> ...
> 3) drag the method to the package you created. It appears in the Loose
> methods, but the package is not marked and Dolphin remains marked.

You're right, of course, that the target package should be marked as
changed. However it is expected that Dolphin will remain marked, because as
far as the system is concerned the package has still been changed. The
change information is not held at a sufficient level of detail to determine
that the method was the source of the original change, and that has now been
moved out. This means you may need to clear change flags in Dolphin
occassionally.

(An aside for David Gorisek: David, what would STS do in this case?)

> Since the arrow is so useful, it would be nice if it worked for loose
> methods too.

It it intended to, and we will fix this in a forthcoming maintenance (free
upgrade) release.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Saving a Bitmap (and a Package Browser bug)

Mikael Svane
>Yes, that is true. I should have mentioned that it is necessary to make
>OLEPicture>>saveAsFile: a loose method in in your own package. I assumed
you
>would do this because otherwise you would lose the method if you saved your
>package and reloaded it into a fresh image, but I should have mentioned it
>instead of making assumptions. Sorry.
>


Ok. I thought that the method was so useful that I saved the image instead
of doing what I would normally do (I keep all my changes in packages that I
install every day).

>> ...
>> 3) drag the method to the package you created. It appears in the Loose
>> methods, but the package is not marked and Dolphin remains marked.
>
>You're right, of course, that the target package should be marked as
>changed. However it is expected that Dolphin will remain marked, because as
>far as the system is concerned the package has still been changed. The
>change information is not held at a sufficient level of detail to determine
>that the method was the source of the original change, and that has now
been
>moved out. This means you may need to clear change flags in Dolphin
>occassionally.
>

I had thought that it was the _method_ that was marked as changed and that a
package that contained such a method would be marked with the arrow, but if
it is the package that is marked, I understand why Dolphin behaves like
this.

Regards,

Mikael
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Saving a Bitmap (and a Package Browser bug)

Blair McGlashan
Mikael

You wrote in message news:93da6a$9vd74$[hidden email]...
> >Yes, that is true. I should have mentioned that it is necessary to make
> >OLEPicture>>saveAsFile: a loose method in in your own package. I assumed
> you
> >would do this because otherwise you would lose the method if you saved
your
> >package and reloaded it into a fresh image, but I should have mentioned
it
> >instead of making assumptions. Sorry.
> >
>
>
> Ok. I thought that the method was so useful that I saved the image instead
> of doing what I would normally do (I keep all my changes in packages that
I
> install every day).

I'm not for one minute suggesting that you shouldn't save the image. The
image is the state of the world, and should be saved, every few minutes or
even more often. Not doing so is throwing away one of Smalltalk's greatest
treasures. I know some people don't like to work like that, but I can only
refer you to Andy's "You're all bonkers" posting (check Ian's archive) :-).

Regards

Blair