Files
wxWidgets/src/generic/bmpsvg.cpp
Vadim Zeitlin 8adfaa37f7 Add wxBitmapBundle::FromSVGFile() helper
This is just a trivial wrapper for wxBitmapBundle::FromSVG(), but it can
still be convenient to have.
2021-11-29 12:55:22 +00:00

244 lines
6.8 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/generic/bmpsvg.cpp
// Purpose: Generic wxBitmapBundle::FromSVG() implementation
// Author: Vadim Zeitlin, Gunter Königsmann
// Created: 2021-09-28
// Copyright: (c) 2019 Gunter Königsmann <wxMaxima@physikbuch.de>
// (c) 2021 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include "wx/bmpbndl.h"
#ifdef wxHAS_SVG
// Try to help people updating their sources from Git and forgetting to
// initialize new submodules, if possible: if you get this error, it means that
// your source tree doesn't contain 3rdparty/nanosvg and you should initialize
// and update the corresponding submodule.
#ifdef __has_include
#if ! __has_include("../../3rdparty/nanosvg/src/nanosvg.h")
#error You need to run "git submodule update --init 3rdparty/nanosvg".
#undef wxHAS_SVG
#endif
#endif // __has_include
#endif // wxHAS_SVG
#ifdef wxHAS_SVG
#ifndef WX_PRECOMP
#include "wx/utils.h" // Only for wxMin()
#endif // WX_PRECOMP
#ifdef wxUSE_FFILE
#include "wx/ffile.h"
#elif wxUSE_FILE
#include "wx/file.h"
#else
#define wxNO_SVG_FILE
#endif
#include "wx/rawbmp.h"
#include "wx/private/bmpbndl.h"
// Disable some warnings inside NanoSVG code that we're not interested in.
#ifdef __VISUALC__
#pragma warning(push)
#pragma warning(disable:4456)
#pragma warning(disable:4702)
// Also make nanosvg.h compile with older MSVC versions which didn't have
// strtoll().
#if _MSC_VER < 1800
#define strtoll _strtoi64
#endif
#endif
#define NANOSVG_IMPLEMENTATION
#define NANOSVGRAST_IMPLEMENTATION
#include "../../3rdparty/nanosvg/src/nanosvg.h"
#include "../../3rdparty/nanosvg/src/nanosvgrast.h"
#ifdef __VISUALC__
#pragma warning(pop)
#endif
// ----------------------------------------------------------------------------
// private helpers
// ----------------------------------------------------------------------------
namespace
{
class wxBitmapBundleImplSVG : public wxBitmapBundleImpl
{
public:
// Ctor must be passed a valid NSVGimage and takes ownership of it.
wxBitmapBundleImplSVG(NSVGimage* svgImage, const wxSize& sizeDef)
: m_svgImage(svgImage),
m_svgRasterizer(nsvgCreateRasterizer()),
m_sizeDef(sizeDef)
{
}
~wxBitmapBundleImplSVG()
{
nsvgDeleteRasterizer(m_svgRasterizer);
nsvgDelete(m_svgImage);
}
virtual wxSize GetDefaultSize() const wxOVERRIDE;
virtual wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE;
virtual wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE;
private:
wxBitmap DoRasterize(const wxSize& size);
NSVGimage* const m_svgImage;
NSVGrasterizer* const m_svgRasterizer;
const wxSize m_sizeDef;
// Cache the last used bitmap (may be invalid if not used yet).
//
// Note that we cache only the last bitmap and not all the bitmaps ever
// requested from GetBitmap() for the different sizes because there would
// be no way to clear such cache and its growth could be unbounded,
// resulting in too many bitmap objects being used in an application using
// SVG for all of its icons.
wxBitmap m_cachedBitmap;
wxDECLARE_NO_COPY_CLASS(wxBitmapBundleImplSVG);
};
} // anonymous namespace
// ============================================================================
// wxBitmapBundleImplSVG implementation
// ============================================================================
wxSize wxBitmapBundleImplSVG::GetDefaultSize() const
{
return m_sizeDef;
}
wxSize wxBitmapBundleImplSVG::GetPreferredSizeAtScale(double scale) const
{
// We consider that we can render at any scale.
return m_sizeDef*scale;
}
wxBitmap wxBitmapBundleImplSVG::GetBitmap(const wxSize& size)
{
if ( !m_cachedBitmap.IsOk() || m_cachedBitmap.GetSize() != size )
{
m_cachedBitmap = DoRasterize(size);
}
return m_cachedBitmap;
}
wxBitmap wxBitmapBundleImplSVG::DoRasterize(const wxSize& size)
{
wxVector<unsigned char> buffer(size.x*size.y*4);
nsvgRasterize
(
m_svgRasterizer,
m_svgImage,
0.0, 0.0, // no offset
wxMin
(
size.x/m_svgImage->width,
size.y/m_svgImage->height
), // scale
&buffer[0],
size.x, size.y,
size.x*4 // stride -- we have no gaps between lines
);
wxBitmap bitmap(size, 32);
wxAlphaPixelData bmpdata(bitmap);
wxAlphaPixelData::Iterator dst(bmpdata);
const unsigned char* src = &buffer[0];
for ( int y = 0; y < size.y; ++y )
{
dst.MoveTo(bmpdata, 0, y);
for ( int x = 0; x < size.x; ++x )
{
const unsigned char a = src[3];
dst.Red() = src[0] * a / 255;
dst.Green() = src[1] * a / 255;
dst.Blue() = src[2] * a / 255;
dst.Alpha() = a;
++dst;
src += 4;
}
}
return bitmap;
}
/* static */
wxBitmapBundle wxBitmapBundle::FromSVG(char* data, const wxSize& sizeDef)
{
NSVGimage* const svgImage = nsvgParse(data, "px", 96);
if ( !svgImage )
return wxBitmapBundle();
return wxBitmapBundle(new wxBitmapBundleImplSVG(svgImage, sizeDef));
}
/* static */
wxBitmapBundle wxBitmapBundle::FromSVG(const char* data, const wxSize& sizeDef)
{
wxCharBuffer copy(data);
return FromSVG(copy.data(), sizeDef);
}
/* static */
wxBitmapBundle wxBitmapBundle::FromSVGFile(const wxString& path, const wxSize& sizeDef)
{
// There is nsvgParseFromFile(), but it doesn't work with Unicode filenames
// under MSW and does exactly the same thing that we do here in any case,
// so it seems better to use our code.
#ifndef wxNO_SVG_FILE
#if wxUSE_FFILE
wxFFile file(path, "rb");
#elif wxUSE_FILE
wxFile file(path);
#endif
if ( file.IsOpened() )
{
const wxFileOffset lenAsOfs = file.Length();
if ( lenAsOfs != wxInvalidOffset )
{
const size_t len = static_cast<size_t>(lenAsOfs);
wxCharBuffer buf(len);
char* const ptr = buf.data();
if ( file.Read(ptr, len) == len )
return wxBitmapBundle::FromSVG(ptr, sizeDef);
}
}
#endif // !wxNO_SVG_FILE
return wxBitmapBundle();
}
#endif // wxHAS_SVG