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