File indexing completed on 2025-01-05 03:55:20

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2005-06-14
0007  * Description : digiKam 8/16 bits image management API.
0008  *               Transform operations.
0009  *
0010  * SPDX-FileCopyrightText: 2005-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "dimg_p.h"
0018 
0019 namespace Digikam
0020 {
0021 
0022 void DImg::crop(const QRect& rect)
0023 {
0024     crop(rect.x(), rect.y(), rect.width(), rect.height());
0025 }
0026 
0027 void DImg::crop(int x, int y, int w, int h)
0028 {
0029     if (isNull() || (w <= 0) || (h <= 0))
0030     {
0031         return;
0032     }
0033 
0034     uint  oldw = width();
0035     uint  oldh = height();
0036     QScopedArrayPointer<uchar> old(stripImageData());
0037 
0038     // set new image data, bits(), width(), height() change
0039 
0040     setImageDimension(w, h);
0041     allocateData();
0042 
0043     // copy image region (x|y), wxh, from old data to point (0|0) of new data
0044 
0045     bitBlt(old.data(), bits(), x, y, w, h, 0, 0, oldw, oldh, width(), height(), sixteenBit(), bytesDepth(), bytesDepth());
0046 }
0047 
0048 void DImg::resize(int w, int h)
0049 {
0050     if (isNull() || (w <= 0) || (h <= 0))
0051     {
0052         return;
0053     }
0054 
0055     DImg image   = smoothScale(w, h);
0056 
0057     delete [] m_priv->data;
0058     m_priv->data = image.stripImageData();
0059     setImageDimension(w, h);
0060 }
0061 
0062 void DImg::rotate(ANGLE angle)
0063 {
0064     if (isNull())
0065     {
0066         return;
0067     }
0068 
0069     bool switchDims = false;
0070 
0071     switch (angle)
0072     {
0073         case (ROT90):
0074         {
0075             uint w  = height();
0076             uint h  = width();
0077 
0078             if (sixteenBit())
0079             {
0080                 ullong* newData = DImgLoader::new_failureTolerant<ullong>(w * h);
0081                 ullong* from    = reinterpret_cast<ullong*>(m_priv->data);
0082                 ullong* to      = nullptr;
0083 
0084                 for (int y = w - 1 ; y >= 0 ; --y)
0085                 {
0086                     to = newData + y;
0087 
0088                     for (uint x = 0 ; x < h ; ++x)
0089                     {
0090                         *to = *from++;
0091                         to += w;
0092                     }
0093                 }
0094 
0095                 switchDims = true;
0096 
0097                 delete [] m_priv->data;
0098                 m_priv->data = (uchar*)newData;
0099             }
0100             else
0101             {
0102                 uint* newData = DImgLoader::new_failureTolerant<uint>(w * h);
0103                 uint* from    = reinterpret_cast<uint*>(m_priv->data);
0104                 uint* to      = nullptr;
0105 
0106                 for (int y = w - 1 ; y >= 0 ; --y)
0107                 {
0108                     to = newData + y;
0109 
0110                     for (uint x = 0 ; x < h ; ++x)
0111                     {
0112                         *to = *from++;
0113                         to += w;
0114                     }
0115                 }
0116 
0117                 switchDims = true;
0118 
0119                 delete [] m_priv->data;
0120                 m_priv->data = (uchar*)newData;
0121             }
0122 
0123             break;
0124         }
0125 
0126         case (ROT180):
0127         {
0128             uint w          = width();
0129             uint h          = height();
0130             int middle_line = -1;
0131 
0132             if (h % 2)
0133             {
0134                 middle_line = h / 2;
0135             }
0136 
0137             if (sixteenBit())
0138             {
0139                 ullong* line1 = nullptr;
0140                 ullong* line2 = nullptr;
0141                 ullong* data  = reinterpret_cast<ullong*>(bits());
0142                 ullong  tmp;
0143 
0144                 // can be done inplace
0145 
0146                 uint ymax = (h + 1) / 2;
0147 
0148                 for (uint y = 0 ; y < ymax ; ++y)
0149                 {
0150                     line1 = data + y * w;
0151                     line2 = data + (h - y) * w - 1;
0152 
0153                     for (uint x = 0 ; x < w ; ++x)
0154                     {
0155                         tmp    = *line1;
0156                         *line1 = *line2;
0157                         *line2 = tmp;
0158 
0159                         ++line1;
0160                         --line2;
0161 
0162                         if (((int)y == middle_line) && ((x * 2) >= w))
0163                         {
0164                             break;
0165                         }
0166                     }
0167                 }
0168             }
0169             else
0170             {
0171                 uint* line1 = nullptr;
0172                 uint* line2 = nullptr;
0173                 uint* data  = reinterpret_cast<uint*>(bits());
0174                 uint  tmp;
0175 
0176                 // can be done inplace
0177 
0178                 uint ymax = (h + 1) / 2;
0179 
0180                 for (uint y = 0 ; y < ymax ; ++y)
0181                 {
0182                     line1 = data + y * w;
0183                     line2 = data + (h - y) * w - 1;
0184 
0185                     for (uint x = 0 ; x < w ; ++x)
0186                     {
0187                         tmp    = *line1;
0188                         *line1 = *line2;
0189                         *line2 = tmp;
0190 
0191                         ++line1;
0192                         --line2;
0193 
0194                         if (((int)y == middle_line) && ((x * 2) >= w))
0195                         {
0196                             break;
0197                         }
0198                     }
0199                 }
0200             }
0201 
0202             break;
0203         }
0204 
0205         case (ROT270):
0206         {
0207             uint w  = height();
0208             uint h  = width();
0209 
0210             if (sixteenBit())
0211             {
0212                 ullong* newData = DImgLoader::new_failureTolerant<ullong>(w * h);
0213                 ullong* from    = reinterpret_cast<ullong*>(m_priv->data);
0214                 ullong* to      = nullptr;
0215 
0216                 for (uint y = 0 ; y < w ; ++y)
0217                 {
0218                     to = newData + y + w * (h - 1);
0219 
0220                     for (uint x = 0 ; x < h ; ++x)
0221                     {
0222                         *to = *from++;
0223                         to -= w;
0224                     }
0225                 }
0226 
0227                 switchDims = true;
0228 
0229                 delete [] m_priv->data;
0230                 m_priv->data = (uchar*)newData;
0231             }
0232             else
0233             {
0234                 uint* newData = DImgLoader::new_failureTolerant<uint>(w * h);
0235                 uint* from    = reinterpret_cast<uint*>(m_priv->data);
0236                 uint* to      = nullptr;
0237 
0238                 for (uint y = 0 ; y < w ; ++y)
0239                 {
0240                     to = newData + y + w * (h - 1);
0241 
0242                     for (uint x = 0 ; x < h ; ++x)
0243                     {
0244                         *to = *from++;
0245                         to -= w;
0246                     }
0247                 }
0248 
0249                 switchDims = true;
0250 
0251                 delete [] m_priv->data;
0252                 m_priv->data = (uchar*)newData;
0253             }
0254 
0255             break;
0256         }
0257 
0258         default:
0259             break;
0260     }
0261 
0262     if (switchDims)
0263     {
0264         setImageDimension(height(), width());
0265         QMap<QString, QVariant>::iterator it = m_priv->attributes.find(QLatin1String("originalSize"));
0266 
0267         if (it != m_priv->attributes.end())
0268         {
0269             QSize size = it.value().toSize();
0270             it.value() = QSize(size.height(), size.width());
0271         }
0272     }
0273 }
0274 
0275 // NOTE: This method have been tested in-deep with valgrind.
0276 
0277 void DImg::flip(FLIP direction)
0278 {
0279     if (isNull())
0280     {
0281         return;
0282     }
0283 
0284     switch (direction)
0285     {
0286         case (HORIZONTAL):
0287         {
0288             uint w  = width();
0289             uint h  = height();
0290 
0291             if (sixteenBit())
0292             {
0293                 unsigned short  tmp[4];
0294                 unsigned short* beg  = nullptr;
0295                 unsigned short* end  = nullptr;
0296                 unsigned short* data = reinterpret_cast<unsigned short*>(bits());
0297 
0298                 // can be done inplace
0299 
0300                 uint wHalf = (w / 2);
0301 
0302                 for (uint y = 0 ; y < h ; ++y)
0303                 {
0304                     beg = data + y * w * 4;
0305                     end = beg  + (w - 1) * 4;
0306 
0307                     for (uint x = 0 ; x < wHalf ; ++x)
0308                     {
0309                         memcpy(&tmp, beg,  8);
0310                         memcpy(beg,  end,  8);
0311                         memcpy(end,  &tmp, 8);
0312 
0313                         beg += 4;
0314                         end -= 4;
0315                     }
0316                 }
0317             }
0318             else
0319             {
0320                 uchar  tmp[4];
0321                 uchar* beg  = nullptr;
0322                 uchar* end  = nullptr;
0323                 uchar* data = bits();
0324 
0325                 // can be done inplace
0326 
0327                 uint wHalf = (w / 2);
0328 
0329                 for (uint y = 0 ; y < h ; ++y)
0330                 {
0331                     beg = data + y * w * 4;
0332                     end = beg  + (w - 1) * 4;
0333 
0334                     for (uint x = 0 ; x < wHalf ; ++x)
0335                     {
0336                         memcpy(&tmp, beg,  4);
0337                         memcpy(beg,  end,  4);
0338                         memcpy(end,  &tmp, 4);
0339 
0340                         beg += 4;
0341                         end -= 4;
0342                     }
0343                 }
0344             }
0345 
0346             break;
0347         }
0348 
0349         case (VERTICAL):
0350         {
0351             uint w  = width();
0352             uint h  = height();
0353 
0354             if (sixteenBit())
0355             {
0356                 unsigned short  tmp[4];
0357                 unsigned short* line1 = nullptr;
0358                 unsigned short* line2 = nullptr;
0359                 unsigned short* data  = reinterpret_cast<unsigned short*>(bits());
0360 
0361                 // can be done inplace
0362 
0363                 uint hHalf = (h / 2);
0364 
0365                 for (uint y = 0 ; y < hHalf ; ++y)
0366                 {
0367                     line1 = data + y * w * 4;
0368                     line2 = data + (h - y - 1) * w * 4;
0369 
0370                     for (uint x = 0 ; x < w ; ++x)
0371                     {
0372                         memcpy(&tmp,  line1, 8);
0373                         memcpy(line1, line2, 8);
0374                         memcpy(line2, &tmp,  8);
0375 
0376                         line1 += 4;
0377                         line2 += 4;
0378                     }
0379                 }
0380             }
0381             else
0382             {
0383                 uchar  tmp[4];
0384                 uchar* line1 = nullptr;
0385                 uchar* line2 = nullptr;
0386                 uchar* data  = bits();
0387 
0388                 // can be done inplace
0389 
0390                 uint hHalf = (h / 2);
0391 
0392                 for (uint y = 0 ; y < hHalf ; ++y)
0393                 {
0394                     line1 = data + y * w * 4;
0395                     line2 = data + (h - y - 1) * w * 4;
0396 
0397                     for (uint x = 0 ; x < w ; ++x)
0398                     {
0399                         memcpy(&tmp,  line1, 4);
0400                         memcpy(line1, line2, 4);
0401                         memcpy(line2, &tmp,  4);
0402 
0403                         line1 += 4;
0404                         line2 += 4;
0405                     }
0406                 }
0407             }
0408 
0409             break;
0410         }
0411 
0412         default:
0413             break;
0414     }
0415 }
0416 
0417 bool DImg::rotateAndFlip(int orientation)
0418 {
0419     bool rotatedOrFlipped = false;
0420 
0421     switch (orientation)
0422     {
0423         case DMetadata::ORIENTATION_NORMAL:
0424         case DMetadata::ORIENTATION_UNSPECIFIED:
0425         {
0426             return false;
0427         }
0428 
0429         case DMetadata::ORIENTATION_HFLIP:
0430         {
0431             flip(DImg::HORIZONTAL);
0432             rotatedOrFlipped = true;
0433             break;
0434         }
0435 
0436         case DMetadata::ORIENTATION_ROT_180:
0437         {
0438             rotate(DImg::ROT180);
0439             rotatedOrFlipped = true;
0440             break;
0441         }
0442 
0443         case DMetadata::ORIENTATION_VFLIP:
0444         {
0445             flip(DImg::VERTICAL);
0446             rotatedOrFlipped = true;
0447             break;
0448         }
0449 
0450         case DMetadata::ORIENTATION_ROT_90_HFLIP:
0451         {
0452             rotate(DImg::ROT90);
0453             flip(DImg::HORIZONTAL);
0454             rotatedOrFlipped = true;
0455             break;
0456         }
0457 
0458         case DMetadata::ORIENTATION_ROT_90:
0459         {
0460             rotate(DImg::ROT90);
0461             rotatedOrFlipped = true;
0462             break;
0463         }
0464 
0465         case DMetadata::ORIENTATION_ROT_90_VFLIP:
0466         {
0467             rotate(DImg::ROT90);
0468             flip(DImg::VERTICAL);
0469             rotatedOrFlipped = true;
0470             break;
0471         }
0472 
0473         case DMetadata::ORIENTATION_ROT_270:
0474         {
0475             rotate(DImg::ROT270);
0476             rotatedOrFlipped = true;
0477             break;
0478         }
0479     }
0480 
0481     return rotatedOrFlipped;
0482 }
0483 
0484 bool DImg::reverseRotateAndFlip(int orientation)
0485 {
0486     bool rotatedOrFlipped = false;
0487 
0488     switch (orientation)
0489     {
0490         case DMetadata::ORIENTATION_NORMAL:
0491         case DMetadata::ORIENTATION_UNSPECIFIED:
0492         {
0493             return false;
0494         }
0495 
0496         case DMetadata::ORIENTATION_HFLIP:
0497         {
0498             flip(DImg::HORIZONTAL);
0499             rotatedOrFlipped = true;
0500             break;
0501         }
0502 
0503         case DMetadata::ORIENTATION_ROT_180:
0504         {
0505             rotate(DImg::ROT180);
0506             rotatedOrFlipped = true;
0507             break;
0508         }
0509 
0510         case DMetadata::ORIENTATION_VFLIP:
0511         {
0512             flip(DImg::VERTICAL);
0513             rotatedOrFlipped = true;
0514             break;
0515         }
0516 
0517         case DMetadata::ORIENTATION_ROT_90_HFLIP:
0518         {
0519             flip(DImg::HORIZONTAL);
0520             rotate(DImg::ROT270);
0521             rotatedOrFlipped = true;
0522             break;
0523         }
0524 
0525         case DMetadata::ORIENTATION_ROT_90:
0526         {
0527             rotate(DImg::ROT270);
0528             rotatedOrFlipped = true;
0529             break;
0530         }
0531 
0532         case DMetadata::ORIENTATION_ROT_90_VFLIP:
0533         {
0534             flip(DImg::VERTICAL);
0535             rotate(DImg::ROT270);
0536             rotatedOrFlipped = true;
0537             break;
0538         }
0539 
0540         case DMetadata::ORIENTATION_ROT_270:
0541         {
0542             rotate(DImg::ROT90);
0543             rotatedOrFlipped = true;
0544             break;
0545         }
0546     }
0547 
0548     return rotatedOrFlipped;
0549 }
0550 
0551 bool DImg::transform(int transformAction)
0552 {
0553     switch (transformAction)
0554     {
0555         case MetaEngineRotation::NoTransformation:
0556         default:
0557         {
0558             return false;
0559         }
0560 
0561         case MetaEngineRotation::FlipHorizontal:
0562         {
0563             flip(DImg::HORIZONTAL);
0564             break;
0565         }
0566 
0567         case MetaEngineRotation::FlipVertical:
0568         {
0569             flip(DImg::VERTICAL);
0570             break;
0571         }
0572 
0573         case MetaEngineRotation::Rotate90:
0574         {
0575             rotate(DImg::ROT90);
0576             break;
0577         }
0578 
0579         case MetaEngineRotation::Rotate180:
0580         {
0581             rotate(DImg::ROT180);
0582             break;
0583         }
0584 
0585         case MetaEngineRotation::Rotate270:
0586         {
0587             rotate(DImg::ROT270);
0588             break;
0589         }
0590     }
0591 
0592     return true;
0593 }
0594 
0595 bool DImg::wasExifRotated()
0596 {
0597     QVariant rotated(attribute(QLatin1String("exifRotated")));
0598 
0599     return (rotated.isValid() && rotated.toBool());
0600 }
0601 
0602 bool DImg::exifRotate(const QString& filePath)
0603 {
0604     if (wasExifRotated())
0605     {
0606         return false;
0607     }
0608 
0609     // Rotate image based on metadata orientation information
0610 
0611     setAttribute(QLatin1String("exifRotated"), true);
0612 
0613     return rotateAndFlip(exifOrientation(filePath));
0614 }
0615 
0616 bool DImg::reverseExifRotate(const QString& filePath)
0617 {
0618     return reverseRotateAndFlip(exifOrientation(filePath));
0619 }
0620 
0621 int DImg::orientation() const
0622 {
0623     QScopedPointer<DMetadata> meta(new DMetadata(getMetadata()));
0624 
0625     return (int)meta->getItemOrientation();
0626 }
0627 
0628 } // namespace Digikam