KiCad PCB EDA Suite
Loading...
Searching...
No Matches
cairo_print.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2019 CERN
3 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
4 *
5 * Author: Maciej Suminski <[email protected]>
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
23
24#include <stdexcept>
25#include <wx/dcclient.h>
26#include <wx/dcgraph.h>
27#include <wx/dcmemory.h>
28#include <wx/dcprint.h>
29#include <wx/rawbmp.h>
30
31#ifdef NOMINMAX /* workaround for gdiplus.h */
32#include <algorithm>
33using std::max;
34using std::min;
35#endif
36
37#ifdef __WXMSW__
38#include <windows.h>
39#include <objidl.h>
40#include <gdiplus.h>
41#include <cairo-win32.h>
42#include <wx/msw/enhmeta.h>
43#endif /* __WXMSW__ */
44
45
46#ifdef __WXMAC__
47#include <ApplicationServices/ApplicationServices.h>
48#include <cairo-quartz.h>
49#endif /* __WXMAC__ */
50
51using namespace KIGFX;
52
53
54CAIRO_PRINT_CTX::CAIRO_PRINT_CTX( wxImage* aImage, double aDPI ) :
55 m_targetImage( aImage ),
56 m_dpi( aDPI )
57{
58 wxCHECK( aImage, /* void */ );
59
60 m_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, aImage->GetWidth(), aImage->GetHeight() );
61
62 if( !m_surface || cairo_surface_status( m_surface ) != CAIRO_STATUS_SUCCESS )
63 throw std::runtime_error( "Could not create Cairo surface" );
64
65 m_ctx = cairo_create( m_surface );
66
67 if( !m_ctx || cairo_status( m_ctx ) != CAIRO_STATUS_SUCCESS )
68 throw std::runtime_error( "Could not create Cairo context" );
69
70 cairo_set_antialias( m_ctx, CAIRO_ANTIALIAS_GOOD );
71}
72
73
75{
76 if( wxPrinterDC* printerDC = dynamic_cast<wxPrinterDC*>( aDC ) )
77 m_gcdc = new wxGCDC( *printerDC );
78 else if( wxMemoryDC* memoryDC = dynamic_cast<wxMemoryDC*>( aDC ) )
79 m_gcdc = new wxGCDC( *memoryDC );
80 else if( wxWindowDC* windowDC = dynamic_cast<wxWindowDC*>( aDC ) )
81 m_gcdc = new wxGCDC( *windowDC );
82#ifdef __WXMSW__
83 else if( wxEnhMetaFileDC* enhMFDC = dynamic_cast<wxEnhMetaFileDC*>( aDC ) )
84 m_gcdc = new wxGCDC( *enhMFDC );
85#endif /* __WXMSW__ */
86 else
87 throw std::runtime_error( "Unhandled wxDC type" );
88
89 wxGraphicsContext* gctx = m_gcdc->GetGraphicsContext();
90
91 if( !gctx )
92 throw std::runtime_error( "Could not get the Graphics Context" );
93
94#ifdef __WXGTK__
95 m_ctx = static_cast<cairo_t*>( gctx->GetNativeContext() );
96 m_surface = cairo_get_target( m_ctx );
97
98// On linux, cairo printers have 72 DPI by default.
99// This is an unusable resolution for us.
100// A better resolution could be 4800 DPI (at 600 DPI, we still have minor
101// but visible artifacts, for instance with arcs, but not at 4800 DPI)
102// so modify the default:
103#define DEFAULT_DPI 72.0
104#define KICAD_PRINTER_DPI 4800.0
105
106 // our device scale is DEFAULT_DPI / KICAD_PRINTER_DPI
107 cairo_surface_set_device_scale( m_surface, DEFAULT_DPI / KICAD_PRINTER_DPI,
108 DEFAULT_DPI / KICAD_PRINTER_DPI );
109 m_dpi = KICAD_PRINTER_DPI;
110
111 cairo_reference( m_ctx );
112 cairo_surface_reference( m_surface );
113#endif /* __WXGTK__ */
114
115#ifdef __WXMSW__
116 Gdiplus::Graphics* g = static_cast<Gdiplus::Graphics*>( gctx->GetNativeContext() );
117 m_hdc = g->GetHDC();
118 m_surface = cairo_win32_printing_surface_create( static_cast<HDC>( m_hdc ) );
119 m_ctx = cairo_create( m_surface );
120 wxASSERT( aDC->GetPPI().x == aDC->GetPPI().y );
121 m_dpi = aDC->GetPPI().x;
122#endif /* __WXMSW__ */
123
124#ifdef __WXMAC__
125 wxSize size = m_gcdc->GetSize();
126 CGContextRef cg = (CGContextRef) gctx->GetNativeContext();
127 m_surface = cairo_quartz_surface_create_for_cg_context( cg, size.x, size.y );
128 m_ctx = cairo_create( m_surface );
129 wxASSERT( aDC->GetPPI().x == aDC->GetPPI().y );
130 m_dpi = aDC->GetPPI().x;
131#endif /* __WXMAC__ */
132
133 if( !m_ctx || cairo_status( m_ctx ) != CAIRO_STATUS_SUCCESS )
134 throw std::runtime_error( "Could not create Cairo context" );
135
136 if( !m_surface || cairo_surface_status( m_surface ) != CAIRO_STATUS_SUCCESS )
137 throw std::runtime_error( "Could not create Cairo surface" );
138}
139
140
142{
143 if( m_surface )
144 {
145 cairo_surface_flush( m_surface );
146
147 if( m_targetImage )
148 {
149 // Convert data from cairo to wxImage
150 unsigned char* srcData = cairo_image_surface_get_data( m_surface );
151 int height = cairo_image_surface_get_height( m_surface );
152 int stride = cairo_image_surface_get_stride( m_surface );
153
154 unsigned char* dstRGB = m_targetImage->GetData();
155 unsigned char* dstAlpha = m_targetImage->GetAlpha();
156 unsigned char* srcRow = srcData;
157
158 for( int y = 0; y < height; y++ )
159 {
160 for( int x = 0; x < stride; x += 4 )
161 {
162 const uint32_t pix = *(uint32_t*) ( srcRow + x );
163
164 const uint8_t b = pix >> 0;
165 const uint8_t g = pix >> 8;
166 const uint8_t r = pix >> 16;
167 const uint8_t alpha = pix >> 24;
168
169 // Un-premultiply alpha
170 if( alpha == 0 )
171 {
172 dstRGB[0] = dstRGB[1] = dstRGB[2] = 0;
173 dstAlpha[0] = 0;
174 }
175 else
176 {
177 dstRGB[0] = ( (uint32_t) r * 255 ) / alpha;
178 dstRGB[1] = ( (uint32_t) g * 255 ) / alpha;
179 dstRGB[2] = ( (uint32_t) b * 255 ) / alpha;
180 dstAlpha[0] = alpha;
181 }
182
183 dstRGB += 3;
184 dstAlpha += 1;
185 }
186
187 srcRow += stride;
188 }
189 }
190
191 cairo_surface_destroy( m_surface );
192 }
193
194 if( m_ctx )
195 cairo_destroy( m_ctx );
196
197 delete m_gcdc;
198}
199
200
202 std::unique_ptr<CAIRO_PRINT_CTX> aContext ) :
203 CAIRO_GAL_BASE( aDisplayOptions )
204{
205 m_printCtx = std::move( aContext );
206 m_context = m_currentContext = m_printCtx->GetContext();
207 m_surface = m_printCtx->GetSurface();
208 cairo_reference( m_context );
209 cairo_surface_reference( m_surface );
210 m_clearColor = COLOR4D( 1.0, 1.0, 1.0, 1.0 );
212 resetContext();
213
214 SetScreenDPI( m_printCtx->GetNativeDPI() );
215}
216
217
219{
221 const VECTOR2D paperSizeIU = VECTOR2D( m_nativePaperSize.y, m_nativePaperSize.x ) /* inches */
222 / m_worldUnitLength; /* 1" in IU */
223 const VECTOR2D paperSizeIUTransposed( paperSizeIU.y, paperSizeIU.x );
224
225 MATRIX3x3D scale, translation, flip, rotate, lookat;
226
227 scale.SetIdentity();
228 translation.SetIdentity();
229 flip.SetIdentity();
230 rotate.SetIdentity();
231 lookat.SetIdentity();
232
234 {
235 translation.SetTranslation( 0.5 / m_zoomFactor * paperSizeIUTransposed );
236 }
237 else
238 {
239 if( isLandscape() )
240 {
241 translation.SetTranslation( 0.5 / m_zoomFactor * paperSizeIU );
242 rotate.SetRotation( 90.0 * M_PI / 180.0 );
243 }
244 else
245 {
246 translation.SetTranslation( 0.5 / m_zoomFactor * paperSizeIUTransposed );
247 }
248 }
249
250 scale.SetScale( VECTOR2D( m_worldScale, m_worldScale ) );
251 flip.SetScale( VECTOR2D( m_globalFlipX ? -1.0 : 1.0, m_globalFlipY ? -1.0 : 1.0 ) );
252 lookat.SetTranslation( -m_lookAtPoint );
253
254 m_worldScreenMatrix = scale * translation * flip * rotate * lookat;
256}
257
258
259void CAIRO_PRINT_GAL::SetNativePaperSize( const VECTOR2D& aSize, bool aHasNativeLandscapeRotation )
260{
261 m_nativePaperSize = aSize;
262 m_hasNativeLandscapeRotation = aHasNativeLandscapeRotation;
263}
264
265
267{
268 // Convert aSize (inches) to pixels
269 SetScreenSize( VECTOR2I( std::ceil( aSize.x * m_screenDPI ) * 2,
270 std::ceil( aSize.y * m_screenDPI ) * 2 ) );
271}
272
273
274std::unique_ptr<GAL_PRINT> GAL_PRINT::Create( GAL_DISPLAY_OPTIONS& aOptions, wxDC* aDC )
275{
276 auto printCtx = std::make_unique<CAIRO_PRINT_CTX>( aDC );
277 return std::make_unique<CAIRO_PRINT_GAL>( aOptions, std::move( printCtx ) );
278}
279
280
281std::unique_ptr<CAIRO_PRINT_GAL> CAIRO_PRINT_GAL::Create( GAL_DISPLAY_OPTIONS& aOptions, wxImage* aImage, double aDPI )
282{
283 auto printCtx = std::make_unique<CAIRO_PRINT_CTX>( aImage, aDPI );
284 return std::make_unique<CAIRO_PRINT_GAL>( aOptions, std::move( printCtx ) );
285}
#define DEFAULT_DPI
cairo_surface_t * m_surface
Cairo surface.
Definition cairo_gal.h:369
CAIRO_GAL_BASE(GAL_DISPLAY_OPTIONS &aDisplayOptions)
Definition cairo_gal.cpp:52
cairo_t * m_context
Cairo image.
Definition cairo_gal.h:368
cairo_t * m_currentContext
Currently used Cairo context for drawing.
Definition cairo_gal.h:367
CAIRO_PRINT_CTX(wxImage *aImage, double aDPI)
cairo_surface_t * m_surface
Definition cairo_print.h:74
void SetSheetSize(const VECTOR2D &aSize) override
bool isLandscape() const
< Returns true if page orientation is landscape
CAIRO_PRINT_GAL(GAL_DISPLAY_OPTIONS &aDisplayOptions, std::unique_ptr< CAIRO_PRINT_CTX > aContext)
void SetNativePaperSize(const VECTOR2D &aSize, bool aRotateIfLandscape) override
std::unique_ptr< CAIRO_PRINT_CTX > m_printCtx
static std::unique_ptr< CAIRO_PRINT_GAL > Create(GAL_DISPLAY_OPTIONS &aOptions, wxImage *aImage, double aDPI)
void ComputeWorldScreenMatrix() override
Compute the world <-> screen transformation matrix.
VECTOR2D m_nativePaperSize
Flag indicating whether the platform rotates page automatically or GAL needs to handle it in the tran...
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
static std::unique_ptr< GAL_PRINT > Create(GAL_DISPLAY_OPTIONS &aOptions, wxDC *aDC)
MATRIX3x3D m_worldScreenMatrix
World transformation.
MATRIX3x3D m_screenWorldMatrix
Screen transformation.
double m_zoomFactor
The zoom factor.
double m_worldUnitLength
The unit length of the world coordinates [inch].
double m_worldScale
The scale factor world->screen.
bool m_globalFlipY
Flag for Y axis flipping.
void SetScreenSize(const VECTOR2I &aSize)
double m_screenDPI
The dots per inch of the screen.
bool m_globalFlipX
Flag for X axis flipping.
VECTOR2D m_lookAtPoint
Point to be looked at in world space.
void SetScreenDPI(double aScreenDPI)
Set the dots per inch of the screen.
void SetIdentity()
Set the matrix to the identity matrix.
Definition matrix3x3.h:240
void SetRotation(T aAngle)
Set the rotation components of the matrix.
Definition matrix3x3.h:275
void SetScale(VECTOR2< T > aScale)
Set the scale components of the matrix.
Definition matrix3x3.h:287
void SetTranslation(VECTOR2< T > aTranslation)
Set the translation components of the matrix.
Definition matrix3x3.h:256
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:473
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:33
const int scale
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694