Skip to content

Commit

Permalink
add qopengl renderers, qopengl rgbwaveformwidget and qopengl wvumeter…
Browse files Browse the repository at this point in the history
…gl and qopengl wspinny

destroy textures in their context (avoids warning)

render spinnies with qopengl
  • Loading branch information
m0dB committed Oct 22, 2022
1 parent f3b4a97 commit 5f34265
Show file tree
Hide file tree
Showing 41 changed files with 2,888 additions and 29 deletions.
19 changes: 17 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1146,19 +1146,34 @@ else()
src/widget/woverviewhsv.cpp
src/widget/woverviewlmh.cpp
src/widget/woverviewrgb.cpp
src/widget/wspinny.cpp
src/widget/wvumetergl.cpp
src/widget/wwaveformviewer.cpp
)
if(QOPENGL)
target_sources(mixxx-lib PRIVATE
src/widget/openglwindow.cpp
src/widget/wglwidgetqopengl.cpp
src/widget/qopengl/wvumetergl.cpp
src/widget/qopengl/wspinny.cpp
src/waveform/renderers/qopengl/waveformrenderbackground.cpp
src/waveform/renderers/qopengl/waveformrenderbeat.cpp
src/waveform/renderers/qopengl/waveformrenderer.cpp
src/waveform/renderers/qopengl/waveformrendererendoftrack.cpp
src/waveform/renderers/qopengl/waveformrendererpreroll.cpp
src/waveform/renderers/qopengl/waveformrendererrgb.cpp
src/waveform/renderers/qopengl/waveformrenderersignalbase.cpp
src/waveform/renderers/qopengl/waveformrendermark.cpp
src/waveform/renderers/qopengl/waveformrendermarkrange.cpp
src/waveform/widgets/qopengl/waveformwidget.cpp
src/waveform/widgets/qopengl/rgbwaveformwidget.cpp
src/waveform/widgets/qopengl/waveformwidget.cpp
src/waveform/widgets/qopengl/rgbwaveformwidget.cpp
)
else()
target_sources(mixxx-lib PRIVATE
src/waveform/sharedglcontext.cpp
src/widget/wglwidgetqglwidget.cpp
src/widget/wvumetergl.cpp
src/widget/wspinny.cpp
)
endif()
endif()
Expand Down
62 changes: 62 additions & 0 deletions src/waveform/renderers/qopengl/fixedpointcalc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/// functions to do fixed point calculations, used by qopengl::WaveformRendererRGB

// float to fixed point with 8 fractional bits, clipped at 4.0
inline uint32_t toFrac8(float x) {
return std::min<uint32_t>(static_cast<uint32_t>(std::max(x, 0.f) * 256.f), 4 * 256);
}

// scaled sqrt lookable table to convert maxAll and maxAllNext as calculated
// in updatePaintNode back to y coordinates
class Frac16SqrtTableSingleton {
public:
static constexpr size_t frac16sqrtTableSize{(3 * 4 * 255 * 256) / 16 + 1};

static Frac16SqrtTableSingleton& getInstance() {
static Frac16SqrtTableSingleton instance;
return instance;
}

inline float get(uint32_t x) const {
// The maximum value of fact16x can be (as uint32_t) 3 * 4 * 255 * 256,
// which would be exessive for the table size. We divide by 16 in order
// to get a more reasonable size.
return m_table[x >> 4];
}

private:
float* m_table;
Frac16SqrtTableSingleton()
: m_table(new float[frac16sqrtTableSize]) {
// In the original implementation, the result of sqrt(maxAll) is divided
// by sqrt(3 * 255 * 255);
// We get rid of that division and bake it into this table.
// Additionally, we divide the index for the lookup by 16 (see get(...)),
// so we need to invert that here.
const float f = (3.f * 255.f * 255.f / 16.f);
for (uint32_t i = 0; i < frac16sqrtTableSize; i++) {
m_table[i] = std::sqrt(static_cast<float>(i) / f);
}
}
~Frac16SqrtTableSingleton() {
delete[] m_table;
}
Frac16SqrtTableSingleton(const Frac16SqrtTableSingleton&) = delete;
Frac16SqrtTableSingleton& operator=(const Frac16SqrtTableSingleton&) = delete;
};

inline float frac16_sqrt(uint32_t x) {
return Frac16SqrtTableSingleton::getInstance().get(x);
}

inline uint32_t frac8Pow2ToFrac16(uint32_t x) {
// x is the result of multiplying two fixedpoint values with 8 fraction bits,
// thus x has 16 fraction bits, which is also what we want to return for this
// function. We would naively return (x * x) >> 16, but x * x would overflow
// the 32 bits for values > 1, so we shift before multiplying.
x >>= 8;
return (x * x);
}

inline uint32_t math_max_u32(uint32_t a, uint32_t b, uint32_t c) {
return std::max(a, std::max(b, c));
}
18 changes: 18 additions & 0 deletions src/waveform/renderers/qopengl/iwaveformrenderer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <QOpenGLFunctions>

/// Interface for QOpenGL-based waveform renderers

namespace qopengl {
class IWaveformRenderer;
}

class qopengl::IWaveformRenderer : public QOpenGLFunctions {
public:
virtual void initializeGL() {
}
virtual void resizeGL(int w, int h) {
}
virtual void renderGL() = 0;
};
73 changes: 73 additions & 0 deletions src/waveform/renderers/qopengl/waveformrenderbackground.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "waveform/renderers/qopengl/waveformrenderbackground.h"

#include "waveform/renderers/waveformwidgetrenderer.h"
#include "widget/wimagestore.h"
#include "widget/wskincolor.h"
#include "widget/wwidget.h"

using namespace qopengl;

WaveformRenderBackground::WaveformRenderBackground(
WaveformWidgetRenderer* waveformWidgetRenderer)
: WaveformRenderer(waveformWidgetRenderer),
m_backgroundColor(0, 0, 0) {
}

WaveformRenderBackground::~WaveformRenderBackground() {
}

void WaveformRenderBackground::setup(const QDomNode& node,
const SkinContext& context) {
m_backgroundColor = m_waveformRenderer->getWaveformSignalColors()->getBgColor();
QString backgroundPixmapPath = context.selectString(node, "BgPixmap");
if (!backgroundPixmapPath.isEmpty()) {
m_backgroundPixmapPath = context.makeSkinPath(backgroundPixmapPath);
}
setDirty(true);
}

void WaveformRenderBackground::renderGL() {
if (isDirty()) {
// TODO @m0dB
// generateImage();
}

// If there is no background image, just fill the painter with the
// background color.
if (m_backgroundImage.isNull()) {
glClearColor(m_backgroundColor.redF(),
m_backgroundColor.greenF(),
m_backgroundColor.blueF(),
1.f);
glClear(GL_COLOR_BUFFER_BIT);
return;
}

//painter->drawImage(QPoint(0, 0), m_backgroundImage);
}

void WaveformRenderBackground::generateImage() {
m_backgroundImage = QImage();
if (!m_backgroundPixmapPath.isEmpty()) {
QImage backgroundImage = *WImageStore::getImage(
m_backgroundPixmapPath,
scaleFactor());

if (!backgroundImage.isNull()) {
if (backgroundImage.width() == m_waveformRenderer->getWidth() &&
backgroundImage.height() == m_waveformRenderer->getHeight()) {
m_backgroundImage = backgroundImage.convertToFormat(QImage::Format_RGB32);
} else {
m_backgroundImage = QImage(m_waveformRenderer->getWidth(),
m_waveformRenderer->getHeight(),
QImage::Format_RGB32);
QPainter painter(&m_backgroundImage);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.drawImage(m_backgroundImage.rect(),
backgroundImage,
backgroundImage.rect());
}
}
}
setDirty(false);
}
31 changes: 31 additions & 0 deletions src/waveform/renderers/qopengl/waveformrenderbackground.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <QColor>
#include <QImage>

#include "util/class.h"
#include "waveform/renderers/qopengl/waveformrenderer.h"

class QDomNode;
class SkinContext;

namespace qopengl {
class WaveformRenderBackground;
}
class qopengl::WaveformRenderBackground : public qopengl::WaveformRenderer {
public:
explicit WaveformRenderBackground(WaveformWidgetRenderer* waveformWidgetRenderer);
~WaveformRenderBackground() override;

void setup(const QDomNode& node, const SkinContext& context) override;
void renderGL() override;

private:
void generateImage();

QString m_backgroundPixmapPath;
QColor m_backgroundColor;
QImage m_backgroundImage;

DISALLOW_COPY_AND_ASSIGN(WaveformRenderBackground);
};
149 changes: 149 additions & 0 deletions src/waveform/renderers/qopengl/waveformrenderbeat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#include "waveform/renderers/qopengl/waveformrenderbeat.h"

#include <QDomNode>

#include "control/controlobject.h"
#include "skin/legacy/skincontext.h"
#include "track/track.h"
#include "waveform/widgets/qopengl/waveformwidget.h"
#include "widget/wskincolor.h"
#include "widget/wwidget.h"

using namespace qopengl;

WaveformRenderBeat::WaveformRenderBeat(WaveformWidgetRenderer* waveformWidget)
: WaveformRenderer(waveformWidget) {
m_beatLineVertices.resize(1024);
}

WaveformRenderBeat::~WaveformRenderBeat() {
}

void WaveformRenderBeat::initializeGL() {
QString vertexShaderCode =
"\
uniform mat4 matrix;\n\
attribute vec4 position;\n\
void main()\n\
{\n\
gl_Position = matrix * position;\n\
}\n";

QString fragmentShaderCode =
"\
uniform vec4 color;\n\
void main()\n\
{\n\
gl_FragColor = color;\n\
}\n";

if (!m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderCode)) {
return;
}

if (!m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderCode)) {
return;
}

if (!m_shaderProgram.link()) {
return;
}

if (!m_shaderProgram.bind()) {
return;
}
}

void WaveformRenderBeat::setup(const QDomNode& node, const SkinContext& context) {
m_beatColor.setNamedColor(context.selectString(node, "BeatColor"));
m_beatColor = WSkinColor::getCorrectColor(m_beatColor).toRgb();
}

void WaveformRenderBeat::renderGL() {
TrackPointer trackInfo = m_waveformRenderer->getTrackInfo();

if (!trackInfo) {
return;
}

mixxx::BeatsPointer trackBeats = trackInfo->getBeats();
if (!trackBeats) {
return;
}

int alpha = m_waveformRenderer->getBeatGridAlpha();
if (alpha == 0) {
return;
}

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

m_beatColor.setAlphaF(alpha / 100.0);

const int trackSamples = m_waveformRenderer->getTrackSamples();
if (trackSamples <= 0) {
return;
}

const double firstDisplayedPosition =
m_waveformRenderer->getFirstDisplayedPosition();
const double lastDisplayedPosition =
m_waveformRenderer->getLastDisplayedPosition();

const auto startPosition = mixxx::audio::FramePos::fromEngineSamplePos(
firstDisplayedPosition * trackSamples);
const auto endPosition = mixxx::audio::FramePos::fromEngineSamplePos(
lastDisplayedPosition * trackSamples);
auto it = trackBeats->iteratorFrom(startPosition);

// TODO @m0dB use rendererWidth for vertical orientation
// and apply a 90 degrees rotation to the matrix
//const float rendererWidth = m_waveformRenderer->getWidth();
const float rendererHeight = m_waveformRenderer->getHeight();

int vertexCount = 0;

for (; it != trackBeats->cend() && *it <= endPosition; ++it) {
double beatPosition = it->toEngineSamplePos();
double xBeatPoint =
m_waveformRenderer->transformSamplePositionInRendererWorld(beatPosition);

xBeatPoint = qRound(xBeatPoint);

// If we don't have enough space, double the size.
if (vertexCount >= m_beatLineVertices.size()) {
m_beatLineVertices.resize(m_beatLineVertices.size() * 2);
}

m_beatLineVertices[vertexCount++] = xBeatPoint;
m_beatLineVertices[vertexCount++] = 0.f;
m_beatLineVertices[vertexCount++] = xBeatPoint + 1;
m_beatLineVertices[vertexCount++] = 0.f;
m_beatLineVertices[vertexCount++] = xBeatPoint;
m_beatLineVertices[vertexCount++] = rendererHeight;
m_beatLineVertices[vertexCount++] = xBeatPoint;
m_beatLineVertices[vertexCount++] = rendererHeight;
m_beatLineVertices[vertexCount++] = xBeatPoint + 1;
m_beatLineVertices[vertexCount++] = rendererHeight;
m_beatLineVertices[vertexCount++] = xBeatPoint + 1;
m_beatLineVertices[vertexCount++] = 0.f;
}
m_shaderProgram.bind();

int vertexLocation = m_shaderProgram.attributeLocation("position");
int matrixLocation = m_shaderProgram.uniformLocation("matrix");
int colorLocation = m_shaderProgram.uniformLocation("color");

QMatrix4x4 matrix;
matrix.ortho(QRectF(0, 0, m_waveformRenderer->getWidth(), m_waveformRenderer->getHeight()));

m_shaderProgram.enableAttributeArray(vertexLocation);
m_shaderProgram.setAttributeArray(
vertexLocation, GL_FLOAT, m_beatLineVertices.constData(), 2);

m_shaderProgram.setUniformValue(matrixLocation, matrix);
m_shaderProgram.setUniformValue(colorLocation, m_beatColor);

glDrawArrays(GL_TRIANGLES, 0, vertexCount / 2);
}
Loading

0 comments on commit 5f34265

Please sign in to comment.