2014-06-20 01:30:09 +00:00
|
|
|
#include "components/ImageComponent.h"
|
2012-08-02 01:43:55 +00:00
|
|
|
#include <iostream>
|
2012-08-09 21:19:07 +00:00
|
|
|
#include <boost/filesystem.hpp>
|
2012-09-07 21:44:07 +00:00
|
|
|
#include <math.h>
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "Log.h"
|
|
|
|
#include "Renderer.h"
|
|
|
|
#include "ThemeData.h"
|
|
|
|
#include "Util.h"
|
|
|
|
#include "resources/SVGResource.h"
|
2012-08-02 01:43:55 +00:00
|
|
|
|
2013-07-10 11:29:43 +00:00
|
|
|
Eigen::Vector2i ImageComponent::getTextureSize() const
|
2013-06-21 16:49:29 +00:00
|
|
|
{
|
|
|
|
if(mTexture)
|
|
|
|
return mTexture->getSize();
|
|
|
|
else
|
2013-07-10 11:29:43 +00:00
|
|
|
return Eigen::Vector2i(0, 0);
|
2013-06-21 16:49:29 +00:00
|
|
|
}
|
2012-08-09 21:19:07 +00:00
|
|
|
|
2013-08-06 13:15:20 +00:00
|
|
|
Eigen::Vector2f ImageComponent::getCenter() const
|
|
|
|
{
|
|
|
|
return Eigen::Vector2f(mPosition.x() - (getSize().x() * mOrigin.x()) + getSize().x() / 2,
|
|
|
|
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2);
|
|
|
|
}
|
|
|
|
|
2014-01-24 22:21:10 +00:00
|
|
|
ImageComponent::ImageComponent(Window* window) : GuiComponent(window),
|
2014-01-19 18:23:01 +00:00
|
|
|
mTargetIsMax(false), mFlipX(false), mFlipY(false), mOrigin(0.0, 0.0), mTargetSize(0, 0), mColorShift(0xFFFFFFFF)
|
2012-08-02 01:43:55 +00:00
|
|
|
{
|
2014-04-12 01:48:13 +00:00
|
|
|
updateColors();
|
2012-08-02 01:43:55 +00:00
|
|
|
}
|
|
|
|
|
2013-06-02 15:08:32 +00:00
|
|
|
ImageComponent::~ImageComponent()
|
2012-08-02 01:43:55 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-06-21 16:49:29 +00:00
|
|
|
void ImageComponent::resize()
|
2012-08-09 21:19:07 +00:00
|
|
|
{
|
2013-06-21 16:49:29 +00:00
|
|
|
if(!mTexture)
|
2012-08-29 18:53:53 +00:00
|
|
|
return;
|
2012-08-29 19:22:05 +00:00
|
|
|
|
2014-05-23 21:51:56 +00:00
|
|
|
SVGResource* svg = dynamic_cast<SVGResource*>(mTexture.get());
|
|
|
|
|
|
|
|
const Eigen::Vector2f textureSize = svg ? svg->getSourceImageSize() : Eigen::Vector2f((float)mTexture->getSize().x(), (float)mTexture->getSize().y());
|
2014-05-26 22:52:55 +00:00
|
|
|
if(textureSize.isZero())
|
|
|
|
return;
|
2012-08-29 21:52:25 +00:00
|
|
|
|
2014-01-19 18:23:01 +00:00
|
|
|
if(mTexture->isTiled())
|
2014-01-10 23:45:47 +00:00
|
|
|
{
|
|
|
|
mSize = mTargetSize;
|
|
|
|
}else{
|
2014-05-23 21:51:56 +00:00
|
|
|
// SVG rasterization is determined by height (see SVGResource.cpp), and rasterization is done in terms of pixels
|
|
|
|
// if rounding is off enough in the rasterization step (for images with extreme aspect ratios), it can cause cutoff when the aspect ratio breaks
|
|
|
|
// so, we always make sure the resultant height is an integer to make sure cutoff doesn't happen, and scale width from that
|
|
|
|
// (you'll see this scattered throughout the function)
|
|
|
|
// this is probably not the best way, so if you're familiar with this problem and have a better solution, please make a pull request!
|
|
|
|
|
2014-01-10 23:45:47 +00:00
|
|
|
if(mTargetIsMax)
|
2013-06-02 21:05:29 +00:00
|
|
|
{
|
2014-01-10 23:45:47 +00:00
|
|
|
mSize = textureSize;
|
2012-08-29 21:52:25 +00:00
|
|
|
|
2014-01-10 23:45:47 +00:00
|
|
|
Eigen::Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
|
|
|
|
|
|
|
if(resizeScale.x() < resizeScale.y())
|
|
|
|
{
|
|
|
|
mSize[0] *= resizeScale.x();
|
|
|
|
mSize[1] *= resizeScale.x();
|
|
|
|
}else{
|
|
|
|
mSize[0] *= resizeScale.y();
|
|
|
|
mSize[1] *= resizeScale.y();
|
|
|
|
}
|
2012-08-29 21:52:25 +00:00
|
|
|
|
2014-05-23 21:51:56 +00:00
|
|
|
// for SVG rasterization, always calculate width from rounded height (see comment above)
|
|
|
|
mSize[1] = round(mSize[1]);
|
|
|
|
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
|
|
|
|
2014-01-10 23:45:47 +00:00
|
|
|
}else{
|
|
|
|
// if both components are set, we just stretch
|
|
|
|
// if no components are set, we don't resize at all
|
|
|
|
mSize = mTargetSize.isZero() ? textureSize : mTargetSize;
|
|
|
|
|
|
|
|
// if only one component is set, we resize in a way that maintains aspect ratio
|
2014-05-23 21:51:56 +00:00
|
|
|
// for SVG rasterization, we always calculate width from rounded height (see comment above)
|
2014-01-10 23:45:47 +00:00
|
|
|
if(!mTargetSize.x() && mTargetSize.y())
|
|
|
|
{
|
2014-05-23 21:51:56 +00:00
|
|
|
mSize[1] = round(mTargetSize.y());
|
|
|
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
2014-01-10 23:45:47 +00:00
|
|
|
}else if(mTargetSize.x() && !mTargetSize.y())
|
|
|
|
{
|
2014-05-23 21:51:56 +00:00
|
|
|
mSize[1] = round((mTargetSize.x() / textureSize.x()) * textureSize.y());
|
|
|
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
2014-01-10 23:45:47 +00:00
|
|
|
}
|
|
|
|
}
|
2012-08-29 21:52:25 +00:00
|
|
|
}
|
2014-03-20 01:13:59 +00:00
|
|
|
|
|
|
|
if(svg)
|
|
|
|
{
|
2014-05-23 21:51:56 +00:00
|
|
|
// mSize.y() should already be rounded
|
2014-03-22 21:02:25 +00:00
|
|
|
svg->rasterizeAt((int)round(mSize.x()), (int)round(mSize.y()));
|
2014-03-20 01:13:59 +00:00
|
|
|
}
|
2014-04-12 01:48:13 +00:00
|
|
|
|
|
|
|
onSizeChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageComponent::onSizeChanged()
|
|
|
|
{
|
|
|
|
updateVertices();
|
2012-08-13 18:32:53 +00:00
|
|
|
}
|
|
|
|
|
2014-01-19 18:23:01 +00:00
|
|
|
void ImageComponent::setImage(std::string path, bool tile)
|
2012-08-09 21:19:07 +00:00
|
|
|
{
|
2013-11-12 23:28:15 +00:00
|
|
|
if(path.empty() || !ResourceManager::getInstance()->fileExists(path))
|
2013-07-09 05:44:24 +00:00
|
|
|
mTexture.reset();
|
|
|
|
else
|
2014-01-19 18:23:01 +00:00
|
|
|
mTexture = TextureResource::get(path, tile);
|
2012-08-02 01:43:55 +00:00
|
|
|
|
2013-11-12 23:28:15 +00:00
|
|
|
resize();
|
|
|
|
}
|
|
|
|
|
2014-01-19 18:23:01 +00:00
|
|
|
void ImageComponent::setImage(const char* path, size_t length, bool tile)
|
2013-09-20 23:55:05 +00:00
|
|
|
{
|
|
|
|
mTexture.reset();
|
|
|
|
|
2014-01-19 18:23:01 +00:00
|
|
|
mTexture = TextureResource::get("", tile);
|
2013-09-20 23:55:05 +00:00
|
|
|
mTexture->initFromMemory(path, length);
|
2014-01-19 18:23:01 +00:00
|
|
|
|
2013-09-20 23:55:05 +00:00
|
|
|
resize();
|
|
|
|
}
|
|
|
|
|
2014-01-19 18:23:01 +00:00
|
|
|
void ImageComponent::setImage(const std::shared_ptr<TextureResource>& texture)
|
2012-08-13 18:32:53 +00:00
|
|
|
{
|
2014-01-19 18:23:01 +00:00
|
|
|
mTexture = texture;
|
|
|
|
resize();
|
2012-08-13 18:32:53 +00:00
|
|
|
}
|
|
|
|
|
2014-01-19 18:23:01 +00:00
|
|
|
void ImageComponent::setOrigin(float originX, float originY)
|
2012-08-13 18:32:53 +00:00
|
|
|
{
|
2014-01-19 18:23:01 +00:00
|
|
|
mOrigin << originX, originY;
|
2014-04-12 01:48:13 +00:00
|
|
|
updateVertices();
|
2014-01-10 23:45:47 +00:00
|
|
|
}
|
2013-06-02 22:33:49 +00:00
|
|
|
|
2014-01-10 23:45:47 +00:00
|
|
|
void ImageComponent::setResize(float width, float height)
|
|
|
|
{
|
|
|
|
mTargetSize << width, height;
|
|
|
|
mTargetIsMax = false;
|
2013-06-02 22:33:49 +00:00
|
|
|
resize();
|
2012-08-13 18:32:53 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 23:45:47 +00:00
|
|
|
void ImageComponent::setMaxSize(float width, float height)
|
2012-10-05 20:04:12 +00:00
|
|
|
{
|
2013-07-10 11:29:43 +00:00
|
|
|
mTargetSize << width, height;
|
2014-01-10 23:45:47 +00:00
|
|
|
mTargetIsMax = true;
|
2012-10-05 20:04:12 +00:00
|
|
|
resize();
|
|
|
|
}
|
|
|
|
|
2013-06-02 15:08:32 +00:00
|
|
|
void ImageComponent::setFlipX(bool flip)
|
2012-10-07 22:25:51 +00:00
|
|
|
{
|
|
|
|
mFlipX = flip;
|
2014-04-12 01:48:13 +00:00
|
|
|
updateVertices();
|
2012-10-07 22:25:51 +00:00
|
|
|
}
|
|
|
|
|
2013-06-02 15:08:32 +00:00
|
|
|
void ImageComponent::setFlipY(bool flip)
|
2012-10-07 22:25:51 +00:00
|
|
|
{
|
|
|
|
mFlipY = flip;
|
2014-04-12 01:48:13 +00:00
|
|
|
updateVertices();
|
2012-10-07 22:25:51 +00:00
|
|
|
}
|
|
|
|
|
2013-08-07 22:40:27 +00:00
|
|
|
void ImageComponent::setColorShift(unsigned int color)
|
|
|
|
{
|
|
|
|
mColorShift = color;
|
2014-04-12 01:48:13 +00:00
|
|
|
updateColors();
|
2013-08-07 22:40:27 +00:00
|
|
|
}
|
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
void ImageComponent::setOpacity(unsigned char opacity)
|
2012-08-02 01:43:55 +00:00
|
|
|
{
|
2014-04-12 01:48:13 +00:00
|
|
|
mOpacity = opacity;
|
|
|
|
mColorShift = (mColorShift >> 8 << 8) | mOpacity;
|
|
|
|
updateColors();
|
2012-08-29 21:52:25 +00:00
|
|
|
}
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
void ImageComponent::updateVertices()
|
2012-08-29 21:52:25 +00:00
|
|
|
{
|
2014-04-12 01:48:13 +00:00
|
|
|
if(!mTexture || !mTexture->isInitialized())
|
|
|
|
return;
|
|
|
|
|
2014-03-29 23:03:38 +00:00
|
|
|
// 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
|
|
|
|
Eigen::Vector2f topLeft(-mSize.x() * mOrigin.x(), -mSize.y() * mOrigin.y());
|
|
|
|
Eigen::Vector2f bottomRight(mSize.x() * (1 -mOrigin.x()), mSize.y() * (1 - mOrigin.y()));
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-03-29 23:03:38 +00:00
|
|
|
const float width = round(bottomRight.x() - topLeft.x());
|
|
|
|
const float height = round(bottomRight.y() - topLeft.y());
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-06-10 17:36:03 +00:00
|
|
|
topLeft[0] = floor(topLeft[0]);
|
|
|
|
topLeft[1] = floor(topLeft[1]);
|
2014-03-29 23:03:38 +00:00
|
|
|
bottomRight[0] = topLeft[0] + width;
|
|
|
|
bottomRight[1] = topLeft[1] + height;
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
mVertices[0].pos << topLeft.x(), topLeft.y();
|
|
|
|
mVertices[1].pos << topLeft.x(), bottomRight.y();
|
|
|
|
mVertices[2].pos << bottomRight.x(), topLeft.y();
|
|
|
|
|
|
|
|
mVertices[3].pos << bottomRight.x(), topLeft.y();
|
|
|
|
mVertices[4].pos << topLeft.x(), bottomRight.y();
|
|
|
|
mVertices[5].pos << bottomRight.x(), bottomRight.y();
|
2014-03-29 23:03:38 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
float px, py;
|
|
|
|
if(mTexture->isTiled())
|
|
|
|
{
|
|
|
|
px = mSize.x() / getTextureSize().x();
|
|
|
|
py = mSize.y() / getTextureSize().y();
|
|
|
|
}else{
|
|
|
|
px = 1;
|
|
|
|
py = 1;
|
|
|
|
}
|
2012-09-07 21:44:07 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
mVertices[0].tex << 0, py;
|
|
|
|
mVertices[1].tex << 0, 0;
|
|
|
|
mVertices[2].tex << px, py;
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
mVertices[3].tex << px, py;
|
|
|
|
mVertices[4].tex << 0, 0;
|
|
|
|
mVertices[5].tex << px, 0;
|
2012-10-07 22:25:51 +00:00
|
|
|
|
|
|
|
if(mFlipX)
|
|
|
|
{
|
2014-04-12 01:48:13 +00:00
|
|
|
for(int i = 0; i < 6; i++)
|
|
|
|
mVertices[i].tex[0] = mVertices[i].tex[0] == px ? 0 : px;
|
2012-10-07 22:25:51 +00:00
|
|
|
}
|
|
|
|
if(mFlipY)
|
|
|
|
{
|
2014-04-12 01:48:13 +00:00
|
|
|
for(int i = 1; i < 6; i++)
|
|
|
|
mVertices[i].tex[1] = mVertices[i].tex[1] == py ? 0 : py;
|
2012-10-07 22:25:51 +00:00
|
|
|
}
|
2012-09-07 21:44:07 +00:00
|
|
|
}
|
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
void ImageComponent::updateColors()
|
2012-09-07 21:44:07 +00:00
|
|
|
{
|
2014-05-24 15:49:59 +00:00
|
|
|
Renderer::buildGLColorArray(mColors, mColorShift, 6);
|
2014-04-12 01:48:13 +00:00
|
|
|
}
|
2013-06-21 16:49:29 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
void ImageComponent::render(const Eigen::Affine3f& parentTrans)
|
|
|
|
{
|
|
|
|
Eigen::Affine3f trans = roundMatrix(parentTrans * getTransform());
|
|
|
|
Renderer::setMatrix(trans);
|
|
|
|
|
|
|
|
if(mTexture && mOpacity > 0)
|
|
|
|
{
|
|
|
|
if(mTexture->isInitialized())
|
|
|
|
{
|
|
|
|
// actually draw the image
|
|
|
|
mTexture->bind();
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
2012-10-17 17:15:58 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &mVertices[0].pos);
|
|
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &mVertices[0].tex);
|
|
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, 0, mColors);
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
2012-08-29 18:53:53 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
}else{
|
|
|
|
LOG(LogError) << "Image texture is not initialized!";
|
|
|
|
mTexture.reset();
|
|
|
|
}
|
|
|
|
}
|
2012-10-17 17:15:58 +00:00
|
|
|
|
2014-04-12 01:48:13 +00:00
|
|
|
GuiComponent::renderChildren(trans);
|
2012-08-02 01:43:55 +00:00
|
|
|
}
|
2012-09-04 16:45:16 +00:00
|
|
|
|
2013-06-02 15:08:32 +00:00
|
|
|
bool ImageComponent::hasImage()
|
2012-10-10 13:51:48 +00:00
|
|
|
{
|
2013-11-12 23:28:15 +00:00
|
|
|
return (bool)mTexture;
|
2012-10-10 13:51:48 +00:00
|
|
|
}
|
2013-04-08 16:52:40 +00:00
|
|
|
|
2014-01-01 05:39:22 +00:00
|
|
|
void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
|
|
|
{
|
|
|
|
using namespace ThemeFlags;
|
|
|
|
|
|
|
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "image");
|
|
|
|
if(!elem)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Eigen::Vector2f scale = getParent() ? getParent()->getSize() : Eigen::Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
|
|
|
|
|
|
|
if(properties & POSITION && elem->has("pos"))
|
|
|
|
{
|
|
|
|
Eigen::Vector2f denormalized = elem->get<Eigen::Vector2f>("pos").cwiseProduct(scale);
|
|
|
|
setPosition(Eigen::Vector3f(denormalized.x(), denormalized.y(), 0));
|
|
|
|
}
|
|
|
|
|
2014-01-10 23:45:47 +00:00
|
|
|
if(properties & ThemeFlags::SIZE)
|
|
|
|
{
|
|
|
|
if(elem->has("size"))
|
|
|
|
setResize(elem->get<Eigen::Vector2f>("size").cwiseProduct(scale));
|
|
|
|
else if(elem->has("maxSize"))
|
|
|
|
setMaxSize(elem->get<Eigen::Vector2f>("maxSize").cwiseProduct(scale));
|
|
|
|
}
|
2014-01-01 05:39:22 +00:00
|
|
|
|
2014-01-03 14:26:39 +00:00
|
|
|
// position + size also implies origin
|
|
|
|
if((properties & ORIGIN || (properties & POSITION && properties & ThemeFlags::SIZE)) && elem->has("origin"))
|
2014-01-01 05:39:22 +00:00
|
|
|
setOrigin(elem->get<Eigen::Vector2f>("origin"));
|
|
|
|
|
|
|
|
if(properties & PATH && elem->has("path"))
|
2014-01-19 18:23:01 +00:00
|
|
|
{
|
|
|
|
bool tile = (elem->has("tile") && elem->get<bool>("tile"));
|
|
|
|
setImage(elem->get<std::string>("path"), tile);
|
|
|
|
}
|
2014-01-23 21:30:32 +00:00
|
|
|
|
|
|
|
if(properties & COLOR && elem->has("color"))
|
|
|
|
setColorShift(elem->get<unsigned int>("color"));
|
2014-01-01 05:39:22 +00:00
|
|
|
}
|
2014-05-16 21:21:33 +00:00
|
|
|
|
|
|
|
std::vector<HelpPrompt> ImageComponent::getHelpPrompts()
|
|
|
|
{
|
|
|
|
std::vector<HelpPrompt> ret;
|
|
|
|
ret.push_back(HelpPrompt("a", "select"));
|
|
|
|
return ret;
|
|
|
|
}
|