Backport: "FormSpec: 9-slice images, animated_images, and fgimg_middle (#10265)"

Co-Authored-By: Vincent Robinson <robinsonvincent89@gmail.com>
master
Maksim 2022-06-15 10:45:19 +03:00
parent d6c82c3f7c
commit fbd1c6b88d
12 changed files with 177 additions and 186 deletions

View File

@ -2284,20 +2284,23 @@ Elements
* `bgcolor` tooltip background color as `ColorString` (optional) * `bgcolor` tooltip background color as `ColorString` (optional)
* `fontcolor` tooltip font color as `ColorString` (optional) * `fontcolor` tooltip font color as `ColorString` (optional)
### `image[<X>,<Y>;<W>,<H>;<texture name>]` ### `image[<X>,<Y>;<W>,<H>;<texture name>;<middle>]`
* Show an image * Show an image.
* `middle` (optional): Makes the image render in 9-sliced mode and defines the middle rect.
Requires formspec version >= 4. See `background9[]` documentation for more information.
### `animated_image[<X>,<Y>;<W>,<H>;<name>;<texture name>;<frame count>;<frame duration>;<frame start>]` ### `animated_image[<X>,<Y>;<W>,<H>;<name>;<texture name>;<frame count>;<frame duration>;<frame start>;<middle>]`
* Show an animated image. The image is drawn like a "vertical_frames" tile * Show an animated image. The image is drawn like a "vertical_frames" tile
animation (See [Tile animation definition]), but uses a frame count/duration animation (See [Tile animation definition]), but uses a frame count/duration for simplicity
for simplicity
* `name`: Element name to send when an event occurs. The event value is the index of the current frame. * `name`: Element name to send when an event occurs. The event value is the index of the current frame.
* `texture name`: The image to use. * `texture name`: The image to use.
* `frame count`: The number of frames animating the image. * `frame count`: The number of frames animating the image.
* `frame duration`: Milliseconds between each frame. `0` means the frames don't advance. * `frame duration`: Milliseconds between each frame. `0` means the frames don't advance.
* `frame start` (Optional): The index of the frame to start on. Default `1`. * `frame start` (optional): The index of the frame to start on. Default `1`.
* `middle` (optional): Makes the image render in 9-sliced mode and defines the middle rect.
Requires formspec version >= 4. See `background9[]` documentation for more information.
### `model[<X>,<Y>;<W>,<H>;<name>;<mesh>;<textures>;<rotation X,Y>;<continuous>;<mouse control>;<frame loop range>]` ### `model[<X>,<Y>;<W>,<H>;<name>;<mesh>;<textures>;<rotation X,Y>;<continuous>;<mouse control>;<frame loop range>]`
@ -2912,6 +2915,8 @@ Some types may inherit styles from parent types.
* This is deprecated, use states instead. * This is deprecated, use states instead.
* fgimg_pressed - image when pressed. Defaults to fgimg when not provided. * fgimg_pressed - image when pressed. Defaults to fgimg when not provided.
* This is deprecated, use states instead. * This is deprecated, use states instead.
* fgimg_middle - Makes the fgimg textures render in 9-sliced mode and defines the middle rect.
See background9[] documentation for more details.
* NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed
* sound - a sound to be played when triggered. * sound - a sound to be played when triggered.
* scrollbar * scrollbar

View File

@ -171,52 +171,61 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
} }
void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture,
const core::rect<s32> &rect, const core::rect<s32> &middle, const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
const core::rect<s32> *cliprect, const video::SColor *const colors) const core::rect<s32> &middlerect, const core::rect<s32> *cliprect,
const video::SColor *const colors)
{ {
auto originalSize = texture->getOriginalSize(); // `-x` is interpreted as `w - x`
core::vector2di lowerRightOffset = core::vector2di(originalSize.Width, originalSize.Height) - middle.LowerRightCorner; core::rect<s32> middle = middlerect;
if (middlerect.LowerRightCorner.X < 0)
middle.LowerRightCorner.X += srcrect.getWidth();
if (middlerect.LowerRightCorner.Y < 0)
middle.LowerRightCorner.Y += srcrect.getHeight();
core::vector2di lower_right_offset = core::vector2di(srcrect.getWidth(),
srcrect.getHeight()) - middle.LowerRightCorner;
for (int y = 0; y < 3; ++y) { for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 3; ++x) { for (int x = 0; x < 3; ++x) {
core::rect<s32> src({0, 0}, originalSize); core::rect<s32> src = srcrect;
core::rect<s32> dest = rect; core::rect<s32> dest = destrect;
switch (x) { switch (x) {
case 0: case 0:
dest.LowerRightCorner.X = rect.UpperLeftCorner.X + middle.UpperLeftCorner.X; dest.LowerRightCorner.X = destrect.UpperLeftCorner.X + middle.UpperLeftCorner.X;
src.LowerRightCorner.X = middle.UpperLeftCorner.X; src.LowerRightCorner.X = srcrect.UpperLeftCorner.X + middle.UpperLeftCorner.X;
break; break;
case 1: case 1:
dest.UpperLeftCorner.X += middle.UpperLeftCorner.X; dest.UpperLeftCorner.X += middle.UpperLeftCorner.X;
dest.LowerRightCorner.X -= lowerRightOffset.X; dest.LowerRightCorner.X -= lower_right_offset.X;
src.UpperLeftCorner.X = middle.UpperLeftCorner.X; src.UpperLeftCorner.X += middle.UpperLeftCorner.X;
src.LowerRightCorner.X = middle.LowerRightCorner.X; src.LowerRightCorner.X -= lower_right_offset.X;
break; break;
case 2: case 2:
dest.UpperLeftCorner.X = rect.LowerRightCorner.X - lowerRightOffset.X; dest.UpperLeftCorner.X = destrect.LowerRightCorner.X - lower_right_offset.X;
src.UpperLeftCorner.X = middle.LowerRightCorner.X; src.UpperLeftCorner.X = srcrect.LowerRightCorner.X - lower_right_offset.X;
break; break;
} }
switch (y) { switch (y) {
case 0: case 0:
dest.LowerRightCorner.Y = rect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y; dest.LowerRightCorner.Y = destrect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y;
src.LowerRightCorner.Y = middle.UpperLeftCorner.Y; src.LowerRightCorner.Y = srcrect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y;
break; break;
case 1: case 1:
dest.UpperLeftCorner.Y += middle.UpperLeftCorner.Y; dest.UpperLeftCorner.Y += middle.UpperLeftCorner.Y;
dest.LowerRightCorner.Y -= lowerRightOffset.Y; dest.LowerRightCorner.Y -= lower_right_offset.Y;
src.UpperLeftCorner.Y = middle.UpperLeftCorner.Y; src.UpperLeftCorner.Y += middle.UpperLeftCorner.Y;
src.LowerRightCorner.Y = middle.LowerRightCorner.Y; src.LowerRightCorner.Y -= lower_right_offset.Y;
break; break;
case 2: case 2:
dest.UpperLeftCorner.Y = rect.LowerRightCorner.Y - lowerRightOffset.Y; dest.UpperLeftCorner.Y = destrect.LowerRightCorner.Y - lower_right_offset.Y;
src.UpperLeftCorner.Y = middle.LowerRightCorner.Y; src.UpperLeftCorner.Y = srcrect.LowerRightCorner.Y - lower_right_offset.Y;
break; break;
} }

View File

@ -46,13 +46,13 @@ video::ITexture *guiScalingImageButton(video::IVideoDriver *driver, video::IText
*/ */
void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
const core::rect<s32> &destrect, const core::rect<s32> &srcrect, const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
const core::rect<s32> *cliprect = 0, const video::SColor *const colors = 0, const core::rect<s32> *cliprect = nullptr,
bool usealpha = false); const video::SColor *const colors = nullptr, bool usealpha = false);
/* /*
* 9-slice / segment drawing * 9-slice / segment drawing
*/ */
void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture,
const core::rect<s32> &rect, const core::rect<s32> &middle, const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
const core::rect<s32> *cliprect = nullptr, const core::rect<s32> &middlerect, const core::rect<s32> *cliprect = nullptr,
const video::SColor *const colors = nullptr); const video::SColor *const colors = nullptr);

View File

@ -45,6 +45,7 @@ public:
BGIMG_PRESSED, // Note: Deprecated property BGIMG_PRESSED, // Note: Deprecated property
FGIMG, FGIMG,
FGIMG_HOVERED, // Note: Deprecated property FGIMG_HOVERED, // Note: Deprecated property
FGIMG_MIDDLE,
FGIMG_PRESSED, // Note: Deprecated property FGIMG_PRESSED, // Note: Deprecated property
ALPHA, ALPHA,
CONTENT_OFFSET, CONTENT_OFFSET,
@ -101,6 +102,8 @@ public:
return FGIMG; return FGIMG;
} else if (name == "fgimg_hovered") { } else if (name == "fgimg_hovered") {
return FGIMG_HOVERED; return FGIMG_HOVERED;
} else if (name == "fgimg_middle") {
return FGIMG_MIDDLE;
} else if (name == "fgimg_pressed") { } else if (name == "fgimg_pressed") {
return FGIMG_PRESSED; return FGIMG_PRESSED;
} else if (name == "alpha") { } else if (name == "alpha") {

View File

@ -9,40 +9,37 @@
#include <vector> #include <vector>
GUIAnimatedImage::GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, GUIAnimatedImage::GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
s32 id, const core::rect<s32> &rectangle, const std::string &texture_name, s32 id, const core::rect<s32> &rectangle) :
s32 frame_count, s32 frame_duration, ISimpleTextureSource *tsrc) : gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle)
gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), m_tsrc(tsrc)
{ {
m_texture = m_tsrc->getTexture(texture_name);
m_frame_count = std::max(frame_count, 1);
m_frame_duration = std::max(frame_duration, 0);
if (m_texture != nullptr) {
core::dimension2d<u32> size = m_texture->getOriginalSize();
if (size.Height < (u64)m_frame_count)
m_frame_count = size.Height;
} else {
// No need to step an animation if we have nothing to draw
m_frame_count = 1;
}
} }
void GUIAnimatedImage::draw() void GUIAnimatedImage::draw()
{ {
// Render the current frame if (m_texture == nullptr)
if (m_texture != nullptr) { return;
video::IVideoDriver *driver = Environment->getVideoDriver();
video::IVideoDriver *driver = Environment->getVideoDriver();
core::dimension2d<u32> size = m_texture->getOriginalSize();
if ((u32)m_frame_count > size.Height)
m_frame_count = size.Height;
if (m_frame_idx >= m_frame_count)
m_frame_idx = m_frame_count - 1;
size.Height /= m_frame_count;
core::rect<s32> rect(core::position2d<s32>(0, size.Height * m_frame_idx), size);
core::rect<s32> *cliprect = NoClip ? nullptr : &AbsoluteClippingRect;
if (m_middle.getArea() == 0) {
const video::SColor color(255, 255, 255, 255); const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color}; const video::SColor colors[] = {color, color, color, color};
draw2DImageFilterScaled(driver, m_texture, AbsoluteRect, rect, cliprect,
core::dimension2d<u32> size = m_texture->getOriginalSize(); colors, true);
size.Height /= m_frame_count; } else {
draw2DImage9Slice(driver, m_texture, AbsoluteRect, rect, m_middle, cliprect);
draw2DImageFilterScaled(driver, m_texture, AbsoluteRect,
core::rect<s32>(core::position2d<s32>(0, size.Height * m_frame_idx), size),
NoClip ? nullptr : &AbsoluteClippingRect, colors, true);
} }
// Step the animation // Step the animation
@ -55,7 +52,7 @@ void GUIAnimatedImage::draw()
m_global_time = new_global_time; m_global_time = new_global_time;
// Advance by the number of elapsed frames, looping if necessary // Advance by the number of elapsed frames, looping if necessary
m_frame_idx += u32(m_frame_time / m_frame_duration); m_frame_idx += (u32)(m_frame_time / m_frame_duration);
m_frame_idx %= m_frame_count; m_frame_idx %= m_frame_count;
// If 1 or more frames have elapsed, reset the frame time counter with // If 1 or more frames have elapsed, reset the frame time counter with
@ -63,11 +60,3 @@ void GUIAnimatedImage::draw()
m_frame_time %= m_frame_duration; m_frame_time %= m_frame_duration;
} }
} }
void GUIAnimatedImage::setFrameIndex(s32 frame)
{
s32 idx = std::max(frame, 0);
if (idx > 0 && idx < m_frame_count)
m_frame_idx = idx;
}

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include <algorithm>
#include <string> #include <string>
class ISimpleTextureSource; class ISimpleTextureSource;
@ -8,21 +9,33 @@ class ISimpleTextureSource;
class GUIAnimatedImage : public gui::IGUIElement { class GUIAnimatedImage : public gui::IGUIElement {
public: public:
GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
s32 id, const core::rect<s32> &rectangle, const std::string &texture_name, s32 id, const core::rect<s32> &rectangle);
s32 frame_count, s32 frame_duration, ISimpleTextureSource *tsrc);
virtual void draw() override; virtual void draw() override;
void setFrameIndex(s32 frame); void setTexture(video::ITexture *texture) { m_texture = texture; };
video::ITexture *getTexture() const { return m_texture; };
void setMiddleRect(const core::rect<s32> &middle) { m_middle = middle; };
core::rect<s32> getMiddleRect() const { return m_middle; };
void setFrameDuration(u64 duration) { m_frame_duration = duration; };
u64 getFrameDuration() const { return m_frame_duration; };
void setFrameCount(s32 count) { m_frame_count = std::max(count, 1); };
s32 getFrameCount() const { return m_frame_count; };
void setFrameIndex(s32 frame) { m_frame_idx = std::max(frame, 0); };
s32 getFrameIndex() const { return m_frame_idx; }; s32 getFrameIndex() const { return m_frame_idx; };
private: private:
ISimpleTextureSource *m_tsrc;
video::ITexture *m_texture = nullptr; video::ITexture *m_texture = nullptr;
u64 m_global_time = 0; u64 m_global_time = 0;
s32 m_frame_idx = 0; s32 m_frame_idx = 0;
s32 m_frame_count = 1; s32 m_frame_count = 1;
u64 m_frame_duration = 1; u64 m_frame_duration = 0;
u64 m_frame_time = 0; u64 m_frame_time = 0;
core::rect<s32> m_middle;
}; };

View File

@ -48,21 +48,15 @@ void GUIBackgroundImage::draw()
video::IVideoDriver *driver = Environment->getVideoDriver(); video::IVideoDriver *driver = Environment->getVideoDriver();
core::rect<s32> srcrect(core::position2d<s32>(0, 0),
core::dimension2di(texture->getOriginalSize()));
if (m_middle.getArea() == 0) { if (m_middle.getArea() == 0) {
const video::SColor color(255, 255, 255, 255); const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color}; const video::SColor colors[] = {color, color, color, color};
draw2DImageFilterScaled(driver, texture, rect, draw2DImageFilterScaled(driver, texture, rect, srcrect, nullptr, colors, true);
core::rect<s32>(core::position2d<s32>(0, 0),
core::dimension2di(texture->getOriginalSize())),
nullptr, colors, true);
} else { } else {
core::rect<s32> middle = m_middle; draw2DImage9Slice(driver, texture, rect, srcrect, m_middle);
// `-x` is interpreted as `w - x`
if (middle.LowerRightCorner.X < 0)
middle.LowerRightCorner.X += texture->getOriginalSize().Width;
if (middle.LowerRightCorner.Y < 0)
middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
draw2DImage9Slice(driver, texture, rect, middle);
} }
IGUIElement::draw(); IGUIElement::draw();

View File

@ -320,15 +320,9 @@ void GUIButton::draw()
sourceRect, &AbsoluteClippingRect, sourceRect, &AbsoluteClippingRect,
image_colors, UseAlphaChannel); image_colors, UseAlphaChannel);
} else { } else {
core::rect<s32> middle = BgMiddle;
// `-x` is interpreted as `w - x`
if (middle.LowerRightCorner.X < 0)
middle.LowerRightCorner.X += texture->getOriginalSize().Width;
if (middle.LowerRightCorner.Y < 0)
middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
draw2DImage9Slice(driver, texture, draw2DImage9Slice(driver, texture,
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()), ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
middle, &AbsoluteClippingRect, image_colors); sourceRect, BgMiddle, &AbsoluteClippingRect, image_colors);
} }
// END PATCH // END PATCH
} }

View File

@ -32,15 +32,15 @@ using namespace gui;
GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment,
gui::IGUIElement *parent, s32 id, core::rect<s32> rectangle, gui::IGUIElement *parent, s32 id, core::rect<s32> rectangle,
ISimpleTextureSource *tsrc, bool noclip) ISimpleTextureSource *tsrc, bool noclip)
: GUIButton (environment, parent, id, rectangle, tsrc, noclip) : GUIButton(environment, parent, id, rectangle, tsrc, noclip)
{ {
m_image = Environment->addImage( GUIButton::setScaleImage(true);
core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), this); m_image = new GUIAnimatedImage(environment, this, id, rectangle);
m_image->setScaleImage(isScalingImage());
sendToBack(m_image); sendToBack(m_image);
} }
void GUIButtonImage::setForegroundImage(video::ITexture *image) void GUIButtonImage::setForegroundImage(video::ITexture *image,
const core::rect<s32> &middle)
{ {
if (image == m_foreground_image) if (image == m_foreground_image)
return; return;
@ -52,11 +52,12 @@ void GUIButtonImage::setForegroundImage(video::ITexture *image)
m_foreground_image->drop(); m_foreground_image->drop();
m_foreground_image = image; m_foreground_image = image;
m_image->setImage(image); m_image->setTexture(image);
m_image->setMiddleRect(middle);
} }
//! Set element properties from a StyleSpec //! Set element properties from a StyleSpec
void GUIButtonImage::setFromStyle(const StyleSpec& style) void GUIButtonImage::setFromStyle(const StyleSpec &style)
{ {
GUIButton::setFromStyle(style); GUIButton::setFromStyle(style);
@ -67,19 +68,13 @@ void GUIButtonImage::setFromStyle(const StyleSpec& style)
getTextureSource()); getTextureSource());
setForegroundImage(guiScalingImageButton(driver, texture, setForegroundImage(guiScalingImageButton(driver, texture,
AbsoluteRect.getWidth(), AbsoluteRect.getHeight())); AbsoluteRect.getWidth(), AbsoluteRect.getHeight()),
setScaleImage(true); style.getRect(StyleSpec::FGIMG_MIDDLE, m_image->getMiddleRect()));
} else { } else {
setForegroundImage(nullptr); setForegroundImage();
} }
} }
void GUIButtonImage::setScaleImage(bool scaleImage)
{
GUIButton::setScaleImage(scaleImage);
m_image->setScaleImage(scaleImage);
}
GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment, GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment,
const core::rect<s32> &rectangle, ISimpleTextureSource *tsrc, const core::rect<s32> &rectangle, ISimpleTextureSource *tsrc,
IGUIElement *parent, s32 id, const wchar_t *text, IGUIElement *parent, s32 id, const wchar_t *text,

View File

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiButton.h" #include "guiButton.h"
#include "IGUIButton.h" #include "IGUIButton.h"
#include "guiAnimatedImage.h"
using namespace irr; using namespace irr;
@ -32,12 +33,11 @@ public:
s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc, s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc,
bool noclip = false); bool noclip = false);
void setForegroundImage(video::ITexture *image = nullptr); void setForegroundImage(video::ITexture *image = nullptr,
const core::rect<s32> &middle = core::rect<s32>());
//! Set element properties from a StyleSpec //! Set element properties from a StyleSpec
virtual void setFromStyle(const StyleSpec& style) override; virtual void setFromStyle(const StyleSpec &style) override;
virtual void setScaleImage(bool scaleImage=true) override;
//! Do not drop returned handle //! Do not drop returned handle
static GUIButtonImage *addButton(gui::IGUIEnvironment *environment, static GUIButtonImage *addButton(gui::IGUIEnvironment *environment,
@ -47,5 +47,5 @@ public:
private: private:
video::ITexture *m_foreground_image = nullptr; video::ITexture *m_foreground_image = nullptr;
gui::IGUIImage *m_image; GUIAnimatedImage *m_image;
}; };

View File

@ -802,8 +802,8 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
{ {
std::vector<std::string> parts = split(element,';'); std::vector<std::string> parts = split(element,';');
if ((parts.size() == 3) || if (parts.size() == 3 || parts.size() == 4 ||
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) (parts.size() > 4 && m_formspec_version > FORMSPEC_API_VERSION))
{ {
std::vector<std::string> v_pos = split(parts[0],','); std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_geom = split(parts[1],','); std::vector<std::string> v_geom = split(parts[1],',');
@ -825,14 +825,8 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
} }
if (!data->explicit_size) if (!data->explicit_size)
warningstream<<"invalid use of image without a size[] element"<<std::endl; warningstream << "Invalid use of image without a size[] element"
<< std::endl;
video::ITexture *texture = m_tsrc->getTexture(name);
if (!texture) {
errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:"
<< std::endl << "\t" << name << std::endl;
return;
}
FieldSpec spec( FieldSpec spec(
name, name,
@ -841,54 +835,26 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
258 + m_fields.size(), 258 + m_fields.size(),
1 1
); );
core::rect<s32> rect(pos, pos + geom);
gui::IGUIImage *e = Environment->addImage(rect, data->current_parent, core::rect<s32> rect = core::rect<s32>(pos, pos + geom);
spec.fid, 0, true);
e->setImage(texture); core::rect<s32> middle;
e->setScaleImage(true); if (parts.size() >= 4)
parseMiddleRect(parts[3], &middle);
GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent,
spec.fid, rect);
e->setTexture(m_tsrc->getTexture(name));
e->setMiddleRect(middle);
auto style = getDefaultStyleForElement("image", spec.fname); auto style = getDefaultStyleForElement("image", spec.fname);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
m_fields.push_back(spec);
// images should let events through // Animated images should let events through
e->grab();
m_clickthrough_elements.push_back(e); m_clickthrough_elements.push_back(e);
return;
}
if (parts.size() == 2) {
std::vector<std::string> v_pos = split(parts[0],',');
std::string name = unescape_string(parts[1]);
MY_CHECKPOS("image", 0);
v2s32 pos = getElementBasePos(&v_pos);
if (!data->explicit_size)
warningstream<<"invalid use of image without a size[] element"<<std::endl;
video::ITexture *texture = m_tsrc->getTexture(name);
if (!texture) {
errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:"
<< std::endl << "\t" << name << std::endl;
return;
}
FieldSpec spec(
name,
L"",
L"",
258 + m_fields.size()
);
gui::IGUIImage *e = Environment->addImage(texture, pos, true,
data->current_parent, spec.fid, 0);
auto style = getDefaultStyleForElement("image", spec.fname);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
m_fields.push_back(spec); m_fields.push_back(spec);
// images should let events through
e->grab();
m_clickthrough_elements.push_back(e);
return; return;
} }
errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl; errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
@ -898,8 +864,8 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el
{ {
std::vector<std::string> parts = split(element, ';'); std::vector<std::string> parts = split(element, ';');
if (parts.size() != 6 && parts.size() != 7 && if (parts.size() < 6 ||
!(parts.size() > 7 && m_formspec_version > FORMSPEC_API_VERSION)) { (parts.size() > 8 && m_formspec_version <= FORMSPEC_API_VERSION)) {
errorstream << "Invalid animated_image element(" << parts.size() errorstream << "Invalid animated_image element(" << parts.size()
<< "): '" << element << "'" << std::endl; << "): '" << element << "'" << std::endl;
return; return;
@ -928,7 +894,8 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el
} }
if (!data->explicit_size) if (!data->explicit_size)
warningstream << "Invalid use of animated_image without a size[] element" << std::endl; warningstream << "Invalid use of animated_image without a size[] element"
<< std::endl;
FieldSpec spec( FieldSpec spec(
name, name,
@ -941,9 +908,17 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el
core::rect<s32> rect = core::rect<s32>(pos, pos + geom); core::rect<s32> rect = core::rect<s32>(pos, pos + geom);
GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, spec.fid, core::rect<s32> middle;
rect, texture_name, frame_count, frame_duration, m_tsrc); if (parts.size() >= 8)
parseMiddleRect(parts[7], &middle);
GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent,
spec.fid, rect);
e->setTexture(m_tsrc->getTexture(texture_name));
e->setMiddleRect(middle);
e->setFrameDuration(frame_duration);
e->setFrameCount(frame_count);
if (parts.size() >= 7) if (parts.size() >= 7)
e->setFrameIndex(stoi(parts[6]) - 1); e->setFrameIndex(stoi(parts[6]) - 1);
@ -1076,6 +1051,35 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl; errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl;
} }
bool GUIFormSpecMenu::parseMiddleRect(const std::string &value, core::rect<s32> *parsed_rect)
{
core::rect<s32> rect;
std::vector<std::string> v_rect = split(value, ',');
if (v_rect.size() == 1) {
s32 x = stoi(v_rect[0]);
rect.UpperLeftCorner = core::vector2di(x, x);
rect.LowerRightCorner = core::vector2di(-x, -x);
} else if (v_rect.size() == 2) {
s32 x = stoi(v_rect[0]);
s32 y = stoi(v_rect[1]);
rect.UpperLeftCorner = core::vector2di(x, y);
rect.LowerRightCorner = core::vector2di(-x, -y);
// `-x` is interpreted as `w - x`
} else if (v_rect.size() == 4) {
rect.UpperLeftCorner = core::vector2di(stoi(v_rect[0]), stoi(v_rect[1]));
rect.LowerRightCorner = core::vector2di(stoi(v_rect[2]), stoi(v_rect[3]));
} else {
warningstream << "Invalid rectangle string format: \"" << value
<< "\"" << std::endl;
return false;
}
*parsed_rect = rect;
return true;
}
void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element) void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element)
{ {
std::vector<std::string> parts = split(element,';'); std::vector<std::string> parts = split(element,';');
@ -1117,25 +1121,8 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme
} }
core::rect<s32> middle; core::rect<s32> middle;
if (parts.size() >= 5) { if (parts.size() >= 5)
std::vector<std::string> v_middle = split(parts[4], ','); parseMiddleRect(parts[4], &middle);
if (v_middle.size() == 1) {
s32 x = stoi(v_middle[0]);
middle.UpperLeftCorner = core::vector2di(x, x);
middle.LowerRightCorner = core::vector2di(-x, -x);
} else if (v_middle.size() == 2) {
s32 x = stoi(v_middle[0]);
s32 y = stoi(v_middle[1]);
middle.UpperLeftCorner = core::vector2di(x, y);
middle.LowerRightCorner = core::vector2di(-x, -y);
// `-x` is interpreted as `w - x`
} else if (v_middle.size() == 4) {
middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1]));
middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3]));
} else {
warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl;
}
}
if (!data->explicit_size && !clip) if (!data->explicit_size && !clip)
warningstream << "invalid use of unclipped background without a size[] element" << std::endl; warningstream << "invalid use of unclipped background without a size[] element" << std::endl;

View File

@ -452,6 +452,8 @@ private:
void parseSetFocus(const std::string &element); void parseSetFocus(const std::string &element);
void parseModel(parserData *data, const std::string &element); void parseModel(parserData *data, const std::string &element);
bool parseMiddleRect(const std::string &value, core::rect<s32> *parsed_rect);
void tryClose(); void tryClose();
void showTooltip(const std::wstring &text, const irr::video::SColor &color, void showTooltip(const std::wstring &text, const irr::video::SColor &color,