| 1 | Unit HelpBitmap;
 | 
|---|
| 2 | 
 | 
|---|
| 3 | // NewView - a new OS/2 Help Viewer
 | 
|---|
| 4 | // Copyright 2003 Aaron Lawrence (aaronl at consultant dot com)
 | 
|---|
| 5 | // This software is released under the Gnu Public License - see readme.txt
 | 
|---|
| 6 | 
 | 
|---|
| 7 | Interface
 | 
|---|
| 8 | 
 | 
|---|
| 9 | // Encapsulates a bitmap as stored in a IPF file.
 | 
|---|
| 10 | // Once created from file data they can be used as a normal bitmap.
 | 
|---|
| 11 | 
 | 
|---|
| 12 | uses
 | 
|---|
| 13 |   BseDos,
 | 
|---|
| 14 |   OS2Def,
 | 
|---|
| 15 |   PMBitmap,
 | 
|---|
| 16 |   Classes,
 | 
|---|
| 17 |   Graphics,
 | 
|---|
| 18 |   SysUtils,
 | 
|---|
| 19 |   IPFFileFormatUnit;
 | 
|---|
| 20 | 
 | 
|---|
| 21 | type
 | 
|---|
| 22 |   EHelpBitmapException = class( Exception )
 | 
|---|
| 23 |   end;
 | 
|---|
| 24 | 
 | 
|---|
| 25 |   // Lead part of BITMAPARRAYFILEHEADER
 | 
|---|
| 26 |   INFBITMAPARRAYHEADER = record
 | 
|---|
| 27 |     usType: USHORT;    // 'BA', 16706
 | 
|---|
| 28 |     cbSize: ULONG;     // Size of the BITMAPARRAYFILEHEADER structure in bytes.
 | 
|---|
| 29 |     offNext: ULONG;    // Offset of the next BITMAPARRAYFILEHEADER structure from the start of the file.
 | 
|---|
| 30 |     cxDisplay: USHORT; // Device width, in pels.
 | 
|---|
| 31 |     cyDisplay: USHORT; // Device height, in pels.
 | 
|---|
| 32 |   end;
 | 
|---|
| 33 | 
 | 
|---|
| 34 |   INFBITMAPHEADER = record
 | 
|---|
| 35 |     // BITMAP FILE HEADER
 | 
|---|
| 36 |     usType: USHORT; // = 'bM'
 | 
|---|
| 37 |     cbSize: ULONG;
 | 
|---|
| 38 |     xHotspot: USHORT;
 | 
|---|
| 39 |     yHotspot: USHORT;
 | 
|---|
| 40 |     offBits: ULONG; // =size(hdr)+size(palette)
 | 
|---|
| 41 |     // BITMAP INFO HEADER
 | 
|---|
| 42 |     cbFIx: ULONG; // =size(info_hdr) (usually = 12?)
 | 
|---|
| 43 |     cx: USHORT; // x size
 | 
|---|
| 44 |     cy: USHORT; // y size
 | 
|---|
| 45 |     cPlanes: USHORT; // planes, =1
 | 
|---|
| 46 |     cBitCount: USHORT;
 | 
|---|
| 47 |   end;
 | 
|---|
| 48 | 
 | 
|---|
| 49 |   THelpBitmap = class( TBitmap )
 | 
|---|
| 50 |   protected
 | 
|---|
| 51 |     _Header: INFBITMAPHEADER;
 | 
|---|
| 52 |     _PaletteColorCount: longint;
 | 
|---|
| 53 |     _pPalette: ^RGB;
 | 
|---|
| 54 |     _BitsSize: longint;
 | 
|---|
| 55 |     _FileHandle: HFile;
 | 
|---|
| 56 | 
 | 
|---|
| 57 |     _UncompressedBlockSize: longint;
 | 
|---|
| 58 | 
 | 
|---|
| 59 |     function GetPaletteSize: longint;
 | 
|---|
| 60 |     procedure ReadBitmapData( Blocks: TList;
 | 
|---|
| 61 |                               TotalSize: longint );
 | 
|---|
| 62 |   public
 | 
|---|
| 63 |     constructor CreateFromHelpFile( FileHandle: HFile;
 | 
|---|
| 64 |                                     Offset: longint );
 | 
|---|
| 65 |     destructor Destroy; override;
 | 
|---|
| 66 |   end;
 | 
|---|
| 67 | 
 | 
|---|
| 68 | var
 | 
|---|
| 69 |   LZWDecompressBlock: function( pInput: PBYTE;
 | 
|---|
| 70 |                                pOutput: PBYTE;
 | 
|---|
| 71 |                                bytesIn: ULONG;
 | 
|---|
| 72 |                                Var bytesOut: ULONG;
 | 
|---|
| 73 |                                Var FinalCode: byte ): BOOL;
 | 
|---|
| 74 |   APIENTRY;
 | 
|---|
| 75 | //  'newview' index 1;
 | 
|---|
| 76 | 
 | 
|---|
| 77 | Implementation
 | 
|---|
| 78 | 
 | 
|---|
| 79 | uses
 | 
|---|
| 80 |   ACLFileIOUtility, ACLUtility,
 | 
|---|
| 81 |   ACLDialogs;
 | 
|---|
| 82 | 
 | 
|---|
| 83 | const
 | 
|---|
| 84 |   BFT_bMAP =$4d62; // 'bM'
 | 
|---|
| 85 |   BFT_BITMAP_ARRAY = $4142; // 'BA'
 | 
|---|
| 86 | 
 | 
|---|
| 87 | type
 | 
|---|
| 88 |   INFBITMAPDATAHEADER = record
 | 
|---|
| 89 |     ulTotalSize: ULONG;
 | 
|---|
| 90 |     usUncompressedBlockSize: USHORT; // bytes per block, after decompression
 | 
|---|
| 91 |   end;
 | 
|---|
| 92 | 
 | 
|---|
| 93 |   TBitmapBlock = class
 | 
|---|
| 94 |     _Data: PBYTE;
 | 
|---|
| 95 |     _Size: USHORT;
 | 
|---|
| 96 |     _CompressionType: uint8;
 | 
|---|
| 97 |     destructor Destroy; override;
 | 
|---|
| 98 |   end;
 | 
|---|
| 99 | 
 | 
|---|
| 100 | destructor TBitmapBlock.Destroy;
 | 
|---|
| 101 | begin
 | 
|---|
| 102 |   DeallocateMemory( _Data );
 | 
|---|
| 103 |   inherited Destroy;
 | 
|---|
| 104 | end;
 | 
|---|
| 105 | 
 | 
|---|
| 106 | constructor THelpBitmap.CreateFromHelpFile( FileHandle: HFILE;
 | 
|---|
| 107 |                                             Offset: longint );
 | 
|---|
| 108 | var
 | 
|---|
| 109 |   WordsPerLine: longint;
 | 
|---|
| 110 |   LineSize: longint;
 | 
|---|
| 111 |   DataHeader: INFBITMAPDATAHEADER;
 | 
|---|
| 112 |   BytesRead: longint;
 | 
|---|
| 113 | 
 | 
|---|
| 114 |   Block: TBitmapBlock;
 | 
|---|
| 115 |   Blocks: TList;
 | 
|---|
| 116 |   BlockIndex: longint;
 | 
|---|
| 117 |   ImageType: USHORT;
 | 
|---|
| 118 |   BitmapArrayHeader: INFBITMAPARRAYHEADER;
 | 
|---|
| 119 | begin
 | 
|---|
| 120 |   MySeek( FileHandle, Offset );
 | 
|---|
| 121 |   MyRead( FileHandle, Addr( ImageType ), sizeof( ImageType ) );
 | 
|---|
| 122 | 
 | 
|---|
| 123 |   if ImageType = BFT_BITMAP_ARRAY then
 | 
|---|
| 124 |   begin
 | 
|---|
| 125 |     // skip array header and read first bitmap only
 | 
|---|
| 126 |     MySkip( FileHandle, Sizeof( BitmapArrayHeader ) - sizeof( ImageType ) );
 | 
|---|
| 127 |   end
 | 
|---|
| 128 |   else
 | 
|---|
| 129 |   begin
 | 
|---|
| 130 |     // skip back over imagetype bytes to read header.
 | 
|---|
| 131 |     MySkip( FileHandle, - sizeof( ImageType ) );
 | 
|---|
| 132 |   end;
 | 
|---|
| 133 | 
 | 
|---|
| 134 |   // Read bitmap header
 | 
|---|
| 135 |   MyRead( FileHandle, Addr( _Header ), sizeof( _Header ) );
 | 
|---|
| 136 | 
 | 
|---|
| 137 |   // Check it's got a valid type
 | 
|---|
| 138 |   if _Header.usType <> BFT_bMAP then
 | 
|---|
| 139 |     raise EHelpBitmapException.Create( 'Invalid bitmap header' );
 | 
|---|
| 140 | 
 | 
|---|
| 141 |   _Header.usType := $4d42; // sibyl only accepts 'BM' not 'bM'
 | 
|---|
| 142 | 
 | 
|---|
| 143 |   // We can only parse bitmaps with 1 colour plane
 | 
|---|
| 144 |   // (I can't be bothered and have never seen bitmaps
 | 
|---|
| 145 |   //  with more than 1 color plane)
 | 
|---|
| 146 |   if _Header.cPlanes <> 1 then
 | 
|---|
| 147 |     exit;
 | 
|---|
| 148 | 
 | 
|---|
| 149 |   _PaletteColorCount := 0;
 | 
|---|
| 150 |   if _Header.cBitCount < 24 then
 | 
|---|
| 151 |     _PaletteColorCount := 1 shl _Header.cBitCount;
 | 
|---|
| 152 | 
 | 
|---|
| 153 |   // OS/2 always rounds bitmap rows up to a word:
 | 
|---|
| 154 |   WordsPerLine := ( _Header.cBitCount * _Header.cx + 31 ) div 32;
 | 
|---|
| 155 |   LineSize := WordsPerLine * 4;
 | 
|---|
| 156 | 
 | 
|---|
| 157 |   // Total size of the bitmap pixel data
 | 
|---|
| 158 |   _BitsSize := LineSize * _Header.cy;
 | 
|---|
| 159 | 
 | 
|---|
| 160 |   // Correct header offset - it is wrong in the header (why?)
 | 
|---|
| 161 |   _Header.OffBits := sizeof( _Header ) + GetPaletteSize;
 | 
|---|
| 162 | 
 | 
|---|
| 163 |   // Load palette
 | 
|---|
| 164 |   _pPalette := AllocateMemory( GetPaletteSize );
 | 
|---|
| 165 |   MyRead( FileHandle, _pPalette, GetPaletteSize );
 | 
|---|
| 166 | 
 | 
|---|
| 167 |   // Read data header
 | 
|---|
| 168 |   MyRead( FileHandle, Addr( DataHeader ), sizeof( DataHeader ) );
 | 
|---|
| 169 |   _UncompressedBlockSize := DataHeader.usUncompressedBlockSize;
 | 
|---|
| 170 | 
 | 
|---|
| 171 |   // For counting total size, we have already read some bytes:
 | 
|---|
| 172 |   // the uncompressedblocksize field
 | 
|---|
| 173 |   BytesRead := sizeof( DataHeader.usUncompressedBlockSize );
 | 
|---|
| 174 |   Blocks := TList.Create;
 | 
|---|
| 175 | 
 | 
|---|
| 176 |   while BytesRead < DataHeader.ulTotalSize do
 | 
|---|
| 177 |   begin
 | 
|---|
| 178 |     Block := TBitmapBlock.Create;
 | 
|---|
| 179 | 
 | 
|---|
| 180 |     // Read the block size
 | 
|---|
| 181 |     MyRead( FileHandle, Addr( Block._Size ), sizeof( Block._Size ) );
 | 
|---|
| 182 |     inc( BytesRead, sizeof( Block._Size ) );
 | 
|---|
| 183 | 
 | 
|---|
| 184 |     // Read the compression type
 | 
|---|
| 185 |     MyRead( FileHandle, Addr( Block._CompressionType ), sizeof( Block._CompressionType ) );
 | 
|---|
| 186 |     inc( BytesRead, sizeof( Block._CompressionType ) );
 | 
|---|
| 187 | 
 | 
|---|
| 188 |     // since size in the file includes this compression type field, subtract it
 | 
|---|
| 189 |     dec( Block._Size, sizeof( Block._CompressionType ) );
 | 
|---|
| 190 | 
 | 
|---|
| 191 |     // Now read the block
 | 
|---|
| 192 |     Block._Data := AllocateMemory( Block._Size );
 | 
|---|
| 193 |     MyRead( FileHandle, Block._Data, Block._Size );
 | 
|---|
| 194 | 
 | 
|---|
| 195 |     inc( BytesRead, Block._Size  );
 | 
|---|
| 196 | 
 | 
|---|
| 197 |     Blocks.Add( Block );
 | 
|---|
| 198 | 
 | 
|---|
| 199 |   end;
 | 
|---|
| 200 |   ReadBitmapData( Blocks, sizeof( _Header ) + GetPaletteSize + _BitsSize );
 | 
|---|
| 201 | 
 | 
|---|
| 202 |   for BlockIndex := 0 to Blocks.Count - 1 do
 | 
|---|
| 203 |   begin
 | 
|---|
| 204 |     Block := Blocks[ BlockIndex ];
 | 
|---|
| 205 |     Block.Destroy;
 | 
|---|
| 206 |   end;
 | 
|---|
| 207 | 
 | 
|---|
| 208 |   Blocks.Destroy;
 | 
|---|
| 209 | end;
 | 
|---|
| 210 | 
 | 
|---|
| 211 | function THelpBitmap.GetPaletteSize: longint;
 | 
|---|
| 212 | begin
 | 
|---|
| 213 |   Result := sizeof( RGB ) * _PaletteColorCount;
 | 
|---|
| 214 | end;
 | 
|---|
| 215 | 
 | 
|---|
| 216 | destructor THelpBitmap.Destroy;
 | 
|---|
| 217 | begin
 | 
|---|
| 218 |   DeallocateMemory( _pPalette );
 | 
|---|
| 219 |   inherited Destroy;
 | 
|---|
| 220 | end;
 | 
|---|
| 221 | 
 | 
|---|
| 222 | procedure THelpBitmap.ReadBitmapData( Blocks: TList;
 | 
|---|
| 223 |                                       TotalSize: longint );
 | 
|---|
| 224 | var
 | 
|---|
| 225 |   BytesWritten: longint;
 | 
|---|
| 226 |   BytesWrittenFromBlock: longword;
 | 
|---|
| 227 |   BytesRemainingInBlock: longword;
 | 
|---|
| 228 |   BytesRemainingInBitmap: longword;
 | 
|---|
| 229 |   FillerBytesRequired: longint;
 | 
|---|
| 230 |   lastOutByte: byte;
 | 
|---|
| 231 |   BitmapOutputPointer: PByte;
 | 
|---|
| 232 |   Block: TBitmapBlock;
 | 
|---|
| 233 |   BlockIndex: longint;
 | 
|---|
| 234 |   BitmapData: PBYTE;
 | 
|---|
| 235 | begin
 | 
|---|
| 236 |   // Allocate memory to store the bitmap
 | 
|---|
| 237 |   GetMem( BitmapData, TotalSize );
 | 
|---|
| 238 | 
 | 
|---|
| 239 |   // Copy header to bitmap
 | 
|---|
| 240 |   MemCopy( Addr( _Header ),
 | 
|---|
| 241 |            BitmapData,
 | 
|---|
| 242 |            sizeof( _Header ) );
 | 
|---|
| 243 | 
 | 
|---|
| 244 |   // Copy palette into bitmap
 | 
|---|
| 245 |   MemCopy( _pPalette,
 | 
|---|
| 246 |            BitmapData + sizeof( _Header ),
 | 
|---|
| 247 |            GetPaletteSize );
 | 
|---|
| 248 | 
 | 
|---|
| 249 |   BytesWritten := 0;
 | 
|---|
| 250 | 
 | 
|---|
| 251 |   // Point to start writing to bitmap bits.
 | 
|---|
| 252 |   BitmapOutputPointer := BitmapData + sizeof( _Header ) + GetPaletteSize;
 | 
|---|
| 253 | 
 | 
|---|
| 254 |   for BlockIndex := 0 to Blocks.Count - 1 do
 | 
|---|
| 255 |   begin
 | 
|---|
| 256 |     Block := Blocks[ BlockIndex ];
 | 
|---|
| 257 | 
 | 
|---|
| 258 |     case Block._CompressionType of
 | 
|---|
| 259 |       0,1: // uncompressed (I'm not sure about 1)
 | 
|---|
| 260 |       begin
 | 
|---|
| 261 |         MemCopy( Block._Data,
 | 
|---|
| 262 |                  BitmapOutputPointer,
 | 
|---|
| 263 |                  Block._Size );
 | 
|---|
| 264 |         BytesWrittenFromBlock := Block._Size;
 | 
|---|
| 265 |         inc( BytesWritten, BytesWrittenFromBlock );
 | 
|---|
| 266 |       end;
 | 
|---|
| 267 | 
 | 
|---|
| 268 |       2: // LZW compression
 | 
|---|
| 269 |       begin
 | 
|---|
| 270 |         // decompress block
 | 
|---|
| 271 |         if not Assigned( LZWDecompressBlock )then
 | 
|---|
| 272 |           raise EHelpBitmapException.Create( 'Cannot decode bitmap - DLL not found' );
 | 
|---|
| 273 | 
 | 
|---|
| 274 |         LZWDecompressBlock( Block._Data,
 | 
|---|
| 275 |                             BitmapOutputPointer,
 | 
|---|
| 276 |                             Block._Size,
 | 
|---|
| 277 |                             BytesWrittenFromBlock,
 | 
|---|
| 278 |                             lastOutByte );
 | 
|---|
| 279 | 
 | 
|---|
| 280 |         inc( BytesWritten, BytesWrittenFromBlock );
 | 
|---|
| 281 | 
 | 
|---|
| 282 |         // If the uncompressed data stopped short then
 | 
|---|
| 283 |         // copy the final code (byte) into remaining bytes
 | 
|---|
| 284 |         if ( BytesWrittenFromBlock < _UncompressedBlockSize )
 | 
|---|
| 285 |           and ( BytesWritten < _BitsSize ) then
 | 
|---|
| 286 |         begin
 | 
|---|
| 287 |           BytesRemainingInBlock := _UncompressedBlockSize - BytesWrittenFromBlock;
 | 
|---|
| 288 |           BytesRemainingInBitmap := _BitsSize - BytesWritten;
 | 
|---|
| 289 | 
 | 
|---|
| 290 |           FillerBytesRequired := Min( BytesRemainingInBlock,
 | 
|---|
| 291 |                                       BytesRemainingInBitmap );
 | 
|---|
| 292 | 
 | 
|---|
| 293 |           FillMem( BitmapOutputPointer + BytesWrittenFromBlock,
 | 
|---|
| 294 |                    FillerBytesRequired,
 | 
|---|
| 295 |                    LastOutByte );
 | 
|---|
| 296 |           inc( BytesWritten, FillerBytesRequired );
 | 
|---|
| 297 |           inc( BytesWrittenFromBlock, FillerBytesRequired );
 | 
|---|
| 298 |         end;
 | 
|---|
| 299 |       end;
 | 
|---|
| 300 |       else
 | 
|---|
| 301 |         raise EHelpBitmapException.Create( 'Unrecognised bitmap block type' );
 | 
|---|
| 302 |     end; // case
 | 
|---|
| 303 | 
 | 
|---|
| 304 |     assert( BytesWrittenFromBlock <= _UncompressedBlockSize );
 | 
|---|
| 305 |     assert( BytesWritten <= _BitsSize );
 | 
|---|
| 306 | 
 | 
|---|
| 307 |     if ( BitmapOutputPointer + BytesWrittenFromBlock
 | 
|---|
| 308 |          > BitmapData + TotalSize ) then
 | 
|---|
| 309 |       assert( false );
 | 
|---|
| 310 | 
 | 
|---|
| 311 |     inc( BitmapOutputPointer, BytesWrittenFromBlock );
 | 
|---|
| 312 |   end;
 | 
|---|
| 313 | 
 | 
|---|
| 314 |   LoadFromMem( BitmapData^, TotalSize );
 | 
|---|
| 315 |   FreeMem( BitmapData, TotalSize );
 | 
|---|
| 316 | end;
 | 
|---|
| 317 | 
 | 
|---|
| 318 | Initialization
 | 
|---|
| 319 | End.
 | 
|---|