ultimatepp/bazaar/plugin/gdal/frmts/nitf/nitfaridpcm.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

521 lines
18 KiB
C++

/******************************************************************************
* $Id: nitfaridpcm.cpp 27745 2014-09-27 16:38:57Z goatbar $
*
* Project: NITF Read/Write Library
* Purpose: ARIDPCM reading code.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
**********************************************************************
* Copyright (c) 2007, Frank Warmerdam
* Copyright (c) 2009, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "gdal.h"
#include "nitflib.h"
#include "cpl_conv.h"
CPL_CVSID("$Id: nitfaridpcm.cpp 27745 2014-09-27 16:38:57Z goatbar $");
static const int neighbourhood_size_75[4] = { 23, 47, 74, 173 };
static const int bits_per_level_by_busycode_75[4/*busy code*/][4/*level*/] = {
{ 8, 5, 0, 0 }, // BC = 00
{ 8, 5, 2, 0 }, // BC = 01
{ 8, 6, 4, 0 }, // BC = 10
{ 8, 7, 4, 2 }};// BC = 11
#define CR075 1
// Level for each index value.
static const int level_index_table[64] =
{ 0,
1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 };
// List of i,j to linear index macros mappings.
// Note that i is vertical and j is horizontal and progression is
// right to left, bottom to top.
#define IND(i,j) (ij_index[i+j*8]-1)
static const int ij_index[64] = {
1, // 0, 0
18, // 1, 0
6, // 2, 0
30, // 3, 0
3, // 4, 0
42, // 5, 0
12, // 6, 0
54, // 7, 0
17, // 0, 1
19, // 1, 1
29, // 2, 1
31, // 3, 1
41, // 4, 1
43, // 5, 1
53, // 6, 1
55, // 7, 1
5, // 0, 2
21, // 1, 2
7, // 2, 2
33, // 3, 2
11, // 4, 2
45, // 5, 2
13, // 6, 2
57, // 7, 2
20, // 0, 3
22, // 1, 3
32, // 2, 3
34, // 3, 3
44, // 4, 3
46, // 5, 3
56, // 6, 3
58, // 7, 3
2, // 0, 4
24, // 1, 4
9, // 2, 4
36, // 3, 4
4, // 4, 4
48, // 5, 4
15, // 6, 4
60, // 7, 4
23, // 0, 5
25, // 1, 5
35, // 2, 5
37, // 3, 5
47, // 4, 5
49, // 5, 5
59, // 6, 5
61, // 7, 5
8, // 0, 6
27, // 1, 6
10, // 2, 6
39, // 3, 6
14, // 4, 6
51, // 5, 6
16, // 6, 6
63, // 7, 6
26, // 0, 7
28, // 1, 7
38, // 2, 7
40, // 3, 7
50, // 4, 7
52, // 5, 7
62, // 6, 7
64};// 7, 7
static const int delta_075_level_2_bc_0[32] =
{-71, -49, -38, -32, -27, -23, -20, -17, -14, -12, -10, -8, -6, -4, -3, -1,
1, 2, 4, 6, 8, 12, 14, 16, 19, 22, 26, 31, 37, 46, 72 };
static const int delta_075_level_2_bc_1[32] =
{-71, -49, -38, -32, -27, -23, -20, -17, -14, -12, -10, -8, -6, -4, -3, -1,
1, 2, 4, 6, 8, 12, 14, 16, 19, 22, 26, 31, 37, 46, 72 };
static const int delta_075_level_2_bc_2[64] =
{ -109, -82, -68, -59, -52, -46, -41, -37, -33, -30, -27, -25, -22, -20,
-18, -16, -15, -13, -11, -10, -9, -8, -7, -6, -5,
-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,
13,14,15,16,17,18,19,20,21,24,26,28,31,35,38,
42,47,52,60,69,85,118};
static const int delta_075_level_2_bc_3[128] =
{-159,-134,-122,-113,-106,-100,-94,-88,-83,-79,-76,-72,-69,-66,-63,-61,
-58,-56,-54,-52,-50,-48,-47,-45,-43,-42,-40,-39,-37,-36,-35,-33,-32,-31,
-30,-29,-28,-27,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,
-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,
12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,
35,36,37,38,39,40,41,42,43,45,48,52,56,60,64,68,73,79,85,92,100,109,
118,130,144,159,177,196,217,236};
static const int *delta_075_level_2[4] =
{ delta_075_level_2_bc_0, delta_075_level_2_bc_1,
delta_075_level_2_bc_2, delta_075_level_2_bc_3 };
static const int delta_075_level_3_bc_1[4] = { -24, -6, 6, 24 };
static const int delta_075_level_3_bc_2[16] =
{-68,-37,-23,-15, -9, -6, -3, -1, 1, 4, 7,10,16,24,37,70 };
static const int delta_075_level_3_bc_3[16] =
{-117,-72, -50, -36, -25, -17, -10, -5,-1, 3, 7,14,25,45,82,166};
static const int *delta_075_level_3[4] =
{ NULL, delta_075_level_3_bc_1,
delta_075_level_3_bc_2, delta_075_level_3_bc_3 };
static const int delta_075_level_4_bc_3[4] = {-47,-8,4,43};
static const int *delta_075_level_4[4] = { NULL, NULL, NULL, delta_075_level_4_bc_3 };
static const int **delta_075_by_level_by_bc[4] =
{ NULL, delta_075_level_2, delta_075_level_3, delta_075_level_4 };
/************************************************************************/
/* get_bits() */
/************************************************************************/
static int
get_bits( unsigned char *buffer, int first_bit, int num_bits )
{
int i;
int total =0;
for( i = first_bit; i < first_bit+num_bits; i++ )
{
total = total * 2;
if( buffer[i>>3] & (0x80 >> (i&7)) )
total++;
}
return total;
}
/************************************************************************/
/* get_delta() */
/* */
/* Compute the delta value for a particular (i,j) location. */
/************************************************************************/
static int
get_delta( unsigned char *srcdata,
int nInputBytes,
int busy_code,
CPL_UNUSED int comrat,
int block_offset,
CPL_UNUSED int block_size,
int i,
int j,
int *pbError )
{
CPLAssert( comrat == CR075 );
int pixel_index = IND(i,j);
int level_index = level_index_table[pixel_index];
const int *bits_per_level = bits_per_level_by_busycode_75[busy_code];
int delta_bits = bits_per_level[level_index];
int delta_offset = 0;
*pbError = FALSE;
if( delta_bits == 0 )
return 0;
if( level_index == 3 )
delta_offset = bits_per_level[0] + bits_per_level[1] * 3
+ bits_per_level[2] * 12 + (pixel_index - 16) * bits_per_level[3];
else if( level_index == 2 )
delta_offset = bits_per_level[0] + bits_per_level[1] * 3
+ (pixel_index - 4) * bits_per_level[2];
else if( level_index == 1 )
delta_offset = bits_per_level[0] + (pixel_index-1)*bits_per_level[1];
if (nInputBytes * 8 < block_offset+delta_offset + delta_bits)
{
CPLError( CE_Failure, CPLE_AppDefined, "Input buffer too small");
*pbError = TRUE;
return 0;
}
int delta_raw = get_bits( srcdata, block_offset+delta_offset, delta_bits );
int delta = delta_raw;
/* Should not happen as delta_075_by_level_by_bc[level_index] == NULL if and
only if level_index == 0, which means that pixel_index == 0, which means
(i, j) = (0, 0). That cannot happen as we are never called with those
values
*/
CPLAssert( delta_075_by_level_by_bc[level_index] != NULL );
const int *lookup_table = delta_075_by_level_by_bc[level_index][busy_code];
CPLAssert( lookup_table != NULL );
delta = lookup_table[delta_raw];
return delta;
}
/************************************************************************/
/* decode_block() */
/* */
/* Decode one 8x8 block. The 9x9 L buffer is pre-loaded with */
/* the left and top values from previous blocks. */
/************************************************************************/
static int
decode_block( unsigned char *srcdata, int nInputBytes,
int busy_code, int comrat,
int block_offset, int block_size,
int left_side, int top_side, int L[9][9] )
{
int i, j;
int bError;
// Level 2
L[0][4] = (L[0][0] + L[0][8])/2
+ get_delta(srcdata,nInputBytes,busy_code,comrat,block_offset,block_size,0,4,&bError);
if (bError) return FALSE;
L[4][0] = (L[0][0] + L[8][0])/2
+ get_delta(srcdata,nInputBytes,busy_code,comrat,block_offset,block_size,4,0,&bError);
if (bError) return FALSE;
L[4][4] = (L[0][0] + L[8][0] + L[0][8] + L[8][8])/4
+ get_delta(srcdata,nInputBytes,busy_code,comrat,block_offset,block_size,4,4,&bError);
if (bError) return FALSE;
if( left_side )
L[4][8] = L[4][0];
if( top_side )
L[8][4] = L[0][4];
// Level 3
for( i = 0; i < 8; i += 4 )
{
for( j = 0; j < 8; j += 4 )
{
// above
L[i+2][j] = (L[i][j]+L[i+4][j])/2
+ get_delta(srcdata,nInputBytes,busy_code,comrat,
block_offset,block_size,i+2,j,&bError);
if (bError) return FALSE;
// left
L[i][j+2] = (L[i][j]+L[i][j+4])/2
+ get_delta(srcdata,nInputBytes,busy_code,comrat,
block_offset,block_size,i,j+2,&bError);
if (bError) return FALSE;
// up-left
L[i+2][j+2] = (L[i][j]+L[i][j+4]+L[i+4][j]+L[i+4][j+4])/4
+ get_delta(srcdata,nInputBytes,busy_code,comrat,
block_offset,block_size,i+2,j+2,&bError);
if (bError) return FALSE;
}
}
if( left_side )
{
L[2][8] = L[2][0];
L[6][8] = L[6][0];
}
if( top_side )
{
L[8][2] = L[0][2];
L[8][6] = L[0][6];
}
// Level 4
for( i = 0; i < 8; i += 2 )
{
for( j = 0; j < 8; j += 2 )
{
// above
L[i+1][j] = (L[i][j]+L[i+2][j])/2
+ get_delta(srcdata,nInputBytes,busy_code,comrat,
block_offset,block_size,i+1,j,&bError);
if (bError) return FALSE;
// left
L[i][j+1] = (L[i][j]+L[i][j+2])/2
+ get_delta(srcdata,nInputBytes,busy_code,comrat,
block_offset,block_size,i,j+1,&bError);
if (bError) return FALSE;
// up-left
L[i+1][j+1] = (L[i][j]+L[i][j+2]+L[i+2][j]+L[i+2][j+2])/4
+ get_delta(srcdata,nInputBytes,busy_code,comrat,
block_offset,block_size,i+1,j+1,&bError);
if (bError) return FALSE;
}
}
return TRUE;
}
/************************************************************************/
/* NITFUncompressARIDPCM() */
/************************************************************************/
int NITFUncompressARIDPCM( NITFImage *psImage,
GByte *pabyInputData,
int nInputBytes,
GByte *pabyOutputImage )
{
/* -------------------------------------------------------------------- */
/* First, verify that we are a COMRAT 0.75 image, which is all */
/* we currently support. */
/* -------------------------------------------------------------------- */
if( !EQUAL(psImage->szCOMRAT,"0.75") )
{
CPLError( CE_Failure, CPLE_AppDefined,
"COMRAT=%s ARIDPCM is not supported.\n"
"Currently only 0.75 is supported.",
psImage->szCOMRAT );
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Setup up the various info we need for each 8x8 neighbourhood */
/* (which we call blocks in this context). */
/* -------------------------------------------------------------------- */
int blocks_x = (psImage->nBlockWidth + 7) / 8;
int blocks_y = (psImage->nBlockHeight + 7) / 8;
int block_count = blocks_x * blocks_y;
int rowlen = blocks_x * 8;
if( psImage->nBlockWidth > 1000 || /* to detect int overflow above */
psImage->nBlockHeight > 1000 ||
block_count > 1000 )
{
CPLError( CE_Failure, CPLE_AppDefined, "Block too large to be decoded");
return FALSE;
}
int block_offset[1000];
int block_size[1000];
int busy_code[1000];
int busy_code_table_size = blocks_x * blocks_y * 2;
unsigned char L00[1000];
/* -------------------------------------------------------------------- */
/* We allocate a working copy of the full image that may be a */
/* bit larger than the output buffer if the width or height is */
/* not divisible by 8. */
/* -------------------------------------------------------------------- */
GByte *full_image = (GByte *) CPLMalloc(blocks_x * blocks_y * 8 * 8 );
/* -------------------------------------------------------------------- */
/* Scan through all the neighbourhoods determining the busyness */
/* code, and the offset to each's data as well as the L00 value. */
/* -------------------------------------------------------------------- */
int i, j, total = busy_code_table_size;
for( i = 0; i < blocks_x * blocks_y; i++ )
{
if (nInputBytes * 8 < i * 2 + 2)
{
CPLError( CE_Failure, CPLE_AppDefined, "Input buffer too small");
CPLFree(full_image);
return FALSE;
}
busy_code[i] = get_bits( pabyInputData, i*2, 2 );
block_offset[i] = total;
block_size[i] = neighbourhood_size_75[busy_code[i]];
if (nInputBytes * 8 < block_offset[i] + 8)
{
CPLError( CE_Failure, CPLE_AppDefined, "Input buffer too small");
CPLFree(full_image);
return FALSE;
}
L00[i] = (unsigned char) get_bits( pabyInputData, block_offset[i], 8 );
total += block_size[i];
}
/* -------------------------------------------------------------------- */
/* Process all the blocks, forming into a final image. */
/* -------------------------------------------------------------------- */
int iX, iY;
for( iY = 0; iY < blocks_y; iY++ )
{
for( iX = 0; iX < blocks_x; iX++ )
{
int iBlock = iX + iY * blocks_x;
int L[9][9];
unsigned char *full_tl = full_image + iX * 8 + iY * 8 * rowlen;
L[0][0] = L00[iBlock];
if( iX > 0 )
{
L[0][8] = full_tl[rowlen * 7 - 1];
L[2][8] = full_tl[rowlen * 5 - 1];
L[4][8] = full_tl[rowlen * 3 - 1];
L[6][8] = full_tl[rowlen * 1 - 1];
}
else
{
L[0][8] = L[0][0];
L[2][8] = L[0][8]; // need to reconstruct the rest!
L[4][8] = L[0][8];
L[6][8] = L[0][8];
}
if( iY > 0 )
{
L[8][0] = full_tl[7 - rowlen];
L[8][2] = full_tl[5 - rowlen];
L[8][4] = full_tl[3 - rowlen];
L[8][6] = full_tl[1 - rowlen];
}
else
{
L[8][0] = L[0][0];
L[8][2] = L[0][0]; // Need to reconstruct the rest!
L[8][4] = L[0][0];
L[8][5] = L[0][0];
}
if( iX == 0 || iY == 0 )
L[8][8] = L[0][0];
else
L[8][8] = full_tl[-1-rowlen];
if (!(decode_block( pabyInputData, nInputBytes, busy_code[iBlock], CR075,
block_offset[iBlock], block_size[iBlock],
iX == 0, iY == 0, L )))
{
CPLFree( full_image );
return FALSE;
}
// Assign to output matrix.
for( i = 0; i < 8; i++ )
{
for( j = 0; j < 8; j++ )
{
int value = L[i][j];
if( value < 0 )
value = 0;
if( value > 255 )
value = 255;
full_tl[8-j-1 + (8-i-1) * rowlen] = (unsigned char) value;
}
}
}
}
/* -------------------------------------------------------------------- */
/* Copy full image back into target buffer, and free. */
/* -------------------------------------------------------------------- */
for( iY = 0; iY < psImage->nBlockHeight; iY++ )
{
memcpy( pabyOutputImage + iY * psImage->nBlockWidth,
full_image + iY * rowlen,
psImage->nBlockWidth );
}
CPLFree( full_image );
return TRUE;
}