Skip to content

Commit

Permalink
Introducing alternative renderer for SVG files.
Browse files Browse the repository at this point in the history
  • Loading branch information
achary committed Jan 27, 2023
1 parent cb397a2 commit 7bbb1b1
Show file tree
Hide file tree
Showing 13 changed files with 434 additions and 186 deletions.
196 changes: 158 additions & 38 deletions OMEdit/OMEditLIB/Annotations/BitmapAnnotation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,58 @@
#include "BitmapAnnotation.h"
#include "Modeling/Commands.h"
#include "Util/ResourceCache.h"
#include "Renderers.h"

#include <QMessageBox>

const char* bitmapResourceName = ":/Resources/icons/bitmap-shape.svg";

/*!
* \brief A very simple heuristic to clasify the content type.
*
* Note that it's not super critical if this logic breaks for a weirdly contaminated SVG
* file. In a worst-case scenario, we will return `false` and fall back to processing the file
* with QImage, which will work and only make the rendering less perfect at larger zoom levels.
* Therefore not overinvesting into this here.
*
* \return true if the given are of an SVG image, false otherwise.
*/
bool isSvgImage(const QByteArray &bytes)
{
return bytes.left(1024).toLower().contains("<svg") && bytes.right(256).toLower().contains("</svg>");
}

/*!
* \brief A factory function for a Renderers, based on input binary image data.
* \param bytes The binry data bytes representing the image.
* \return A pointer to a newly created rendered object.
*/
std::unique_ptr<Renderer> makeRenderer(const QByteArray &bytes)
{
if (isSvgImage(bytes)) {
return std::make_unique<SvgRenderer>(bytes);
}
return std::make_unique<BitmapRenderer>(bytes);
}

/*!
* \brief A helper function to get a file content as byte array.
*/
QByteArray getFileAsBytes(const QString &fileName)
{
QFile file(fileName);
file.open(QIODevice::ReadOnly);
return file.readAll();
}

BitmapAnnotation::BitmapAnnotation(QString classFileName, QString annotation, GraphicsView *pGraphicsView)
: ShapeAnnotation(false, pGraphicsView, 0, 0)
{
mpOriginItem = new OriginItem(this);
mpOriginItem->setPassive();
mClassFileName = classFileName;
// set the default values
GraphicItem::setDefaults();
ShapeAnnotation::setDefaults();
setDefaults();
// set users default value by reading the settings file.
ShapeAnnotation::setUserDefaults();
parseShapeAnnotation(annotation);
Expand All @@ -61,21 +101,22 @@ BitmapAnnotation::BitmapAnnotation(ModelInstance::Bitmap *pBitmap, const QString
mpBitmap = pBitmap;
mClassFileName = classFileName;
// set the default values
GraphicItem::setDefaults();
FilledShape::setDefaults();
ShapeAnnotation::setDefaults();
setDefaults();
// set users default value by reading the settings file.
ShapeAnnotation::setUserDefaults();
parseShapeAnnotation();
setShapeFlags(true);
}

BitmapAnnotation::BitmapAnnotation(ShapeAnnotation *pShapeAnnotation, Element *pParent)
: ShapeAnnotation(pShapeAnnotation, pParent)
BitmapAnnotation::BitmapAnnotation(BitmapAnnotation *pBitmapAnnotation, Element *pParent)
: ShapeAnnotation(pBitmapAnnotation, pParent)
{
mpOriginItem = 0;
updateShape(pShapeAnnotation);
updateShape(pBitmapAnnotation);
applyTransformation();
setFileName(pBitmapAnnotation->getFileName());
setImageSource(pBitmapAnnotation->getImageSource());
updateRenderer();
}

BitmapAnnotation::BitmapAnnotation(ModelInstance::Bitmap *pBitmap, const QString &classFileName, Element *pParent)
Expand All @@ -85,9 +126,7 @@ BitmapAnnotation::BitmapAnnotation(ModelInstance::Bitmap *pBitmap, const QString
mpBitmap = pBitmap;
mClassFileName = classFileName;
// set the default values
GraphicItem::setDefaults();
FilledShape::setDefaults();
ShapeAnnotation::setDefaults();
setDefaults();
// set users default value by reading the settings file.
ShapeAnnotation::setUserDefaults();
parseShapeAnnotation();
Expand Down Expand Up @@ -119,8 +158,7 @@ BitmapAnnotation::BitmapAnnotation(QString classFileName, GraphicsView *pGraphic
mpOriginItem->setPassive();
mClassFileName = classFileName;
// set the default values
GraphicItem::setDefaults();
ShapeAnnotation::setDefaults();
setDefaults();
// set users default value by reading the settings file.
ShapeAnnotation::setUserDefaults();
QVector<QPointF> extents;
Expand All @@ -131,10 +169,24 @@ BitmapAnnotation::BitmapAnnotation(QString classFileName, GraphicsView *pGraphic
setShapeFlags(true);

setFileName(mClassFileName);
if (!mFileName.isEmpty() && QFile::exists(mFileName)) {
mImage.load(mFileName);
setImageSource("");
updateRenderer();
}

// No, we can not put this simply in the declaration and have unique_ptr accepting incomplete type.
BitmapAnnotation::~BitmapAnnotation() = default;

void BitmapAnnotation::updateRenderer()
{
if (!mImageSource.isEmpty()) {
QByteArray bytes = QByteArray::fromBase64(mImageSource.toLatin1());
mRenderer = makeRenderer(bytes);
} else {
mImage = ResourceCache::getImage(":/Resources/icons/bitmap-shape.svg");
if (!mFileName.isEmpty() && QFile::exists(mFileName)) {
mRenderer = makeRenderer(getFileAsBytes(getFileName()));
} else {
mRenderer = std::make_unique<SvgRenderer>(getFileAsBytes(bitmapResourceName));
}
}
}

Expand All @@ -152,15 +204,9 @@ void BitmapAnnotation::parseShapeAnnotation(QString annotation)
setFileName(StringHandler::removeFirstLastQuotes(stripDynamicSelect(list.at(4))));
// 6th item is the imageSource
if (list.size() >= 6) {
mImageSource = StringHandler::removeFirstLastQuotes(stripDynamicSelect(list.at(5)));
}
if (!mImageSource.isEmpty()) {
mImage.loadFromData(QByteArray::fromBase64(mImageSource.toLatin1()));
} else if (!mFileName.isEmpty() && QFile::exists(mFileName)) {
mImage.load(mFileName);
} else {
mImage = ResourceCache::getImage(":/Resources/icons/bitmap-shape.svg");
setImageSource(StringHandler::removeFirstLastQuotes(stripDynamicSelect(list.at(5))));
}
updateRenderer();
}

void BitmapAnnotation::parseShapeAnnotation()
Expand All @@ -170,14 +216,8 @@ void BitmapAnnotation::parseShapeAnnotation()
mExtent = mpBitmap->getExtent();
mExtent.evaluate(mpBitmap->getParentModel());
setFileName(StringHandler::removeFirstLastQuotes(stripDynamicSelect(mpBitmap->getFileName())));
mImageSource = StringHandler::removeFirstLastQuotes(stripDynamicSelect(mpBitmap->getImageSource()));
if (!mImageSource.isEmpty()) {
mImage.loadFromData(QByteArray::fromBase64(mImageSource.toLatin1()));
} else if (!mFileName.isEmpty() && QFile::exists(mFileName)) {
mImage.load(mFileName);
} else {
mImage = ResourceCache::getImage(":/Resources/icons/bitmap-shape.svg");
}
setImageSource(StringHandler::removeFirstLastQuotes(stripDynamicSelect(mpBitmap->getImageSource())));
updateRenderer();
}

QRectF BitmapAnnotation::boundingRect() const
Expand Down Expand Up @@ -209,11 +249,10 @@ void BitmapAnnotation::paint(QPainter *painter, const QStyleOptionGraphicsItem *
void BitmapAnnotation::drawBitmapAnnotation(QPainter *painter)
{
QRectF rect = getBoundingRect().normalized();
QImage image = mImage.scaled(rect.width(), rect.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPointF centerPoint = rect.center() - image.rect().center();
QRectF target(centerPoint.x(), centerPoint.y(), image.width(), image.height());

painter->drawImage(target, mImage.mirrored());
if (mRenderer) {
mRenderer->render(painter, rect, true);
}
}

/*!
Expand Down Expand Up @@ -271,8 +310,6 @@ QString BitmapAnnotation::getShapeAnnotation()
void BitmapAnnotation::updateShape(ShapeAnnotation *pShapeAnnotation)
{
// set the default values
GraphicItem::setDefaults(pShapeAnnotation);
FilledShape::setDefaults(pShapeAnnotation);
ShapeAnnotation::setDefaults(pShapeAnnotation);
}

Expand Down Expand Up @@ -304,3 +341,86 @@ void BitmapAnnotation::duplicate()
setSelected(false);
pBitmapAnnotation->setSelected(true);
}

/*!
* \brief ShapeAnnotation::setFileName
* Sets the file name.
* \param fileName
*/
void BitmapAnnotation::setFileName(QString fileName)
{
if (fileName.isEmpty()) {
mOriginalFileName = fileName;
mFileName = fileName;
return;
}

OMCProxy* pOMCProxy = MainWindow::instance()->getOMCProxy();
mOriginalFileName = fileName;
QUrl fileUrl(mOriginalFileName);
QFileInfo fileInfo(mOriginalFileName);
QFileInfo classFileInfo(mClassFileName);
/* if its a modelica:// link then make it absolute path */
if (fileUrl.scheme().toLower().compare("modelica") == 0) {
mFileName = pOMCProxy->uriToFilename(mOriginalFileName);
} else if (fileInfo.isRelative()) {
mFileName = QString(classFileInfo.absoluteDir().absolutePath()).append("/").append(mOriginalFileName);
} else if (fileInfo.isAbsolute()) {
mFileName = mOriginalFileName;
} else {
mFileName = "";
}
}

/*!
Returns the file name.
\return the file name.
*/
QString BitmapAnnotation::getFileName()
{
return mOriginalFileName;
}

/*!
\brief Sets the image source.
*/
void BitmapAnnotation::setImageSource(QString imageSource)
{
mImageSource = imageSource;
}

/*!
Returns the base 64 image source.
\return the image source.
*/
QString BitmapAnnotation::getImageSource()
{
return mImageSource;
}

/*!
Returns the image.
\return the image.
*/
QImage BitmapAnnotation::getImage()
{
if (mRenderer)
{
return mRenderer->getImage();
}
return BitmapAnnotation::getPlaceholderImage();
}

QImage BitmapAnnotation::getPlaceholderImage()
{
return ResourceCache::getImage(bitmapResourceName);
}

void BitmapAnnotation::setDefaults()
{
ShapeAnnotation::setDefaults();

mFileName = "";
mImageSource = "";
updateRenderer();
}
40 changes: 31 additions & 9 deletions OMEdit/OMEditLIB/Annotations/BitmapAnnotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,35 +38,57 @@
#include "ShapeAnnotation.h"
#include "Util/Utilities.h"

#include <memory>

class Renderer;
class Element;
class BitmapAnnotation : public ShapeAnnotation
{
class BitmapAnnotation : public ShapeAnnotation {
Q_OBJECT
public:
// Used for icon/diagram shape
BitmapAnnotation(QString classFileName, QString annotation, GraphicsView *pGraphicsView);
BitmapAnnotation(ModelInstance::Bitmap *pBitmap, const QString &classFileName, bool inherited, GraphicsView *pGraphicsView);
BitmapAnnotation(QString classFileName, QString annotation,
GraphicsView *pGraphicsView);
BitmapAnnotation(ModelInstance::Bitmap *pBitmap, const QString &classFileName,
bool inherited, GraphicsView *pGraphicsView);
// Used for shape inside a component
BitmapAnnotation(ShapeAnnotation *pShapeAnnotation, Element *pParent);
BitmapAnnotation(ModelInstance::Bitmap *pBitmap, const QString &classFileName, Element *pParent);
BitmapAnnotation(BitmapAnnotation *pBitmapAnnotation, Element *pParent);
BitmapAnnotation(ModelInstance::Bitmap *pBitmap, const QString &classFileName,
Element *pParent);
// Used for icon/diagram inherited shape
BitmapAnnotation(ShapeAnnotation *pShapeAnnotation, GraphicsView *pGraphicsView);
BitmapAnnotation(ShapeAnnotation *pShapeAnnotation,
GraphicsView *pGraphicsView);
// Used for OMSimulator FMU
BitmapAnnotation(QString classFileName, GraphicsView *pGraphicsView);
~BitmapAnnotation();
void parseShapeAnnotation(QString annotation) override;
void parseShapeAnnotation();
QRectF boundingRect() const override;
QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget = 0) override;
void drawBitmapAnnotation(QPainter *painter);
QString getOMCShapeAnnotation() override;
QString getOMCShapeAnnotationWithShapeName() override;
QString getShapeAnnotation() override;
void updateShape(ShapeAnnotation *pShapeAnnotation) override;
ModelInstance::Model *getParentModel() const override;
void setBitmap(ModelInstance::Bitmap *pBitmap) {mpBitmap = pBitmap;}
void setBitmap(ModelInstance::Bitmap *pBitmap) { mpBitmap = pBitmap; }
void setFileName(QString fileName);
QString getFileName();
void setImageSource(QString imageSource);
QString getImageSource();
QImage getImage();
void setDefaults();
void updateRenderer();
static QImage getPlaceholderImage();

private:
ModelInstance::Bitmap *mpBitmap;

protected:
QString mFileName;
QString mImageSource;
std::unique_ptr<Renderer> mRenderer;
public slots:
void duplicate() override;
};
Expand Down

0 comments on commit 7bbb1b1

Please sign in to comment.