/* GNU Ocrad - Optical Character Recognition program
Copyright (C) 2003, 2004, 2005, 2006, 2007 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include "common.h"
#include "rational.h"
#include "rectangle.h"
#include "page_image.h"
namespace {
void enlarge_2( std::vector< std::vector< unsigned char > > & data ) throw()
{
const int height = data.size(), width = data[0].size();
std::vector< std::vector< unsigned char > > new_data( 2 * height );
for( unsigned int row = 0; row < new_data.size(); ++row )
new_data[row].resize( 2 * width, 1 );
for( int row = 0; row < height; ++row )
{
const std::vector< unsigned char > & datarow = data[row];
std::vector< unsigned char > & new_datarow0 = new_data[2*row];
std::vector< unsigned char > & new_datarow1 = new_data[2*row+1];
for( int col = 0; col < width; ++col )
{
if( datarow[col] == 0 )
{
const bool l = col > 0 && datarow[col-1] == 0;
const bool t = row > 0 && data[row-1][col] == 0;
const bool r = col < width - 1 && datarow[col+1] == 0;
const bool b = row < height - 1 && data[row+1][col] == 0;
const bool lt = row > 0 && col > 0 && data[row-1][col-1] == 0;
const bool rt = row > 0 && col < width - 1 && data[row-1][col+1] == 0;
const bool lb = row < height - 1 && col > 0 && data[row+1][col-1] == 0;
const bool rb = row < height - 1 && col < width - 1 && data[row+1][col+1] == 0;
if( l || t || lt || ( !rt && !lb ) ) new_datarow0[2*col] = 0;
if( r || t || rt || ( !lt && !rb ) ) new_datarow0[2*col+1] = 0;
if( l || b || lb || ( !lt && !rb ) ) new_datarow1[2*col] = 0;
if( r || b || rb || ( !rt && !lb ) ) new_datarow1[2*col+1] = 0;
}
}
}
data.swap( new_data );
}
void enlarge_3( std::vector< std::vector< unsigned char > > & data ) throw()
{
const int height = data.size(), width = data[0].size();
std::vector< std::vector< unsigned char > > new_data( 3 * height );
for( unsigned int row = 0; row < new_data.size(); ++row )
new_data[row].resize( 3 * width, 1 );
for( int row = 0; row < height; ++row )
{
const int row3 = 3 * row;
const std::vector< unsigned char > & datarow = data[row];
std::vector< unsigned char > & new_datarow0 = new_data[row3];
std::vector< unsigned char > & new_datarow1 = new_data[row3+1];
std::vector< unsigned char > & new_datarow2 = new_data[row3+2];
for( int col = 0; col < width; ++col )
{
const int col3 = 3 * col;
const bool l = col > 0 && datarow[col-1] == 0;
const bool t = row > 0 && data[row-1][col] == 0;
const bool r = col < width - 1 && datarow[col+1] == 0;
const bool b = row < height - 1 && data[row+1][col] == 0;
const bool lt = row > 0 && col > 0 && data[row-1][col-1] == 0;
const bool rt = row > 0 && col < width - 1 && data[row-1][col+1] == 0;
const bool lb = row < height - 1 && col > 0 && data[row+1][col-1] == 0;
const bool rb = row < height - 1 && col < width - 1 && data[row+1][col+1] == 0;
if( datarow[col] == 0 )
{
if( l || t || lt || ( !rt && !lb ) ) new_datarow0[col3] = 0;
new_datarow0[col3+1] = 0;
if( r || t || rt || ( !lt && !rb ) ) new_datarow0[col3+2] = 0;
new_datarow1[col3] = new_datarow1[col3+1] = new_datarow1[col3+2] = 0;
if( l || b || lb || ( !lt && !rb ) ) new_datarow2[col3] = 0;
new_datarow2[col3+1] = 0;
if( r || b || rb || ( !rt && !lb ) ) new_datarow2[col3+2] = 0;
}
else
{
if( l && t && lt && ( !rt || !lb ) ) new_datarow0[col3] = 0;
if( r && t && rt && ( !lt || !rb ) ) new_datarow0[col3+2] = 0;
if( l && b && lb && ( !lt || !rb ) ) new_datarow2[col3] = 0;
if( r && b && rb && ( !rt || !lb ) ) new_datarow2[col3+2] = 0;
}
}
}
data.swap( new_data );
}
void enlarge_n( std::vector< std::vector< unsigned char > > & data, const int n ) throw()
{
if( n < 2 ) return;
const int height = data.size(), width = data[0].size();
std::vector< std::vector< unsigned char > > new_data;
new_data.reserve( n * height );
for( int row = 0; row < height; ++row )
{
const std::vector< unsigned char > & datarow = data[row];
new_data.push_back( std::vector< unsigned char >() );
for( int col = 0; col < width; ++col )
{
const unsigned char d = datarow[col];
for( int i = 0; i < n; ++i ) new_data.back().push_back( d );
}
for( int i = 1; i < n; ++i ) new_data.push_back( new_data.back() );
}
data.swap( new_data );
}
void mirror_left_right( std::vector< std::vector< unsigned char > > & data ) throw()
{
const int height = data.size();
for( int row = 0; row < height; ++row )
std::reverse( data[row].begin(), data[row].end() );
}
void mirror_top_bottom( std::vector< std::vector< unsigned char > > & data ) throw()
{
for( int u = 0, d = data.size() - 1; u < d; ++u, --d )
data[u].swap( data[d] );
}
void mirror_diagonal( std::vector< std::vector< unsigned char > > & data,
Rectangle & re ) throw()
{
const int size = std::max( re.height(), re.width() );
if( re.height() < size )
{
data.resize( size );
for( int row = re.height(); row < size; ++row )
data[row].resize( size );
}
else if( re.width() < size )
for( int row = 0; row < re.height(); ++row )
data[row].resize( size );
for( int row = 0; row < size; ++row )
{
std::vector< unsigned char > & datarow = data[row];
for( int col = 0; col < row; ++col )
{
unsigned char tmp = datarow[col];
datarow[col] = data[col][row]; data[col][row] = tmp;
}
}
const int h = re.height(), w = re.width();
re.height( w ); re.width( h );
if( re.height() < size ) data.resize( re.height() );
else if( re.width() < size )
for( int row = 0; row < re.height(); ++row )
data[row].resize( re.width() );
}
} // end namespace
bool Page_image::scale( int n ) throw( Page_image::Error )
{
if( n <= -2 )
{ Page_image reduced( *this, -n ); *this = reduced; return true; }
if( n >= 2 )
{
if( (long long)width() * height() * n > (long long)INT_MAX )
throw Error( "scale factor too big. `int' will overflow." );
if( _maxval == 1 )
{
if( n && ( n % 2 ) == 0 ) { enlarge_2( data ); n /= 2; }
else if( n && ( n % 3 ) == 0 ) { enlarge_3( data ); n /= 3; }
}
if( n > 1 ) enlarge_n( data, n );
Rectangle::height( data.size() );
Rectangle::width( data[0].size() );
return true;
}
return false;
}
void Page_image::transform( const Transformation & t ) throw()
{
switch( t.type() )
{
case Transformation::none:
break;
case Transformation::rotate90:
mirror_diagonal( data, *this ); mirror_top_bottom( data ); break;
case Transformation::rotate180:
mirror_left_right( data ); mirror_top_bottom( data ); break;
case Transformation::rotate270:
mirror_diagonal( data, *this ); mirror_left_right( data ); break;
case Transformation::mirror_lr:
mirror_left_right( data ); break;
case Transformation::mirror_tb:
mirror_top_bottom( data ); break;
case Transformation::mirror_d1:
mirror_diagonal( data, *this ); break;
case Transformation::mirror_d2:
mirror_diagonal( data, *this );
mirror_left_right( data ); mirror_top_bottom( data ); break;
}
}