khtml Library API Documentation

render_image.cpp

00001 
00025 //#define DEBUG_LAYOUT
00026 
00027 #include "render_image.h"
00028 #include "render_root.h"
00029 
00030 #include <qdrawutil.h>
00031 #include <qpainter.h>
00032 
00033 #include <kapplication.h>
00034 #include <kdebug.h>
00035 
00036 #include "css/csshelper.h"
00037 #include "misc/helper.h"
00038 #include "misc/htmlattrs.h"
00039 #include "misc/htmltags.h"
00040 #include "html/html_formimpl.h"
00041 #include "html/html_imageimpl.h"
00042 #include "html/dtd.h"
00043 #include "xml/dom2_eventsimpl.h"
00044 #include "html/html_documentimpl.h"
00045 #include "html/html_objectimpl.h"
00046 #include <math.h>
00047 
00048 using namespace DOM;
00049 using namespace khtml;
00050 
00051 // -------------------------------------------------------------------------
00052 
00053 RenderImage::RenderImage(HTMLElementImpl *_element)
00054     : RenderReplaced(_element)
00055 {
00056     image = 0;
00057     berrorPic = false;
00058     loadEventSent = false;
00059 
00060     setIntrinsicWidth( 0 );
00061     setIntrinsicHeight( 0 );
00062 }
00063 
00064 RenderImage::~RenderImage()
00065 {
00066     if(image) image->deref(this);
00067 }
00068 
00069 void RenderImage::setStyle(RenderStyle* _style)
00070 {
00071     RenderReplaced::setStyle(_style);
00072     // init RenderObject attributes
00073     setInline( style()->display()==INLINE );
00074     //setOverhangingContents(style()->height().isPercent());
00075     setSpecialObjects(true);
00076 
00077     CachedObject* co = style()->contentObject();
00078     if (co && image != co ) {
00079         if (image) image->deref(this);
00080         image = static_cast<CachedImage*>(style()->contentObject());
00081         if (image) image->ref(this);
00082     }
00083 }
00084 
00085 void RenderImage::setPixmap( const QPixmap &p, const QRect& r, CachedImage *o)
00086 {
00087     if(o != image) {
00088         RenderReplaced::setPixmap(p, r, o);
00089         return;
00090     }
00091 
00092     bool iwchanged = false;
00093 
00094     if(o->isErrorImage()) {
00095         int iw = p.width() + 8;
00096         int ih = p.height() + 8;
00097 
00098         // we have an alt and the user meant it (its not a text we invented)
00099         if ( element() && !alt.isEmpty() && !element()->getAttribute( ATTR_ALT ).isNull()) {
00100             const QFontMetrics &fm = style()->fontMetrics();
00101             QRect br = fm.boundingRect (  0, 0, 1024, 256, Qt::AlignAuto|Qt::WordBreak, alt.string() );
00102             if ( br.width() > iw )
00103                 iw = br.width();
00104             if ( br.height() > ih )
00105                 ih = br.height();
00106         }
00107 
00108         if ( iw != intrinsicWidth() ) {
00109             setIntrinsicWidth( iw );
00110             iwchanged = true;
00111         }
00112         if ( ih != intrinsicHeight() ) {
00113             setIntrinsicHeight( ih );
00114             iwchanged = true;
00115         }
00116         if ( element() && element()->id() == ID_OBJECT ) {
00117             static_cast<HTMLObjectElementImpl*>(  element() )->renderAlternative();
00118             return;
00119         }
00120     }
00121     berrorPic = o->isErrorImage();
00122 
00123     bool needlayout = false;
00124 
00125     // Image dimensions have been changed, see what needs to be done
00126     if( o->pixmap_size().width() != intrinsicWidth() ||
00127        o->pixmap_size().height() != intrinsicHeight() || iwchanged )
00128     {
00129 //          qDebug("image dimensions have been changed, old: %d/%d  new: %d/%d",
00130 //                 intrinsicWidth(), intrinsicHeight(),
00131 //               o->pixmap_size().width(), o->pixmap_size().height());
00132 
00133         if(!o->isErrorImage()) {
00134             setIntrinsicWidth( o->pixmap_size().width() );
00135             setIntrinsicHeight( o->pixmap_size().height() );
00136         }
00137 
00138         // lets see if we need to relayout at all..
00139         int oldwidth = m_width;
00140         int oldheight = m_height;
00141         calcWidth();
00142         calcHeight();
00143 
00144         if(iwchanged || m_width != oldwidth || m_height != oldheight)
00145             needlayout = true;
00146 
00147         m_width = oldwidth;
00148         m_height = oldheight;
00149     }
00150 
00151     pix = p;
00152 
00153     if(needlayout)
00154     {
00155         setLayouted(false);
00156         setMinMaxKnown(false);
00157 
00158 //         kdDebug( 6040 ) << "m_width: : " << m_width << " height: " << m_height << endl;
00159 //         kdDebug( 6040 ) << "Image: size " << m_width << "/" << m_height << endl;
00160     }
00161     else
00162     {
00163         bool completeRepaint = !resizeCache.isNull();
00164         int cHeight = contentHeight();
00165         int scaledHeight = intrinsicHeight() ? ((o->valid_rect().height()*cHeight)/intrinsicHeight()) : 0;
00166 
00167         // don't bog down X server doing xforms
00168         if(completeRepaint && cHeight >= 5 &&  o->valid_rect().height() < intrinsicHeight() &&
00169            (scaledHeight / (cHeight/5) == resizeCache.height() / (cHeight/5)))
00170             return;
00171 
00172         resizeCache = QPixmap(); // for resized animations
00173         if(completeRepaint)
00174             repaintRectangle(borderLeft()+paddingLeft(), borderTop()+paddingTop(), contentWidth(), contentHeight());
00175         else
00176         {
00177             repaintRectangle(r.x() + borderLeft() + paddingLeft(), r.y() + borderTop() + paddingTop(),
00178                              r.width(), r.height());
00179         }
00180     }
00181 }
00182 
00183 void RenderImage::paintObject(QPainter *p, int /*_x*/, int /*_y*/, int /*_w*/, int /*_h*/, int _tx, int _ty)
00184 {
00185     // add offset for relative positioning
00186     if(isRelPositioned())
00187         relativePositionOffset(_tx, _ty);
00188 
00189     int cWidth = contentWidth();
00190     int cHeight = contentHeight();
00191     int leftBorder = borderLeft();
00192     int topBorder = borderTop();
00193     int leftPad = paddingLeft();
00194     int topPad = paddingTop();
00195 
00196     if (khtml::printpainter && !root()->paintImages())
00197         return;
00198 
00199     //kdDebug( 6040 ) << "    contents (" << contentWidth << "/" << contentHeight << ") border=" << borderLeft() << " padding=" << paddingLeft() << endl;
00200     if ( pix.isNull() || berrorPic)
00201     {
00202         if(cWidth > 2 && cHeight > 2)
00203         {
00204             if ( !berrorPic ) {
00205                 //qDebug("qDrawShadePanel %d/%d/%d/%d", _tx + leftBorder, _ty + topBorder, cWidth, cHeight);
00206                 qDrawShadePanel( p, _tx + leftBorder + leftPad, _ty + topBorder + topPad, cWidth, cHeight,
00207                                  KApplication::palette().inactive(), true, 1 );
00208             }
00209             if(berrorPic && !pix.isNull() && (cWidth >= pix.width()+4) && (cHeight >= pix.height()+4) )
00210             {
00211                 QRect r(pix.rect());
00212                 r = r.intersect(QRect(0, 0, cWidth-4, cHeight-4));
00213                 p->drawPixmap( QPoint( _tx + leftBorder + leftPad+2, _ty + topBorder + topPad+2), pix, r );
00214             }
00215             if(!alt.isEmpty()) {
00216                 QString text = alt.string();
00217                 p->setFont(style()->font());
00218                 p->setPen( style()->color() );
00219                 int ax = _tx + leftBorder + leftPad + 2;
00220                 int ay = _ty + topBorder + topPad + 2;
00221                 const QFontMetrics &fm = style()->fontMetrics();
00222                 if (cWidth>5 && cHeight>=fm.height())
00223                     p->drawText(ax, ay+1, cWidth - 4, cHeight - 4, Qt::WordBreak, text );
00224             }
00225         }
00226     }
00227     else if (image && !image->isTransparent())
00228     {
00229         if ( (cWidth != intrinsicWidth() ||  cHeight != intrinsicHeight()) &&
00230              pix.width() > 0 && pix.height() > 0 && image->valid_rect().isValid())
00231         {
00232             if (resizeCache.isNull() && cWidth && cHeight)
00233             {
00234                 QRect scaledrect(image->valid_rect());
00235 //                 kdDebug(6040) << "time elapsed: " << dt->elapsed() << endl;
00236 //                  kdDebug( 6040 ) << "have to scale: " << endl;
00237 //                  qDebug("cw=%d ch=%d  pw=%d ph=%d  rcw=%d, rch=%d",
00238 //                          cWidth, cHeight, intrinsicWidth(), intrinsicHeight(), resizeCache.width(), resizeCache.height());
00239                 QWMatrix matrix;
00240                 matrix.scale( (float)(cWidth)/intrinsicWidth(),
00241                               (float)(cHeight)/intrinsicHeight() );
00242                 resizeCache = pix.xForm( matrix );
00243                 scaledrect.setWidth( ( cWidth*scaledrect.width() ) / intrinsicWidth() );
00244                 scaledrect.setHeight( ( cHeight*scaledrect.height() ) / intrinsicHeight() );
00245 //                   qDebug("resizeCache size: %d/%d", resizeCache.width(), resizeCache.height());
00246 //                   qDebug("valid: %d/%d, scaled: %d/%d",
00247 //                          image->valid_rect().width(), image->valid_rect().height(),
00248 //                          scaledrect.width(), scaledrect.height());
00249 
00250                 // sometimes scaledrect.width/height are off by one because
00251                 // of rounding errors. if the image is fully loaded, we
00252                 // make sure that we don't do unnecessary resizes during painting
00253                 QSize s(scaledrect.size());
00254                 if(image->valid_rect().size() == QSize( intrinsicWidth(), intrinsicHeight() )) // fully loaded
00255                     s = QSize(cWidth, cHeight);
00256                 if(QABS(s.width() - cWidth) < 2) // rounding errors
00257                     s.setWidth(cWidth);
00258                 if(resizeCache.size() != s)
00259                     resizeCache.resize(s);
00260 
00261                 p->drawPixmap( QPoint( _tx + leftBorder + leftPad, _ty + topBorder + topPad),
00262                                resizeCache, scaledrect );
00263             }
00264             else
00265                 p->drawPixmap( QPoint( _tx + leftBorder + leftPad, _ty + topBorder + topPad), resizeCache );
00266         }
00267         else
00268         {
00269             // we might be just about switching images
00270             // so pix contains the old one (we want to paint), but image->valid_rect is still invalid
00271             // so use intrinsic Size instead.
00272             // ### maybe no progressive loading for the second image ?
00273             QRect rect(image->valid_rect().isValid() ? image->valid_rect()
00274                        : QRect(0, 0, intrinsicWidth(), intrinsicHeight()));
00275 
00276             QPoint offs( _tx + leftBorder + leftPad, _ty + topBorder + topPad);
00277 //             qDebug("normal paint rect %d/%d/%d/%d", rect.x(), rect.y(), rect.width(), rect.height());
00278 //             rect = rect & QRect( 0 , y - offs.y() - 10, w, 10 + y + h  - offs.y());
00279 
00280 //             qDebug("normal paint rect after %d/%d/%d/%d", rect.x(), rect.y(), rect.width(), rect.height());
00281 //             qDebug("normal paint: offs.y(): %d, y: %d, diff: %d", offs.y(), y, y - offs.y());
00282 //             qDebug("");
00283 
00284 //           p->setClipRect(QRect(x,y,w,h));
00285 
00286 
00287 //             p->drawPixmap( offs.x(), y, pix, rect.x(), rect.y(), rect.width(), rect.height() );
00288              p->drawPixmap(offs, pix, rect);
00289 
00290         }
00291     }
00292     if(style()->outlineWidth())
00293         paintOutline(p, _tx, _ty, width(), height(), style());
00294 }
00295 
00296 void RenderImage::layout()
00297 {
00298     KHTMLAssert(!layouted());
00299     KHTMLAssert( minMaxKnown() );
00300 
00301     short oldwidth = m_width;
00302     int oldheight = m_height;
00303 
00304     // minimum height
00305     m_height = image && image->isErrorImage() ? intrinsicHeight() : 0;
00306 
00307     calcWidth();
00308     calcHeight();
00309 
00310     // if they are variable width and we calculate a huge height or width, we assume they
00311     // actually wanted the intrinsic width.
00312     if ( m_width > 2048 && !style()->width().isFixed() )
00313     m_width = intrinsicWidth();
00314     if ( m_height > 2048 && !style()->height().isFixed() )
00315     m_height = intrinsicHeight();
00316     // limit total size to not run out of memory when doing the xform call.
00317     if ( m_width * m_height > 2048*2048 ) {
00318     float scale = sqrt( m_width*m_height / ( 2048.*2048. ) );
00319     m_width = (int) (m_width/scale);
00320     m_height = (int) (m_height/scale);
00321     }
00322 
00323     if ( m_width != oldwidth || m_height != oldheight )
00324         resizeCache = QPixmap();
00325 
00326     setLayouted();
00327 }
00328 
00329 void RenderImage::notifyFinished(CachedObject *finishedObj)
00330 {
00331     if (image == finishedObj && !loadEventSent && element()) {
00332         loadEventSent = true;
00333         element()->dispatchHTMLEvent(
00334             image->isErrorImage() ? EventImpl::ERROR_EVENT : EventImpl::LOAD_EVENT,
00335             false,false);
00336     }
00337 }
00338 
00339 bool RenderImage::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty)
00340 {
00341     bool inside = RenderReplaced::nodeAtPoint(info, _x, _y, _tx, _ty);
00342 
00343     if (inside && element()) {
00344         int tx = _tx + m_x;
00345         int ty = _ty + m_y;
00346         if (isRelPositioned())
00347             relativePositionOffset(tx, ty);
00348 
00349         HTMLImageElementImpl* i = element()->id() == ID_IMG ? static_cast<HTMLImageElementImpl*>(element()) : 0;
00350         HTMLMapElementImpl* map;
00351         if (i && i->getDocument()->isHTMLDocument() &&
00352             (map = static_cast<HTMLDocumentImpl*>(i->getDocument())->getMap(i->imageMap()))) {
00353             // we're a client side image map
00354             inside = map->mapMouseEvent(_x - tx, _y - ty, contentWidth(), contentHeight(), info);
00355         }
00356     }
00357 
00358     return inside;
00359 }
00360 
00361 void RenderImage::updateFromElement()
00362 {
00363     DOMString u = element()->id() == ID_OBJECT ?
00364                   element()->getAttribute(ATTR_DATA) : element()->getAttribute(ATTR_SRC);
00365 
00366     if (!u.isEmpty()) {
00367         CachedImage *new_image = element()->getDocument()->docLoader()->
00368                                  requestImage(khtml::parseURL(u));
00369 
00370         if(new_image && new_image != image && (!style() || !style()->contentObject())) {
00371             loadEventSent = false;
00372             CachedImage* oldimage = image;
00373             image = new_image;
00374             image->ref(this);
00375             if(oldimage) oldimage->deref(this);
00376             berrorPic = image->isErrorImage();
00377         }
00378     }
00379 
00380     if (element()->id() == ID_INPUT)
00381         alt = static_cast<HTMLInputElementImpl*>(element())->altText();
00382     else if (element()->id() == ID_IMG)
00383         alt = static_cast<HTMLImageElementImpl*>(element())->altText();
00384 }
00385 
00386 bool RenderImage::complete() const
00387 {
00388     // "complete" means that the image has been loaded
00389     // but also that its width/height (contentWidth(),contentHeight()) have been calculated.
00390     return !pix.isNull() && layouted();
00391 }
00392 
00393 short RenderImage::calcReplacedWidth() const
00394 {
00395     const Length w = style()->width();
00396 
00397     if (w.isVariable()) {
00398         const Length h = style()->height();
00399         if ( m_intrinsicHeight > 0 && ( h.isPercent() || h.isFixed() ) )
00400             return ( ( h.isPercent() ? calcReplacedHeight() : h.value() )*intrinsicWidth() ) / m_intrinsicHeight;
00401     }
00402 
00403     return RenderReplaced::calcReplacedWidth();
00404 }
00405 
00406 int RenderImage::calcReplacedHeight() const
00407 {
00408     const Length h = style()->height();
00409 
00410     if (h.isVariable()) {
00411         const Length w = style()->width();
00412         if( m_intrinsicWidth > 0 && ( w.isFixed() || w.isPercent() ))
00413             return (( w.isPercent() ? calcReplacedWidth() : w.value() ) * intrinsicHeight()) / m_intrinsicWidth;
00414 
00415     }
00416 
00417     return RenderReplaced::calcReplacedHeight();
00418 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:16:39 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001