File indexing completed on 2025-01-05 03:59:36

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2006-2023 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0005 // SPDX-FileCopyrightText: 2008-2010 Jens-Michael Hoffmann <jensmh@gmx.de>
0006 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0007 //
0008 
0009 #include "StackedTile.h"
0010 
0011 #include "TextureTile.h"
0012 
0013 #include "digikam_debug.h"
0014 
0015 using namespace Marble;
0016 
0017 static const uint **jumpTableFromQImage32( const QImage &img )
0018 {
0019     if ( img.depth() != 48 && img.depth() != 32 )
0020         return nullptr;
0021 
0022     const int  height = img.height();
0023     const int  bpl    = img.bytesPerLine() / 4;
0024     const uint  *data = reinterpret_cast<const QRgb*>(img.bits());
0025     const uint **jumpTable = new const uint*[height];
0026 
0027     for ( int y = 0; y < height; ++y ) {
0028         jumpTable[ y ] = data;
0029         data += bpl;
0030     }
0031 
0032     return jumpTable;
0033 }
0034 
0035 
0036 static const uchar **jumpTableFromQImage8( const QImage &img )
0037 {
0038     if ( img.depth() != 8 && img.depth() != 1 )
0039         return nullptr;
0040 
0041     const int  height = img.height();
0042     const int  bpl    = img.bytesPerLine();
0043     const uchar  *data = img.bits();
0044     const uchar **jumpTable = new const uchar*[height];
0045 
0046     for ( int y = 0; y < height; ++y ) {
0047         jumpTable[ y ] = data;
0048         data += bpl;
0049     }
0050 
0051     return jumpTable;
0052 }
0053 
0054 // return channelwise average of colors c1 and c2
0055 static inline uint colorMix50(uint c1, uint c2)
0056 {
0057     return (((c1 ^ c2) & 0xfefefefeUL) >> 1) + (c1 & c2);
0058 }
0059 
0060 static inline uint colorMix75(uint c1, uint c2)
0061 {
0062     return colorMix50(c1, colorMix50(c1, c2)); // 75% c1
0063 }
0064 
0065 static inline uint colorMix25(uint c1, uint c2)
0066 {
0067     return colorMix50(colorMix50(c1, c2), c2 ); // 25% c1
0068 }
0069 
0070 StackedTile::StackedTile( const TileId &id, const QImage &resultImage, QVector<QSharedPointer<TextureTile> > const &tiles ) :
0071       Tile( id ),
0072       m_resultImage( resultImage ),
0073       m_depth( resultImage.depth() ),
0074       m_isGrayscale( resultImage.isGrayscale() ),
0075       m_tiles( tiles ),
0076       jumpTable8( jumpTableFromQImage8( m_resultImage ) ),
0077       jumpTable32( jumpTableFromQImage32( m_resultImage ) ),
0078       m_byteCount( calcByteCount( resultImage, tiles ) ),
0079       m_isUsed( false )
0080 {
0081     Q_ASSERT( !tiles.isEmpty() );
0082 
0083     if ( jumpTable32 == nullptr && jumpTable8 == nullptr ) {
0084         qCWarning(DIGIKAM_MARBLE_LOG) << "Color depth" << m_depth << " is not supported.";
0085     }
0086 }
0087 
0088 StackedTile::~StackedTile()
0089 {
0090       delete [] jumpTable32;
0091       delete [] jumpTable8;
0092 }
0093 
0094 uint StackedTile::pixel( int x, int y ) const
0095 {
0096     if ( m_depth == 32 && !m_isGrayscale )
0097         return (jumpTable32)[y][x];
0098 
0099     if ( m_depth == 8 ) {
0100         if ( m_isGrayscale )
0101             return (jumpTable8)[y][x];
0102         else
0103             return m_resultImage.color( (jumpTable8)[y][x] );
0104     }
0105 
0106     if ( m_depth == 1 && !m_isGrayscale )
0107         return m_resultImage.color((jumpTable8)[y][x/8] >> 7);
0108 
0109     return m_resultImage.pixel( x, y );
0110 }
0111 
0112 #define CHEAPHIGH
0113 #ifdef CHEAPHIGH
0114 
0115 uint StackedTile::pixelF(qreal x, qreal y, const QRgb& topLeftValue) const
0116 {
0117     // Bilinear interpolation to determine the color of a subpixel
0118     int iX = (int)(x);
0119     int iY = (int)(y);
0120 
0121     qreal fY = 8 * (y - iY);
0122 
0123     // Interpolation in y-direction
0124     if ((iY + 1) < m_resultImage.height())
0125     {
0126         QRgb bottomLeftValue = pixel(iX, iY + 1);
0127 
0128         QRgb leftValue;
0129         if (fY < 1)
0130             leftValue = topLeftValue;
0131         else if (fY < 3)
0132             leftValue = colorMix75( topLeftValue, bottomLeftValue);
0133         else if (fY < 5)
0134             leftValue = colorMix50( topLeftValue, bottomLeftValue);
0135         else if (fY < 7)
0136             leftValue = colorMix25( topLeftValue, bottomLeftValue);
0137         else
0138             leftValue = bottomLeftValue;
0139 
0140         // Interpolation in x-direction
0141         if (iX + 1 < m_resultImage.width())
0142         {
0143             qreal fX = 8 * (x - iX);
0144 
0145             QRgb topRightValue    = pixel(iX + 1, iY);
0146             QRgb bottomRightValue = pixel(iX + 1, iY + 1);
0147 
0148             QRgb rightValue;
0149             if (fY < 1)
0150                 rightValue = topRightValue;
0151             else if (fY < 3)
0152                 rightValue = colorMix75( topRightValue, bottomRightValue);
0153             else if (fY < 5)
0154                 rightValue = colorMix50( topRightValue, bottomRightValue);
0155             else if (fY < 7)
0156                 rightValue = colorMix25( topRightValue, bottomRightValue);
0157             else
0158                 rightValue = bottomRightValue;
0159 
0160 
0161             QRgb averageValue;
0162 
0163             if (fX < 1)
0164                 averageValue = leftValue;
0165             else if (fX < 3)
0166                 averageValue = colorMix75( leftValue, rightValue);
0167             else if (fX < 5)
0168                 averageValue = colorMix50( leftValue, rightValue);
0169             else if (fX < 7)
0170                 averageValue = colorMix25( leftValue, rightValue);
0171             else
0172                 averageValue = rightValue;
0173 
0174             return averageValue;
0175         }
0176         else {
0177             return leftValue;
0178         }
0179     }
0180     else {
0181         // Interpolation in x-direction
0182         if ( iX + 1 < m_resultImage.width() ) {
0183 
0184             qreal fX = 8 * (x - iX);
0185 
0186             if ( fX == 0 )
0187                 return topLeftValue;
0188 
0189             QRgb topRightValue = pixel( iX + 1, iY );
0190 
0191             QRgb topValue;
0192             if (fX < 1)
0193                 topValue = topLeftValue;
0194             else if (fX < 3)
0195                 topValue = colorMix75( topLeftValue, topRightValue);
0196             else if (fX < 5)
0197                 topValue = colorMix50( topLeftValue, topRightValue);
0198             else if (fX < 7)
0199                 topValue = colorMix25( topLeftValue, topRightValue);
0200             else
0201                 topValue = topRightValue;
0202 
0203             return topValue;
0204         }
0205     }
0206 
0207     return topLeftValue;
0208 }
0209 
0210 #else
0211 
0212 uint StackedTile::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) const
0213 {
0214     // Bilinear interpolation to determine the color of a subpixel
0215 
0216     int iX = (int)(x);
0217     int iY = (int)(y);
0218 
0219     qreal fY = y - iY;
0220 
0221     // Interpolation in y-direction
0222     if ( ( iY + 1 ) < m_resultImage.height() ) {
0223 
0224         QRgb bottomLeftValue  =  pixel( iX, iY + 1 );
0225         // blending the color values of the top left and bottom left point
0226         qreal ml_red   = ( 1.0 - fY ) * qRed  ( topLeftValue  ) + fY * qRed  ( bottomLeftValue  );
0227         qreal ml_green = ( 1.0 - fY ) * qGreen( topLeftValue  ) + fY * qGreen( bottomLeftValue  );
0228         qreal ml_blue  = ( 1.0 - fY ) * qBlue ( topLeftValue  ) + fY * qBlue ( bottomLeftValue  );
0229 
0230         // Interpolation in x-direction
0231         if ( iX + 1 < m_resultImage.width() ) {
0232 
0233             qreal fX = x - iX;
0234 
0235             QRgb topRightValue    =  pixel( iX + 1, iY );
0236             QRgb bottomRightValue =  pixel( iX + 1, iY + 1 );
0237 
0238             // blending the color values of the top right and bottom right point
0239             qreal mr_red   = ( 1.0 - fY ) * qRed  ( topRightValue ) + fY * qRed  ( bottomRightValue );
0240             qreal mr_green = ( 1.0 - fY ) * qGreen( topRightValue ) + fY * qGreen( bottomRightValue );
0241             qreal mr_blue  = ( 1.0 - fY ) * qBlue ( topRightValue ) + fY * qBlue ( bottomRightValue );
0242 
0243             // blending the color values of the resulting middle left
0244             // and middle right points
0245             int mm_red   = (int)( ( 1.0 - fX ) * ml_red   + fX * mr_red   );
0246             int mm_green = (int)( ( 1.0 - fX ) * ml_green + fX * mr_green );
0247             int mm_blue  = (int)( ( 1.0 - fX ) * ml_blue  + fX * mr_blue  );
0248 
0249             return qRgb( mm_red, mm_green, mm_blue );
0250         }
0251         else {
0252             return qRgb( ml_red, ml_green, ml_blue );
0253         }
0254     }
0255     else {
0256         // Interpolation in x-direction
0257         if ( iX + 1 < m_resultImage.width() ) {
0258 
0259             qreal fX = x - iX;
0260 
0261             if ( fX == 0.0 )
0262                 return topLeftValue;
0263 
0264             QRgb topRightValue    =  pixel( iX + 1, iY );
0265             // blending the color values of the top left and top right point
0266             int tm_red   = (int)( ( 1.0 - fX ) * qRed  ( topLeftValue ) + fX * qRed  ( topRightValue ) );
0267             int tm_green = (int)( ( 1.0 - fX ) * qGreen( topLeftValue ) + fX * qGreen( topRightValue ) );
0268             int tm_blue  = (int)( ( 1.0 - fX ) * qBlue ( topLeftValue ) + fX * qBlue ( topRightValue ) );
0269 
0270             return qRgb( tm_red, tm_green, tm_blue );
0271         }
0272     }
0273 
0274     return topLeftValue;
0275 }
0276 
0277 #endif
0278 
0279 int StackedTile::calcByteCount( const QImage &resultImage, const QVector<QSharedPointer<TextureTile> > &tiles )
0280 {
0281     int byteCount = resultImage.sizeInBytes();
0282 
0283     QVector<QSharedPointer<TextureTile> >::const_iterator pos = tiles.constBegin();
0284     QVector<QSharedPointer<TextureTile> >::const_iterator const end = tiles.constEnd();
0285     for (; pos != end; ++pos )
0286         byteCount += (*pos)->byteCount();
0287 
0288     return byteCount;
0289 }
0290 
0291 void StackedTile::setUsed( bool used )
0292 {
0293     m_isUsed = used;
0294 }
0295 
0296 bool StackedTile::used() const
0297 {
0298     return m_isUsed;
0299 }
0300 
0301 uint StackedTile::pixelF( qreal x, qreal y ) const
0302 {
0303     int iX = (int)(x);
0304     int iY = (int)(y);
0305 
0306     QRgb topLeftValue  =  pixel( iX, iY );
0307 
0308     return pixelF( x, y, topLeftValue );
0309 }
0310 
0311 int StackedTile::depth() const
0312 {
0313     return m_depth;
0314 }
0315 
0316 int StackedTile::byteCount() const
0317 {
0318     return m_byteCount;
0319 }
0320 
0321 QVector<QSharedPointer<TextureTile> > StackedTile::tiles() const
0322 {
0323     return m_tiles;
0324 }
0325 
0326 QImage const * StackedTile::resultImage() const
0327 {
0328     return &m_resultImage;
0329 }
0330