diff --git a/src/fence_gui.cpp b/src/fence_gui.cpp
index 1e9d178..fbc9718 100644
--- a/src/fence_gui.cpp
+++ b/src/fence_gui.cpp
@@ -115,7 +115,7 @@ void FenceGui::DrawWidget(WidgetNumber wid_num, const BaseWidget *wid) const
lines--;
Recolouring recolouring;
- _video.BlitImage({rect.x, rect.y - sprite->yoffset}, sprite, recolouring, GS_NORMAL);
+ _video.BlitImage({rect.x, rect.y - sprite->yoffset}, sprite, recolouring);
rect.y += sprite->height;
}
break;
diff --git a/src/freerct.cpp b/src/freerct.cpp
index bc093eb..89d57b9 100644
--- a/src/freerct.cpp
+++ b/src/freerct.cpp
@@ -98,15 +98,9 @@ int freerct_main(int argc, char **argv)
/* Load RCD files. */
InitImageStorage();
_rcd_collection.ScanDirectories();
- _sprite_manager.LoadRcdFiles();
InitLanguage();
- if (!_gui_sprites.HasSufficientGraphics()) {
- fprintf(stderr, "Insufficient graphics loaded.\n");
- return 1;
- }
-
cfg_file.Load("freerct.cfg");
const char *font_path = cfg_file.GetValue("font", "medium-path");
int font_size = cfg_file.GetNum("font", "medium-size");
@@ -126,6 +120,12 @@ int freerct_main(int argc, char **argv)
return 1;
}
+ _sprite_manager.LoadRcdFiles();
+ if (!_gui_sprites.HasSufficientGraphics()) {
+ fprintf(stderr, "Insufficient graphics loaded.\n");
+ return 1;
+ }
+
InitMouseModes();
StartNewGame();
diff --git a/src/gui_graphics.cpp b/src/gui_graphics.cpp
index dfa6910..e974ee2 100644
--- a/src/gui_graphics.cpp
+++ b/src/gui_graphics.cpp
@@ -55,7 +55,7 @@ void DrawBorderSprites(const BorderSpriteData &bsd, bool pressed, const Rectangl
int xleft = pt.x;
int ytop = pt.y;
if (spr_base[WBS_TOP_LEFT] != nullptr) {
- _video.BlitImage(pt, spr_base[WBS_TOP_LEFT], rc, GS_NORMAL);
+ _video.BlitImage(pt, spr_base[WBS_TOP_LEFT], rc);
xleft += spr_base[WBS_TOP_LEFT]->xoffset + spr_base[WBS_TOP_LEFT]->width;
ytop += spr_base[WBS_TOP_LEFT]->yoffset + spr_base[WBS_TOP_LEFT]->height;
}
@@ -63,7 +63,7 @@ void DrawBorderSprites(const BorderSpriteData &bsd, bool pressed, const Rectangl
pt.x = rect.base.x + rect.width - 1;
int xright = pt.x;
if (spr_base[WBS_TOP_RIGHT] != nullptr) {
- _video.BlitImage(pt, spr_base[WBS_TOP_RIGHT], rc, GS_NORMAL);
+ _video.BlitImage(pt, spr_base[WBS_TOP_RIGHT], rc);
xright += spr_base[WBS_TOP_RIGHT]->xoffset;
}
@@ -79,7 +79,7 @@ void DrawBorderSprites(const BorderSpriteData &bsd, bool pressed, const Rectangl
pt.y = rect.base.y + rect.height - 1;
int ybot = pt.y;
if (spr_base[WBS_BOTTOM_LEFT] != nullptr) {
- _video.BlitImage(pt, spr_base[WBS_BOTTOM_LEFT], rc, GS_NORMAL);
+ _video.BlitImage(pt, spr_base[WBS_BOTTOM_LEFT], rc);
ybot += spr_base[WBS_BOTTOM_LEFT]->yoffset;
}
@@ -93,7 +93,7 @@ void DrawBorderSprites(const BorderSpriteData &bsd, bool pressed, const Rectangl
pt.x = rect.base.x + rect.width - 1;
pt.y = rect.base.y + rect.height - 1;
- if (spr_base[WBS_BOTTOM_RIGHT] != nullptr) _video.BlitImage(pt, spr_base[WBS_BOTTOM_RIGHT], rc, GS_NORMAL);
+ if (spr_base[WBS_BOTTOM_RIGHT] != nullptr) _video.BlitImage(pt, spr_base[WBS_BOTTOM_RIGHT], rc);
if (spr_base[WBS_BOTTOM_MIDDLE] != nullptr) _video.BlitHorizontal(xleft, numx, pt.y, spr_base[WBS_BOTTOM_MIDDLE], rc);
@@ -117,21 +117,14 @@ void OverlayShaded(const Rectangle32 &rect)
r.RestrictTo(0, 0, _video.GetXSize(), _video.GetYSize());
if (r.width == 0 || r.height == 0) return;
- /* Set clipped area to the rectangle. */
- ClippedRectangle cr(_video.GetClippedRectangle());
- ClippedRectangle new_cr(cr, r.base.x, r.base.y, r.width, r.height);
- _video.SetClippedRectangle(new_cr);
-
/* Align the disabled sprite so it becomes a continuous pattern. */
- int32 base_x = -(r.base.x % img->width);
- int32 base_y = -(r.base.y % img->height);
+ int32 base_x = r.base.x - (r.base.x % img->width);
+ int32 base_y = r.base.y - (r.base.y % img->height);
uint16 numx = (r.width + img->width - 1) / img->width;
uint16 numy = (r.height + img->height - 1) / img->height;
static const Recolouring recolour; // Fixed recolouring mapping.
_video.BlitImages({base_x, base_y}, img, numx, numy, recolour);
-
- _video.SetClippedRectangle(cr); // Restore clipped area.
}
/**
@@ -151,10 +144,11 @@ void DrawString(StringID strid, uint8 colour, int x, int y, int width, Alignment
DrawText(strid, buffer, lengthof(buffer));
/** \todo Reduce the naiviness of this. */
if (outline) {
- _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x + 1, y, width, align);
- _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x, y + 1, width, align);
- _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x - 1, y, width, align);
- _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x, y - 1, width, align);
+// _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x + 1, y, width, align);
+// _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x, y + 1, width, align);
+// _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x - 1, y, width, align);
+// _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x, y - 1, width, align);
+ _video.BlitText(buffer, MakeRGBA(0, 0, 0, OPAQUE), x + 1, y + 1, width, align);
}
_video.BlitText(buffer, _palette[colour], x, y, width, align);
}
diff --git a/src/money.h b/src/money.h
index cf32ff3..a9d54f5 100644
--- a/src/money.h
+++ b/src/money.h
@@ -77,7 +77,7 @@ public:
*/
inline Money& operator+=(const Money& other)
{
- if ((INT64_MAX - abs(other.m_value)) < abs(this->m_value) &&
+ if ((INT64_MAX - std::abs(other.m_value)) < std::abs(this->m_value) &&
(this->m_value < 0) == (other.m_value < 0)) {
this->m_value = (this->m_value < 0) ? INT64_MIN : INT64_MAX ;
} else {
@@ -219,7 +219,7 @@ public:
*/
inline Money& operator*=(const int factor)
{
- if (factor != 0 && (INT64_MAX / abs(factor)) < abs(this->m_value)) {
+ if (factor != 0 && (INT64_MAX / std::abs(factor)) < std::abs(this->m_value)) {
this->m_value = ((this->m_value < 0) == (factor < 0)) ? INT64_MAX : INT64_MIN;
} else {
this->m_value *= factor;
diff --git a/src/palette.h b/src/palette.h
index 42041f4..9c5fd62 100644
--- a/src/palette.h
+++ b/src/palette.h
@@ -12,6 +12,9 @@
#ifndef PALETTE_H
#define PALETTE_H
+#include <functional>
+#include <map>
+
class Random;
extern const uint32 _palette[256]; ///< The 8bpp FreeRCT palette.
@@ -86,6 +89,8 @@ static inline uint32 SetA(uint32 rgba, uint8 opacity)
return rgba | opacity;
}
+#define SPLIT_RGBA(rgba) GetR(rgba), GetG(rgba), GetB(rgba), GetA(rgba)
+
/** Names of colour ranges. */
enum ColourRange {
COL_RANGE_GREY,
@@ -129,7 +134,8 @@ enum PaletteColours {
/** Shifting of the gradient to make the sprite lighter or darker. */
enum GradientShift {
- GS_NIGHT, ///< Shift gradient four steps darker.
+ GS_START,
+ GS_NIGHT = GS_START, ///< Shift gradient four steps darker.
GS_VERY_DARK, ///< Shift gradient three steps darker.
GS_DARK, ///< Shift gradient two steps darker.
GS_SLIGHTLY_DARK, ///< Shift gradient one step darker.
@@ -143,119 +149,19 @@ enum GradientShift {
GS_INVALID = 0xff, ///< Invalid gradient shift.
};
-static const int STEP_SIZE = 18; ///< Amount of colour shift for each gradient step.
-typedef uint8 (*ShiftFunc)(uint8); ///< Type of the gradient shift function.
-
-/**
- * Gradient shift function for #GS_NIGHT.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientNight(uint8 col)
-{
- return (col <= 4 * STEP_SIZE) ? 0 : col - 4 * STEP_SIZE;
-}
-
-/**
- * Gradient shift function for #GS_VERY_DARK.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientVeryDark(uint8 col)
-{
- return (col <= 3 * STEP_SIZE) ? 0 : col - 3 * STEP_SIZE;
-}
-
-/**
- * Gradient shift function for #GS_DARK
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientDark(uint8 col)
-{
- return (col <= 2 * STEP_SIZE) ? 0 : col - 2 * STEP_SIZE;
-}
-
-/**
- * Gradient shift function for #GS_SLIGHTLY_DARK.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientSlightlyDark(uint8 col)
-{
- return (col <= STEP_SIZE) ? 0 : col - STEP_SIZE;
-}
-
-/**
- * Gradient shift function for #GS_NORMAL.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientNormal(uint8 col)
-{
- return col;
-}
-
-/**
- * Gradient shift function for #GS_SLIGHTLY_LIGHT.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientSlightlyLight(uint8 col)
-{
- return (col >= 255 - STEP_SIZE) ? 255: col + STEP_SIZE;
-}
-
-/**
- * Gradient shift function for #GS_LIGHT.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientLight(uint8 col)
-{
- return (col >= 255 - 2 * STEP_SIZE) ? 255: col + 2 * STEP_SIZE;
-}
-
-/**
- * Gradient shift function for #GS_VERY_LIGHT.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientVeryLight(uint8 col)
-{
- return (col >= 255 - 3 * STEP_SIZE) ? 255: col + 3 * STEP_SIZE;
-}
-
-/**
- * Gradient shift function for #GS_DAY.
- * @param col Input colour.
- * @return Shifted result colour.
- */
-static inline uint8 ShiftGradientDay(uint8 col)
-{
- return (col >= 255 - 4 * STEP_SIZE) ? 255: col + 4 * STEP_SIZE;
-}
-
-/**
- * Select gradient shift function based on the \a shift.
- * @param shift Desired amount of gradient shift.
- * @return Recolour function implementing the shift.
- */
-static inline ShiftFunc GetGradientShiftFunc(GradientShift shift)
-{
- switch (shift) {
- case GS_NIGHT: return ShiftGradientNight;
- case GS_VERY_DARK: return ShiftGradientVeryDark;
- case GS_DARK: return ShiftGradientDark;
- case GS_SLIGHTLY_DARK: return ShiftGradientSlightlyDark;
- case GS_NORMAL: return ShiftGradientNormal;
- case GS_SLIGHTLY_LIGHT: return ShiftGradientSlightlyLight;
- case GS_LIGHT: return ShiftGradientLight;
- case GS_VERY_LIGHT: return ShiftGradientVeryLight;
- case GS_DAY: return ShiftGradientDay;
- default: NOT_REACHED();
- }
-}
+static const int STEP_SIZE = 18;
+
+static const std::map<GradientShift, std::function<uint8(uint8)>> gsmap = {
+ {GS_NIGHT, [](uint8 col){return (col <= 4 * STEP_SIZE) ? 0: col - 4 * STEP_SIZE;}},
+ {GS_VERY_DARK, [](uint8 col){return (col <= 3 * STEP_SIZE) ? 0: col - 3 * STEP_SIZE;}},
+ {GS_DARK, [](uint8 col){return (col <= 2 * STEP_SIZE) ? 0: col - 2 * STEP_SIZE;}},
+ {GS_SLIGHTLY_DARK, [](uint8 col){return (col <= 1 * STEP_SIZE) ? 0: col - 1 * STEP_SIZE;}},
+ {GS_NORMAL, [](uint8 col){return col; }},
+ {GS_SLIGHTLY_LIGHT, [](uint8 col){return (col >= 255 - 1 * STEP_SIZE) ? 255: col + 1 * STEP_SIZE;}},
+ {GS_LIGHT, [](uint8 col){return (col >= 255 - 2 * STEP_SIZE) ? 255: col + 2 * STEP_SIZE;}},
+ {GS_VERY_LIGHT, [](uint8 col){return (col >= 255 - 3 * STEP_SIZE) ? 255: col + 3 * STEP_SIZE;}},
+ {GS_DAY, [](uint8 col){return (col >= 255 - 4 * STEP_SIZE) ? 255: col + 4 * STEP_SIZE;}},
+};
/**
* Get the index of the base colour of a colour range.
diff --git a/src/path_gui.cpp b/src/path_gui.cpp
index 4a0e0ef..d5beca9 100644
--- a/src/path_gui.cpp
+++ b/src/path_gui.cpp
@@ -226,7 +226,7 @@ void PathBuildGui::DrawWidget(WidgetNumber wid_num, const BaseWidget *wid) const
int dx = (wid->pos.width - path_type_button_size.width) / 2;
int dy = (wid->pos.height - path_type_button_size.height) / 2;
Point32 pt(GetWidgetScreenX(wid) + dx - path_type_button_size.base.x, GetWidgetScreenY(wid) + dy - path_type_button_size.base.y);
- _video.BlitImage(pt, img, recolour, GS_NORMAL);
+ _video.BlitImage(pt, img, recolour);
}
}
break;
@@ -241,7 +241,7 @@ void PathBuildGui::DrawWidget(WidgetNumber wid_num, const BaseWidget *wid) const
int dx = (wid->pos.width - path_type_button_size.width) / 2;
int dy = (wid->pos.height - path_type_button_size.height) / 2;
Point32 pt(GetWidgetScreenX(wid) + dx - path_type_button_size.base.x, GetWidgetScreenY(wid) + dy - path_type_button_size.base.y);
- _video.BlitImage(pt, img, recolour, GS_NORMAL);
+ _video.BlitImage(pt, img, recolour);
}
}
break;
diff --git a/src/ride_gui.cpp b/src/ride_gui.cpp
index f72631d..c007c69 100644
--- a/src/ride_gui.cpp
+++ b/src/ride_gui.cpp
@@ -222,7 +222,7 @@ void RideSelectGui::DrawWidget(WidgetNumber wid_num, const BaseWidget *wid) cons
if (ride_type != nullptr && ride_type->kind == RTK_SHOP) {
static const Recolouring recolour; // Never modified, display 'original' image in the GUI.
Point32 pt(this->GetWidgetScreenX(wid) + wid->pos.width / 2, this->GetWidgetScreenY(wid) + 40);
- _video.BlitImage(pt, ride_type->GetView(_shop_placer.orientation), recolour, GS_NORMAL);
+ _video.BlitImage(pt, ride_type->GetView(_shop_placer.orientation), recolour);
}
}
break;
diff --git a/src/sprite_data.cpp b/src/sprite_data.cpp
index d221e5c..113e760 100644
--- a/src/sprite_data.cpp
+++ b/src/sprite_data.cpp
@@ -14,7 +14,9 @@
#include "sprite_data.h"
#include "fileio.h"
#include "bitmath.h"
+#include "video.h"
+#include <map>
#include <vector>
static const int MAX_IMAGE_COUNT = 5000; ///< Maximum number of images that can be loaded (arbitrary number).
@@ -25,14 +27,19 @@ ImageData::ImageData()
{
this->width = 0;
this->height = 0;
- this->table = nullptr;
- this->data = nullptr;
+ for (int gs = GS_START; gs < GS_COUNT; gs++) {
+ this->images[static_cast<GradientShift>(gs)] = nullptr;
+ }
}
ImageData::~ImageData()
{
- delete[] this->table;
- delete[] this->data;
+ for (auto &pair : this->images) {
+ if (pair.second != nullptr) SDL_DestroyTexture(pair.second);
+ }
+
+ for (auto &mask : this->masks) SDL_DestroyTexture(mask);
+ SDL_FreeSurface(this->img);
}
/**
@@ -56,38 +63,37 @@ bool ImageData::Load8bpp(RcdFileReader *rcd_file, size_t length)
length -= 8;
if (length > 100 * 1024) return false; // Another arbitrary limit.
- size_t jmp_table = 4 * this->height;
- if (length <= jmp_table) return false; // You need at least place for the jump table.
- length -= jmp_table;
+ if (length <= this->height * sizeof(uint32)) return false; // You need at least place for the jump table.
+ length -= this->height * sizeof(uint32);
- this->table = new uint32[jmp_table / 4];
- this->data = new uint8[length];
- if (this->table == nullptr || this->data == nullptr) return false;
+ uint32 *jmp_table = new uint32[this->height];
+ uint8 *data = new uint8[length];
+ if (jmp_table == nullptr || data == nullptr) return false;
/* Load jump table, adjusting the entries while loading. */
for (uint i = 0; i < this->height; i++) {
uint32 dest = rcd_file->GetUInt32();
if (dest == 0) {
- this->table[i] = INVALID_JUMP;
+ jmp_table[i] = INVALID_JUMP;
continue;
}
- dest -= jmp_table;
+ dest -= this->height * sizeof(uint32);
if (dest >= length) return false;
- this->table[i] = dest;
+ jmp_table[i] = dest;
}
- rcd_file->GetBlob(this->data, length); // Load the image data.
+ rcd_file->GetBlob(data, length); // Load the image data.
/* Verify the image data. */
for (uint i = 0; i < this->height; i++) {
- uint32 offset = this->table[i];
+ uint32 offset = jmp_table[i];
if (offset == INVALID_JUMP) continue;
uint32 xpos = 0;
for (;;) {
if (offset + 2 >= length) return false;
- uint8 rel_pos = this->data[offset];
- uint8 count = this->data[offset + 1];
+ uint8 rel_pos = data[offset];
+ uint8 count = data[offset + 1];
xpos += (rel_pos & 127) + count;
offset += 2 + count;
if ((rel_pos & 128) == 0) {
@@ -98,14 +104,14 @@ bool ImageData::Load8bpp(RcdFileReader *rcd_file, size_t length)
}
}
}
- return true;
+ return this->Blit8bppSprite(jmp_table, data);
}
/**
* Load a 32bpp image.
* @param rcd_file Input stream to read from.
* @param length Length of the 32bpp block.
- * @return Exeit code, \0 means ok, every other number indicates an error.
+ * @return Exit code, \0 means ok, every other number indicates an error.
*/
bool ImageData::Load32bpp(RcdFileReader *rcd_file, size_t length)
{
@@ -122,14 +128,14 @@ bool ImageData::Load32bpp(RcdFileReader *rcd_file, size_t length)
if (length > 100 * 1024) return false; // Another arbitrary limit.
/* Allocate and load the image data. */
- this->data = new uint8[length];
- if (this->data == nullptr) return false;
- rcd_file->GetBlob(this->data, length);
+ uint8 *data = new uint8[length];
+ if (data == nullptr) return false;
+ rcd_file->GetBlob(data, length);
/* Verify the data. */
- uint8 *abs_end = this->data + length;
+ uint8 *abs_end = data + length;
int line_count = 0;
- const uint8 *ptr = this->data;
+ const uint8 *ptr = data;
bool finished = false;
while (ptr < abs_end && !finished) {
line_count++;
@@ -169,92 +175,136 @@ bool ImageData::Load32bpp(RcdFileReader *rcd_file, size_t length)
}
if (line_count != this->height) return false;
if (ptr != abs_end) return false;
- return true;
+ return this->Blit32bppSprite(data);
}
/**
- * Return the pixel-value of the provided position.
- * @param xoffset Horizontal offset in the sprite.
- * @param yoffset Vertical offset in the sprite.
- * @param recolour Recolouring to apply to the retrieved pixel. Use \c nullptr for disabling recolouring.
- * @param shift Gradient shift to apply to the retrieved pixel. Use #GS_NORMAL for not shifting the colour.
- * @return Pixel value at the given position, or \c 0 if transparent.
+ * @todo Add other gradientshifts
*/
-uint32 ImageData::GetPixel(uint16 xoffset, uint16 yoffset, const Recolouring *recolour, GradientShift shift) const
+bool ImageData::SetupTextures(const std::vector<uint32> &mem, const std::vector<uint32> &recol)
{
- if (xoffset >= this->width) return _palette[0];
- if (yoffset >= this->height) return _palette[0];
+ this->img = SDL_CreateRGBSurfaceFrom((void *)mem.data(), this->width, this->height, 32, this->width * sizeof(uint32),
+ 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
+
+ this->images[GS_NORMAL] = SDL_CreateTextureFromSurface(_video.GetRenderer(), this->img);
+ SDL_SetTextureBlendMode(this->images[GS_NORMAL], SDL_BLENDMODE_BLEND);
+// for (auto &pair : this->images) {
+// pair.second = SDL_CreateTextureFromSurface(_video.GetRenderer(), this->img);
+// SDL_SetTextureBlendMode(pair.second, SDL_BLENDMODE_BLEND);
+// }
+
+ return this->img != nullptr;
+}
- if (GB(this->flags, IFG_IS_8BPP, 1) != 0) {
- /* 8bpp image. */
- uint32 offset = this->table[yoffset];
- if (offset == INVALID_JUMP) return _palette[0];
-
- uint16 xpos = 0;
- while (xpos <= xoffset) {
- uint8 rel_pos = this->data[offset];
- uint8 count = this->data[offset + 1];
- xpos += (rel_pos & 127);
- if (xpos > xoffset) return _palette[0];
- if (xoffset - xpos < count) {
- uint8 pixel = this->data[offset + 2 + xoffset - xpos];
- if (recolour != nullptr) {
- const uint8 *recolour_table = recolour->GetPalette(shift);
- pixel = recolour_table[pixel];
+/**
+ */
+bool ImageData::Blit8bppSprite(const uint32 *jmp_table, const uint8 *data)
+{
+ if (this->height == 0 || this->width == 0) return false;
+ std::vector<uint32> mem(this->height * this->width);
+ std::vector<uint32> recol(this->height * this->width);
+
+ for (int ypos = 0; ypos < this->height; ypos++) {
+ uint32 offset = jmp_table[ypos];
+ if (offset != INVALID_JUMP) {
+ for (int xpos = 0;;) {
+ uint8 rel_off = data[offset];
+ uint8 count = data[offset + 1];
+ const uint8 *pixels = &data[offset + 2];
+ offset += 2 + count;
+
+ xpos += rel_off & 0x7F;
+ for(; count > 0; count--) {
+ // RECOLOURING
+ mem.at(ypos * this->width + xpos) = _palette[*pixels];
+ pixels++;
+ xpos++;
}
- return _palette[pixel];
+ if ((rel_off & 0x80) != 0) break;
}
- xpos += count;
- offset += 2 + count;
- if ((rel_pos & 128) != 0) break;
- }
- return _palette[0];
- } else {
- /* 32bpp image. */
- const uint8 *ptr = this->data;
- while (yoffset > 0) {
- uint16 length = ptr[0] | (ptr[1] << 8);
- ptr += length;
- yoffset--;
}
- ptr += 2;
- while (xoffset > 0) {
- uint8 mode = *ptr++;
+ }
+ return this->SetupTextures(mem, recol);
+}
+
+/**
+ */
+bool ImageData::Blit32bppSprite(const uint8 *data)
+{
+ if (this->height == 0 || this->width == 0) return false;
+ std::vector<uint32> mem(this->height * this->width);
+ std::vector<uint32> recol(this->height * this->width);
+
+ const uint8 *src = data + 2; // Skip the length word.
+ for (int ypos = 0; ypos < this->height; ypos++) {
+ for (int xpos = 0;;) {
+ uint8 mode = *src++;
if (mode == 0) break;
- if ((mode & 0x3F) < xoffset) {
- xoffset -= mode & 0x3F;
- switch (mode >> 6) {
- case 0: ptr += 3 * (mode & 0x3F); break;
- case 1: ptr += 1 + 3 * (mode & 0x3F); break;
- case 2: ptr++; break;
- case 3: ptr += 1 + 1 + (mode & 0x3F); break;
- }
- } else {
- ShiftFunc sf = GetGradientShiftFunc(shift);
- switch (mode >> 6) {
- case 0:
- ptr += 3 * xoffset;
- return MakeRGBA(sf(ptr[0]), sf(ptr[1]), sf(ptr[2]), OPAQUE);
- case 1: {
- uint8 opacity = *ptr;
- ptr += 1 + 3 * xoffset;
- return MakeRGBA(sf(ptr[0]), sf(ptr[1]), sf(ptr[2]), opacity);
+ int len = mode & 0x3F;
+ switch (mode >> 6) {
+ case 0: // Fully opaque pixels.
+ for (; len > 0; len--) {
+ mem.at(ypos * this->width + xpos) = MakeRGBA(src[0], src[1], src[2], OPAQUE);
+ xpos++;
+ src += 3;
+ }
+ break;
+
+ case 1: { // Partial opaque pixels.
+ uint8 opacity = *src++;
+ for (; len > 0; len--) {
+ mem.at(ypos * this->width + xpos) = MakeRGBA(src[0], src[1], src[2], opacity);
+ xpos++;
+ src += 3;
}
- case 2:
- return _palette[0]; // Arbitrary fully transparent.
- case 3: {
- uint8 opacity = ptr[1];
- if (recolour == nullptr) return MakeRGBA(0, 0, 0, opacity); // Arbitrary colour with the correct opacity.
- const uint32 *table = recolour->GetRecolourTable(ptr[0] - 1);
- ptr += 2 + xoffset;
- uint32 recoloured = table[*ptr];
- return MakeRGBA(sf(GetR(recoloured)), sf(GetG(recoloured)), sf(GetB(recoloured)), opacity);
+ break;
+ }
+ case 2: // Fully transparent pixels.
+ xpos += len;
+ break;
+
+ case 3: { // Recoloured pixels.
+ uint8 layer = *src++;
+ //const uint32 *table = recolour.GetRecolourTable(layer - 1);
+ uint8 opacity = *src++;
+ for (; len > 0; len--) {
+ *src++;//uint32 recoloured = table[*src++];
+ //mem.at(ypos * this->width + xpos) = SetA(recoloured, opacity);
+ xpos++;
}
+ break;
}
}
}
- return _palette[0]; // Arbitrary fully transparent.
+ src += 2; // Skip the length word.
}
+
+ return this->SetupTextures(mem, recol);
+}
+
+
+/**
+ * Return the pixel-value of the provided position.
+ * @param xoffset Horizontal offset in the sprite.
+ * @param yoffset Vertical offset in the sprite.
+ * @param recolour Recolouring to apply to the retrieved pixel. Use \c nullptr for disabling recolouring.
+ * @param shift Gradient shift to apply to the retrieved pixel. Use #GS_NORMAL for not shifting the colour.
+ * @return Pixel value at the given position, or \c 0 if transparent.
+ */
+uint32 ImageData::GetPixel(uint16 xoffset, uint16 yoffset, const Recolouring *recolour) const
+{
+ if (xoffset >= this->width) return _palette[0];
+ if (yoffset >= this->height) return _palette[0];
+
+ SDL_LockSurface(this->img);
+
+ int pitch = this->img->pitch;
+ uint32 *pixels = (uint32 *)this->img->pixels;
+
+ uint32 col = pixels[yoffset * pitch + xoffset];
+
+ SDL_UnlockSurface(this->img);
+ return col;
}
/**
diff --git a/src/sprite_data.h b/src/sprite_data.h
index e1061b1..778f232 100644
--- a/src/sprite_data.h
+++ b/src/sprite_data.h
@@ -12,6 +12,8 @@
#ifndef SPRITE_DATA_H
#define SPRITE_DATA_H
+#include <SDL.h>
+
static const uint32 INVALID_JUMP = UINT32_MAX; ///< Invalid jump destination in image data.
class RcdFileReader;
@@ -33,7 +35,7 @@ public:
bool Load8bpp(RcdFileReader *rcd_file, size_t length);
bool Load32bpp(RcdFileReader *rcd_file, size_t length);
- uint32 GetPixel(uint16 xoffset, uint16 yoffset, const Recolouring *recolour = nullptr, GradientShift shift = GS_NORMAL) const;
+ uint32 GetPixel(uint16 xoffset, uint16 yoffset, const Recolouring *recolour = nullptr) const;
/**
* Is the sprite just a single pixel?
@@ -49,8 +51,15 @@ public:
uint16 height; ///< Height of the image.
int16 xoffset; ///< Horizontal offset of the image.
int16 yoffset; ///< Vertical offset of the image.
- uint32 *table; ///< The jump table. For missing entries, #INVALID_JUMP is used.
- uint8 *data; ///< The image data itself.
+
+ std::map<GradientShift, SDL_Texture *> images;
+ std::vector<SDL_Texture *> masks;
+private:
+ SDL_Surface *img; // Used to determine an image's opacity
+
+ bool Blit8bppSprite(const uint32 *jmp_table, const uint8 *data);
+ bool Blit32bppSprite(const uint8 *data);
+ bool SetupTextures(const std::vector<uint32> &mem, const std::vector<uint32> &recol);
};
ImageData *LoadImage(RcdFileReader *rcd_file);
diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp
index dbf13a5..5cd5eaf 100644
--- a/src/terraform_gui.cpp
+++ b/src/terraform_gui.cpp
@@ -103,7 +103,7 @@ void TerraformGui::DrawWidget(WidgetNumber wid_num, const BaseWidget *wid) const
base.x = this->GetWidgetScreenX(wid) + (wid->pos.width - dot->width) / 2;
base.y = this->GetWidgetScreenY(wid) + (wid->pos.height - dot->height) / 2;
- _video.BlitImage(base, dot, recolour, GS_NORMAL);
+ _video.BlitImage(base, dot, recolour);
return;
}
diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp
index 57ba0ae..51f49ae 100644
--- a/src/toolbar_gui.cpp
+++ b/src/toolbar_gui.cpp
@@ -368,14 +368,14 @@ void BottomToolbarWindow::DrawWidget(WidgetNumber wid_num, const BaseWidget *wid
Viewport *vp = GetViewport();
int dir = (vp == nullptr) ? 0 : vp->orientation;
const ImageData *img = _sprite_manager.GetTableSprite(SPR_GUI_COMPASS_START + dir);
- if (img != nullptr) _video.BlitImage({GetWidgetScreenX(wid), GetWidgetScreenY(wid)}, img, recolour, GS_NORMAL);
+ if (img != nullptr) _video.BlitImage({GetWidgetScreenX(wid), GetWidgetScreenY(wid)}, img, recolour);
break;
}
case BTB_WEATHER: {
int spr = SPR_GUI_WEATHER_START + _weather.GetWeatherType();
const ImageData *img = _sprite_manager.GetTableSprite(spr);
- if (img != nullptr) _video.BlitImage({GetWidgetScreenX(wid), GetWidgetScreenY(wid)}, img, recolour, GS_NORMAL);
+ if (img != nullptr) _video.BlitImage({GetWidgetScreenX(wid), GetWidgetScreenY(wid)}, img, recolour);
break;
}
}
diff --git a/src/video.cpp b/src/video.cpp
index f4fbdb2..7ee8601 100644
--- a/src/video.cpp
+++ b/src/video.cpp
@@ -20,6 +20,8 @@
#include "gamecontrol.h"
#include "window.h"
#include "viewport.h"
+#include "weather.h"
+
#include <string>
VideoSystem _video; ///< Video sub-system.
@@ -31,100 +33,6 @@ void QuitProgram()
_finish = true;
}
-/** Default constructor of a clipped rectangle. */
-ClippedRectangle::ClippedRectangle()
-{
- this->absx = 0;
- this->absy = 0;
- this->width = 0;
- this->height = 0;
- this->address = nullptr; this->pitch = 0;
-}
-
-/**
- * Construct a clipped rectangle from coordinates.
- * @param x Top-left x position.
- * @param y Top-left y position.
- * @param w Width.
- * @param h Height.
- */
-ClippedRectangle::ClippedRectangle(uint16 x, uint16 y, uint16 w, uint16 h)
-{
- this->absx = x;
- this->absy = y;
- this->width = w;
- this->height = h;
- this->address = nullptr; this->pitch = 0;
-}
-
-/**
- * Construct a clipped rectangle inside an existing one.
- * @param cr Existing rectangle.
- * @param x Top-left x position.
- * @param y Top-left y position.
- * @param w Width.
- * @param h Height.
- * @note %Rectangle is clipped to the old one.
- */
-ClippedRectangle::ClippedRectangle(const ClippedRectangle &cr, uint16 x, uint16 y, uint16 w, uint16 h)
-{
- if (x >= cr.width || y >= cr.height) {
- this->absx = 0;
- this->absy = 0;
- this->width = 0;
- this->height = 0;
- this->address = nullptr; this->pitch = 0;
- return;
- }
- if (x + w > cr.width) w = cr.width - x;
- if (y + h > cr.height) h = cr.height - y;
-
- this->absx = cr.absx + x;
- this->absy = cr.absy + y;
- this->width = w;
- this->height = h;
- this->address = nullptr; this->pitch = 0;
-}
-
-/**
- * Copy constructor.
- * @param cr Existing clipped rectangle.
- */
-ClippedRectangle::ClippedRectangle(const ClippedRectangle &cr)
-{
- this->absx = cr.absx;
- this->absy = cr.absy;
- this->width = cr.width;
- this->height = cr.height;
- this->address = cr.address; this->pitch = cr.pitch;
-}
-
-/**
- * Assignment operator override.
- * @param cr Existing clipped rectangle.
- * @return The assigned value.
- */
-ClippedRectangle &ClippedRectangle::operator=(const ClippedRectangle &cr)
-{
- if (this != &cr) {
- this->absx = cr.absx;
- this->absy = cr.absy;
- this->width = cr.width;
- this->height = cr.height;
- this->address = cr.address; this->pitch = cr.pitch;
- }
- return *this;
-}
-
-/** Initialize the #address if not done already. */
-void ClippedRectangle::ValidateAddress()
-{
- if (this->address == nullptr) {
- this->pitch = _video.GetXSize();
- this->address = _video.mem + this->absx + this->absy * this->pitch;
- }
-}
-
/**
* Default constructor, does nothing, never goes wrong.
* Call #Initialize to initialize the system.
@@ -165,9 +73,18 @@ std::string VideoSystem::Initialize(const char *font_name, int font_size)
return err;
}
+ this->renderer = SDL_CreateRenderer(this->window, -1, 0);
+ if (this->renderer == nullptr) {
+ std::string err = "Could not create renderer: ";
+ err += SDL_GetError();
+ SDL_Quit();
+ return err;
+ }
+ SDL_SetRenderDrawBlendMode(this->renderer, SDL_BLENDMODE_BLEND);
+
this->GetResolutions();
- this->SetResolution({800, 600}); // Allocates this->mem, return value is ignored.
+ this->SetResolution({800, 600}); // return value is ignored.
/* SDL_CreateRGBSurfaceFrom() pretends to use a void* for the data,
* but it's really treated as endian-specific uint32*.
@@ -183,12 +100,11 @@ std::string VideoSystem::Initialize(const char *font_name, int font_size)
SDL_SetWindowIcon(this->window, icon);
SDL_FreeSurface(icon);
} else {
- printf("Could not set window icon (%s)\n", SDL_GetError());
+ fprintf(stderr, "Could not set window icon (%s)\n", SDL_GetError());
}
if (TTF_Init() != 0) {
SDL_Quit();
- delete[] this->mem;
std::string err = "TTF font initialization failed: ";
err += TTF_GetError();
return err;
@@ -204,7 +120,6 @@ std::string VideoSystem::Initialize(const char *font_name, int font_size)
err += TTF_GetError();
TTF_Quit();
SDL_Quit();
- delete[] this->mem;
return err;
}
@@ -230,43 +145,12 @@ bool VideoSystem::SetResolution(const Point32 &res)
{
if (this->initialized && this->GetXSize() == res.x && this->GetYSize() == res.y) return true;
- /* Destroy old window, if it exists. */
- if (this->initialized) {
- delete[] mem;
- this->mem = nullptr;
- SDL_DestroyTexture(this->texture);
- this->texture = nullptr;
- SDL_DestroyRenderer(this->renderer);
- this->renderer = nullptr;
- }
-
this->vid_width = res.x;
this->vid_height = res.y;
SDL_SetWindowSize(this->window, this->vid_width, this->vid_height);
- this->renderer = SDL_CreateRenderer(this->window, -1, 0);
- if (this->renderer == nullptr) {
- SDL_Quit();
- fprintf(stderr, "Could not create renderer (%s)\n", SDL_GetError());
- return false;
- }
-
- this->texture = SDL_CreateTexture(this->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, this->vid_width, this->vid_height);
- if (this->texture == nullptr) {
- SDL_Quit();
- fprintf(stderr, "Could not create texture (%s)\n", SDL_GetError());
- return false;
- }
-
- this->mem = new uint32[this->vid_width * this->vid_height];
- if (this->mem == nullptr) {
- SDL_Quit();
- fprintf(stderr, "Failed to obtain window display storage.\n");
- return false;
- }
-
/* Update internal screen size data structures. */
- this->blit_rect = ClippedRectangle(0, 0, this->vid_width, this->vid_height);
+ this->blit_rect = Rectangle32(0, 0, this->vid_width, this->vid_height);
Viewport *vp = GetViewport();
if (vp != nullptr) vp->SetSize(this->vid_width, this->vid_height);
_manager.RepositionAllWindows();
@@ -314,18 +198,17 @@ void VideoSystem::MarkDisplayClean()
* Set the clipped area.
* @param cr New clipped blitting area.
*/
-void VideoSystem::SetClippedRectangle(const ClippedRectangle &cr)
+void VideoSystem::SetClippedRectangle(const Rectangle32 &rect)
{
- this->blit_rect = cr;
+ this->blit_rect = rect;
}
/**
* Get the current clipped blitting area.
* @return Current clipped area.
*/
-ClippedRectangle VideoSystem::GetClippedRectangle()
+Rectangle32 VideoSystem::GetClippedRectangle()
{
- this->blit_rect.ValidateAddress();
return this->blit_rect;
}
@@ -439,6 +322,9 @@ void VideoSystem::MainLoop()
if (_finish) break;
uint32 now = SDL_GetTicks();
+
+ printf("%dms\n", now - start);
+
if (now >= start) { // No wrap around.
now -= start;
if (now < FRAME_DELAY) SDL_Delay(FRAME_DELAY - now); // Too early, wait until next frame.
@@ -458,7 +344,6 @@ void VideoSystem::Shutdown()
TTF_CloseFont(this->font);
TTF_Quit();
SDL_Quit();
- delete[] this->mem;
this->initialized = false;
this->dirty = false;
}
@@ -470,192 +355,13 @@ void VideoSystem::Shutdown()
*/
void VideoSystem::FinishRepaint()
{
- SDL_UpdateTexture(this->texture, nullptr, this->mem, this->GetXSize() * sizeof(uint32)); // Upload memory to the GPU.
- SDL_RenderClear(this->renderer);
- SDL_RenderCopy(this->renderer, this->texture, nullptr, nullptr);
SDL_RenderPresent(this->renderer);
- MarkDisplayClean();
-}
-
-/**
- * Blit pixels from the \a spr relative to #blit_rect into the area.
- * @param img_base Coordinate of the sprite data.
- * @param spr The sprite to blit.
- * @param recolour Sprite recolouring definition.
- * @param shift Gradient shift.
- */
-void VideoSystem::BlitImage(const Point32 &img_base, const ImageData *spr, const Recolouring &recolour, GradientShift shift)
-{
- this->BlitImage(img_base, spr, recolour, shift);
-}
-
-/**
- * Blit a pixel to an area of \a numx times \a numy sprites.
- * @param cr Clipped rectangle.
- * @param scr_base Base address of the screen array.
- * @param xmin Minimal x position.
- * @param ymin Minimal y position.
- * @param numx Number of horizontal count.
- * @param numy Number of vertical count.
- * @param width Width of an image.
- * @param height Height of an image.
- * @param colour Pixel value to blit.
- * @note Function does not handle alpha blending of the new pixel with the background.
- */
-static void BlitPixel(const ClippedRectangle &cr, uint32 *scr_base,
- int32 xmin, int32 ymin, uint16 numx, uint16 numy, uint16 width, uint16 height, uint32 colour)
-{
- const int32 xend = xmin + numx * width;
- const int32 yend = ymin + numy * height;
- while (ymin < yend) {
- if (ymin >= cr.height) return;
-
- if (ymin >= 0) {
- uint32 *scr = scr_base;
- int32 x = xmin;
- while (x < xend) {
- if (x >= cr.width) break;
- if (x >= 0) *scr = colour;
-
- x += width;
- scr += width;
- }
- }
- ymin += height;
- scr_base += height * cr.pitch;
- }
-}
-
-/**
- * Blit 8bpp images to the screen.
- * @param cr Clipped rectangle to draw to.
- * @param x_base Base X coordinate of the sprite data.
- * @param y_base Base Y coordinate of the sprite data.
- * @param spr The sprite to blit.
- * @param numx Number of sprites to draw in horizontal direction.
- * @param numy Number of sprites to draw in vertical direction.
- * @param recoloured Shifted palette to use.
- */
-static void Blit8bppImages(const ClippedRectangle &cr, int32 x_base, int32 y_base, const ImageData *spr, uint16 numx, uint16 numy, const uint8 *recoloured)
-{
- uint32 *line_base = cr.address + x_base + cr.pitch * y_base;
- int32 ypos = y_base;
- for (int yoff = 0; yoff < spr->height; yoff++) {
- uint32 offset = spr->table[yoff];
- if (offset != INVALID_JUMP) {
- int32 xpos = x_base;
- uint32 *src_base = line_base;
- for (;;) {
- uint8 rel_off = spr->data[offset];
- uint8 count = spr->data[offset + 1];
- uint8 *pixels = &spr->data[offset + 2];
- offset += 2 + count;
-
- xpos += rel_off & 127;
- src_base += rel_off & 127;
- while (count > 0) {
- uint32 colour = _palette[recoloured[*pixels]];
- BlitPixel(cr, src_base, xpos, ypos, numx, numy, spr->width, spr->height, colour);
- pixels++;
- xpos++;
- src_base++;
- count--;
- }
- if ((rel_off & 128) != 0) break;
- }
- }
- line_base += cr.pitch;
- ypos++;
- }
-}
-
-/**
- * Blit 32bpp images to the screen.
- * @param cr Clipped rectangle to draw to.
- * @param x_base Base X coordinate of the sprite data.
- * @param y_base Base Y coordinate of the sprite data.
- * @param spr The sprite to blit.
- * @param numx Number of sprites to draw in horizontal direction.
- * @param numy Number of sprites to draw in vertical direction.
- * @param recolour Sprite recolouring definition.
- * @param shift Gradient shift.
- */
-static void Blit32bppImages(const ClippedRectangle &cr, int32 x_base, int32 y_base, const ImageData *spr, uint16 numx, uint16 numy, const Recolouring &recolour, GradientShift shift)
-{
- uint32 *line_base = cr.address + x_base + cr.pitch * y_base;
- ShiftFunc sf = GetGradientShiftFunc(shift);
- int32 ypos = y_base;
- const uint8 *src = spr->data + 2; // Skip the length word.
- for (int yoff = 0; yoff < spr->height; yoff++) {
- int32 xpos = x_base;
- uint32 *src_base = line_base;
- for (;;) {
- uint8 mode = *src++;
- if (mode == 0) break;
- switch (mode >> 6) {
- case 0: // Fully opaque pixels.
- mode &= 0x3F;
- for (; mode > 0; mode--) {
- uint32 colour = MakeRGBA(sf(src[0]), sf(src[1]), sf(src[2]), OPAQUE);
- BlitPixel(cr, src_base, xpos, ypos, numx, numy, spr->width, spr->height, colour);
- xpos++;
- src_base++;
- src += 3;
- }
- break;
-
- case 1: { // Partial opaque pixels.
- uint8 opacity = *src++;
- mode &= 0x3F;
- for (; mode > 0; mode--) {
- /* Cheat transparency a bit by just recolouring the previously drawn pixel */
- uint32 old_pixel = *src_base;
-
- uint r = sf(src[0]) * opacity + GetR(old_pixel) * (256 - opacity);
- uint g = sf(src[1]) * opacity + GetG(old_pixel) * (256 - opacity);
- uint b = sf(src[2]) * opacity + GetB(old_pixel) * (256 - opacity);
-
- /* Opaque, but colour adjusted depending on the old pixel. */
- uint32 ndest = MakeRGBA(r >> 8, g >> 8, b >> 8, OPAQUE);
- BlitPixel(cr, src_base, xpos, ypos, numx, numy, spr->width, spr->height, ndest);
- xpos++;
- src_base++;
- src += 3;
- }
- break;
- }
- case 2: // Fully transparent pixels.
- xpos += mode & 0x3F;
- src_base += mode & 0x3F;
- break;
+ /* Reset the renderer */
+ SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 0);
+ SDL_RenderClear(this->renderer);
- case 3: { // Recoloured pixels.
- uint8 layer = *src++;
- const uint32 *table = recolour.GetRecolourTable(layer - 1);
- uint8 opacity = *src++;
- mode &= 0x3F;
- for (; mode > 0; mode--) {
- uint32 old_pixel = *src_base;
- uint32 recoloured = table[*src++];
-
- uint r = sf(GetR(recoloured)) * opacity + GetR(old_pixel) * (256 - opacity);
- uint g = sf(GetG(recoloured)) * opacity + GetG(old_pixel) * (256 - opacity);
- uint b = sf(GetB(recoloured)) * opacity + GetB(old_pixel) * (256 - opacity);
-
- uint32 colour = MakeRGBA(r >> 8, g >> 8, b >> 8, OPAQUE);
- BlitPixel(cr, src_base, xpos, ypos, numx, numy, spr->width, spr->height, colour);
- xpos++;
- src_base++;
- }
- break;
- }
- }
- }
- line_base += cr.pitch;
- ypos++;
- src += 2; // Skip the length word.
- }
+ MarkDisplayClean();
}
/**
@@ -667,31 +373,28 @@ static void Blit32bppImages(const ClippedRectangle &cr, int32 x_base, int32 y_ba
* @param recolour Sprite recolouring definition.
* @param shift Gradient shift.
*/
-void VideoSystem::BlitImages(const Point32 &pt, const ImageData *spr, uint16 numx, uint16 numy, const Recolouring &recolour, GradientShift shift)
+void VideoSystem::BlitImages(const Point32 &pt, const ImageData *spr, uint16 numx, uint16 numy, const Recolouring &recolour)
{
- this->blit_rect.ValidateAddress();
-
int x_base = pt.x + spr->xoffset;
int y_base = pt.y + spr->yoffset;
- /* Don't draw wildly outside the screen. */
- while (numx > 0 && x_base + spr->width < 0) {
- x_base += spr->width; numx--;
- }
- while (numx > 0 && x_base + (numx - 1) * spr->width >= this->blit_rect.width) numx--;
- if (numx == 0) return;
+ // Get current gradient shift
+ GradientShift gs = static_cast<GradientShift>(GS_LIGHT - _weather.GetWeatherType());
- while (numy > 0 && y_base + spr->height < 0) {
- y_base += spr->height; numy--;
+ SDL_Texture *shifted_sprite = spr->images.find(gs)->second;
+ if (shifted_sprite == nullptr) {
+ shifted_sprite = spr->images.find(GS_NORMAL)->second;
}
- while (numy > 0 && y_base + (numy - 1) * spr->height >= this->blit_rect.height) numy--;
- if (numy == 0) return;
+ assert(shifted_sprite != nullptr);
- if (GB(spr->flags, IFG_IS_8BPP, 1) != 0) {
- Blit8bppImages(this->blit_rect, x_base, y_base, spr, numx, numy, recolour.GetPalette(shift));
- } else {
- Blit32bppImages(this->blit_rect, x_base, y_base, spr, numx, numy, recolour, shift);
+ for (int x = 0; x < numx; x++) {
+ for (int y = 0; y < numy; y++) {
+ SDL_Rect sdl_rect = {x_base + x * spr->width, y_base + y * spr->height, spr->width, spr->height};
+ SDL_RenderCopy(this->renderer, shifted_sprite, nullptr, &sdl_rect);
+ // Also draw recolourings
+ }
}
+
}
/**
@@ -757,7 +460,7 @@ void VideoSystem::GetNumberRangeSize(int64 smallest, int64 biggest, int *width,
*/
void VideoSystem::BlitText(const uint8 *text, uint32 colour, int xpos, int ypos, int width, Alignment align)
{
- SDL_Color col = {0, 0, 0}; // Font colour does not matter as only the bitmap is used.
+ SDL_Color col = {SPLIT_RGBA(colour)};
SDL_Surface *surf = TTF_RenderUTF8_Solid(this->font, (const char *)text, col);
if (surf == nullptr) {
fprintf(stderr, "Rendering text failed (%s)\n", TTF_GetError());
@@ -769,7 +472,10 @@ void VideoSystem::BlitText(const uint8 *text, uint32 colour, int xpos, int ypos,
return;
}
+ SDL_Texture *tex = SDL_CreateTextureFromSurface(this->renderer, surf);
+
int real_w = std::min(surf->w, width);
+
switch (align) {
case ALG_LEFT:
break;
@@ -785,44 +491,10 @@ void VideoSystem::BlitText(const uint8 *text, uint32 colour, int xpos, int ypos,
default: NOT_REACHED();
}
- this->blit_rect.ValidateAddress();
-
- uint8 *src = ((uint8 *)surf->pixels);
- uint32 *dest = this->blit_rect.address + xpos + ypos * this->blit_rect.pitch;
- int h = surf->h;
- if (ypos < 0) {
- h += ypos;
- src -= ypos * surf->pitch;
- dest -= ypos * this->blit_rect.pitch;
- ypos = 0;
- }
- while (h > 0) {
- if (ypos >= this->blit_rect.height) break;
- uint8 *src2 = src;
- uint32 *dest2 = dest;
- int w = real_w;
- int x = xpos;
- if (x < 0) {
- w += x;
- if (w <= 0) break;
- dest2 -= x;
- src2 -= x;
- x = 0;
- }
- while (w > 0) {
- if (x >= this->blit_rect.width) break;
- if (*src2 != 0) *dest2 = colour;
- src2++;
- dest2++;
- x++;
- w--;
- }
- ypos++;
- src += surf->pitch;
- dest += this->blit_rect.pitch;
- h--;
- }
+ SDL_Rect sdl_rect = {xpos, ypos, real_w, surf->h};
+ SDL_RenderCopy(this->renderer, tex, nullptr, &sdl_rect);
+ SDL_DestroyTexture(tex);
SDL_FreeSurface(surf);
}
@@ -834,51 +506,8 @@ void VideoSystem::BlitText(const uint8 *text, uint32 colour, int xpos, int ypos,
*/
void VideoSystem::DrawLine(const Point16 &start, const Point16 &end, uint32 colour)
{
- int16 dx, inc_x, dy, inc_y;
- if (start.x > end.x) {
- dx = start.x - end.x;
- inc_x = -1;
- } else {
- dx = end.x - start.x;
- inc_x = 1;
- }
- if (start.y > end.y) {
- dy = start.y - end.y;
- inc_y = -1;
- } else {
- dy = end.y - start.y;
- inc_y = 1;
- }
-
- int16 step = std::min(dx, dy);
- int16 pos_x = start.x;
- int16 pos_y = start.y;
- int16 sum_x = 0;
- int16 sum_y = 0;
-
- this->blit_rect.ValidateAddress();
- uint32 *dest = this->blit_rect.address + pos_x + pos_y * this->blit_rect.pitch;
-
- for (;;) {
- /* Blit pixel. */
- if (pos_x >= 0 && pos_x < this->blit_rect.width && pos_y >= 0 && pos_y < this->blit_rect.height) {
- *dest = colour;
- }
- if (pos_x == end.x && pos_y == end.y) break;
-
- sum_x += step;
- sum_y += step;
- if (sum_x >= dy) {
- pos_x += inc_x;
- dest += inc_x;
- sum_x -= dy;
- }
- if (sum_y >= dx) {
- pos_y += inc_y;
- dest += inc_y * this->blit_rect.pitch;
- sum_y -= dx;
- }
- }
+ SDL_SetRenderDrawColor(this->renderer, SPLIT_RGBA(colour));
+ SDL_RenderDrawLine(this->renderer, start.x, start.y, end.x, end.y);
}
/**
@@ -888,14 +517,9 @@ void VideoSystem::DrawLine(const Point16 &start, const Point16 &end, uint32 colo
*/
void VideoSystem::DrawRectangle(const Rectangle32 &rect, uint32 colour)
{
- Point16 top_left (static_cast<int16>(rect.base.x), static_cast<int16>(rect.base.y));
- Point16 top_right (static_cast<int16>(rect.base.x + rect.width - 1), static_cast<int16>(rect.base.y));
- Point16 bottom_left (static_cast<int16>(rect.base.x), static_cast<int16>(rect.base.y + rect.height - 1));
- Point16 bottom_right(static_cast<int16>(rect.base.x + rect.width - 1), static_cast<int16>(rect.base.y + rect.height - 1));
- this->DrawLine(top_left, top_right, colour);
- this->DrawLine(top_left, bottom_left, colour);
- this->DrawLine(top_right, bottom_right, colour);
- this->DrawLine(bottom_left, bottom_right, colour);
+ SDL_Rect sdl_rect = RectToSDLRect(rect);
+ SDL_SetRenderDrawColor(this->renderer, SPLIT_RGBA(colour));
+ SDL_RenderDrawRect(this->renderer, &sdl_rect);
}
/**
@@ -905,22 +529,7 @@ void VideoSystem::DrawRectangle(const Rectangle32 &rect, uint32 colour)
*/
void VideoSystem::FillRectangle(const Rectangle32 &rect, uint32 colour)
{
- ClippedRectangle cr = this->GetClippedRectangle();
-
- int x = Clamp((int)rect.base.x, 0, (int)cr.width);
- int w = Clamp((int)(rect.base.x + rect.width), 0, (int)cr.width);
- int y = Clamp((int)rect.base.y, 0, (int)cr.height);
- int h = Clamp((int)(rect.base.y + rect.height), 0, (int)cr.height);
-
- w -= x;
- h -= y;
- if (w == 0 || h == 0) return;
-
- uint32 *pixels_base = cr.address + x + y * cr.pitch;
- while (h > 0) {
- uint32 *pixels = pixels_base;
- for (int i = 0; i < w; i++) *pixels++ = colour;
- pixels_base += cr.pitch;
- h--;
- }
+ SDL_Rect sdl_rect = RectToSDLRect(rect);
+ SDL_SetRenderDrawColor(this->renderer, SPLIT_RGBA(colour));
+ SDL_RenderFillRect(this->renderer, &sdl_rect);
}
diff --git a/src/video.h b/src/video.h
index 9c9c7ba..d3cfd22 100644
--- a/src/video.h
+++ b/src/video.h
@@ -22,27 +22,6 @@ void QuitProgram();
class ImageData;
-/** Clipped rectangle. */
-class ClippedRectangle {
-public:
- ClippedRectangle();
- ClippedRectangle(uint16 x, uint16 y, uint16 w, uint16 h);
- ClippedRectangle(const ClippedRectangle &cr, uint16 x, uint16 y, uint16 w, uint16 h);
-
- ClippedRectangle(const ClippedRectangle &cr);
- ClippedRectangle &operator=(const ClippedRectangle &cr);
-
- void ValidateAddress();
-
- uint16 absx; ///< Absolute X position in the screen of the top-left.
- uint16 absy; ///< Absolute Y position in the screen of the top-left.
- uint16 width; ///< Number of columns.
- uint16 height; ///< Number of rows.
-
- uint32 *address; ///< Base address. @note Call #ValidateAddress prior to use.
- int32 pitch; ///< Pitch of a row in bytes. @note Call #ValidateAddress prior to use.
-};
-
/** How to align text during drawing. */
enum Alignment {
ALG_LEFT, ///< Align to the left edge.
@@ -54,19 +33,26 @@ enum Alignment {
* Class representing the video system.
*/
class VideoSystem {
- friend class ClippedRectangle;
-
public:
VideoSystem();
~VideoSystem();
std::string Initialize(const char *font_name, int font_size);
bool SetResolution(const Point32 &res);
- void GetResolutions();
void MainLoop();
void Shutdown();
/**
+ * Should only ever be used externally for creating SDL_Textures
+ * @return The renderer instance.
+ */
+ inline SDL_Renderer *GetRenderer()
+ {
+ assert(this->initialized);
+ return this->renderer;
+ }
+
+ /**
* Get horizontal size of the screen.
* @return Number of pixels of the screen horizontally.
*/
@@ -98,8 +84,16 @@ public:
void MarkDisplayDirty();
void MarkDisplayDirty(const Rectangle32 &rect);
- void SetClippedRectangle(const ClippedRectangle &cr);
- ClippedRectangle GetClippedRectangle();
+ /**
+ * Blit a single sprite.
+ * @param img_base Coordinates of the sprite data (relative to window).
+ * @param spr Sprite to blit.
+ * @param recolour Sprite recolouring definition.
+ */
+ inline void BlitImage(const Point32 &img_base, const ImageData *spr, const Recolouring &recolour)
+ {
+ this->BlitImages(img_base, spr, 1, 1, recolour);
+ }
/**
* Blit a row of sprites.
@@ -127,8 +121,7 @@ public:
this->BlitImages({x, ymin}, spr, 1, numy, recolour);
}
- void BlitImage(const Point32 &img_base, const ImageData *spr, const Recolouring &recolour, GradientShift shift);
- void BlitImages(const Point32 &pt, const ImageData *spr, uint16 numx, uint16 numy, const Recolouring &recolour, GradientShift shift = GS_NORMAL);
+ void BlitImages(const Point32 &pt, const ImageData *spr, uint16 numx, uint16 numy, const Recolouring &recolour);
void FinishRepaint();
@@ -141,6 +134,9 @@ public:
return this->font_height;
}
+ void SetClippedRectangle(const Rectangle32 &rect);
+ Rectangle32 GetClippedRectangle();
+
void GetTextSize(const uint8 *text, int *width, int *height);
void GetNumberRangeSize(int64 smallest, int64 biggest, int *width, int *height);
void BlitText(const uint8 *text, uint32 colour, int xpos, int ypos, int width = 0x7FFF, Alignment align = ALG_LEFT);
@@ -162,12 +158,20 @@ private:
SDL_Window *window; ///< %Window of the application.
SDL_Renderer *renderer; ///< GPU renderer to the application window.
SDL_Texture *texture; ///< GPU Texture storage of the application window.
- uint32 *mem; ///< Memory used for blitting the application display.
- ClippedRectangle blit_rect; ///< %Rectangle to blit in.
+ Rectangle32 blit_rect;
Point16 digit_size; ///< Size of largest digit (initially a zero-size).
+ void GetResolutions();
bool HandleEvent();
void MarkDisplayClean();
+
+ template<class PZ, class SZ>
+ SDL_Rect RectToSDLRect(const Rectangle<PZ, SZ> &rect)
+ {
+ SDL_Rect sdl_rect = {static_cast<int>(rect.base.x), static_cast<int>(rect.base.y),
+ static_cast<int>(rect.width), static_cast<int>(rect.height)};
+ return sdl_rect;
+ }
};
extern VideoSystem _video;
diff --git a/src/viewport.cpp b/src/viewport.cpp
index 418b901..3a919a6 100644
--- a/src/viewport.cpp
+++ b/src/viewport.cpp
@@ -1404,21 +1404,15 @@ void Viewport::OnDraw()
collector.Collect(this->additions_enabled && this->additions_displayed);
static const Recolouring recolour;
- _video.FillRectangle(this->rect, MakeRGBA(0, 0, 0, OPAQUE)); // Black background.
-
- ClippedRectangle cr = _video.GetClippedRectangle();
- assert(this->rect.base.x >= 0 && this->rect.base.y >= 0);
- ClippedRectangle draw_rect(cr, this->rect.base.x, this->rect.base.y, this->rect.width, this->rect.height);
- _video.SetClippedRectangle(draw_rect);
-
- GradientShift gs = static_cast<GradientShift>(GS_LIGHT - _weather.GetWeatherType());
for (const auto &iter : collector.draw_images) {
const DrawData &dd = iter;
const Recolouring &rec = (dd.recolour == nullptr) ? recolour : *dd.recolour;
- _video.BlitImage(dd.base, dd.sprite, rec, gs);
+ _video.BlitImage(dd.base, dd.sprite, rec);
}
- _video.SetClippedRectangle(cr);
+ //GradientShift gs = static_cast<GradientShift>(GS_LIGHT - _weather.GetWeatherType());
+ /// \todo Actually change brightness overlay depending on weather
+ //_video.FillRectangle(this->rect, MakeRGBA(0, 0, 0, 0));
}
/**
diff --git a/src/widget.cpp b/src/widget.cpp
index 376677e..619ae9f 100644
--- a/src/widget.cpp
+++ b/src/widget.cpp
@@ -292,7 +292,7 @@ void LeafWidget::Draw(const GuiWindow *w)
} else if ((this->flags & LWF_PRESSED) != 0) {
spr_num += WCS_EMPTY_PRESSED;
}
- _video.BlitImage({left, top}, _gui_sprites.radio_button.sprites[spr_num], rc, GS_NORMAL);
+ _video.BlitImage({left, top}, _gui_sprites.radio_button.sprites[spr_num], rc);
return;
}
@@ -319,7 +319,7 @@ void LeafWidget::Draw(const GuiWindow *w)
int yoffset = top + (bottom - 1 - top - _gui_sprites.close_sprite->height) / 2;
const ImageData *imgdata = _gui_sprites.close_sprite;
- if (imgdata != nullptr) _video.BlitImage({xoffset + 1, yoffset + 1}, imgdata, rc, GS_NORMAL);
+ if (imgdata != nullptr) _video.BlitImage({xoffset + 1, yoffset + 1}, imgdata, rc);
/* Closebox is never shaded. */
}
}
@@ -500,7 +500,7 @@ void DataWidget::Draw(const GuiWindow *w)
int xoffset = left + (right + 1 - left - this->value_width) / 2 - rect.base.x;
yoffset -= rect.base.y;
const ImageData *imgdata = _sprite_manager.GetTableSprite(this->value);
- if (imgdata != nullptr) _video.BlitImage({xoffset + pressed, yoffset + pressed}, imgdata, rc, GS_NORMAL);
+ if (imgdata != nullptr) _video.BlitImage({xoffset + pressed, yoffset + pressed}, imgdata, rc);
break;
}
@@ -513,7 +513,7 @@ void DataWidget::Draw(const GuiWindow *w)
if (imgdata != nullptr) {
int triangle_yoff = top + (bottom + 1 - top - imgrect.height) / 2 + pressed;
- _video.BlitImage({right - imgrect.width + pressed, triangle_yoff}, imgdata, rc, GS_NORMAL);
+ _video.BlitImage({right - imgrect.width + pressed, triangle_yoff}, imgdata, rc);
}
/* Note: Reusing the same string parameters from above */
if (this->value != STR_NULL) DrawString(w->TranslateStringNumber(this->value), TEXT_WHITE, left + pressed, yoffset + pressed, right - left - imgrect.width, align);
@@ -589,12 +589,12 @@ void ScrollbarWidget::Draw(const GuiWindow *w)
Point32 pos(w->GetWidgetScreenX(this), w->GetWidgetScreenY(this));
/* Draw left/up button. */
- _video.BlitImage(pos, imd[WLS_LEFT_BUTTON], rc, GS_NORMAL);
+ _video.BlitImage(pos, imd[WLS_LEFT_BUTTON], rc);
if (this->wtype == WT_HOR_SCROLLBAR) pos.x += imd[WLS_LEFT_BUTTON]->width;
if (this->wtype != WT_HOR_SCROLLBAR) pos.y += imd[WLS_LEFT_BUTTON]->height;
/* Draw top/left underground. */
- _video.BlitImage(pos, imd[WLS_LEFT_BED], rc, GS_NORMAL);
+ _video.BlitImage(pos, imd[WLS_LEFT_BED], rc);
if (this->wtype == WT_HOR_SCROLLBAR) pos.x += imd[WLS_LEFT_BED]->width;
if (this->wtype != WT_HOR_SCROLLBAR) pos.y += imd[WLS_LEFT_BED]->height;
@@ -614,7 +614,7 @@ void ScrollbarWidget::Draw(const GuiWindow *w)
}
/* Draw bottom/right underground. */
- _video.BlitImage(pos, imd[WLS_RIGHT_BED], rc, GS_NORMAL);
+ _video.BlitImage(pos, imd[WLS_RIGHT_BED], rc);
if (this->wtype == WT_HOR_SCROLLBAR) {
pos.x += imd[WLS_RIGHT_BED]->width;
} else {
@@ -622,7 +622,7 @@ void ScrollbarWidget::Draw(const GuiWindow *w)
}
/* Draw right/bottom button. */
- _video.BlitImage(pos, imd[WLS_RIGHT_BUTTON], rc, GS_NORMAL);
+ _video.BlitImage(pos, imd[WLS_RIGHT_BUTTON], rc);
int start_edge, slider_length;
this->CalculateSliderPosition(&start_edge, &slider_length);
@@ -634,7 +634,7 @@ void ScrollbarWidget::Draw(const GuiWindow *w)
}
/* Draw top/left slider. */
- _video.BlitImage(pos, imd[WLS_LEFT_SLIDER], rc, GS_NORMAL);
+ _video.BlitImage(pos, imd[WLS_LEFT_SLIDER], rc);
if (this->wtype == WT_HOR_SCROLLBAR) {
pos.x += imd[WLS_LEFT_SLIDER]->width;
} else {
@@ -653,7 +653,7 @@ void ScrollbarWidget::Draw(const GuiWindow *w)
}
/* Draw bottom/right slider. */
- _video.BlitImage(pos, imd[WLS_RIGHT_SLIDER], rc, GS_NORMAL);
+ _video.BlitImage(pos, imd[WLS_RIGHT_SLIDER], rc);
}
/**
diff --git a/src/window.cpp b/src/window.cpp
index 641e9a4..9aa534e 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -989,8 +989,8 @@ void UpdateWindows()
/* Until the entire background is covered by the main display, clean the entire display to ensure deleted
* windows truly disappear (even if there is no other window behind it).
*/
- Rectangle32 rect(0, 0, _video.GetXSize(), _video.GetYSize());
- _video.FillRectangle(rect, MakeRGBA(0, 0, 0, OPAQUE));
+// Rectangle32 rect(0, 0, _video.GetXSize(), _video.GetYSize());
+// _video.FillRectangle(rect, MakeRGBA(0, 0, 0, OPAQUE));
Window *w = _manager.bottom;
while (w != nullptr) {