[prev in list] [next in list] [prev in thread] [next in thread] 

List:       koffice-devel
Subject:    patch: kword repaint performance issues - pixmap caching
From:       Jaymz Julian <jaymz () dspaudio ! com>
Date:       2002-08-09 18:10:35
[Download RAW message or body]

hi!

Short version: this patch implents some caching in kotext's paragraph
drawing, which makes kword responsive on a p100 (tested on a p100 with
32meg ram).  It isn't ready for the prime time, but I'm looking for people
to test it, look at it, and tell me what I did wrong ^_^.  Thanks.

Long Version:  For those who don't know, we (computerbank australia,
http://www.computerbank.org.au, our wepage sucks, tho) give away low end
pentium systems (p133 with 32meg ram and 1 gig disk space, usually),
running kde and koffice.  And this was fine on koffice 1.1, blha blah,
anyhow, so we needed features from 1.2 etc, not fast enough, whatever, ya
know, the usual.  So no, doing this isn't stupid... on that box, a 5 line
paragraph will render *10 chareceters at a time* with a patched qt, and
will fall *way* behind the text on qt 3.0.5.

So, this patch is the first working solution we've managed to come up with
which didn't involve rewriting the entire qrichtext widget :-p (my time is
very limited, since I'm busy with other projects a lot of the time, and I
also have some typing injuries which put a stop to those 72 hour hacking
sessions :-p), which basically involves caching the little bits of text
that QT renders, onto a QPixmap, instead of redrawing everything all the
time.  

How it works is pretty simple... basically it checks if it's drawing the
same thing onto an area of the same size, and if ti is, uses what it drew
last time.  This works a lot better than I expected, and makes kword 1.2
actully usable on our systems, as opposed to only on p6 class systems.  
yay!  (I was worriwd that the overhead of swapping this stuff in and out
of ram would hurt, but it hurts far less than the CPU overhead of
redrawing).

I know about the problem with it not always invalidating the cached object
when the selection changes... I'll work out exactly how selections work
and fix that on sunday, most likely, if not then monday, heh.  I probably
missed some other obvious events, which I'd like people to tell me about,
since this is very not tested, and you know, it's bad to give that to
recipents who've never seen a comuter ^_^.  ("oh, recompile kword" "what's
a recompile?  and what's a kword" "it's that thing you type into" "oh, you
mean untitled document?" :-p :-p)

Anyhow, I'm looking forward to hearing feedback on this. nthanks in
advance etc ^_^.

	- Jaymz

p.s. sorry if this sounds incoherent... sleep starved :-p.

-- 
Jaymz Julian aka A Life in Hell
Coder, Visionary, Fat Ass.
"Any attempt to brew coffee with a teapot should result in the error code
"418 I'm a teapot". The resulting entity body MAY be short and stout."
-- HTCPCP Spec, RFC 2324

["kword-repaint-cache-v1.patch" (TEXT/PLAIN)]

Index: kotextparag.cc
===================================================================
RCS file: /home/kde/koffice/lib/kotext/kotextparag.cc,v
retrieving revision 1.98
diff -u -3 -p -r1.98 kotextparag.cc
--- kotextparag.cc	7 Aug 2002 16:56:33 -0000	1.98
+++ kotextparag.cc	9 Aug 2002 17:47:19 -0000
@@ -26,6 +26,7 @@
 #include <klocale.h>
 #include <assert.h>
 #include <kdebug.h>
+#include <stdlib.h>
 
 /////
 
@@ -449,14 +450,13 @@ void KoTextParag::drawParagString( QPain
     int h_pix = zh->layoutUnitToPixelY( lastY, h );
     //kdDebug(32500) << "KoTextParag::drawParagString h(LU)=" << h << " lastY(LU)=" \
                << lastY
     //        << " h(PIX)=" << h_pix << " lastY(PIX)=" << lastY_pix << endl;
-
-    if ( lastFormat->textBackgroundColor().isValid() )
-        painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, \
                lastFormat->textBackgroundColor() );
-
-    // don't want to draw line breaks but want them when drawing formatting chars
+    
+    // metric calculation stuff moved from below
     int draw_len = len;
     int draw_startX = startX;
     int draw_bw = bw_pix;
+
+    // don't want to draw line breaks but want them when drawing formatting chars
     if ( at( start + len - 1 )->c == '\n' )
     {
       draw_len--;
@@ -465,21 +465,103 @@ void KoTextParag::drawParagString( QPain
         draw_startX = at( start + draw_len - 1 )->x;
     }
     int draw_startX_pix = zh->layoutUnitToPixelX( draw_startX ) /* + at( rightToLeft \
? start+draw_len-1 : start )->pixelxadj*/; +	
+    
+    // cache handing stuff
+    bool noCachedTile=false;
+    bool clearFirst=true;
+    QString cacheString=s.mid(start, len);
+    //kdDebug() << "cacheString: " << cacheString << "\n";
+
+    // check if we need to realloc things....
+    if(cacheInfo.definedSections <= cacheInfo.workingSection)
+    {
+	    noCachedTile=true;
+	    clearFirst=false;
+	    
+	    // allocate storage
+	    cacheInfo.paintWidth=(int *)realloc(cacheInfo.paintWidth, \
(cacheInfo.workingSection+5)*sizeof(int)); +	    cacheInfo.paintHeight=(int \
*)realloc(cacheInfo.paintHeight, (cacheInfo.workingSection+5)*sizeof(int)); +	    \
cacheInfo.rightToLeft=(bool *)realloc(cacheInfo.rightToLeft, \
(cacheInfo.workingSection+5)*sizeof(bool)); +	    cacheInfo.drawSelections=(bool \
*)realloc(cacheInfo.drawSelections, (cacheInfo.workingSection+5)*sizeof(bool)); +	    \
cacheInfo.contents=(QString **)realloc(cacheInfo.contents, \
(cacheInfo.workingSection+5)*sizeof(QString *)); +	    \
cacheInfo.cachedPixmap=(QPixmap **)realloc(cacheInfo.cachedPixmap, \
(cacheInfo.workingSection+5)*sizeof(QPixmap *));  
-    drawParagStringInternal( painter, s, start, draw_len, draw_startX_pix,
-                             lastY_pix, baseLine_pix,
-                             draw_bw,
-                             h_pix, drawSelections, lastFormat, i, selectionStarts,
-                             selectionEnds, cg, rightToLeft, zh );
-
-    if ( !textDocument()->drawingShadow() && textDocument()->drawFormattingChars() )
-    {
-        drawFormattingChars( painter, s, start, len,
-                             startX, lastY, baseLine, h,
-                             startX_pix, lastY_pix, baseLine_pix, bw_pix, h_pix,
-                             drawSelections,
-                             lastFormat, i, selectionStarts,
-                             selectionEnds, cg, rightToLeft );
+	    cacheInfo.definedSections=cacheInfo.workingSection;
+    }
+    // check if the contents have changed
+    else if(cacheString != *cacheInfo.contents[cacheInfo.workingSection])
+    {
+	    noCachedTile=true;
+	    clearFirst=true;
+    }
+    // This should catch zooming, since that'll change width/height, and other misc \
things etc. +    else if(cacheInfo.paintHeight[cacheInfo.workingSection] != h_pix ||
+		    cacheInfo.paintWidth[cacheInfo.workingSection] != draw_bw ||
+		    cacheInfo.drawSelections[cacheInfo.workingSection] != drawSelections ||
+		    cacheInfo.rightToLeft[cacheInfo.workingSection] != rightToLeft )
+    {
+	    noCachedTile=true;
+	    clearFirst=true;
+    }
+    
+    if(noCachedTile)
+    {
+	    QPixmap *cachePixmap=new QPixmap(bw_pix, h_pix);
+	    QPainter *myPainter=new QPainter(cachePixmap);
+	    myPainter->setBackgroundMode(QPainter::TransparentMode);
+
+	    if ( lastFormat->textBackgroundColor().isValid() )
+	        //painter.fillRect( startX_pix, lastY_pix, bw_pix, h_pix, \
lastFormat->textBackgroundColor() ); +	        cachePixmap->fill( \
lastFormat->textBackgroundColor() ); +	    else
+	    {
+		    QColor fillColor = backgroundColor() ? *backgroundColor() : \
QColor(QColorGroup::Base); +		    fillColor=Qt::white;
+		    // transparent shite
+		    //cachePixmap->fill(Qt::color0);
+		    cachePixmap->fill(fillColor);
+	    }
+
+	    // call the actual drawing functions
+	    drawParagStringInternal( *myPainter, s, start, draw_len, 0,
+	                             0, baseLine_pix,
+       	                      draw_bw,
+       	                      h_pix, drawSelections, lastFormat, i, selectionStarts,
+       	                      selectionEnds, cg, rightToLeft, zh );
+	    
+       	    if ( !textDocument()->drawingShadow() && \
textDocument()->drawFormattingChars() ) +	    {
+	        drawFormattingChars( painter, s, start, len,
+	                             startX, lastY, baseLine, h,
+	                             0, 0, baseLine_pix, bw_pix, h_pix,
+	                             drawSelections,
+	                             lastFormat, i, selectionStarts,
+	                             selectionEnds, cg, rightToLeft );
+	    }
+
+	    // clearn up and draw
+	    myPainter->end();
+	    delete myPainter;
+	    painter.drawPixmap(draw_startX_pix, lastY_pix, *cachePixmap);
+
+	    // update the cache
+	    if(clearFirst)
+	    {
+		    // leak for now!
+		    delete cacheInfo.contents[cacheInfo.workingSection];
+		    delete cacheInfo.cachedPixmap[cacheInfo.workingSection];
+	    }
+	    cacheInfo.cachedPixmap[cacheInfo.workingSection]=cachePixmap;
+	    cacheInfo.paintHeight[cacheInfo.workingSection]=h_pix;	
+	    cacheInfo.paintWidth[cacheInfo.workingSection]=draw_bw;
+	    cacheInfo.contents[cacheInfo.workingSection]=new QString(cacheString);
+	    cacheInfo.rightToLeft[cacheInfo.workingSection]=rightToLeft;
+	    cacheInfo.drawSelections[cacheInfo.workingSection]=drawSelections;
+    }
+    else
+    {
+	    painter.drawPixmap(draw_startX_pix, lastY_pix, \
*cacheInfo.cachedPixmap[cacheInfo.workingSection]);  }
 }
 
Index: qrichtext.cpp
===================================================================
RCS file: /home/kde/koffice/lib/kotext/qrichtext.cpp,v
retrieving revision 1.81
diff -u -3 -p -r1.81 qrichtext.cpp
--- qrichtext.cpp	7 Aug 2002 16:08:10 -0000	1.81
+++ qrichtext.cpp	9 Aug 2002 17:47:23 -0000
@@ -3725,6 +3725,15 @@ KoTextParag::KoTextParag( KoTextDocument
     //// kotext
     setNewLinesAllowed(TRUE);
     ////
+    
+    cacheInfo.definedSections= -1;
+
+    cacheInfo.paintHeight=NULL;
+    cacheInfo.paintWidth=NULL;
+    cacheInfo.drawSelections=NULL;
+    cacheInfo.rightToLeft=NULL;
+    cacheInfo.cachedPixmap=NULL;
+    cacheInfo.contents=NULL;
 }
 
 KoTextParag::~KoTextParag()
@@ -3758,6 +3767,20 @@ KoTextParag::~KoTextParag()
     {
         emit document()->paragraphDeleted( this );
     }
+
+    // destroy our cache
+    for(int c=0;c<=cacheInfo.definedSections;c++)
+    {
+	delete cacheInfo.contents[c];
+	delete cacheInfo.cachedPixmap[c];
+    }
+    if(cacheInfo.paintHeight!=NULL) free(cacheInfo.paintHeight);
+    if(cacheInfo.paintWidth!=NULL) free(cacheInfo.paintWidth);
+    if(cacheInfo.drawSelections!=NULL) free(cacheInfo.drawSelections);
+    if(cacheInfo.rightToLeft!=NULL) free(cacheInfo.rightToLeft);
+    if(cacheInfo.cachedPixmap!=NULL) free(cacheInfo.cachedPixmap);
+    if(cacheInfo.contents!=NULL) free(cacheInfo.contents);
+
     //kdDebug(32500) << "KoTextParag::~KoTextParag " << this << endl;
     ////
 }
@@ -4258,6 +4281,9 @@ void KoTextParag::paintDefault( QPainter
 
     QString qstr = str->toString();
 
+    // reset the working section
+    cacheInfo.workingSection=0;
+
     const int nSels = doc ? doc->numSelections() : 1;
     QMemArray<int> selectionStarts( nSels );
     QMemArray<int> selectionEnds( nSels );
@@ -4349,10 +4375,12 @@ void KoTextParag::paintDefault( QPainter
 		if ( chr->isCustom() && chr->customItem()->placement() == \
KoTextCustomItem::PlaceInline ) {  qstr.replace(i,1," ");
 		}
-                // QRT hack removed from this place, it broke justified spaces
-		drawParagString( painter, qstr, paintStart, paintEnd - paintStart + 1, startX, \
                lastY,
-				 lastBaseLine, bw, lasth, drawSelections,
-				 lastFormat, i, selectionStarts, selectionEnds, cg, lastDirection );
+
+  		drawParagString( painter, qstr, paintStart, paintEnd - paintStart + 1, startX, \
lastY, +			 lastBaseLine, bw, lasth, drawSelections,
+			 lastFormat, i, selectionStarts, selectionEnds, cg, lastDirection );
+
+		cacheInfo.workingSection++;
 	    }
 	    if ( !chr->isCustom() ) {
 		paintStart = i;
@@ -4407,9 +4435,11 @@ void KoTextParag::paintDefault( QPainter
 	    }
 	}
 	int x = startX;
 	drawParagString( painter, qstr, paintStart, paintEnd-paintStart+1, x, lastY,
 			 lastBaseLine, bw, h, drawSelections,
 			 lastFormat, i, selectionStarts, selectionEnds, cg, lastDirection );
+	cacheInfo.workingSection++;
     }
 
     // if we should draw a cursor, draw it now
Index: qrichtext_p.h
===================================================================
RCS file: /home/kde/koffice/lib/kotext/qrichtext_p.h,v
retrieving revision 1.38
diff -u -3 -p -r1.38 qrichtext_p.h
--- qrichtext_p.h	30 Jul 2002 11:41:28 -0000	1.38
+++ qrichtext_p.h	9 Aug 2002 17:47:24 -0000
@@ -108,6 +108,18 @@ class KoTextDocCommand;
 #include "korichtext.h"
 ////
 
+struct paragCacheInfo 
+{
+   int *paintWidth;
+   int *paintHeight;
+   bool *drawSelections;
+   bool *rightToLeft;
+
+   QString **contents;
+   QPixmap **cachedPixmap;
+   int definedSections;
+   int workingSection;
+};
 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  
 class Q_EXPORT KoTextStringChar
@@ -1398,7 +1410,9 @@ private:
     KoTextDocCommandHistory *commandHistory;
     int list_val;
     QColor *bgcol;
-
+    
+    // info for redraw cache
+    struct paragCacheInfo cacheInfo;
 };
 
 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



_______________________________________________
koffice-devel mailing list
koffice-devel@mail.kde.org
http://mail.kde.org/mailman/listinfo/koffice-devel

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic