diff --git a/src/engine/h2d_file.cpp b/src/engine/h2d_file.cpp index fe7cf1198d9..14cdd4c117d 100644 --- a/src/engine/h2d_file.cpp +++ b/src/engine/h2d_file.cpp @@ -191,6 +191,7 @@ namespace fheroes2 const size_t size = static_cast( width * height ); image.resize( width, height ); memcpy( image.image(), data.data() + 4 + 4 + 4 + 4, size ); + // TODO: Store in h2d images the 'isSingleLayer' state to disable and skip transform layer for such images. memcpy( image.transform(), data.data() + 4 + 4 + 4 + 4 + size, size ); image.setPosition( x, y ); diff --git a/src/engine/image_tool.cpp b/src/engine/image_tool.cpp index 671657a8109..00b97106fac 100644 --- a/src/engine/image_tool.cpp +++ b/src/engine/image_tool.cpp @@ -339,6 +339,9 @@ namespace fheroes2 const uint8_t * dataEnd = data + sizeData; + // The need for a transform layer can only be determined during ICN decoding. + bool noTransformLayer = true; + while ( true ) { if ( 0 == *data ) { // 0x00 - end of row imageData += width; @@ -347,6 +350,8 @@ namespace fheroes2 ++data; } else if ( 0x80 > *data ) { // 0x01-0x7F - repeat a pixel N times + noTransformLayer = noTransformLayer && ( static_cast( posX ) >= width ); + const uint8_t pixelCount = *data; ++data; @@ -362,13 +367,19 @@ namespace fheroes2 posX += pixelCount; } else if ( 0x80 == *data ) { // 0x80 - end of image + noTransformLayer = noTransformLayer && ( static_cast( posX ) >= width ); + break; } else if ( 0xC0 > *data ) { // 0xBF - empty (transparent) pixels + noTransformLayer = false; + posX += *data - 0x80; ++data; } else if ( 0xC0 == *data ) { // 0xC0 - transform layer + noTransformLayer = false; + ++data; const uint8_t transformValue = *data; @@ -414,6 +425,10 @@ namespace fheroes2 } } + if ( noTransformLayer ) { + sprite._disableTransformLayer(); + } + return sprite; } diff --git a/src/fheroes2/agg/agg_image.cpp b/src/fheroes2/agg/agg_image.cpp index 62e895cfb48..7589f10bdbe 100644 --- a/src/fheroes2/agg/agg_image.cpp +++ b/src/fheroes2/agg/agg_image.cpp @@ -217,15 +217,6 @@ namespace } } - void replaceTransformPixel( fheroes2::Image & image, const int32_t position, const uint8_t value ) - { - assert( !image.singleLayer() ); - if ( ( position < ( image.width() * image.height() ) ) && ( image.transform()[position] != 0 ) ) { - image.transform()[position] = 0; - image.image()[position] = value; - } - } - void fillRandomPixelsFromImage( const fheroes2::Image & original, const fheroes2::Rect & originalRoi, fheroes2::Image & output, const fheroes2::Rect & outputRoi, std::mt19937 & seededGen ) { @@ -2516,9 +2507,9 @@ namespace fheroes2 LoadOriginalICN( id ); if ( _icnVsSprite[id].size() == 1 ) { Sprite & out = _icnVsSprite[id][0]; - // The pixel pixel of the original sprite has a skip value - if ( !out.empty() && out.transform()[0] == 1 ) { - out.transform()[0] = 0; + // The first pixel of the original sprite has incorrect color. + if ( !out.empty() ) { + out._disableTransformLayer(); out.image()[0] = 10; } } @@ -2743,12 +2734,12 @@ namespace fheroes2 LoadOriginalICN( id ); if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; + // This is the main menu image which shouldn't have any transform layer. + original._disableTransformLayer(); if ( original.width() == 640 && original.height() == 480 ) { // Fix incorrect pixel at position 260x305. - replaceTransformPixel( original, 195460, 31 ); + original.image()[195460] = 31; } - // This is the main menu image which shouldn't have any transform layer. - original._disableTransformLayer(); } return true; case ICN::TOWNBKG3: @@ -2757,11 +2748,13 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 640 && original.height() == 256 ) { - replaceTransformPixel( original, 51945, 17 ); - replaceTransformPixel( original, 61828, 25 ); - replaceTransformPixel( original, 64918, 164 ); - replaceTransformPixel( original, 77685, 18 ); - replaceTransformPixel( original, 84618, 19 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[51945] = 17; + imageData[61828] = 25; + imageData[64918] = 164; + imageData[77685] = 18; + imageData[84618] = 19; } } return true; @@ -2771,34 +2764,41 @@ namespace fheroes2 if ( _icnVsSprite[id].size() > 60 ) { Sprite & original = _icnVsSprite[id][60]; if ( original.width() == 30 && original.height() == 22 ) { - replaceTransformPixel( original, 5, 75 ); - replaceTransformPixel( original, 310, 48 ); - replaceTransformPixel( original, 358, 64 ); - replaceTransformPixel( original, 424, 65 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[5] = 75; + imageData[310] = 48; + imageData[358] = 64; + imageData[424] = 65; } } if ( _icnVsSprite[id].size() > 61 ) { Sprite & original = _icnVsSprite[id][61]; if ( original.width() == 30 && original.height() == 22 ) { - replaceTransformPixel( original, 51, 30 ); - replaceTransformPixel( original, 80, 28 ); - replaceTransformPixel( original, 81, 30 ); - replaceTransformPixel( original, 383, 24 ); - replaceTransformPixel( original, 445, 24 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[51] = 30; + imageData[80] = 28; + imageData[81] = 30; + imageData[383] = 24; + imageData[445] = 24; } } if ( _icnVsSprite[id].size() > 65 ) { Sprite & original = _icnVsSprite[id][65]; if ( original.width() == 30 && original.height() == 22 ) { - replaceTransformPixel( original, 499, 60 ); - replaceTransformPixel( original, 601, 24 ); - replaceTransformPixel( original, 631, 28 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[499] = 60; + imageData[601] = 24; + imageData[631] = 28; } } if ( _icnVsSprite[id].size() > 67 ) { Sprite & original = _icnVsSprite[id][67]; if ( original.width() == 30 && original.height() == 22 ) { - replaceTransformPixel( original, 42, 28 ); + original._disableTransformLayer(); + original.image()[42] = 28; } } return true; @@ -2808,7 +2808,8 @@ namespace fheroes2 if ( _icnVsSprite[id].size() > 1 ) { Sprite & original = _icnVsSprite[id][1]; if ( original.width() == 30 && original.height() == 22 ) { - replaceTransformPixel( original, 82, 244 ); + original._disableTransformLayer(); + original.image()[82] = 244; } } return true; @@ -2818,7 +2819,8 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 101 && original.height() == 93 ) { - replaceTransformPixel( original, 9084, 77 ); + original._disableTransformLayer(); + original.image()[9084] = 77; } } return true; @@ -2828,10 +2830,12 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 101 && original.height() == 93 ) { - replaceTransformPixel( original, 2314, 70 ); - replaceTransformPixel( original, 5160, 71 ); - replaceTransformPixel( original, 5827, 18 ); - replaceTransformPixel( original, 7474, 167 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2314] = 70; + imageData[5160] = 71; + imageData[5827] = 18; + imageData[7474] = 167; } } return true; @@ -2841,8 +2845,10 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 101 && original.height() == 93 ) { - replaceTransformPixel( original, 2028, 42 ); - replaceTransformPixel( original, 6674, 100 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2028] = 42; + imageData[6674] = 100; } } return true; @@ -2852,7 +2858,8 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 101 && original.height() == 93 ) { - replaceTransformPixel( original, 2230, 212 ); + original._disableTransformLayer(); + original.image()[2230] = 212; } } return true; @@ -2862,23 +2869,21 @@ namespace fheroes2 // Statue image has bad pixels. Sprite & original = _icnVsSprite[id][7]; if ( original.width() == 135 && original.height() == 57 ) { - replaceTransformPixel( original, 3687, 50 ); - replaceTransformPixel( original, 5159, 108 ); - replaceTransformPixel( original, 5294, 108 ); - } - } - if ( _icnVsSprite[id].size() >= 24 ) { - // Mage tower image has a bad pixel. - Sprite & original = _icnVsSprite[id][23]; - if ( original.width() == 135 && original.height() == 57 ) { - replaceTransformPixel( original, 4333, 23 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[3687] = 50; + imageData[5159] = 108; + imageData[5294] = 108; } } - if ( _icnVsSprite[id].size() >= 29 ) { + if ( _icnVsSprite[id].size() > 28 ) { // Mage tower image has a bad pixel. - Sprite & original = _icnVsSprite[id][28]; - if ( original.width() == 135 && original.height() == 57 ) { - replaceTransformPixel( original, 4333, 23 ); + for ( const uint32_t index : { 23, 28 } ) { + Sprite & original = _icnVsSprite[id][index]; + if ( original.width() == 135 && original.height() == 57 ) { + original._disableTransformLayer(); + original.image()[4333] = 23; + } } } return true; @@ -2888,7 +2893,8 @@ namespace fheroes2 if ( _icnVsSprite[id].size() >= 2 ) { Sprite & original = _icnVsSprite[id][1]; if ( original.width() == 84 && original.height() == 81 ) { - replaceTransformPixel( original, 4934, 18 ); + original._disableTransformLayer(); + original.image()[4934] = 18; } } return true; @@ -2898,10 +2904,12 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 84 && original.height() == 81 ) { - replaceTransformPixel( original, 1692, 26 ); - replaceTransformPixel( original, 2363, 32 ); - replaceTransformPixel( original, 2606, 21 ); - replaceTransformPixel( original, 2608, 21 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[1692] = 26; + imageData[2363] = 32; + imageData[2606] = 21; + imageData[2608] = 21; } } return true; @@ -2911,31 +2919,35 @@ namespace fheroes2 // Rainbow has bad pixels. Sprite & original = _icnVsSprite[id][13]; if ( original.width() == 135 && original.height() == 57 ) { - replaceTransformPixel( original, 2047, 160 ); - replaceTransformPixel( original, 2052, 159 ); - replaceTransformPixel( original, 2055, 160 ); - replaceTransformPixel( original, 2060, 67 ); - replaceTransformPixel( original, 2063, 159 ); - replaceTransformPixel( original, 2067, 67 ); - replaceTransformPixel( original, 2184, 67 ); - replaceTransformPixel( original, 2192, 158 ); - replaceTransformPixel( original, 3508, 67 ); - replaceTransformPixel( original, 3641, 67 ); - replaceTransformPixel( original, 3773, 69 ); - replaceTransformPixel( original, 3910, 67 ); - replaceTransformPixel( original, 4039, 69 ); - replaceTransformPixel( original, 4041, 67 ); - replaceTransformPixel( original, 4172, 67 ); - replaceTransformPixel( original, 4578, 69 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2047] = 160; + imageData[2052] = 159; + imageData[2055] = 160; + imageData[2060] = 67; + imageData[2063] = 159; + imageData[2067] = 67; + imageData[2184] = 67; + imageData[2192] = 158; + imageData[3508] = 67; + imageData[3641] = 67; + imageData[3773] = 69; + imageData[3910] = 67; + imageData[4039] = 69; + imageData[4041] = 67; + imageData[4172] = 67; + imageData[4578] = 69; } } if ( _icnVsSprite[id].size() >= 25 ) { // Red tower has bad pixels. Sprite & original = _icnVsSprite[id][24]; if ( original.width() == 135 && original.height() == 57 ) { - replaceTransformPixel( original, 2830, 165 ); - replaceTransformPixel( original, 3101, 165 ); - replaceTransformPixel( original, 3221, 69 ); + original._disableTransformLayer(); + uint8_t * imageData = original.image(); + imageData[2830] = 165; + imageData[3101] = 165; + imageData[3221] = 69; } } return true; @@ -3072,6 +3084,7 @@ namespace fheroes2 // fix missing black border on the right side of the "up" button Sprite & out = _icnVsSprite[id][4]; if ( out.width() == 16 && out.height() == 16 ) { + out._disableTransformLayer(); Copy( out, 0, 0, out, 15, 0, 1, 16 ); } } @@ -3080,6 +3093,7 @@ namespace fheroes2 // TODO: add a new icon for the Resurrection add-on map type. _icnVsSprite[id].resize( 2 ); for ( Sprite & icon : _icnVsSprite[id] ) { + icon._disableTransformLayer(); icon.resize( 17, 17 ); icon.fill( 0 ); } @@ -3337,6 +3351,7 @@ namespace fheroes2 Sprite & originalImage = _icnVsSprite[id][index]; Sprite temp( originalImage.width(), originalImage.height() ); temp.setPosition( originalImage.x(), originalImage.y() ); + temp._disableTransformLayer(); temp.fill( 0 ); Blit( originalImage, temp ); originalImage = std::move( temp ); @@ -3896,7 +3911,8 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 50 && original.height() == 47 ) { - replaceTransformPixel( original, 280, 117 ); + original._disableTransformLayer(); + original.image()[280] = 117; } } return true; @@ -3907,16 +3923,27 @@ namespace fheroes2 if ( !_icnVsSprite[id].empty() ) { Sprite & original = _icnVsSprite[id][0]; if ( original.width() == 640 && original.height() == 443 ) { - replaceTransformPixel( original, 23165, 24 ); + original._disableTransformLayer(); + original.image()[23165] = 24; } } return true; } + case ICN::SWAPWIN: + case ICN::WELLBKG: { + // Hero Meeting dialog and Castle Well images can be used with disabled transform layer. + LoadOriginalICN( id ); + if ( !_icnVsSprite[id].empty() ) { + _icnVsSprite[id][0]._disableTransformLayer(); + } + return true; + } case ICN::GAME_OPTION_ICON: { _icnVsSprite[id].resize( 2 ); h2d::readImage( "hotkeys_icon.image", _icnVsSprite[id][0] ); h2d::readImage( "graphics_icon.image", _icnVsSprite[id][1] ); + break; } default: @@ -4004,6 +4031,10 @@ namespace fheroes2 Sprite & resizedIcn = _icnVsScaledSprite[icnId][index]; + if ( originalIcn.singleLayer() && !resizedIcn.singleLayer() ) { + resizedIcn._disableTransformLayer(); + } + const double scaleFactorX = static_cast( display.width() ) / Display::DEFAULT_WIDTH; const double scaleFactorY = static_cast( display.height() ) / Display::DEFAULT_HEIGHT; diff --git a/src/fheroes2/battle/battle_interface.cpp b/src/fheroes2/battle/battle_interface.cpp index 83895307fd4..59518b95161 100644 --- a/src/fheroes2/battle/battle_interface.cpp +++ b/src/fheroes2/battle/battle_interface.cpp @@ -3056,6 +3056,7 @@ void Battle::Interface::FadeArena( const bool clearMessageLog ) const fheroes2::Rect srt = border.GetArea(); fheroes2::Image top( srt.width, srt.height ); + top._disableTransformLayer(); fheroes2::Copy( display, srt.x, srt.y, top, 0, 0, srt.width, srt.height ); fheroes2::FadeDisplayWithPalette( top, srt.getPosition(), 5, 300, 5 ); @@ -5424,6 +5425,7 @@ void Battle::Interface::RedrawActionDeathWaveSpell( const int32_t strength ) } fheroes2::Image battleFieldCopy( area.width, area.height ); + battleFieldCopy._disableTransformLayer(); fheroes2::Copy( _mainSurface, 0, 0, battleFieldCopy, 0, 0, area.width, area.height ); // The death wave horizontal length in pixels. @@ -5449,7 +5451,7 @@ void Battle::Interface::RedrawActionDeathWaveSpell( const int32_t strength ) // Prepare the blank image for the Death Wave spell effect with the transform layer equal to "0" fheroes2::Image spellEffect( waveLength, area.height ); - std::fill( spellEffect.transform(), spellEffect.transform() + static_cast( waveLength * area.height ), static_cast( 0 ) ); + spellEffect._disableTransformLayer(); AudioManager::PlaySound( M82::MNRDEATH ); @@ -5553,6 +5555,7 @@ void Battle::Interface::RedrawActionHolyShoutSpell( const uint8_t strength ) } fheroes2::Image battleFieldCopy( area.width, area.height ); + battleFieldCopy._disableTransformLayer(); fheroes2::Copy( _mainSurface, 0, 0, battleFieldCopy, 0, 0, area.width, area.height ); _currentUnit = nullptr; @@ -5689,6 +5692,8 @@ void Battle::Interface::RedrawActionArmageddonSpell() fheroes2::Image spriteWhitening( area.width, area.height ); fheroes2::Image spriteReddish( area.width, area.height ); + spriteWhitening._disableTransformLayer(); + spriteReddish._disableTransformLayer(); fheroes2::Copy( _mainSurface, area.x, area.y, spriteWhitening, 0, 0, area.width, area.height ); fheroes2::Copy( _mainSurface, area.x, area.y, spriteReddish, 0, 0, area.width, area.height ); @@ -5758,6 +5763,7 @@ void Battle::Interface::RedrawActionEarthQuakeSpell( const std::vector & ta cursor.SetThemes( Cursor::WAR_POINTER ); fheroes2::Image sprite( area.width, area.height ); + sprite._disableTransformLayer(); fheroes2::Copy( _mainSurface, area.x, area.y, sprite, 0, 0, area.width, area.height ); _currentUnit = nullptr; diff --git a/src/fheroes2/gui/interface_gamearea.cpp b/src/fheroes2/gui/interface_gamearea.cpp index 577f5716176..14a3162f1df 100644 --- a/src/fheroes2/gui/interface_gamearea.cpp +++ b/src/fheroes2/gui/interface_gamearea.cpp @@ -820,7 +820,7 @@ fheroes2::Image Interface::GameArea::GenerateUltimateArtifactAreaSurface( const } fheroes2::Image result( 448, 448 ); - result.reset(); + result._disableTransformLayer(); // Make a temporary copy GameArea gamearea = AdventureMap::Get().getGameArea(); diff --git a/src/fheroes2/gui/ui_tool.cpp b/src/fheroes2/gui/ui_tool.cpp index 8aeeaeaa049..69fb4e38fa9 100644 --- a/src/fheroes2/gui/ui_tool.cpp +++ b/src/fheroes2/gui/ui_tool.cpp @@ -412,10 +412,9 @@ namespace fheroes2 const int32_t inHeight = in.height(); const int32_t outWaveWidth = x > waveLength ? ( x > inWidth ? ( waveLength - x + inWidth ) : waveLength ) : x; - // If the out image is small for the Death Wave spell effect, resize it and fill the transform layer with "0". + // If the out image is small for the Death Wave spell effect, resize it. if ( out.width() < outWaveWidth || out.height() < inHeight ) { out.resize( outWaveWidth, inHeight ); - std::fill( out.transform(), out.transform() + static_cast( outWaveWidth * inHeight ), static_cast( 0 ) ); } const int32_t outWidth = out.width(); @@ -475,7 +474,7 @@ namespace fheroes2 const double greenBlueCoeff = 4.0 - darkredStrength / 80.0; Image out( width, height ); - std::fill( out.transform(), out.transform() + static_cast( width * height ), static_cast( 0 ) ); + out._disableTransformLayer(); uint8_t * imageOutX = out.image(); const uint8_t * imageIn = in.image();