Bug 132047

Summary: Faster display of images and/or prefetch wished for
Product: [Applications] digikam Reporter: Brad Templeton <kdebug>
Component: Preview-ImageAssignee: Digikam Developers <digikam-bugs-null>
Status: RESOLVED FIXED    
Severity: wishlist CC: althio.forum, caulier.gilles, cimmino.marco, contact, gaetan, gschintgen, krienke, marcel.wiesweg
Priority: NOR    
Version: 0.9.0   
Target Milestone: ---   
Platform: Debian testing   
OS: Linux   
Latest Commit: Version Fixed In: 4.3.0
Sentry Crash Report:

Description Brad Templeton 2006-08-08 09:47:16 UTC
Version:           1:0.9-beta1-2 (using KDE KDE 3.5.4)
Installed from:    Debian testing/unstable Packages

You can never have enough speed.  I've been moving to digikam from other applications, and some of them are a great deal faster at display of today's large jpegs.   Some of them, like kuickshow, are just plain fast at rendering a jpeg.   Go steal its code!  pho is also fast, so is xzgv.

Another technique that can be very useful is to pre-fetch the next and/or previous image and render them in memory, or even in hidden windows/video card buffers.  RAM (screen sized ram anyway) is cheap.   Then the switch can be instant if the user has paused.  Do it in a niced background thread of course.

Digikam is much more full featured than most other programs, but once you get used to speed, it's very hard to give up speed, the features must be very compelling.  Now that digikam has the ability to do ratings, tags and comments in the large picture view (which is a must for me) the speed of going to the next picture in this view becomes important.

Some programs are also clever, and if they see the screen is much smaller than the image (as is typical) they will do a much faster cheap jpeg decode which does not extract all pixels.  Of course that means more time if the image needs zoom.   Clever programs do the mini render and have another background thread do the full render or any more complex processing.
Comment 1 Marcel Wiesweg 2006-08-08 16:29:53 UTC
Your suggestions for fast rendering are important optimisations. For a viewer. Please note that digikam currently has no viewer, only an image editor. (Showfoto is supposed to be a viewer, but in its heart it is an editor)

As the image editor is an editor, it needs the full pixels at any time. Btw, only for JPEGs it is possible to save time loading in a lower resolution.
We can preload the next image, that costs RAM (image size), but no problem.
Rendering cannot be done in a background thread, it must be done in a foreground thread (it is X server communication in the end).

That does not mean we cannot have a dedicated speed-optimized viewer for image _watching_, fully integrated with all sidebar stuff of course, allowing nice slideshows, easily opening the IE if editing is needed.
Comment 2 Brad Templeton 2006-08-08 22:44:10 UTC
Thanks.   My personal taste is I want gimp or other such tools for my true editing, and from my photo organizer I want to edit captions, tags and albums most.  Though of course many people do minor edits in photo organizers.  (Rotate is not so much an edit, since to be lossless for jpeg it usually involves re-reading the file.)

However, I come to digikam mainly for organizing, and it has the potential to be the best at that.   Right now the main barriers are the various beta bugs and crashes (since I am using the 9-beta1-2) which will of course go away, and the speed.   I have a number of other things I would like ( http://ideas.4brad.com/archives/000189.html ) but I think speed is the key missing element.

When I refer to rendering, I mean the decompression of the jpeg, what the editor is doing when it says "loading" with a status bar.  That (and also the scaling down to the planned viewing size) can be done in a background thread for the predicted next picture.   Then when it's time to display, a foreground thread w ould send the graphics to the X-server.   (Though on many displays now it is possible, I believe, to send it in advance to a hidden area, and then reveal the area for a truly instant display.  That's more important for slideshows than image management.)

If you've tried out kuickshow/pho/xzgv you will have seen how much faster they can move from one picture to the next, without pre-fetch.  If you try out gqview, you will see it's not as fast on the loading but does the pre-fetch.

RAM, as we know today, costs about $70 per gigabyte.  8 megapixel images require 24mb.   Who would not spend $1.68 in memory per picture to get lighting response.   If you store it at viewing size, it's probably just 6MB for 1600x1200 screens.  I have a 2560x1600 screen but I can handle it.
Comment 3 Mikolaj Machowski 2006-08-28 12:11:40 UTC
Isn't F3 action opening image viewer?
Comment 4 Ian Hubbertz 2006-09-07 00:22:31 UTC
100% ACK to Brad Templeton.

digikam is a really great photo organizer. So it's a pitty that I usually usw kuickshow to actually view my photos because digikam is that slow.... :-(


BTW: I haven't heard of the F3 action before. "View Image" should belong into the context menu not only the main menu bar. This viewer is exactly what I want (as you have access to all digikam functions with its hotkeys (rating etc.), but it's still too slow in showing the next image.
Comment 5 caulier.gilles 2006-09-08 16:57:05 UTC
Ian,

I know the problem about F3 preview mode speed. Look my comments #2 in B.K.O #133590

When i said that it's a _FAST_ preview mode, it's true with :

* _ALL_ raw files type (500ms-1000ms instead 10s-20s depending of your computer speed).
* _ALL PNG or TIFF file saved with digiKam image editor, witch save a preview image in IPTC metadata ((500ms-1000ms instead 3s-5s)

Gilles Caulier
Comment 6 caulier.gilles 2006-09-08 19:07:14 UTC
SVN commit 582220 by cgilles:

digikam from trunk : add link to image preview feature into album icon item popup menu.
CCBUGS: 132047

 M  +11 -5     albumiconview.cpp  
 M  +2 -1      albumiconview.h  
 M  +5 -2      digikamview.cpp  


--- trunk/extragear/graphics/digikam/digikam/albumiconview.cpp #582219:582220
@@ -107,13 +107,14 @@
 #include "cameradragobject.h"
 #include "dragobjects.h"
 #include "dmetadata.h"
-#include "albumiconitem.h"
-#include "albumicongroupitem.h"
-#include "albumiconview.h"
 #include "albumdb.h"
 #include "imageattributeswatch.h"
 #include "dcrawbinary.h"
 #include "deletedialog.h"
+#include "albumiconitem.h"
+#include "albumicongroupitem.h"
+#include "albumiconview.h"
+#include "albumiconview.moc"
 
 namespace Digikam
 {
@@ -505,6 +506,7 @@
     // --------------------------------------------------------
 
     QPopupMenu popmenu(this);
+    popmenu.insertItem(SmallIcon("viewimage"), i18n("View..."), 18);
     popmenu.insertItem(SmallIcon("editimage"), i18n("Edit..."), 10);
     popmenu.insertItem(i18n("Open With"), &openWithMenu, 11);
     popmenu.insertSeparator();
@@ -657,6 +659,12 @@
           slotSetAlbumThumbnail(iconItem);
           break;
       }
+
+      case 18: 
+      {
+          signalPreviewItem(iconItem);
+          break;
+      }
   
       default:
           break;
@@ -1838,5 +1846,3 @@
 }
 
 }  // namespace Digikam
-
-#include "albumiconview.moc"
--- trunk/extragear/graphics/digikam/digikam/albumiconview.h #582219:582220
@@ -113,8 +113,9 @@
 
 signals:
 
+    void signalPreviewItem(AlbumIconItem*);
     void signalItemsAdded();
-    void signalItemDeleted(AlbumIconItem* iconItem);
+    void signalItemDeleted(AlbumIconItem*);
     void signalCleared();
 
 public slots:
--- trunk/extragear/graphics/digikam/digikam/digikamview.cpp #582219:582220
@@ -69,6 +69,7 @@
 #include "dio.h"
 #include "digikamapp.h"
 #include "digikamview.h"
+#include "digikamview.moc"
 
 namespace Digikam
 {
@@ -226,6 +227,10 @@
     connect(d->iconView, SIGNAL(signalItemsAdded()),
             this, SLOT(slotAlbumHighlight()));
 
+    connect(d->iconView, SIGNAL(signalPreviewItem(AlbumIconItem*)),
+            this, SLOT(slot_imagePreview(AlbumIconItem*)));
+
+
     //connect(d->iconView, SIGNAL(signalItemDeleted(AlbumIconItem*)),
       //      this, SIGNAL(signal_noCurrentItem()));
 
@@ -977,5 +982,3 @@
 }
 
 }  // namespace Digikam
-
-#include "digikamview.moc"
Comment 7 caulier.gilles 2006-12-11 15:00:03 UTC
*** Bug 133590 has been marked as a duplicate of this bug. ***
Comment 8 Arnd Baecker 2006-12-12 08:26:13 UTC
Just pasting a part of my comment from 133590 so that it does not get lost:

What gqview does (if the next image has not yet already been
loaded in the background, e.g. when trying to go through many images
too quickly) is  to incrementally display
the new image starting from the top.
By this it is at least possible to decide earlier to move on or not.
Not sure whether this is possible with the widget used for the display.
Comment 9 Marcel Wiesweg 2007-01-03 22:44:18 UTC
SVN commit 619531 by mwiesweg:

Loading of previews is now multithreaded:
- integrate preview loading into the load-save framework
- the PreviewLoadTask is a cut-down version of the normal SharedLoadingTask
- use the same cache

CCMAIL: digikam-devel@kde.org



 M  +0 -1      digikam/Makefile.am  
 M  +31 -43    digikam/imagepreviewwidget.cpp  
 M  +4 -4      digikam/imagepreviewwidget.h  
 M  +4 -0      libs/threadimageio/Makefile.am  
 M  +11 -3     libs/threadimageio/loadingcache.cpp  
 M  +24 -0     libs/threadimageio/loadingdescription.h  
 M  +1 -1      libs/threadimageio/loadsavetask.cpp  
 M  +1 -1      libs/threadimageio/loadsavetask.h  
 M  +13 -3     libs/threadimageio/loadsavethread.cpp  
 M  +40 -0     libs/threadimageio/managedloadsavethread.cpp  
 M  +1 -0      libs/threadimageio/managedloadsavethread.h  
 A             libs/threadimageio/previewloadthread.cpp   [License: GPL]
 A             libs/threadimageio/previewloadthread.h   [License: GPL]
 A             libs/threadimageio/previewtask.cpp   [License: GPL]
 A             libs/threadimageio/previewtask.h   [License: GPL]
Comment 10 Brad Templeton 2007-01-04 00:44:46 UTC
Multithreading is great since I do have dual core.  But it still doesn't hold a candle to what pre-decompress gets you or plain old faster jpeg decoding as you would get by just taking the code of the other programs.

What might make sense is to have two modes of full-size preview, one of which allows more complex modifications to the image directly, and the other of which requires the image be re-loaded before most pixel modifications (rotate and flip should be possible without re-load since they can be done lossless on the file as in thumb mode.)

This would allow the very fastest loads which are, for large images, only needing 1/4 of the pixels or less.

Now I have to admit, the speed is getting pretty good for me on my dual core Intel 6600, but that's one of the faster systems out there.

Comment 11 caulier.gilles 2007-01-04 07:53:03 UTC
Brad,

>Multithreading is great since I do have dual core.  But it still doesn't hold a candle to what >pre-decompress gets you or plain old faster jpeg decoding as you would get by just taking >the code of the other programs. 
 
No need to re-invent the wheel to take the code from other programs. The image loader implementation are very similar and cannot be optimized anymore. The ultimate optimization to do is to preload in background to the cache the next and previous image around the current selected image from current album. 

Kuickshow and digiKam image editor < 0.9 use this mechanism with imlib2 library. 

>What might make sense is to have two modes of full-size preview, one of which allows >more complex modifications to the image directly, and the other of which requires the >image be re-loaded before most pixel modifications (rotate and flip should be possible >without re-load since they can be done lossless on the file as in thumb mode.) 

This is out of context of this file. Please open a new wish.

Other optimizations to speed-up the drawing of pictures on the screen can be done using Qt4. No need to re-invent the wheel again...

Gilles Caulier
Comment 12 Arnd Baecker 2007-01-04 08:34:36 UTC
> The ultimate optimization to do is to
> preload in background to the cache the next and previous image
> around the current selected image from current album.


One might even think of:
- preloading the next (e.g.) 2-5  images in the background
- keep the last viewed (e.g.) 2-5 images in the cache

The precise numbers + an upper RAM limit for the cache
should be configurable by the user according to his preferences/resources.

Best, Arnd
Comment 13 Marcel Wiesweg 2007-01-04 12:46:42 UTC
The last viewed images are kept in the cache, until it is full. (If a full-blown version is in the cache, it is used as well btw)

For preloading there is a requirement that we dont meet currently: The ability to stop the loading. We can do this with the DImg loaders, but the preview is based on QImage.

I'm currently wondering if it is desirable to use the cheap-loading optimization for JPEGs, that is also used in the thumbnail loader.
Comment 14 Arnd Baecker 2007-01-04 12:55:10 UTC
> The last viewed images are kept in the cache, until it is full. (If
> a full-blown version is in the cache, it is used as well btw)


I see - very nice.
How large is that cache (presently that size is
not configurable, right?)?

> I'm currently wondering if it is desirable to use the cheap-loading
> optimization for JPEGs, that is also used in the thumbnail loader.


If that would bring speed-improvements, why not? ;-)
Comment 15 Gehold Bertin 2007-01-25 19:48:25 UTC
Creating of thumbnails: 2-3 seconds per image (raw)
Viewing image: 5-7 seconds per image (raw)

CPU usage: all free time: 90-95%

System: iMac Intel Core Duo 2.00 GHz

I would be happy, if it could be faster
Comment 16 caulier.gilles 2007-01-25 20:27:34 UTC
> Viewing image: 5-7 seconds per image (raw) 

If you talking about preview (F3) not Editor (F4), here on my poor computer (PIII-650Mhz-300Mb) it take less than 1 second per RAW image...

Gilles Caulier
Comment 17 Gehold Bertin 2007-01-25 20:35:19 UTC
Okay... that is realy intresting... I ment eding (left klick/F4)... Preview needs one second... It's faster then the thumbnail action *LOL*
Comment 18 Kusi 2007-01-27 17:25:37 UTC
I agree that the image viewing in digikam is too slow. Therefore I wrote an OpenGL based image viewer (KIPI plugin). Get it here: http://kde-apps.org/content/show.php?content=52276

Kusi
Comment 19 Marcel Wiesweg 2007-02-14 18:10:36 UTC
SVN commit 633639 by mwiesweg:

Use faster JPEG loading method (scale before decoding) for JPEG previews

- move code from thumbnail ioslave to libs/jpegutils
- use code in PreviewTask

CCBUG: 132047


 M  +2 -149    kioslave/digikamthumbnail.cpp  
 M  +135 -0    libs/jpegutils/jpegutils.cpp  
 M  +2 -0      libs/jpegutils/jpegutils.h  
 M  +1 -0      libs/threadimageio/Makefile.am  
 M  +9 -2      libs/threadimageio/previewtask.cpp  
 M  +1 -0      showfoto/Makefile.am  


--- trunk/extragear/graphics/digikam/kioslave/digikamthumbnail.cpp #633638:633639
@@ -68,6 +68,7 @@
 #include "rawfiles.h"
 #include "dcrawiface.h"
 #include "dmetadata.h"
+#include "jpegutils.h"
 #include "digikamthumbnail.h"
 #include "digikam_export.h"
 
@@ -76,7 +77,6 @@
 extern "C"
 {
 #include <unistd.h>
-#include <jpeglib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
@@ -274,156 +274,9 @@
     return false;
 }
 
-// -- JPEG Extraction ---------------------------------------------------------------------
-
-struct myjpeg_error_mgr : public jpeg_error_mgr
-{
-    jmp_buf setjmp_buffer;
-};
-
-extern "C"
-{
-    static void myjpeg_error_exit(j_common_ptr cinfo)
-    {
-        myjpeg_error_mgr* myerr = (myjpeg_error_mgr*) cinfo->err;
-
-        char buffer[JMSG_LENGTH_MAX];
-        (*cinfo->err->format_message)(cinfo, buffer);
-        kdWarning() << buffer << endl;
-        longjmp(myerr->setjmp_buffer, 1);
-    }
-}
-
 bool kio_digikamthumbnailProtocol::loadJPEG(QImage& image, const QString& path)
 {
-    QString format = QImageIO::imageFormat(path);
-    if (format !="JPEG") return false;
-
-    FILE* inputFile=fopen(QFile::encodeName(path), "rb");
-    if(!inputFile)
-        return false;
-
-    struct jpeg_decompress_struct cinfo;
-    struct myjpeg_error_mgr       jerr;
-
-    // JPEG error handling - thanks to Marcus Meissner
-    cinfo.err             = jpeg_std_error(&jerr);
-    cinfo.err->error_exit = myjpeg_error_exit;
-
-    if (setjmp(jerr.setjmp_buffer)) 
-    {
-        jpeg_destroy_decompress(&cinfo);
-        fclose(inputFile);
-        return false;
-    }
-
-    jpeg_create_decompress(&cinfo);
-    jpeg_stdio_src(&cinfo, inputFile);
-    jpeg_read_header(&cinfo, true);
-
-    int imgSize = QMAX(cinfo.image_width, cinfo.image_height);
-
-    int scale=1;
-    while(cachedSize_*scale*2<=imgSize) 
-    {
-        scale*=2;
-    }
-    if(scale>8) scale=8;
-
-    cinfo.scale_num=1;
-    cinfo.scale_denom=scale;
-
-    switch (cinfo.jpeg_color_space)
-    {
-        case JCS_UNKNOWN:
-            break;
-        case JCS_GRAYSCALE:
-        case JCS_RGB:
-        case JCS_YCbCr:
-            cinfo.out_color_space = JCS_RGB;
-            break;
-        case JCS_CMYK:
-        case JCS_YCCK:
-            cinfo.out_color_space = JCS_CMYK;
-            break;
-    }
-
-    jpeg_start_decompress(&cinfo);
-
-    QImage img;
-
-    // We only take RGB with 1 or 3 components, or CMYK with 4 components
-    if (!(
-           (cinfo.out_color_space == JCS_RGB  && (cinfo.output_components == 3 || cinfo.output_components == 1))
-        || (cinfo.out_color_space == JCS_CMYK &&  cinfo.output_components == 4)
-        ))
-    {
-        jpeg_destroy_decompress(&cinfo);
-        fclose(inputFile);
-        return false;
-    }
-
-    switch(cinfo.output_components) 
-    {
-        case 3:
-        case 4:
-            img.create( cinfo.output_width, cinfo.output_height, 32 );
-            break;
-        case 1: // B&W image
-            img.create( cinfo.output_width, cinfo.output_height, 8, 256 );
-            for (int i = 0 ; i < 256 ; i++)
-                img.setColor(i, qRgb(i, i, i));
-            break;
-    }
-
-    uchar** lines = img.jumpTable();
-    while (cinfo.output_scanline < cinfo.output_height)
-        jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height);
-
-    jpeg_finish_decompress(&cinfo);
-
-    // Expand 24->32 bpp
-    if ( cinfo.output_components == 3 )
-    {
-        for (uint j=0; j<cinfo.output_height; j++) 
-        {
-            uchar *in = img.scanLine(j) + cinfo.output_width*3;
-            QRgb *out = (QRgb*)( img.scanLine(j) );
-
-            for (uint i=cinfo.output_width; i--; ) 
-            {
-                in -= 3;
-                out[i] = qRgb(in[0], in[1], in[2]);
-            }
-        }
-    }
-    else if ( cinfo.output_components == 4 )
-    {
-        // CMYK conversion
-        for (uint j=0; j<cinfo.output_height; j++)
-        {
-            uchar *in = img.scanLine(j) + cinfo.output_width*4;
-            QRgb *out = (QRgb*)( img.scanLine(j) );
-
-            for (uint i=cinfo.output_width; i--; )
-            {
-                in -= 4;
-                int k = in[3];
-                out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
-            }
-        }
-    }
-
-    int newMax = QMAX(cinfo.output_width, cinfo.output_height);
-    int newx = cachedSize_*cinfo.output_width / newMax;
-    int newy = cachedSize_*cinfo.output_height / newMax;
-
-    jpeg_destroy_decompress(&cinfo);
-    fclose(inputFile);
-
-    image = img.smoothScale(newx,newy);
-
-    return true;
+    return Digikam::loadJPEGScaled(image, path, cachedSize_);
 }
 
 void kio_digikamthumbnailProtocol::exifRotate(const QString& filePath, QImage& thumb)
--- trunk/extragear/graphics/digikam/libs/jpegutils/jpegutils.cpp #633638:633639
@@ -108,6 +108,141 @@
 #endif
 }
 
+bool loadJPEGScaled(QImage& image, const QString& path, int maximumSize)
+{
+    QString format = QImageIO::imageFormat(path);
+    if (format !="JPEG") return false;
+
+    FILE* inputFile=fopen(QFile::encodeName(path), "rb");
+    if(!inputFile)
+        return false;
+
+    struct jpeg_decompress_struct cinfo;
+    struct jpegutils_jpeg_error_mgr       jerr;
+
+    // JPEG error handling - thanks to Marcus Meissner
+    cinfo.err                 = jpeg_std_error(&jerr);
+    cinfo.err->error_exit     = jpegutils_jpeg_error_exit;
+    cinfo.err->emit_message   = jpegutils_jpeg_emit_message;
+    cinfo.err->output_message = jpegutils_jpeg_output_message;
+
+    if (setjmp(jerr.setjmp_buffer)) 
+    {
+        jpeg_destroy_decompress(&cinfo);
+        fclose(inputFile);
+        return false;
+    }
+
+    jpeg_create_decompress(&cinfo);
+    jpeg_stdio_src(&cinfo, inputFile);
+    jpeg_read_header(&cinfo, true);
+
+    int imgSize = QMAX(cinfo.image_width, cinfo.image_height);
+
+    // libjpeg supports 1/1, 1/2, 1/4, 1/8
+    int scale=1;
+    while(maximumSize*scale*2<=imgSize)
+    {
+        scale*=2;
+    }
+    if(scale>8) scale=8;
+
+    cinfo.scale_num=1;
+    cinfo.scale_denom=scale;
+
+    switch (cinfo.jpeg_color_space)
+    {
+        case JCS_UNKNOWN:
+            break;
+        case JCS_GRAYSCALE:
+        case JCS_RGB:
+        case JCS_YCbCr:
+            cinfo.out_color_space = JCS_RGB;
+            break;
+        case JCS_CMYK:
+        case JCS_YCCK:
+            cinfo.out_color_space = JCS_CMYK;
+            break;
+    }
+
+    jpeg_start_decompress(&cinfo);
+
+    QImage img;
+
+    // We only take RGB with 1 or 3 components, or CMYK with 4 components
+    if (!(
+           (cinfo.out_color_space == JCS_RGB  && (cinfo.output_components == 3 || cinfo.output_components == 1))
+        || (cinfo.out_color_space == JCS_CMYK &&  cinfo.output_components == 4)
+        ))
+    {
+        jpeg_destroy_decompress(&cinfo);
+        fclose(inputFile);
+        return false;
+    }
+
+    switch(cinfo.output_components) 
+    {
+        case 3:
+        case 4:
+            img.create( cinfo.output_width, cinfo.output_height, 32 );
+            break;
+        case 1: // B&W image
+            img.create( cinfo.output_width, cinfo.output_height, 8, 256 );
+            for (int i = 0 ; i < 256 ; i++)
+                img.setColor(i, qRgb(i, i, i));
+            break;
+    }
+
+    uchar** lines = img.jumpTable();
+    while (cinfo.output_scanline < cinfo.output_height)
+        jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height);
+
+    jpeg_finish_decompress(&cinfo);
+
+    // Expand 24->32 bpp
+    if ( cinfo.output_components == 3 )
+    {
+        for (uint j=0; j<cinfo.output_height; j++) 
+        {
+            uchar *in = img.scanLine(j) + cinfo.output_width*3;
+            QRgb *out = (QRgb*)( img.scanLine(j) );
+
+            for (uint i=cinfo.output_width; i--; ) 
+            {
+                in -= 3;
+                out[i] = qRgb(in[0], in[1], in[2]);
+            }
+        }
+    }
+    else if ( cinfo.output_components == 4 )
+    {
+        // CMYK conversion
+        for (uint j=0; j<cinfo.output_height; j++)
+        {
+            uchar *in = img.scanLine(j) + cinfo.output_width*4;
+            QRgb *out = (QRgb*)( img.scanLine(j) );
+
+            for (uint i=cinfo.output_width; i--; )
+            {
+                in -= 4;
+                int k = in[3];
+                out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
+            }
+        }
+    }
+
+    int newMax = QMAX(cinfo.output_width, cinfo.output_height);
+    int newx = maximumSize*cinfo.output_width / newMax;
+    int newy = maximumSize*cinfo.output_height / newMax;
+
+    jpeg_destroy_decompress(&cinfo);
+    fclose(inputFile);
+
+    image = img.smoothScale(newx,newy);
+
+    return true;
+}
+
 bool exifRotate(const QString& file, const QString& documentName)
 {
     QFileInfo fi(file);
--- trunk/extragear/graphics/digikam/libs/jpegutils/jpegutils.h #633638:633639
@@ -26,10 +26,12 @@
 // Qt includes.
 
 #include <qstring.h>
+#include <qimage.h>
 
 namespace Digikam
 {
 
+bool loadJPEGScaled(QImage& image, const QString& path, int maximumSize);
 bool exifRotate(const QString& file, const QString& documentName);
 bool jpegConvert(const QString& src, const QString& dest, const QString& documentName, 
                  const QString& format=QString("PNG"));
--- trunk/extragear/graphics/digikam/libs/threadimageio/Makefile.am #633638:633639
@@ -18,5 +18,6 @@
 	       -I$(top_srcdir)/digikam/libs/dimg/loaders \
 	       -I$(top_srcdir)/digikam/libs/dmetadata \
 	       -I$(top_srcdir)/digikam/libs/dcraw \
+	       -I$(top_srcdir)/digikam/libs/jpegutils \
 	       -I$(top_srcdir)/digikam/digikam \
 	       $(all_includes)
--- trunk/extragear/graphics/digikam/libs/threadimageio/previewtask.cpp #633638:633639
@@ -31,6 +31,7 @@
 #include "ddebug.h"
 #include "dmetadata.h"
 #include "dcrawiface.h"
+#include "jpegutils.h"
 #include "previewloadthread.h"
 #include "previewtask.h"
 
@@ -137,8 +138,14 @@
     {
         // Try to extract Exif/Iptc preview.
         if ( !loadImagePreview(qimage, m_loadingDescription.filePath) )
-            // Try to load with Qt/KDE.
-            qimage.load(m_loadingDescription.filePath);
+        {
+            // Try to load a JPEG with the fast scale-before-decoding method
+            if (!loadJPEGScaled(qimage, m_loadingDescription.filePath, size))
+            {
+                // Try to load with Qt/KDE.
+                qimage.load(m_loadingDescription.filePath);
+            }
+        }
     }
 
     if (qimage.isNull())
--- trunk/extragear/graphics/digikam/showfoto/Makefile.am #633638:633639
@@ -33,6 +33,7 @@
 	           $(top_builddir)/digikam/utilities/splashscreen/libsplashscreen.la \
 	           $(top_builddir)/digikam/utilities/slideshow/libslideshow.la \
 	           $(top_builddir)/digikam/libs/threadimageio/libthreadimageio.la \
+	           $(top_builddir)/digikam/libs/jpegutils/libjpegutils.la \
 	           $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KUTILS) \
 	           $(LIB_KFILE) $(LIB_KPARTS) $(LIBJPEG) $(EXIV2_LIBS) 
 
Comment 20 caulier.gilles 2007-04-06 18:54:25 UTC
This file is implemented in 0.9.1 release. I close it.

Gilles
Comment 21 Marco Cimmino 2007-04-09 10:54:07 UTC
Photos aren't prefetched in 0.9.1 so why closed as fixed? Photos are still loaded when page down is pressed and not before.
Comment 22 Arnd Baecker 2007-07-03 18:49:38 UTC
I went through all comments for this bug, and agree with #21
that prefetching is not yet done. I therefore re-open this bug.

Summary of open points:
- "The ultimate optimization to do is to preload in background to the
   cache the next and previous image around the current selected image
   from current album. " (Gilles, in #11)

- "Other optimizations to speed-up the drawing of pictures on the
   screen can be done using Qt4."  (Gilles, in #11)

- "For preloading there is a requirement that we dont meet currently: 
   The ability to stop the loading. We can do this with the DImg
   loaders,   but the preview is based on QImage." (Marcel, in #13)



Comment 23 Marcel Wiesweg 2007-07-04 13:34:51 UTC
Since January a lot has changed:

1) There is preloading in image preview. If you dont believe, start digikam from a console and watch the output in a directory with raw images ;-)

2) This is obviously what we are dealing with in the immediate future (weeks)

3) The preview is now based on DImg, but internally, loading of embbeded previews in raws and in IPTC and half-size preview of raws is non-interruptable (the latter may change I think, why not use the DImg loader). We have solved this problem by using two loading threads. Then the only problem is the use of resources by the non-interruptable operation, when the prediction of the preloading was not met (i.e., the currently preloaded image is not the needed one)
Comment 24 Arnd Baecker 2007-07-04 14:09:37 UTC
Ad 1) Well, I didn't believe, and before posting I did
the test with a directory of raw files, however without a look
at the console. And it does feel much slower
to go from the current to the next image than
going back to the previous (or other cached images).
I.e., once they are in cache it works brilliantly!

Looking at the console ouput: it does indeed say that
the next image is preloaded.
However, when going to the next image,
it again says "Running RAW decoding command kdcraw  ...."
on that image (?!) *and* the subsequent image (which is fine).
Sometimes I even observe a kcdraw call for the previous images?
(Just simply going through a bunch of images from the first to the last,
in F3 preview mode, "Embedded preview load full image size"
in the "Album Settings")

Maybe I am interpreting the output incorrectly?

BTW, how large is the cache used for all this (is its size configurable)?

In general, I know that you have been working very hard on this,
and the improvements are brilliant and very much appreciated
(All this, including 2) and 3) look technically rather involved!).

Thanks a lot, Arnd
Comment 25 caulier.gilles 2007-07-04 14:14:01 UTC
Arnd,

To extract an embedded preview from RAW file the command is "kdcraw -e ..."

Look dcraw man page for details.

Gilles
Comment 26 Arnd Baecker 2007-07-04 14:34:17 UTC
Right - thanks Gilles! So to me it seems that it does not preload when
"Embedded preview load full image size" is set in the "Album Settings".

Details: 
- Folder with 4 raw images: IMG_1939.CR2, ..., IMG_1942.CR2,
- album view 
- then press F3 (to preview  IMG_1939.CR2):
  
  digikam: IMG_1939.CR2 : RAW file identified
  Running RAW decoding command: kdcraw -c -v -h -w -a -H 0 -b 1 -q 0 
                                       -o1 IMG_1939.CR2
  Running RAW decoding command: kdcraw -c -e 'IMG_1940.CR2'
  Using embedded RAW preview extraction

  # Is there any preload done here? 
  # I.e. should there be a kcdraw -c -v .... IMG_1940.CR2?

- Page Down:
 
  digikam: IMG_1940.CR2 : RAW file identified
  Running RAW decoding command: kdcraw -c -v -h -w -a -H 0 -b 1 -q 0 
                                       -o 1 IMG_1940.CR2
  Running RAW decoding command: kdcraw -c -e 'IMG_1941.CR2'
  Using embedded RAW preview extraction
  Running RAW decoding command: kdcraw -c -e 'IMG_1939.CR2'
  Using embedded RAW preview extraction

If "embedded preview load full image size" is not active, I only
see the kdcraw -c -e '...' commands (and just 4 of them), and that's it.

Comment 27 Marcus Hardt 2008-02-19 09:20:21 UTC
Same for jpg. Images load really fast -- unless "Embedded preview load full image size" is set in the "Album Settings" which is unfortunate since I cannot zoom in.

While I don't know, where this behaviour comes from, maybe a reasonable workaround is to reload the full image size on zoom?

Comment 28 caulier.gilles 2008-03-19 08:06:42 UTC
Arnd,

Why this file have been re-open ? It's still a problem somewhere ?

Gilles Caulier
Comment 29 Arnd Baecker 2008-03-19 08:40:12 UTC
Gilles, the open issue is detailed in c#26. Unless something has changed,
this problem is still there ...
Comment 30 caulier.gilles 2008-03-19 08:58:16 UTC
Marcel,

What do you think about comments in #26 from Arnd ? Something is wrong in preloading interface ?

Gilles caulier
Comment 31 Marcel Wiesweg 2008-03-19 23:00:13 UTC
SVN commit 787804 by mwiesweg:

Load high quality version also when preloading the preview.
Seems to fix the bug.

CCBUG: 132047


 M  +6 -2      imagepreviewview.cpp  


WebSVN link: http://websvn.kde.org/?view=rev&revision=787804
Comment 32 caulier.gilles 2008-03-19 23:03:10 UTC
Arnd, 

Can you confirm ?

Gilles
Comment 33 Arnd Baecker 2008-03-20 10:52:59 UTC
It feels solved - many thanks!
Still I have two questions:
a) how large is the cache size (it seems that
   around 3-4 Raws are kept in memory)?
   Could/should one make this configurable?
b) When being in preview mode (with loading of the full image
   marked as active), there is something which puzzles me:
   Using PageDown for the sequence  
     IMG_8305.CR2  
     IMG_8306.CR2
     IMG_7061.CR2  
   I get the following  dcraw commands
   shown on the konsole :
   - Still in thumbnail view (i.e. initial start): 
     kdcraw -c -v -4 -h -H 0 -b 1 -w -q 0 -o 1 IMG_8305.CR2
   - then preview:
     kdcraw -c -v    -h -H 0 -b 1 -w -q 0 -o 1 IMG_8305.CR2
     kdcraw -c -v    -h -H 0 -b 1 -w -q 0 -o 1 IMG_8306.CR2
       (i.e preloading works!)
   - then PageDown
     kdcraw -c -v    -h -H 0 -b 1 -w -q 0 -o 1 IMG_7061.CR2
     kdcraw -c -v -4 -h -H 0 -b 1 -w -q 0 -o 1 IMG_8306.CR2

     Question: what is the -4 variant being used for ?

   Such a command sometimes also gets triggered for images, which apparantly
   are still in cache (at least the display is instantaneous)?

Comment 34 caulier.gilles 2008-03-20 13:43:55 UTC
Arnd,

"-4" option is dedicated to handle 16 bits color depth. Perhaps are you enabled  histogram on righ side bar which render stat using 16 bits color depth... And yes, in this case, both version are loaded in memory... it's not optimum (:=)))

Gilles
Comment 35 Arnd Baecker 2008-03-20 17:24:24 UTC
Well, I did not really enable the histogramm ;-) The right side-bar
was closed, however, it seems that the histogramm is still computed
in the background.
So in view of this bug here, everything is fine!
However, I will have to open new (separate) bugs for
a) don't load histogram if it is not visible
b) optimize memory usage/speed for histogram computation (i.e. re-use
   the data in memory, if availble)
c) there is no histogram for raws (only in log scale you see something)
d) allow full size preview (i.e. a 100% view should be a 100% and not a 50% one)

Best, Arnd
Comment 36 Marcel Wiesweg 2008-03-21 15:32:06 UTC
For RAW images, the situation is complex, because there is the preview (jpeg from metadata), the full size preview (fast dcraw loading, 8bit), the image for the histogram (fast dcraw loading, 16 bit), and the full 16-bit image for the editor.

We usually try to reuse as much cached image as possible, but priority is to load as fast as possible, and when then a higher-quality version is required, the already loaded data must be discarded. There is infrastructure to handle the event of a concurrent "more complete loading event": If the histogram is loading an image, and then the image editor begins to load, the histogram is notified and can decide to cancel its own operation and wait on the other.
Worst case is: First thumbnail generation, then preview, then histogram, then editor. This results in four subsequent dcraw calls. But we cannot avoid that: We dont know in advance what the user decides, and if he only wants the preview, we cannot accept to load the full version, which takes much longer, only because the user could choose to need in the next time. We cache what we have, we use higher-quality cached images if lower-quality is requested, and we can handle concurrent operations, cancelling the lower for the higher quality load.

Now we could go on and wait in the preview for the 16 bit image from histogram: Gilles, is this now possible after your auto coloradjustment feature for 16bit raws? Previously, the histogram image could not be reused for viewing.

You see that it is complex to decide what image can be used for what purpose, 
Comment 37 caulier.gilles 2008-03-22 10:00:39 UTC
Marcel,

>Now we could go on and wait in the preview for the 16 bit image from histogram: >Gilles, is this now possible after your auto coloradjustment feature for 16bit >raws? Previously, the histogram image could not be reused for viewing. 

Yes, it's possible, but there is many points where we need to take a care :

- 1st, there is 2 way to get preview : embeded JPEG or half decoded raw image.
- 2nd, histogram is currently always computed in 16 bits color depth with RAW. 
- 3rd, preview is always in 8 bits color depth.
- 4th, editor never use half RAW decoded image but full RAW decoded image instead.

Like 8 and 16 bits color depth RAW decoding have the same auto-gamma now, and if preview != embeded JPEG, i propose :

- to always use the same bits depth everywhere to compute preview and histogram
- to use the same image to compute histogram and preview.
- to always use the same settings about RAW color depth handling everywhere.

This way will simplify all RAW cache operations and will optimize memory consumptions.

Gilles
Comment 38 Stibbons 2009-09-14 18:23:25 UTC
Hello,

Here is what's happining in a unnamed commercial program:
- fast preview is generated as soon as possible for the current directory. Undoubtly the jpeg thumbnails embedded in jpegs files and DNGs
- Fitted previews are then generated after all fast previews are generated. This is used to display "fitted-to-screen" image with the embedded viewer
- Fullscale previews are then generated. To work on a file, all previews are discarded and the file is completly decoded.

My Humble Opinion: histogram is not important for a folder, so when the user is in a folder view, go fast and load all smalest/quickest previews available. If user wishes to see histogram for image "n", when the panel is opened, decode smallest preview if not available, and then decode for histogram. Don't worry about the worst case. If the histogram is updating and the user click on edit, just discard the histogram and load the complete file.

But smallest thumbnails shall be displayed to avoid "white squares of the death" the quickest possible (and not only on the current folder, if I import 5500 photo in 120 differents folder, I don't expect to click on every folder and wait for all the preview to have been generated).
Comment 39 caulier.gilles 2011-12-16 11:18:32 UTC
Brad,

This is an old entry, with some work on it.

It still valid using digiKam 2.4 ?

Gilles Caulier
Comment 40 Brad Templeton 2011-12-16 19:57:13 UTC
Speed is much improved.  But the opengl viewer, for example, is still much faster, and Digikam's main competition (picasa under wine) is also still faster even though it is several years older!     So there is still room for improvement.  Unfortunately the opengl viewer crashes (so that the only way out is to get out of the x server and kill it, the escape key does not work) and also is not integrated with the rest of the UI.      Truth is everybody who is going to do photo editing has a GPU and opengl, so possibly a new ticket is needed for better integration of opengl and associated code is needed.
Comment 41 caulier.gilles 2014-08-29 21:50:29 UTC
All improvements done in this file are enough for now and work as expected with current implementation from git/master.

For new wish about improvement of Preview mode, please open new file.

Gilles Caulier