Bug 42029

Summary: images in html should be scalable like fonts
Product: [Applications] konqueror Reporter: james
Component: khtmlAssignee: Konqueror Developers <konq-bugs>
Status: RESOLVED FIXED    
Severity: wishlist CC: dominik.karall, elias_kunnas, fh, joachim.eibl, mpartap, taj
Priority: NOR    
Version: unspecified   
Target Milestone: ---   
Platform: Mandrake RPMs   
OS: Linux   
Latest Commit: Version Fixed In:
Sentry Crash Report:

Description james 2002-05-02 17:07:50 UTC
(*** This bug was imported into bugs.kde.org ***)

Package:           khtml
Version:           KDE 3.0.0 
Severity:          wishlist
Installed from:    Mandrake RPMs
Compiler:          gcc 2.95
OS:                Linux
OS/Compiler notes: Not Specified

Konqueror has a beautiful feature in that it provides a feature that will scale up/down the fon sizes in a document. This is IMMENSELY useful for sites with tiny fonts or those of us with high resolution displays. However there also needs to be a facility to scale the bitmaps contained in the document for a similar reason. Often times bitmaps contain text or other info that is obscurred when viewed at higher resolution. The code already exists in khtml to scale all the images (as is done explicitly when image sizes are specified in the HTML). There should also be a scale/zoom factor to which all other measurements are multiplied.

For example click on a button to increase the zoom level 10% or 20% so a graphic that is specified to be 100x200 will be 110x220 or 120x240.

Not only should this be easy to implement but to the best of my knowledge is a feature not available with any other browser.

This could be keyed to a DPI setting etc. too.

Better do it before someone patents the idea...

(Submitted via bugs.kde.org)
Comment 1 Maksim Orlovich 2004-06-30 22:51:02 UTC
*** Bug 83359 has been marked as a duplicate of this bug. ***
Comment 2 Maksim Orlovich 2004-08-22 17:45:09 UTC
*** Bug 87728 has been marked as a duplicate of this bug. ***
Comment 3 Taj Morton 2004-08-22 17:50:45 UTC
Opera has this feature.
Comment 4 Roey Katz 2004-10-25 19:19:30 UTC
There exists an extension for Firefox/Mozilla which provides per-image zoom functionality as described above.  
Comment 5 Fela Winkelmolen 2005-04-21 17:04:37 UTC
Yes, it would be great to have this feature in kde-4.0, specially for people using a great resolution. All objects (text, images, flash objects, etc.) should zoom accordingly, Opera does it really wel.
As the original poster already sayed it shouldn't be so difficult to implement. 
Comment 6 Tommi Tervo 2005-08-24 14:42:09 UTC
*** Bug 100284 has been marked as a duplicate of this bug. ***
Comment 7 Thiago Macieira 2005-09-10 03:12:01 UTC
*** Bug 100284 has been marked as a duplicate of this bug. ***
Comment 8 Thiago Macieira 2005-09-10 03:12:16 UTC
*** Bug 112324 has been marked as a duplicate of this bug. ***
Comment 9 Tommi Tervo 2005-10-26 14:49:48 UTC
*** Bug 115115 has been marked as a duplicate of this bug. ***
Comment 10 Dominik Karall 2005-10-26 14:58:36 UTC
*** This bug has been confirmed by popular vote. ***
Comment 11 Maksim Orlovich 2005-10-26 15:06:16 UTC
*** Bug 87862 has been marked as a duplicate of this bug. ***
Comment 12 Germain Garand 2007-02-05 15:18:44 UTC
SVN commit 630479 by ggarand:

A simple "page scaling" zoom à la Opera. 
For now, it replaces the font scaling action, as the method names used for it 
in KHTML part are better suited to a real zoom (e.g. setZoomFactor).
Need to reintroduce pure font-scaling under another name..

Also introduce an alternate, non-blitting painting mode for being able to do 
fixed positioning again.
QScrollArea's quite a bit on the terse side, so there was no such thing ;(
Should fix widget jittering issues as a bonus.


BUG: 42029, 68721



 M  +2 -2      css/cssstyleselector.cpp  
 M  +9 -4      khtml_part.cpp  
 M  +111 -18   khtmlview.cpp  
 M  +20 -1     khtmlview.h  
 M  +15 -8     rendering/render_canvas.cpp  
 M  +1 -1      rendering/render_replaced.cpp  
 M  +2 -0      xml/dom_nodeimpl.cpp  


--- trunk/KDE/kdelibs/khtml/css/cssstyleselector.cpp #630478:630479
@@ -749,7 +749,7 @@
     // FIXME: We only need to invalidate the fixed regions when scrolling.  It's total overkill to
     // prevent the entire view from blitting on a scroll.
     if (style->hasFixedBackgroundImage() && view)
-        view->useSlowRepaints();
+        view->setHasStaticBackground();
 }
 
 unsigned int CSSStyleSelector::addInlineDeclarations(DOM::ElementImpl* e,
@@ -2414,7 +2414,7 @@
             p = ABSOLUTE; break;
         case CSS_VAL_FIXED:
             {
-                view->useSlowRepaints();
+                view->setHasStaticBackground();
                 p = FIXED;
                 break;
             }
--- trunk/KDE/kdelibs/khtml/khtml_part.cpp #630478:630479
@@ -5754,11 +5754,16 @@
   if (d->m_zoomFactor == percent) return;
   d->m_zoomFactor = percent;
 
-  if(d->m_doc) {
+  if(d->m_view) {
       QApplication::setOverrideCursor( Qt::WaitCursor );
-    if (d->m_doc->styleSelector())
-      d->m_doc->styleSelector()->computeFontSizes(d->m_doc->paintDeviceMetrics(), d->m_zoomFactor);
-    d->m_doc->recalcStyle( NodeImpl::Force );
+
+// ### make the increasing/decreasing of font size a separate setting
+//
+//    if (d->m_doc->styleSelector())
+//      d->m_doc->styleSelector()->computeFontSizes(d->m_doc->paintDeviceMetrics(), d->m_zoomFactor);
+//    d->m_doc->recalcStyle( NodeImpl::Force );
+
+    d->m_view->setZoomLevel( d->m_zoomFactor );
     QApplication::restoreOverrideCursor();
   }
 
--- trunk/KDE/kdelibs/khtml/khtmlview.cpp #630478:630479
@@ -186,10 +186,11 @@
 	    oldUnderMouse->deref();
         oldUnderMouse = 0;
         linkPressed = false;
-        useSlowRepaints = false;
+        staticWidget = false;
 	tabMovePending = false;
 	lastTabbingDirection = true;
 	pseudoFocusNode = PFNone;
+	zoomLevel = 100;
 #ifndef KHTML_NO_SCROLLBARS
         //We don't turn off the toolbars here
 	//since if the user turns them
@@ -287,6 +288,8 @@
         scrollSuspended = false;
     }
 
+    bool haveZoom() const { return zoomLevel != 100; }
+
 #ifndef KHTML_NO_CARET
     /** this function returns an instance of the caret view context. If none
      * exists, it will be instantiated.
@@ -324,9 +327,10 @@
     Qt::ScrollBarPolicy hpolicy;
     bool prevScrollbarVisible:1;
     bool linkPressed:1;
-    bool useSlowRepaints:1;
+    bool staticWidget:1;
     bool ignoreWheelEvents:1;
 
+    int zoomLevel;
     int borderX, borderY;
     KSimpleConfig *formCompletions;
 
@@ -510,9 +514,13 @@
     QSize s = viewport()->size();
     resizeContents(s.width(), s.height());
 
-    // ### we'll enable redirection of khtmlview here
-    //     when event and painting issues have been thoroughly worked out
-    // m_kwp->setIsRedirected(true);
+    // ### we'll enable redirection of khtmlview
+    //     when event issues have been thoroughly worked out
+
+    bool redirect = false; // m_part->parentPart() && !isFrame() ...
+
+    m_kwp->setIsRedirected( redirect );
+    d->staticWidget = redirect;
 }
 
 void KHTMLView::clear()
@@ -640,6 +648,7 @@
 
 void KHTMLView::updateContents(int x, int y, int w, int h)
 {
+    applyTransforms(x, y, w, h);
     widget()->update(x, y, w, h);
 }
 
@@ -650,6 +659,7 @@
 
 void KHTMLView::repaintContents(int x, int y, int w, int h)
 {
+    applyTransforms(x, y, w, h);
     widget()->repaint(x, y, w, h);
 }
 
@@ -658,6 +668,42 @@
     repaintContents( r.x(), r.y(), r.width(), r.height() );
 }
 
+void KHTMLView::applyTransforms( int& x, int& y, int& w, int& h) const
+{
+     if (d->staticWidget) {
+        x -= contentsX();
+        y -= contentsY();
+    }
+    if (d->haveZoom()) {
+        const int z = d->zoomLevel;
+        x = x*z/100;
+        y = y*z/100;
+        w = w*z/100;
+        h = h*z/100;
+    }
+}
+
+void KHTMLView::revertTransforms( int& x, int& y, int& w, int& h) const
+{
+     if (d->staticWidget) {
+        x += contentsX();
+        y += contentsY();
+    }
+    if (d->haveZoom()) {
+        const int z = d->zoomLevel;
+        x = x*100/z;
+        y = y*100/z;
+        w = w*100/z;
+        h = h*100/z;
+    }
+}
+
+void KHTMLView::revertTransforms( int& x, int& y ) const
+{
+    int dummy = 0;
+    revertTransforms(x, y, dummy, dummy);
+}
+   
 void KHTMLView::resizeEvent (QResizeEvent* e)
 {
     int dw = e->oldSize().width() - e->size().width();
@@ -692,16 +738,30 @@
 
     QRect r = e->rect();
     QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+    if (d->staticWidget) {
+        QPoint off(contentsX(),contentsY());
+        p.translate(-off);
+        r.translate(off);
+    }
 
     r = r.intersect(v);
     if (!r.isValid() || r.isEmpty()) return;
-    p.setClipRect(v);
+    
+    if (d->haveZoom()) {
+        p.scale( d->zoomLevel/100., d->zoomLevel/100.);
 
+        r.setX(r.x()*100/d->zoomLevel);
+        r.setY(r.y()*100/d->zoomLevel);
+        r.setWidth(r.width()*100/d->zoomLevel);
+        r.setHeight(r.height()*100/d->zoomLevel);
+    }
+    p.setClipRect(r);
+  
     int ex = r.x();
     int ey = r.y();
     int ew = r.width();
     int eh = r.height();
-    
+
     if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
         p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base));
         return;
@@ -882,6 +942,23 @@
     QScrollArea::closeEvent( ev );
 }
 
+void KHTMLView::setZoomLevel(int percent)
+{
+    percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent);
+    int oldpercent = d->zoomLevel;
+    d->zoomLevel = percent;
+    if (percent != oldpercent) {
+        if (d->layoutSchedulingEnabled)
+            layout();
+        widget()->update();
+    }
+}
+
+int KHTMLView::zoomLevel() const
+{
+    return d->zoomLevel;
+}
+
 //
 // Event Handling
 //
@@ -898,6 +975,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n";
 
@@ -1018,6 +1096,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
 
@@ -1087,6 +1166,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove );
     // Do not modify :hover/:active state while mouse is pressed.
@@ -1240,6 +1320,7 @@
 
     int xm = _mouse->x();
     int ym = _mouse->y();
+    revertTransforms(xm, ym);
 
     DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease );
 
@@ -3001,10 +3082,11 @@
 }
 
 
-void KHTMLView::useSlowRepaints()
+void KHTMLView::setHasStaticBackground()
 {
-    d->useSlowRepaints = true;
-//    setStaticBackground(true); ### ?? FIXME
+    if (!d->staticWidget)
+        widget()->move(0,0);
+    d->staticWidget = true;
 }
 
 
@@ -3136,6 +3218,7 @@
     int exceptioncode = 0;
     int pageX = _mouse->x();
     int pageY = _mouse->y();
+    revertTransforms(pageX, pageY);
     int clientX = pageX - contentsX();
     int clientY = pageY - contentsY();
     int screenX = _mouse->globalX();
@@ -3164,7 +3247,7 @@
 
     // mouseout/mouseover
     if (setUnder && d->oldUnderMouse != targetNode) {
-        if (d->oldUnderMouse && d->oldUnderMouse->getDocument() != targetNode->getDocument()) {
+        if (d->oldUnderMouse && d->oldUnderMouse->getDocument() != m_part->xmlDocImpl()) {
             d->oldUnderMouse->deref();
             d->oldUnderMouse = 0;
         }
@@ -3436,16 +3519,29 @@
                      horizontalScrollBar()->maximum()-horizontalScrollBar()->value() : horizontalScrollBar()->value();
     d->contentsY = verticalScrollBar()->value();
 
-    if (d->useSlowRepaints) {
-        widget()->blockSignals( true );
-        widget()->move( widget()->pos().x() + dx, widget()->pos().y() +dy );
-        widget()->blockSignals( false );
+    if ( d->staticWidget ) {
         widget()->repaint();
         return;
     }
     QScrollArea::scrollContentsBy(dx, dy);
 }
 
+void KHTMLView::addChild(QWidget * child, int x, int y)
+{
+    if (!child) 
+        return;
+
+    if (child->parent() != widget())
+        child->setParent( widget() );
+    
+    // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em)
+
+    if (!d->staticWidget)
+        child->move(x, y);
+    else
+        child->move(x-contentsX(), y-contentsY());
+}
+
 void KHTMLView::timerEvent ( QTimerEvent *e )
 {
 //    kDebug() << "timer event " << e->timerId() << endl;
@@ -3518,9 +3614,6 @@
 	}
     }
 
-//    setStaticBackground(d->useSlowRepaints); ?? ### FIXME
-
-//        kDebug() << "scheduled repaint "<< d->repaintTimerId  << endl;
     if (d->repaintTimerId)
         killTimer(d->repaintTimerId);
     d->repaintTimerId = 0;
--- trunk/KDE/kdelibs/khtml/khtmlview.h #630478:630479
@@ -98,6 +98,7 @@
     friend class DOM::HTMLFormElementImpl;
     friend class DOM::HTMLAnchorElementImpl;
     friend class DOM::HTMLInputElementImpl;
+    friend class DOM::NodeImpl;
     friend class DOM::DocumentImpl;
     friend class KHTMLPart;
     friend class khtml::RenderCanvas;
@@ -257,6 +258,8 @@
      */
     void updateContents( const QRect& r );
     void updateContents(int x, int y, int w, int h);
+    
+    void addChild(QWidget *child, int dx, int dy);
 
     /**
      * Requests an immediate repaint of the content area
@@ -264,6 +267,18 @@
      */
     void repaintContents( const QRect& r );
     void repaintContents(int x, int y, int w, int h);
+    
+    /**
+     * Apply a zoom level to the content area
+     * @param percent a zoom level expressed as a percentage
+     */
+    void setZoomLevel( int percent );
+    
+    /**
+     * Retrieve the current zoom level
+     *
+     */
+    int zoomLevel() const;
 
 public Q_SLOTS:
     /**
@@ -393,7 +408,10 @@
     QMap< DOM::ElementImpl*, QChar > buildFallbackAccessKeys() const;
     void displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks );
 
-    void useSlowRepaints();
+    void setHasStaticBackground();
+    void applyTransforms( int& x, int& y, int& w, int& h) const;
+    void revertTransforms( int& x, int& y, int& w, int& h) const;
+    void revertTransforms( int& x, int& y ) const;
 
     void setIgnoreWheelEvents(bool e);
 
@@ -699,6 +717,7 @@
 
     void setWidgetVisible(::khtml::RenderWidget*, bool visible);
 
+
     int _width;
     int _height;
 
--- trunk/KDE/kdelibs/khtml/rendering/render_canvas.cpp #630478:630479
@@ -220,35 +220,42 @@
      // update our cached document size
     int hDocH = m_cachedDocHeight = docHeight();
     int hDocW = m_cachedDocWidth = docWidth();
+    
+    int zLevel = m_view? m_view->zoomLevel() : 100;
+    hDocW = hDocW*zLevel/100;
+    hDocH = hDocH*zLevel/100;
 
     if (!m_pagedMode && m_view) {
 
         bool vss = !m_view->verticalScrollBar()->isHidden();
         bool hss = !m_view->horizontalScrollBar()->isHidden();
         QSize s = m_view->maximumViewportSize();
-        if ( m_cachedDocWidth > s.width() )
+
+        int zoomedDocWidth = m_cachedDocWidth*zLevel/100;
+        int zoomedDocHeight = m_cachedDocHeight*zLevel/100;
+        if ( zoomedDocWidth > s.width() )
             s.setWidth( s.width()-m_view->verticalScrollBar()->sizeHint().width() );
-        if ( m_cachedDocHeight > s.height() )
+        if ( zoomedDocHeight > s.height() )
             s.setHeight( s.height()-m_view->horizontalScrollBar()->sizeHint().height() );
    
         // if we are about to show a scrollbar, and the document is sized to the viewport w or h,
         // then reserve the scrollbar space so that it doesn't trigger the _other_ scrollbar
 
         if (!vss && m_width - m_view->verticalScrollBar()->sizeHint().width() == s.width() &&
-            m_cachedDocWidth <= m_width)
-            hDocW = qMin( m_cachedDocWidth, s.width() );
+            zoomedDocWidth <= m_width)
+            hDocW = qMin( zoomedDocWidth, s.width() );
 
         if (!hss && m_height - m_view->horizontalScrollBar()->sizeHint().height() == s.height() &&
-            m_cachedDocHeight <= m_height)
-            hDocH = qMin( m_cachedDocHeight, s.height() );
+            zoomedDocHeight <= m_height)
+            hDocH = qMin( zoomedDocHeight, s.height() );
 
         // likewise, if a scrollbar is shown, and we have a cunning plan to turn it off,
         // think again if we are falling downright in the hysteresis zone
 
-        if (vss && s.width() > m_cachedDocWidth && m_cachedDocWidth > m_view->visibleWidth())
+        if (vss && s.width() > zoomedDocWidth && zoomedDocWidth > m_view->visibleWidth())
             hDocW = s.width()+1;
 
-        if (hss && s.height() > m_cachedDocHeight && m_cachedDocHeight > m_view->visibleHeight())
+        if (hss && s.height() > zoomedDocHeight && zoomedDocHeight > m_view->visibleHeight())
             hDocH = s.height()+1;
 
         m_view->resizeContents(hDocW, hDocH);
--- trunk/KDE/kdelibs/khtml/rendering/render_replaced.cpp #630478:630479
@@ -524,7 +524,7 @@
     }
     m_view->setWidgetVisible(this, true);
     if (!khtmlw)
-        m_widget->move( xPos, yPos );
+        m_view->addChild( m_widget, xPos, yPos );
     else
         m_widget->move( xPos, -500000 +yPos);
     m_widget->show();
--- trunk/KDE/kdelibs/khtml/xml/dom_nodeimpl.cpp #630478:630479
@@ -503,6 +503,8 @@
     int exceptioncode = 0;
     int pageX = _mouse->x();
     int pageY = _mouse->y();
+    if ( getDocument()->view() )
+        getDocument()->view()->revertTransforms( pageX, pageY );
     int clientX = pageX;
     int clientY = pageY;
     if ( getDocument()->view() )