diff options
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/GScaler.cpp')
-rw-r--r-- | kviewshell/plugins/djvu/libdjvu/GScaler.cpp | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/GScaler.cpp b/kviewshell/plugins/djvu/libdjvu/GScaler.cpp new file mode 100644 index 00000000..0eeb9ebf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GScaler.cpp @@ -0,0 +1,706 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GScaler.cpp,v 1.11 2004/06/03 14:15:18 leonb Exp $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// Rescale images with fast bilinear interpolation +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "GScaler.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +//////////////////////////////////////// +// CONSTANTS + + +#define FRACBITS 4 +#define FRACSIZE (1<<FRACBITS) +#define FRACSIZE2 (FRACSIZE>>1) +#define FRACMASK (FRACSIZE-1) + + + + + + +//////////////////////////////////////// +// UTILITIES + + +static int interp_ok = 0; +static short interp[FRACSIZE][512]; + +static void +prepare_interp() +{ + if (! interp_ok) + { + interp_ok = 1; + for (int i=0; i<FRACSIZE; i++) + { + short *deltas = & interp[i][256]; + for (int j = -255; j <= 255; j++) + deltas[j] = ( j*i + FRACSIZE2 ) >> FRACBITS; + } + } +} + + +static inline int +mini(int x, int y) +{ + return (x < y ? x : y); +} + + +static inline int +maxi(int x, int y) +{ + return (x > y ? x : y); +} + + + + + + +//////////////////////////////////////// +// GSCALER + + +GScaler::GScaler() + : inw(0), inh(0), + xshift(0), yshift(0), redw(0), redh(0), + outw(0), outh(0), + gvcoord(vcoord,0), ghcoord(hcoord,0) +{ +} + + +GScaler::~GScaler() +{ +} + + +void +GScaler::set_input_size(int w, int h) +{ + inw = w; + inh = h; + if (vcoord) + { + gvcoord.resize(0); + } + if (hcoord) + { + ghcoord.resize(0); + } +} + + +void +GScaler::set_output_size(int w, int h) +{ + outw = w; + outh = h; + if (vcoord) + { + gvcoord.resize(0); + } + if (hcoord) + { + ghcoord.resize(0); + } +} + + +static void +prepare_coord(int *coord, int inmax, int outmax, int in, int out) +{ + int len = (in*FRACSIZE); + int beg = (len+out)/(2*out) - FRACSIZE2; + // Bresenham algorithm + int y = beg; + int z = out/2; + int inmaxlim = (inmax-1)*FRACSIZE; + for (int x=0; x<outmax; x++) + { + coord[x] = mini(y,inmaxlim); + z = z + len; + y = y + z / out; + z = z % out; + } + // Result must fit exactly + if (out==outmax && y!=beg+len) + G_THROW( ERR_MSG("GScaler.assertion") ); +} + + +void +GScaler::set_horz_ratio(int numer, int denom) +{ + if (! (inw>0 && inh>0 && outw>0 && outh>0)) + G_THROW( ERR_MSG("GScaler.undef_size") ); + // Implicit ratio (determined by the input/output sizes) + if (numer==0 && denom==0) { + numer = outw; + denom = inw; + } else if (numer<=0 || denom<=0) + G_THROW( ERR_MSG("GScaler.ratios") ); + // Compute horz reduction + xshift = 0; + redw = inw; + while (numer+numer < denom) { + xshift += 1; + redw = (redw + 1) >> 1; + numer = numer << 1; + } + // Compute coordinate table + if (! hcoord) + ghcoord.resize(outw); + prepare_coord(hcoord, redw, outw, denom, numer); +} + + +void +GScaler::set_vert_ratio(int numer, int denom) +{ + if (! (inw>0 && inh>0 && outw>0 && outh>0)) + G_THROW( ERR_MSG("GScaler.undef_size") ); + // Implicit ratio (determined by the input/output sizes) + if (numer==0 && denom==0) { + numer = outh; + denom = inh; + } else if (numer<=0 || denom<=0) + G_THROW( ERR_MSG("GScaler.ratios") ); + // Compute horz reduction + yshift = 0; + redh = inh; + while (numer+numer < denom) { + yshift += 1; + redh = (redh + 1) >> 1; + numer = numer << 1; + } + // Compute coordinate table + if (! vcoord) + { + gvcoord.resize(outh); + } + prepare_coord(vcoord, redh, outh, denom, numer); +} + + +void +GScaler::make_rectangles(const GRect &desired, GRect &red, GRect &inp) +{ + // Parameter validation + if (desired.xmin<0 || desired.ymin<0 || + desired.xmax>outw || desired.ymax>outh ) + G_THROW( ERR_MSG("GScaler.too_big") ); + // Compute ratio (if not done yet) + if (!vcoord) + set_vert_ratio(0,0); + if (!hcoord) + set_horz_ratio(0,0); + // Compute reduced bounds + red.xmin = (hcoord[desired.xmin]) >> FRACBITS; + red.ymin = (vcoord[desired.ymin]) >> FRACBITS; + red.xmax = (hcoord[desired.xmax-1]+FRACSIZE-1) >> FRACBITS; + red.ymax = (vcoord[desired.ymax-1]+FRACSIZE-1) >> FRACBITS; + // Borders + red.xmin = maxi(red.xmin, 0); + red.xmax = mini(red.xmax+1, redw); + red.ymin = maxi(red.ymin, 0); + red.ymax = mini(red.ymax+1, redh); + // Input + inp.xmin = maxi(red.xmin<<xshift, 0); + inp.xmax = mini(red.xmax<<xshift, inw); + inp.ymin = maxi(red.ymin<<yshift, 0); + inp.ymax = mini(red.ymax<<yshift, inh); +} + + +void +GScaler::get_input_rect( const GRect &desired_output, GRect &required_input ) +{ + GRect red; + make_rectangles(desired_output, red, required_input); +} + + + + + + +//////////////////////////////////////// +// GBITMAPSCALER + + +GBitmapScaler::GBitmapScaler() + : glbuffer(lbuffer,0), gconv(conv,0), gp1(p1,0), gp2(p2,0) +{ +} + + +GBitmapScaler::GBitmapScaler(int inw, int inh, int outw, int outh) + : glbuffer(lbuffer,0), gconv(conv,0), gp1(p1,0), gp2(p2,0) +{ + set_input_size(inw, inh); + set_output_size(outw, outh); +} + + +GBitmapScaler::~GBitmapScaler() +{ +} + + +unsigned char * +GBitmapScaler::get_line(int fy, + const GRect &required_red, + const GRect &provided_input, + const GBitmap &input ) +{ + if (fy < required_red.ymin) + fy = required_red.ymin; + else if (fy >= required_red.ymax) + fy = required_red.ymax - 1; + // Cached line + if (fy == l2) + return p2; + if (fy == l1) + return p1; + // Shift + unsigned char *p = p1; + p1 = p2; + l1 = l2; + p2 = p; + l2 = fy; + if (xshift==0 && yshift==0) + { + // Fast mode + int dx = required_red.xmin-provided_input.xmin; + int dx1 = required_red.xmax-provided_input.xmin; + const unsigned char *inp1 = input[fy-provided_input.ymin] + dx; + while (dx++ < dx1) + *p++ = conv[*inp1++]; + return p2; + } + else + { + // Compute location of line + GRect line; + line.xmin = required_red.xmin << xshift; + line.xmax = required_red.xmax << xshift; + line.ymin = fy << yshift; + line.ymax = (fy+1) << yshift; + line.intersect(line, provided_input); + line.translate(-provided_input.xmin, -provided_input.ymin); + // Prepare variables + const unsigned char *botline = input[line.ymin]; + int rowsize = input.rowsize(); + int sw = 1<<xshift; + int div = xshift+yshift; + int rnd = 1<<(div-1); + // Compute averages + for (int x=line.xmin; x<line.xmax; x+=sw,p++) + { + int g=0, s=0; + const unsigned char *inp0 = botline + x; + int sy1 = mini(line.height(), (1<<yshift)); + for (int sy=0; sy<sy1; sy++,inp0+=rowsize) + { + const unsigned char *inp1; + const unsigned char *inp2 = inp0 + mini(x+sw, line.xmax) - x; + for (inp1=inp0; inp1<inp2; inp1++) + { + g += conv[*inp1]; + s += 1; + } + } + if (s == rnd+rnd) + *p = (g+rnd)>>div; + else + *p = (g+s/2)/s; + } + // Return + return p2; + } +} + + +void +GBitmapScaler::scale( const GRect &provided_input, const GBitmap &input, + const GRect &desired_output, GBitmap &output ) +{ + // Compute rectangles + GRect required_input; + GRect required_red; + make_rectangles(desired_output, required_red, required_input); + // Parameter validation + if (provided_input.width() != (int)input.columns() || + provided_input.height() != (int)input.rows() ) + G_THROW( ERR_MSG("GScaler.no_match") ); + if (provided_input.xmin > required_input.xmin || + provided_input.ymin > required_input.ymin || + provided_input.xmax < required_input.xmax || + provided_input.ymax < required_input.ymax ) + G_THROW( ERR_MSG("GScaler.too_small") ); + // Adjust output pixmap + if (desired_output.width() != (int)output.columns() || + desired_output.height() != (int)output.rows() ) + output.init(desired_output.height(), desired_output.width()); + output.set_grays(256); + // Prepare temp stuff + gp1.resize(0); + gp2.resize(0); + glbuffer.resize(0); + prepare_interp(); + const int bufw = required_red.width(); + glbuffer.resize(bufw+2); + gp1.resize(bufw); + gp2.resize(bufw); + l1 = l2 = -1; + // Prepare gray conversion array (conv) + gconv.resize(0); + gconv.resize(256); + int maxgray = input.get_grays()-1; + for (int i=0; i<256; i++) + { + conv[i]=(i<= maxgray) + ?(((i*255) + (maxgray>>1)) / maxgray) + :255; + } + // Loop on output lines + for (int y=desired_output.ymin; y<desired_output.ymax; y++) + { + // Perform vertical interpolation + { + int fy = vcoord[y]; + int fy1 = fy>>FRACBITS; + int fy2 = fy1+1; + const unsigned char *lower, *upper; + // Obtain upper and lower line in reduced image + lower = get_line(fy1, required_red, provided_input, input); + upper = get_line(fy2, required_red, provided_input, input); + // Compute line + unsigned char *dest = lbuffer+1; + const short *deltas = & interp[fy&FRACMASK][256]; + for(unsigned char const * const edest=(unsigned char const *)dest+bufw; + dest<edest;upper++,lower++,dest++) + { + const int l = *lower; + const int u = *upper; + *dest = l + deltas[u-l]; + } + } + // Perform horizontal interpolation + { + // Prepare for side effects + lbuffer[0] = lbuffer[1]; + lbuffer[bufw] = lbuffer[bufw]; + unsigned char *line = lbuffer+1-required_red.xmin; + unsigned char *dest = output[y-desired_output.ymin]; + // Loop horizontally + for (int x=desired_output.xmin; x<desired_output.xmax; x++) + { + int n = hcoord[x]; + const unsigned char *lower = line + (n>>FRACBITS); + const short *deltas = &interp[n&FRACMASK][256]; + int l = lower[0]; + int u = lower[1]; + *dest = l + deltas[u-l]; + dest++; + } + } + } + // Free temporaries + gp1.resize(0); + gp2.resize(0); + glbuffer.resize(0); + gconv.resize(0); +} + + + + + + +//////////////////////////////////////// +// GPIXMAPSCALER + + +GPixmapScaler::GPixmapScaler() + : glbuffer((void *&)lbuffer,0,sizeof(GPixel)), + gp1((void *&)p1,0,sizeof(GPixel)), + gp2((void *&)p2,0,sizeof(GPixel)) +{ +} + + +GPixmapScaler::GPixmapScaler(int inw, int inh, int outw, int outh) + : glbuffer((void *&)lbuffer,0,sizeof(GPixel)), + gp1((void *&)p1,0,sizeof(GPixel)), + gp2((void *&)p2,0,sizeof(GPixel)) +{ + set_input_size(inw, inh); + set_output_size(outw, outh); +} + + +GPixmapScaler::~GPixmapScaler() +{ +} + + +GPixel * +GPixmapScaler::get_line(int fy, + const GRect &required_red, + const GRect &provided_input, + const GPixmap &input ) +{ + if (fy < required_red.ymin) + fy = required_red.ymin; + else if (fy >= required_red.ymax) + fy = required_red.ymax - 1; + // Cached line + if (fy == l2) + return p2; + if (fy == l1) + return p1; + // Shift + GPixel *p=p1; + p1 = p2; + l1 = l2; + p2 = p; + l2 = fy; + // Compute location of line + GRect line; + line.xmin = required_red.xmin << xshift; + line.xmax = required_red.xmax << xshift; + line.ymin = fy << yshift; + line.ymax = (fy+1) << yshift; + line.intersect(line, provided_input); + line.translate(-provided_input.xmin, -provided_input.ymin); + // Prepare variables + const GPixel *botline = input[line.ymin]; + int rowsize = input.rowsize(); + int sw = 1<<xshift; + int div = xshift+yshift; + int rnd = 1<<(div-1); + // Compute averages + for (int x=line.xmin; x<line.xmax; x+=sw,p++) + { + int r=0, g=0, b=0, s=0; + const GPixel *inp0 = botline + x; + int sy1 = mini(line.height(), (1<<yshift)); + for (int sy=0; sy<sy1; sy++,inp0+=rowsize) + { + const GPixel *inp1; + const GPixel *inp2 = inp0 + mini(x+sw, line.xmax) - x; + for (inp1 = inp0; inp1<inp2; inp1++) + { + r += inp1->r; + g += inp1->g; + b += inp1->b; + s += 1; + } + } + if (s == rnd+rnd) + { + p->r = (r+rnd) >> div; + p->g = (g+rnd) >> div; + p->b = (b+rnd) >> div; + } + else + { + p->r = (r+s/2)/s; + p->g = (g+s/2)/s; + p->b = (b+s/2)/s; + } + } + // Return + return (GPixel *)p2; +} + + +void +GPixmapScaler::scale( const GRect &provided_input, const GPixmap &input, + const GRect &desired_output, GPixmap &output ) +{ + // Compute rectangles + GRect required_input; + GRect required_red; + make_rectangles(desired_output, required_red, required_input); + // Parameter validation + if (provided_input.width() != (int)input.columns() || + provided_input.height() != (int)input.rows() ) + G_THROW( ERR_MSG("GScaler.no_match") ); + if (provided_input.xmin > required_input.xmin || + provided_input.ymin > required_input.ymin || + provided_input.xmax < required_input.xmax || + provided_input.ymax < required_input.ymax ) + G_THROW( ERR_MSG("GScaler.too_small") ); + // Adjust output pixmap + if (desired_output.width() != (int)output.columns() || + desired_output.height() != (int)output.rows() ) + output.init(desired_output.height(), desired_output.width()); + // Prepare temp stuff + gp1.resize(0,sizeof(GPixel)); + gp2.resize(0,sizeof(GPixel)); + glbuffer.resize(0,sizeof(GPixel)); + prepare_interp(); + const int bufw = required_red.width(); + glbuffer.resize(bufw+2,sizeof(GPixel)); + if (xshift>0 || yshift>0) + { + gp1.resize(bufw,sizeof(GPixel)); + gp2.resize(bufw,sizeof(GPixel)); + l1 = l2 = -1; + } + // Loop on output lines + for (int y=desired_output.ymin; y<desired_output.ymax; y++) + { + // Perform vertical interpolation + { + int fy = vcoord[y]; + int fy1 = fy>>FRACBITS; + int fy2 = fy1+1; + const GPixel *lower, *upper; + // Obtain upper and lower line in reduced image + if (xshift>0 || yshift>0) + { + lower = get_line(fy1, required_red, provided_input, input); + upper = get_line(fy2, required_red, provided_input, input); + } + else + { + int dx = required_red.xmin-provided_input.xmin; + fy1 = maxi(fy1, required_red.ymin); + fy2 = mini(fy2, required_red.ymax-1); + lower = input[fy1-provided_input.ymin] + dx; + upper = input[fy2-provided_input.ymin] + dx; + } + // Compute line + GPixel *dest = lbuffer+1; + const short *deltas = & interp[fy&FRACMASK][256]; + for(GPixel const * const edest = (GPixel const *)dest+bufw; + dest<edest;upper++,lower++,dest++) + { + const int lower_r = lower->r; + const int delta_r = deltas[(int)upper->r - lower_r]; + dest->r = lower_r + delta_r; + const int lower_g = lower->g; + const int delta_g = deltas[(int)upper->g - lower_g]; + dest->g = lower_g + delta_g; + const int lower_b = lower->b; + const int delta_b = deltas[(int)upper->b - lower_b]; + dest->b = lower_b + delta_b; + } + } + // Perform horizontal interpolation + { + // Prepare for side effects + lbuffer[0] = lbuffer[1]; + lbuffer[bufw] = lbuffer[bufw]; + GPixel *line = lbuffer+1-required_red.xmin; + GPixel *dest = output[y-desired_output.ymin]; + // Loop horizontally + for (int x=desired_output.xmin; x<desired_output.xmax; x++,dest++) + { + const int n = hcoord[x]; + const GPixel *lower = line + (n>>FRACBITS); + const short *deltas = &interp[n&FRACMASK][256]; + const int lower_r = lower[0].r; + const int delta_r = deltas[(int)lower[1].r - lower_r]; + dest->r = lower_r + delta_r; + const int lower_g = lower[0].g; + const int delta_g = deltas[(int)lower[1].g - lower_g]; + dest->g = lower_g + delta_g; + const int lower_b = lower[0].b; + const int delta_b = deltas[(int)lower[1].b - lower_b]; + dest->b = lower_b + delta_b; + } + } + } + // Free temporaries + gp1.resize(0,sizeof(GPixel)); + gp2.resize(0,sizeof(GPixel)); + glbuffer.resize(0,sizeof(GPixel)); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + |