File indexing completed on 2024-04-14 03:48:04

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