1 /******************************************************************************
2  *
3  * Purpose:  Implementation of the CPCIDSKBitmap class.
4  *
5  ******************************************************************************
6  * Copyright (c) 2010
7  * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, 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 #include "cpl_port.h"
39 
40 using namespace PCIDSK;
41 
42 /************************************************************************/
43 /*                           CPCIDSKBitmap()                            */
44 /************************************************************************/
45 
CPCIDSKBitmap(PCIDSKFile * file,int segment,const char * segment_pointer)46 CPCIDSKBitmap::CPCIDSKBitmap( PCIDSKFile *file, int segment,
47                               const char *segment_pointer )
48         : CPCIDSKSegment( file, segment, segment_pointer )
49 
50 {
51     loaded = false;
52 }
53 
54 /************************************************************************/
55 /*                           ~CPCIDSKBitmap()                           */
56 /************************************************************************/
57 
~CPCIDSKBitmap()58 CPCIDSKBitmap::~CPCIDSKBitmap()
59 
60 {
61 }
62 
63 /************************************************************************/
64 /*                             Initialize()                             */
65 /*                                                                      */
66 /*      Set up a newly created bitmap segment.  We just need to         */
67 /*      write some stuff into the segment header.                       */
68 /************************************************************************/
69 
Initialize()70 void CPCIDSKBitmap::Initialize()
71 
72 {
73     loaded = false;
74 
75     CPCIDSKBitmap *pThis = (CPCIDSKBitmap *) this;
76 
77     PCIDSKBuffer &bheader = pThis->GetHeader();
78 
79     bheader.Put( 0, 160     , 16 );
80     bheader.Put( 0, 160+16*1, 16 );
81     bheader.Put( file->GetWidth(), 160+16*2, 16 );
82     bheader.Put( file->GetHeight(), 160+16*3, 16 );
83     bheader.Put( -1, 160+16*4, 16 );
84 
85     file->WriteToFile( bheader.buffer, data_offset, 1024 );
86 }
87 
88 /************************************************************************/
89 /*                                Load()                                */
90 /************************************************************************/
91 
Load() const92 void CPCIDSKBitmap::Load() const
93 
94 {
95     if( loaded )
96         return;
97 
98     // We don't really mean the internals are const, just a lie to
99     // keep the const interfaces happy.
100 
101     CPCIDSKBitmap *pThis = (CPCIDSKBitmap *) this;
102 
103     PCIDSKBuffer &bheader = pThis->GetHeader();
104 
105     pThis->width  = bheader.GetInt( 192,    16 );
106     pThis->height = bheader.GetInt( 192+16, 16 );
107 
108     // Choosing 8 lines per block ensures that each block
109     // starts on a byte boundary.
110     pThis->block_width = pThis->width;
111     pThis->block_height = 8;
112 
113     pThis->loaded = true;
114 }
115 
116 /************************************************************************/
117 /*                           GetBlockWidth()                            */
118 /************************************************************************/
119 
GetBlockWidth() const120 int CPCIDSKBitmap::GetBlockWidth() const
121 
122 {
123     if( !loaded )
124         Load();
125 
126     return block_width;
127 }
128 
129 /************************************************************************/
130 /*                           GetBlockHeight()                           */
131 /************************************************************************/
132 
GetBlockHeight() const133 int CPCIDSKBitmap::GetBlockHeight() const
134 
135 {
136     if( !loaded )
137         Load();
138 
139     return block_height;
140 }
141 
142 /************************************************************************/
143 /*                           GetBlockCount()                            */
144 /************************************************************************/
145 
GetBlockCount() const146 int CPCIDSKBitmap::GetBlockCount() const
147 
148 {
149     if( !loaded )
150         Load();
151 
152     return ((width + block_width - 1) / block_width)
153         * ((height + block_height - 1) / block_height);
154 }
155 
156 /************************************************************************/
157 /*                              GetWidth()                              */
158 /************************************************************************/
159 
GetWidth() const160 int CPCIDSKBitmap::GetWidth() const
161 
162 {
163     if( !loaded )
164         Load();
165 
166     return width;
167 }
168 
169 /************************************************************************/
170 /*                             GetHeight()                              */
171 /************************************************************************/
172 
GetHeight() const173 int CPCIDSKBitmap::GetHeight() const
174 
175 {
176     if( !loaded )
177         Load();
178 
179     return height;
180 }
181 
182 /************************************************************************/
183 /*                              GetType()                               */
184 /************************************************************************/
185 
GetType() const186 eChanType CPCIDSKBitmap::GetType() const
187 
188 {
189     return CHN_BIT;
190 }
191 
192 /************************************************************************/
193 /*                          PCIDSK_CopyBits()                           */
194 /*                                                                      */
195 /*      Copy bit strings - adapted from GDAL.                           */
196 /************************************************************************/
197 
198 static void
PCIDSK_CopyBits(const uint8 * pabySrcData,int nSrcOffset,int nSrcStep,uint8 * pabyDstData,int nDstOffset,int nDstStep,int nBitCount,int nStepCount)199 PCIDSK_CopyBits( const uint8 *pabySrcData, int nSrcOffset, int nSrcStep,
200                  uint8 *pabyDstData, int nDstOffset, int nDstStep,
201                  int nBitCount, int nStepCount )
202 
203 {
204     int iStep;
205     int iBit;
206 
207     for( iStep = 0; iStep < nStepCount; iStep++ )
208     {
209         for( iBit = 0; iBit < nBitCount; iBit++ )
210         {
211             if( pabySrcData[nSrcOffset>>3]
212                 & (0x80 >>(nSrcOffset & 7)) )
213                 pabyDstData[nDstOffset>>3] |= (0x80 >> (nDstOffset & 7));
214             else
215                 pabyDstData[nDstOffset>>3] &= ~(0x80 >> (nDstOffset & 7));
216 
217 
218             nSrcOffset++;
219             nDstOffset++;
220         }
221 
222         nSrcOffset += (nSrcStep - nBitCount);
223         nDstOffset += (nDstStep - nBitCount);
224     }
225 }
226 
227 /************************************************************************/
228 /*                             ReadBlock()                              */
229 /************************************************************************/
230 
ReadBlock(int block_index,void * buffer,int win_xoff,int win_yoff,int win_xsize,int win_ysize)231 int CPCIDSKBitmap::ReadBlock( int block_index, void *buffer,
232                               int win_xoff, int win_yoff,
233                               int win_xsize, int win_ysize )
234 
235 {
236     uint64 block_size = (block_width * block_height + 7) / 8;
237     uint8 *wrk_buffer = (uint8 *) buffer;
238 
239     if( block_index < 0 || block_index >= GetBlockCount() )
240     {
241         ThrowPCIDSKException( "Requested non-existant block (%d)",
242                               block_index );
243     }
244 /* -------------------------------------------------------------------- */
245 /*      If we are doing subwindowing, we will need to create a          */
246 /*      temporary bitmap to load into.  If we are concerned about       */
247 /*      high performance access to small windows in big bitmaps we      */
248 /*      will eventually want to reimplement this to avoid reading       */
249 /*      the whole block to subwindow from.                              */
250 /* -------------------------------------------------------------------- */
251     if( win_ysize != -1 )
252     {
253         if( win_xoff < 0 || win_xoff + win_xsize > GetBlockWidth()
254             || win_yoff < 0 || win_yoff + win_ysize > GetBlockHeight() )
255         {
256             ThrowPCIDSKException(
257                 "Invalid window in CPCIDSKBitmap::ReadBlock(): xoff=%d,yoff=%d,xsize=%d,ysize=%d",
258                 win_xoff, win_yoff, win_xsize, win_ysize );
259         }
260 
261         wrk_buffer = (uint8 *) malloc((size_t) block_size);
262         if( wrk_buffer == NULL )
263             ThrowPCIDSKException( "Out of memory allocating %d bytes in CPCIDSKBitmap::ReadBlock()",
264                                   (int) block_size );
265     }
266 
267 /* -------------------------------------------------------------------- */
268 /*      Read the block, taking care in the case of partial blocks at    */
269 /*      the bottom of the image.                                        */
270 /* -------------------------------------------------------------------- */
271     if( (block_index+1) * block_height <= height )
272         ReadFromFile( wrk_buffer, block_size * block_index, block_size );
273     else
274     {
275         uint64 short_block_size;
276 
277         memset( buffer, 0, (size_t) block_size );
278 
279         short_block_size =
280             ((height - block_index*block_height) * block_width + 7) / 8;
281 
282         ReadFromFile( wrk_buffer, block_size * block_index, short_block_size );
283     }
284 
285 /* -------------------------------------------------------------------- */
286 /*      Perform subwindowing if needed.                                 */
287 /* -------------------------------------------------------------------- */
288     if( win_ysize != -1 )
289     {
290         int y_out;
291 
292         for( y_out = 0; y_out <  win_ysize; y_out++ )
293         {
294             PCIDSK_CopyBits( wrk_buffer,
295                              win_xoff + (y_out+win_yoff)*block_width, 0,
296                              (uint8*) buffer, y_out * win_xsize, 0,
297                              win_xsize, 1 );
298         }
299 
300         free( wrk_buffer );
301     }
302 
303     return 0;
304 }
305 
306 /************************************************************************/
307 /*                             WriteBlock()                             */
308 /************************************************************************/
309 
WriteBlock(int block_index,void * buffer)310 int CPCIDSKBitmap::WriteBlock( int block_index, void *buffer )
311 
312 {
313     uint64 block_size = (block_width * block_height) / 8;
314 
315     if( (block_index+1) * block_height <= height )
316         WriteToFile( buffer, block_size * block_index, block_size );
317     else
318     {
319         uint64 short_block_size;
320 
321         short_block_size =
322             ((height - block_index*block_height) * block_width + 7) / 8;
323 
324         WriteToFile( buffer, block_size * block_index, short_block_size );
325     }
326 
327     return 1;
328 }
329 
330 /************************************************************************/
331 /*                          GetOverviewCount()                          */
332 /************************************************************************/
333 
GetOverviewCount()334 int CPCIDSKBitmap::GetOverviewCount()
335 {
336     return 0;
337 }
338 
339 /************************************************************************/
340 /*                            GetOverview()                             */
341 /************************************************************************/
342 
GetOverview(CPL_UNUSED int i)343 PCIDSKChannel *CPCIDSKBitmap::GetOverview( CPL_UNUSED int i )
344 {
345     // The %d is ignored in the exception.
346     ThrowPCIDSKException("Non-existant overview %d requested on bitmap segment.");
347     return NULL;
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     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     ThrowPCIDSKException( "Attempt to SetEChanInfo() on a bitmap." );
530 }
531