| home
Using
Picture Objects
Ernest Murphy ernie@surfree.com
June 30 2001
Sample code for
this article is available.
Abstract:
------------------------------------------------------------------------------
The COM API provides methods to load many
useful picture formats, at least
.bmp. .ico, .wmf, .gif, and .,jpg. The use,
care and feeding of this interface will
be explained
Introduction:
------------------------------------------------------------------------------
This tutorial is based on the work in Q218972
- How To Load and Display Graphics Files w/LOADPIC.EXE. Basically,
all this tutorial consists of is a port of that C code to asm (no, not
by a program, by the "see C, write ASM method).
There are quite a few steps to actually get
a picture object, but these are always the same and may be contained in
a procedure such as in the tutorial.
What's a Picture Object Anyway?
------------------------------------------------------------------------------
Picture objects are the COM/OLE standard
way of loading picture files for ActiveX use. It's a nice, well behaved
API you can use in any application.
A Picture object is any object that provides
the IPicture interface. And IPicture does plenty of work for us. Let's
look at it's properties and methods:
| get_Handle |
Returns the Windows GDI handle of the picture
managed within this picture object. |
| get_Hpal |
Returns a copy of the palette currently
used by the picture object. |
| get_Type |
Returns the current type of the picture. |
| get_Width |
Returns the current width of the picture
in the picture object. |
| get_Height |
Returns the current height of the picture
in the picture object. |
| Render |
Draws the specified portion of the picture
onto the specified device context, positioned at the specified location. |
| set_Hpal |
Sets the current palette of the picture. |
| get_CurDC |
Returns the current device context into
which this picture is selected. |
| SelectPicture |
Selects a bitmap picture into a given device
context, returning the device context in which the picture was previously
selected as well as the picture's GDI handle. |
| get_KeepOriginalFormat |
Returns the current value of the picture
object's KeepOriginalFormat property. |
| put_KeepOriginalFormat |
Sets the picture object's KeepOriginalFormat
property. |
| PictureChanged |
Notifies the picture object that its picture
resource changed. |
| SaveAsFile |
Saves the picture's data into a stream in
the same format that it would save itself into a file. |
| get_Attributes |
Returns the current set of the picture's
bit attributes. |
That's quite a few goodies to work with for
one package. True, many seem to be wrappers for existing GDI methods, but
here they are all contained in one nice package (hey, maybe that's why
objects are so useful)
Creating a Picture Object
------------------------------------------------------------------------------
A Picture object is created from a stream
object, which is created from a.... OK, let's just jump to the code.
|
LoadPictureFile PROC pszFile:DWORD,
ppPicture:DWORD
LOCAL hFile:DWORD,
pvData:DWORD, dwBytesRead:DWORD
LOCAL pstm:DWORD,
dwFileSize:DWORD, hGlobal:DWORD
LOCAL ghWnd:DWORD,
bRead:DWORD
; This function loads a file into
an IStream.
; open file
invoke CreateFile,
pszFile, GENERIC_READ, 0, NULL,
OPEN_EXISTING, 0, NULL
mov hFile, eax
.IF hFile ==
INVALID_HANDLE_VALUE
; we had an error
mov eax, -1
ret
.ENDIF
; get file size
invoke GetFileSize,
hFile, NULL
mov dwFileSize,
eax
.IF dwFileSize
== -1
; we had an error
invoke CloseHandle, hFile
mov eax, -1
ret
.ENDIF
mov pvData, NULL
; alloc memory
based on file size
invoke GlobalAlloc,
GMEM_MOVEABLE, dwFileSize
mov hGlobal,
eax
.IF !hGlobal
; we had an error
invoke CloseHandle, hFile
mov eax, -1
ret
.ENDIF
invoke GlobalLock,
hGlobal
mov pvData ,
eax
.IF !pvData
; we had an error
ret
.ENDIF
mov dwBytesRead,
NULL
; read file
and store in global memory
invoke ReadFile,
hFile, pvData, dwFileSize,
ADDR dwBytesRead, NULL
mov bRead, eax
.IF bRead ==
FALSE ; we had an error
ret
.ENDIF
invoke GlobalUnlock,
hGlobal
invoke CloseHandle,
hFile
mov pstm, NULL
; create IStream*
from global memory
invoke CreateStreamOnHGlobal,
hGlobal, TRUE, ADDR pstm
.IF_FAILED
ret
.ENDIF
.IF !pstm
ret
.ENDIF
; Create IPicture
from image file
.IF ppPicture
mov ecx, ppPicture
mov ecx, [ecx]
.IF ecx;gpPicture1
coinvoke ecx, IUnknown, Release
.ENDIF
invoke OleLoadPicture, pstm, dwFileSize, FALSE,
ADDR IID_IPicture, ppPicture
.IF_FAILED
coinvoke pstm, IUnknown, Release
ret
.ENDIF
.ENDIF
coinvoke pstm,
IUnknown, Release
ret
LoadPictureFile ENDP
;----------------------------------------------
|
The LoadPictureFile
procedure
takes a character string (psz) that contains the picture's filename, and
assigns the picture object pointer to our pic object. It's smart enough
to release an existing pic object if you pass one in. Actually, there is
much more error handling in this routine then is usual, but I wanted to
make this a reusable routine for you.
All we did here is use standard API methods
to move a file into global memory. The first new COM method is the CreateStreamOnHGlobal
method. A "stream" is OLE-speak for a storage medium where data may be
streamed in or out. Of course, being a COM method it returns a stream object.
Since all we need do is pass this on and release it, we'll pretend it isn't
there.
The big action takes place with OleLoadPicture.
This method will create the picture object for us from the stream object.
Using the Picture Object
------------------------------------------------------------------------------
Once we have the picture object (which we
call to be created inside the WM_CREATE message handler) we want to use
it. IPicture does have a very nice Render method that is akin to a BitBlt
instruction, in that it copies a selected portion of the Picture contained
in the object to the device context you choose.
One real bit of true weirdness in this Render
method is vertically it works backwards! I mean, if you have a 20x30 pixel
bitmap and instruct it to render it starting at 0,0 and copy 20x30 piece,
you will get an image that is upside down. I have no reason to explain
this, just a work around. When Rendering this picture, instead tell it
to place it at 0x30 (i.e., down by the Y dimension) and to copy 20x-30
pixels (i.e., work up and not down).
The other bit of predictable weirdness is
the Picture object works in high metric units, so we need to convert these.
So let's go to the code:
|
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,
hWnd, ADDR ps
mov hdc, eax
.IF gpPicture1
; get width and height of picture
coinvoke gpPicture1, IPicture, get_Width,
ADDR hmWidth
coinvoke gpPicture1, IPicture, get_Height,
ADDR hmHeight
; convert himetric to pixels
invoke GetDeviceCaps,hdc, LOGPIXELSX
invoke MulDiv, hmWidth, eax, HIMETRIC_INCH
mov nWidth, eax
invoke GetDeviceCaps,hdc, LOGPIXELSY
invoke MulDiv, hmHeight, eax, HIMETRIC_INCH
mov nHeight, eax
xor eax, eax
sub eax, hmHeight
mov neghmHeight, eax
invoke GetClientRect, hWnd, ADDR rect
; display picture using IPicture::Render
coinvoke gpPicture1, IPicture, Render, hdc, 0, 0,
nWidth, nHeight, 0, hmHeight,
hmWidth, neghmHeight, ADDR rect
.ENDIF
invoke EndPaint,
hWnd, ADDR ps
.ELSEIF
|
That's about it, except for one last bit
of clean-up. When your app exits, and if you still have a picture object
handy, you need to release it like so:
|
.IF gpPicture1
coinvoke gpPicture1, IUnknown, Release
.ENDIF |
------------------------------------------------------------------------------
I hope you get some work out of this useful
object.
Enjoy
home |