diff --git a/.vscode/settings.json b/.vscode/settings.json index 15bb3e9d..62643d27 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "*.cc": "cpp", "params.txt": "json", "CMakeLists*.txt": "cmake", - "__config": "c" + "__config": "c", + "cstdio": "cpp" } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9f4e3c4c..dd690e14 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,6 +3,20 @@ // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ + { + "label": "Cleanup workspace (git clean -ffdx)", + "type": "shell", + "command": "git", + "args": [ + "clean", + "-ffdx" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "group": "build", + "problemMatcher": "$gcc" + }, { "label": "[Debug] Build Android Demo", "type": "shell", diff --git a/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java b/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java index 08135a3c..9ab17665 100644 --- a/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java +++ b/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java @@ -32,6 +32,7 @@ public class MainActivity extends AppCompatActivity { public static final String EFFECT_CONFIGS[] = { "", "@curve RGB(0,255)(255,0) @style cm mapping0.jpg 80 80 8 3", // ASCII art (字符画效果) + "@style waveform 0.01 0.01 0.4 0.4", "@beautify face 1 480 640", //Beautify "@adjust lut edgy_amber.png", "@adjust lut filmstock.png", diff --git a/library/src/main/jni/Android.mk b/library/src/main/jni/Android.mk index 1c65e184..3b25df52 100644 --- a/library/src/main/jni/Android.mk +++ b/library/src/main/jni/Android.mk @@ -73,6 +73,9 @@ LOCAL_SRC_FILES := \ $(CGE_SOURCE)/filters/cgeHalftoneFilter.cpp \ $(CGE_SOURCE)/filters/cgeEdgeFilter.cpp \ $(CGE_SOURCE)/filters/cgeEmbossFilter.cpp \ + \ + $(CGE_SOURCE)/filters/cgeWaveformFilter.cpp \ + \ $(CGE_SOURCE)/filters/cgeCrosshatchFilter.cpp \ $(CGE_SOURCE)/filters/cgeLiquifyFilter.cpp \ $(CGE_SOURCE)/filters/cgeRandomBlurFilter.cpp \ diff --git a/library/src/main/jni/cge/common/cgeCommonDefine.cpp b/library/src/main/jni/cge/common/cgeCommonDefine.cpp index c80a5d95..55c697f7 100644 --- a/library/src/main/jni/cge/common/cgeCommonDefine.cpp +++ b/library/src/main/jni/cge/common/cgeCommonDefine.cpp @@ -300,6 +300,7 @@ GLuint cgeGenTextureWithBuffer(const void* bufferData, GLint w, GLint h, GLenum assert(w != 0 && h != 0); GLuint tex; static const GLenum eArrs[] = { GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA }; + static const GLenum eArrsSized8[] = { GL_R8, GL_RG8, GL_RGB8, GL_RGBA8 }; if (channel <= 0 || channel > 4) return 0; const GLenum& internalFormat = eArrs[channel - 1]; @@ -307,7 +308,18 @@ GLuint cgeGenTextureWithBuffer(const void* bufferData, GLint w, GLint h, GLenum glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +#ifdef ANDROID_NDK + if (bufferData == nullptr && dataFmt == GL_UNSIGNED_BYTE) + { + glTexStorage2D(GL_TEXTURE_2D, 1, eArrsSized8[channel - 1], w, h); + } + else + { + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, channelFmt, dataFmt, bufferData); + } +#else glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, channelFmt, dataFmt, bufferData); +#endif glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texWrap); diff --git a/library/src/main/jni/cge/common/cgeGLFunctions.cpp b/library/src/main/jni/cge/common/cgeGLFunctions.cpp index 7b50f633..fd4a53fa 100644 --- a/library/src/main/jni/cge/common/cgeGLFunctions.cpp +++ b/library/src/main/jni/cge/common/cgeGLFunctions.cpp @@ -212,71 +212,182 @@ char* cgeGetScaledBufferInSize(const void* buffer, int& w, int& h, int channel, ///////////////////////////////////////////////// -SharedTexture::SharedTexture(GLuint textureID, int w, int h) +TextureObject::TextureObject(GLuint texture, const CGESizei& size) : + m_texture(texture), m_size(size) { - m_textureID = textureID; - m_refCount = new int(1); - width = w; - height = h; - CGE_LOG_CODE( - if (m_textureID == 0) - CGE_LOG_ERROR("CGESharedTexture : Invalid TextureID!"); - else { - CGE_LOG_INFO("---CGESharedTexture creating, textureID %d, total : %d ###\n", textureID, ++sTextureCount); - }); + if (texture == 0 && size.width != 0 && size.height != 0) + { + resize(size.width, size.height); + } +} + +TextureObject::TextureObject(TextureObject&& t) noexcept : + m_texture(t.texture()), m_size(t.size()) +{ + t.cleanup(false); } -SharedTexture::~SharedTexture() +TextureObject::~TextureObject() { - if (m_refCount == nullptr) + if (m_texture != 0) { - CGE_LOG_CODE( - if (m_textureID != 0) { - CGE_LOG_ERROR("SharedTexture : Error occurred!"); - }); - return; + cleanup(true); } +} - --*m_refCount; - if (*m_refCount <= 0) +TextureObject& TextureObject::operator=(TextureObject&& t) noexcept +{ + if (this == &t) { - clear(); + return *this; } - CGE_LOG_CODE( - else { - CGE_LOG_INFO("@@@ Texture %d deRef count: %d\n", m_textureID, *m_refCount); - }) + m_texture = t.texture(); + m_size = t.size(); + t.cleanup(false); + return *this; } -void SharedTexture::forceRelease(bool bDelTexture) +TextureObject& TextureObject::operator=(TextureInfo&& t) { - assert(m_refCount == nullptr || *m_refCount == 1); // 使用 forceRelease 时 SharedTexture 必须保证只存在一个实例 - if (bDelTexture) - glDeleteTextures(1, &m_textureID); - m_textureID = 0; - CGE_DELETE(m_refCount); - width = 0; - height = 0; - CGE_LOG_CODE( - --sTextureCount;); + m_texture = t.name; + m_size = { t.width, t.height }; + t.name = 0; + return *this; } -void SharedTexture::clear() +void TextureObject::cleanup(bool deleteTexture) { - CGE_LOG_CODE( - if (m_textureID == 0) { - CGE_LOG_ERROR("!!!CGESharedTexture - Invalid TextureID To Release!\n"); - } else { - CGE_LOG_INFO("###CGESharedTexture deleting, textureID %d, now total : %d ###\n", m_textureID, --sTextureCount); - }); + if (deleteTexture && m_texture != 0) + { + assert(glIsTexture(m_texture)); + CGE_DELETE_GL_OBJS(glDeleteTextures, m_texture); + } + m_texture = 0; + m_size.set(0, 0); +} - assert(*m_refCount == 0); // 未知错误 +bool TextureObject::resize(int w, int h, const void* buffer, GLenum format) +{ + if (m_texture == 0 || m_size.width != w || m_size.height != h || buffer != nullptr) + { + if (w == 0 || h == 0) + { + assert(0 && "TextureObject::resize must not be 0!"); + return false; + } - glDeleteTextures(1, &m_textureID); - m_textureID = 0; + int channel; + switch (format) + { + case GL_LUMINANCE: + channel = 1; + break; + case GL_LUMINANCE_ALPHA: + channel = 2; + break; + case GL_RGB: + channel = 3; + break; + case GL_RGBA: + channel = 4; + break; + default: + assert(0); + channel = 4; + break; + } - CGE_DELETE(m_refCount); - width = 0; - height = 0; + if (m_texture == 0) + { + m_texture = cgeGenTextureWithBuffer(buffer, w, h, format, GL_UNSIGNED_BYTE, channel); + m_size.set(w, h); + } + else + { + glBindTexture(GL_TEXTURE_2D, m_texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (m_size.width != w || m_size.height != h) + { + m_size.set(w, h); + glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, buffer); + } + else + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, buffer); + } + } + + return true; + } + return false; } + +////////////// + +FrameBufferWithTexture::~FrameBufferWithTexture() +{ + if (m_renderBuffer != 0) + { + CGE_DELETE_GL_OBJS(glDeleteRenderbuffers, m_renderBuffer); + } +} + +void FrameBufferWithTexture::bindTexture2D(GLsizei w, GLsizei h, const void* buffer) +{ + if (resize(w, h, buffer)) + { + FrameBuffer::bindTexture2D(m_texture); + + // auto resize renderbuffer if exist. + if (m_renderBuffer != 0) + { + attachDepthBuffer(); + } + assert(checkStatus()); + } + else + { + FrameBuffer::bind(); + } +} + +void FrameBufferWithTexture::attachDepthBuffer() +{ + bool shouldCreate = false; + + if (m_renderBuffer == 0) + { + shouldCreate = true; + } + else + { + GLint param[2] = { 0, 0 }; + glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, param); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, param + 1); + shouldCreate = (param[0] != m_size.width || param[1] != m_size.height); + } + + if (shouldCreate) + { + if (m_renderBuffer == 0) + glGenRenderbuffers(1, &m_renderBuffer); + + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); + glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, m_size.width, m_size.height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_renderBuffer); + } +} + +bool FrameBufferWithTexture::checkStatus() +{ + GLenum ret = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (ret != GL_FRAMEBUFFER_COMPLETE) + { + CGE_LOG_ERROR("Frame buffer incomplete: %x!\n", ret); + } + return ret == GL_FRAMEBUFFER_COMPLETE; +} + } // namespace CGE diff --git a/library/src/main/jni/cge/common/cgeGLFunctions.h b/library/src/main/jni/cge/common/cgeGLFunctions.h index 61c8da9f..f317cdcd 100644 --- a/library/src/main/jni/cge/common/cgeGLFunctions.h +++ b/library/src/main/jni/cge/common/cgeGLFunctions.h @@ -74,71 +74,6 @@ inline GLint cgeGetMaxTextureSize() return n - 1; } -class SharedTexture -{ -public: - SharedTexture(int w = 0, int h = 0) : - m_textureID(0), m_refCount(nullptr), width(w), height(h) {} - SharedTexture(GLuint textureID, int w, int h); - - SharedTexture(const SharedTexture& other) : - m_textureID(0), m_refCount(nullptr) - { - *this = other; - } - - ~SharedTexture(); - - inline SharedTexture& operator=(const SharedTexture& other) - { - assert(this != &other && (other.m_refCount == nullptr || other.m_textureID != 0)); - - if (m_refCount != nullptr && --*m_refCount <= 0) - { - clear(); - } - - m_textureID = other.m_textureID; - m_refCount = other.m_refCount; - if (m_refCount) - { - ++*m_refCount; - CGE_LOG_INFO("CGESharedTexture assgin: textureID %d, refCount: %d\n", m_textureID, *m_refCount); - } - - width = other.width; - height = other.height; - return *this; - } - - inline GLuint texID() const { return m_textureID; } - - inline void bindToIndex(GLint index) const - { - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, m_textureID); - } - - void forceRelease(bool bDelTexture); - - //特殊用法, 与 forceRelease 配对使用 - inline void forceAssignTextureID(GLuint texID) - { - m_textureID = texID; - } - -public: - int width; // public, for easy accessing. - int height; - -protected: - void clear(); - -private: - GLuint m_textureID; - mutable int* m_refCount; -}; - class FrameBuffer { public: @@ -147,16 +82,6 @@ class FrameBuffer inline void bind() const { glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); } - inline void bindTexture2D(const SharedTexture& texture, GLenum attachment = GL_COLOR_ATTACHMENT0) const - { - bindTexture2D(texture.texID(), texture.width, texture.height, attachment); - } - - inline void bindTexture2D(const SharedTexture& texture, GLsizei x, GLsizei y, GLsizei w, GLsizei h, GLenum attachment = GL_COLOR_ATTACHMENT0) const - { - bindTexture2D(texture.texID(), x, y, w, h, attachment); - } - inline void bindTexture2D(GLuint texID, GLsizei w, GLsizei h, GLenum attachment = GL_COLOR_ATTACHMENT0) const { bindTexture2D(texID, 0, 0, w, h, attachment); @@ -179,9 +104,9 @@ class FrameBuffer glViewport(x, y, w, h); } - inline GLuint getID() { return m_framebuffer; } + inline GLuint fbo() { return m_framebuffer; } -private: +protected: GLuint m_framebuffer; }; @@ -223,6 +148,68 @@ struct CGESizef GLfloat height; }; +struct TextureInfo +{ + GLuint name{}; + int width{}, height{}; +}; + +class TextureObject +{ +public: + virtual ~TextureObject(); + explicit TextureObject(GLuint texture = 0, const CGESizei& size = CGESizei()); + explicit TextureObject(const TextureObject&) = delete; + TextureObject(TextureObject&&) noexcept; + explicit TextureObject(TextureInfo&& t) : + m_texture(t.name), m_size(t.width, t.height) { t.name = 0; } + + GLuint texture() const { return m_texture; } + GLint width() const { return m_size.width; } + GLint height() const { return m_size.height; } + CGESizei size() const { return m_size; } + + void cleanup(bool deleteTexture = true); + + // 注意, format 不支持改变, 如果有相关需求需要自行添加 + bool resize(int w, int h, const void* buffer = nullptr, GLenum format = GL_RGBA); + inline bool updateTextureData(int w, int h, const void* buffer = nullptr, GLenum format = GL_RGBA) + { + return resize(w, h, buffer, format); + } + + TextureObject& operator=(TextureObject&& t) noexcept; + TextureObject& operator=(TextureInfo&& t); + +protected: + GLuint m_texture = 0; + CGESizei m_size; +}; + +class FrameBufferWithTexture : protected FrameBuffer, public TextureObject +{ +public: + using FrameBuffer::FrameBuffer; + ~FrameBufferWithTexture() override; + + GLuint renderbuffer() const { return m_renderBuffer; } + + // act like resize. + // it will resize renderbuffer if exist. + void bindTexture2D(GLsizei w, GLsizei h, const void* buffer = nullptr); + + void attachDepthBuffer(); + + bool checkStatus(); + + using FrameBuffer::bind; + using FrameBuffer::fbo; + +private: + using TextureObject::resize; + GLuint m_renderBuffer = 0; +}; + struct CGELuminance { enum diff --git a/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp b/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp index 97430c9c..8ba92182 100644 --- a/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp +++ b/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp @@ -137,4 +137,8 @@ CGEBeautifyFilter* createBeautifyFilter() COMMON_FUNC(CGEBeautifyFilter); } +CGEWaveformFilter* createWaveformFilter() +{ + COMMON_FUNC(CGEWaveformFilter); +} } // namespace CGE diff --git a/library/src/main/jni/cge/filters/cgeAdvancedEffects.h b/library/src/main/jni/cge/filters/cgeAdvancedEffects.h index 05f2c706..a5c756e4 100644 --- a/library/src/main/jni/cge/filters/cgeAdvancedEffects.h +++ b/library/src/main/jni/cge/filters/cgeAdvancedEffects.h @@ -22,6 +22,7 @@ #include "cgePolkaDotFilter.h" #include "cgeRandomBlurFilter.h" #include "cgeSketchFilter.h" +#include "cgeWaveformFilter.h" namespace CGE { @@ -48,6 +49,8 @@ CGELerpblurFilter* createLerpblurFilter(); CGESketchFilter* createSketchFilter(); CGEBeautifyFilter* createBeautifyFilter(); + +CGEWaveformFilter* createWaveformFilter(); } // namespace CGE #endif diff --git a/library/src/main/jni/cge/filters/cgeDataParsingEngine.cpp b/library/src/main/jni/cge/filters/cgeDataParsingEngine.cpp index 8591c537..92fc3fb6 100644 --- a/library/src/main/jni/cge/filters/cgeDataParsingEngine.cpp +++ b/library/src/main/jni/cge/filters/cgeDataParsingEngine.cpp @@ -1093,6 +1093,23 @@ CGEImageFilterInterface* CGEDataParsingEngine::advancedStyleParser(const char* p { ADJUSTHELP_COMMON_FUNC2(pstr, CGECrosshatchFilter, setCrosshatchSpacing, setLineWidth); } + else if (strcmp(buffer, "waveform") == 0) + { + float x, y, width, height; + if (sscanf(pstr, "%f%*c%f%*c%f%*c%f", &x, &y, &width, &height) < 4) + { + LOG_ERROR_PARAM(pstr); + return nullptr; + } + + CGEWaveformFilter* filter = createWaveformFilter(); + if (filter != nullptr) + { + proc = filter; + filter->setFormPosition(x, y); + filter->setFormSize(width, height); + } + } else if (strcmp(buffer, "edge") == 0) { ADJUSTHELP_COMMON_FUNC2(pstr, CGEEdgeSobelFilter, setIntensity, setStride); diff --git a/library/src/main/jni/cge/filters/cgeWaveformFilter.cpp b/library/src/main/jni/cge/filters/cgeWaveformFilter.cpp new file mode 100644 index 00000000..f903f64e --- /dev/null +++ b/library/src/main/jni/cge/filters/cgeWaveformFilter.cpp @@ -0,0 +1,110 @@ +#include "cgeWaveformFilter.h" + +#define USING_ALPHA 1 /// 视图增加一个 80% 的半透明 + +static CGEConstString s_cshWaveform = "#version 310 es\n" CGE_SHADER_STRING( + precision highp float; + precision highp int; + layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + layout(rgba8ui, binding = 0) uniform readonly highp uimage2D inputImageTexture; + layout(rgba8ui, binding = 1) uniform writeonly highp uimage2D outputImage; + + void main() { + ivec2 texCoord = ivec2(gl_GlobalInvocationID); + uvec3 color = imageLoad(inputImageTexture, texCoord).rgb; + float lum = dot(vec3(color.rgb), vec3(0.299, 0.587, 0.114)); + ivec2 newLoc = ivec2(texCoord.x, uint(lum)); + imageStore(outputImage, newLoc, uvec4(255, 255, 255, 255)); + }); + +static CGEConstString s_cshClearImage = "#version 310 es\n" CGE_SHADER_STRING( + precision highp float; + precision highp int; + layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + layout(rgba8ui, binding = 1) uniform writeonly highp uimage2D outputImage; + + void main() { + ivec2 texCoord = ivec2(gl_GlobalInvocationID); + imageStore(outputImage, texCoord, uvec4(0, 0, 0, 255)); + }); + +namespace CGE +{ +CGEWaveformFilter::~CGEWaveformFilter() = default; + +bool CGEWaveformFilter::init() +{ + if (m_program.initWithComputeShader(s_cshWaveform) && m_clearImageProgram.initWithComputeShader(s_cshClearImage)) + { + m_program.bind(); + setFormPosition(0.1f, 0.1f); + setFormSize(0.3f, 0.3f); + m_drawer.reset(TextureDrawer::create()); + m_drawer->setFlipScale(1.0f, -1.0f); // flip upside down, meet the gl coord. + m_diagramTexture = std::make_unique(); + return true; + } + + CGE_LOG_ERROR(R"(CGEWaveformFilter::init failed. This filter needs GLES3.1 and later! + Only GLES 3.1+ support image store. + You need to imp a fallback version which reading pixels every frame like `cgeColorMappingFilter` +)"); + CGE_LOG_ERROR("Failed Compute Shader: %s\n", s_cshWaveform); + return false; +} + +void CGEWaveformFilter::render2Texture(CGEImageHandlerInterface* handler, GLuint srcTexture, GLuint vertexBufferID) +{ + auto&& sz = handler->getOutputFBOSize(); + if (sz.width != m_diagramTexture->width() || m_diagramTexture->texture() == 0) + { + m_diagramTexture->resize(sz.width, 256); + } + + glBindImageTexture(0, handler->getTargetTextureID(), 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8UI); + glBindImageTexture(1, m_diagramTexture->texture(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8UI); + + // Clear diagram texture before frame. + // You can also use glClear(GL_COLOR_BUFFER_BIT) on some devices. + { + // @attention: glClear does not work on some devices. e.g. Mali-G76 + // Perform clear with a compute shader. + m_clearImageProgram.bind(); + glDispatchCompute(sz.width, sz.height, 1); + } + + // glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + m_program.bind(); + + glDispatchCompute(sz.width, sz.height, 1); + + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + +#if USING_ALPHA + glEnable(GL_BLEND); + glBlendColor(1, 1, 1, 0.8); + glBlendFunc(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA); +#endif + + handler->setAsTarget(); + glViewport(m_position[0] * sz.width, m_position[1] * sz.height, m_size[0] * sz.width, m_size[1] * sz.height); + m_drawer->drawTexture(m_diagramTexture->texture()); + +#if USING_ALPHA + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); +#endif +} + +void CGEWaveformFilter::setFormPosition(float left, float top) +{ + m_position = { left, top }; +} + +void CGEWaveformFilter::setFormSize(float width, float height) +{ + m_size = { width, height }; +} + +} // namespace CGE \ No newline at end of file diff --git a/library/src/main/jni/cge/filters/cgeWaveformFilter.h b/library/src/main/jni/cge/filters/cgeWaveformFilter.h new file mode 100644 index 00000000..55933473 --- /dev/null +++ b/library/src/main/jni/cge/filters/cgeWaveformFilter.h @@ -0,0 +1,38 @@ +#ifndef _WAVEFORMFILTER_H_ +#define _WAVEFORMFILTER_H_ + +#include "cgeImageFilter.h" +#include "cgeTextureUtils.h" +#include "cgeVec.h" + +namespace CGE +{ +class CGEWaveformFilter : public CGEImageFilterInterface +{ +public: + ~CGEWaveformFilter() override; + + /** + * @brief 左上角的点 + */ + void setFormPosition(float left, float top); + /** + * @brief 相对大小 + */ + void setFormSize(float width, float height); + + bool init() override; + + void render2Texture(CGEImageHandlerInterface* handler, GLuint srcTexture, GLuint vertexBufferID) override; + +protected: + std::unique_ptr m_drawer; + std::unique_ptr m_diagramTexture; + ProgramObject m_clearImageProgram; + + Vec2f m_position; + Vec2f m_size; +}; +} // namespace CGE + +#endif \ No newline at end of file