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
  • Loading branch information
m0dB committed Oct 22, 2022
1 parent f3b4a97 commit ceca510
Show file tree
Hide file tree
Showing 43 changed files with 3,866 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 ceca510

Please sign in to comment.