|
|
Microsoft OS/2 SDK PM 02-24-1989
/************************************************************************
*
* lffile.c -- File handling subroutines for LineFrac.
*
* Created by Microsoft Corporation, 1989
*
************************************************************************/
#define INCL_WIN
#define INCL_GPI
#define INCL_DOSMEMMGR
#define INCL_DOSFILEMGR
#define INCL_BITMAPFILEFORMAT
#include <os2.h>
#include <stdio.h>
#include "opendlg.h"
#define INCL_GLOBALS
#define INCL_THREADS
#include "linefrac.h"
#define INCL_LFFILE
#define INCL_LFPS
#define INCL_LFUTIL
#include "lffuncs.h"
/*
* this is the bitmap resource file format structure
*/
typedef struct {
USHORT wType;
ULONG dwSize;
int xHotspot;
int yHotspot;
ULONG dwBitsOffset;
USHORT bmWidth; /* from here this is a BitmapInfo table */
USHORT bmHeight;
USHORT bmPlanes;
USHORT bmBitcount;
} RCBITMAP;
typedef RCBITMAP FAR *PRCBITMAP;
/************************************************************************
*
* Global Variables
*
************************************************************************/
extern GLOBALDATA global;
/************************************************************************
*
* LfReadFile
*
* Calls the OpenDlg function to ask the user what file name to
* read from.
*
************************************************************************/
VOID
LfReadFile(hwnd, pthr)
HWND hwnd;
PTHR pthr;
{
HFILE hFile;
DLF dlf;
dlf.rgbAction = DLG_OPENDLG;
dlf.rgbFlags = ATTRDIRLIST;
dlf.phFile = &hFile;
dlf.pszExt = (PSZ)"\\*.bmp";
dlf.pszAppName = "LineFrac";
dlf.pszTitle = "Load Bitmap";
dlf.pszInstructions = NULL;
dlf.szFileName[0] = '\0';
dlf.szOpenFile[0] = '\0';
switch (DlgFile(hwnd,&dlf))
{
case TDF_ERRMEM:
case TDF_INVALID:
MyMessageBox(hwnd, "Error reading file.");
break;
case TDF_NOOPEN:
break;
default:
if (!LfReadBMP(pthr, &dlf))
MyMessageBox(hwnd, "Error reading file.\nIs AutoResize enabled?");
}
}
/************************************************************************
*
* LfReadBMP
*
* Read a bitmap in from a BMP format file. Prepare the DC for the
* given thread to accept it. The user can have the DC resized to
* fit exactly the bitmap, or fit the bits in as best as we can.
* If we're not resizing and the bitmap is larger than the thread's
* DC, then load the bits flush with the lower left.
*
* Both old-style (PRCBITMAP) and new-style (PBITMAPFILEHEADER)
* bitmaps can be read.
*
* Free up memory and close the file before leaving. The file
* will have been opened by the time this function is called,
* and the file handle will be in the *pdlf structure.
*
************************************************************************/
BOOL
LfReadBMP(pthr, pdlf)
PTHR pthr;
PDLF pdlf; /* File information filled by DlgFile. */
{
HFILE hfile;
ULONG cScans;
ULONG ulSize; /* Number of bytes occupied by bitmap bits. */
USHORT cSegs; /* Number of 64K segments in ulSize. */
USHORT cbExtra; /* Bytes in last segment of ulSize. */
SEL sel; /* Base selector to file data. */
USHORT hugeshift; /* Segment index shift value. */
USHORT cbRead1; /* Number of bytes to read first call to DosRead */
USHORT cbRead2; /* Number of bytes to read second call to DosRead */
USHORT cbRead; /* Number of bytes read by DosRead. */
BOOL fRet = FALSE; /* Function return code. */
int i; /* Generic loop index. */
FILESTATUS fsts;
PBITMAPFILEHEADER pbfh;
PRCBITMAP rb;
PBYTE pImage;
/*******************************************************************
* Find out how big the file is so we can read the whole thing in.
*******************************************************************/
hfile = *(pdlf->phFile);
if( DosQFileInfo( hfile, 1, &fsts, sizeof(FILESTATUS)) != 0)
goto lfread_error_close_file;
ulSize = fsts.cbFile;
cSegs = (USHORT)(ulSize/0x10000L);
cbExtra = (USHORT)(ulSize%0x10000L);
if (DosAllocHuge(cSegs, cbExtra, (PSEL)&sel, 0, 0))
goto lfread_error_close_file;
if (DosGetHugeShift(&hugeshift))
goto lfread_error_free_bits;
pImage = (PBYTE)MAKEP(sel, 0);
rb = (PRCBITMAP)pImage;
pbfh = (PBITMAPFILEHEADER)pImage;
/*******************************************************************
* Read the bits in from the file. The DosRead function allows a
* maximum of 64K-1 bytes read at a time. We get around this
* by reading two 32K chunks for each 64K segment, and reading the
* last segment in one piece.
*******************************************************************/
for (i = 0; i <= cSegs; ++i)
{
if (i < cSegs)
{
/* This segment is 64K bytes long, so split it up. */
cbRead1 = 0x8000;
cbRead2 = 0x8000;
}
else
{
/* This segment is less than 64K bytes long, so read it all. */
cbRead1 = cbExtra;
cbRead2 = 0;
}
/* There's a possibility that cbExtra will be 0, so check
* to avoid an unnecessary system call.
*/
if (cbRead1 > 0)
{
if (DosRead( hfile
, (PVOID)MAKEP(sel+(i<<hugeshift), 0)
, cbRead1
, &cbRead))
goto lfread_error_free_bits;
if (cbRead1 != cbRead)
goto lfread_error_free_bits;
}
/* This will always be skipped on the last partial segment. */
if (cbRead2 > 0)
{
if (DosRead( hfile
, (PVOID)MAKEP(sel+(i<<hugeshift), cbRead1)
, cbRead2
, &cbRead))
goto lfread_error_free_bits;
if (cbRead2 != cbRead)
goto lfread_error_free_bits;
}
}
/*******************************************************************
* At this point we have the bitmap completely in memory. Now we
* look at how the user wants them set into the thread's PS. If
* the thread has fAutoResizePS set, then make the PS fit the size
* of the bitmap (the easy case). If the flag is not set, then
* figure out how to place it.
*******************************************************************/
if (pthr->fAutoSizePS)
{
if (pbfh->bmp.cbFix != sizeof(BITMAPINFOHEADER))
{
global.bm.cx = rb->bmWidth;
global.bm.cy = rb->bmHeight;
global.bm.cPlanes = rb->bmPlanes;
global.bm.cBitCount = rb->bmBitcount;
}
else
{
global.bm.cx = pbfh->bmp.cx;
global.bm.cy = pbfh->bmp.cy;
global.bm.cPlanes = pbfh->bmp.cPlanes;
global.bm.cBitCount = pbfh->bmp.cBitCount;
}
LfResizePS(pthr);
}
else
goto lfread_error_free_bits;
/*******************************************************************
* Tell GPI to put the bits into the thread's PS. The function returns
* the number of scan lines of the bitmap that were copied. We want
* all of them at once.
*******************************************************************/
if (pbfh->bmp.cbFix != sizeof(BITMAPINFOHEADER))
{
pImage += rb->dwBitsOffset;
rb->dwBitsOffset = sizeof(BITMAPINFOHEADER);
cScans = GpiSetBitmapBits( pthr->hps
, 0L
, (LONG)rb->bmHeight
, pImage
, (PBITMAPINFO)&(rb->dwBitsOffset));
if (cScans != (LONG)rb->bmHeight) /* compare with original number of scans */
goto lfread_error_free_bits;
}
else
{
cScans = GpiSetBitmapBits( pthr->hps
, 0L
, (LONG)pbfh->bmp.cy
, pImage + pbfh->offBits
, (PBITMAPINFO)&(pbfh->bmp));
if (cScans != (LONG)pbfh->bmp.cy) /* compare with original number of scans */
goto lfread_error_free_bits;
}
fRet = TRUE; /* We made it! The bits are in the thread's PS. */
/*******************************************************************
* Close the file, free the buffer space and leave. This is a
* common exit point from the function. Since the same cleanup
* operations need to be performed for such a large number of
* possible error conditions, this is a concise way to do the right
* thing.
*******************************************************************/
lfread_error_free_bits:
DosFreeSeg(sel);
lfread_error_close_file:
DosClose(hfile);
return fRet;
}
/************************************************************************
*
* LfWriteFile
*
* Calls the OpenDlg function to ask the user what file name to
* save under.
*
************************************************************************/
VOID
LfWriteFile(hwnd, pthr)
HWND hwnd;
PTHR pthr;
{
HFILE hFile;
DLF dlf;
BITMAPINFOHEADER bmih;
SetUpDLF( &dlf
, DLG_SAVEDLG
, &hFile
, (PSZ)"\\*.BMP"
, (PSZ)"LineFrac"
, (PSZ)"Save Bitmap"
, NULL );
dlf.szFileName[0] = '\0';
dlf.szOpenFile[0] = '\0';
switch (DlgFile(hwnd,&dlf))
{
case TDF_ERRMEM:
case TDF_INVALID:
MyMessageBox(hwnd, "Error opening file.");
break;
case TDF_NOSAVE:
break;
default:
bmih.cbFix = sizeof(BITMAPINFOHEADER);
bmih.cx = (USHORT) pthr->rcl.xRight;
bmih.cy = (USHORT) pthr->rcl.yTop;
bmih.cPlanes = pthr->cPlanes;
bmih.cBitCount = pthr->cBitCount;
if (!LfWriteBMP(pthr->hps, &bmih, &dlf))
MyMessageBox(hwnd, "Error writing file.");
}
}
/************************************************************************
*
* LfWriteBMP
*
* Write the bitmap out to a BMP format file. Write the file
* header first, then the bitmap bits. Space for the header
* and the bits is allocated. Huge bitmaps are supported.
* Free up memory and close the file before leaving. The file
* will have been opened by the time this function is called,
* and the file handle will be in the *pdlf structure.
*
************************************************************************/
BOOL
LfWriteBMP(hPS, pbmih, pdlf)
HPS hPS; /* hPS from which to get bitmap bits. */
PBITMAPINFOHEADER pbmih;/* Bitmap information. */
PDLF pdlf; /* File information filled by DlgFile. */
{
HFILE hfile;
ULONG cScans;
ULONG ulSize; /* Number of bytes occupied by bitmap bits. */
USHORT cSegs; /* Number of 64K segments in ulSize. */
USHORT cbExtra; /* Bytes in last segment of ulSize. */
SEL selBits; /* Base selector to bitmap bits. */
USHORT hugeshift; /* Segment index shift value. */
USHORT cbBMHdr; /* Size of bitmap header. */
PBITMAPFILEHEADER pbfh; /* Pointer to private copy of bitmap info data. */
USHORT cbWrite1; /* Number of bytes to write first call to DosWrite */
USHORT cbWrite2; /* Number of bytes to write second call to DosWrite */
USHORT cbWritten; /* Number of bytes written by DosWrite. */
BOOL fRet = FALSE; /* Function return code. */
int i; /* Generic loop index. */
struct
{
LONG cPlanes;
LONG cBitCount;
} bmFmt;
hfile = *(pdlf->phFile);
/*******************************************************************
* If the bitmap was created with either 0 planes or 0 bits per
* pixel, then query the format to write with. By asking for just
* one format (two LONGs, or one instance of structure of bmFmt),
* we'll get the device's favored format.
*******************************************************************/
if ((pbmih->cPlanes == 0) || (pbmih->cBitCount))
{
if (!GpiQueryDeviceBitmapFormats(hPS, 2L, (PLONG)&bmFmt))
goto lfwrite_error_close_file;
}
else
{
bmFmt.cPlanes = pbmih->cPlanes;
bmFmt.cBitCount = pbmih->cBitCount;
}
/*******************************************************************
* Determine size of bitmap header. The header consists of a
* a fixed-size part and a variable-length color table. The
* latter has 2^cBitCount entries, each of which is sizeof(RGB)
* bytes long. The exception is when cBitCount is 24, in which
* case the color table is omitted because the pixels are direct
* rgb values.
*******************************************************************/
i = (int) bmFmt.cBitCount;
if (i == 24)
cbBMHdr = 0;
else
for (cbBMHdr = sizeof(RGB); i > 0; --i)
cbBMHdr *= 2;
cbBMHdr += sizeof(BITMAPFILEHEADER);
/*******************************************************************
* Copy structure from input to work buffer. The call to
* GpiQueryBitmapBits will have write-access to this, so we won't
* let it have the user's data.
*******************************************************************/
pbfh = 0;
if (DosAllocSeg(cbBMHdr, ((PUSHORT)&pbfh)+1, 0))
goto lfwrite_error_close_file;
pbfh->bmp = *pbmih;
if ((pbmih->cPlanes == 0) || (pbmih->cBitCount))
{
pbfh->bmp.cPlanes = (USHORT) bmFmt.cPlanes;
pbfh->bmp.cBitCount = (USHORT) bmFmt.cBitCount;
}
/*******************************************************************
* Allocate space for the bitmap bits -- all of them at once.
* The extra ULONG casts are there to force all the arithmetic
* to be done in 32 bits.
*******************************************************************/
ulSize = (
(
(
(ULONG)pbfh->bmp.cBitCount
* (ULONG)pbfh->bmp.cx
+ 31L
) / 32L
) * (ULONG)pbfh->bmp.cPlanes * 4L
) * (ULONG)pbfh->bmp.cy;
cSegs = (USHORT)(ulSize/0x10000L);
cbExtra = (USHORT)(ulSize%0x10000L);
if (DosAllocHuge(cSegs, cbExtra, (PSEL)&selBits, 0, 0))
goto lfwrite_error_free_header;
if (DosGetHugeShift(&hugeshift))
goto lfwrite_error_free_bits;
/*******************************************************************
* Tell GPI to give us the bits. The function returns the number
* of scan lines of the bitmap that were copied. We want all of
* them at once.
*******************************************************************/
cScans = GpiQueryBitmapBits( hPS
, 0L
, (ULONG)pbfh->bmp.cy
, (PBYTE)MAKEP(selBits, 0)
, (PBITMAPINFO)&pbfh->bmp);
if (cScans != pbfh->bmp.cy) /* compare with original number of scans */
goto lfwrite_error_free_bits;
/*******************************************************************
* Fill in the extra header fields and write the header out to
* the file.
*******************************************************************/
pbfh->usType = 0x4D42; /* 'MB' */
pbfh->cbSize = ulSize + cbBMHdr;
pbfh->xHotspot = pbfh->bmp.cx / 2; /* why bother ? */
pbfh->yHotspot = pbfh->bmp.cy / 2;
pbfh->offBits = cbBMHdr;
if (DosWrite( hfile
, (PVOID)pbfh
, cbBMHdr
, &cbWritten))
goto lfwrite_error_free_bits;
if (cbWritten != cbBMHdr)
goto lfwrite_error_free_bits;
/*******************************************************************
* Write the bits out to the file. The DosWrite function allows a
* maximum of 64K-1 bytes written at a time. We get around this
* by writing two 32K chunks for each 64K segment, and writing the
* last segment in one piece.
*******************************************************************/
for (i = 0; i <= cSegs; ++i)
{
if (i < cSegs)
{
/* This segment is 64K bytes long, so split it up. */
cbWrite1 = 0x8000;
cbWrite2 = 0x8000;
}
else
{
/* This segment is less than 64K bytes long, so write it all. */
cbWrite1 = cbExtra;
cbWrite2 = 0;
}
/* There's a possibility that cbExtra will be 0, so check
* to avoid an unnecessary system call.
*/
if (cbWrite1 > 0)
{
if (DosWrite( hfile
, (PVOID)MAKEP(selBits+(i<<hugeshift), 0)
, cbWrite1
, &cbWritten))
goto lfwrite_error_free_bits;
if (cbWrite1 != cbWritten)
goto lfwrite_error_free_bits;
}
/* This will always be skipped on the last partial segment. */
if (cbWrite2 > 0)
{
if (DosWrite( hfile
, (PVOID)MAKEP(selBits+(i<<hugeshift), cbWrite1)
, cbWrite2
, &cbWritten))
goto lfwrite_error_free_bits;
if (cbWrite2 != cbWritten)
goto lfwrite_error_free_bits;
}
}
fRet = TRUE; /* We made it! The bits are on the disk. */
/*******************************************************************
* Close the file, free the buffer space and leave. This is a
* common exit point from the function. Since the same cleanup
* operations need to be performed for such a large number of
* possible error conditions, this is concise way to do the right
* thing.
*******************************************************************/
lfwrite_error_free_bits:
DosFreeSeg(selBits);
lfwrite_error_free_header:
DosFreeSeg(*((PUSHORT)&pbfh+1));
lfwrite_error_close_file:
DosClose(hfile);
return fRet;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.