00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063
00064 #include "DjVuImage.h"
00065 #include "GScaler.h"
00066 #include "DjVuDocument.h"
00067 #include "DjVuPalette.h"
00068 #include "GContainer.h"
00069 #include "GSmartPointer.h"
00070 #include "JB2Image.h"
00071 #include "IW44Image.h"
00072 #include "DataPool.h"
00073 #include "ByteStream.h"
00074 #include "GMapAreas.h"
00075 #include "DjVuText.h"
00076 #include "IFFByteStream.h"
00077 #include "BSByteStream.h"
00078 #include "debug.h"
00079 #include <stdarg.h>
00080
00081
00082 #ifdef HAVE_NAMESPACES
00083 namespace DJVU {
00084 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00085 }
00086 #endif
00087 #endif
00088
00089
00090
00092
00093 DjVuImage::DjVuImage(void)
00094 : rotate_count(-1),relayout_sent(false)
00095 {
00096 }
00097
00098 void
00099 DjVuImage::connect(const GP<DjVuFile> & xfile)
00100 {
00101 file=xfile;
00102 DjVuPort::get_portcaster()->add_route(file, this);
00103 }
00104
00105
00106
00107
00109
00110 GP<DjVuInfo>
00111 DjVuImage::get_info(const GP<DjVuFile> & file) const
00112 {
00113 if (file->info)
00114 {
00115 if(rotate_count<0)
00116 {
00117 const_cast<DjVuImage *>(this)->init_rotate(*(file->info));
00118 }
00119 return file->info;
00120 }
00121 GPList<DjVuFile> list=file->get_included_files();
00122 for(GPosition pos=list;pos;++pos)
00123 {
00124 GP<DjVuInfo> info=get_info(list[pos]);
00125 if (info)
00126 {
00127 if(rotate_count<0)
00128 {
00129 const_cast<DjVuImage *>(this)->init_rotate(*(file->info));
00130 }
00131 return info;
00132 }
00133 }
00134 return 0;
00135 }
00136
00137 GP<IW44Image>
00138 DjVuImage::get_bg44(const GP<DjVuFile> & file) const
00139 {
00140 if (file->bg44)
00141 return file->bg44;
00142 GPList<DjVuFile> list=file->get_included_files();
00143 for(GPosition pos=list;pos;++pos)
00144 {
00145 GP<IW44Image> bg44=get_bg44(list[pos]);
00146 if (bg44)
00147 return bg44;
00148 }
00149 return 0;
00150 }
00151
00152 GP<GPixmap>
00153 DjVuImage::get_bgpm(const GP<DjVuFile> & file) const
00154 {
00155 if (file->bgpm)
00156 return file->bgpm;
00157 GPList<DjVuFile> list=file->get_included_files();
00158 for(GPosition pos=list;pos;++pos)
00159 {
00160 GP<GPixmap> bgpm=get_bgpm(list[pos]);
00161 if (bgpm) return bgpm;
00162 }
00163 return 0;
00164 }
00165
00166 GP<JB2Image>
00167 DjVuImage::get_fgjb(const GP<DjVuFile> & file) const
00168 {
00169 if (file->fgjb)
00170 return file->fgjb;
00171 GPList<DjVuFile> list=file->get_included_files();
00172 for(GPosition pos=list;pos;++pos)
00173 {
00174 GP<JB2Image> fgjb=get_fgjb(list[pos]);
00175 if (fgjb)
00176 return fgjb;
00177 }
00178 return 0;
00179 }
00180
00181 GP<GPixmap>
00182 DjVuImage::get_fgpm(const GP<DjVuFile> & file) const
00183 {
00184 if (file->fgpm)
00185 return file->fgpm;
00186 GPList<DjVuFile> list=file->get_included_files();
00187 for(GPosition pos=list;pos;++pos)
00188 {
00189 GP<GPixmap> fgpm=get_fgpm(list[pos]);
00190 if (fgpm)
00191 return fgpm;
00192 }
00193 return 0;
00194 }
00195
00196 GP<DjVuPalette>
00197 DjVuImage::get_fgbc(const GP<DjVuFile> & file) const
00198 {
00199 if (file->fgbc)
00200 return file->fgbc;
00201 GPList<DjVuFile> list=file->get_included_files();
00202 for(GPosition pos=list;pos;++pos)
00203 {
00204 GP<DjVuPalette> fgbc=get_fgbc(list[pos]);
00205 if (fgbc) return fgbc;
00206 }
00207 return 0;
00208 }
00209
00210 GP<DjVuInfo>
00211 DjVuImage::get_info() const
00212 {
00213 if (file)
00214 {
00215 return get_info(file);
00216 }else
00217 {
00218 return 0;
00219 }
00220 }
00221
00222 GP<ByteStream>
00223 DjVuImage::get_anno() const
00224 {
00225 GP<ByteStream> out = ByteStream::create();
00226 ByteStream &mbs = *out;
00227 if (file)
00228 {
00229 file->merge_anno(mbs);
00230 }
00231 mbs.seek(0);
00232 if(!mbs.size())
00233 {
00234 out=0;
00235 }
00236 return out;
00237 }
00238
00239 GP<ByteStream>
00240 DjVuImage::get_text() const
00241 {
00242 GP<ByteStream> out = ByteStream::create();
00243 ByteStream &mbs = *out;
00244 if (file)
00245 {
00246 file->get_text(mbs);
00247 }
00248 mbs.seek(0);
00249 if(!mbs.size())
00250 {
00251 out=0;
00252 }
00253 return out;
00254 }
00255
00256 GP<ByteStream>
00257 DjVuImage::get_meta() const
00258 {
00259 GP<ByteStream> out = ByteStream::create();
00260 ByteStream &mbs = *out;
00261 if (file)
00262 {
00263 file->get_meta(mbs);
00264 }
00265 mbs.seek(0);
00266 if(!mbs.size())
00267 {
00268 out=0;
00269 }
00270 return out;
00271 }
00272
00273 GP<IW44Image>
00274 DjVuImage::get_bg44() const
00275 {
00276 if (file)
00277 return get_bg44(file);
00278 else
00279 return 0;
00280 }
00281
00282 GP<GPixmap>
00283 DjVuImage::get_bgpm() const
00284 {
00285 if (file)
00286 return get_bgpm(file);
00287 else
00288 return 0;
00289 }
00290
00291 GP<JB2Image>
00292 DjVuImage::get_fgjb() const
00293 {
00294 if (file)
00295 return get_fgjb(file);
00296 else
00297 return 0;
00298 }
00299
00300 GP<GPixmap>
00301 DjVuImage::get_fgpm() const
00302 {
00303 if (file)
00304 return get_fgpm(file);
00305 else
00306 return 0;
00307 }
00308
00309 GP<DjVuPalette>
00310 DjVuImage::get_fgbc() const
00311 {
00312 if (file)
00313 return get_fgbc(file);
00314 else
00315 return 0;
00316 }
00317
00318 int
00319 DjVuImage::get_width() const
00320 {
00321 GP<DjVuInfo> info=get_info();
00322 return info?((rotate_count&1)?(info->height):(info->width)):0;
00323 }
00324
00325 int
00326 DjVuImage::get_height() const
00327 {
00328 GP<DjVuInfo> info=get_info();
00329 return info?((rotate_count&1)?(info->width):(info->height)):0;
00330 }
00331
00332 int
00333 DjVuImage::get_real_width() const
00334 {
00335 GP<DjVuInfo> info=get_info();
00336 return info ? info->width : 0;
00337 }
00338
00339 int
00340 DjVuImage::get_real_height() const
00341 {
00342 GP<DjVuInfo> info=get_info();
00343 return info ? info->height : 0;
00344 }
00345
00346 int
00347 DjVuImage::get_version() const
00348 {
00349 GP<DjVuInfo> info=get_info();
00350 return info ? info->version : DJVUVERSION;
00351 }
00352
00353 int
00354 DjVuImage::get_dpi() const
00355 {
00356 GP<DjVuInfo> info=get_info();
00357 return info ? info->dpi : 300;
00358 }
00359
00360 int
00361 DjVuImage::get_rounded_dpi() const
00362 {
00363 return (get_dpi()+5)/10*10;
00364 #if 0
00365
00366
00367 int dpi=get_dpi();
00368 if (dpi>700) return dpi;
00369
00370 const int std_dpi[]={ 25, 50, 75, 100, 150, 300, 600 };
00371 const int std_dpis=sizeof(std_dpi)/sizeof(std_dpi[0]);
00372 int min_dist=abs(dpi-std_dpi[0]);
00373 int min_idx=0;
00374 for(int i=1;i<std_dpis;i++)
00375 if (abs(std_dpi[i]-dpi)<min_dist)
00376 {
00377 min_dist=abs(std_dpi[i]-dpi);
00378 min_idx=i;
00379 };
00380 return std_dpi[min_idx];
00381 #endif
00382 }
00383
00384 double
00385 DjVuImage::get_gamma() const
00386 {
00387 GP<DjVuInfo> info=get_info();
00388 return info ? info->gamma : 2.2;
00389 }
00390
00391 GUTF8String
00392 DjVuImage::get_mimetype() const
00393 {
00394 return file ? file->mimetype : GUTF8String();
00395 }
00396
00397
00399
00400 GUTF8String
00401 DjVuImage::get_short_description() const
00402 {
00403 GUTF8String msg = "Empty";
00404 int width = get_width();
00405 int height = get_height();
00406 if (width && height)
00407 if (file && file->file_size>100)
00408
00409 msg.format( ERR_MSG("DjVuImage.short1") "\t%d\t%d\t%0.1f", width, height, file->file_size/1024.0 );
00410 else
00411
00412 msg.format( ERR_MSG("DjVuImage.short2") "\t%d\t%d", width, height );
00413 return msg;
00414 }
00415
00416 GUTF8String
00417 DjVuImage::get_long_description() const
00418 {
00419 return file?(file->description):GUTF8String();
00420 }
00421
00422
00423 void
00424 DjVuImage::notify_chunk_done(const DjVuPort *, const GUTF8String & name)
00425 {
00426 if (!relayout_sent &&
00427 ( !name.cmp("INFO", 4) ||
00428 !name.cmp("PMxx", 2) ||
00429 !name.cmp("BMxx", 2) ) )
00430 {
00431 DjVuPort::get_portcaster()->notify_relayout(this);
00432 relayout_sent=true;
00433 }
00434 else if (!name.cmp("Sxxx", 1) ||
00435 !name.cmp("BGxx", 2) ||
00436 !name.cmp("FGxx", 2) ||
00437 !name.cmp("BMxx", 2) ||
00438 !name.cmp("PMxx", 2) )
00439 DjVuPort::get_portcaster()->notify_redisplay(this);
00440 }
00441
00442
00443
00444
00445
00446
00448
00449 DjVuInterface::~DjVuInterface()
00450 {
00451 }
00452
00453 class DjVuImageNotifier : public DjVuPort
00454 {
00455 friend class DjVuImage;
00456 DjVuInterface *notifier;
00457 GP<DataPool> stream_pool;
00458 GURL stream_url;
00459 public:
00460 DjVuImageNotifier(DjVuInterface *notifier);
00461 GP<DataPool> request_data(const DjVuPort *src, const GURL & url);
00462 void notify_chunk_done(const DjVuPort *, const GUTF8String &name);
00463 void notify_redisplay(const class DjVuImage * source);
00464 void notify_relayout(const class DjVuImage * source);
00465 };
00466
00467 DjVuImageNotifier::DjVuImageNotifier(DjVuInterface *notifier)
00468 : notifier(notifier)
00469 {
00470 }
00471
00472 GP<DataPool>
00473 DjVuImageNotifier::request_data(const DjVuPort *src, const GURL & url)
00474 {
00475 if (url!=stream_url)
00476 G_THROW( ERR_MSG("DjVuImage.not_decode") );
00477 return stream_pool;
00478 }
00479
00480 void
00481 DjVuImageNotifier::notify_redisplay(const class DjVuImage * source)
00482 {
00483 if (notifier)
00484 notifier->notify_redisplay();
00485 }
00486
00487 void
00488 DjVuImageNotifier::notify_relayout(const class DjVuImage * source)
00489 {
00490 if (notifier)
00491 notifier->notify_relayout();
00492 }
00493
00494 void
00495 DjVuImageNotifier::notify_chunk_done(const DjVuPort *, const GUTF8String &name)
00496 {
00497 if (notifier)
00498 notifier->notify_chunk(name, "" );
00499 }
00500
00501 void
00502 DjVuImage::decode(ByteStream & str, DjVuInterface *notifier)
00503 {
00504 DEBUG_MSG("DjVuImage::decode(): decoding old way...\n");
00505 DEBUG_MAKE_INDENT(3);
00506 if (file)
00507 G_THROW( ERR_MSG("DjVuImage.bad_call") );
00508 GP<DjVuImageNotifier> pport = new DjVuImageNotifier(notifier);
00509 pport->stream_url=GURL::UTF8("internal://fake/fake.djvu");
00510 pport->stream_pool=DataPool::create();
00511
00512 int length;
00513 char buffer[1024];
00514 while((length=str.read(buffer, 1024)))
00515 pport->stream_pool->add_data(buffer, length);
00516 pport->stream_pool->set_eof();
00517 GP<DjVuDocument> doc = DjVuDocument::create_wait(pport->stream_url, (DjVuImageNotifier*)pport);
00518 GP<DjVuImage> dimg=doc->get_page(-1, true, (DjVuImageNotifier*)pport);
00519 file=dimg->get_djvu_file();
00520 if (file->is_decode_stopped())
00521 G_THROW( DataPool::Stop );
00522 if (file->is_decode_failed())
00523 G_THROW( ByteStream::EndOfFile );
00524 if (!file->is_decode_ok())
00525 G_THROW( ERR_MSG("DjVuImage.mult_error") );
00526 DEBUG_MSG("decode DONE\n");
00527 }
00528
00529
00531
00532 static int
00533 compute_red(int w, int h, int rw, int rh)
00534 {
00535 for (int red=1; red<16; red++)
00536 if (((w+red-1)/red==rw) && ((h+red-1)/red==rh))
00537 return red;
00538 return 16;
00539 }
00540
00541
00542 int
00543 DjVuImage::is_legal_bilevel() const
00544 {
00545
00546 GP<DjVuInfo> info = get_info();
00547 GP<JB2Image> fgjb = get_fgjb();
00548 GP<IW44Image> bg44 = get_bg44();
00549 GP<GPixmap> bgpm = get_bgpm();
00550 GP<GPixmap> fgpm = get_fgpm();
00551
00552 if (! info)
00553 return 0;
00554 int width = info->width;
00555 int height = info->height;
00556 if (! (width>0 && height>0))
00557 return 0;
00558
00559 if (!fgjb)
00560 return 0;
00561 if (fgjb->get_width()!=width || fgjb->get_height()!=height)
00562 return 0;
00563
00564 if (bg44 || bgpm || fgpm)
00565 return 0;
00566
00567 return 1;
00568 }
00569
00570 int
00571 DjVuImage::is_legal_photo() const
00572 {
00573
00574 GP<DjVuInfo> info = get_info();
00575 GP<JB2Image> fgjb = get_fgjb();
00576 GP<IW44Image> bg44 = get_bg44();
00577 GP<GPixmap> bgpm = get_bgpm();
00578 GP<GPixmap> fgpm = get_fgpm();
00579
00580 if (! info)
00581 return 0;
00582 int width = info->width;
00583 int height = info->height;
00584 if (! (width>0 && height>0))
00585 return 0;
00586
00587 if (fgjb || fgpm)
00588 return 0;
00589
00590 if (bg44 && bg44->get_width()==width && bg44->get_height()==height)
00591 return 1;
00592
00593 if (bgpm && (int)bgpm->columns()==width && (int)bgpm->rows()==height)
00594 return 1;
00595
00596 return 0;
00597 }
00598
00599 int
00600 DjVuImage::is_legal_compound() const
00601 {
00602
00603 GP<DjVuInfo> info = get_info();
00604 GP<JB2Image> fgjb = get_fgjb();
00605 GP<IW44Image> bg44 = get_bg44();
00606 GP<GPixmap> bgpm = get_bgpm();
00607 GP<GPixmap> fgpm = get_fgpm();
00608 GP<DjVuPalette> fgbc = get_fgbc();
00609
00610 if (! info)
00611 return 0;
00612 int width = info->width;
00613 int height = info->height;
00614 if (! (width>0 && height>0))
00615 return 0;
00616
00617 if (!fgjb)
00618 return 0;
00619 if (fgjb->get_width()!=width || fgjb->get_height()!=height)
00620 return 0;
00621
00622 int bgred = 0;
00623 if (bg44)
00624 bgred = compute_red(width, height, bg44->get_width(), bg44->get_height());
00625 else if (bgpm)
00626 bgred = compute_red(width, height, bgpm->columns(), bgpm->rows());
00627 if (bgred<1 || bgred>12)
00628 return 0;
00629
00630 int fgred = 0;
00631 if (fgbc)
00632 fgred = 1;
00633 else if (fgpm)
00634 fgred = compute_red(width, height, fgpm->columns(), fgpm->rows());
00635 if (fgred<1 || fgred>12)
00636 return 0;
00637
00638 if (fgjb && bgred && fgred)
00639 return 1;
00640
00641 return 0;
00642 }
00643
00644
00646
00647 GP<GBitmap>
00648 DjVuImage::get_bitmap(const GRect &rect,
00649 int subsample, int align) const
00650 {
00651
00652 int width = get_real_width();
00653 int height = get_real_height();
00654 GP<JB2Image> fgjb = get_fgjb();
00655 if ( width && height && fgjb &&
00656 (fgjb->get_width() == width) &&
00657 (fgjb->get_height() == height) )
00658 {
00659 return fgjb->get_bitmap(rect, subsample, align);
00660 }
00661 return 0;
00662 }
00663
00664 GP<GPixmap>
00665 DjVuImage::get_bg_pixmap(const GRect &rect,
00666 int subsample, double gamma) const
00667 {
00668 GP<GPixmap> pm = 0;
00669
00670
00671 GP<DjVuInfo> info = get_info();
00672 int width = get_real_width();
00673 int height = get_real_height();
00674
00675
00676 if (width<=0 || height<=0 || !info) return 0;
00677
00678 double gamma_correction = 1.0;
00679 if (gamma > 0)
00680 gamma_correction = gamma / info->gamma;
00681 if (gamma_correction < 0.1)
00682 gamma_correction = 0.1;
00683 else if (gamma_correction > 10)
00684 gamma_correction = 10;
00685
00686
00687 GP<IW44Image> bg44 = get_bg44();
00688 if (bg44)
00689 {
00690 int w = bg44->get_width();
00691 int h = bg44->get_height();
00692
00693 if (w==0 || h==0 || width==0 || height==0)
00694 return 0;
00695
00696 int red = compute_red(width,height,w,h);
00697 if (red<1 || red>12)
00698 return 0;
00699
00700 if (subsample == red)
00701 pm = bg44->get_pixmap(1,rect);
00702 else if (subsample == 2*red)
00703 pm = bg44->get_pixmap(2,rect);
00704 else if (subsample == 4*red)
00705 pm = bg44->get_pixmap(4,rect);
00706 else if (subsample == 8*red)
00707 pm = bg44->get_pixmap(8,rect);
00708
00709 else if (red*4 == subsample*3)
00710 {
00711 GRect nrect = rect;
00712 GRect xrect = rect;
00713 xrect.xmin = (xrect.xmin/3)*4;
00714 xrect.ymin = (xrect.ymin/3)*4;
00715 xrect.xmax = ((xrect.xmax+2)/3)*4;
00716 xrect.ymax = ((xrect.ymax+2)/3)*4;
00717 nrect.translate(-xrect.xmin*3/4, -xrect.ymin*3/4);
00718 if (xrect.xmax > w)
00719 xrect.xmax = w;
00720 if (xrect.ymax > h)
00721 xrect.ymax = h;
00722 GP<GPixmap> ipm = bg44->get_pixmap(1,xrect);
00723 pm = GPixmap::create();
00724 pm->downsample43(ipm, &nrect);
00725 }
00726
00727 else
00728 {
00729
00730 int po2 = 16;
00731 while (po2>1 && subsample<po2*red)
00732 po2 >>= 1;
00733
00734 int inw = (w+po2-1)/po2;
00735 int inh = (h+po2-1)/po2;
00736 int outw = (width+subsample-1)/subsample;
00737 int outh = (height+subsample-1)/subsample;
00738 GP<GPixmapScaler> gps=GPixmapScaler::create(inw, inh, outw, outh);
00739 GPixmapScaler &ps=*gps;
00740 ps.set_horz_ratio(red*po2, subsample);
00741 ps.set_vert_ratio(red*po2, subsample);
00742
00743 GRect xrect;
00744 ps.get_input_rect(rect,xrect);
00745 GP<GPixmap> ipm = bg44->get_pixmap(po2,xrect);
00746 pm = GPixmap::create();
00747 ps.scale(xrect, *ipm, rect, *pm);
00748 }
00749
00750 if (pm && gamma_correction!=1.0)
00751 pm->color_correct(gamma_correction);
00752 return pm;
00753 }
00754
00755
00756 GP<GPixmap> bgpm = get_bgpm();
00757 if (bgpm)
00758 {
00759 int w = bgpm->columns();
00760 int h = bgpm->rows();
00761
00762 if (w==0 || h==0 || width==0 || height==0)
00763 return 0;
00764
00765 int red = compute_red(width,height,w,h);
00766 if (red<1 || red>12)
00767 return 0;
00768
00769 int ratio = subsample/red;
00770 if (subsample==ratio*red && ratio>=1)
00771 {
00772 pm = GPixmap::create();
00773 if (ratio == 1)
00774 pm->init(*bgpm, rect);
00775 else if (ratio > 1)
00776 pm->downsample(bgpm, ratio, &rect);
00777 }
00778
00779 else
00780 {
00781
00782 int outw = (width+subsample-1)/subsample;
00783 int outh = (height+subsample-1)/subsample;
00784 GP<GPixmapScaler> gps=GPixmapScaler::create(w, h, outw, outh);
00785 GPixmapScaler &ps=*gps;
00786 ps.set_horz_ratio(red, subsample);
00787 ps.set_vert_ratio(red, subsample);
00788
00789 pm = GPixmap::create();
00790 GRect xrect(0,0,w,h);
00791 ps.scale(xrect, *bgpm, rect, *pm);
00792 }
00793
00794 if (pm && gamma_correction!=1.0)
00795 pm->color_correct(gamma_correction);
00796 return pm;
00797 }
00798
00799
00800 return 0;
00801 }
00802
00803
00804
00805 int
00806 DjVuImage::stencil(GPixmap *pm, const GRect &rect,
00807 int subsample, double gamma) const
00808 {
00809
00810 if (!pm)
00811 return 0;
00812
00813
00814 GP<DjVuInfo> info = get_info();
00815 int width = get_real_width();
00816 int height = get_real_height();
00817
00818
00819 if (width<=0 || height<=0 || !info) return 0;
00820 GP<JB2Image> fgjb = get_fgjb();
00821 GP<GPixmap> fgpm = get_fgpm();
00822 GP<DjVuPalette> fgbc = get_fgbc();
00823
00824
00825 double gamma_correction = 1.0;
00826 if (gamma > 0)
00827 gamma_correction = gamma / info->gamma;
00828 if (gamma_correction < 0.1)
00829 gamma_correction = 0.1;
00830 else if (gamma_correction > 10)
00831 gamma_correction = 10;
00832
00833
00834 GList<int> components;
00835 GP<GBitmap> bm;
00836 if (fgjb)
00837 {
00838 JB2Image *jimg = fgjb;
00839 if (! (width && height &&
00840 jimg->get_width() == width &&
00841 jimg->get_height() == height ) )
00842 return 0;
00843
00844 bm = GBitmap::create(rect.height(), rect.width());
00845 bm->set_grays(1+subsample*subsample);
00846 int rxmin = rect.xmin * subsample;
00847 int rymin = rect.ymin * subsample;
00848 for (int blitno = 0; blitno < jimg->get_blit_count(); blitno++)
00849 {
00850 const JB2Blit *pblit = jimg->get_blit(blitno);
00851 const JB2Shape &pshape = jimg->get_shape(pblit->shapeno);
00852 if (pshape.bits &&
00853 pblit->left <= rect.xmax * subsample &&
00854 pblit->bottom <= rect.ymax * subsample &&
00855 pblit->left + (int)pshape.bits->columns() >= rect.xmin * subsample &&
00856 pblit->bottom + (int)pshape.bits->rows() >= rect.ymin * subsample )
00857 {
00858
00859 if (fgbc) components.append(blitno);
00860
00861 bm->blit(pshape.bits,
00862 pblit->left - rxmin, pblit->bottom - rymin,
00863 subsample);
00864 }
00865 }
00866 }
00867
00868
00869
00870 if (bm && fgbc)
00871 {
00872
00873 pm->attenuate(bm, 0, 0);
00874
00875 JB2Image *jimg = fgjb;
00876 DjVuPalette *fg = fgbc;
00877 if (jimg->get_blit_count() != fg->colordata.size())
00878 return 0;
00879
00880 int palettesize = fg->size();
00881 GTArray<GPixel> colors(0,palettesize-1);
00882 for (int i=0; i<palettesize; i++)
00883 fg->index_to_color(i, colors[i]);
00884 GPixmap::color_correct(gamma_correction, colors, palettesize);
00885
00886 while (components.size() > 0)
00887 {
00888 GPosition nullpos;
00889 GPosition pos = components;
00890 int lastx = 0;
00891 int colorindex = fg->colordata[components[pos]];
00892 if (colorindex >= palettesize)
00893 G_THROW( ERR_MSG("DjVuImage.corrupted") );
00894
00895 GList<int> compset;
00896 GRect comprect;
00897 while (pos)
00898 {
00899 int blitno = components[pos];
00900 const JB2Blit *pblit = jimg->get_blit(blitno);
00901 if (pblit->left < lastx) break;
00902 lastx = pblit->left;
00903 if (fg->colordata[blitno] == colorindex)
00904 {
00905 const JB2Shape &pshape = jimg->get_shape(pblit->shapeno);
00906 GRect rect(pblit->left, pblit->bottom,
00907 pshape.bits->columns(), pshape.bits->rows());
00908 comprect.recthull(comprect, rect);
00909 compset.insert_before(nullpos, components, pos);
00910 continue;
00911 }
00912 ++pos;
00913 }
00914
00915 comprect.xmin = comprect.xmin / subsample;
00916 comprect.ymin = comprect.ymin / subsample;
00917 comprect.xmax = (comprect.xmax+subsample-1) / subsample;
00918 comprect.ymax = (comprect.ymax+subsample-1) / subsample;
00919 comprect.intersect(comprect, rect);
00920
00921 bm = 0;
00922 bm = GBitmap::create(comprect.height(), comprect.width());
00923 bm->set_grays(1+subsample*subsample);
00924 int rxmin = comprect.xmin * subsample;
00925 int rymin = comprect.ymin * subsample;
00926 for (pos=compset; pos; ++pos)
00927 {
00928 int blitno = compset[pos];
00929 const JB2Blit *pblit = jimg->get_blit(blitno);
00930 const JB2Shape &pshape = jimg->get_shape(pblit->shapeno);
00931 bm->blit(pshape.bits,
00932 pblit->left - rxmin, pblit->bottom - rymin,
00933 subsample);
00934 }
00935
00936 pm->blit(bm, comprect.xmin-rect.xmin, comprect.ymin-rect.ymin, &colors[colorindex]);
00937 }
00938 return 1;
00939 }
00940
00941
00942
00943 if (bm && fgpm)
00944 {
00945
00946
00947
00948
00949
00950 #ifdef SIMPLE_THREE_LAYER_RENDERING
00951 int w = fgpm->columns();
00952 int h = fgpm->rows();
00953
00954 int red = compute_red(width,height, w, h);
00955 if (red<1 || red>12)
00956 return 0;
00957
00958 GPixmapScaler ps(w,h,width/subsample+1,height/subsample+1);
00959 ps.set_horz_ratio(red,subsample);
00960 ps.set_vert_ratio(red,subsample);
00961 GP<GPixmap> nfg = new GPixmap;
00962 GRect provided(0,0,w,h);
00963 ps.scale(provided, *fgpm, rect, *nfg);
00964
00965 nfg->color_correct(gamma_correction);
00966 pm->blend(bm, 0, 0, nfg);
00967 return 1;
00968 #else
00969
00970
00971
00972
00973 int w = fgpm->columns();
00974 int h = fgpm->rows();
00975
00976 int red = compute_red(width,height,w,h);
00977 if (red<1 || red>12)
00978 return 0;
00979
00980 int supersample = ( red>subsample ? red/subsample : 1);
00981 int wantedred = supersample*subsample;
00982
00983 if (red == wantedred)
00984 {
00985
00986 pm->stencil(bm, fgpm, supersample, &rect, gamma_correction);
00987 return 1;
00988 }
00989 else
00990 {
00991
00992 GP<GPixmap> nfg;
00993 int desw = (w*red+wantedred-1)/wantedred;
00994 int desh = (h*red+wantedred-1)/wantedred;
00995
00996 static const DjVuImage *tagimage = 0;
00997 static const GPixmap *tagfgpm = 0;
00998 static GP<GPixmap> cachednfg = 0;
00999
01000 if ( cachednfg && this==tagimage && fgpm==tagfgpm
01001 && desw==(int)cachednfg->columns()
01002 && desh==(int)cachednfg->rows() )
01003 {
01004 nfg = cachednfg;
01005 }
01006 else
01007 {
01008 GP<GPixmapScaler> gps=GPixmapScaler::create(w,h,desw,desh);
01009 GPixmapScaler &ps=*gps;
01010 ps.set_horz_ratio(red, wantedred);
01011 ps.set_vert_ratio(red, wantedred);
01012 nfg = GPixmap::create();
01013 GRect provided(0,0,w,h);
01014 GRect desired(0,0,desw,desh);
01015 ps.scale(provided, *fgpm, desired, *nfg);
01016 }
01017
01018 pm->stencil(bm, nfg, supersample, &rect, gamma_correction);
01019
01020 tagimage = this;
01021 tagfgpm = fgpm;
01022 cachednfg = nfg;
01023 return 1;
01024 }
01025 #endif
01026 }
01027
01028
01029 return 0;
01030 }
01031
01032
01033 GP<GPixmap>
01034 DjVuImage::get_fg_pixmap(const GRect &rect,
01035 int subsample, double gamma) const
01036 {
01037
01038 GP<GPixmap> pm;
01039
01040 const int width = get_real_width();
01041 const int height = get_real_height();
01042 if (width && height)
01043 {
01044 pm = GPixmap::create(rect.height(),rect.width(), &GPixel::WHITE);
01045 if (!stencil(pm, rect, subsample, gamma))
01046 pm=0;
01047 }
01048 return pm;
01049 }
01050
01051
01052 GP<GPixmap>
01053 DjVuImage::get_pixmap(const GRect &rect, int subsample, double gamma) const
01054 {
01055
01056 GP<GPixmap> pm = get_bg_pixmap(rect, subsample, gamma);
01057
01058 if (! stencil(pm, rect, subsample, gamma))
01059
01060 if (get_fgjb()) return 0;
01061
01062 return pm;
01063 }
01064
01065
01067
01068 typedef GP<GBitmap>(DjVuImage::*BImager)(const GRect &, int, int) const;
01069 typedef GP<GPixmap>(DjVuImage::*PImager)(const GRect &, int, double) const;
01070
01071 static GP<GBitmap>
01072 do_bitmap(const DjVuImage &dimg, BImager get,
01073 const GRect &inrect, const GRect &inall, int align )
01074 {
01075 GRect rect=inrect;
01076 GRect all=inall;
01078 if( dimg.get_rotate()%4 )
01079 {
01080 GRectMapper mapper;
01081 mapper.rotate((4-dimg.get_rotate())%4);
01082 mapper.map(rect);
01083 mapper.map(all);
01084 }
01086
01087
01088 if (! ( all.contains(rect.xmin, rect.ymin) &&
01089 all.contains(rect.xmax-1, rect.ymax-1) ))
01090 G_THROW( ERR_MSG("DjVuImage.bad_rect") );
01091
01092 int red;
01093 int w = dimg.get_real_width();
01094 int h = dimg.get_real_height();
01095
01096 int rw = all.width();
01097 int rh = all.height();
01098 GRect zrect = rect;
01099 zrect.translate(-all.xmin, -all.ymin);
01100 for (red=1; red<=15; red++)
01101 if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red)
01102 {
01103 GP<GBitmap> bm=(dimg.*get)(zrect, red, align);
01104 if(bm)
01105 return bm->rotate((4-dimg.get_rotate())%4);
01106 else
01107 return NULL;
01108 }
01109
01110 for (red=15; red>1; red--)
01111 if ( (rw*red < w && rh*red < h) ||
01112 (rw*red*3 < w || rh*red*3 < h) )
01113 break;
01114
01115 if (! (w && h)) return 0;
01116 GP<GBitmapScaler> gbs=GBitmapScaler::create();
01117 GBitmapScaler &bs=*gbs;
01118 bs.set_input_size( (w+red-1)/red, (h+red-1)/red );
01119 bs.set_output_size( rw, rh );
01120 bs.set_horz_ratio( rw*red, w );
01121 bs.set_vert_ratio( rh*red, h );
01122
01123 GRect srect;
01124 bs.get_input_rect(zrect, srect);
01125 GP<GBitmap> sbm = (dimg.*get)(srect, red, 1);
01126 if (!sbm) return 0;
01127 int border = ((zrect.width() + align - 1) & ~(align - 1)) - zrect.width();
01128 GP<GBitmap> bm = GBitmap::create(zrect.height(), zrect.width(), border);
01129 bs.scale(srect, *sbm, zrect, *bm);
01130 if( bm )
01131 return bm->rotate((4-dimg.get_rotate())%4);
01132 else
01133 return NULL;
01134 }
01135
01136 static GP<GPixmap>
01137 do_pixmap(const DjVuImage &dimg, PImager get,
01138 const GRect &inrect, const GRect &inall, double gamma )
01139 {
01140
01141 GRect rect=inrect;
01142 GRect all=inall;
01144 if( dimg.get_rotate()%4 )
01145 {
01146 GRectMapper mapper;
01147 mapper.rotate((4-dimg.get_rotate())%4);
01148 mapper.map(rect);
01149 mapper.map(all);
01150 }
01152
01153
01154 if (! ( all.contains(rect.xmin, rect.ymin) &&
01155 all.contains(rect.xmax-1, rect.ymax-1) ))
01156 G_THROW( ERR_MSG("DjVuImage.bad_rect2") );
01157
01158 int red, w=0, h=0, rw=0, rh=0;
01159 w = dimg.get_real_width();
01160 h = dimg.get_real_height();
01161
01162
01163 rw = all.width();
01164 rh = all.height();
01165 GRect zrect = rect;
01166 zrect.translate(-all.xmin, -all.ymin);
01167 for (red=1; red<=15; red++)
01168 if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red)
01169 {
01170 GP<GPixmap> pm = (dimg.*get)(zrect, red, gamma);
01171 if( pm )
01172 return pm->rotate((4-dimg.get_rotate())%4);
01173 else
01174 return NULL;
01175 }
01176
01177 static int fastred[] = { 12,6,4,3,2,1 };
01178
01179 for (int i=0; (red=fastred[i])>1; i++)
01180 if ( (rw*red < w && rh*red < h) ||
01181 (rw*red*3 < w || rh*red*3 < h) )
01182 break;
01183
01184 if (w<0 || h<0) return 0;
01185 GP<GPixmapScaler> gps=GPixmapScaler::create();
01186 GPixmapScaler &ps=*gps;
01187 ps.set_input_size( (w+red-1)/red, (h+red-1)/red );
01188 ps.set_output_size( rw, rh );
01189 ps.set_horz_ratio( rw*red, w );
01190 ps.set_vert_ratio( rh*red, h );
01191
01192 GRect srect;
01193 ps.get_input_rect(zrect, srect);
01194 GP<GPixmap> spm = (dimg.*get)(srect, red, gamma);
01195 if (!spm) return 0;
01196 GP<GPixmap> pm = GPixmap::create();
01197 ps.scale(srect, *spm, zrect, *pm);
01198 if(pm)
01199 return pm->rotate((4-dimg.get_rotate())%4);
01200 else
01201 return NULL;
01202 }
01203
01204 GP<GPixmap>
01205 DjVuImage::get_pixmap(const GRect &rect, const GRect &all, double gamma) const
01206 {
01207 return do_pixmap(*this, & DjVuImage::get_pixmap, rect, all, gamma);
01208 }
01209
01210 GP<GBitmap>
01211 DjVuImage::get_bitmap(const GRect &rect, const GRect &all, int align) const
01212 {
01213 return do_bitmap(*this, & DjVuImage::get_bitmap, rect, all, align);
01214 }
01215
01216 GP<GPixmap>
01217 DjVuImage::get_bg_pixmap(const GRect &rect, const GRect &all, double gamma) const
01218 {
01219 return do_pixmap(*this, & DjVuImage::get_bg_pixmap, rect, all, gamma);
01220 }
01221
01222 GP<GPixmap>
01223 DjVuImage::get_fg_pixmap(const GRect &rect, const GRect &all, double gamma) const
01224 {
01225 return do_pixmap(*this, & DjVuImage::get_fg_pixmap, rect, all, gamma);
01226 }
01227
01228 int
01229 DjVuImage::get_rotate() const
01230 {
01231 return (rotate_count<0)?0:rotate_count;
01232 }
01233
01234 void
01235 DjVuImage::init_rotate(const DjVuInfo &info)
01236 {
01237 rotate_count=((360-GRect::findangle(info.orientation))/90)%4;
01238 }
01239
01240 void DjVuImage::set_rotate(int count)
01241 {
01242 rotate_count=((count%4)+4)%4;
01243 }
01244
01245 GP<DjVuAnno>
01246 DjVuImage::get_decoded_anno()
01247 {
01248 GP<DjVuAnno> djvuanno = DjVuAnno::create();
01249 GP<ByteStream> bs=get_anno();
01250 if( bs )
01251 {
01252 djvuanno->decode(bs);
01253
01254 const int rotate_count=get_rotate();
01255 if( rotate_count % 4 )
01256 {
01258 GRect input, output;
01259 input = GRect(0,0,get_width(), get_height());
01260 output = GRect(0,0, get_real_width(), get_real_height());
01261
01262 GRectMapper mapper;
01263 mapper.clear();
01264 mapper.set_input(input);
01265 mapper.set_output(output);
01266 mapper.rotate((4-rotate_count)%4);
01267
01268 GPList<GMapArea> &list=djvuanno->ant->map_areas;
01269 for(GPosition pos=list;pos;++pos)
01270 {
01271 list[pos]->unmap(mapper);
01272 }
01273 }
01274 return djvuanno;
01275 }
01276 else
01277 return NULL;
01278 }
01279
01280
01281 void
01282 DjVuImage::map(GRect &rect) const
01283 {
01284 GRect input, output;
01285 const int rotate_count=get_rotate();
01286 if(rotate_count%4)
01287 {
01288 input = GRect(0,0,get_width(), get_height());
01289 output = GRect(0,0, get_real_width(), get_real_height());
01290
01291 GRectMapper mapper;
01292 mapper.clear();
01293 mapper.set_input(input);
01294 mapper.set_output(output);
01295 mapper.rotate((4-rotate_count)%4);
01296 mapper.map(rect);
01297 }
01298 }
01299
01300 void
01301 DjVuImage::unmap(GRect &rect) const
01302 {
01303 GRect input, output;
01304 const int rotate_count=get_rotate();
01305 if(rotate_count%4)
01306 {
01307 input = GRect(0,0,get_width(), get_height());
01308 output = GRect(0,0, get_real_width(), get_real_height());
01309
01310 GRectMapper mapper;
01311 mapper.clear();
01312 mapper.set_input(input);
01313 mapper.set_output(output);
01314 mapper.rotate((4-rotate_count)%4);
01315 mapper.unmap(rect);
01316 }
01317 }
01318
01319 void
01320 DjVuImage::map(int &x, int &y) const
01321 {
01322 GRect input, output;
01323 const int rotate_count=get_rotate();
01324 if(rotate_count%4)
01325 {
01326 input = GRect(0,0,get_width(), get_height());
01327 output = GRect(0,0, get_real_width(), get_real_height());
01328
01329 GRectMapper mapper;
01330 mapper.clear();
01331 mapper.set_input(input);
01332 mapper.set_output(output);
01333 mapper.rotate((4-rotate_count)%4);
01334 mapper.map(x, y);
01335 }
01336 }
01337
01338 void
01339 DjVuImage::unmap(int &x, int &y) const
01340 {
01341 GRect input, output;
01342 const int rotate_count=get_rotate();
01343 if(rotate_count%4)
01344 {
01345 input = GRect(0,0,get_width(), get_height());
01346 output = GRect(0,0, get_real_width(), get_real_height());
01347
01348 GRectMapper mapper;
01349 mapper.clear();
01350 mapper.set_input(input);
01351 mapper.set_output(output);
01352 mapper.rotate((4-rotate_count)%4);
01353 mapper.unmap(x, y);
01354 }
01355 }
01356
01357 bool
01358 DjVuImage::wait_for_complete_decode(void)
01359 {
01360 if (file)
01361 {
01362 file->resume_decode(true);
01363 return file->is_decode_ok();
01364 }
01365 return 0;
01366 }
01367
01368
01369 void
01370 DjVuImage::writeXML(ByteStream &str_out,const GURL &doc_url,const int flags) const
01371 {
01372 const int height=get_height();
01373
01374 static const char *Object="<OBJECT data=\"";
01375 const GURL url(get_djvu_file()->get_url());
01376 const GUTF8String pagename(url.fname());
01377 GUTF8String page_param;
01378 if(doc_url.is_valid() && !doc_url.is_empty() && (doc_url != url))
01379 {
01380 str_out.writestring(Object+doc_url.get_string());
01381 page_param="<PARAM name=\"PAGE\" value=\""+pagename+"\" />\n";
01382 }else
01383 {
01384 str_out.writestring(Object+doc_url.get_string());
01385 }
01386 str_out.writestring("\" type=\""+get_mimetype()+"\" height=\""
01387 +GUTF8String(height)+"\" width=\""+GUTF8String(get_width())
01388 +"\" usemap=\""+pagename.toEscaped()+"\" >\n");
01389 if(!(flags & NOINFO))
01390 {
01391 const GP<DjVuInfo> info(get_info());
01392 if(info)
01393 {
01394 info->writeParam(str_out);
01395 }
01396 }
01397 str_out.writestring(page_param);
01398 const GP<DjVuAnno> anno(DjVuAnno::create());
01399 if(!(flags & NOINFO)||!(flags&NOMAP))
01400 {
01401 const GP<ByteStream> anno_str(get_anno());
01402 if(anno_str)
01403 {
01404 anno->decode(anno_str);
01405 }
01406 if(!(flags & NOINFO))
01407 {
01408 anno->writeParam(str_out);
01409 }
01410 }
01411 if(!(flags & NOTEXT))
01412 {
01413 const GP<DjVuText> text(DjVuText::create());
01414 {
01415 const GP<ByteStream> text_str(get_text());
01416 if(text_str)
01417 {
01418 text->decode(text_str);
01419 }
01420 text->writeText(str_out,height);
01421 }
01422 }
01423 if(!(flags & NOMETA))
01424 {
01425 const GP<ByteStream> meta_str(get_meta());
01426 if(meta_str)
01427 {
01428 GP<IFFByteStream> giff=IFFByteStream::create(meta_str);
01429 IFFByteStream &iff=*giff;
01430 GUTF8String chkid;
01431 while( iff.get_chunk(chkid))
01432 {
01433 GP<ByteStream> gbs(iff.get_bytestream());
01434 if(chkid == "METa")
01435 {
01436 str_out.copy(*gbs);
01437
01438 }else if(chkid == "METz")
01439 {
01440 gbs=BSByteStream::create(gbs);
01441 str_out.copy(*gbs);
01442
01443 }
01444 iff.close_chunk();
01445 }
01446 }
01447 }
01448 str_out.writestring(GUTF8String("</OBJECT>\n"));
01449 if(!(flags & NOMAP))
01450 {
01451 anno->writeMap(str_out,pagename,height);
01452 }
01453 }
01454
01455
01456 void
01457 DjVuImage::writeXML(ByteStream &str_out) const
01458 {
01459 writeXML(str_out,GURL());
01460 }
01461
01462
01463 GUTF8String
01464 DjVuImage::get_XML(const GURL &doc_url,const int flags) const
01465 {
01466 GP<ByteStream> gbs(ByteStream::create());
01467 ByteStream &bs=*gbs;
01468 writeXML(bs,doc_url);
01469 bs.seek(0L);
01470 return bs.getAsUTF8();
01471 }
01472
01473
01474 GUTF8String
01475 DjVuImage::get_XML(void) const
01476 {
01477 return get_XML(GURL());
01478 }
01479
01480
01481 #ifdef HAVE_NAMESPACES
01482 }
01483 # ifndef NOT_USING_DJVU_NAMESPACE
01484 using namespace DJVU;
01485 # endif
01486 #endif