diff --git a/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java b/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java index 9ab17665..00d12207 100644 --- a/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java +++ b/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java @@ -31,8 +31,9 @@ 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 hist 0.01 0.01 0.4 0.4 0.0 0.0 0.0 0.9", "@style waveform 0.01 0.01 0.4 0.4", + "@curve RGB(0,255)(255,0) @style cm mapping0.jpg 80 80 8 3", // ASCII art (字符画效果) "@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 b8e8c6fb..6459ed47 100644 --- a/library/src/main/jni/Android.mk +++ b/library/src/main/jni/Android.mk @@ -75,6 +75,7 @@ LOCAL_SRC_FILES := \ $(CGE_SOURCE)/filters/cgeEmbossFilter.cpp \ \ $(CGE_SOURCE)/filters/cgeWaveformFilter.cpp \ + $(CGE_SOURCE)/filters/cgeHistogramFilter.cpp \ \ $(CGE_SOURCE)/filters/cgeCrosshatchFilter.cpp \ $(CGE_SOURCE)/filters/cgeLiquifyFilter.cpp \ diff --git a/library/src/main/jni/cge/common/cgeGLFunctions.cpp b/library/src/main/jni/cge/common/cgeGLFunctions.cpp index fd4a53fa..ff20ff20 100644 --- a/library/src/main/jni/cge/common/cgeGLFunctions.cpp +++ b/library/src/main/jni/cge/common/cgeGLFunctions.cpp @@ -1,4 +1,4 @@ -/* +/* * cgeGLFunctions.cpp * * Created on: 2013-12-5 @@ -6,7 +6,7 @@ */ #include "cgeGLFunctions.h" - +#include <EGL/egl.h> #include <cmath> CGE_LOG_CODE( @@ -390,4 +390,75 @@ bool FrameBufferWithTexture::checkStatus() return ret == GL_FRAMEBUFFER_COMPLETE; } +////////////// + + FrameBufferTexture::FrameBufferTexture() : m_fbo(0), m_texture(0), m_width(0), m_height(0) + { + glGenFramebuffers(1, &m_fbo); + assert(m_fbo != 0); + } + + FrameBufferTexture::~FrameBufferTexture() + { + glDeleteFramebuffers(1, &m_fbo); + glDeleteTextures(1, &m_texture); + } + + GLuint FrameBufferTexture::texture() const + { + return m_texture; + } + + GLsizei FrameBufferTexture::width() const + { + return m_width; + } + + GLsizei FrameBufferTexture::height() const + { + return m_height; + } + + void FrameBufferTexture::bindTexture2D(GLsizei width, GLsizei height) + { + if (m_texture == 0) + { + glGenTextures(1, &m_texture); + assert(m_texture != 0); + } + + glBindTexture(GL_TEXTURE_2D, m_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + m_width = width; + m_height = height; + } + + void FrameBufferTexture::bind() + { + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0); + } + + unsigned char* FrameBufferTexture::mapBuffer() + { + glBindTexture(GL_TEXTURE_2D, m_texture); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + const auto bufferSize = m_width * m_height * sizeof(float); + auto buffer = new unsigned char[bufferSize]; + glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); +// glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, buffer); + return buffer; + } + + void FrameBufferTexture::unmapBuffer() + { + glBindTexture(GL_TEXTURE_2D, 0); + } + } // namespace CGE diff --git a/library/src/main/jni/cge/common/cgeGLFunctions.h b/library/src/main/jni/cge/common/cgeGLFunctions.h index f317cdcd..d1596248 100644 --- a/library/src/main/jni/cge/common/cgeGLFunctions.h +++ b/library/src/main/jni/cge/common/cgeGLFunctions.h @@ -1,4 +1,4 @@ -/* +/* * cgeGLFunctions.h * * Created on: 2013-12-5 @@ -103,13 +103,14 @@ class FrameBuffer bindTexture2D(texID, attachment); glViewport(x, y, w, h); } - inline GLuint fbo() { return m_framebuffer; } + protected: GLuint m_framebuffer; }; + struct CGESizei { CGESizei() : @@ -210,6 +211,27 @@ class FrameBufferWithTexture : protected FrameBuffer, public TextureObject GLuint m_renderBuffer = 0; }; +class FrameBufferTexture + { + public: + FrameBufferTexture(); + ~FrameBufferTexture(); + + GLuint texture() const; + GLsizei width() const; + GLsizei height() const; + void bindTexture2D(GLsizei width, GLsizei height); + void bind(); + unsigned char* mapBuffer(); + void unmapBuffer(); + + private: + GLuint m_fbo; + GLuint m_texture; + GLsizei m_width; + GLsizei m_height; +}; + struct CGELuminance { enum diff --git a/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp b/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp index 8ba92182..bf161dc2 100644 --- a/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp +++ b/library/src/main/jni/cge/filters/cgeAdvancedEffects.cpp @@ -137,6 +137,11 @@ CGEBeautifyFilter* createBeautifyFilter() COMMON_FUNC(CGEBeautifyFilter); } +CGEHistogramFilter* createHistogramFilter() +{ + COMMON_FUNC(CGEHistogramFilter); +} + CGEWaveformFilter* createWaveformFilter() { COMMON_FUNC(CGEWaveformFilter); diff --git a/library/src/main/jni/cge/filters/cgeAdvancedEffects.h b/library/src/main/jni/cge/filters/cgeAdvancedEffects.h index a5c756e4..aac44680 100644 --- a/library/src/main/jni/cge/filters/cgeAdvancedEffects.h +++ b/library/src/main/jni/cge/filters/cgeAdvancedEffects.h @@ -23,6 +23,7 @@ #include "cgeRandomBlurFilter.h" #include "cgeSketchFilter.h" #include "cgeWaveformFilter.h" +#include "cgeHistogramFilter.h" namespace CGE { @@ -51,6 +52,7 @@ CGESketchFilter* createSketchFilter(); CGEBeautifyFilter* createBeautifyFilter(); CGEWaveformFilter* createWaveformFilter(); +CGEHistogramFilter* createHistogramFilter(); } // namespace CGE #endif diff --git a/library/src/main/jni/cge/filters/cgeDataParsingEngine.cpp b/library/src/main/jni/cge/filters/cgeDataParsingEngine.cpp index 92fc3fb6..3bcfd6e8 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, "hist") == 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; + } + + CGEHistogramFilter* filter = createHistogramFilter(); + if (filter != nullptr) + { + proc = filter; + filter->setFormPosition(x, y); + filter->setFormSize(width, height); + } + } else if (strcmp(buffer, "waveform") == 0) { float x, y, width, height; diff --git a/library/src/main/jni/cge/filters/cgeHistogramFilter.cpp b/library/src/main/jni/cge/filters/cgeHistogramFilter.cpp new file mode 100644 index 00000000..7e50c6ff --- /dev/null +++ b/library/src/main/jni/cge/filters/cgeHistogramFilter.cpp @@ -0,0 +1,126 @@ + +#include "cgeHistogramFilter.h" + +#include <EGL/egl.h> +#include <array> + +static CGEConstString s_vshHistogram = "#version 320 es\n" CGE_SHADER_STRING_PRECISION_H( + layout(location = 0) in vec2 position; + layout(location = 0) out vec2 textureCoordinate; + void main() { + gl_Position = vec4(position, 0.0, 1.0); + textureCoordinate = (position.xy + 1.0) / 2.0; + }); + +static CGEConstString s_fshHistogram = "#version 320 es\n" CGE_SHADER_STRING( + precision highp float; + precision highp int; + layout(location = 0) in vec2 textureCoordinate; + layout(rgba8ui, binding = 0) uniform readonly highp uimage2D inputImageTexture; + layout(rgba8ui, binding = 1) uniform writeonly highp uimage2D outputImage; + layout(location = 0) out vec4 fragColor; + + void main() { + fragColor = vec4(1.0); + + vec4 color = texture(inputImageTexture, textureCoordinate); + float lum = dot(color.rgb, vec3(0.299, 0.587, 0.114)); + int newLoc = int(lum * 255.0); + if (newLoc >= 0 && newLoc < 256) { + //vec2 location = vec2(newLoc, 0.0); + ivec2 location = ivec2(newLoc, 0); + atomicAdd(imageAtomic(outputImage, location), 1); + //imageAtomic(outputImage, ivec2(location.x, location.y), uvec4(1, 0, 0, 0)); + } + }); + +namespace CGE { + + CGEHistogramFilter::~CGEHistogramFilter() {} + + bool CGEHistogramFilter::init() { + if (initShadersFromString(s_vshHistogram, s_fshHistogram)) { + 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); + m_renderTarget = std::make_unique<FrameBufferTexture>(); + return true; + } + + return false; + } + + void CGEHistogramFilter::render2Texture(CGEImageHandlerInterface* handler, GLuint srcTexture, GLuint vertexBufferID) { + auto&& sz = handler->getOutputFBOSize(); + if (sz.width != m_renderTarget->width() || m_renderTarget->texture() == 0) { + m_renderTarget->bindTexture2D(sz.width, sz.height); + } + m_renderTarget->bind(); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + glFinish(); + + m_program.bind(); + glColorMask(false, false, false, false); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, srcTexture); + + glUniform1i(m_program.uniformLocation("inputImageTexture"), 0); + + // 获取纹理尺寸,用于计算像素位置 + const auto texSize = handler->getOutputFBOSize(); + // 清空直方图数组 + std::array<float, 256> histogram{}; + // 遍历纹理像素,计算亮度直方图 + std::vector<unsigned char> pixels(texSize.width * texSize.height * 4); + glReadPixels(0, 0, texSize.width, texSize.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); +// glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + for (int i = 0; i < pixels.size(); i += 4) { + const auto r = pixels[i]; + const auto g = pixels[i + 1]; + const auto b = pixels[i + 2]; + const auto lum = 0.299f * r + 0.587f * g + 0.114f * b; + histogram[static_cast<int>(lum * 255)]++; + } + + // 将直方图数据传入图像数据,写入 GLSL Image 中 + auto data = m_renderTarget->mapBuffer(); + for (int i = 0; i < histogram.size(); i++) { + const auto height = static_cast<int>(histogram[i] * 256); + for (int j = 0; j < height; j++) { + const auto pixel = reinterpret_cast<unsigned char*>(data) + j * m_renderTarget->width() * 4 + i * 4; + pixel[0] = 255; + pixel[1] = 255; + pixel[2] = 255; + pixel[3] = 255; + } + } + m_renderTarget->unmapBuffer(); + + 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_renderTarget->texture()); + + glColorMask(true, true, true, true); + + glDisableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + void CGEHistogramFilter::setFormPosition(float left, float top) + { + m_position = {left, top}; + } + + void CGEHistogramFilter::setFormSize(float width, float height) + { + m_size = {width, height}; + } +} diff --git a/library/src/main/jni/cge/filters/cgeHistogramFilter.h b/library/src/main/jni/cge/filters/cgeHistogramFilter.h new file mode 100644 index 00000000..3b032b2a --- /dev/null +++ b/library/src/main/jni/cge/filters/cgeHistogramFilter.h @@ -0,0 +1,29 @@ +#ifndef _HISTOGRAMFILTER_H_ +#define _HISTOGRAMFILTER_H_ + +#include "cgeImageFilter.h" +#include "cgeTextureUtils.h" +#include "cgeVec.h" + +namespace CGE{ +class CGEHistogramFilter : public CGEImageFilterInterface + { + public: + ~CGEHistogramFilter() override; + + void setFormPosition(float left, float top); + + void setFormSize(float width, float height); + + bool init() override; + + void render2Texture(CGEImageHandlerInterface* handler, GLuint srcTexture, GLuint vertexBufferID) override; + + protected: + std::unique_ptr<TextureDrawer> m_drawer; + std::unique_ptr<FrameBufferTexture> m_renderTarget; + Vec2f m_position; + Vec2f m_size; + }; +} +#endif