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 +#include + 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> 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 #include 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(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 &mem, const std::vector &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 mem(this->height * this->width); + std::vector 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 mem(this->height * this->width); + std::vector 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 + 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 images; + std::vector 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 &mem, const std::vector &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 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(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(rect.base.x), static_cast(rect.base.y)); - Point16 top_right (static_cast(rect.base.x + rect.width - 1), static_cast(rect.base.y)); - Point16 bottom_left (static_cast(rect.base.x), static_cast(rect.base.y + rect.height - 1)); - Point16 bottom_right(static_cast(rect.base.x + rect.width - 1), static_cast(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 + SDL_Rect RectToSDLRect(const Rectangle &rect) + { + SDL_Rect sdl_rect = {static_cast(rect.base.x), static_cast(rect.base.y), + static_cast(rect.width), static_cast(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(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(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) {