Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not allocate memory to transform layer if image is already set as single-layer #8241

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bfe72a2
Don't interact with the transform layer if the image is a single layer.
Districh-ru Dec 17, 2023
dc56af5
Add assertion to get transform layer only for double-layer images, fi…
Districh-ru Dec 24, 2023
c7d6c6e
Fix fheroes2::Resize() for the case it in and out images have differe…
Districh-ru Dec 24, 2023
12fb09f
Merge branch 'ihhub:master' into single-layer-do-not-access-transform
Districh-ru Dec 24, 2023
ca8874d
Add assertions to more places where transform layer is accessed
Districh-ru Dec 24, 2023
309266e
Do not allocate memory for transform() layer when image is single-layer
Districh-ru Dec 24, 2023
03c726e
Merge branch 'master' into single-layer-no-transform-memory-allocate
Districh-ru Dec 28, 2023
c05b8c6
Merge branch 'ihhub:master' into single-layer-do-not-access-transform
Districh-ru Jan 4, 2024
8782450
Apply IWYU suggestion
Districh-ru Jan 4, 2024
8377456
Revert "Apply IWYU suggestion"
Districh-ru Jan 4, 2024
67edb14
Update copyrights ... can it be automated? :)
Districh-ru Jan 4, 2024
87733e7
Merge branch 'single-layer-do-not-access-transform' into single-layer…
Districh-ru Jan 4, 2024
6a83307
Merge branch 'master' into single-layer-no-transform-memory-allocate
Districh-ru Jan 7, 2024
dbb5fb8
Update copyright years where needed
Districh-ru Jan 7, 2024
6e7fe0c
Merge branch 'ihhub:master' into single-layer-no-transform-memory-all…
Districh-ru Jan 14, 2024
12969d6
Merge branch 'ihhub:master'
Districh-ru Jan 20, 2024
400b5cf
Fix format
Districh-ru Jan 20, 2024
46268db
Fix Clang-Tidy issue
Districh-ru Jan 20, 2024
4981b22
Revert changes in game_credits.cpp
Districh-ru Jan 20, 2024
3d97053
Merge branch 'ihhub:master' into single-layer-no-transform-memory-all…
Districh-ru Jan 21, 2024
613acd9
Merge branch 'ihhub:master' into single-layer-no-transform-memory-all…
Districh-ru Apr 14, 2024
491e19a
Merge branch 'ihhub:master' into single-layer-no-transform-memory-all…
Districh-ru May 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/engine/h2d_file.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2021 - 2023 *
* Copyright (C) 2021 - 2024 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -173,6 +173,9 @@ namespace fheroes2

bool readImageFromH2D( H2DReader & reader, const std::string & name, Sprite & image )
{
// TODO: Store in h2d images the 'isSingleLayer' state to disable and skip transform layer for such images.
assert( !image.singleLayer() );

const std::vector<uint8_t> & data = reader.getFile( name );
if ( data.size() < 4 + 4 + 4 + 4 + 1 ) {
// Empty or invalid image.
Expand All @@ -191,7 +194,6 @@ namespace fheroes2
const size_t size = static_cast<size_t>( 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 );
Expand All @@ -201,7 +203,8 @@ namespace fheroes2

bool writeImageToH2D( H2DWriter & writer, const std::string & name, const Sprite & image )
{
assert( !image.empty() );
// TODO: Store in h2d images the 'isSingleLayer' state to disable and skip transform layer for such images.
assert( !image.empty() && !image.singleLayer() );

StreamBuf stream;
stream.putLE32( static_cast<uint32_t>( image.width() ) );
Expand Down
140 changes: 104 additions & 36 deletions src/engine/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include "image.h"

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdlib>
Expand Down Expand Up @@ -503,7 +502,11 @@ namespace fheroes2
if ( !empty() ) {
const size_t totalSize = static_cast<size_t>( _width ) * _height;
memset( image(), value, totalSize );
memset( transform(), static_cast<uint8_t>( 0 ), totalSize );

if ( !_singleLayer ) {
// For double-layer image: set the transform layer not to skip all data.
memset( transform(), static_cast<uint8_t>( 0 ), totalSize );
}
}
}

Expand All @@ -519,9 +522,10 @@ namespace fheroes2
return;
}

const size_t size = static_cast<size_t>( width_ ) * height_;
// Allocate memory only for the used layers.
const size_t size = static_cast<size_t>( width_ ) * height_ * ( _singleLayer ? 1 : 2 );

_data.reset( new uint8_t[size * 2] );
_data.reset( new uint8_t[size] );

_width = width_;
_height = height_;
Expand All @@ -532,8 +536,11 @@ namespace fheroes2
if ( !empty() ) {
const size_t totalSize = static_cast<size_t>( _width ) * _height;
memset( image(), static_cast<uint8_t>( 0 ), totalSize );
// Set the transform layer to skip all data.
memset( transform(), static_cast<uint8_t>( 1 ), totalSize );

if ( !_singleLayer ) {
// Set the transform layer to skip all data.
memset( transform(), static_cast<uint8_t>( 1 ), totalSize );
}
}
}

Expand All @@ -545,18 +552,19 @@ namespace fheroes2
return;
}

const size_t size = static_cast<size_t>( image._width ) * image._height;
// Allocate memory and copy data only for the used layers.
const size_t size = static_cast<size_t>( image._width ) * image._height * ( _singleLayer ? 1 : 2 );

_singleLayer = image._singleLayer;

if ( image._width != _width || image._height != _height ) {
_data.reset( new uint8_t[size * 2] );
_data.reset( new uint8_t[size] );

_width = image._width;
_height = image._height;
}

_singleLayer = image._singleLayer;

memcpy( _data.get(), image._data.get(), size * 2 );
memcpy( _data.get(), image._data.get(), size );
}

Sprite::Sprite( const int32_t width_, const int32_t height_, const int32_t x_ /* = 0 */, const int32_t y_ /* = 0 */ )
Expand Down Expand Up @@ -2722,7 +2730,18 @@ namespace fheroes2

const uint8_t * gamePalette = getGamePalette();

if ( in.singleLayer() && out.singleLayer() ) {
if ( in.singleLayer() ) {
if ( !out.singleLayer() ) {
// In this case we make the output image fully non-transparent in the given output area.

uint8_t * transformY = out.transform() + outY * widthOut + outX;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
const uint8_t * transformYEnd = transformY + heightRoiOut * widthOut;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved

for ( ; transformY != transformYEnd; transformY += widthOut ) {
memset( transformY, static_cast<uint8_t>( 0 ), widthRoiOut );
}
}

for ( int32_t y = 0; y < heightRoiOut; ++y, imageOutY += widthOut ) {
const double posY = static_cast<double>( y * heightRoiIn ) / heightRoiOut;
const int32_t startY = static_cast<int32_t>( posY );
Expand Down Expand Up @@ -2763,52 +2782,63 @@ namespace fheroes2
}
else {
const uint8_t * transformInY = in.transform() + offsetInY;
uint8_t * transformOutY = out.transform() + offsetOutY;
const bool isOutNotSingleLayer = !out.singleLayer();
uint8_t * transformOutY = isOutNotSingleLayer ? ( out.transform() + offsetOutY ) : nullptr;

for ( int32_t y = 0; y < heightRoiOut; ++y, imageOutY += widthOut, transformOutY += widthOut ) {
for ( int32_t y = 0; y < heightRoiOut; ++y, imageOutY += widthOut ) {
const double posY = static_cast<double>( y * heightRoiIn ) / heightRoiOut;
const int32_t startY = static_cast<int32_t>( posY );
const double coeffY = posY - startY;

uint8_t * imageOutX = imageOutY;
uint8_t * transformOutX = transformOutY;

for ( int32_t x = 0; x < widthRoiOut; ++x, ++imageOutX, ++transformOutX ) {
for ( int32_t x = 0; x < widthRoiOut; ++x, ++imageOutX ) {
const double posX = positionX[x];
const int32_t startX = static_cast<int32_t>( posX );
const int32_t offsetIn = startY * widthIn + startX;

const uint8_t * imageInX = imageInY + offsetIn;
const uint8_t * transformInX = transformInY + offsetIn;

if ( posX < widthIn - 1 && posY < heightRoiIn - 1 ) {
if ( *transformInX == 0 && *( transformInX + 1 ) == 0 && *( transformInX + widthRoiIn ) == 0 && *( transformInX + widthRoiIn + 1 ) == 0 ) {
const double coeffX = posX - startX;
const double coeff1 = ( 1 - coeffX ) * ( 1 - coeffY );
const double coeff2 = coeffX * ( 1 - coeffY );
const double coeff3 = ( 1 - coeffX ) * coeffY;
const double coeff4 = coeffX * coeffY;
if ( posX < widthIn - 1 && posY < heightRoiIn - 1 && *transformInX == 0 && *( transformInX + 1 ) == 0 && *( transformInX + widthRoiIn ) == 0
&& *( transformInX + widthRoiIn + 1 ) == 0 ) {
const double coeffX = posX - startX;
const double coeff1 = ( 1 - coeffX ) * ( 1 - coeffY );
const double coeff2 = coeffX * ( 1 - coeffY );
const double coeff3 = ( 1 - coeffX ) * coeffY;
const double coeff4 = coeffX * coeffY;

const uint8_t * id1 = gamePalette + static_cast<uint32_t>( *imageInX ) * 3;
const uint8_t * id2 = gamePalette + static_cast<uint32_t>( *( imageInX + 1 ) ) * 3;
const uint8_t * id3 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn ) ) * 3;
const uint8_t * id4 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn + 1 ) ) * 3;
const uint8_t * id1 = gamePalette + static_cast<uint32_t>( *imageInX ) * 3;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
const uint8_t * id2 = gamePalette + static_cast<uint32_t>( *( imageInX + 1 ) ) * 3;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
const uint8_t * id3 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn ) ) * 3;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
const uint8_t * id4 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn + 1 ) ) * 3;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved

const double red = *id1 * coeff1 + *id2 * coeff2 + *id3 * coeff3 + *id4 * coeff4 + 0.5;
const double green = *( id1 + 1 ) * coeff1 + *( id2 + 1 ) * coeff2 + *( id3 + 1 ) * coeff3 + *( id4 + 1 ) * coeff4 + 0.5;
const double blue = *( id1 + 2 ) * coeff1 + *( id2 + 2 ) * coeff2 + *( id3 + 2 ) * coeff3 + *( id4 + 2 ) * coeff4 + 0.5;
const double red = *id1 * coeff1 + *id2 * coeff2 + *id3 * coeff3 + *id4 * coeff4 + 0.5;
const double green = *( id1 + 1 ) * coeff1 + *( id2 + 1 ) * coeff2 + *( id3 + 1 ) * coeff3 + *( id4 + 1 ) * coeff4 + 0.5;
const double blue = *( id1 + 2 ) * coeff1 + *( id2 + 2 ) * coeff2 + *( id3 + 2 ) * coeff3 + *( id4 + 2 ) * coeff4 + 0.5;

*imageOutX = GetPALColorId( static_cast<uint8_t>( red ), static_cast<uint8_t>( green ), static_cast<uint8_t>( blue ) );
}
else {
*imageOutX = GetPALColorId( static_cast<uint8_t>( red ), static_cast<uint8_t>( green ), static_cast<uint8_t>( blue ) );
}
else {
if ( isOutNotSingleLayer || *transformInX == 0 ) {
// Output image is double-layer or single-layer with non-transparent current pixel.
*imageOutX = *imageInX;
}
else if ( *transformInX != 1 ) {
// Apply a transformation.
*imageOutX = *( transformTable + ( *transformInX ) * 256 + *imageOutX );
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
}
}
else {
*imageOutX = *imageInX;

if ( isOutNotSingleLayer ) {
*transformOutX = *transformInX;
++transformOutX;
}
}

*transformOutX = *transformInX;
if ( !isOutNotSingleLayer ) {
transformOutY += widthOut;
}
}
}
Expand All @@ -2823,7 +2853,18 @@ namespace fheroes2
positionX[x] = ( x * widthRoiIn ) / widthRoiOut;
}

if ( in.singleLayer() && out.singleLayer() ) {
if ( in.singleLayer() ) {
if ( !out.singleLayer() ) {
// In this case we make the output image fully non-transparent in the given output area.

uint8_t * transformY = out.transform() + outY * widthOut + outX;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
const uint8_t * transformYEnd = transformY + heightRoiOut * widthOut;
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved

for ( ; transformY != transformYEnd; transformY += widthOut ) {
memset( transformY, static_cast<uint8_t>( 0 ), widthRoiOut );
}
}

for ( ; imageOutY != imageOutYEnd; imageOutY += widthOut, ++idY ) {
uint8_t * imageOutX = imageOutY;

Expand All @@ -2836,7 +2877,34 @@ namespace fheroes2
}
}
}
else if ( out.singleLayer() ) {
const uint8_t * transformInY = in.transform() + offsetInY;

for ( ; imageOutY != imageOutYEnd; imageOutY += widthOut, ++idY ) {
uint8_t * imageOutX = imageOutY;

const int32_t offset = ( ( idY * heightRoiIn ) / heightRoiOut ) * widthIn;
const uint8_t * imageInX = imageInY + offset;
const uint8_t * transformInX = transformInY + offset;

for ( const int32_t posX : positionX ) {
const uint8_t * transformIn = transformInX + posX;
if ( *transformIn > 0 ) {
if ( *transformIn != 1 ) {
// Apply a transformation.
*imageOutX = *( transformTable + ( *transformIn ) * 256 + *imageOutX );
Districh-ru marked this conversation as resolved.
Show resolved Hide resolved
}
}
else {
*imageOutX = *( imageInX + posX );
}

++imageOutX;
}
}
}
else {
// Both 'in' and 'out' are double-layer.
const uint8_t * transformInY = in.transform() + offsetInY;
uint8_t * transformOutY = out.transform() + offsetOutY;

Expand Down
24 changes: 17 additions & 7 deletions src/engine/image.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2020 - 2023 *
* Copyright (C) 2020 - 2024 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand All @@ -19,6 +19,7 @@
***************************************************************************/
#pragma once

#include <cassert>
#include <cstdint>
#include <memory>
#include <utility>
Expand All @@ -28,7 +29,7 @@

namespace fheroes2
{
// Image contains image layer and transform layer.
// Image always contains an image layer and if image is not a single-layer then also a transform layer.
// - image layer contains visible pixels which are copy to a destination image
// - transform layer is used to apply some transformation to an image on which we draw the current one. For example, shadowing
class Image
Expand Down Expand Up @@ -63,11 +64,17 @@ namespace fheroes2

uint8_t * transform()
{
// Why do you want to get transform layer from the single-layer image?
assert( !_singleLayer );

return _data.get() + width() * height();
}

const uint8_t * transform() const
{
// Why do you want to get transform layer from the single-layer image?
assert( !_singleLayer );

return _data.get() + width() * height();
}

Expand All @@ -76,14 +83,17 @@ namespace fheroes2
return !_data;
}

void reset(); // makes image fully transparent (transform layer is set to 1)
void clear(); // makes the image empty
// Set all data in the image layer to 0 and make double-layer images fully transparent (transform layer is set to 1).
void reset();

// Make the image empty.
void clear();

// Fill 'image' layer with given value, setting 'transform' layer to 0.
// Fill 'image' layer with given value, setting the double-layer images 'transform' layer to 0.
void fill( const uint8_t value );

// This is an optional indicator for image processing functions.
// The whole image still consists of 2 layers but transform layer might be ignored in computations.
// This is an indicator for image processing functions.
// The single-layer image can not contain transform layer so this layer is never accessed for such images.
bool singleLayer() const
{
return _singleLayer;
Expand Down
8 changes: 7 additions & 1 deletion src/engine/image_tool.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2020 - 2023 *
* Copyright (C) 2020 - 2024 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -155,6 +155,12 @@ namespace fheroes2

bool Load( const std::string & path, Image & image )
{
if ( image.singleLayer() ) {
// Output image should be double-layer!
assert( 0 );
return false;
}

std::unique_ptr<SDL_Surface, std::function<void( SDL_Surface * )>> surface( nullptr, SDL_FreeSurface );
std::unique_ptr<SDL_Surface, std::function<void( SDL_Surface * )>> loadedSurface( nullptr, SDL_FreeSurface );

Expand Down
10 changes: 5 additions & 5 deletions src/engine/localevent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1238,14 +1238,14 @@ void LocalEvent::HandleRenderDeviceResetEvent()
{
// All textures has to be recreated. The only way to do it is to reset everything and render it back.
fheroes2::Display & display = fheroes2::Display::instance();
fheroes2::Image temp( display.width(), display.height() );
if ( display.singleLayer() ) {
temp._disableTransformLayer();
}
fheroes2::Image temp;

assert( display.singleLayer() );

temp._disableTransformLayer();

fheroes2::Copy( display, temp );
display.release();
display.resize( temp.width(), temp.height() );
fheroes2::Copy( temp, display );
}

Expand Down
Loading