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
19 * along with this program. If not, see <https://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// A better resolution could be 4800 DPI (at 600 DPI, we still have minor
95// but visible artifacts, for instance with arcs, but not at 4800 DPI)
96#define KICAD_PRINTER_DPI 4800.0
97
98#ifdef __WXGTK__
99 m_ctx = static_cast<cairo_t*>( gctx->GetNativeContext() );
100 m_surface = cairo_get_target( m_ctx );
101
102// On linux, cairo printers have 72 DPI by default.
103// This is an unusable resolution for us, so modify the default:
104#define DEFAULT_DPI 72.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,
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
132 // Printer PPI is typically 300 and the GAL snaps geometry to whole device units
133 if( dynamic_cast<wxPrinterDC*>( aDC ) )
134 {
135 cairo_surface_set_device_scale( m_surface, m_dpi / KICAD_PRINTER_DPI, m_dpi / KICAD_PRINTER_DPI );
137 }
138#endif /* __WXMAC__ */
139
140 if( !m_ctx || cairo_status( m_ctx ) != CAIRO_STATUS_SUCCESS )
141 throw std::runtime_error( "Could not create Cairo context" );
142
143 if( !m_surface || cairo_surface_status( m_surface ) != CAIRO_STATUS_SUCCESS )
144 throw std::runtime_error( "Could not create Cairo surface" );
145}
146
147
149{
150 if( m_surface )
151 {
152 cairo_surface_flush( m_surface );
153
154 if( m_targetImage )
155 {
156 // Convert data from cairo to wxImage
157 unsigned char* srcData = cairo_image_surface_get_data( m_surface );
158 int height = cairo_image_surface_get_height( m_surface );
159 int stride = cairo_image_surface_get_stride( m_surface );
160
161 unsigned char* dstRGB = m_targetImage->GetData();
162 unsigned char* dstAlpha = m_targetImage->GetAlpha();
163 unsigned char* srcRow = srcData;
164
165 for( int y = 0; y < height; y++ )
166 {
167 for( int x = 0; x < stride; x += 4 )
168 {
169 const uint32_t pix = *(uint32_t*) ( srcRow + x );
170
171 const uint8_t b = pix >> 0;
172 const uint8_t g = pix >> 8;
173 const uint8_t r = pix >> 16;
174 const uint8_t alpha = pix >> 24;
175
176 // Un-premultiply alpha
177 if( alpha == 0 )
178 {
179 dstRGB[0] = dstRGB[1] = dstRGB[2] = 0;
180 dstAlpha[0] = 0;
181 }
182 else
183 {
184 dstRGB[0] = ( (uint32_t) r * 255 ) / alpha;
185 dstRGB[1] = ( (uint32_t) g * 255 ) / alpha;
186 dstRGB[2] = ( (uint32_t) b * 255 ) / alpha;
187 dstAlpha[0] = alpha;
188 }
189
190 dstRGB += 3;
191 dstAlpha += 1;
192 }
193
194 srcRow += stride;
195 }
196 }
197
198 cairo_surface_destroy( m_surface );
199 }
200
201 if( m_ctx )
202 cairo_destroy( m_ctx );
203
204 delete m_gcdc;
205}
206
207
209 std::unique_ptr<CAIRO_PRINT_CTX> aContext ) :
210 CAIRO_GAL_BASE( aDisplayOptions )
211{
212 m_printCtx = std::move( aContext );
213 m_context = m_currentContext = m_printCtx->GetContext();
214 m_surface = m_printCtx->GetSurface();
215 cairo_reference( m_context );
216 cairo_surface_reference( m_surface );
217 m_clearColor = COLOR4D( 1.0, 1.0, 1.0, 1.0 );
219 resetContext();
220
221 SetScreenDPI( m_printCtx->GetNativeDPI() );
222}
223
224
226{
228 const VECTOR2D paperSizeIU = VECTOR2D( m_nativePaperSize.y, m_nativePaperSize.x ) /* inches */
229 / m_worldUnitLength; /* 1" in IU */
230 const VECTOR2D paperSizeIUTransposed( paperSizeIU.y, paperSizeIU.x );
231
232 MATRIX3x3D scale, translation, flip, rotate, lookat;
233
234 scale.SetIdentity();
235 translation.SetIdentity();
236 flip.SetIdentity();
237 rotate.SetIdentity();
238 lookat.SetIdentity();
239
241 {
242 translation.SetTranslation( 0.5 / m_zoomFactor * paperSizeIUTransposed );
243 }
244 else
245 {
246 if( isLandscape() )
247 {
248 translation.SetTranslation( 0.5 / m_zoomFactor * paperSizeIU );
249 rotate.SetRotation( 90.0 * M_PI / 180.0 );
250 }
251 else
252 {
253 translation.SetTranslation( 0.5 / m_zoomFactor * paperSizeIUTransposed );
254 }
255 }
256
257 scale.SetScale( VECTOR2D( m_worldScale, m_worldScale ) );
258 flip.SetScale( VECTOR2D( m_globalFlipX ? -1.0 : 1.0, m_globalFlipY ? -1.0 : 1.0 ) );
259 lookat.SetTranslation( -m_lookAtPoint );
260
261 m_worldScreenMatrix = scale * translation * flip * rotate * lookat;
263}
264
265
266void CAIRO_PRINT_GAL::SetNativePaperSize( const VECTOR2D& aSize, bool aHasNativeLandscapeRotation )
267{
268 m_nativePaperSize = aSize;
269 m_hasNativeLandscapeRotation = aHasNativeLandscapeRotation;
270}
271
272
274{
275 // Convert aSize (inches) to pixels
276 SetScreenSize( VECTOR2I( std::ceil( aSize.x * m_screenDPI ) * 2,
277 std::ceil( aSize.y * m_screenDPI ) * 2 ) );
278}
279
280
281std::unique_ptr<GAL_PRINT> GAL_PRINT::Create( GAL_DISPLAY_OPTIONS& aOptions, wxDC* aDC )
282{
283 auto printCtx = std::make_unique<CAIRO_PRINT_CTX>( aDC );
284 return std::make_unique<CAIRO_PRINT_GAL>( aOptions, std::move( printCtx ) );
285}
286
287
288std::unique_ptr<CAIRO_PRINT_GAL> CAIRO_PRINT_GAL::Create( GAL_DISPLAY_OPTIONS& aOptions, wxImage* aImage, double aDPI )
289{
290 auto printCtx = std::make_unique<CAIRO_PRINT_CTX>( aImage, aDPI );
291 return std::make_unique<CAIRO_PRINT_GAL>( aOptions, std::move( printCtx ) );
292}
#define DEFAULT_DPI
#define KICAD_PRINTER_DPI
cairo_surface_t * m_surface
Cairo surface.
Definition cairo_gal.h:394
CAIRO_GAL_BASE(GAL_DISPLAY_OPTIONS &aDisplayOptions)
Definition cairo_gal.cpp:49
cairo_t * m_context
Cairo image.
Definition cairo_gal.h:393
cairo_t * m_currentContext
Currently used Cairo context for drawing.
Definition cairo_gal.h:392
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.
bool m_hasNativeLandscapeRotation
Flag indicating whether the platform rotates page automatically or GAL needs to handle it in the tran...
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
Printout size.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
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:236
void SetRotation(T aAngle)
Set the rotation components of the matrix.
Definition matrix3x3.h:271
void SetScale(VECTOR2< T > aScale)
Set the scale components of the matrix.
Definition matrix3x3.h:283
void SetTranslation(VECTOR2< T > aTranslation)
Set the translation components of the matrix.
Definition matrix3x3.h:252
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:469
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:30
const int scale
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707