images_renderer.cpp

Go to the documentation of this file.
00001 /*
00002  * File Name: images_renderer.cpp
00003  */
00004 
00005 /*
00006  * This file is part of uds-plugin-images.
00007  *
00008  * uds-plugin-images 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  * uds-plugin-images 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) 2008 iRex Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 #include <cassert>
00028 #include <iostream>
00029 #include "images_renderer.h"
00030 #include "image_render_task.h"
00031 #include "log.h"
00032 
00033 namespace images
00034 {
00035 
00036 static const int MAX_ORIGINAL_IMAGE = 10 * 1024 * 1024; // Maximum original image size.
00037 
00038 
00039 ImagesRenderer::ImagesRenderer(ImagesDocument * document)
00040 : doc(document)
00041 , render_thread()
00042 , pages_cache()
00043 , last_render_page(0)    
00044 {
00045     LOGPRINTF("%p", document);
00046 
00047     render_thread.start();
00048     
00049     //  Pre-render policy
00050     set_prerender_policy(2, 2);
00051 }
00052 
00053 ImagesRenderer::~ImagesRenderer(void)
00054 {
00055     LOGPRINTF("entry");
00056 
00057     render_thread.stop();
00058     // pages_cache.clear();
00059 }
00060 
00061 ImageRenderStatus ImagesRenderer::render(const std::string & anchor,
00062                                          const ImagePageAttrs & attrs,
00063                                          int ref_id,
00064                                          void * user_data)
00065 {
00066     LOGPRINTF("%s, zoom[%f], rotation[%d], ref_id[%d]",
00067               anchor.c_str(), attrs.zoom, attrs.rotation, ref_id);
00068     
00069     // Return code.
00070     ImageRenderStatus ret = IMG_RENDER_OK;
00071     
00072     // Clear the old tasks.
00073     render_thread.clear_all();
00074 
00075     ImageRenderTask * task = 0;
00076     
00077     // Get the render result from 'pages cache'
00078     ImagePage * page = get_page(anchor, attrs);
00079     if (!page)
00080     {
00081         ret = can_render(anchor, attrs);
00082         if (ret == IMG_RENDER_OK)
00083         {
00084             // Prepend 'render task' to 'render_thread'
00085             task = new ImageRenderTask(anchor, attrs, ref_id, this, user_data);
00086             // Notes, The flag 'true' means aborting the current task.
00087             // But for images, the implementation of the task 
00088             // doesn't support aborting feature.
00089             render_thread.prepend_task(task, true);
00090         }
00091         else
00092         {
00093             // Notify renderer this page is not ready because of what. e.g OOM.
00094             notify_page_ready(0, ref_id, ret, user_data);
00095         }
00096     }
00097     else
00098     {
00099         WARNPRINTF("Page %s already in pages_cache!", anchor.c_str());
00100 
00101         // This requested page is already in 'pages_cache'.
00102         // Notify page is ready now.
00103         notify_page_ready(page, ref_id, IMG_RENDER_OK, user_data);
00104     }
00105 
00106     // Do pre_rendering.
00107     assert(doc);
00108     int page_num = doc->get_position(anchor);
00109 
00110     bool page_down = (last_render_page <= page_num) ? true : false;
00111     last_render_page = page_num;
00112     pre_render(page_num, attrs, page_down);
00113     
00114     return ret;
00115 }
00116 
00117 ImagePage * ImagesRenderer::get_page(const std::string & anchor,
00118                                      const ImagePageAttrs & attrs)
00119 {
00120     LOGPRINTF("%s zoom[%f] rotation[%d]", 
00121               anchor.c_str(), attrs.zoom, attrs.rotation);
00122 
00123     size_t key = ImagePage::calc_key(anchor, 
00124                                      attrs.desired_width, 
00125                                      attrs.desired_height,
00126                                      attrs.zoom,
00127                                      attrs.rotation);
00128     
00129     ImagePage * page = pages_cache.get_page(key);
00130    
00131     LOGPRINTF("return %p", page);
00132 
00133     return page;
00134 }
00135 
00136 void ImagesRenderer::add_page(ImagePage * page)
00137 {
00138     LOGPRINTF("%p", page);
00139     
00140     if (page)
00141     {
00142         pages_cache.add_page(page);
00143     }
00144 }
00145 
00146 // (1)Check whether the image is valid or not.
00147 // (2)Check whether there is enough memory to render. 
00148 ImageRenderStatus ImagesRenderer::can_render(const std::string & anchor, 
00149                                              const ImagePageAttrs & attrs)
00150 {
00151     bool is_valid_image = false;
00152     if ((attrs.original_width > 0) && (attrs.original_height > 0))
00153     {
00154         is_valid_image = true;
00155     }        
00156 
00157     // Check whether the image is valid or not.
00158     if (!is_valid_image)
00159     {
00160         WARNPRINTF("Invalid image page %s.", anchor.c_str());            
00161         return IMG_RENDER_INVALID;
00162     }
00163 
00164     // Check for maximum image size.
00165     // The temporary memory used by gdk_pixbuf_load_at_scale() during rendering
00166     // cannot be estimated correctly but it depends partly on the image size.
00167     // To prevent memory overflows we accept images up to a maximum size only.
00168     if (attrs.original_width * attrs.original_height > MAX_ORIGINAL_IMAGE)
00169     {
00170         WARNPRINTF("Image too big: w [%d] h [%d] max [%d]", attrs.original_width, attrs.original_height, MAX_ORIGINAL_IMAGE);
00171         return IMG_RENDER_FAIL;
00172     }
00173 
00174     // Check whether there is enough memory to render.
00175     gint64 size = ImagePage::estimate_length(attrs);
00176     if (!pages_cache.make_enough_memory(size))
00177     {
00178         LOGPRINTF("Not enough memory to render page [%s]", anchor.c_str());
00179         return IMG_RENDER_OOM;
00180     }
00181     
00182     return IMG_RENDER_OK;
00183     
00184 }
00185 
00186 void ImagesRenderer::pre_render(int page_num, 
00187                                 const ImagePageAttrs & attrs,
00188                                 bool page_down)
00189 {
00190     LOGPRINTF("%d page_down = %d", page_num, page_down);
00191 
00192     // Append 'pre-render task' to 'render_thread' if neccessary.
00193 
00194     // According to the direction of turning page, 
00195     // render the previous pages or the next pages firstly.
00196     if (page_down)
00197     {
00198         LOGPRINTF("Render the next pages firstly.");
00199         render_next_pages(page_num, attrs);
00200         render_prev_pages(page_num, attrs);
00201     }
00202     else
00203     {
00204         LOGPRINTF("Render the previous pages firstly.");
00205         render_prev_pages(page_num, attrs);
00206         render_next_pages(page_num, attrs);
00207     }
00208 }
00209 
00210 void ImagesRenderer::render_prev_pages(int page_num, 
00211                         const ImagePageAttrs & attrs)
00212 {
00213     // Previous range pages.
00214     int prev = page_num - prev_range_pages;
00215     if (prev < 1) { prev = 1; }
00216     
00217     ImagePage * page = 0;
00218     ImageRenderTask * task = 0;
00219 
00220     for (int i = page_num - 1; i >= prev; i--)
00221     {
00222         std::string anchor;
00223         if (doc->get_anchor_of_page(i, anchor))
00224         {        
00225             page = get_page(anchor, attrs);
00226             if (!page)
00227             {
00228                 if (can_render(anchor, attrs) == IMG_RENDER_OK)
00229                 {
00230                     task = new ImageRenderTask(anchor, attrs, -1, this, 0);
00231                     render_thread.append_task(task);
00232                 }
00233             }
00234             else
00235             {
00236                 WARNPRINTF("Page %s already in pages_cache!", anchor.c_str());
00237                 notify_page_ready(page, -1, IMG_RENDER_OK, 0);
00238             }
00239         }
00240     }
00241 
00242 }
00243 
00244 void ImagesRenderer::render_next_pages(int page_num,
00245                         const ImagePageAttrs & attrs)
00246 {
00247     // Next range pages
00248     int next = page_num + next_range_pages;
00249     if (next > ((int)doc->page_count())) 
00250     { 
00251         next = ((int)doc->page_count()); 
00252     }
00253 
00254     ImagePage * page = 0;
00255     ImageRenderTask * task = 0;
00256 
00257     for (int i = page_num + 1; i <= next; i++)
00258     {
00259         std::string anchor;
00260         if (doc->get_anchor_of_page(i, anchor))
00261         {                
00262             page = get_page(anchor, attrs);
00263             if (!page)
00264             {
00265                 if (can_render(anchor, attrs) == IMG_RENDER_OK)
00266                 {
00267                     task = new ImageRenderTask(anchor, attrs, -1, this, 0);
00268                     render_thread.append_task(task);
00269                 }
00270             }
00271             else
00272             {
00273                 WARNPRINTF("Page %s already in pages_cache!", anchor.c_str());
00274                 notify_page_ready(page, -1, IMG_RENDER_OK, 0);
00275             }
00276         }
00277     }
00278 }
00279 
00280 void ImagesRenderer::notify_page_ready(ImagePage * page, 
00281                                        int ref_id,
00282                                        ImageRenderStatus status,
00283                                        void * user_data)
00284 {
00285     LOGPRINTF("%p %d", page, ref_id);
00286 
00287     pages_cache.lock();
00288 
00289     if (page)
00290     {
00291         page->update_timestamp();
00292     }
00293     
00294     if (ref_id != -1)
00295     {
00296         sig_page_ready.broadcast(page, ref_id, status, user_data);
00297     }
00298 
00299     pages_cache.unlock();
00300 }
00301 
00302 void ImagesRenderer::set_prerender_policy(int prev_range, int next_range)
00303 {
00304     LOGPRINTF("range[%d, %d]", prev_range, next_range);
00305 
00306     prev_range_pages = prev_range;
00307     next_range_pages = next_range;
00308 }
00309 
00310 bool ImagesRenderer::set_memory_limit(const int bytes)
00311 {
00312     LOGPRINTF("entry");
00313 
00314     return pages_cache.set_memory_limit(bytes);
00315 }
00316 
00317 void ImagesRenderer::stop(void)
00318 {
00319     render_thread.stop();    
00320 }
00321 
00322 };
00323 
00324 
Generated by  doxygen 1.6.2-20100208