From 1e367ea153e1af273a9d7cb68a5dfdd410e3beb0 Mon Sep 17 00:00:00 2001
From: Sebastião Guerra <sebastiao.luiz.guerra@gmail.com>
Date: Wed, 11 Nov 2026 07:33:40 -0300
Subject: kuickshow: avoid double JPEG EXIF orientation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Recognize Imlib2 versions that orient JPEG pixels during loading and avoid applying the same EXIF transform again. Keep the loader result separate from cached manual transforms and preserve the relative initial transform composition from master.

Signed-off-by: Sebastião Guerra <sebastiao.luiz.guerra@gmail.com>
---
 kuickshow/src/imagewindow.cpp |  10 ++--
 kuickshow/src/imagewindow.h   |   2 +-
 kuickshow/src/imlibwidget.cpp | 112 +++++++++++++++++++++++++++---------------
 kuickshow/src/imlibwidget.h   |   5 +-
 4 files changed, 81 insertions(+), 48 deletions(-)

diff --git a/kuickshow/src/imagewindow.cpp b/kuickshow/src/imagewindow.cpp
index b9f0d311..b0faef43 100644
--- a/kuickshow/src/imagewindow.cpp
+++ b/kuickshow/src/imagewindow.cpp
@@ -1019,10 +1019,12 @@ void ImageWindow::toggleFullscreen()
     showWindow();
 }
 
-void ImageWindow::loaded( KuickImage *kuim )
+void ImageWindow::loaded( KuickImage *kuim, bool decoded )
 {
 	if ( !kdata->isModsEnabled ) {
 		// ### BUG: should be "restorePreviousSize"
+		if ( decoded )
+			undoAutoRotate( kuim );
 		kuim->restoreOriginalSize();
 	}
 	else
@@ -1095,9 +1097,6 @@ void ImageWindow::autoScale( KuickImage *kuim )
 // only called when kdata->isModsEnabled is true
 bool ImageWindow::autoRotate( KuickImage *kuim )
 {
-    // Apply EXIF first. ImlibWidget::autoRotate() also resets any cached
-    // transform state to the metadata orientation, so the user's defaults
-    // below can be composed on top without preserving later manual edits.
     bool exifApplied = kdata->autoRotation && ImlibWidget::autoRotate( kuim );
 
     // The initial user preferences are applied after metadata handling;
@@ -1110,7 +1109,8 @@ bool ImageWindow::autoRotate( KuickImage *kuim )
     if ( kdata->rotation != ROT_0 )
         op *= kdata->rotation;
 
-    if ( exifApplied )
+    bool loaderOrientationUndone = !kdata->autoRotation && undoAutoRotate( kuim );
+    if ( exifApplied || loaderOrientationUndone )
     {
         kuim->flipRotate( op );
     }
diff --git a/kuickshow/src/imagewindow.h b/kuickshow/src/imagewindow.h
index 61e3efb5..21d507bd 100644
--- a/kuickshow/src/imagewindow.h
+++ b/kuickshow/src/imagewindow.h
@@ -100,7 +100,7 @@ protected:
   void 		centerImage();
   void          addAlternativeShortcut( TDEAction *action, int key );
   virtual void	updateGeometry( int imWidth, int imHeight );
-  virtual void  loaded( KuickImage * );
+  virtual void  loaded( KuickImage *, bool decoded );
   virtual bool  canZoomTo( int newWidth, int newHeight );
   virtual void  rotated( KuickImage *kuim, int rotation );
 
diff --git a/kuickshow/src/imlibwidget.cpp b/kuickshow/src/imlibwidget.cpp
index f4b6b797..8efcf942 100644
--- a/kuickshow/src/imlibwidget.cpp
+++ b/kuickshow/src/imlibwidget.cpp
@@ -129,7 +129,8 @@ KuickImage * ImlibWidget::loadImageInternal( KuickFile * file )
 {
     assert( file->isAvailable() );
 
-    KuickImage *kuim = imageCache->getKuimage( file );
+    bool decoded = false;
+    KuickImage *kuim = imageCache->getKuimage( file, decoded );
     if ( !kuim ) {// couldn't load file, maybe corrupt or wrong format
 	kdWarning() << "ImlibWidget: can't load image " << file->url().prettyURL() << endl;
 	return 0L;
@@ -141,7 +142,7 @@ KuickImage * ImlibWidget::loadImageInternal( KuickFile * file )
     colMod.gamma = idata->gamma;
     applyImageModifier( kuim );
 
-    loaded( kuim ); // maybe upscale/downscale/rotate in subclasses
+    loaded( kuim, decoded ); // maybe upscale/downscale/rotate in subclasses
 
     return kuim;
 }
@@ -269,7 +270,23 @@ void ImlibWidget::showImageOriginalSize()
     showImage();
 }
 
-bool ImlibWidget::autoRotate( KuickImage *kuim )
+static FlipRotation exifOrientation( int orientation )
+{
+    switch ( orientation )
+    {
+        case 2: return FlipRotation::hflip();
+        case 3: return FlipRotation( ROT_180 );
+        case 4: return FlipRotation::vflip();
+        case 5: return FlipRotation( ROT_90 ) * FlipRotation::hflip();
+        case 6: return FlipRotation( ROT_90 );
+        case 7: return FlipRotation( ROT_270 ) * FlipRotation::hflip();
+        case 8: return FlipRotation( ROT_270 );
+        case 1:
+        default: return FlipRotation( ROT_0 );
+    }
+}
+
+static bool readExifOrientation( KuickImage *kuim, FlipRotation& orientation )
 {
     KFileMetaInfo metadatas( kuim->file().localFile() );
     if ( !metadatas.isValid() )
@@ -282,48 +299,61 @@ bool ImlibWidget::autoRotate( KuickImage *kuim )
         return false;
 
 
-    switch ( metaitem.value().toInt() )
-    {
-        //  Orientation:
-        //  1:      normal
-        //  2:      flipped horizontally
-        //  3:      ROT 180
-        //  4:      flipped vertically
-        //  5:      ROT 90 -> flip horizontally
-        //  6:      ROT 90
-        //  7:      ROT 270 -> flip horizontally
-        //  8:      ROT 270
+    orientation = exifOrientation( metaitem.value().toInt() );
+    return true;
+}
 
-        case 1:
-        default:
-            kuim->flipRotateAbs( FlipRotation( ROT_0 ) );
-            break;
-        case 2:
-            kuim->flipRotateAbs( FlipRotation::hflip() );
-            break;
-        case 3:
-            kuim->flipRotateAbs( FlipRotation( ROT_180 ) );
-            break;
-        case 4:
-            kuim->flipRotateAbs( FlipRotation::vflip() );
-            break;
-        case 5:
-            kuim->flipRotateAbs( FlipRotation( ROT_90 ) * FlipRotation::hflip() );
-            break;
-        case 6:
-            kuim->flipRotateAbs( FlipRotation( ROT_90 ) );
-            break;
-        case 7:
-            kuim->flipRotateAbs( FlipRotation( ROT_270 ) * FlipRotation::hflip() );
-            break;
-        case 8:
-            kuim->flipRotateAbs( FlipRotation( ROT_270 ) );
-            break;
+static bool imlibAutoOrientsExif( KuickImage *kuim, Imlib_Context context )
+{
+#if defined(IMLIB2_VERSION) && IMLIB2_VERSION >= 10705
+    imlib_context_push( context );
+    // Smooth resizing creates an image without a format; the retained
+    // original still identifies which loader handled the image.
+    imlib_context_set_image( kuim->originalImage() );
+    TQString format = imlib_image_format();
+    imlib_context_pop();
+
+    // Imlib2 1.7.5 added automatic EXIF orientation to its JPEG loader.
+    return format == "jpg" || format == "jpeg";
+#else
+    (void) kuim;
+    (void) context;
+    return false;
+#endif
+}
+
+bool ImlibWidget::autoRotate( KuickImage *kuim )
+{
+    FlipRotation orientation;
+    if ( !readExifOrientation( kuim, orientation ) )
+        return false;
+
+    if ( imlibAutoOrientsExif( kuim, context ) )
+    {
+        kuim->flipRotateAbs( FlipRotation( ROT_0 ) );
+        return true;
     }
 
+    kuim->flipRotateAbs( orientation );
     return true;
 }
 
+bool ImlibWidget::undoAutoRotate( KuickImage *kuim )
+{
+#if defined(IMLIB2_VERSION) && IMLIB2_VERSION >= 10705
+    FlipRotation orientation;
+    if ( readExifOrientation( kuim, orientation ) &&
+         imlibAutoOrientsExif( kuim, context ) )
+    {
+        kuim->flipRotateAbs( orientation.inverse() );
+        return true;
+    }
+#else
+    (void) kuim;
+#endif
+    return false;
+}
+
 
 void ImlibWidget::setRotation( Rotation rot )
 {
@@ -632,8 +662,9 @@ Imlib_Image ImageCache::fromTQImage( const TQString& fileName ) const
 }
 
 
-KuickImage * ImageCache::getKuimage( KuickFile * file )
+KuickImage * ImageCache::getKuimage( KuickFile * file, bool &decoded )
 {
+    decoded = false;
     if ( !file )
 	return 0L;
 
@@ -688,6 +719,7 @@ KuickImage * ImageCache::getKuimage( KuickFile * file )
 
 	kuickList.insert( 0, kuim );
 	fileList.prepend( file );
+	decoded = true;
     }
 
     if ( kuickList.count() > (uint) myMaxImages ) {
diff --git a/kuickshow/src/imlibwidget.h b/kuickshow/src/imlibwidget.h
index d0281055..13ffe961 100644
--- a/kuickshow/src/imlibwidget.h
+++ b/kuickshow/src/imlibwidget.h
@@ -43,7 +43,7 @@ public:
   void setMaxImages( int maxImages );
   int  maxImages() const { return myMaxImages; }
 
-  KuickImage* getKuimage( KuickFile * file );
+  KuickImage* getKuimage( KuickFile * file, bool &decoded );
 
 private:
   Imlib_Image fromTQImage( const TQString& fileName ) const;
@@ -114,13 +114,14 @@ public slots:
 
 protected:
   KuickImage *	loadImageInternal( KuickFile * file );
+	bool		undoAutoRotate( KuickImage *kuim );
   void 		showImage();
   void 		applyImageModifier( KuickImage *kuim );
   void 		resetImageModifier();
   void 		rotate( int );
   void 		updateWidget( bool geometryUpdate=true );
   virtual void 	updateGeometry( int width, int height );
-  virtual void  loaded( KuickImage * ) {}
+  virtual void  loaded( KuickImage *, bool decoded ) {}
   virtual bool  canZoomTo( int newWidth, int newHeight );
   virtual void  rotated( KuickImage *kuim, int rotation ) {}
 
-- 
cgit v1.2.3

