Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SVG support + basic MidiKeyboard example #141

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fa450e2
Initial work on SVG support
pdesaulniers Apr 28, 2019
23cc7da
Finish basic implementation of SVG support + MidiKeyboard example
pdesaulniers Apr 29, 2019
7505c9b
Update MidiKeyboard's README
pdesaulniers Apr 29, 2019
cc70a9d
Avoid constexpr and add leak detector
pdesaulniers Apr 30, 2019
9b1867b
Avoid comparison between signed and unsigned integers
pdesaulniers Apr 30, 2019
9308d52
Avoid potential memory leak
pdesaulniers Apr 30, 2019
b901c49
Get rid of bufferSize variable
pdesaulniers May 1, 2019
f82df26
Bring back the comment mentioning the need for a temporary buffer
pdesaulniers May 1, 2019
60ab304
Cleanup
pdesaulniers May 1, 2019
54013df
Use static vars for UI width/height
pdesaulniers May 1, 2019
0b05f45
SVG cleanup and subtle UI polish
pdesaulniers May 1, 2019
a27c8d8
Allow playing notes using the computer keyboard
pdesaulniers May 1, 2019
abbc504
Use regular int for keyboardDeltaWidth
pdesaulniers May 1, 2019
d83aaf2
Add keycodes support
pdesaulniers Jul 29, 2019
21f3696
Better documentation for PuglKeyCodes; change keycode acronym to KC
pdesaulniers Jul 29, 2019
9510c10
Merge branch 'keycodes-support' of https://github.com/pdesaulniers/DP…
pdesaulniers Jul 29, 2019
2d9b13d
Update piano widget to use keycodes
pdesaulniers Jul 29, 2019
6ea0cc3
Remove superfluous notes & fix build
pdesaulniers Jul 29, 2019
5bffb9f
Fix comment
pdesaulniers Jul 30, 2019
cb5517b
Add some comments
pdesaulniers Jul 30, 2019
07c6585
Adjustments to keycode support
pdesaulniers Aug 26, 2019
86c40bc
Tweak indentation
pdesaulniers Aug 26, 2019
b34b29c
Expose keycodes in Base.hpp
pdesaulniers Aug 26, 2019
116aeff
Merge branch 'keycodes-support' into svg
pdesaulniers Aug 26, 2019
d304efb
Use new KeyCode enum, instead of PuglKeyCodes
pdesaulniers Aug 26, 2019
ab8b5f1
Don't include pugl.h
pdesaulniers Aug 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ examples: dgl
$(MAKE) all -C examples/MidiThrough
$(MAKE) all -C examples/Parameters
$(MAKE) all -C examples/States
$(MAKE) all -C examples/MidiKeyboard

ifeq ($(HAVE_CAIRO),true)
$(MAKE) all -C examples/CairoUI
Expand Down Expand Up @@ -60,6 +61,7 @@ clean:
$(MAKE) clean -C examples/MidiThrough
$(MAKE) clean -C examples/Parameters
$(MAKE) clean -C examples/States
$(MAKE) clean -C examples/MidiKeyboard
$(MAKE) clean -C utils/lv2-ttl-generator
rm -rf bin build

Expand Down
134 changes: 134 additions & 0 deletions dgl/Base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,140 @@ enum Key {
kKeySuper
};

/**
Layout-independent keycodes.
These keycodes are relative to an US QWERTY keyboard.
Therefore, the keycode for the letter 'A' on an AZERTY keyboard will be equal to kKeyCodeQ.
*/
enum KeyCode {
/* Zero, does not correspond to any key. */
kKeyCodeNone = 0,

kKeyCodeA = 4,
kKeyCodeB = 5,
kKeyCodeC = 6,
kKeyCodeD = 7,
kKeyCodeE = 8,
kKeyCodeF = 9,
kKeyCodeG = 10,
kKeyCodeH = 11,
kKeyCodeI = 12,
kKeyCodeJ = 13,
kKeyCodeK = 14,
kKeyCodeL = 15,
kKeyCodeM = 16,
kKeyCodeN = 17,
kKeyCodeO = 18,
kKeyCodeP = 19,
kKeyCodeQ = 20,
kKeyCodeR = 21,
kKeyCodeS = 22,
kKeyCodeT = 23,
kKeyCodeU = 24,
kKeyCodeV = 25,
kKeyCodeW = 26,
kKeyCodeX = 27,
kKeyCodeY = 28,
kKeyCodeZ = 29,
kKeyCode1 = 30,
kKeyCode2 = 31,
kKeyCode3 = 32,
kKeyCode4 = 33,
kKeyCode5 = 34,
kKeyCode6 = 35,
kKeyCode7 = 36,
kKeyCode8 = 37,
kKeyCode9 = 38,
kKeyCode0 = 39,
kKeyCodeEscape = 41,
kKeyCodeDelete = 42,
kKeyCodeTab = 43,
kKeyCodeSpace = 44,
kKeyCodeMinus = 45,
kKeyCodeEquals = 46,
kKeyCodeLeftBracket = 47,
kKeyCodeRightBracket = 48,
kKeyCodeBackslash = 49,
kKeyCodeSemicolon = 51,
kKeyCodeQuote = 52,
kKeyCodeGrave = 53,
kKeyCodeComma = 54,
kKeyCodePeriod = 55,
kKeyCodeSlash = 56,
kKeyCodeCapsLock = 57,
kKeyCodeF1 = 58,
kKeyCodeF2 = 59,
kKeyCodeF3 = 60,
kKeyCodeF4 = 61,
kKeyCodeF5 = 62,
kKeyCodeF6 = 63,
kKeyCodeF7 = 64,
kKeyCodeF8 = 65,
kKeyCodeF9 = 66,
kKeyCodeF10 = 67,
kKeyCodeF11 = 68,
kKeyCodeF12 = 69,
kKeyCodePrintScreen = 70,
kKeyCodeScrollLock = 71,
kKeyCodePause = 72,
kKeyCodeInsert = 73,
kKeyCodeHome = 74,
kKeyCodePageUp = 75,
kKeyCodeDeleteForward = 76,
kKeyCodeEnd = 77,
kKeyCodePageDown = 78,
kKeyCodeRight = 79,
kKeyCodeLeft = 80,
kKeyCodeDown = 81,
kKeyCodeUp = 82,
kKeyCodeKP_NumLock = 83,
kKeyCodeKP_Divide = 84,
kKeyCodeKP_Multiply = 85,
kKeyCodeKP_Subtract = 86,
kKeyCodeKP_Add = 87,
kKeyCodeKP_Enter = 88,
kKeyCodeKP_1 = 89,
kKeyCodeKP_2 = 90,
kKeyCodeKP_3 = 91,
kKeyCodeKP_4 = 92,
kKeyCodeKP_5 = 93,
kKeyCodeKP_6 = 94,
kKeyCodeKP_7 = 95,
kKeyCodeKP_8 = 96,
kKeyCodeKP_9 = 97,
kKeyCodeKP_0 = 98,
kKeyCodePoint = 99,
kKeyCodeNonUSBackslash = 100,
kKeyCodeKP_Equals = 103,
kKeyCodeF13 = 104,
kKeyCodeF14 = 105,
kKeyCodeF15 = 106,
kKeyCodeF16 = 107,
kKeyCodeF17 = 108,
kKeyCodeF18 = 109,
kKeyCodeF19 = 110,
kKeyCodeF20 = 111,
kKeyCodeF21 = 112,
kKeyCodeF22 = 113,
kKeyCodeF23 = 114,
kKeyCodeF24 = 115,
kKeyCodeHelp = 117,
kKeyCodeMenu = 118,
kKeyCodeMute = 127,
kKeyCodeSysReq = 154,
kKeyCodeReturn = 158,
kKeyCodeKP_Clear = 216,
kKeyCodeKP_Decimal = 220,
kKeyCodeLeftControl = 224,
kKeyCodeLeftShift = 225,
kKeyCodeLeftAlt = 226,
kKeyCodeLeftGUI = 227,
kKeyCodeRightControl = 228,
kKeyCodeRightShift = 229,
kKeyCodeRightAlt = 230,
kKeyCodeRightGUI = 231
};

// -----------------------------------------------------------------------
// Base DGL classes

Expand Down
6 changes: 6 additions & 0 deletions dgl/Image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ class Image : public ImageBase
const GLenum format = GL_BGRA,
const GLenum type = GL_UNSIGNED_BYTE) noexcept;

/**
* Load image data from an SVG.
* @note The SVG instance must remain valid during the lifetime of this image.
*/
void loadFromSVG(const SVG& svg) noexcept;

/**
Get the image format.
*/
Expand Down
1 change: 1 addition & 0 deletions dgl/ImageBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define DGL_IMAGE_BASE_HPP_INCLUDED

#include "Geometry.hpp"
#include "SVG.hpp"

START_NAMESPACE_DGL

Expand Down
1 change: 1 addition & 0 deletions dgl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ OBJS_common = \
../build/dgl/Geometry.cpp.o \
../build/dgl/ImageBase.cpp.o \
../build/dgl/Resources.cpp.o \
../build/dgl/SVG.cpp.o \
../build/dgl/Widget.cpp.o

# TODO: ImageWidgets.cpp
Expand Down
75 changes: 75 additions & 0 deletions dgl/SVG.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_SVG_HPP_INCLUDED
#define DGL_SVG_HPP_INCLUDED

#include "Geometry.hpp"

struct NSVGrasterizer;

START_NAMESPACE_DGL

/**
* Utility class for loading SVGs.
*/
class SVG
{
public:

/**
Constructor for a null SVG.
*/
SVG();

/**
Destructor.
*/
~SVG();

/**
Load SVG data from memory.
@note @a rawData must remain valid for the lifetime of this SVG.
*/
void loadFromMemory(const char* const rawData,
const uint dataSize,
const float scaling = 1.0f) noexcept;

/**
Check if this SVG is valid.
*/
const Size<uint>& getSize() const noexcept;

/**
Get the RGBA data of the rasterized SVG.
*/
const unsigned char* getRGBAData() const noexcept;

/**
Check if this SVG is valid.
*/
bool isValid() const noexcept;

private:
Size<uint> fSize;
unsigned char* fRGBAData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SVG)
};

END_NAMESPACE_DGL

#endif
1 change: 1 addition & 0 deletions dgl/Widget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class Widget
struct KeyboardEvent : BaseEvent {
bool press;
uint key;
uint keycode;

/** Constuctor */
KeyboardEvent() noexcept
Expand Down
10 changes: 8 additions & 2 deletions dgl/src/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ void Image::loadFromMemory(const char* const rawData,
fIsReady = false;
}

void Image::loadFromSVG(const SVG& svg) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(svg.isValid(),)

loadFromMemory((const char*)svg.getRGBAData(), svg.getSize(), GL_RGBA);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty bad, as the RGBA data becomes invalid when the SVG is freed. The caller needs to keep both the SVG and the Image 'alive'.

What should we do instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a parameter to allocate memory. if true, do malloc and copy data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me like this would be a pretty wasteful copy. I guess it could be avoided if there was an object that combined both SVG and Image.

}

GLenum Image::getFormat() const noexcept
{
return fFormat;
Expand All @@ -114,8 +121,7 @@ Image& Image::operator=(const Image& image) noexcept

void Image::_drawAt(const Point<int>& pos)
{
if (fTextureId == 0 || ! isValid())
return;
DISTRHO_SAFE_ASSERT_RETURN(fTextureId != 0 && isValid(), )

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fTextureId);
Expand Down
98 changes: 98 additions & 0 deletions dgl/src/SVG.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "SVG.hpp"

#define NANOSVG_IMPLEMENTATION
#include "nanosvg/nanosvg.h"

#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvg/nanosvgrast.h"


START_NAMESPACE_DGL

SVG::SVG()
: fSize(),
fRGBAData(nullptr)
{
}

void SVG::loadFromMemory(const char* const rawData, const uint dataSize, const float scaling) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(rawData != nullptr, )
DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0f, )

if (fRGBAData != nullptr)
{
free(fRGBAData);
fRGBAData = nullptr;
}

// nsvgParse modifies the input data, so we must use a temporary buffer
char* tmpBuffer = (char*)malloc(dataSize);

DISTRHO_SAFE_ASSERT_RETURN(tmpBuffer != nullptr, )

strncpy(tmpBuffer, rawData, dataSize);
tmpBuffer[dataSize - 1] = '\0';

const float dpi = 96;

NSVGimage* image = nsvgParse(tmpBuffer, "px", dpi);

free(tmpBuffer);

DISTRHO_SAFE_ASSERT_RETURN(image != nullptr, )

const uint scaledWidth = image->width * scaling;
const uint scaledHeight = image->height * scaling;

DISTRHO_SAFE_ASSERT_RETURN(scaledWidth > 0 && scaledHeight > 0, )

fRGBAData = (unsigned char*)malloc(scaledWidth * scaledHeight * 4);

NSVGrasterizer* rasterizer = nsvgCreateRasterizer();
nsvgRasterize(rasterizer, image, 0, 0, scaling, fRGBAData, scaledWidth, scaledHeight, scaledWidth * 4);

fSize.setSize(scaledWidth, scaledHeight);

nsvgDelete(image);
nsvgDeleteRasterizer(rasterizer);
}

SVG::~SVG()
{
free(fRGBAData);
}

const Size<uint>& SVG::getSize() const noexcept
{
return fSize;
}

const unsigned char* SVG::getRGBAData() const noexcept
{
return fRGBAData;
}

bool SVG::isValid() const noexcept
{
return (fRGBAData != nullptr && fSize.isValid());
}

END_NAMESPACE_DGL

Loading