Support transparent background in 3D viewer PNG/clipboard export.

Only really works with realtime renderer currently.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/12060
This commit is contained in:
Alex Shvartzkop 2024-02-29 17:11:53 +03:00
parent 4602a27eb8
commit 80457d5871
16 changed files with 104 additions and 137 deletions

View File

@ -81,11 +81,11 @@ BEGIN_EVENT_TABLE( EDA_3D_CANVAS, HIDPI_GL_3D_CANVAS )
END_EVENT_TABLE()
EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList,
EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
S3D_CACHE* a3DCachePointer ) :
HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, wxID_ANY,
aAttribList, wxDefaultPosition,
HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
m_eventDispatcher( nullptr ),
m_parentStatusBar( nullptr ),

View File

@ -52,12 +52,12 @@ public:
* Create a new 3D Canvas with an attribute list.
*
* @param aParent the parent creator of this canvas.
* @param aAttribList a list of openGL options created by GetOpenGL_AttributesList.
* @param aGLAttribs openGL attributes created by #OGL_ATT_LIST::GetAttributesList.
* @param aBoard The board.
* @param aSettings the settings options to be used by this canvas.
*/
EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList,
BOARD_ADAPTER& aSettings, CAMERA& aCamera, S3D_CACHE* a3DCachePointer );
EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs, BOARD_ADAPTER& aSettings,
CAMERA& aCamera, S3D_CACHE* a3DCachePointer );
~EDA_3D_CANVAS();

View File

@ -85,9 +85,9 @@ END_EVENT_TABLE()
#define RANGE_SCALE_3D 8.0f
EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER( wxWindow* aParent, const int* aAttribList,
EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
S3D_CACHE* aCacheManager ) :
HIDPI_GL_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aParent, wxID_ANY, aAttribList,
HIDPI_GL_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aParent, aGLAttribs, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE ),
m_trackBallCamera( RANGE_SCALE_3D * 4.0f ),
@ -302,7 +302,7 @@ void EDA_3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
// clear color and depth buffers
glEnable( GL_DEPTH_TEST );
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClearDepth( 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

View File

@ -49,9 +49,9 @@ public:
* Create a new 3D Canvas with a attribute list.
*
* @param aParent the parent creator of this canvas.
* @param aAttribList a list of openGL options created by #OGL_ATT_LIST::GetAttributesList.
* @param aGLAttribs openGL attributes created by #OGL_ATT_LIST::GetAttributesList.
*/
EDA_3D_MODEL_VIEWER( wxWindow* aParent, const int* aAttribList = nullptr,
EDA_3D_MODEL_VIEWER( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
S3D_CACHE* aCacheManager = nullptr );
~EDA_3D_MODEL_VIEWER();

View File

@ -495,7 +495,7 @@ bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
glEnable( GL_MULTISAMPLE );
// clear color and depth buffers
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClearDepth( 1.0f );
glClearStencil( 0x00 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
@ -503,8 +503,7 @@ bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
OglResetTextureState();
// Draw the background ( rectangle with color gradient)
OglDrawBackground( SFVEC3F( m_boardAdapter.m_BgColorTop ),
SFVEC3F( m_boardAdapter.m_BgColorBot ) );
OglDrawBackground( m_boardAdapter.m_BgColorTop, m_boardAdapter.m_BgColorBot );
glEnable( GL_DEPTH_TEST );

View File

@ -191,7 +191,7 @@ bool RENDER_3D_RAYTRACE::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
// Clear buffers
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClearDepth( 1.0f );
glClearStencil( 0x00 );
glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
@ -220,8 +220,7 @@ bool RENDER_3D_RAYTRACE::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
if( m_cameraLight )
m_cameraLight->SetDirection( -m_camera.GetDir() );
OglDrawBackground( SFVEC3F( m_boardAdapter.m_BgColorTop),
SFVEC3F( m_boardAdapter.m_BgColorBot) );
OglDrawBackground( m_boardAdapter.m_BgColorTop, m_boardAdapter.m_BgColorBot );
// Bind PBO
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );

View File

@ -116,8 +116,8 @@ EDA_3D_VIEWER_FRAME::EDA_3D_VIEWER_FRAME( KIWAY* aKiway, PCB_BASE_FRAME* aParent
EDA_3D_VIEWER_SETTINGS* cfg = mgr.GetAppSettings<EDA_3D_VIEWER_SETTINGS>();
ANTIALIASING_MODE aaMode = static_cast<ANTIALIASING_MODE>( cfg->m_Render.opengl_AA_mode );
m_canvas = new EDA_3D_CANVAS( this, OGL_ATT_LIST::GetAttributesList( aaMode ), m_boardAdapter,
m_currentCamera,
m_canvas = new EDA_3D_CANVAS( this, OGL_ATT_LIST::GetAttributesList( aaMode, true ),
m_boardAdapter, m_currentCamera,
PROJECT_PCB::Get3DCacheManager( &Prj() ) );
m_appearancePanel = new APPEARANCE_CONTROLS_3D( this, GetCanvas() );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -33,94 +33,45 @@
#include <core/arraydim.h>
const int OGL_ATT_LIST::m_openGL_attributes_list[] = {
// Boolean attributes (using itself at padding):
// 0 1
WX_GL_RGBA, WX_GL_RGBA,
// 2 3
WX_GL_DOUBLEBUFFER, WX_GL_DOUBLEBUFFER,
// Normal attributes with values:
// 4 5
WX_GL_DEPTH_SIZE, 16,
// 6 7
WX_GL_STENCIL_SIZE, 8,
// This ones need to be the last in the list (as the tags will set to 0 if AA fails)
// 8 9
WX_GL_SAMPLES, 0, // Disable AA for the start.
//10 11
WX_GL_SAMPLE_BUFFERS, 1, // Enable multisampling support (antialiasing).
0, 0 // NULL termination
};
#define ATT_WX_GL_SAMPLES_OFFSET 8
#define ATT_WX_GL_SAMPLES_OFFSET_DATA 9
#define ATT_WX_GL_SAMPLE_BUFFERS_OFFSET 10
#define ATT_WX_GL_SAMPLE_BUFFERS_DATA 11
int OGL_ATT_LIST::m_openGL_attributes_list_to_use[
arrayDim( OGL_ATT_LIST::m_openGL_attributes_list ) ] = { 0 };
const int* OGL_ATT_LIST::GetAttributesList( ANTIALIASING_MODE aAntiAliasingMode )
const wxGLAttributes OGL_ATT_LIST::GetAttributesList( ANTIALIASING_MODE aAntiAliasingMode,
bool aAlpha )
{
wxASSERT( aAntiAliasingMode <= ANTIALIASING_MODE::AA_8X );
memcpy( m_openGL_attributes_list_to_use, m_openGL_attributes_list,
sizeof( m_openGL_attributes_list_to_use ) );
auto makeAttribs = [aAlpha]( int aSamplers )
{
wxGLAttributes dispAttrs;
dispAttrs.RGBA()
.DoubleBuffer()
.Depth( 16 )
.Stencil( 8 )
.Samplers( aSamplers )
.SampleBuffers( aSamplers >= 0 ? 1 : -1 )
.MinRGBA( 8, 8, 8, aAlpha ? 8 : -1 )
.EndList();
return dispAttrs;
};
int maxSamples = -1;
if( aAntiAliasingMode > ANTIALIASING_MODE::AA_NONE )
{
// There is a bug on wxGLCanvas that makes IsDisplaySupported fail
// while testing for antialiasing.
// http://trac.wxwidgets.org/ticket/16909
// this next code will only work after this bug is fixed
//
// On my experience (Mario) it was only working on Linux but failing on
// Windows, so there was no AA.
// Check if the canvas supports multisampling.
if( wxGLCanvas::IsDisplaySupported( m_openGL_attributes_list_to_use ) )
if( wxGLCanvas::IsDisplaySupported( makeAttribs( 0 ) ) )
{
static const int aaSamples[4] = {0, 2, 4, 8};
static const int aaSamples[4] = { 0, 2, 4, 8 };
// Check for possible sample sizes, start form the requested.
int maxSamples = aaSamples[static_cast<int>( aAntiAliasingMode )];
maxSamples = aaSamples[static_cast<int>( aAntiAliasingMode )];
m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET_DATA] = maxSamples;
for( ; (maxSamples > 0) &&
( !wxGLCanvas::IsDisplaySupported( m_openGL_attributes_list_to_use ) );
maxSamples = maxSamples >> 1 )
while( maxSamples > 0 && !wxGLCanvas::IsDisplaySupported( makeAttribs( maxSamples ) ) )
{
m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET_DATA] = maxSamples;
maxSamples = maxSamples >> 1;
}
}
else
{
aAntiAliasingMode = ANTIALIASING_MODE::AA_NONE;
}
}
// Disable antialiasing if it failed or was not requested
if( aAntiAliasingMode == ANTIALIASING_MODE::AA_NONE )
{
// Remove multisampling information
// (hoping that the GPU driver will decide what is best)
m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET] = 0;
m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLES_OFFSET_DATA] = 0;
m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLE_BUFFERS_OFFSET] = 0;
m_openGL_attributes_list_to_use[ATT_WX_GL_SAMPLE_BUFFERS_DATA] = 0;
}
return m_openGL_attributes_list_to_use;
return makeAttribs( maxSamples );
}

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -30,6 +30,8 @@
#ifndef _OGL_ATT_LIST_H
#define _OGL_ATT_LIST_H
#include <wx/glcanvas.h>
/// Anti-aliasing options
enum class ANTIALIASING_MODE
{
@ -51,29 +53,12 @@ public:
* Get a list of attributes to pass to wxGLCanvas.
*
* @param aAntiAliasingMode = 0 - disabled; try to initialize (if is supported) the
* list with anti aliasing capabilities
* @return a list of options to be passed in the creation of a EDA_3D_CANVAS class
* list with anti aliasing capabilities.
* @param aAlpha set to enable alpha channel.
* @return wxGLAttributes to be passed in the creation of a EDA_3D_CANVAS class
*/
static const int* GetAttributesList( ANTIALIASING_MODE aAntiAliasingMode );
private:
/**
* Attributes list to be passed to a wxGLCanvas creation.
*
* This array should be 2*n+1
* Sadly wxwidgets / glx < 13 allowed
* a thing named "boolean attributes" that don't take a value.
* (See src/unix/glx11.cpp -> wxGLCanvasX11::ConvertWXAttrsToGL() ).
* To avoid problems due to this, just specify those attributes twice.
* Only WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_STEREO are such boolean
* attributes.
*/
static const int m_openGL_attributes_list[];
/**
* Attributes list that was (eventually) changed and are passed to creation.
*/
static int m_openGL_attributes_list_to_use[];
static const wxGLAttributes GetAttributesList( ANTIALIASING_MODE aAntiAliasingMode,
bool aAlpha = false );
};
#endif // _OGL_ATT_LIST_H

View File

@ -46,7 +46,7 @@ void OglGetScreenshot( wxImage& aDstImage )
glGetIntegerv( GL_VIEWPORT, (GLint*) &viewport );
unsigned char* pixelbuffer = (unsigned char*) malloc( viewport.x * viewport.y * 3 );
unsigned char* pixelbuffer = (unsigned char*) malloc( viewport.x * viewport.y * 4 );
// Call glFinish before screenshot to ensure everything is fully drawn.
glFinish();
@ -54,15 +54,40 @@ void OglGetScreenshot( wxImage& aDstImage )
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
glReadBuffer( GL_BACK_LEFT );
glReadPixels( viewport.originX, viewport.originY, viewport.x, viewport.y, GL_RGB,
glReadPixels( viewport.originX, viewport.originY, viewport.x, viewport.y, GL_RGBA,
GL_UNSIGNED_BYTE, pixelbuffer );
unsigned char* rgbBuffer = (unsigned char*) malloc( viewport.x * viewport.y * 3 );
unsigned char* alphaBuffer = (unsigned char*) malloc( viewport.x * viewport.y );
unsigned char* rgbaPtr = pixelbuffer;
unsigned char* rgbPtr = rgbBuffer;
unsigned char* alphaPtr = alphaBuffer;
for( int y = 0; y < viewport.y; y++ )
{
for( int x = 0; x < viewport.x; x++ )
{
rgbPtr[0] = rgbaPtr[0];
rgbPtr[1] = rgbaPtr[1];
rgbPtr[2] = rgbaPtr[2];
alphaPtr[0] = rgbaPtr[3];
rgbaPtr += 4;
rgbPtr += 3;
alphaPtr += 1;
}
}
// "Sets the image data without performing checks.
// The data given must have the size (width*height*3)
// The data must have been allocated with malloc()
// If static_data is false, after this call the pointer to the data is owned
// by the wxImage object, that will be responsible for deleting it."
aDstImage.SetData( pixelbuffer, viewport.x, viewport.y, false );
aDstImage.SetData( rgbBuffer, viewport.x, viewport.y, false );
aDstImage.SetAlpha( alphaBuffer, false );
free( pixelbuffer );
aDstImage = aDstImage.Mirror( false );
}
@ -157,7 +182,7 @@ void OglSetDiffuseMaterial( const SFVEC3F &aMaterialDiffuse, float aOpacity,
}
void OglDrawBackground( const SFVEC3F& aTopColor, const SFVEC3F& aBotColor )
void OglDrawBackground( const SFVEC4F& aTopColor, const SFVEC4F& aBotColor )
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
@ -173,14 +198,14 @@ void OglDrawBackground( const SFVEC3F& aTopColor, const SFVEC3F& aBotColor )
glDisable( GL_ALPHA_TEST );
glBegin( GL_QUADS );
glColor4f( aTopColor.x, aTopColor.y, aTopColor.z, 1.0f );
glColor4f( aTopColor.r, aTopColor.g, aTopColor.b, aTopColor.a );
glVertex2f( -1.0, 1.0 ); // Top left corner
glColor4f( aBotColor.x, aBotColor.y, aBotColor.z, 1.0f );
glColor4f( aBotColor.r, aBotColor.g, aBotColor.b, aBotColor.a );
glVertex2f( -1.0,-1.0 ); // bottom left corner
glVertex2f( 1.0,-1.0 ); // bottom right corner
glColor4f( aTopColor.x, aTopColor.y, aTopColor.z, 1.0f );
glColor4f( aTopColor.r, aTopColor.g, aTopColor.b, aTopColor.a);
glVertex2f( 1.0, 1.0 ); // top right corner
glEnd();
}

View File

@ -71,7 +71,7 @@ GLuint OglLoadTexture( const IMAGE& aImage );
*/
void OglGetScreenshot( wxImage& aDstImage );
void OglDrawBackground( const SFVEC3F& aTopColor, const SFVEC3F& aBotColor );
void OglDrawBackground( const SFVEC4F& aTopColor, const SFVEC4F& aBotColor );
/**
* Reset to default state the texture settings.

View File

@ -28,11 +28,11 @@
const float HIDPI_GL_3D_CANVAS::m_delta_move_step_factor = 0.7f;
HIDPI_GL_3D_CANVAS::HIDPI_GL_3D_CANVAS( const KIGFX::VC_SETTINGS& aVcSettings, CAMERA& aCamera,
wxWindow* aParent, wxWindowID,
const int* aAttribList, const wxPoint& aPos,
wxWindow* aParent, const wxGLAttributes& aGLAttribs,
wxWindowID, const wxPoint& aPos,
const wxSize& aSize, long aStyle, const wxString& aName,
const wxPalette& aPalette ) :
HIDPI_GL_CANVAS( aVcSettings, aParent, wxID_ANY, aAttribList, aPos, aSize, aStyle, aName, aPalette ),
HIDPI_GL_CANVAS( aVcSettings, aParent, aGLAttribs, wxID_ANY, aPos, aSize, aStyle, aName, aPalette ),
m_mouse_is_moving( false ),
m_mouse_was_moved( false ),
m_camera_is_moving( false ),

View File

@ -29,11 +29,11 @@
#include <gal/dpi_scaling.h>
HIDPI_GL_CANVAS::HIDPI_GL_CANVAS( const KIGFX::VC_SETTINGS& aSettings, wxWindow* aParent, wxWindowID aId,
const int* aAttribList,
HIDPI_GL_CANVAS::HIDPI_GL_CANVAS( const KIGFX::VC_SETTINGS& aSettings, wxWindow* aParent,
const wxGLAttributes& aGLAttribs, wxWindowID aId,
const wxPoint& aPos, const wxSize& aSize, long aStyle,
const wxString& aName, const wxPalette& aPalette ) :
wxGLCanvas( aParent, aId, aAttribList, aPos, aSize, aStyle, aName, aPalette ),
wxGLCanvas( aParent, aGLAttribs, aId, aPos, aSize, aStyle, aName, aPalette ),
m_settings( aSettings ),
m_scale_factor( DPI_SCALING::GetDefaultScaleFactor() )
{

View File

@ -71,8 +71,15 @@ using namespace KIGFX;
#include <glsl_kicad_vert.h>
using namespace KIGFX::BUILTIN_FONT;
static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
static const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
static wxGLAttributes getGLAttribs()
{
wxGLAttributes attribs;
attribs.RGBA().DoubleBuffer().Depth( 8 ).EndList();
return attribs;
}
wxGLContext* OPENGL_GAL::m_glMainContext = nullptr;
int OPENGL_GAL::m_instanceCounter = 0;
@ -299,12 +306,13 @@ GLuint GL_BITMAP_CACHE::cacheBitmap( const BITMAP_BASE* aBitmap )
return textureID;
}
OPENGL_GAL::OPENGL_GAL( const KIGFX::VC_SETTINGS& aVcSettings, GAL_DISPLAY_OPTIONS& aDisplayOptions,
wxWindow* aParent,
wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
const wxString& aName ) :
GAL( aDisplayOptions ),
HIDPI_GL_CANVAS( aVcSettings, aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition,
HIDPI_GL_CANVAS( aVcSettings, aParent, getGLAttribs(), wxID_ANY, wxDefaultPosition,
wxDefaultSize,
wxEXPAND, aName ),
m_mouseListener( aMouseListener ),

View File

@ -42,8 +42,8 @@ public:
// wxGLCanvas constructor
HIDPI_GL_3D_CANVAS( const KIGFX::VC_SETTINGS& aVcSettings, CAMERA& aCamera, wxWindow* parent,
wxWindowID id = wxID_ANY,
const int* attribList = nullptr, const wxPoint& pos = wxDefaultPosition,
const wxGLAttributes& aGLAttribs, wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0,
const wxString& name = wxGLCanvasName,
const wxPalette& palette = wxNullPalette );

View File

@ -42,7 +42,7 @@ class GAL_API HIDPI_GL_CANVAS : public wxGLCanvas
public:
// wxGLCanvas constructor
HIDPI_GL_CANVAS( const KIGFX::VC_SETTINGS& aSettings, wxWindow* aParent,
wxWindowID aId = wxID_ANY, const int* aAttribList = nullptr,
const wxGLAttributes& aGLAttribs, wxWindowID aId = wxID_ANY,
const wxPoint& aPos = wxDefaultPosition, const wxSize& aSize = wxDefaultSize,
long aStyle = 0, const wxString& aName = wxGLCanvasName,
const wxPalette& aPalette = wxNullPalette );