pcbjam/tests/gal-regression/native/gal_test_accessor.cpp
Viktor Vaczi 344cf3c432 docs(gal-test): Add README and document DrawBitmap limitation
- Add comprehensive README documenting all 28 GAL test scenarios
- Document known limitation: DrawBitmap uses legacy OpenGL immediate
  mode (glBegin/glEnd) incompatible with shader-based test harness
- Add shader accessor for DrawBitmap workaround attempt
- Clean up debug code from bitmap scenario

The DrawBitmap limitation is acceptable because:
- It's primarily used for reference images in schematics
- WebGL port will need its own bitmap rendering implementation
- All other 69 GAL methods are fully tested

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 13:37:08 +01:00

134 lines
4.2 KiB
C++

/**
* GAL Test Accessor Implementation
*
* Uses a member pointer technique to access private members without
* the #define private public hack that breaks standard library headers.
*/
#include "gal_test_accessor.h"
#include "kicad_stubs.h"
#include <gal/opengl/opengl_gal.h>
#include <gal/opengl/opengl_compositor.h>
#include <gal/opengl/shader.h>
// Template-based private member accessor trick
// See: https://bloglitb.blogspot.com/2010/07/access-to-private-members-thats-easy.html
template<typename Tag>
struct result {
typedef typename Tag::type type;
static type ptr;
};
template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;
template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
struct filler {
filler() { result<Tag>::ptr = p; }
};
static filler filler_obj;
};
template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;
// Tags for the private members we need to access
struct OPENGL_GAL_compositor { typedef KIGFX::OPENGL_COMPOSITOR* KIGFX::OPENGL_GAL::*type; };
struct OPENGL_GAL_mainBuffer { typedef unsigned int KIGFX::OPENGL_GAL::*type; };
struct OPENGL_GAL_shader { typedef KIGFX::SHADER* KIGFX::OPENGL_GAL::*type; };
struct OPENGL_COMPOSITOR_mainFbo { typedef GLuint KIGFX::OPENGL_COMPOSITOR::*type; };
// Instantiate the accessors
template struct rob<OPENGL_GAL_compositor, &KIGFX::OPENGL_GAL::m_compositor>;
template struct rob<OPENGL_GAL_mainBuffer, &KIGFX::OPENGL_GAL::m_mainBuffer>;
template struct rob<OPENGL_GAL_shader, &KIGFX::OPENGL_GAL::m_shader>;
template struct rob<OPENGL_COMPOSITOR_mainFbo, &KIGFX::OPENGL_COMPOSITOR::m_mainFbo>;
GLuint GetCompositorMainBufferTexture(KIGFX::OPENGL_GAL* gal) {
// Get the compositor pointer using the member pointer accessor
KIGFX::OPENGL_COMPOSITOR* compositor = gal->*result<OPENGL_GAL_compositor>::ptr;
unsigned int mainBuffer = gal->*result<OPENGL_GAL_mainBuffer>::ptr;
if (compositor && mainBuffer > 0) {
return compositor->GetBufferTexture(mainBuffer);
}
return 0;
}
void GetCompositorBufferSize(KIGFX::OPENGL_GAL* gal, int* width, int* height) {
KIGFX::OPENGL_COMPOSITOR* compositor = gal->*result<OPENGL_GAL_compositor>::ptr;
if (compositor) {
VECTOR2I size = compositor->GetScreenSize();
*width = size.x;
*height = size.y;
} else {
*width = 0;
*height = 0;
}
}
GLuint GetCompositorMainFBO(KIGFX::OPENGL_GAL* gal) {
KIGFX::OPENGL_COMPOSITOR* compositor = gal->*result<OPENGL_GAL_compositor>::ptr;
if (compositor) {
return compositor->*result<OPENGL_COMPOSITOR_mainFbo>::ptr;
}
return 0;
}
unsigned int GetMainBufferHandle(KIGFX::OPENGL_GAL* gal) {
return gal->*result<OPENGL_GAL_mainBuffer>::ptr;
}
bool ReadCompositorFBOPixels(KIGFX::OPENGL_GAL* gal, std::vector<uint8_t>& pixels, int* width, int* height) {
KIGFX::OPENGL_COMPOSITOR* compositor = gal->*result<OPENGL_GAL_compositor>::ptr;
unsigned int mainBuffer = gal->*result<OPENGL_GAL_mainBuffer>::ptr;
if (!compositor || mainBuffer == 0) {
return false;
}
GLuint mainFbo = compositor->*result<OPENGL_COMPOSITOR_mainFbo>::ptr;
// Get size from compositor
VECTOR2I size = compositor->GetScreenSize();
*width = size.x;
*height = size.y;
// Compute attachment point: GL_COLOR_ATTACHMENT0 + (mainBuffer - 1)
GLenum attachmentPoint = GL_COLOR_ATTACHMENT0_EXT + (mainBuffer - 1);
// Bind the FBO for reading
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mainFbo);
glReadBuffer(attachmentPoint);
// Read pixels
pixels.resize((*width) * (*height) * 4);
glReadPixels(0, 0, *width, *height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
// Unbind FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return true;
}
KIGFX::SHADER* GetGALShader(KIGFX::OPENGL_GAL* gal) {
return gal->*result<OPENGL_GAL_shader>::ptr;
}
void DeactivateGALShader(KIGFX::OPENGL_GAL* gal) {
KIGFX::SHADER* shader = gal->*result<OPENGL_GAL_shader>::ptr;
if (shader) {
shader->Deactivate();
}
}
void ActivateGALShader(KIGFX::OPENGL_GAL* gal) {
KIGFX::SHADER* shader = gal->*result<OPENGL_GAL_shader>::ptr;
if (shader) {
shader->Use();
}
}