notepad_pages.cpp

Go to the documentation of this file.
00001 /*
00002  * File Name: notepad_pages.cpp
00003  */
00004 
00005 /*
00006  * This file is part of notepad.
00007  *
00008  * notepad is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * notepad is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00022 /**
00023  * Copyright (C) 2010 IREX Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 #include <cstring>
00028 #include <set>
00029 
00030 #include "notepad_ipc.h"
00031 #include "notepad_utils.h"
00032 #include "notepad_display_sched.h"
00033 #include "notepad_doc.h"
00034 #include "notepad_commandqueue.h"
00035 #include "notepad_pages.h"
00036 #include "log.h"
00037 
00038 namespace notepad 
00039 {
00040 
00041     // Operations ----------------------------------------------------------------------
00042     class renderCommand : public Command
00043     {
00044         public:
00045             renderCommand(CNotepadPages* pages, RenderPageCtx* aCtx) {the_pages = pages; params = aCtx; };
00046             bool execute() { bool r = the_pages->execRenderPage(params); LOGPRINTF(); delete params; return r; };
00047 
00048         private:
00049             CNotepadPages* the_pages;
00050             RenderPageCtx* params;
00051     };
00052 
00053 
00054 
00055     // object lifecycle
00056     CNotepadPages::CNotepadPages()
00057         : doc(0)
00058         , gctx(0)
00059         , current_page(0)
00060         , drawsem(0)
00061         , pages_rw_sem(1)
00062     {
00063         LOGPRINTF("entry");
00064         for (gint i = 0; i < NPAGES; i++)
00065         {
00066             pages[i].image = 0;
00067             pages[i].pageno = 0;
00068             pages[i].dirty = TRUE;
00069         }
00070         LOGPRINTF("exit");
00071     }
00072 
00073     CNotepadPages::~CNotepadPages()
00074     {
00075         stop();
00076         for (gint i = 0; i < NPAGES; i++)
00077         {
00078             if (pages[i].image != 0)
00079             {
00080                 g_object_unref(G_OBJECT(pages[i].image) );
00081                 pages[i].image = 0;
00082             }
00083         }
00084     }
00085 
00086     void CNotepadPages::init(CNotepadDoc* aDoc, GCtx* aCtx, int aPage, GdkVisual* aVisual) 
00087     {
00088         g_assert(aDoc   != 0);
00089         g_assert(aCtx   != 0);
00090         g_assert(aPage  > 0);
00091         g_assert(aVisual!= 0);
00092 
00093         visual          = aVisual;
00094         doc             = aDoc;
00095         gctx            = aCtx;
00096         current_page    = aPage;
00097 
00098         // Start the thread before executing the first task. 
00099         // The thread will only be started once!
00100         cmd_queue.start();
00101     }
00102 
00103     void CNotepadPages::stop() 
00104     {
00105         cmd_queue.stop();
00106     }
00107 
00108 
00109 
00110     // internal functions
00111 
00112     gint CNotepadPages::find_page(const int pageno)
00113     {
00114         LOGPRINTF("entry %d", pageno);
00115 
00116         g_assert(pageno > 0);
00117 
00118         // find page on pagenumber, return index in array, NOTFOUND (-1) if not found
00119         for (gint i = 0; i < NPAGES; i++)
00120         {
00121             if (pages[i].pageno == pageno)
00122             {
00123                 LOGPRINTF("exit, page %d(%d) i %d dirty %d", pages[i].pageno, pageno, i, pages[i].dirty);
00124                 return i;
00125             }
00126         } 
00127 
00128         LOGPRINTF("exit, not found");
00129         return NOTFOUND;
00130     }
00131 
00132     gint CNotepadPages::replace_page_at_index(gint index, int pageno)
00133     {
00134         LOGPRINTF("entry %d %d", index, pageno);
00135 
00136         g_assert(pageno > 0);
00137         g_assert(index >= 0 && index < NPAGES);
00138 
00139         GdkImage* image = pages[index].image; // to avoid array algebra in loop
00140         // replace image Note: delete and create new one, size may have changed due to rotation
00141         if (image != 0)
00142         {
00143             g_object_unref(G_OBJECT(image)); // delete previous image
00144             image = 0;
00145         }
00146 
00147         // note: use GDK_IMAGE_NORMAL, because GDK_IMAGE_SHARED will cause an XLib async error
00148         image = gdk_image_new(GDK_IMAGE_NORMAL, visual, gctx->width, gctx->height);
00149         LOGPRINTF("%p", image);
00150 
00151         // clear image / draw white background
00152         g_assert(image != 0);
00153         memset(image->mem, 0xFF, image->bpl*image->height); // draw white, Note: 1 byte per pixel; byte per line = bytes in width line.
00154 
00155         pages[index].image = image;
00156         pages[index].pageno = pageno;
00157         pages[index].dirty = TRUE; // mark for redraw
00158 
00159         LOGPRINTF("exit");
00160         return index;
00161     }
00162 
00163     gint CNotepadPages::remove_from_cache(const gint index)
00164     {
00165         // free element from cache
00166         LOGPRINTF("entry");
00167 
00168         g_assert(index >= 0 && index < NPAGES);
00169 
00170         if (pages[index].image != 0)
00171         {
00172             g_object_unref(G_OBJECT(pages[index].image) );
00173             pages[index].image = 0;
00174         }
00175         pages[index].pageno = 0;
00176         pages[index].dirty = TRUE;
00177 
00178         LOGPRINTF("exit");
00179         return index;
00180     }
00181 
00182     gint CNotepadPages::remove_page_from_cache(const int pageno)
00183     {
00184         // find page and free element from cache
00185         LOGPRINTF("entry");
00186 
00187         g_assert(pageno > 0);
00188 
00189         gint i = find_page(pageno);
00190         if (i != NOTFOUND)
00191         {
00192             i = remove_from_cache(i);
00193         }
00194 
00195         LOGPRINTF("exit");
00196         return i;
00197     }
00198 
00199     gint CNotepadPages::make_room(const int pageno)
00200     {
00201         // find farthest page on pagenumber and drop out of the cache, 
00202         // return index of free(d) element in array.
00203         // called in combination with replace_page_at_index to insert 
00204         // or add a page to the cache 
00205         LOGPRINTF("entry");
00206 
00207         g_assert(pageno > 0);
00208 
00209         gint ifarthest  = 0;
00210         gint dfarthest  = 0;
00211         gint i          = 0;
00212 
00213         for (i = 0; i < NPAGES; i++)
00214         {
00215             if (pages[i].pageno == 0) 
00216             {
00217                 LOGPRINTF("exit, found free at %d", i);
00218                 return i;
00219             }
00220             else
00221             {
00222                 gint dist = ABS(pages[i].pageno - pageno);
00223                 if (dist > dfarthest)
00224                 {
00225                     dfarthest = dist;
00226                     ifarthest = i;
00227                 }
00228             }
00229         } 
00230 
00231         // free farthest element
00232         i = remove_from_cache(ifarthest);
00233 
00234         LOGPRINTF("exit, freed %d", ifarthest);
00235         return i;
00236     }
00237 
00238     // pages interface functions
00239     void CNotepadPages::insert_page(const int pageno)
00240     {
00241         // insert page at position, and increase all next pages
00242         LOGPRINTF("entry");
00243 
00244         g_assert(pageno > 0);
00245 
00246         // flush running queue
00247         cmd_queue.flush();
00248 
00249         pages_rw_sem.p(); // guarded write
00250 
00251         gint ifree = make_room(pageno);
00252         g_assert(ifree >= 0 && ifree < NPAGES);
00253 
00254         for (gint i = 0; i < NPAGES; i++)
00255         {
00256             if (pages[i].pageno >= pageno)
00257             {
00258                 // page positions are also increased inside scribble library;
00259                 pages[i].pageno++;
00260             }
00261         }
00262 
00263         replace_page_at_index(ifree, pageno);
00264         pages_rw_sem.v();
00265 
00266         LOGPRINTF("exit");
00267     }
00268 
00269     void CNotepadPages::delete_page(const int pageno)
00270     {
00271         // remove page with same position, and decrease all next pages
00272         LOGPRINTF("entry");
00273 
00274         g_assert(pageno > 0);
00275 
00276         // flush running queue
00277         cmd_queue.flush();
00278 
00279         pages_rw_sem.p(); // guarded write
00280 
00281         gint i = find_page(pageno);
00282         if (i != NOTFOUND)
00283         {
00284             for (gint j = 0; j < NPAGES; j++)
00285             {
00286                 if (pages[j].pageno > pageno)
00287                 {
00288                     // page positions are also decreased inside scribble library;
00289                     pages[j].pageno--;
00290                 }
00291             }
00292             remove_from_cache(i);
00293         }
00294         pages_rw_sem.v();
00295 
00296         LOGPRINTF("exit");
00297     }
00298 
00299     void CNotepadPages::clear_page(const int pageno)
00300     {
00301         // clear page: implemented here to mark for redraw in cache, where 
00302         // the data of the page is assumed to be cleared before. 
00303         LOGPRINTF("entry");
00304 
00305         g_assert(pageno > 0);
00306 
00307         mark_dirty(pageno); 
00308 
00309         LOGPRINTF("exit");
00310     }
00311 
00312     void CNotepadPages::mark_dirty(const int pageno)
00313     {
00314         // mark for redraw at update.
00315         LOGPRINTF("entry");
00316 
00317         g_assert(pageno > 0);
00318 
00319         gint i = find_page(pageno);
00320         if (i != NOTFOUND)
00321         {
00322             pages[i].dirty = TRUE; // mark for redraw at update.
00323         }
00324 
00325         LOGPRINTF("exit");
00326     }
00327 
00328     void CNotepadPages::mark_all_dirty()
00329     {
00330         // mark whole cache for redraw at update.
00331         LOGPRINTF("entry");
00332 
00333         for (gint i = 0; i < NPAGES; i++)
00334         {
00335             pages[i].dirty = TRUE; // mark for redraw at update.
00336         }
00337 
00338         LOGPRINTF("exit");
00339     }
00340 
00341 
00342     // drawing functions
00343 
00344     GdkImage*   CNotepadPages::get_page(const int pageno)
00345     {
00346         // returns image of current page, a commandqueue may be used to fetch the page
00347         LOGPRINTF("entry %d", pageno);
00348 
00349         g_assert(pageno > 0);
00350 
00351         int i = find_page(pageno);
00352 
00353         // queue render taks if page not in cache, pageflip or page marked dirty
00354         if ((i == NOTFOUND) || (pageno != current_page) || pages[i].dirty)
00355         {
00356             // flush running queue
00357             cmd_queue.flush();
00358 
00359             // Define smart order of caching
00360             const int order[NPAGES] = {0, +1, +2, -1, +3, -2, +4, -3, +5, -4, -5};
00361             // queue render task for current and adjacent pages.
00362             for (gint j = 0; j < NPAGES; j++)
00363             {
00364                 int relpage = pageno + order[j];
00365                 if ( (relpage > 0) && (relpage <= doc->get_num_pages()) )
00366                 {
00367                     i = find_page(relpage);
00368                     if ((i == NOTFOUND) || (pages[i].dirty)) // rerender page
00369                     {
00370                         LOGPRINTF("add to queue [%d] page %d", j, relpage);
00371                         RenderPageCtx* params = new RenderPageCtx; // deleted by execute rendercommand
00372                         memset(params, 0, sizeof params);
00373                         params->pageno = relpage;
00374                         params->isCurrent = (j == 0); // if waiting for current page ready must be signalled.
00375                         renderCommand* rc = new renderCommand(this, params);
00376                         cmd_queue.add(rc);
00377 
00378                         if (j == 0) // current page
00379                         {
00380                             LOGPRINTF("waiting for semaphore");
00381                             ipc_sys_busy(true); // begin busy
00382                             drawsem.p(); // wait for render result of current window
00383                             LOGPRINTF("ready waiting for semaphore");
00384                         }
00385                     }
00386                 }
00387             }
00388         }
00389 
00390         i = find_page(pageno);
00391         g_assert(i >= 0 && i < NPAGES); // return valid page
00392 
00393         // remember current page
00394         current_page = pageno;
00395 
00396         ipc_sys_busy(false); // end busy
00397  
00398         // return current image for use in scribbling
00399         LOGPRINTF("exit");
00400         return pages[i].image;
00401     }
00402 
00403     //--------------------------------------------------------------------------------------------------
00404     // workerthread below
00405     //--------------------------------------------------------------------------------------------------
00406 
00407     bool CNotepadPages::execRenderPage( RenderPageCtx* params )
00408     {
00409         // Called in render queue workerthread
00410         g_assert(params != 0);
00411 
00412         bool redraw = false;
00413 
00414         pages_rw_sem.p(); // guarded write
00415 
00416         // find out if page has to be redrawn
00417         int i = find_page(params->pageno);
00418         if (i == NOTFOUND) 
00419         {
00420             i = make_room(params->pageno);
00421             LOGPRINTF("added page %d in %d", params->pageno, i);
00422             redraw = true;
00423         }
00424         else
00425         {
00426             redraw = pages[i].dirty; 
00427         }
00428 
00429         if (redraw)
00430         {
00431             LOGPRINTF("redraw page %d : %d c %d i %d", params->pageno, redraw, params->isCurrent, i);
00432 
00433             // Create new image, page may be dirty because of rotation, deletion etc.
00434             i = replace_page_at_index(i, params->pageno);
00435 
00436             // render page. Offline drawing on gdk_image
00437             g_assert(i >= 0 && i < NPAGES);
00438             g_assert(pages[i].image != 0);
00439             doc->draw_scribble_page(params->pageno, *gctx, pages[i].image); 
00440             pages[i].dirty = FALSE;
00441         }
00442 
00443         pages_rw_sem.v();
00444 
00445         if (params->isCurrent)
00446         {
00447             LOGPRINTF("ready rendering current page");
00448             drawsem.v(); // unlock display of current page
00449         }
00450 
00451         LOGPRINTF("exit");
00452         return true;
00453     }
00454 
00455 } // namespace
00456 
Generated by  doxygen 1.6.2-20100208