void create_fgbz_chunk(IFFByteStream &iff) { int nzones = g().colorzones.size(); int npalette = g().colorpalette->size() / 3; GP<DjVuPalette> pal = DjVuPalette::create(); g().colorpalette->seek(0); pal->decode_rgb_entries(*g().colorpalette, npalette); pal->colordata.resize(0,blit_count-1); for (int d=0; d<blit_count; d++) { JB2Blit *blit = g().stencil->get_blit(d); const JB2Shape &shape = g().stencil->get_shape(blit->shapeno); GRect brect(blit->left, blit->bottom, shape.bits->columns(), shape.bits->rows()); int index = nzones; for (int i=0; i<nzones; i++) { GRect zrect = g().colorzones[i]; if (zrect.isempty() || zrect.intersect(brect, zrect)) index = i; } if (index >= npalette) G_THROW("create_fgbz_chunk: internal error"); pal->colordata[d] = index; } iff.put_chunk("FGbz"); pal->encode(iff.get_bytestream()); iff.close_chunk(); }
void QDBase::slotDisableDisplayAllHLinks(void) { DEBUG_MSG("QDBase::slotDisableDisplayAllHLinks() called\n"); DEBUG_MAKE_INDENT(3); try { if (!display_all_hlinks) { DEBUG_MSG("already disabled => returning\n"); return; } GRect grect; grect.intersect(rectVisible, rectDocument); // We want to hide here for(GPosition pos=map_areas;pos;++pos) { GP<MapArea> ma=map_areas[pos]; ma->setForceAlwaysActive(false); ma->setActive(false, true); } display_all_hlinks=0; } catch(const GException & exc) { showError(this, tr("DjVu Error"), exc); } }
void MapArea::drawOutline(const GRect & grect, QPainter * p_in) { if (pane && mapper) { GRect brect=gmap_area->get_bound_rect(); mapper->map(brect); brect.inflate(3, 3); GRect irect; if (irect.intersect(brect, grect)) { QPainter * p=p_in; try { if (!p_in) p=new QPainter(pane); else p_in->save(); p->setRasterOp(XorROP); p->setPen(QColor(0xff, 0xff, 0xff)); p->setClipRect(G2Q(irect)); ma_drawOutline(p, irect); if (editControlsEnabled()) drawEditControls(grect, p); if (!p_in) { delete p; p=0; } else p_in->restore(); } catch(...) { if (!p_in) delete p; throw; } } } }
void MapArea::setActive(bool on, bool redraw) { if ((!on && isAlwaysActive()) || active==on) return; active=on; if (redraw && pane && mapper) { if (isActiveOutlineMode()) drawOutline(Q2G(pane->rect())); else if (!isCacheUsed()) repaint(); else { // Use cache to draw ourselves GRect brect=gmap_area->get_bound_rect(); mapper->map(brect); brect.inflate(3, 3); GRect pane_rect=Q2G(pane->rect()); GRect urect; if (urect.intersect(pane_rect, brect)) { for(GPosition pos=pieces;pos;++pos) { GP<MapPiece> piece=pieces[pos]; GRect piece_rect=*piece; mapper->map(piece_rect); GRect irect; if (irect.intersect(piece_rect, urect)) { QPixmap & pix=active ? piece->getOnPixmap() : piece->getOffPixmap(); if (pix.isNull()) repaint(piece_rect); QPainter p(pane); GRect crect=doc_rect; mapper->map(crect); p.setClipRect(G2Q(crect)); p.drawPixmap(irect.xmin, irect.ymin, pix, irect.xmin-piece_rect.xmin, irect.ymin-piece_rect.ymin, irect.width(), irect.height()); } } } } } }
void MapRect::ma_drawInactive(const GRect & pm_rect, const GP<GPixmap> & pm) // pm_rect is the rectangle of the pane in screen coordinates // where pm is supposed to go to { if (mapper && pm) { GRect border_rect=gmap_area->get_bound_rect(); mapper->map(border_rect); GRect irect; if (irect.intersect(border_rect, pm_rect)) { // Translate the intersection into the GPixmap's coordinate irect.translate(-pm_rect.xmin, -pm_rect.ymin); // Do hiliting first if (gmap_area->hilite_color!=0xffffffff) { if (gmap_area->hilite_color==0xff000000) { // Do XOR hiliting for(int y=irect.ymin;y<irect.ymax;y++) { GPixel * pix=(*pm)[pm->rows()-1-y]+irect.xmin; for(int x=irect.xmin;x<irect.xmax;x++, pix++) { pix->r^=0xff; pix->g^=0xff; pix->b^=0xff; } } } else { // Do COLOR hiliting int r=(gmap_area->hilite_color & 0xff0000) >> (16+2); int g=(gmap_area->hilite_color & 0xff00) >> (8+2); int b=(gmap_area->hilite_color & 0xff) >> 2; for(int y=irect.ymin;y<irect.ymax;y++) { GPixel * pix=(*pm)[pm->rows()-1-y]+irect.xmin; for(int x=irect.xmin;x<irect.xmax;x++, pix++) { pix->r=((((int) pix->r << 1)+(int) pix->r) >> 2)+r; pix->g=((((int) pix->g << 1)+(int) pix->g) >> 2)+g; pix->b=((((int) pix->b << 1)+(int) pix->b) >> 2)+b; } } } } }
void MapArea::repaintPiece(const GP<MapPiece> & piece) // Sends PaintEvent to cause repaint of the specified piece { if (pane && mapper) { GRect grect=*piece; mapper->map(grect); GRect prect=Q2G(pane->rect()); GRect irect; if (irect.intersect(grect, prect)) repaint(irect); } }
void MapArea::repaint(void) // Generates PaintEvents for the whole area { if (pane && mapper) { GRect brect=gmap_area->get_bound_rect(); mapper->map(brect); brect.inflate(3, 3); // Take into account possible edit controls GRect prect=Q2G(pane->rect()); GRect irect; if (irect.intersect(brect, prect)) repaint(irect); } }
void MapArea::drawEditControls(const GRect & grect, QPainter * p_in) // grect is in the screen coord system // Correct painter modes WILL be set { GRect brect=gmap_area->get_bound_rect(); mapper->map(brect); brect.inflate(3, 3); GRect irect; if (irect.intersect(grect, brect)) { QPainter * p=p_in; try { // Prepare if (!p_in) p=new QPainter(pane); else p_in->save(); p->setRasterOp(XorROP); p->setPen(QColor(0xff, 0xff, 0xff)); p->setClipRect(G2Q(irect)); // Draw int xmin=brect.xmin+3, xmax=brect.xmax-3; int ymin=brect.ymin+3, ymax=brect.ymax-3; _drawFrame(*p, xmin-1, ymin-1, xmax+1, ymax+1, 10); _drawFrame(*p, xmin-3, ymin-3, xmax+3, ymax+3, 12); ma_drawEditControls(p); // Finish if (!p_in) { delete p; p=0; } else p_in->restore(); } catch(...) { if (!p_in) delete p; throw; } } }
void QDBase::eraseMapAreas(bool search_results_too, bool allow_draw) { DEBUG_MSG("QDBase::eraseMapAreas(): Erasing the map areas\n"); DEBUG_MAKE_INDENT(3); GRect grect; grect.intersect(rectDocument, rectVisible); for(GPosition pos=map_areas;pos;) { GP<MapArea> ma=map_areas[pos]; if (search_results_too || ma->getComment()!=search_results_name) { GPosition this_pos=pos; ++pos; map_areas.del(this_pos); if (!grect.isempty() && allow_draw) ma->repaint(); } else ++pos; } cur_map_area=0; delete map_area_tip; map_area_tip=0; }
void MapArea::draw(const GRect & bm_rect, const GP<GBitmap> & bm_in, GRect & pm_rect, GP<GPixmap> & pm_out, DRAW_MODE draw_mode) // Draws itself into the specified bitmap. The bm_rect should be // in the pane's coordinates. Since GBitmap may not contain color // information, we will create a color GPixmap patch (pm_out), where we // will actually be drawing. The pm_rect is a rectangle in // pane coordinates where this pm_out should go to. { DEBUG_MSG("MapArea::draw(): Highlighting " << gmap_area->url << ":" << gmap_area->target << "\n"); DEBUG_MAKE_INDENT(3); if (pane) { GRect brect=gmap_area->get_bound_rect(); mapper->map(brect); brect.inflate(3, 3); GRect irect; if (irect.intersect(bm_rect, brect)) { pm_rect=irect; irect.translate(-bm_rect.xmin, -bm_rect.ymin); pm_out=GPixmap::create(*bm_in, GRect(irect.xmin, bm_in->rows()-irect.ymax, irect.width(), irect.height())); if (!isInactiveOutlineMode()) if (draw_mode==DRAW_INACTIVE || draw_mode==DRAW_ACTIVE) ma_drawInactive(pm_rect, pm_out); if (!isActiveOutlineMode()) if (draw_mode==APPLY_ACTIVE || draw_mode==DRAW_ACTIVE) ma_applyActive(pm_rect, pm_out); } } }
void QDBase::layout(bool allow_redraw) { DEBUG_MSG("QDBase::layout() called\n"); DEBUG_MAKE_INDENT(3); if (in_layout) { DEBUG_MSG("but we seem to be already here => must be called because of resize\n"); return; } IncFlag inc(&in_layout); // Obtain document size int doc_w=0; int doc_h=0; if (dimg) { doc_w=dimg->get_width(); doc_h=dimg->get_height(); if (doc_w>0 && doc_h>0) image_size_known = 1; } if (!doc_w || !doc_h) { DEBUG_MSG("document rectangle is empty...just hide scroll bars\n"); hscroll->hide(); vscroll->hide(); pane->resize(main_widget->width(), main_widget->height()); return; } bool do_redraw = false; bool update_tbar = false; bool hscroll_visible = false; bool vscroll_visible = false; int toolbar_height = toolbar->computeHeight(main_widget->width()); toolbar_enabled = (override_flags.toolbar && prefs.toolBarOn); if (toolbar_height + 2 * hscroll->height() > main_widget->height() ) toolbar_enabled = 0; if (!isToolBarEnabled()) toolbar_height = 0; else if (!isToolBarStuck()) toolbar_height = toolbar_edge; bool fullscreen = false; emit sigQueryFullScreen(fullscreen); rectVisible = GRect(0, 0, main_widget->width(), main_widget->height()-toolbar_height); if (cmd_zoom==IDC_ZOOM_STRETCH) { do_redraw |= allow_redraw; rectDocument = rectVisible; } else { // The scaling code can reduce the image *fast* 1, 2, 3, 4, 6 or 12 times // When "Favor fast magnifications for fast resolutions" is set, // we're forcing plugin to use these "fast" reductions. // Note, that the actual zoom factor (in %%) will depend on the // screen and image dpi. const int fast_red[]={ 1, 2, 3, 4, 6, 12 }; const int fast_reds=sizeof(fast_red)/sizeof(fast_red[0]); int red_ind=0; float red=1/100000.0; while(true) // Loop until we're sure about scrollbars and toolbar height { int image_dpi=300; if (dimg) image_dpi=dimg->get_rounded_dpi(); if (image_dpi<=0 || image_dpi>=2400) image_dpi=300; DEBUG_MSG("displ_dpi=" << displ_dpi << ", image_dpi=" << image_dpi << "\n"); // Compute reduction from cmd_zoom if (cmd_zoom>=IDC_ZOOM_MIN && cmd_zoom<=IDC_ZOOM_MAX) { // Fixed resolution int zoom=cmd_zoom-IDC_ZOOM_MIN; DEBUG_MSG("zoom=" << zoom << "%\n"); red=(float) image_dpi/zoom; } else if (cmd_zoom==IDC_ZOOM_ONE2ONE) { red=1; } else if (cmd_zoom==IDC_ZOOM_PAGE) { // IDC_ZOOM_PAGE case if (prefs.fastZoom) { // We may continue to loop from the previous scroll bar iteration for(;red_ind<fast_reds;red_ind++) { red=fast_red[red_ind]; if (doc_w<=rectVisible.width()*red && doc_h<=rectVisible.height()*red) break; } } if (doc_w>rectVisible.width()*red || doc_h>rectVisible.height()*red) { // Either we're not in the "fast" mode, or the // capabilities of the "fast" mode are not sufficient // to reduce the document so much float red_w=(float) doc_w/rectVisible.width(); float red_h=(float) doc_h/rectVisible.height(); float red_new=red_w<red_h ? red_h : red_w; // The following is necessary to avoid infinite loop with // appearing and disappearing scroll bars. if (red_new>red) red=red_new; } } else { // Make it IDC_ZOOM_WIDTH even if it's not if (prefs.fastZoom) { // We may continue to loop from the previous scroll bar iteration for(;red_ind<fast_reds;red_ind++) { red=fast_red[red_ind]; if (doc_w<=rectVisible.width()*red) break; } } if (doc_w>rectVisible.width()*red) { // Either we're not in the "fast" mode, or the // capabilities of the "fast" mode are not sufficient // to reduce the document so much float red_new=(float) doc_w/rectVisible.width(); // The following is necessary to avoid infinite loop with // appearing and disappearing scroll bars. if (red_new>red) red=red_new; } } DEBUG_MSG("reduction=" << red << ", scale factor=" << (1/red) << "\n"); // determine scrollbar visibility and modify rectVisible rectVisible=GRect(0, 0, main_widget->width(), main_widget->height()-toolbar_height); DEBUG_MSG("before engaging scrolls rectVisible=(" << rectVisible.xmin << ", " << rectVisible.ymin << ", " << rectVisible.width() << ", " << rectVisible.height() << ")\n"); int sh = hscroll->height(); int sw = vscroll->width(); bool hs_vis=false, vs_vis=false; if (override_flags.scrollbars && !fullscreen && rectVisible.height()>2*sh && rectVisible.width()>2*sw) { while(1) { if (!hs_vis && doc_w>round(rectVisible.width()*red)) { rectVisible.ymax=main_widget->height()-toolbar_height-sh; hs_vis=true; } else if (!vs_vis && doc_h>round(rectVisible.height()*red)) { rectVisible.xmax=main_widget->width()-sw; vs_vis=true; } else { break; } } } DEBUG_MSG("after engaging scrolls rectVisible=(" << rectVisible.xmin << ", " << rectVisible.ymin << ", " << rectVisible.width() << ", " << rectVisible.height() << ")\n"); if (hs_vis==hscroll_visible && vs_vis==vscroll_visible) break; DEBUG_MSG("Looks like we need another pass thru resolutions:\n"); DEBUG_MSG("hscroll_visible: was=" << hscroll_visible << ", now=" << hs_vis << "\n"); DEBUG_MSG("vscroll_visible: was=" << vscroll_visible << ", now=" << vs_vis << "\n"); hscroll_visible=hs_vis; vscroll_visible=vs_vis; } DEBUG_MSG("hscroll_visible=" << hscroll_visible << ", vscroll_visible=" << vscroll_visible << "\n"); GRect prevRectDocument=rectDocument; // Modify rectDocument maintaining document position if (round(rectDocument.width()*red)!=doc_w || round(rectDocument.height()*red)!=doc_h) { if (!rectDocument.isempty()) { GRect rect; rect.intersect(rectVisible, rectDocument); int cx=(rect.xmin+rect.xmax)/2-rectDocument.xmin; int cy=(rect.ymin+rect.ymax)/2-rectDocument.ymin; rectDocument.xmin=rectDocument.xmax =(int) (rectVisible.width()/2-cx*doc_w/ (red*rectDocument.width())); rectDocument.ymin=rectDocument.ymax =(int) (rectVisible.height()/2-cy*doc_h/ (red*rectDocument.height())); } } // Determine scaled document rectangle (adjusting xmin and ymin) rectDocument.xmax=rectDocument.xmin+round(doc_w/red); rectDocument.ymax=rectDocument.ymin+round(doc_h/red); if (isToolBarEnabled() && (prevRectDocument.width()!=rectDocument.width() || prevRectDocument.height()!=rectDocument.height())) update_tbar=true; // Since getZoom() may now return different value if (prevRectDocument.width()!=rectDocument.width() || prevRectDocument.height()!=rectDocument.height() || prevRectDocument.xmin!=rectDocument.xmin || prevRectDocument.ymin!=rectDocument.ymin) do_redraw|=allow_redraw; DEBUG_MSG("before aligning rectDocument=(" << rectDocument.xmin << ", " << rectDocument.ymin << ", " << rectDocument.width() << ", " << rectDocument.height() << ")\n"); // adjust document location. Please note: there is a copy of this code in // Scroll(). If you make any changes, make sure to do it twice :) int xoff=0; int yoff=0; int hor_align=DjVuANT::ALIGN_CENTER; int ver_align=DjVuANT::ALIGN_CENTER; if (anno && anno->ant) { hor_align=anno->ant->hor_align; ver_align=anno->ant->ver_align; } if (override_flags.hor_align!=DjVuANT::ALIGN_UNSPEC) hor_align=override_flags.hor_align; if (override_flags.ver_align!=DjVuANT::ALIGN_UNSPEC) ver_align=override_flags.ver_align; if (rectDocument.width()<=rectVisible.width()) { switch(hor_align) { case DjVuANT::ALIGN_LEFT: xoff=rectVisible.xmin-rectDocument.xmin; break; case DjVuANT::ALIGN_CENTER: case DjVuANT::ALIGN_UNSPEC: xoff=rectVisible.xmin-rectDocument.xmin+ (rectVisible.width()-rectDocument.width())/2; break; case DjVuANT::ALIGN_RIGHT: xoff=rectVisible.xmax-rectDocument.xmax; break; } } else if (rectDocument.xmin>rectVisible.xmin) xoff=rectVisible.xmin-rectDocument.xmin; else if (rectDocument.xmax<rectVisible.xmax) xoff=rectVisible.xmax-rectDocument.xmax; if (rectDocument.height()<=rectVisible.height()) { switch(ver_align) { case DjVuANT::ALIGN_TOP: yoff=rectVisible.ymin-rectDocument.ymin; break; case DjVuANT::ALIGN_CENTER: case DjVuANT::ALIGN_UNSPEC: yoff=rectVisible.ymin-rectDocument.ymin+ (rectVisible.height()-rectDocument.height())/2; break; case DjVuANT::ALIGN_BOTTOM: yoff=rectVisible.ymax-rectDocument.ymax; break; } } else if (rectDocument.ymin>rectVisible.ymin) yoff=rectVisible.ymin-rectDocument.ymin; else if (rectDocument.ymax<rectVisible.ymax) yoff=rectVisible.ymax-rectDocument.ymax; if (rectDocument.width()>0 && rectDocument.height()>0) rectDocument.translate(xoff, yoff); DEBUG_MSG("translated rectDocument=(" << rectDocument.xmin << ", " << rectDocument.ymin << ", " << rectDocument.width() << ", " << rectDocument.height() << ")\n"); } // Make the pane be on top so that any transient movements (of the // toolbar and scrollbars) will not cause any extra Expose events // TODO: RAISE: pane->raise(); // Resize the pane and scroll bars if (hscroll_visible) { hscroll->move(0, rectVisible.height()+toolbar_height); hscroll->show(); } else hscroll->hide(); if (vscroll_visible) { vscroll->move(rectVisible.width(), 0); vscroll->show(); } else vscroll->hide(); // Since QDPane is created with WResizeNoErase flag, we need to // redraw ourselves if the pane's size changed. if (pane->width()!=rectVisible.width() || pane->height()!=rectVisible.height()) do_redraw |= allow_redraw; pane->resize(rectVisible.width(), rectVisible.height()); hscroll->resize(rectVisible.width(), hscroll->height()); vscroll->resize(vscroll->width(), rectVisible.height()+toolbar_height); if (isToolBarEnabled()) { toolbar->resize(rectVisible.width(), toolbar->height()); toolbar->move(0, rectVisible.height()); if (!isToolBarStuck() && isToolBarShown()) { toolbar_shown=false; showToolBar(false); } } else if (toolbar) toolbar->move(0, main_widget->height()); // Now, when everything is in place set the stacking order properly if (toolbar) toolbar->raise(); hscroll->raise(); vscroll->raise(); // Adjust scroll bars parameters if (vscroll->isVisible()) { int value, sliderSize; value=rectVisible.ymin-rectDocument.ymin; if (value<0) value=0; sliderSize=rectVisible.height()<rectDocument.height() ? rectVisible.height() : rectDocument.height(); vscroll->setRange(0, rectDocument.height()-sliderSize); vscroll->setValue(value); vscroll->setSteps(rectDocument.height()/25, sliderSize); } if (hscroll->isVisible()) { int value, sliderSize; value=rectVisible.xmin-rectDocument.xmin; if (value<0) value=0; sliderSize=rectVisible.width()<rectDocument.width() ? rectVisible.width() : rectDocument.width(); hscroll->setRange(0, rectDocument.width()-sliderSize); hscroll->setValue(value); hscroll->setSteps(rectDocument.width()/25, sliderSize); } // Set rectangle mapper setMappers(); for(GPosition pos=map_areas;pos;++pos) map_areas[pos]->layout(GRect(0, 0, dimg->get_width(), dimg->get_height())); // Resizing margin caches: bm_cache.resize(rectDocument.width(), rectDocument.height(), rectVisible.width(), rectVisible.height()); pm_cache.resize(rectDocument.width(), rectDocument.height(), rectVisible.width(), rectVisible.height()); if (do_redraw) redraw(); if (update_tbar) updateToolBar(); DEBUG_MSG("QDBase::layout(): DONE\n"); }
void MapArea::updateCache(const GRect & pm_rect, const GP<GPixmap> & pm, GRectMapper * sdoc_mapper) // Takes the passed pixmap and updated the internal cache. // The pixmap should already contain the hyperlink draw in the // INACTIVE state. We will copy it and apply ACTIVE part here. // pm_rect is a rectangle in pane's coordinates where pm is supposed // to go to. sdoc_mapper maps screen coordinates to the coordinates // of the scaled document (see qd_base_paint.cpp) { DEBUG_MSG("MapArea::updateCache(): updating caches\n"); DEBUG_MAKE_INDENT(3); if (!isCacheUsed() || !pm || !pane) return; GRect brect=gmap_area->get_bound_rect(); mapper->map(brect); brect.inflate(3, 3); // To take into account edit controls GRect urect; if (urect.intersect(pm_rect, brect)) { for(GPosition pos=pieces;pos;++pos) { GP<MapPiece> piece=pieces[pos]; GRect prect=*piece; mapper->map(prect); GRect irect; if (irect.intersect(prect, urect)) { if (piece->getOnPixmap().isNull() || piece->getOffPixmap().isNull()) piece->createPixmaps(); QPixmap & on_pix=piece->getOnPixmap(); QPixmap & off_pix=piece->getOffPixmap(); // Now I need to make a copy of the area to be cached. // The problem is that I'll need to draw into the GPixmap // and I don't want to spoil the original. GP<GPixmap> ipix_off; GRect pix_rect=irect; pix_rect.translate(-pm_rect.xmin, -pm_rect.ymin); ipix_off=GPixmap::create(*pm, GRect(pix_rect.xmin, pm->rows()-pix_rect.ymax, pix_rect.width(), pix_rect.height())); GP<GPixmap> ipix_on=GPixmap::create(*ipix_off); // Now ipix_off and ipix_on contains the data, which can be modified. // Draw the map area into them draw(irect, ipix_on, APPLY_ACTIVE); // Dither pix_off and pix_on GRect drect=irect; sdoc_mapper->map(drect); if (qxImager) qxImager->dither(*ipix_on, drect.xmin, drect.ymin); if (qxImager) qxImager->dither(*ipix_off, drect.xmin, drect.ymin); // Now copy the GPixmaps into QPixmaps to be used for caching QDPainter p_off(&off_pix); p_off.drawPixmap(GRect(irect.xmin-prect.xmin, irect.ymin-prect.ymin, irect.width(), irect.height()), ipix_off); p_off.end(); QDPainter p_on(&on_pix); p_on.drawPixmap(GRect(irect.xmin-prect.xmin, irect.ymin-prect.ymin, irect.width(), irect.height()), ipix_on); p_on.end(); } } } }