summaryrefslogtreecommitdiffstats
path: root/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp')
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp1486
1 files changed, 1486 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp
new file mode 100644
index 00000000..f384ce97
--- /dev/null
+++ b/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp
@@ -0,0 +1,1486 @@
+//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: DjVuImage.cpp,v 1.10 2005/04/27 16:34:13 leonb Exp $
+// $Name: release_3_5_15 $
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#if NEED_GNUG_PRAGMAS
+# pragma implementation
+#endif
+
+#include "DjVuImage.h"
+#include "GScaler.h"
+#include "DjVuDocument.h"
+#include "DjVuPalette.h"
+#include "GContainer.h"
+#include "GSmartPointer.h"
+#include "JB2Image.h"
+#include "IW44Image.h"
+#include "DataPool.h"
+#include "ByteStream.h"
+#include "GMapAreas.h"
+#include "DjVuText.h"
+#include "IFFByteStream.h"
+#include "BSByteStream.h"
+#include "debug.h"
+#include <stdarg.h>
+
+
+#ifdef HAVE_NAMESPACES
+namespace DJVU {
+# ifdef NOT_DEFINED // Just to fool emacs c++ mode
+}
+#endif
+#endif
+
+
+
+//// DJVUIMAGE: CONSTRUCTION
+
+DjVuImage::DjVuImage(void)
+: rotate_count(-1),relayout_sent(false)
+{
+}
+
+void
+DjVuImage::connect(const GP<DjVuFile> & xfile)
+{
+ file=xfile;
+ DjVuPort::get_portcaster()->add_route(file, this);
+}
+
+
+
+
+//// DJVUIMAGE: DATA COLLECTORS
+
+GP<DjVuInfo>
+DjVuImage::get_info(const GP<DjVuFile> & file) const
+{
+ if (file->info)
+ {
+ if(rotate_count<0)
+ {
+ const_cast<DjVuImage *>(this)->init_rotate(*(file->info));
+ }
+ return file->info;
+ }
+ GPList<DjVuFile> list=file->get_included_files();
+ for(GPosition pos=list;pos;++pos)
+ {
+ GP<DjVuInfo> info=get_info(list[pos]);
+ if (info)
+ {
+ if(rotate_count<0)
+ {
+ const_cast<DjVuImage *>(this)->init_rotate(*(file->info));
+ }
+ return info;
+ }
+ }
+ return 0;
+}
+
+GP<IW44Image>
+DjVuImage::get_bg44(const GP<DjVuFile> & file) const
+{
+ if (file->bg44)
+ return file->bg44;
+ GPList<DjVuFile> list=file->get_included_files();
+ for(GPosition pos=list;pos;++pos)
+ {
+ GP<IW44Image> bg44=get_bg44(list[pos]);
+ if (bg44)
+ return bg44;
+ }
+ return 0;
+}
+
+GP<GPixmap>
+DjVuImage::get_bgpm(const GP<DjVuFile> & file) const
+{
+ if (file->bgpm)
+ return file->bgpm;
+ GPList<DjVuFile> list=file->get_included_files();
+ for(GPosition pos=list;pos;++pos)
+ {
+ GP<GPixmap> bgpm=get_bgpm(list[pos]);
+ if (bgpm) return bgpm;
+ }
+ return 0;
+}
+
+GP<JB2Image>
+DjVuImage::get_fgjb(const GP<DjVuFile> & file) const
+{
+ if (file->fgjb)
+ return file->fgjb;
+ GPList<DjVuFile> list=file->get_included_files();
+ for(GPosition pos=list;pos;++pos)
+ {
+ GP<JB2Image> fgjb=get_fgjb(list[pos]);
+ if (fgjb)
+ return fgjb;
+ }
+ return 0;
+}
+
+GP<GPixmap>
+DjVuImage::get_fgpm(const GP<DjVuFile> & file) const
+{
+ if (file->fgpm)
+ return file->fgpm;
+ GPList<DjVuFile> list=file->get_included_files();
+ for(GPosition pos=list;pos;++pos)
+ {
+ GP<GPixmap> fgpm=get_fgpm(list[pos]);
+ if (fgpm)
+ return fgpm;
+ }
+ return 0;
+}
+
+GP<DjVuPalette>
+DjVuImage::get_fgbc(const GP<DjVuFile> & file) const
+{
+ if (file->fgbc)
+ return file->fgbc;
+ GPList<DjVuFile> list=file->get_included_files();
+ for(GPosition pos=list;pos;++pos)
+ {
+ GP<DjVuPalette> fgbc=get_fgbc(list[pos]);
+ if (fgbc) return fgbc;
+ }
+ return 0;
+}
+
+GP<DjVuInfo>
+DjVuImage::get_info() const
+{
+ if (file)
+ {
+ return get_info(file);
+ }else
+ {
+ return 0;
+ }
+}
+
+GP<ByteStream>
+DjVuImage::get_anno() const
+{
+ GP<ByteStream> out = ByteStream::create();
+ ByteStream &mbs = *out;
+ if (file)
+ {
+ file->merge_anno(mbs);
+ }
+ mbs.seek(0);
+ if(!mbs.size())
+ {
+ out=0;
+ }
+ return out;
+}
+
+GP<ByteStream>
+DjVuImage::get_text() const
+{
+ GP<ByteStream> out = ByteStream::create();
+ ByteStream &mbs = *out;
+ if (file)
+ {
+ file->get_text(mbs);
+ }
+ mbs.seek(0);
+ if(!mbs.size())
+ {
+ out=0;
+ }
+ return out;
+}
+
+GP<ByteStream>
+DjVuImage::get_meta() const
+{
+ GP<ByteStream> out = ByteStream::create();
+ ByteStream &mbs = *out;
+ if (file)
+ {
+ file->get_meta(mbs);
+ }
+ mbs.seek(0);
+ if(!mbs.size())
+ {
+ out=0;
+ }
+ return out;
+}
+
+GP<IW44Image>
+DjVuImage::get_bg44() const
+{
+ if (file)
+ return get_bg44(file);
+ else
+ return 0;
+}
+
+GP<GPixmap>
+DjVuImage::get_bgpm() const
+{
+ if (file)
+ return get_bgpm(file);
+ else
+ return 0;
+}
+
+GP<JB2Image>
+DjVuImage::get_fgjb() const
+{
+ if (file)
+ return get_fgjb(file);
+ else
+ return 0;
+}
+
+GP<GPixmap>
+DjVuImage::get_fgpm() const
+{
+ if (file)
+ return get_fgpm(file);
+ else
+ return 0;
+}
+
+GP<DjVuPalette>
+DjVuImage::get_fgbc() const
+{
+ if (file)
+ return get_fgbc(file);
+ else
+ return 0;
+}
+
+int
+DjVuImage::get_width() const
+{
+ GP<DjVuInfo> info=get_info();
+ return info?((rotate_count&1)?(info->height):(info->width)):0;
+}
+
+int
+DjVuImage::get_height() const
+{
+ GP<DjVuInfo> info=get_info();
+ return info?((rotate_count&1)?(info->width):(info->height)):0;
+}
+
+int
+DjVuImage::get_real_width() const
+{
+ GP<DjVuInfo> info=get_info();
+ return info ? info->width : 0;
+}
+
+int
+DjVuImage::get_real_height() const
+{
+ GP<DjVuInfo> info=get_info();
+ return info ? info->height : 0;
+}
+
+int
+DjVuImage::get_version() const
+{
+ GP<DjVuInfo> info=get_info();
+ return info ? info->version : DJVUVERSION;
+}
+
+int
+DjVuImage::get_dpi() const
+{
+ GP<DjVuInfo> info=get_info();
+ return info ? info->dpi : 300;
+}
+
+int
+DjVuImage::get_rounded_dpi() const
+{
+ return (get_dpi()+5)/10*10;
+#if 0
+ /* This code used to round the reported dpi to 25, 50, 75, 100, 150,
+ 300, and 600. Now we just round the dpi to 10ths and return it */
+ int dpi=get_dpi();
+ if (dpi>700) return dpi;
+
+ const int std_dpi[]={ 25, 50, 75, 100, 150, 300, 600 };
+ const int std_dpis=sizeof(std_dpi)/sizeof(std_dpi[0]);
+ int min_dist=abs(dpi-std_dpi[0]);
+ int min_idx=0;
+ for(int i=1;i<std_dpis;i++)
+ if (abs(std_dpi[i]-dpi)<min_dist)
+ {
+ min_dist=abs(std_dpi[i]-dpi);
+ min_idx=i;
+ };
+ return std_dpi[min_idx];
+#endif
+}
+
+double
+DjVuImage::get_gamma() const
+{
+ GP<DjVuInfo> info=get_info();
+ return info ? info->gamma : 2.2;
+}
+
+GUTF8String
+DjVuImage::get_mimetype() const
+{
+ return file ? file->mimetype : GUTF8String();
+}
+
+
+//// DJVUIMAGE: UTILITIES
+
+GUTF8String
+DjVuImage::get_short_description() const
+{
+ GUTF8String msg = "Empty";
+ int width = get_width();
+ int height = get_height();
+ if (width && height)
+ if (file && file->file_size>100)
+ //msg.format("%dx%d in %0.1f Kb", width, height, file->file_size/1024.0);
+ msg.format( ERR_MSG("DjVuImage.short1") "\t%d\t%d\t%0.1f", width, height, file->file_size/1024.0 );
+ else
+ //msg.format("%dx%d", width, height);
+ msg.format( ERR_MSG("DjVuImage.short2") "\t%d\t%d", width, height );
+ return msg;
+}
+
+GUTF8String
+DjVuImage::get_long_description() const
+{
+ return file?(file->description):GUTF8String();
+}
+
+
+void
+DjVuImage::notify_chunk_done(const DjVuPort *, const GUTF8String & name)
+{
+ if (!relayout_sent &&
+ ( !name.cmp("INFO", 4) ||
+ !name.cmp("PMxx", 2) ||
+ !name.cmp("BMxx", 2) ) )
+ {
+ DjVuPort::get_portcaster()->notify_relayout(this);
+ relayout_sent=true;
+ }
+ else if (!name.cmp("Sxxx", 1) ||
+ !name.cmp("BGxx", 2) ||
+ !name.cmp("FGxx", 2) ||
+ !name.cmp("BMxx", 2) ||
+ !name.cmp("PMxx", 2) )
+ DjVuPort::get_portcaster()->notify_redisplay(this);
+}
+
+
+
+
+
+
+//// DJVUIMAGE: OLD-STYLE DECODING
+
+DjVuInterface::~DjVuInterface()
+{
+}
+
+class DjVuImageNotifier : public DjVuPort
+{
+ friend class DjVuImage;
+ DjVuInterface *notifier;
+ GP<DataPool> stream_pool;
+ GURL stream_url;
+public:
+ DjVuImageNotifier(DjVuInterface *notifier);
+ GP<DataPool> request_data(const DjVuPort *src, const GURL & url);
+ void notify_chunk_done(const DjVuPort *, const GUTF8String &name);
+ void notify_redisplay(const class DjVuImage * source);
+ void notify_relayout(const class DjVuImage * source);
+};
+
+DjVuImageNotifier::DjVuImageNotifier(DjVuInterface *notifier)
+ : notifier(notifier)
+{
+}
+
+GP<DataPool>
+DjVuImageNotifier::request_data(const DjVuPort *src, const GURL & url)
+{
+ if (url!=stream_url)
+ G_THROW( ERR_MSG("DjVuImage.not_decode") );
+ return stream_pool;
+}
+
+void
+DjVuImageNotifier::notify_redisplay(const class DjVuImage * source)
+{
+ if (notifier)
+ notifier->notify_redisplay();
+}
+
+void
+DjVuImageNotifier::notify_relayout(const class DjVuImage * source)
+{
+ if (notifier)
+ notifier->notify_relayout();
+}
+
+void
+DjVuImageNotifier::notify_chunk_done(const DjVuPort *, const GUTF8String &name)
+{
+ if (notifier)
+ notifier->notify_chunk(name, "" );
+}
+
+void
+DjVuImage::decode(ByteStream & str, DjVuInterface *notifier)
+{
+ DEBUG_MSG("DjVuImage::decode(): decoding old way...\n");
+ DEBUG_MAKE_INDENT(3);
+ if (file)
+ G_THROW( ERR_MSG("DjVuImage.bad_call") );
+ GP<DjVuImageNotifier> pport = new DjVuImageNotifier(notifier);
+ pport->stream_url=GURL::UTF8("internal://fake/fake.djvu");
+ pport->stream_pool=DataPool::create();
+ // Get all the data first
+ int length;
+ char buffer[1024];
+ while((length=str.read(buffer, 1024)))
+ pport->stream_pool->add_data(buffer, length);
+ pport->stream_pool->set_eof();
+ GP<DjVuDocument> doc = DjVuDocument::create_wait(pport->stream_url, (DjVuImageNotifier*)pport);
+ GP<DjVuImage> dimg=doc->get_page(-1, true, (DjVuImageNotifier*)pport);
+ file=dimg->get_djvu_file();
+ if (file->is_decode_stopped())
+ G_THROW( DataPool::Stop );
+ if (file->is_decode_failed())
+ G_THROW( ByteStream::EndOfFile ); // guess
+ if (!file->is_decode_ok())
+ G_THROW( ERR_MSG("DjVuImage.mult_error") );
+ DEBUG_MSG("decode DONE\n");
+}
+
+
+//// DJVUIMAGE: CHECKING
+
+static int
+compute_red(int w, int h, int rw, int rh)
+{
+ for (int red=1; red<16; red++)
+ if (((w+red-1)/red==rw) && ((h+red-1)/red==rh))
+ return red;
+ return 16;
+}
+
+
+int
+DjVuImage::is_legal_bilevel() const
+{
+ // Components
+ GP<DjVuInfo> info = get_info();
+ GP<JB2Image> fgjb = get_fgjb();
+ GP<IW44Image> bg44 = get_bg44();
+ GP<GPixmap> bgpm = get_bgpm();
+ GP<GPixmap> fgpm = get_fgpm();
+ // Check info
+ if (! info)
+ return 0;
+ int width = info->width;
+ int height = info->height;
+ if (! (width>0 && height>0))
+ return 0;
+ // Check fgjb
+ if (!fgjb)
+ return 0;
+ if (fgjb->get_width()!=width || fgjb->get_height()!=height)
+ return 0;
+ // Check that color information is not present.
+ if (bg44 || bgpm || fgpm)
+ return 0;
+ // Ok.
+ return 1;
+}
+
+int
+DjVuImage::is_legal_photo() const
+{
+ // Components
+ GP<DjVuInfo> info = get_info();
+ GP<JB2Image> fgjb = get_fgjb();
+ GP<IW44Image> bg44 = get_bg44();
+ GP<GPixmap> bgpm = get_bgpm();
+ GP<GPixmap> fgpm = get_fgpm();
+ // Check info
+ if (! info)
+ return 0;
+ int width = info->width;
+ int height = info->height;
+ if (! (width>0 && height>0))
+ return 0;
+ // Check that extra information is not present.
+ if (fgjb || fgpm)
+ return 0;
+ // Check bg44
+ if (bg44 && bg44->get_width()==width && bg44->get_height()==height)
+ return 1;
+ // Check bgpm
+ if (bgpm && (int)bgpm->columns()==width && (int)bgpm->rows()==height)
+ return 1;
+ // Ok.
+ return 0;
+}
+
+int
+DjVuImage::is_legal_compound() const
+{
+ // Components
+ GP<DjVuInfo> info = get_info();
+ GP<JB2Image> fgjb = get_fgjb();
+ GP<IW44Image> bg44 = get_bg44();
+ GP<GPixmap> bgpm = get_bgpm();
+ GP<GPixmap> fgpm = get_fgpm();
+ GP<DjVuPalette> fgbc = get_fgbc();
+ // Check size
+ if (! info)
+ return 0;
+ int width = info->width;
+ int height = info->height;
+ if (! (width>0 && height>0))
+ return 0;
+ // Check fgjb
+ if (!fgjb)
+ return 0;
+ if (fgjb->get_width()!=width || fgjb->get_height()!=height)
+ return 0;
+ // Check background
+ int bgred = 0;
+ if (bg44)
+ bgred = compute_red(width, height, bg44->get_width(), bg44->get_height());
+ else if (bgpm)
+ bgred = compute_red(width, height, bgpm->columns(), bgpm->rows());
+ if (bgred<1 || bgred>12)
+ return 0;
+ // Check foreground colors
+ int fgred = 0;
+ if (fgbc)
+ fgred = 1;
+ else if (fgpm)
+ fgred = compute_red(width, height, fgpm->columns(), fgpm->rows());
+ if (fgred<1 || fgred>12)
+ return 0;
+ // Check that all components are present
+ if (fgjb && bgred && fgred)
+ return 1;
+ // Unrecognized
+ return 0;
+}
+
+
+//// DJVUIMAGE: LOW LEVEL RENDERING
+
+GP<GBitmap>
+DjVuImage::get_bitmap(const GRect &rect,
+ int subsample, int align) const
+{
+ // Access image size
+ int width = get_real_width();
+ int height = get_real_height();
+ GP<JB2Image> fgjb = get_fgjb();
+ if ( width && height && fgjb &&
+ (fgjb->get_width() == width) &&
+ (fgjb->get_height() == height) )
+ {
+ return fgjb->get_bitmap(rect, subsample, align);
+ }
+ return 0;
+}
+
+GP<GPixmap>
+DjVuImage::get_bg_pixmap(const GRect &rect,
+ int subsample, double gamma) const
+{
+ GP<GPixmap> pm = 0;
+ // Access image size
+
+ GP<DjVuInfo> info = get_info();
+ int width = get_real_width();
+ int height = get_real_height();
+
+
+ if (width<=0 || height<=0 || !info) return 0;
+ // Compute gamma_correction
+ double gamma_correction = 1.0;
+ if (gamma > 0)
+ gamma_correction = gamma / info->gamma;
+ if (gamma_correction < 0.1)
+ gamma_correction = 0.1;
+ else if (gamma_correction > 10)
+ gamma_correction = 10;
+
+ // CASE1: Incremental BG IW44Image
+ GP<IW44Image> bg44 = get_bg44();
+ if (bg44)
+ {
+ int w = bg44->get_width();
+ int h = bg44->get_height();
+ // Avoid silly cases
+ if (w==0 || h==0 || width==0 || height==0)
+ return 0;
+ // Determine how much bg44 is reduced
+ int red = compute_red(width,height,w,h);
+ if (red<1 || red>12)
+ return 0;
+ // Handle pure downsampling cases
+ if (subsample == red)
+ pm = bg44->get_pixmap(1,rect);
+ else if (subsample == 2*red)
+ pm = bg44->get_pixmap(2,rect);
+ else if (subsample == 4*red)
+ pm = bg44->get_pixmap(4,rect);
+ else if (subsample == 8*red)
+ pm = bg44->get_pixmap(8,rect);
+ // Handle fractional downsampling case
+ else if (red*4 == subsample*3)
+ {
+ GRect nrect = rect;
+ GRect xrect = rect;
+ xrect.xmin = (xrect.xmin/3)*4;
+ xrect.ymin = (xrect.ymin/3)*4;
+ xrect.xmax = ((xrect.xmax+2)/3)*4;
+ xrect.ymax = ((xrect.ymax+2)/3)*4;
+ nrect.translate(-xrect.xmin*3/4, -xrect.ymin*3/4);
+ if (xrect.xmax > w)
+ xrect.xmax = w;
+ if (xrect.ymax > h)
+ xrect.ymax = h;
+ GP<GPixmap> ipm = bg44->get_pixmap(1,xrect);
+ pm = GPixmap::create();
+ pm->downsample43(ipm, &nrect);
+ }
+ // Handle all other cases with pixmapscaler
+ else
+ {
+ // find suitable power of two
+ int po2 = 16;
+ while (po2>1 && subsample<po2*red)
+ po2 >>= 1;
+ // setup pixmap scaler
+ int inw = (w+po2-1)/po2;
+ int inh = (h+po2-1)/po2;
+ int outw = (width+subsample-1)/subsample;
+ int outh = (height+subsample-1)/subsample;
+ GP<GPixmapScaler> gps=GPixmapScaler::create(inw, inh, outw, outh);
+ GPixmapScaler &ps=*gps;
+ ps.set_horz_ratio(red*po2, subsample);
+ ps.set_vert_ratio(red*po2, subsample);
+ // run pixmap scaler
+ GRect xrect;
+ ps.get_input_rect(rect,xrect);
+ GP<GPixmap> ipm = bg44->get_pixmap(po2,xrect);
+ pm = GPixmap::create();
+ ps.scale(xrect, *ipm, rect, *pm);
+ }
+ // Apply gamma correction
+ if (pm && gamma_correction!=1.0)
+ pm->color_correct(gamma_correction);
+ return pm;
+ }
+
+ // CASE 2: Raw background pixmap
+ GP<GPixmap> bgpm = get_bgpm();
+ if (bgpm)
+ {
+ int w = bgpm->columns();
+ int h = bgpm->rows();
+ // Avoid silly cases
+ if (w==0 || h==0 || width==0 || height==0)
+ return 0;
+ // Determine how much bgpm is reduced
+ int red = compute_red(width,height,w,h);
+ if (red<1 || red>12)
+ return 0;
+ // Handle pure downsampling cases
+ int ratio = subsample/red;
+ if (subsample==ratio*red && ratio>=1)
+ {
+ pm = GPixmap::create();
+ if (ratio == 1)
+ pm->init(*bgpm, rect);
+ else if (ratio > 1)
+ pm->downsample(bgpm, ratio, &rect);
+ }
+ // Handle all other cases with pixmapscaler
+ else
+ {
+ // setup pixmap scaler
+ int outw = (width+subsample-1)/subsample;
+ int outh = (height+subsample-1)/subsample;
+ GP<GPixmapScaler> gps=GPixmapScaler::create(w, h, outw, outh);
+ GPixmapScaler &ps=*gps;
+ ps.set_horz_ratio(red, subsample);
+ ps.set_vert_ratio(red, subsample);
+ // run pixmap scaler
+ pm = GPixmap::create();
+ GRect xrect(0,0,w,h);
+ ps.scale(xrect, *bgpm, rect, *pm);
+ }
+ // Apply gamma correction
+ if (pm && gamma_correction!=1.0)
+ pm->color_correct(gamma_correction);
+ return pm;
+ }
+
+ // FAILURE
+ return 0;
+}
+
+
+
+int
+DjVuImage::stencil(GPixmap *pm, const GRect &rect,
+ int subsample, double gamma) const
+{
+ // Warping and blending.
+ if (!pm)
+ return 0;
+ // Access components
+
+ GP<DjVuInfo> info = get_info();
+ int width = get_real_width();
+ int height = get_real_height();
+
+
+ if (width<=0 || height<=0 || !info) return 0;
+ GP<JB2Image> fgjb = get_fgjb();
+ GP<GPixmap> fgpm = get_fgpm();
+ GP<DjVuPalette> fgbc = get_fgbc();
+
+ // Compute gamma_correction
+ double gamma_correction = 1.0;
+ if (gamma > 0)
+ gamma_correction = gamma / info->gamma;
+ if (gamma_correction < 0.1)
+ gamma_correction = 0.1;
+ else if (gamma_correction > 10)
+ gamma_correction = 10;
+
+ // Compute alpha map and relevant JB2Image components
+ GList<int> components;
+ GP<GBitmap> bm;
+ if (fgjb)
+ {
+ JB2Image *jimg = fgjb;
+ if (! (width && height &&
+ jimg->get_width() == width &&
+ jimg->get_height() == height ) )
+ return 0;
+ // Decode bitmap
+ bm = GBitmap::create(rect.height(), rect.width());
+ bm->set_grays(1+subsample*subsample);
+ int rxmin = rect.xmin * subsample;
+ int rymin = rect.ymin * subsample;
+ for (int blitno = 0; blitno < jimg->get_blit_count(); blitno++)
+ {
+ const JB2Blit *pblit = jimg->get_blit(blitno);
+ const JB2Shape &pshape = jimg->get_shape(pblit->shapeno);
+ if (pshape.bits &&
+ pblit->left <= rect.xmax * subsample &&
+ pblit->bottom <= rect.ymax * subsample &&
+ pblit->left + (int)pshape.bits->columns() >= rect.xmin * subsample &&
+ pblit->bottom + (int)pshape.bits->rows() >= rect.ymin * subsample )
+ {
+ // Record component list
+ if (fgbc) components.append(blitno);
+ // Blit
+ bm->blit(pshape.bits,
+ pblit->left - rxmin, pblit->bottom - rymin,
+ subsample);
+ }
+ }
+ }
+
+
+ // TWO LAYER MODEL
+ if (bm && fgbc)
+ {
+ // Perform attenuation from scratch
+ pm->attenuate(bm, 0, 0);
+ // Check that fgbc has the correct size
+ JB2Image *jimg = fgjb;
+ DjVuPalette *fg = fgbc;
+ if (jimg->get_blit_count() != fg->colordata.size())
+ return 0;
+ // Copy and color correct palette
+ int palettesize = fg->size();
+ GTArray<GPixel> colors(0,palettesize-1);
+ for (int i=0; i<palettesize; i++)
+ fg->index_to_color(i, colors[i]);
+ GPixmap::color_correct(gamma_correction, colors, palettesize);
+ // Blit all components (one color at a time)
+ while (components.size() > 0)
+ {
+ GPosition nullpos;
+ GPosition pos = components;
+ int lastx = 0;
+ int colorindex = fg->colordata[components[pos]];
+ if (colorindex >= palettesize)
+ G_THROW( ERR_MSG("DjVuImage.corrupted") );
+ // Gather relevant components and relevant rectangle
+ GList<int> compset;
+ GRect comprect;
+ while (pos)
+ {
+ int blitno = components[pos];
+ const JB2Blit *pblit = jimg->get_blit(blitno);
+ if (pblit->left < lastx) break;
+ lastx = pblit->left;
+ if (fg->colordata[blitno] == colorindex)
+ {
+ const JB2Shape &pshape = jimg->get_shape(pblit->shapeno);
+ GRect rect(pblit->left, pblit->bottom,
+ pshape.bits->columns(), pshape.bits->rows());
+ comprect.recthull(comprect, rect);
+ compset.insert_before(nullpos, components, pos);
+ continue;
+ }
+ ++pos;
+ }
+ // Round alpha map rectangle
+ comprect.xmin = comprect.xmin / subsample;
+ comprect.ymin = comprect.ymin / subsample;
+ comprect.xmax = (comprect.xmax+subsample-1) / subsample;
+ comprect.ymax = (comprect.ymax+subsample-1) / subsample;
+ comprect.intersect(comprect, rect);
+ // Compute alpha map for that color
+ bm = 0;
+ bm = GBitmap::create(comprect.height(), comprect.width());
+ bm->set_grays(1+subsample*subsample);
+ int rxmin = comprect.xmin * subsample;
+ int rymin = comprect.ymin * subsample;
+ for (pos=compset; pos; ++pos)
+ {
+ int blitno = compset[pos];
+ const JB2Blit *pblit = jimg->get_blit(blitno);
+ const JB2Shape &pshape = jimg->get_shape(pblit->shapeno);
+ bm->blit(pshape.bits,
+ pblit->left - rxmin, pblit->bottom - rymin,
+ subsample);
+ }
+ // Blend color into background pixmap
+ pm->blit(bm, comprect.xmin-rect.xmin, comprect.ymin-rect.ymin, &colors[colorindex]);
+ }
+ return 1;
+ }
+
+
+ // THREE LAYER MODEL
+ if (bm && fgpm)
+ {
+ // This follows fig. 4 in Adelson "Layered representations for image
+ // coding" (1991) http://www-bcs.mit.edu/people/adelson/papers.html.
+ // The properly warped background is already in PM. The properly warped
+ // alpha map is already in BM. We just have to warp the foreground and
+ // perform alpha blending.
+#ifdef SIMPLE_THREE_LAYER_RENDERING
+ int w = fgpm->columns();
+ int h = fgpm->rows();
+ // Determine foreground reduction
+ int red = compute_red(width,height, w, h);
+ if (red<1 || red>12)
+ return 0;
+ // Warp foreground pixmap
+ GPixmapScaler ps(w,h,width/subsample+1,height/subsample+1);
+ ps.set_horz_ratio(red,subsample);
+ ps.set_vert_ratio(red,subsample);
+ GP<GPixmap> nfg = new GPixmap;
+ GRect provided(0,0,w,h);
+ ps.scale(provided, *fgpm, rect, *nfg);
+ // Attenuate background and blit
+ nfg->color_correct(gamma_correction);
+ pm->blend(bm, 0, 0, nfg); // blend == attenuate + blit
+ return 1;
+#else
+ // Things are now a little bit more complex because the convenient
+ // function GPixmap::stencil() simultaneously upsamples the foreground
+ // by an integer factor and performs the alpha blending. We have
+ // to determine how and when this facility can be used.
+ int w = fgpm->columns();
+ int h = fgpm->rows();
+ // Determine foreground reduction
+ int red = compute_red(width,height,w,h);
+ if (red<1 || red>12)
+ return 0;
+ // Try supersampling foreground pixmap by an integer factor
+ int supersample = ( red>subsample ? red/subsample : 1);
+ int wantedred = supersample*subsample;
+ // Try simple foreground upsampling
+ if (red == wantedred)
+ {
+ // Simple foreground upsampling is enough.
+ pm->stencil(bm, fgpm, supersample, &rect, gamma_correction);
+ return 1;
+ }
+ else
+ {
+ // Must pre-warp foreground pixmap
+ GP<GPixmap> nfg;
+ int desw = (w*red+wantedred-1)/wantedred;
+ int desh = (h*red+wantedred-1)/wantedred;
+ // Cache rescaled fgpm for speed
+ static const DjVuImage *tagimage = 0;
+ static const GPixmap *tagfgpm = 0;
+ static GP<GPixmap> cachednfg = 0;
+ // Check whether cached fgpm applies.
+ if ( cachednfg && this==tagimage && fgpm==tagfgpm
+ && desw==(int)cachednfg->columns()
+ && desh==(int)cachednfg->rows() )
+ {
+ nfg = cachednfg;
+ }
+ else
+ {
+ GP<GPixmapScaler> gps=GPixmapScaler::create(w,h,desw,desh);
+ GPixmapScaler &ps=*gps;
+ ps.set_horz_ratio(red, wantedred);
+ ps.set_vert_ratio(red, wantedred);
+ nfg = GPixmap::create();
+ GRect provided(0,0,w,h);
+ GRect desired(0,0,desw,desh);
+ ps.scale(provided, *fgpm, desired, *nfg);
+ }
+ // Use combined warp+blend function
+ pm->stencil(bm, nfg, supersample, &rect, gamma_correction);
+ // Cache
+ tagimage = this;
+ tagfgpm = fgpm;
+ cachednfg = nfg;
+ return 1;
+ }
+#endif
+ }
+
+ // FAILURE
+ return 0;
+}
+
+
+GP<GPixmap>
+DjVuImage::get_fg_pixmap(const GRect &rect,
+ int subsample, double gamma) const
+{
+ // Obtain white background pixmap
+ GP<GPixmap> pm;
+ // Access components
+ const int width = get_real_width();
+ const int height = get_real_height();
+ if (width && height)
+ {
+ pm = GPixmap::create(rect.height(),rect.width(), &GPixel::WHITE);
+ if (!stencil(pm, rect, subsample, gamma))
+ pm=0;
+ }
+ return pm;
+}
+
+
+GP<GPixmap>
+DjVuImage::get_pixmap(const GRect &rect, int subsample, double gamma) const
+{
+ // Get background
+ GP<GPixmap> pm = get_bg_pixmap(rect, subsample, gamma);
+ // Superpose foreground
+ if (! stencil(pm, rect, subsample, gamma))
+ // Avoid ugly progressive display (hack)
+ if (get_fgjb()) return 0;
+ // Return
+ return pm;
+}
+
+
+//// DJVUIMAGE: RENDERING (ARBITRARY SCALE)
+
+typedef GP<GBitmap>(DjVuImage::*BImager)(const GRect &, int, int) const;
+typedef GP<GPixmap>(DjVuImage::*PImager)(const GRect &, int, double) const;
+
+static GP<GBitmap>
+do_bitmap(const DjVuImage &dimg, BImager get,
+ const GRect &inrect, const GRect &inall, int align )
+{
+ GRect rect=inrect;
+ GRect all=inall;
+///* rotate code
+ if( dimg.get_rotate()%4 )
+ {
+ GRectMapper mapper;
+ mapper.rotate((4-dimg.get_rotate())%4);
+ mapper.map(rect);
+ mapper.map(all);
+ }
+///* rotate code ends
+
+ // Sanity
+ if (! ( all.contains(rect.xmin, rect.ymin) &&
+ all.contains(rect.xmax-1, rect.ymax-1) ))
+ G_THROW( ERR_MSG("DjVuImage.bad_rect") );
+ // Check for integral reduction
+ int red;
+ int w = dimg.get_real_width();
+ int h = dimg.get_real_height();
+
+ int rw = all.width();
+ int rh = all.height();
+ GRect zrect = rect;
+ zrect.translate(-all.xmin, -all.ymin);
+ for (red=1; red<=15; red++)
+ if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red)
+ {
+ GP<GBitmap> bm=(dimg.*get)(zrect, red, align);
+ if(bm)
+ return bm->rotate((4-dimg.get_rotate())%4);
+ else
+ return NULL;
+ }
+ // Find best reduction
+ for (red=15; red>1; red--)
+ if ( (rw*red < w && rh*red < h) ||
+ (rw*red*3 < w || rh*red*3 < h) )
+ break;
+ // Setup bitmap scaler
+ if (! (w && h)) return 0;
+ GP<GBitmapScaler> gbs=GBitmapScaler::create();
+ GBitmapScaler &bs=*gbs;
+ bs.set_input_size( (w+red-1)/red, (h+red-1)/red );
+ bs.set_output_size( rw, rh );
+ bs.set_horz_ratio( rw*red, w );
+ bs.set_vert_ratio( rh*red, h );
+ // Scale
+ GRect srect;
+ bs.get_input_rect(zrect, srect);
+ GP<GBitmap> sbm = (dimg.*get)(srect, red, 1);
+ if (!sbm) return 0;
+ int border = ((zrect.width() + align - 1) & ~(align - 1)) - zrect.width();
+ GP<GBitmap> bm = GBitmap::create(zrect.height(), zrect.width(), border);
+ bs.scale(srect, *sbm, zrect, *bm);
+ if( bm )
+ return bm->rotate((4-dimg.get_rotate())%4);
+ else
+ return NULL;
+}
+
+static GP<GPixmap>
+do_pixmap(const DjVuImage &dimg, PImager get,
+ const GRect &inrect, const GRect &inall, double gamma )
+{
+
+ GRect rect=inrect;
+ GRect all=inall;
+///* rotate code
+ if( dimg.get_rotate()%4 )
+ {
+ GRectMapper mapper;
+ mapper.rotate((4-dimg.get_rotate())%4);
+ mapper.map(rect);
+ mapper.map(all);
+ }
+///* rotate code ends
+
+ // Sanity
+ if (! ( all.contains(rect.xmin, rect.ymin) &&
+ all.contains(rect.xmax-1, rect.ymax-1) ))
+ G_THROW( ERR_MSG("DjVuImage.bad_rect2") );
+ // Check for integral reduction
+ int red, w=0, h=0, rw=0, rh=0;
+ w = dimg.get_real_width();
+ h = dimg.get_real_height();
+
+
+ rw = all.width();
+ rh = all.height();
+ GRect zrect = rect;
+ zrect.translate(-all.xmin, -all.ymin);
+ for (red=1; red<=15; red++)
+ if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red)
+ {
+ GP<GPixmap> pm = (dimg.*get)(zrect, red, gamma);
+ if( pm )
+ return pm->rotate((4-dimg.get_rotate())%4);
+ else
+ return NULL;
+ }
+ // These reductions usually go faster (improve!)
+ static int fastred[] = { 12,6,4,3,2,1 };
+ // Find best reduction
+ for (int i=0; (red=fastred[i])>1; i++)
+ if ( (rw*red < w && rh*red < h) ||
+ (rw*red*3 < w || rh*red*3 < h) )
+ break;
+ // Setup pixmap scaler
+ if (w<0 || h<0) return 0;
+ GP<GPixmapScaler> gps=GPixmapScaler::create();
+ GPixmapScaler &ps=*gps;
+ ps.set_input_size( (w+red-1)/red, (h+red-1)/red );
+ ps.set_output_size( rw, rh );
+ ps.set_horz_ratio( rw*red, w );
+ ps.set_vert_ratio( rh*red, h );
+ // Scale
+ GRect srect;
+ ps.get_input_rect(zrect, srect);
+ GP<GPixmap> spm = (dimg.*get)(srect, red, gamma);
+ if (!spm) return 0;
+ GP<GPixmap> pm = GPixmap::create();
+ ps.scale(srect, *spm, zrect, *pm);
+ if(pm)
+ return pm->rotate((4-dimg.get_rotate())%4);
+ else
+ return NULL;
+}
+
+GP<GPixmap>
+DjVuImage::get_pixmap(const GRect &rect, const GRect &all, double gamma) const
+{
+ return do_pixmap(*this, & DjVuImage::get_pixmap, rect, all, gamma);
+}
+
+GP<GBitmap>
+DjVuImage::get_bitmap(const GRect &rect, const GRect &all, int align) const
+{
+ return do_bitmap(*this, & DjVuImage::get_bitmap, rect, all, align);
+}
+
+GP<GPixmap>
+DjVuImage::get_bg_pixmap(const GRect &rect, const GRect &all, double gamma) const
+{
+ return do_pixmap(*this, & DjVuImage::get_bg_pixmap, rect, all, gamma);
+}
+
+GP<GPixmap>
+DjVuImage::get_fg_pixmap(const GRect &rect, const GRect &all, double gamma) const
+{
+ return do_pixmap(*this, & DjVuImage::get_fg_pixmap, rect, all, gamma);
+}
+
+int
+DjVuImage::get_rotate() const
+{
+ return (rotate_count<0)?0:rotate_count;
+}
+
+void
+DjVuImage::init_rotate(const DjVuInfo &info)
+{
+ rotate_count=((360-GRect::findangle(info.orientation))/90)%4;
+}
+
+void DjVuImage::set_rotate(int count)
+{
+ rotate_count=((count%4)+4)%4;
+}
+
+GP<DjVuAnno>
+DjVuImage::get_decoded_anno()
+{
+ GP<DjVuAnno> djvuanno = DjVuAnno::create();
+ GP<ByteStream> bs=get_anno();
+ if( bs )
+ {
+ djvuanno->decode(bs);
+
+ const int rotate_count=get_rotate();
+ if( rotate_count % 4 )
+ {
+ ///map hyperlinks correctly for rotation
+ GRect input, output;
+ input = GRect(0,0,get_width(), get_height());
+ output = GRect(0,0, get_real_width(), get_real_height());
+
+ GRectMapper mapper;
+ mapper.clear();
+ mapper.set_input(input);
+ mapper.set_output(output);
+ mapper.rotate((4-rotate_count)%4);
+
+ GPList<GMapArea> &list=djvuanno->ant->map_areas;
+ for(GPosition pos=list;pos;++pos)
+ {
+ list[pos]->unmap(mapper);
+ }
+ }
+ return djvuanno;
+ }
+ else
+ return NULL;
+}
+
+
+void
+DjVuImage::map(GRect &rect) const
+{
+ GRect input, output;
+ const int rotate_count=get_rotate();
+ if(rotate_count%4)
+ {
+ input = GRect(0,0,get_width(), get_height());
+ output = GRect(0,0, get_real_width(), get_real_height());
+
+ GRectMapper mapper;
+ mapper.clear();
+ mapper.set_input(input);
+ mapper.set_output(output);
+ mapper.rotate((4-rotate_count)%4);
+ mapper.map(rect);
+ }
+}
+
+void
+DjVuImage::unmap(GRect &rect) const
+{
+ GRect input, output;
+ const int rotate_count=get_rotate();
+ if(rotate_count%4)
+ {
+ input = GRect(0,0,get_width(), get_height());
+ output = GRect(0,0, get_real_width(), get_real_height());
+
+ GRectMapper mapper;
+ mapper.clear();
+ mapper.set_input(input);
+ mapper.set_output(output);
+ mapper.rotate((4-rotate_count)%4);
+ mapper.unmap(rect);
+ }
+}
+
+void
+DjVuImage::map(int &x, int &y) const
+{
+ GRect input, output;
+ const int rotate_count=get_rotate();
+ if(rotate_count%4)
+ {
+ input = GRect(0,0,get_width(), get_height());
+ output = GRect(0,0, get_real_width(), get_real_height());
+
+ GRectMapper mapper;
+ mapper.clear();
+ mapper.set_input(input);
+ mapper.set_output(output);
+ mapper.rotate((4-rotate_count)%4);
+ mapper.map(x, y);
+ }
+}
+
+void
+DjVuImage::unmap(int &x, int &y) const
+{
+ GRect input, output;
+ const int rotate_count=get_rotate();
+ if(rotate_count%4)
+ {
+ input = GRect(0,0,get_width(), get_height());
+ output = GRect(0,0, get_real_width(), get_real_height());
+
+ GRectMapper mapper;
+ mapper.clear();
+ mapper.set_input(input);
+ mapper.set_output(output);
+ mapper.rotate((4-rotate_count)%4);
+ mapper.unmap(x, y);
+ }
+}
+
+bool
+DjVuImage::wait_for_complete_decode(void)
+{
+ if (file)
+ {
+ file->resume_decode(true);
+ return file->is_decode_ok();
+ }
+ return 0;
+}
+
+// Write out a DjVuXML object tag and map tag.
+void
+DjVuImage::writeXML(ByteStream &str_out,const GURL &doc_url,const int flags) const
+{
+ const int height=get_height();
+
+ static const char *Object="<OBJECT data=\"";
+ const GURL url(get_djvu_file()->get_url());
+ const GUTF8String pagename(url.fname());
+ GUTF8String page_param;
+ if(doc_url.is_valid() && !doc_url.is_empty() && (doc_url != url))
+ {
+ str_out.writestring(Object+doc_url.get_string());
+ page_param="<PARAM name=\"PAGE\" value=\""+pagename+"\" />\n";
+ }else
+ {
+ str_out.writestring(Object+doc_url.get_string());
+ }
+ str_out.writestring("\" type=\""+get_mimetype()+"\" height=\""
+ +GUTF8String(height)+"\" width=\""+GUTF8String(get_width())
+ +"\" usemap=\""+pagename.toEscaped()+"\" >\n");
+ if(!(flags & NOINFO))
+ {
+ const GP<DjVuInfo> info(get_info());
+ if(info)
+ {
+ info->writeParam(str_out);
+ }
+ }
+ str_out.writestring(page_param);
+ const GP<DjVuAnno> anno(DjVuAnno::create());
+ if(!(flags & NOINFO)||!(flags&NOMAP))
+ {
+ const GP<ByteStream> anno_str(get_anno());
+ if(anno_str)
+ {
+ anno->decode(anno_str);
+ }
+ if(!(flags & NOINFO))
+ {
+ anno->writeParam(str_out);
+ }
+ }
+ if(!(flags & NOTEXT))
+ {
+ const GP<DjVuText> text(DjVuText::create());
+ {
+ const GP<ByteStream> text_str(get_text());
+ if(text_str)
+ {
+ text->decode(text_str);
+ }
+ text->writeText(str_out,height);
+ }
+ }
+ if(!(flags & NOMETA))
+ {
+ const GP<ByteStream> meta_str(get_meta());
+ if(meta_str)
+ {
+ GP<IFFByteStream> giff=IFFByteStream::create(meta_str);
+ IFFByteStream &iff=*giff;
+ GUTF8String chkid;
+ while( iff.get_chunk(chkid))
+ {
+ GP<ByteStream> gbs(iff.get_bytestream());
+ if(chkid == "METa")
+ {
+ str_out.copy(*gbs);
+ //str_out.writestring(gbs->getAsUTF8());
+ }else if(chkid == "METz")
+ {
+ gbs=BSByteStream::create(gbs);
+ str_out.copy(*gbs);
+ //str_out.writestring(gbs->getAsUTF8());
+ }
+ iff.close_chunk();
+ }
+ }
+ }
+ str_out.writestring(GUTF8String("</OBJECT>\n"));
+ if(!(flags & NOMAP))
+ {
+ anno->writeMap(str_out,pagename,height);
+ }
+}
+
+// Write out a DjVuXML object tag and map tag.
+void
+DjVuImage::writeXML(ByteStream &str_out) const
+{
+ writeXML(str_out,GURL());
+}
+
+// Write out a DjVuXML object tag and map tag.
+GUTF8String
+DjVuImage::get_XML(const GURL &doc_url,const int flags) const
+{
+ GP<ByteStream> gbs(ByteStream::create());
+ ByteStream &bs=*gbs;
+ writeXML(bs,doc_url);
+ bs.seek(0L);
+ return bs.getAsUTF8();
+}
+
+// Write out a DjVuXML object tag and map tag.
+GUTF8String
+DjVuImage::get_XML(void) const
+{
+ return get_XML(GURL());
+}
+
+
+#ifdef HAVE_NAMESPACES
+}
+# ifndef NOT_USING_DJVU_NAMESPACE
+using namespace DJVU;
+# endif
+#endif