1 /******************************************************************************
2  *
3  * Purpose:  Implementation of the CPCIDSKBitmap class.
4  *
5  ******************************************************************************
6  * Copyright (c) 2010
7  * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  ****************************************************************************/
27 
28 #include "pcidsk_exception.h"
29 #include "segment/cpcidskbitmap.h"
30 #include "pcidsk_file.h"
31 #include "core/pcidsk_utils.h"
32 #include <cassert>
33 #include <cstring>
34 #include <cstdlib>
35 #include <cstdio>
36 #include <cctype>
37 
38 using namespace PCIDSK;
39 
40 /************************************************************************/
41 /*                           CPCIDSKBitmap()                            */
42 /************************************************************************/
43 
CPCIDSKBitmap(PCIDSKFile * fileIn,int segmentIn,const char * segment_pointer)44 CPCIDSKBitmap::CPCIDSKBitmap( PCIDSKFile *fileIn, int segmentIn,
45                               const char *segment_pointer )
46         : CPCIDSKSegment( fileIn, segmentIn, segment_pointer )
47 
48 {
49     loaded = false;
50     width = 0;
51     height = 0;
52     block_width = 0;
53     block_height = 0;
54 }
55 
56 /************************************************************************/
57 /*                           ~CPCIDSKBitmap()                           */
58 /************************************************************************/
59 
~CPCIDSKBitmap()60 CPCIDSKBitmap::~CPCIDSKBitmap()
61 
62 {
63 }
64 
65 /************************************************************************/
66 /*                             Initialize()                             */
67 /*                                                                      */
68 /*      Set up a newly created bitmap segment.  We just need to         */
69 /*      write some stuff into the segment header.                       */
70 /************************************************************************/
71 
Initialize()72 void CPCIDSKBitmap::Initialize()
73 
74 {
75     loaded = false;
76 
77     CPCIDSKBitmap *pThis = (CPCIDSKBitmap *) this;
78 
79     PCIDSKBuffer &bheader = pThis->GetHeader();
80 
81     bheader.Put( 0, 160     , 16 );
82     bheader.Put( 0, 160+16*1, 16 );
83     bheader.Put( file->GetWidth(), 160+16*2, 16 );
84     bheader.Put( file->GetHeight(), 160+16*3, 16 );
85     bheader.Put( -1, 160+16*4, 16 );
86 
87     file->WriteToFile( bheader.buffer, data_offset, 1024 );
88 }
89 
90 /************************************************************************/
91 /*                                Load()                                */
92 /************************************************************************/
93 
Load() const94 void CPCIDSKBitmap::Load() const
95 
96 {
97     if( loaded )
98         return;
99 
100     // We don't really mean the internals are const, just a lie to
101     // keep the const interfaces happy.
102 
103     CPCIDSKBitmap *pThis = (CPCIDSKBitmap *) this;
104 
105     PCIDSKBuffer &bheader = pThis->GetHeader();
106 
107     pThis->width  = bheader.GetInt( 192,    16 );
108     pThis->height = bheader.GetInt( 192+16, 16 );
109 
110     // Choosing 8 lines per block ensures that each block
111     // starts on a byte boundary.
112     pThis->block_width = pThis->width;
113     pThis->block_height = 8;
114 
115     pThis->loaded = true;
116 }
117 
118 /************************************************************************/
119 /*                           GetBlockWidth()                            */
120 /************************************************************************/
121 
GetBlockWidth() const122 int CPCIDSKBitmap::GetBlockWidth() const
123 
124 {
125     if( !loaded )
126         Load();
127 
128     return block_width;
129 }
130 
131 /************************************************************************/
132 /*                           GetBlockHeight()                           */
133 /************************************************************************/
134 
GetBlockHeight() const135 int CPCIDSKBitmap::GetBlockHeight() const
136 
137 {
138     if( !loaded )
139         Load();
140 
141     return block_height;
142 }
143 
144 /************************************************************************/
145 /*                           GetBlockCount()                            */
146 /************************************************************************/
147 
GetBlockCount() const148 int CPCIDSKBitmap::GetBlockCount() const
149 
150 {
151     if( !loaded )
152         Load();
153 
154     return ((width + block_width - 1) / block_width)
155         * ((height + block_height - 1) / block_height);
156 }
157 
158 /************************************************************************/
159 /*                              GetWidth()                              */
160 /************************************************************************/
161 
GetWidth() const162 int CPCIDSKBitmap::GetWidth() const
163 
164 {
165     if( !loaded )
166         Load();
167 
168     return width;
169 }
170 
171 /************************************************************************/
172 /*                             GetHeight()                              */
173 /************************************************************************/
174 
GetHeight() const175 int CPCIDSKBitmap::GetHeight() const
176 
177 {
178     if( !loaded )
179         Load();
180 
181     return height;
182 }
183 
184 /************************************************************************/
185 /*                              GetType()                               */
186 /************************************************************************/
187 
GetType() const188 eChanType CPCIDSKBitmap::GetType() const
189 
190 {
191     return CHN_BIT;
192 }
193 
194 /************************************************************************/
195 /*                          PCIDSK_CopyBits()                           */
196 /*                                                                      */
197 /*      Copy bit strings - adapted from GDAL.                           */
198 /************************************************************************/
199 
200 static void
PCIDSK_CopyBits(const uint8 * pabySrcData,int nSrcOffset,int nSrcStep,uint8 * pabyDstData,int nDstOffset,int nDstStep,int nBitCount,int nStepCount)201 PCIDSK_CopyBits( const uint8 *pabySrcData, int nSrcOffset, int nSrcStep,
202                  uint8 *pabyDstData, int nDstOffset, int nDstStep,
203                  int nBitCount, int nStepCount )
204 
205 {
206     int iStep;
207     int iBit;
208 
209     for( iStep = 0; iStep < nStepCount; iStep++ )
210     {
211         for( iBit = 0; iBit < nBitCount; iBit++ )
212         {
213             if( pabySrcData[nSrcOffset>>3]
214                 & (0x80 >>(nSrcOffset & 7)) )
215                 pabyDstData[nDstOffset>>3] |= (0x80 >> (nDstOffset & 7));
216             else
217                 pabyDstData[nDstOffset>>3] &= ~(0x80 >> (nDstOffset & 7));
218 
219 
220             nSrcOffset++;
221             nDstOffset++;
222         }
223 
224         nSrcOffset += (nSrcStep - nBitCount);
225         nDstOffset += (nDstStep - nBitCount);
226     }
227 }
228 
229 /************************************************************************/
230 /*                             ReadBlock()                              */
231 /************************************************************************/
232 
ReadBlock(int block_index,void * buffer,int win_xoff,int win_yoff,int win_xsize,int win_ysize)233 int CPCIDSKBitmap::ReadBlock( int block_index, void *buffer,
234                               int win_xoff, int win_yoff,
235                               int win_xsize, int win_ysize )
236 
237 {
238     uint64 block_size = (static_cast<uint64>(block_width) * block_height + 7) / 8;
239     uint8 *wrk_buffer = (uint8 *) buffer;
240 
241     if( block_index < 0 || block_index >= GetBlockCount() )
242     {
243         return ThrowPCIDSKException(0, "Requested non-existent block (%d)",
244                               block_index );
245     }
246 /* -------------------------------------------------------------------- */
247 /*      If we are doing subwindowing, we will need to create a          */
248 /*      temporary bitmap to load into.  If we are concerned about       */
249 /*      high performance access to small windows in big bitmaps we      */
250 /*      will eventually want to reimplement this to avoid reading       */
251 /*      the whole block to subwindow from.                              */
252 /* -------------------------------------------------------------------- */
253     if( win_ysize != -1 )
254     {
255         if( win_xoff < 0 || win_xoff + win_xsize > GetBlockWidth()
256             || win_yoff < 0 || win_yoff + win_ysize > GetBlockHeight() )
257         {
258             return ThrowPCIDSKException( 0,
259                 "Invalid window in CPCIDSKBitmap::ReadBlock(): xoff=%d,yoff=%d,xsize=%d,ysize=%d",
260                 win_xoff, win_yoff, win_xsize, win_ysize );
261         }
262 
263         wrk_buffer = (uint8 *) malloc((size_t) block_size);
264         if( wrk_buffer == nullptr )
265             return ThrowPCIDSKException(0, "Out of memory allocating %d bytes in CPCIDSKBitmap::ReadBlock()",
266                                   (int) block_size );
267     }
268 
269 /* -------------------------------------------------------------------- */
270 /*      Read the block, taking care in the case of partial blocks at    */
271 /*      the bottom of the image.                                        */
272 /* -------------------------------------------------------------------- */
273     if( (block_index+1) * block_height <= height )
274         ReadFromFile( wrk_buffer, block_size * block_index, block_size );
275     else
276     {
277         uint64 short_block_size;
278 
279         memset( buffer, 0, (size_t) block_size );
280 
281         short_block_size =
282             (static_cast<uint64>(height - block_index*block_height) * block_width + 7) / 8;
283 
284         ReadFromFile( wrk_buffer, block_size * block_index, short_block_size );
285     }
286 
287 /* -------------------------------------------------------------------- */
288 /*      Perform subwindowing if needed.                                 */
289 /* -------------------------------------------------------------------- */
290     if( win_ysize != -1 )
291     {
292         int y_out;
293 
294         for( y_out = 0; y_out <  win_ysize; y_out++ )
295         {
296             PCIDSK_CopyBits( wrk_buffer,
297                              win_xoff + (y_out+win_yoff)*block_width, 0,
298                              (uint8*) buffer, y_out * win_xsize, 0,
299                              win_xsize, 1 );
300         }
301 
302         free( wrk_buffer );
303     }
304 
305     return 0;
306 }
307 
308 /************************************************************************/
309 /*                             WriteBlock()                             */
310 /************************************************************************/
311 
WriteBlock(int block_index,void * buffer)312 int CPCIDSKBitmap::WriteBlock( int block_index, void *buffer )
313 
314 {
315     uint64 block_size = (static_cast<uint64>(block_width) * block_height) / 8;
316 
317     if( (block_index+1) * block_height <= height )
318         WriteToFile( buffer, block_size * block_index, block_size );
319     else
320     {
321         uint64 short_block_size;
322 
323         short_block_size =
324             (static_cast<uint64>(height - block_index*block_height) * block_width + 7) / 8;
325 
326         WriteToFile( buffer, block_size * block_index, short_block_size );
327     }
328 
329     return 1;
330 }
331 
332 /************************************************************************/
333 /*                          GetOverviewCount()                          */
334 /************************************************************************/
335 
GetOverviewCount()336 int CPCIDSKBitmap::GetOverviewCount()
337 {
338     return 0;
339 }
340 
341 /************************************************************************/
342 /*                            GetOverview()                             */
343 /************************************************************************/
344 
GetOverview(int i)345 PCIDSKChannel *CPCIDSKBitmap::GetOverview( int i )
346 {
347     return (PCIDSKChannel*) ThrowPCIDSKExceptionPtr("Non-existent overview %d requested on bitmap segment.", i);
348 }
349 
350 /************************************************************************/
351 /*                          IsOverviewValid()                           */
352 /************************************************************************/
353 
IsOverviewValid(CPL_UNUSED int i)354 bool CPCIDSKBitmap::IsOverviewValid( CPL_UNUSED int i )
355 {
356     return false;
357 }
358 
359 /************************************************************************/
360 /*                       GetOverviewResampling()                        */
361 /************************************************************************/
362 
GetOverviewResampling(CPL_UNUSED int i)363 std::string CPCIDSKBitmap::GetOverviewResampling( CPL_UNUSED int i )
364 {
365     return "";
366 }
367 
368 /************************************************************************/
369 /*                        SetOverviewValidity()                         */
370 /************************************************************************/
371 
SetOverviewValidity(CPL_UNUSED int i,CPL_UNUSED bool validity)372 void CPCIDSKBitmap::SetOverviewValidity( CPL_UNUSED int i, CPL_UNUSED bool validity )
373 {
374 }
375 
376 /************************************************************************/
377 /*                          GetMetadataValue()                          */
378 /************************************************************************/
379 
GetMetadataValue(const std::string & key) const380 std::string CPCIDSKBitmap::GetMetadataValue( const std::string &key ) const
381 
382 {
383     return CPCIDSKSegment::GetMetadataValue( key );
384 }
385 
386 /************************************************************************/
387 /*                          SetMetadataValue()                          */
388 /************************************************************************/
389 
SetMetadataValue(const std::string & key,const std::string & value)390 void CPCIDSKBitmap::SetMetadataValue( const std::string &key,
391                                       const std::string &value )
392 
393 {
394     CPCIDSKSegment::SetMetadataValue( key, value );
395 }
396 
397 /************************************************************************/
398 /*                   GetOverviewLevelMapping()                          */
399 /************************************************************************/
GetOverviewLevelMapping() const400 std::vector<int> CPCIDSKBitmap::GetOverviewLevelMapping() const
401 {
402     std::vector<int> ov;
403 
404     return ov;
405 }
406 
407 /************************************************************************/
408 /*                          GetMetadataKeys()                           */
409 /************************************************************************/
410 
GetMetadataKeys() const411 std::vector<std::string> CPCIDSKBitmap::GetMetadataKeys() const
412 
413 {
414     return CPCIDSKSegment::GetMetadataKeys();
415 }
416 
417 /************************************************************************/
418 /*                            Synchronize()                             */
419 /************************************************************************/
420 
Synchronize()421 void CPCIDSKBitmap::Synchronize()
422 
423 {
424     // TODO
425 
426     CPCIDSKSegment::Synchronize();
427 }
428 
429 /************************************************************************/
430 /*                           GetDescription()                           */
431 /************************************************************************/
432 
GetDescription()433 std::string CPCIDSKBitmap::GetDescription()
434 
435 {
436     return CPCIDSKSegment::GetDescription();
437 }
438 
439 /************************************************************************/
440 /*                           SetDescription()                           */
441 /************************************************************************/
442 
SetDescription(const std::string & description)443 void CPCIDSKBitmap::SetDescription( const std::string &description )
444 
445 {
446     CPCIDSKSegment::SetDescription( description );
447 }
448 
449 /************************************************************************/
450 /*                         GetHistoryEntries()                          */
451 /************************************************************************/
452 
GetHistoryEntries() const453 std::vector<std::string> CPCIDSKBitmap::GetHistoryEntries() const
454 
455 {
456     return CPCIDSKSegment::GetHistoryEntries();
457 }
458 
459 /************************************************************************/
460 /*                         SetHistoryEntries()                          */
461 /************************************************************************/
462 
SetHistoryEntries(const std::vector<std::string> & entries)463 void CPCIDSKBitmap::SetHistoryEntries( const std::vector<std::string> &entries )
464 
465 {
466     CPCIDSKSegment::SetHistoryEntries( entries );
467 }
468 
469 /************************************************************************/
470 /*                            PushHistory()                             */
471 /************************************************************************/
472 
PushHistory(const std::string & app,const std::string & message)473 void CPCIDSKBitmap::PushHistory( const std::string &app,
474                                  const std::string &message )
475 
476 {
477     CPCIDSKSegment::PushHistory( app, message );
478 }
479 
480 /************************************************************************/
481 /*                            GetChanInfo()                             */
482 /************************************************************************/
GetChanInfo(std::string & filename,uint64 & image_offset,uint64 & pixel_offset,uint64 & line_offset,bool & little_endian) const483 void CPCIDSKBitmap::GetChanInfo( std::string &filename, uint64 &image_offset,
484                                  uint64 &pixel_offset, uint64 &line_offset,
485                                  bool &little_endian ) const
486 
487 {
488     image_offset = 0;
489     pixel_offset = 0;
490     line_offset = 0;
491     little_endian = true;
492     filename = "";
493 }
494 
495 /************************************************************************/
496 /*                            SetChanInfo()                             */
497 /************************************************************************/
498 
SetChanInfo(CPL_UNUSED std::string filename,CPL_UNUSED uint64 image_offset,CPL_UNUSED uint64 pixel_offset,CPL_UNUSED uint64 line_offset,CPL_UNUSED bool little_endian)499 void CPCIDSKBitmap::SetChanInfo( CPL_UNUSED std::string filename, CPL_UNUSED uint64 image_offset,
500                                  CPL_UNUSED uint64 pixel_offset, CPL_UNUSED uint64 line_offset,
501                                  CPL_UNUSED bool little_endian )
502 {
503     return ThrowPCIDSKException( "Attempt to SetChanInfo() on a bitmap." );
504 }
505 
506 /************************************************************************/
507 /*                            GetEChanInfo()                            */
508 /************************************************************************/
GetEChanInfo(std::string & filename,int & echannel,int & exoff,int & eyoff,int & exsize,int & eysize) const509 void CPCIDSKBitmap::GetEChanInfo( std::string &filename, int &echannel,
510                                   int &exoff, int &eyoff,
511                                   int &exsize, int &eysize ) const
512 {
513     echannel = 0;
514     exoff = 0;
515     eyoff = 0;
516     exsize = 0;
517     eysize = 0;
518     filename = "";
519 }
520 
521 /************************************************************************/
522 /*                            SetEChanInfo()                            */
523 /************************************************************************/
524 
SetEChanInfo(CPL_UNUSED std::string filename,CPL_UNUSED int echannel,CPL_UNUSED int exoff,CPL_UNUSED int eyoff,CPL_UNUSED int exsize,CPL_UNUSED int eysize)525 void CPCIDSKBitmap::SetEChanInfo( CPL_UNUSED std::string filename, CPL_UNUSED int echannel,
526                                   CPL_UNUSED int exoff, CPL_UNUSED int eyoff,
527                                   CPL_UNUSED int exsize, CPL_UNUSED int eysize )
528 {
529     return ThrowPCIDSKException( "Attempt to SetEChanInfo() on a bitmap." );
530 }
531