diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index ff291e54b..4c15ba578 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -56,7 +56,7 @@ public: void setRotationOrigin(float originX, float originY); inline void setRotationOrigin(Vector2f origin) { setRotationOrigin(origin.x(), origin.y()); } - Vector2f getSize() const; + virtual Vector2f getSize() const; inline void setSize(const Vector2f& size) { setSize(size.x(), size.y()); } void setSize(float w, float h); virtual void onSizeChanged() {}; diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 7dddba271..a416b76ed 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -14,9 +14,15 @@ Vector2i ImageComponent::getTextureSize() const return Vector2i::Zero(); } +Vector2f ImageComponent::getSize() const +{ + return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop); +} + ImageComponent::ImageComponent(Window* window, bool forceLoad, bool dynamic) : GuiComponent(window), - mTargetIsMax(false), mFlipX(false), mFlipY(false), mTargetSize(0, 0), mColorShift(0xFFFFFFFF), - mForceLoad(forceLoad), mDynamic(dynamic), mFadeOpacity(0), mFading(false), mRotateByTargetSize(false) + mTargetIsMax(false), mTargetIsMin(false), mFlipX(false), mFlipY(false), mTargetSize(0, 0), mColorShift(0xFFFFFFFF), + mForceLoad(forceLoad), mDynamic(dynamic), mFadeOpacity(0), mFading(false), mRotateByTargetSize(false), + mTopLeftCrop(0.0f, 0.0f), mBottomRightCrop(1.0f, 1.0f) { updateColors(); } @@ -63,6 +69,31 @@ void ImageComponent::resize() mSize[1] = Math::round(mSize[1]); mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); + }else if(mTargetIsMin) + { + mSize = textureSize; + + Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y())); + + if(resizeScale.x() > resizeScale.y()) + { + mSize[0] *= resizeScale.x(); + mSize[1] *= resizeScale.x(); + + float cropPercent = (mSize.y() - mTargetSize.y()) / (mSize.y() * 2); + crop(0, cropPercent, 0, cropPercent); + }else{ + mSize[0] *= resizeScale.y(); + mSize[1] *= resizeScale.y(); + + float cropPercent = (mSize.x() - mTargetSize.x()) / (mSize.x() * 2); + crop(cropPercent, 0, cropPercent, 0); + } + + // for SVG rasterization, always calculate width from rounded height (see comment above) + mSize[1] = Math::round(mSize[1]); + mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); + }else{ // if both components are set, we just stretch // if no components are set, we don't resize at all @@ -132,6 +163,7 @@ void ImageComponent::setResize(float width, float height) { mTargetSize = Vector2f(width, height); mTargetIsMax = false; + mTargetIsMin = false; resize(); } @@ -139,6 +171,15 @@ void ImageComponent::setMaxSize(float width, float height) { mTargetSize = Vector2f(width, height); mTargetIsMax = true; + mTargetIsMin = false; + resize(); +} + +void ImageComponent::setMinSize(float width, float height) +{ + mTargetSize = Vector2f(width, height); + mTargetIsMax = false; + mTargetIsMin = true; resize(); } @@ -152,6 +193,43 @@ void ImageComponent::setRotateByTargetSize(bool rotate) mRotateByTargetSize = rotate; } +void ImageComponent::cropLeft(float percent) +{ + assert(percent >= 0.0f && percent <= 1.0f); + mTopLeftCrop.x() = percent; +} + +void ImageComponent::cropTop(float percent) +{ + assert(percent >= 0.0f && percent <= 1.0f); + mTopLeftCrop.y() = percent; +} + +void ImageComponent::cropRight(float percent) +{ + assert(percent >= 0.0f && percent <= 1.0f); + mBottomRightCrop.x() = 1.0f - percent; +} + +void ImageComponent::cropBot(float percent) +{ + assert(percent >= 0.0f && percent <= 1.0f); + mBottomRightCrop.y() = 1.0f - percent; +} + +void ImageComponent::crop(float left, float top, float right, float bot) +{ + cropLeft(left); + cropTop(top); + cropRight(right); + cropBot(bot); +} + +void ImageComponent::uncrop() +{ + crop(0, 0, 0, 0); +} + void ImageComponent::setFlipX(bool flip) { mFlipX = flip; @@ -187,8 +265,9 @@ void ImageComponent::updateVertices() // we go through this mess to make sure everything is properly rounded // if we just round vertices at the end, edge cases occur near sizes of 0.5 - Vector2f topLeft(0.0, 0.0); - Vector2f bottomRight(Math::round(mSize.x()), Math::round(mSize.y())); + Vector2f size(Math::round(mSize.x()), Math::round(mSize.y())); + Vector2f topLeft(size * mTopLeftCrop); + Vector2f bottomRight(size * mBottomRightCrop); mVertices[0].pos = Vector2f(topLeft.x(), topLeft.y()); mVertices[1].pos = Vector2f(topLeft.x(), bottomRight.y()); @@ -208,23 +287,23 @@ void ImageComponent::updateVertices() py = 1; } - mVertices[0].tex = Vector2f(0, py); - mVertices[1].tex = Vector2f(0, 0); - mVertices[2].tex = Vector2f(px, py); + mVertices[0].tex = Vector2f(mTopLeftCrop.x(), py - mTopLeftCrop.y()); + mVertices[1].tex = Vector2f(mTopLeftCrop.x(), 1 - mBottomRightCrop.y()); + mVertices[2].tex = Vector2f(px * mBottomRightCrop.x(), py - mTopLeftCrop.y()); - mVertices[3].tex = Vector2f(px, py); - mVertices[4].tex = Vector2f(0, 0); - mVertices[5].tex = Vector2f(px, 0); + mVertices[3].tex = Vector2f(px * mBottomRightCrop.x(), py - mTopLeftCrop.y()); + mVertices[4].tex = Vector2f(mTopLeftCrop.x(), 1 - mBottomRightCrop.y()); + mVertices[5].tex = Vector2f(px * mBottomRightCrop.x(), 1 - mBottomRightCrop.y()); if(mFlipX) { for(int i = 0; i < 6; i++) - mVertices[i].tex[0] = mVertices[i].tex[0] == px ? 0 : px; + mVertices[i].tex[0] = px - mVertices[i].tex[0]; } if(mFlipY) { for(int i = 0; i < 6; i++) - mVertices[i].tex[1] = mVertices[i].tex[1] == py ? 0 : py; + mVertices[i].tex[1] = py - mVertices[i].tex[1]; } } @@ -352,6 +431,8 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, const s setResize(elem->get("size") * scale); else if(elem->has("maxSize")) setMaxSize(elem->get("maxSize") * scale); + else if(elem->has("minSize")) + setMinSize(elem->get("minSize") * scale); } // position + size also implies origin diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index b557c9b5c..7a93a0d85 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -40,8 +40,20 @@ public: void setMaxSize(float width, float height); inline void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); } + void setMinSize(float width, float height); + inline void setMinSize(const Vector2f& size) { setMinSize(size.x(), size.y()); } + Vector2f getRotationSize() const override; + // Applied AFTER image positioning and sizing + // cropTop(0.2) will crop 20% of the top of the image. + void cropLeft(float percent); + void cropTop(float percent); + void cropRight(float percent); + void cropBot(float percent); + void crop(float left, float top, float right, float bot); + void uncrop(); + // Multiply all pixels in the image by this color when rendering. void setColorShift(unsigned int color); @@ -53,6 +65,8 @@ public: // Returns the size of the current texture, or (0, 0) if none is loaded. May be different than drawn size (use getSize() for that). Vector2i getTextureSize() const; + Vector2f getSize() const override; + bool hasImage(); void render(const Transform4x4f& parentTrans) override; @@ -63,7 +77,7 @@ public: private: Vector2f mTargetSize; - bool mFlipX, mFlipY, mTargetIsMax; + bool mFlipX, mFlipY, mTargetIsMax, mTargetIsMin; // Calculates the correct mSize from our resizing information (set by setResize/setMaxSize). // Used internally whenever the resizing parameters or texture change. @@ -86,11 +100,14 @@ private: std::string mDefaultPath; std::shared_ptr mTexture; - unsigned char mFadeOpacity; - bool mFading; - bool mForceLoad; + unsigned char mFadeOpacity; + bool mFading; + bool mForceLoad; bool mDynamic; bool mRotateByTargetSize; + + Vector2f mTopLeftCrop; + Vector2f mBottomRightCrop; }; #endif // ES_CORE_COMPONENTS_IMAGE_COMPONENT_H