From d0f57fed6fef10bb9e40d3de0f0027c4ad96a626 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 10 Feb 2023 15:29:30 +0200 Subject: [PATCH] Cleaned up code of Minimap --- client/adventureMap/CMinimap.cpp | 202 ++++++++-------------------- client/adventureMap/CMinimap.h | 37 +++-- client/render/Canvas.cpp | 36 ++++- client/render/Canvas.h | 20 ++- client/renderSDL/SDL_Extensions.cpp | 110 +++++++++------ client/renderSDL/SDL_Extensions.h | 5 +- client/windows/CQuestLog.cpp | 8 +- 7 files changed, 196 insertions(+), 222 deletions(-) diff --git a/client/adventureMap/CMinimap.cpp b/client/adventureMap/CMinimap.cpp index 15e78a689..219702235 100644 --- a/client/adventureMap/CMinimap.cpp +++ b/client/adventureMap/CMinimap.cpp @@ -19,6 +19,7 @@ #include "../gui/CGuiHandler.h" #include "../render/Colors.h" #include "../renderSDL/SDL_PixelAccess.h" +#include "../windows/InfoWindows.h" #include "../../CCallback.h" #include "../../lib/CGeneralTextHandler.h" @@ -26,159 +27,65 @@ #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapping/CMapDefines.h" -#include - -const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos) +ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const { const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false); // if tile is not visible it will be black on minimap if(!tile) - return Colors::BLACK; + return CSDL_Ext::fromSDL(Colors::BLACK); // if object at tile is owned - it will be colored as its owner for (const CGObjectInstance *obj : tile->blockingObjects) { - //heroes will be blitted later - switch (obj->ID) - { - case Obj::HERO: - case Obj::PRISON: - continue; - } - PlayerColor player = obj->getOwner(); if(player == PlayerColor::NEUTRAL) - return *graphics->neutralColor; - else + return CSDL_Ext::fromSDL(*graphics->neutralColor); + if (player < PlayerColor::PLAYER_LIMIT) - return graphics->playerColors[player.getNum()]; + return CSDL_Ext::fromSDL(graphics->playerColors[player.getNum()]); } - // else - use terrain color (blocked version or normal) - const auto & colorPair = parent->colors.find(tile->terType->getId())->second; if (tile->blocked && (!tile->visitable)) - return colorPair.second; + return tile->terType->minimapBlocked; else - return colorPair.first; -} -void CMinimapInstance::tileToPixels (const int3 &tile, int &x, int &y, int toX, int toY) -{ - int3 mapSizes = LOCPLINT->cb->getMapSize(); - - double stepX = double(pos.w) / mapSizes.x; - double stepY = double(pos.h) / mapSizes.y; - - x = static_cast(toX + stepX * tile.x); - y = static_cast(toY + stepY * tile.y); -} - -void CMinimapInstance::blitTileWithColor(const SDL_Color &color, const int3 &tile, SDL_Surface *to, int toX, int toY) -{ - //coordinates of rectangle on minimap representing this tile - // begin - first to blit, end - first NOT to blit - int xBegin, yBegin, xEnd, yEnd; - tileToPixels (tile, xBegin, yBegin, toX, toY); - tileToPixels (int3 (tile.x + 1, tile.y + 1, tile.z), xEnd, yEnd, toX, toY); - - for (int y=yBegin; ypixels + y * to->pitch + xBegin * minimap->format->BytesPerPixel; - - for (int x=xBegin; x::PutColor(ptr, color); - } + return tile->terType->minimapUnblocked; } void CMinimapInstance::refreshTile(const int3 &tile) { - blitTileWithColor(getTileColor(int3(tile.x, tile.y, level)), tile, minimap, 0, 0); + if (level == tile.z) + minimap->drawPoint(Point(tile.x, tile.y), getTileColor(tile)); } -void CMinimapInstance::drawScaled(int level) +void CMinimapInstance::redrawMinimap() { int3 mapSizes = LOCPLINT->cb->getMapSize(); - //size of one map tile on our minimap - double stepX = double(pos.w) / mapSizes.x; - double stepY = double(pos.h) / mapSizes.y; - - double currY = 0; - for (int y=0; y(currX); - int yBegin = static_cast(currY); - int xEnd = static_cast(currX + stepX); - int yEnd = static_cast(currY + stepY); - - for (int y=yBegin; ypixels + y * minimap->pitch + xBegin * minimap->format->BytesPerPixel; - - for (int x=xBegin; x::PutColor(ptr, color); - } - } - } + for (int y = 0; y < mapSizes.y; ++y) + for (int x = 0; x < mapSizes.x; ++x) + minimap->drawPoint(Point(x, y), getTileColor(int3(x, y, level))); } CMinimapInstance::CMinimapInstance(CMinimap *Parent, int Level): parent(Parent), - minimap(CSDL_Ext::createSurfaceWithBpp<4>(parent->pos.w, parent->pos.h)), + minimap(new Canvas(Point(LOCPLINT->cb->getMapSize().x, LOCPLINT->cb->getMapSize().y))), level(Level) { pos.w = parent->pos.w; pos.h = parent->pos.h; - drawScaled(level); -} - -CMinimapInstance::~CMinimapInstance() -{ - SDL_FreeSurface(minimap); + redrawMinimap(); } void CMinimapInstance::showAll(SDL_Surface * to) { - blitAtLoc(minimap, 0, 0, to); - - //draw heroes - std::vector heroes = LOCPLINT->cb->getHeroesInfo(false); //TODO: do we really need separate function for drawing heroes? - for(auto & hero : heroes) - { - int3 position = hero->visitablePos(); - if(position.z == level) - { - const SDL_Color & color = graphics->playerColors[hero->getOwner().getNum()]; - blitTileWithColor(color, position, to, pos.x, pos.y); - } - } -} - -std::map > CMinimap::loadColors() -{ - std::map > ret; - - for(const auto & terrain : CGI->terrainTypeHandler->objects) - { - SDL_Color normal = CSDL_Ext::toSDL(terrain->minimapUnblocked); - SDL_Color blocked = CSDL_Ext::toSDL(terrain->minimapBlocked); - - ret[terrain->getId()] = std::make_pair(normal, blocked); - } - return ret; + Canvas target(to); + target.draw(*minimap, pos.topLeft(), pos.dimensions()); } CMinimap::CMinimap(const Rect & position) : CIntObject(LCLICK | RCLICK | HOVER | MOVE, position.topLeft()), - level(0), - colors(loadColors()) + level(0) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); pos.w = position.w; @@ -188,21 +95,36 @@ CMinimap::CMinimap(const Rect & position) aiShield->disable(); } -int3 CMinimap::translateMousePosition() +int3 CMinimap::pixelToTile(const Point & cursorPos) const { // 0 = top-left corner, 1 = bottom-right corner - double dx = double(GH.getCursorPosition().x - pos.x) / pos.w; - double dy = double(GH.getCursorPosition().y - pos.y) / pos.h; + double dx = static_cast(cursorPos.x) / pos.w; + double dy = static_cast(cursorPos.y) / pos.h; int3 mapSizes = LOCPLINT->cb->getMapSize(); - int3 tile ((si32)(mapSizes.x * dx), (si32)(mapSizes.y * dy), level); - return tile; + int tileX(std::round(mapSizes.x * dx)); + int tileY(std::round(mapSizes.y * dy)); + + return int3(tileX, tileY, level); +} + +Point CMinimap::tileToPixels(const int3 &tile) const +{ + int3 mapSizes = LOCPLINT->cb->getMapSize(); + + double stepX = static_cast(pos.w) / mapSizes.x; + double stepY = static_cast(pos.h) / mapSizes.y; + + int x = static_cast(stepX * tile.x); + int y = static_cast(stepY * tile.y); + + return Point(x,y); } void CMinimap::moveAdvMapSelection() { - int3 newLocation = translateMousePosition(); + int3 newLocation = pixelToTile(GH.getCursorPosition() - pos.topLeft()); adventureInt->centerOn(newLocation); if (!(adventureInt->active & GENERAL)) @@ -219,7 +141,8 @@ void CMinimap::clickLeft(tribool down, bool previousState) void CMinimap::clickRight(tribool down, bool previousState) { - adventureInt->handleRightClick(CGI->generaltexth->zelp[291].second, down); + if (down) + CRClickPopup::createAndPush(CGI->generaltexth->zelp[291].second); } void CMinimap::hover(bool on) @@ -241,33 +164,22 @@ void CMinimap::showAll(SDL_Surface * to) CIntObject::showAll(to); if(minimap) { + Canvas target(to); + int3 mapSizes = LOCPLINT->cb->getMapSize(); int3 tileCountOnScreen = adventureInt->terrain.tileCountOnScreen(); //draw radar - Rect oldClip; Rect radar = { - si16(adventureInt->position.x * pos.w / mapSizes.x + pos.x), - si16(adventureInt->position.y * pos.h / mapSizes.y + pos.y), - ui16(tileCountOnScreen.x * pos.w / mapSizes.x), - ui16(tileCountOnScreen.y * pos.h / mapSizes.y) + adventureInt->position.x * pos.w / mapSizes.x, + adventureInt->position.y * pos.h / mapSizes.y, + tileCountOnScreen.x * pos.w / mapSizes.x - 1, + tileCountOnScreen.y * pos.h / mapSizes.y - 1 }; - if(adventureInt->mode == EAdvMapMode::WORLD_VIEW) - { - // adjusts radar so that it doesn't go out of map in world view mode (since there's no frame) - radar.x = std::min(std::max(pos.x, radar.x), pos.x + pos.w - radar.w); - radar.y = std::min(std::max(pos.y, radar.y), pos.y + pos.h - radar.h); - - if(radar.x < pos.x && radar.y < pos.y) - return; // whole map is visible at once, no point in redrawing border - } - - CSDL_Ext::getClipRect(to, oldClip); - CSDL_Ext::setClipRect(to, pos); - CSDL_Ext::drawDashedBorder(to, radar, Colors::PURPLE); - CSDL_Ext::setClipRect(to, oldClip); + Canvas clippedTarget(target, pos); + clippedTarget.drawBorderDashed(radar, CSDL_Ext::fromSDL(Colors::PURPLE)); } } @@ -283,6 +195,9 @@ void CMinimap::update() void CMinimap::setLevel(int newLevel) { + if (level == newLevel) + return; + level = newLevel; update(); } @@ -299,18 +214,13 @@ void CMinimap::setAIRadar(bool on) aiShield->disable(); update(); } - // this my happen during AI turn when this interface is inactive + + // this may happen during AI turn when this interface is inactive // force redraw in order to properly update interface GH.totalRedraw(); } -void CMinimap::hideTile(const int3 &pos) -{ - if(minimap) - minimap->refreshTile(pos); -} - -void CMinimap::showTile(const int3 &pos) +void CMinimap::updateTile(const int3 &pos) { if(minimap) minimap->refreshTile(pos); diff --git a/client/adventureMap/CMinimap.h b/client/adventureMap/CMinimap.h index eb872681e..134be5f16 100644 --- a/client/adventureMap/CMinimap.h +++ b/client/adventureMap/CMinimap.h @@ -11,67 +11,60 @@ #include "../gui/CIntObject.h" #include "../../lib/GameConstants.h" +#include "../render/Canvas.h" - -struct SDL_Color; +class ColorRGBA; class CMinimap; class CMinimapInstance : public CIntObject { CMinimap * parent; - SDL_Surface * minimap; + std::unique_ptr minimap; int level; //get color of selected tile on minimap - const SDL_Color & getTileColor(const int3 & pos); + ColorRGBA getTileColor(const int3 & pos) const; - void blitTileWithColor(const SDL_Color & color, const int3 & pos, SDL_Surface * to, int x, int y); - - //draw minimap already scaled. - //result is not antialiased. Will result in "missing" pixels on huge maps (>144) - void drawScaled(int level); + void redrawMinimap(); public: CMinimapInstance(CMinimap * parent, int level); - ~CMinimapInstance(); void showAll(SDL_Surface * to) override; - void tileToPixels (const int3 & tile, int & x, int & y, int toX = 0, int toY = 0); void refreshTile(const int3 & pos); }; /// Minimap which is displayed at the right upper corner of adventure map class CMinimap : public CIntObject { -protected: std::shared_ptr aiShield; //the graphic displayed during AI turn std::shared_ptr minimap; int level; - //to initialize colors - std::map > loadColors(); - void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; void hover (bool on) override; void mouseMoved (const Point & cursorPosition) override; + /// relocates center of adventure map screen to currently hovered tile void moveAdvMapSelection(); +protected: + /// computes coordinates of tile below cursor pos + int3 pixelToTile(const Point & cursorPos) const; + + /// computes position of tile within minimap instance + Point tileToPixels(const int3 & position) const; + public: - // terrainID -> (normal color, blocked color) - const std::map > colors; - CMinimap(const Rect & position); + explicit CMinimap(const Rect & position); - //should be called to invalidate whole map - different player or level - int3 translateMousePosition(); void update(); void setLevel(int level); void setAIRadar(bool on); void showAll(SDL_Surface * to) override; - void hideTile(const int3 &pos); //puts FoW - void showTile(const int3 &pos); //removes FoW + void updateTile(const int3 &pos); }; diff --git a/client/render/Canvas.cpp b/client/render/Canvas.cpp index 82ccd447f..9e2dd7d94 100644 --- a/client/render/Canvas.cpp +++ b/client/render/Canvas.cpp @@ -23,14 +23,14 @@ Canvas::Canvas(SDL_Surface * surface): surface->refcount++; } -Canvas::Canvas(Canvas & other): +Canvas::Canvas(const Canvas & other): surface(other.surface), renderOffset(other.renderOffset) { surface->refcount++; } -Canvas::Canvas(Canvas & other, const Rect & newClipRect): +Canvas::Canvas(const Canvas & other, const Rect & newClipRect): Canvas(other) { clipRect.emplace(); @@ -43,9 +43,9 @@ Canvas::Canvas(Canvas & other, const Rect & newClipRect): } Canvas::Canvas(const Point & size): - renderOffset(0,0) + renderOffset(0,0), + surface(CSDL_Ext::newSurface(size.x, size.y)) { - surface = CSDL_Ext::newSurface(size.x, size.y); } Canvas::~Canvas() @@ -75,9 +75,35 @@ void Canvas::draw(Canvas & image, const Point & pos) CSDL_Ext::blitAt(image.surface, renderOffset.x + pos.x, renderOffset.y + pos.y, surface); } +void Canvas::draw(Canvas & image, const Point & pos, const Point & targetSize) +{ + SDL_Rect targetRect = CSDL_Ext::toSDL(Rect(pos, targetSize)); + SDL_BlitScaled(image.surface, nullptr, surface, &targetRect ); +} + +void Canvas::drawPoint(const Point & dest, const ColorRGBA & color) +{ + CSDL_Ext::putPixelWithoutRefreshIfInSurf(surface, dest.x, dest.y, color.r, color.g, color.b, color.a); +} + void Canvas::drawLine(const Point & from, const Point & dest, const ColorRGBA & colorFrom, const ColorRGBA & colorDest) { - CSDL_Ext::drawLine(surface, renderOffset.x + from.x, renderOffset.y + from.y, renderOffset.x + dest.x, renderOffset.y + dest.y, CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest)); + CSDL_Ext::drawLine(surface, renderOffset + from, renderOffset + dest, CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest)); +} + +void Canvas::drawLineDashed(const Point & from, const Point & dest, const ColorRGBA & color) +{ + CSDL_Ext::drawLineDashed(surface, renderOffset + from, renderOffset + dest, CSDL_Ext::toSDL(color)); +} + +void Canvas::drawBorderDashed(const Rect & target, const ColorRGBA & color) +{ + Rect realTarget = target + renderOffset; + + CSDL_Ext::drawLineDashed(surface, realTarget.topLeft(), realTarget.topRight(), CSDL_Ext::toSDL(color)); + CSDL_Ext::drawLineDashed(surface, realTarget.bottomLeft(), realTarget.bottomRight(), CSDL_Ext::toSDL(color)); + CSDL_Ext::drawLineDashed(surface, realTarget.topLeft(), realTarget.bottomLeft(), CSDL_Ext::toSDL(color)); + CSDL_Ext::drawLineDashed(surface, realTarget.topRight(), realTarget.bottomRight(), CSDL_Ext::toSDL(color)); } void Canvas::drawText(const Point & position, const EFonts & font, const SDL_Color & colorDest, ETextAlignment alignment, const std::string & text ) diff --git a/client/render/Canvas.h b/client/render/Canvas.h index 73f795f2c..73c12986d 100644 --- a/client/render/Canvas.h +++ b/client/render/Canvas.h @@ -34,16 +34,16 @@ class Canvas public: /// constructs canvas using existing surface. Caller maintains ownership on the surface - Canvas(SDL_Surface * surface); + explicit Canvas(SDL_Surface * surface); /// copy contructor - Canvas(Canvas & other); + Canvas(const Canvas & other); /// creates canvas that only covers specified subsection of a surface - Canvas(Canvas & other, const Rect & clipRect); + Canvas(const Canvas & other, const Rect & clipRect); /// constructs canvas of specified size - Canvas(const Point & size); + explicit Canvas(const Point & size); ~Canvas(); @@ -56,9 +56,21 @@ public: /// renders another canvas onto this canvas void draw(Canvas & image, const Point & pos); + /// renders another canvas onto this canvas with scaling + void draw(Canvas & image, const Point & pos, const Point & targetSize); + + /// renders single pixels with specified color + void drawPoint(const Point & dest, const ColorRGBA & color); + /// renders continuous, 1-pixel wide line with color gradient void drawLine(const Point & from, const Point & dest, const ColorRGBA & colorFrom, const ColorRGBA & colorDest); + /// renders dashed, 1-pixel wide line with specified color + void drawLineDashed(const Point & from, const Point & dest, const ColorRGBA & color); + + /// renders rectangular, dashed border in specified location + void drawBorderDashed(const Rect & target, const ColorRGBA & color); + /// renders single line of text with specified parameters void drawText(const Point & position, const EFonts & font, const SDL_Color & colorDest, ETextAlignment alignment, const std::string & text ); diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 8cacfdc82..a366fa690 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -364,11 +364,40 @@ uint32_t CSDL_Ext::colorTouint32_t(const SDL_Color * color) return ret; } -static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2) +static void drawLineXDashed(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color) { + double length(x2 - x1); + for(int x = x1; x <= x2; x++) { - float f = float(x - x1) / float(x2 - x1); + double f = (x - x1) / length; + int y = vstd::lerp(y1, y2, f); + + if (std::abs(x - x1) % 5 != 4) + CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x, y, color.r, color.g, color.b); + } +} + +static void drawLineYDashed(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color) +{ + double length(y2 - y1); + + for(int y = y1; y <= y2; y++) + { + double f = (y - y1) / length; + int x = vstd::lerp(x1, x2, f); + + if (std::abs(y - y1) % 5 != 4) + CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, x, y, color.r, color.g, color.b); + } +} + +static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2) +{ + double length(x2 - x1); + for(int x = x1; x <= x2; x++) + { + double f = (x - x1) / length; int y = vstd::lerp(y1, y2, f); uint8_t r = vstd::lerp(color1.r, color2.r, f); @@ -383,9 +412,10 @@ static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2) { + double length(y2 - y1); for(int y = y1; y <= y2; y++) { - float f = float(y - y1) / float(y2 - y1); + double f = (y - y1) / length; int x = vstd::lerp(x1, x2, f); uint8_t r = vstd::lerp(color1.r, color2.r, f); @@ -398,31 +428,60 @@ static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S } } -void CSDL_Ext::drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2) +void CSDL_Ext::drawLine(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color1, const SDL_Color & color2) { - int width = std::abs(x1-x2); - int height = std::abs(y1-y2); + //FIXME: duplicated code with drawLineDashed + int width = std::abs(from.x - dest.x); + int height = std::abs(from.y - dest.y); if ( width == 0 && height == 0) { - uint8_t *p = CSDL_Ext::getPxPtr(sur, x1, y1); + uint8_t *p = CSDL_Ext::getPxPtr(sur, from.x, from.y); ColorPutter<4, 0>::PutColorAlpha(p, color1); return; } if (width > height) { - if ( x1 < x2) - drawLineX(sur, x1,y1,x2,y2, color1, color2); + if ( from.x < dest.x) + drawLineX(sur, from.x, from.y, dest.x, dest.y, color1, color2); else - drawLineX(sur, x2,y2,x1,y1, color2, color1); + drawLineX(sur, dest.x, dest.y, from.x, from.y, color2, color1); } else { - if ( y1 < y2) - drawLineY(sur, x1,y1,x2,y2, color1, color2); + if ( from.y < dest.y) + drawLineY(sur, from.x, from.y, dest.x, dest.y, color1, color2); else - drawLineY(sur, x2,y2,x1,y1, color2, color1); + drawLineY(sur, dest.x, dest.y, from.x, from.y, color2, color1); + } +} + +void CSDL_Ext::drawLineDashed(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color) +{ + //FIXME: duplicated code with drawLine + int width = std::abs(from.x - dest.x); + int height = std::abs(from.y - dest.y); + + if ( width == 0 && height == 0) + { + CSDL_Ext::putPixelWithoutRefreshIfInSurf(sur, from.x, from.y, color.r, color.g, color.b); + return; + } + + if (width > height) + { + if ( from.x < dest.x) + drawLineXDashed(sur, from.x, from.y, dest.x, dest.y, color); + else + drawLineXDashed(sur, dest.x, dest.y, from.x, from.y, color); + } + else + { + if ( from.y < dest.y) + drawLineYDashed(sur, from.x, from.y, dest.x, dest.y, color); + else + drawLineYDashed(sur, dest.x, dest.y, from.x, from.y, color); } } @@ -449,31 +508,6 @@ void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &co drawBorder(sur, r.x, r.y, r.w, r.h, color, depth); } -void CSDL_Ext::drawDashedBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color) -{ - const int y1 = r.y, y2 = r.y + r.h-1; - for (int i=0; itile; - int x,y; - minimap->tileToPixels (tile, x, y); + Point offset = tileToPixels(tile); - if (level != tile.z) - setLevel(tile.z); + setLevel(tile.z); - auto pic = std::make_shared("VwSymbol.def", 3, x, y); + auto pic = std::make_shared("VwSymbol.def", 3, offset.x, offset.y); pic->moveBy (Point ( -pic->pos.w/2, -pic->pos.h/2)); pic->callback = std::bind (&CQuestMinimap::iconClicked, this);