SVG support (#4306)

* first version of svg support

* merge some nanosvg pr

* look for svg icons first

* bug fix

* scale svg depends on screen size

* use irrlicht headers to get screen size

* finish isALoadableFileFormat for svg

* delete dynamically allocated memory

* fix wrong delete

* only rescale icons, due to size settings in stkskin.xml depends on the size of the image file

* check svg in FileManager::getAsset

* fix large icon in list
This commit is contained in:
riso 2020-06-14 02:15:47 -05:00 committed by GitHub
parent fa8665a360
commit 4694223c03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 4823 additions and 19 deletions

View File

@ -126,6 +126,7 @@ source/Irrlicht/CImage.cpp
source/Irrlicht/CImageLoaderBMP.cpp
source/Irrlicht/CImageLoaderJPG.cpp
source/Irrlicht/CImageLoaderPNG.cpp
source/Irrlicht/CImageLoaderSVG.cpp
source/Irrlicht/CImageWriterBMP.cpp
source/Irrlicht/CImageWriterJPG.cpp
source/Irrlicht/CImageWriterPNG.cpp
@ -255,6 +256,7 @@ source/Irrlicht/CImage.h
source/Irrlicht/CImageLoaderBMP.h
source/Irrlicht/CImageLoaderJPG.h
source/Irrlicht/CImageLoaderPNG.h
source/Irrlicht/CImageLoaderSVG.h
source/Irrlicht/CImageWriterBMP.h
source/Irrlicht/CImageWriterJPG.h
source/Irrlicht/CImageWriterPNG.h

View File

@ -45,6 +45,8 @@ public:
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const = 0;
virtual core::dimension2du getImageSize(io::IReadFile* file) const { return core::dimension2du(0, 0); }
virtual bool supportThreadedLoading() const { return false; }
virtual void setScreenSize(const core::dimension2d<u32> &screen_size) {};
};

View File

@ -0,0 +1,151 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
// This file is modified by riso from CImageLoaderJPG.h
// nanosvg as parser and rasterizer for SVG files.
// The nanosvg headers are based on those in SDL2_image-2.0.5
#include "CImageLoaderSVG.h"
#include "IReadFile.h"
#include "CImage.h"
#include "os.h"
#include "irrString.h"
#include "CNullDriver.h"
namespace irr
{
namespace video
{
//! constructor
CImageLoaderSVG::CImageLoaderSVG()
{
}
//! set the screen size
void CImageLoaderSVG::setScreenSize(const core::dimension2d<u32> &screen_size)
{
ScreenSize = screen_size;
}
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".tga")
bool CImageLoaderSVG::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "svg" );
}
//! returns true if the file maybe is able to be loaded by this class
bool CImageLoaderSVG::isALoadableFileFormat(io::IReadFile* file) const
{
// read first 4095 characters, check if can find "<svg"
// this implementation refers IMG_isSVG() in SDL2_Image
long filesize = file->getSize();
long cropsize = filesize < 4095L ? filesize : 4095L;
char* data = new char[cropsize+1];
int readsize = file->read(data,cropsize);
data[cropsize] = '\0';
if( (long)readsize != cropsize) {
os::Printer::log("Couldn't read SVG image file", file->getFileName(), ELL_ERROR);
delete[] data;
return 0;
}
// check if can find "<svg" in the file
if ( strstr(data, "<svg") )
{
delete[] data;
return 1;
}
delete[] data;
return 0;
}
//! creates a surface from the file
IImage* CImageLoaderSVG::loadImage(io::IReadFile* file, bool skip_checking) const
{
// check IMG_LoadSVG_RW
struct NSVGimage *img = 0;
struct NSVGrasterizer *rasterizer = 0;
video::IImage* image = 0;
int w, h;
// read all the characters in the svg file to data
long filesize = file->getSize();
char* data = new char[filesize+1];
int readsize = file->read(data,filesize);
data[filesize] = '\0';
if( (long)readsize != filesize) {
os::Printer::log("Couldn't read SVG image file", file->getFileName(), ELL_ERROR);
delete[] data;
return 0;
}
// parse the svg image
img = nsvgParse(data, "px", 96);
if ( !img ) {
os::Printer::log("Couldn't parse SVG image", ELL_ERROR);
delete[] data;
return 0;
}
delete[] data;
rasterizer = nsvgCreateRasterizer();
if ( !rasterizer ) {
os::Printer::log("Couldn't create SVG rasterizer", ELL_ERROR);
nsvgDelete( img );
return 0;
}
float scale =1.0f;
// only rescale the icons
if ( strstr(file->getFileName().c_str(),"gui/icons/") )
{
// determine scaling based on screen size
float screen_height = ScreenSize.Height;
float desired_icon_size = 0.21*screen_height + 30.0f; // phenomenological
scale = desired_icon_size/img->height;
}
// create surface
w = (int)(img->width*scale);
h = (int)(img->height*scale);
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(w, h));
if ( !image ) {
os::Printer::log("LOAD SVG: create image struct failure", file->getFileName(), ELL_ERROR);
nsvgDeleteRasterizer( rasterizer );
nsvgDelete( img );
return 0;
}
// rasterization
nsvgRasterize(rasterizer, img, 0.0f, 0.0f, scale, (unsigned char *)image->lock(), (int)image->getDimension().Width, (int)image->getDimension().Height, (int)image->getPitch());
if (image)
image->unlock();
// clean up
nsvgDeleteRasterizer( rasterizer );
nsvgDelete( img );
return image;
}
//! creates a loader which is able to load svg images
IImageLoader* createImageLoaderSVG()
{
return new CImageLoaderSVG();
}
} // end namespace video
} // end namespace irr

View File

@ -0,0 +1,64 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
// This file is modified by riso from CImageLoaderJPG.h
// nanosvg as parser and rasterizer for SVG files.
// The nanosvg headers are based on those in SDL2_image-2.0.5
#ifndef __C_IMAGE_LOADER_SVG_H_INCLUDED__
#define __C_IMAGE_LOADER_SVG_H_INCLUDED__
#include "IrrCompileConfig.h"
#include "IImageLoader.h"
#include <stdio.h>
#include <string.h>
#include <float.h>
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
namespace irr
{
namespace video
{
/*!
Surface Loader for SVG images
*/
class CImageLoaderSVG : public IImageLoader
{
public:
//! constructor
CImageLoaderSVG();
// set the screen size, used to determine scaling
virtual void setScreenSize(const core::dimension2d<u32> &screen_size);
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".tga")
virtual bool isALoadableFileExtension(const io::path& filename) const;
//! returns true if the file maybe is able to be loaded by this class
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
private:
core::dimension2d<u32> ScreenSize;
};
} // end namespace video
} // end namespace irr
#endif

View File

@ -58,6 +58,9 @@ IImageLoader* createImageLoaderPPM();
//! creates a loader which is able to load rgb images
IImageLoader* createImageLoaderRGB();
//! creates a loader which is able to load svg images
IImageLoader* createImageLoaderSVG();
//! creates a writer which is able to save bmp images
IImageWriter* createImageWriterBMP();
@ -131,6 +134,8 @@ CNullDriver::CNullDriver(io::IFileSystem* io, const core::dimension2d<u32>& scre
#ifdef _IRR_COMPILE_WITH_BMP_LOADER_
SurfaceLoader.push_back(video::createImageLoaderBMP());
#endif
SurfaceLoader.push_back(video::createImageLoaderSVG());
#ifdef _IRR_COMPILE_WITH_JPG_WRITER_
SurfaceWriter.push_back(video::createImageWriterJPG());
#endif
@ -1298,6 +1303,8 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file, video::IImageLoade
*loader = SurfaceLoader[i];
return 0;
}
// pass screen size to ImageLoaderSVG. For other formats (BMP,JPG,PNG), setScreenSize() does nothing
SurfaceLoader[i]->setScreenSize(getCurrentRenderTargetSize());
// reset file position which might have changed due to previous loadImage calls
file->seek(0);
image = SurfaceLoader[i]->loadImage(file);
@ -1318,6 +1325,8 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file, video::IImageLoade
*loader = SurfaceLoader[i];
return 0;
}
// pass screen size to ImageLoaderSVG. For other formats (BMP,JPG,PNG), setScreenSize() does nothing
SurfaceLoader[i]->setScreenSize(getCurrentRenderTargetSize());
file->seek(0);
image = SurfaceLoader[i]->loadImage(file);
if (image)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2888,22 +2888,37 @@ bool Skin::hasFont() const
* icon. */
std::string Skin::getThemedIcon(const std::string& relative_path) const
{
if (!SkinConfig::m_icon_theme ||
relative_path.find("gui/icons/") == std::string::npos)
// First check if an svg icon is available
const std::vector<std::string> ext {".svg", ".png"};
// get the path without extension
const std::string path_no_extension = StringUtils::removeExtension(relative_path);
// loop the file extensions: svg first
for(auto s : ext)
{
return file_manager->getAsset(relative_path);
}
std::string relative_path2 = path_no_extension + s;
if (!SkinConfig::m_icon_theme ||
relative_path2.find("gui/icons/") == std::string::npos)
{
std::string tmp_path = file_manager->getAsset(relative_path2);
if (file_manager->fileExists(tmp_path))
{
return tmp_path;
}
}
if (relative_path.find(SkinConfig::m_data_path) != std::string::npos &&
file_manager->fileExists(relative_path))
{
// Absolute path given
return relative_path;
}
if (relative_path2.find(SkinConfig::m_data_path) != std::string::npos &&
file_manager->fileExists(relative_path2))
{
// Absolute path given
return relative_path2;
}
std::string test_path = SkinConfig::m_data_path + "data/" + relative_path;
if (file_manager->fileExists(test_path))
return test_path;
else
return file_manager->getAsset(relative_path);
std::string test_path = SkinConfig::m_data_path + "data/" + relative_path2;
if (file_manager->fileExists(test_path))
{
return test_path;
}
}
// if nothing found, return the bundled one
return file_manager->getAsset(relative_path);
} // getThemedIcon

View File

@ -787,10 +787,14 @@ std::string FileManager::getAsset(FileManager::AssetType type,
{
if (type == GUI_ICON && GUIEngine::getSkin()->hasIconTheme())
{
const std::string test_path = GUIEngine::getSkin()->getDataPath() +
"data/gui/icons/" + name;
if (fileExists(test_path))
return test_path;
// remove the extension to check both .svg and .png
const std::string test_path = StringUtils::removeExtension
(GUIEngine::getSkin()->getDataPath() + "data/gui/icons/" + name);
// first check if there is an SVG version
if (fileExists(test_path + ".svg"))
return test_path + ".svg";
else if (fileExists(test_path + ".png"))
return test_path + ".png";
else
return m_subdir_name[type] + name;
}

View File

@ -158,6 +158,7 @@ void AddonsScreen::init()
m_icon_height = GUIEngine::getFontHeight() * 2;
// 128 is the height of the image file
m_icon_bank->setScale((float)GUIEngine::getFontHeight() / 72.0f);
m_icon_bank->setTargetIconSize(128,128);
w_list->setIcons(m_icon_bank, (int)(m_icon_height));
m_type = "kart";

View File

@ -148,6 +148,7 @@ void NetworkingLobby::loadedFromFile()
m_icon_bank->addTextureAsSprite(icon_6);
m_icon_bank->setScale((float)GUIEngine::getFontHeight() / 96.0f);
m_icon_bank->setTargetIconSize(128, 128);
} // loadedFromFile
// ---------------------------------------------------------------------------

View File

@ -75,6 +75,7 @@ void OptionsScreenInput::loadedFromFile()
// scale icons depending on font height
const float scale = GUIEngine::getFontHeight() / 72.0f;
m_icon_bank->setScale(scale);
m_icon_bank->setTargetIconSize(128, 128);
} // loadFromFile
// -----------------------------------------------------------------------------