You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

507 lines
15 KiB

// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 jean-pierre.charras
  5. * Copyright (C) 2011-2024 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include "bitmap_base.h"
  25. #include <gr_basic.h>
  26. #include <math/util.h> // for KiROUND
  27. #include <memory> // for make_unique, unique_ptr
  28. #include <plotters/plotter.h>
  29. #include <richio.h>
  30. #include <wx/bitmap.h> // for wxBitmap
  31. #include <wx/mstream.h>
  32. #include <wx/stream.h> // for wxInputStream, wxOutputStream
  33. #include <wx/string.h> // for wxString
  34. #include <wx/wfstream.h> // for wxFileInputStream
  35. BITMAP_BASE::BITMAP_BASE( const VECTOR2I& pos )
  36. {
  37. m_scale = 1.0; // 1.0 = original bitmap size
  38. m_imageType = wxBITMAP_TYPE_INVALID;
  39. m_bitmap = nullptr;
  40. m_image = nullptr;
  41. m_originalImage = nullptr;
  42. m_ppi = 300; // the bitmap definition. the default is 300PPI
  43. m_pixelSizeIu = 254000.0 / m_ppi; // a pixel size value OK for bitmaps using 300 PPI
  44. // for Eeschema which uses currently 254000PPI
  45. m_isMirroredX = false;
  46. m_isMirroredY = false;
  47. m_rotation = ANGLE_0;
  48. }
  49. BITMAP_BASE::BITMAP_BASE( const BITMAP_BASE& aSchBitmap )
  50. {
  51. m_scale = aSchBitmap.m_scale;
  52. m_ppi = aSchBitmap.m_ppi;
  53. m_pixelSizeIu = aSchBitmap.m_pixelSizeIu;
  54. m_isMirroredX = aSchBitmap.m_isMirroredX;
  55. m_isMirroredY = aSchBitmap.m_isMirroredY;
  56. m_rotation = aSchBitmap.m_rotation;
  57. m_imageType = aSchBitmap.m_imageType;
  58. m_image = nullptr;
  59. m_bitmap = nullptr;
  60. m_originalImage = nullptr;
  61. if( aSchBitmap.m_image )
  62. {
  63. m_image = new wxImage( *aSchBitmap.m_image );
  64. m_bitmap = new wxBitmap( *m_image );
  65. m_originalImage = new wxImage( *aSchBitmap.m_originalImage );
  66. m_imageType = aSchBitmap.m_imageType;
  67. m_imageData = aSchBitmap.m_imageData;
  68. m_imageId = aSchBitmap.m_imageId;
  69. }
  70. }
  71. void BITMAP_BASE::rebuildBitmap( bool aResetID )
  72. {
  73. if( m_bitmap )
  74. delete m_bitmap;
  75. m_bitmap = new wxBitmap( *m_image );
  76. if( aResetID )
  77. m_imageId = KIID();
  78. }
  79. void BITMAP_BASE::updatePPI()
  80. {
  81. // Todo: eventually we need to support dpi / scaling in both dimensions
  82. int dpiX = m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
  83. if( dpiX > 1 )
  84. {
  85. if( m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
  86. m_ppi = KiROUND( dpiX * 2.54 );
  87. else
  88. m_ppi = dpiX;
  89. }
  90. }
  91. void BITMAP_BASE::ImportData( BITMAP_BASE& aItem )
  92. {
  93. *m_image = *aItem.m_image;
  94. *m_bitmap = *aItem.m_bitmap;
  95. *m_originalImage = *aItem.m_originalImage;
  96. m_imageId = aItem.m_imageId;
  97. m_scale = aItem.m_scale;
  98. m_ppi = aItem.m_ppi;
  99. m_pixelSizeIu = aItem.m_pixelSizeIu;
  100. m_isMirroredX = aItem.m_isMirroredX;
  101. m_isMirroredY = aItem.m_isMirroredY;
  102. m_rotation = aItem.m_rotation;
  103. m_imageType = aItem.m_imageType;
  104. m_imageData = aItem.m_imageData;
  105. }
  106. bool BITMAP_BASE::ReadImageFile( wxInputStream& aInStream )
  107. {
  108. // Store the original image data in m_imageData
  109. size_t dataSize = aInStream.GetLength();
  110. m_imageData.SetBufSize( dataSize );
  111. aInStream.Read( m_imageData.GetData(), dataSize );
  112. m_imageData.SetDataLen( dataSize );
  113. std::unique_ptr<wxImage> new_image = std::make_unique<wxImage>();
  114. // Load the image from the stream into new_image
  115. wxMemoryInputStream mem_stream( m_imageData.GetData(), dataSize );
  116. if( !new_image->LoadFile( mem_stream ) )
  117. return false;
  118. delete m_image;
  119. m_imageType = new_image->GetType();
  120. m_image = new_image.release();
  121. // Create a new wxImage object from m_image
  122. delete m_originalImage;
  123. m_originalImage = new wxImage( *m_image );
  124. rebuildBitmap();
  125. updatePPI();
  126. return true;
  127. }
  128. bool BITMAP_BASE::ReadImageFile( wxMemoryBuffer& aBuf )
  129. {
  130. // Store the original image data in m_imageData
  131. m_imageData = aBuf;
  132. std::unique_ptr<wxImage> new_image = std::make_unique<wxImage>();
  133. // Load the image from the buffer into new_image
  134. wxMemoryInputStream mem_stream( m_imageData.GetData(), m_imageData.GetBufSize() );
  135. if( !new_image->LoadFile( mem_stream ) )
  136. return false;
  137. delete m_image;
  138. m_imageType = new_image->GetType();
  139. m_image = new_image.release();
  140. // Create a new wxImage object from m_image
  141. delete m_originalImage;
  142. m_originalImage = new wxImage( *m_image );
  143. rebuildBitmap();
  144. updatePPI();
  145. return true;
  146. }
  147. bool BITMAP_BASE::ReadImageFile(const wxString& aFullFilename)
  148. {
  149. wxFileInputStream file_stream(aFullFilename);
  150. // Check if the file could be opened successfully
  151. if (!file_stream.IsOk())
  152. return false;
  153. return ReadImageFile(file_stream);
  154. }
  155. bool BITMAP_BASE::SaveImageData( wxOutputStream& aOutStream ) const
  156. {
  157. if( m_imageData.IsEmpty() )
  158. {
  159. // If m_imageData is empty, use wxImage::Save() method to write m_image contents to the stream.
  160. wxBitmapType type = m_imageType == wxBITMAP_TYPE_JPEG ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG;
  161. if( !m_image->SaveFile( aOutStream, type ) )
  162. {
  163. return false;
  164. }
  165. }
  166. else
  167. {
  168. // Write the contents of m_imageData to the stream.
  169. aOutStream.Write( m_imageData.GetData(), m_imageData.GetBufSize() );
  170. }
  171. return true;
  172. }
  173. bool BITMAP_BASE::LoadLegacyData( LINE_READER& aLine, wxString& aErrorMsg )
  174. {
  175. wxMemoryOutputStream stream;
  176. char* line;
  177. while( true )
  178. {
  179. if( !aLine.ReadLine() )
  180. {
  181. aErrorMsg = wxT("Unexpected end of data");
  182. return false;
  183. }
  184. line = aLine.Line();
  185. if( strncasecmp( line, "EndData", 4 ) == 0 )
  186. {
  187. // all the PNG date is read.
  188. // We expect here m_image and m_bitmap are void
  189. m_image = new wxImage();
  190. wxMemoryInputStream istream( stream );
  191. m_image->LoadFile( istream, wxBITMAP_TYPE_ANY );
  192. m_bitmap = new wxBitmap( *m_image );
  193. m_originalImage = new wxImage( *m_image );
  194. UpdateImageDataBuffer();
  195. break;
  196. }
  197. // Read PNG data, stored in hexadecimal,
  198. // each byte = 2 hexadecimal digits and a space between 2 bytes
  199. // and put it in memory stream buffer
  200. int len = strlen( line );
  201. for( ; len > 0; len -= 3, line += 3 )
  202. {
  203. int value = 0;
  204. if( sscanf( line, "%X", &value ) == 1 )
  205. stream.PutC( (char) value );
  206. else
  207. break;
  208. }
  209. }
  210. return true;
  211. }
  212. const BOX2I BITMAP_BASE::GetBoundingBox() const
  213. {
  214. BOX2I bbox;
  215. VECTOR2I size = GetSize();
  216. bbox.Inflate( size.x / 2, size.y / 2 );
  217. return bbox;
  218. }
  219. void BITMAP_BASE::DrawBitmap( wxDC* aDC, const VECTOR2I& aPos,
  220. const KIGFX::COLOR4D& aBackgroundColor ) const
  221. {
  222. if( m_bitmap == nullptr )
  223. return;
  224. VECTOR2I pos = aPos;
  225. VECTOR2I size = GetSize();
  226. // This fixes a bug in OSX that should be fixed in the 3.0.3 version or later.
  227. if( ( size.x == 0 ) || ( size.y == 0 ) )
  228. return;
  229. // To draw the bitmap, pos is the upper left corner position
  230. pos.x -= size.x / 2;
  231. pos.y -= size.y / 2;
  232. double scale;
  233. int logicalOriginX, logicalOriginY;
  234. aDC->GetUserScale( &scale, &scale );
  235. aDC->GetLogicalOrigin( &logicalOriginX, &logicalOriginY );
  236. // We already have issues to draw a bitmap on the wxDC, depending on wxWidgets version.
  237. // Now we have an issue on wxWidgets 3.1.6 to fix the clip area
  238. // and the bitmap position when using TransformMatrix
  239. // So for version == 3.1.6 do not use it
  240. // Be carefull before changing the code.
  241. bool useTransform = aDC->CanUseTransformMatrix();
  242. wxAffineMatrix2D init_matrix = aDC->GetTransformMatrix();
  243. // Note: clipping bitmap area was made to fix a minor issue in old versions of
  244. // Kicad/wxWidgets (5.1 / wx 3.0)
  245. // However SetClippingRegion creates a lot of issues (different ways to fix the
  246. // position and size of the area, depending on wxWidget version)because it changes with
  247. // each versions of wxWigets, so it is now disabled
  248. // However the code is still here, just in case
  249. // #define USE_CLIP_AREA
  250. wxPoint clipAreaPos;
  251. if( useTransform )
  252. {
  253. wxAffineMatrix2D matrix = aDC->GetTransformMatrix();
  254. matrix.Translate( pos.x, pos.y );
  255. matrix.Scale( GetScalingFactor(), GetScalingFactor() );
  256. aDC->SetTransformMatrix( matrix );
  257. // Needed on wx <= 3.1.5, and this is strange...
  258. // Nevertheless, this code has problem (the bitmap is not seen)
  259. // with wx version > 3.1.5
  260. clipAreaPos.x = pos.x;
  261. clipAreaPos.y = pos.y;
  262. pos.x = pos.y = 0;
  263. }
  264. else
  265. {
  266. aDC->SetUserScale( scale * GetScalingFactor(), scale * GetScalingFactor() );
  267. aDC->SetLogicalOrigin( logicalOriginX / GetScalingFactor(),
  268. logicalOriginY / GetScalingFactor() );
  269. pos.x = KiROUND( pos.x / GetScalingFactor() );
  270. pos.y = KiROUND( pos.y / GetScalingFactor() );
  271. size.x = KiROUND( size.x / GetScalingFactor() );
  272. size.y = KiROUND( size.y / GetScalingFactor() );
  273. clipAreaPos.x = pos.x;
  274. clipAreaPos.y = pos.y;
  275. }
  276. #ifdef USE_CLIP_AREA
  277. aDC->DestroyClippingRegion();
  278. aDC->SetClippingRegion( clipAreaPos, wxSize( size.x, size.y ) );
  279. #endif
  280. if( aBackgroundColor != COLOR4D::UNSPECIFIED && m_bitmap->HasAlpha() )
  281. {
  282. // Most printers don't support transparent images properly,
  283. // so blend the image with background color.
  284. int w = m_bitmap->GetWidth();
  285. int h = m_bitmap->GetHeight();
  286. wxImage image( w, h );
  287. wxColour bgColor = aBackgroundColor.ToColour();
  288. image.SetRGB( wxRect( 0, 0, w, h ), bgColor.Red(), bgColor.Green(), bgColor.Blue() );
  289. image.Paste( m_bitmap->ConvertToImage(), 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE );
  290. if( GetGRForceBlackPenState() )
  291. image = image.ConvertToGreyscale();
  292. aDC->DrawBitmap( wxBitmap( image ), pos.x, pos.y, true );
  293. }
  294. else if( GetGRForceBlackPenState() )
  295. {
  296. wxBitmap result( m_bitmap->ConvertToImage().ConvertToGreyscale() );
  297. aDC->DrawBitmap( result, pos.x, pos.y, true );
  298. }
  299. else
  300. {
  301. aDC->DrawBitmap( *m_bitmap, pos.x, pos.y, true );
  302. }
  303. if( useTransform )
  304. aDC->SetTransformMatrix( init_matrix );
  305. else
  306. {
  307. aDC->SetUserScale( scale, scale );
  308. aDC->SetLogicalOrigin( logicalOriginX, logicalOriginY );
  309. }
  310. #ifdef USE_CLIP_AREA
  311. aDC->DestroyClippingRegion();
  312. #endif
  313. }
  314. VECTOR2I BITMAP_BASE::GetSize() const
  315. {
  316. VECTOR2I size;
  317. if( m_bitmap )
  318. {
  319. size.x = m_bitmap->GetWidth();
  320. size.y = m_bitmap->GetHeight();
  321. size.x = KiROUND( size.x * GetScalingFactor() );
  322. size.y = KiROUND( size.y * GetScalingFactor() );
  323. }
  324. return size;
  325. }
  326. void BITMAP_BASE::Mirror( FLIP_DIRECTION aFlipDirection )
  327. {
  328. if( m_image )
  329. {
  330. // wxImage::Mirror() clear some parameters of the original image.
  331. // We need to restore them, especially resolution and unit, to be
  332. // sure image parameters saved in file are the right parameters, not
  333. // the defualt values
  334. int resX = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
  335. int resY = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
  336. int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
  337. *m_image = m_image->Mirror( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT );
  338. m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT , unit);
  339. m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX);
  340. m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY);
  341. if( aFlipDirection == FLIP_DIRECTION::TOP_BOTTOM )
  342. m_isMirroredY = !m_isMirroredY;
  343. else
  344. m_isMirroredX = !m_isMirroredX;
  345. rebuildBitmap( false );
  346. UpdateImageDataBuffer();
  347. }
  348. }
  349. void BITMAP_BASE::Rotate( bool aRotateCCW )
  350. {
  351. if( m_image )
  352. {
  353. // wxImage::Rotate90() clear some parameters of the original image.
  354. // We need to restore them, especially resolution and unit, to be
  355. // sure image parameters saved in file are the right parameters, not
  356. // the defualt values
  357. int resX = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
  358. int resY = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
  359. int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
  360. *m_image = m_image->Rotate90( aRotateCCW );
  361. m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT , unit);
  362. m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX);
  363. m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY);
  364. m_rotation += ( aRotateCCW ? ANGLE_90 : -ANGLE_90 );
  365. rebuildBitmap( false );
  366. UpdateImageDataBuffer();
  367. }
  368. }
  369. void BITMAP_BASE::ConvertToGreyscale()
  370. {
  371. if( m_image )
  372. {
  373. *m_image = m_image->ConvertToGreyscale();
  374. *m_originalImage = m_originalImage->ConvertToGreyscale();
  375. rebuildBitmap();
  376. UpdateImageDataBuffer();
  377. }
  378. }
  379. void BITMAP_BASE::PlotImage( PLOTTER* aPlotter, const VECTOR2I& aPos,
  380. const COLOR4D& aDefaultColor,
  381. int aDefaultPensize ) const
  382. {
  383. if( m_image == nullptr )
  384. return;
  385. // These 2 lines are useful only for plotters that cannot plot a bitmap
  386. // and plot a rectangle instead of.
  387. aPlotter->SetColor( aDefaultColor );
  388. aPlotter->SetCurrentLineWidth( aDefaultPensize );
  389. aPlotter->PlotImage( *m_image, aPos, GetScalingFactor() );
  390. }
  391. void BITMAP_BASE::UpdateImageDataBuffer()
  392. {
  393. if( m_image )
  394. {
  395. wxMemoryOutputStream stream;
  396. wxBitmapType type = m_imageType == wxBITMAP_TYPE_JPEG ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG;
  397. if( !m_image->SaveFile( stream, type ) )
  398. return;
  399. m_imageData.GetWriteBuf( stream.GetLength() );
  400. stream.CopyTo( m_imageData.GetData(), stream.GetLength() );
  401. m_imageData.SetDataLen( stream.GetLength() );
  402. }
  403. }