diff --git a/CMakeLists.txt b/CMakeLists.txt index fe70fcc87d..fd201e2c4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,8 @@ add_subdirectory(kicad) add_subdirectory(pcbnew) add_subdirectory(polygon) add_subdirectory(polygon/kbool/src) +add_subdirectory(potrace) +add_subdirectory(bitmap2component) ############# # Resources # diff --git a/bitmap2component/CMakeLists.txt b/bitmap2component/CMakeLists.txt new file mode 100644 index 0000000000..35c46d3569 --- /dev/null +++ b/bitmap2component/CMakeLists.txt @@ -0,0 +1,22 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ../potrace + ../polygon/kbool/include + ) + +set(BITMAP2COMPONENT_SRCS + bitmap2component.cpp + ) + +add_executable(bitmap2component WIN32 MACOSX_BUNDLE ${BITMAP2COMPONENT_SRCS} ${BITMAP2COMPONENT_RESOURCES}) + + +target_link_libraries( bitmap2component potrace kbool ) + +if(APPLE) + set_target_properties(bitmap2component PROPERTIES ) +endif(APPLE) + +install(TARGETS bitmap2component + DESTINATION ${KICAD_BIN} + COMPONENT binary) + diff --git a/bitmap2component/bitmap2component.cpp b/bitmap2component/bitmap2component.cpp new file mode 100644 index 0000000000..047169754a --- /dev/null +++ b/bitmap2component/bitmap2component.cpp @@ -0,0 +1,570 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 1992-2010 jean-pierre.charras + * Copyright (C) 1992-2010 Kicad Developers, see change_log.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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "kbool/booleng.h" + +#include +#include +#include +#include +#include +#include + +#include "potracelib.h" +#include "bitmap_io.h" +#include "auxiliary.h" + + +#ifndef max + #define max( a, b ) ( ( (a) > (b) ) ? (a) : (b) ) + #endif + #ifndef min + #define min( a, b ) ( ( (a) < (b) ) ? (a) : (b) ) + #endif + + +enum output_format { + POSTSCRIPT_FMT = 1, + PCBNEW_FMT, + EESCHEMA_FMT +}; + + +/* Helper class th handle useful info to convert a bitmpa to + * a polygonal object description + */ +class BITMAPCONV_INFO +{ +public: + enum output_format m_Format; + int m_PixmapWidth; + int m_PixmapHeight; // the bitmap size in pixels + double m_ScaleX; + double m_ScaleY; // the conversion scale + potrace_path_t* m_Paths; // the list of paths, from potrace (list of lines and bezier curves) + FILE* m_Outfile; +public: + BITMAPCONV_INFO(); +}; + +static void BezierToPolyline( std::vector & aCornersBuffer, + potrace_dpoint_t p1, + potrace_dpoint_t p2, + potrace_dpoint_t p3, + potrace_dpoint_t p4 ); + +static void CreateOutputFile( BITMAPCONV_INFO& aInfo ); + +static const char* CmpName = "LOGO"; + + +BITMAPCONV_INFO::BITMAPCONV_INFO() +{ + m_Format = POSTSCRIPT_FMT; + m_PixmapWidth = 0; + m_PixmapHeight = 0; + m_ScaleX = 1.0; + m_ScaleY = 1.0; + m_Paths = NULL; + m_Outfile = NULL; +} + + +/** Function ArmBoolEng + * Initialise parameters used in kbool + * @param aBooleng = pointer to the Bool_Engine to initialise + * @param aConvertHoles = mode for holes when a boolean operation is made + * true: in resulting polygon, holes are linked into outer contours by double overlapping segments + * false: in resulting polygons, holes are not linked: they are separate polygons + */ +void ArmBoolEng( Bool_Engine* aBooleng, bool aConvertHoles ) +{ + // set some global vals to arm the boolean engine + + // input points are scaled up with GetDGrid() * GetGrid() + + // DGRID is only meant to make fractional parts of input data which + + /* + * The input data scaled up with DGrid is related to the accuracy the user has in his input data. + * User data with a minimum accuracy of 0.001, means set the DGrid to 1000. + * The input data may contain data with a minimum accuracy much smaller, but by setting the DGrid + * everything smaller than 1/DGrid is rounded. + * + * DGRID is only meant to make fractional parts of input data which can be + * doubles, part of the integers used in vertexes within the boolean algorithm. + * And therefore DGRID bigger than 1 is not usefull, you would only loose accuracy. + * Within the algorithm all input data is multiplied with DGRID, and the result + * is rounded to an integer. + */ + double DGRID = 1000.0; // round coordinate X or Y value in calculations to this (initial value = 1000.0 in kbool example) + // kbool uses DGRID to convert float user units to integer + // kbool unit = (int)(user unit * DGRID) + // Note: in kicad, coordinates are already integer so DGRID could be set to 1 + // we can choose 1.0, + // but choose DGRID = 1000.0 solves some filling problems +// (perhaps because this allows a better precision in kbool internal calculations + + double MARGE = 1.0 / DGRID; // snap with in this range points to lines in the intersection routines + // should always be >= 1/DGRID a MARGE >= 10/DGRID is ok + // this is also used to remove small segments and to decide when + // two segments are in line. ( initial value = 0.001 ) + // For kicad we choose MARGE = 1/DGRID + + double CORRECTIONFACTOR = 0.0; // correct the polygons by this number: used in BOOL_CORRECTION operation + // this operation shrinks a polygon if CORRECTIONFACTOR < 0 + // or stretch it if CORRECTIONFACTOR > 0 + // the size change is CORRECTIONFACTOR (holes are correctly handled) + double CORRECTIONABER = 1.0; // the accuracy for the rounded shapes used in correction + double ROUNDFACTOR = 1.5; // when will we round the correction shape to a circle + double SMOOTHABER = 10.0; // accuracy when smoothing a polygon + double MAXLINEMERGE = 1000.0; // leave as is, segments of this length in smoothen + + + /* + * Grid makes sure that the integer data used within the algorithm has room for extra intersections + * smaller than the smallest number within the input data. + * The input data scaled up with DGrid is related to the accuracy the user has in his input data. + * Another scaling with Grid is applied on top of it to create space in the integer number for + * even smaller numbers. + */ + int GRID = (int) 10000 / DGRID; // initial value = 10000 in kbool example + + // But we use 10000/DGRID because the scalling is made + // by DGRID on integer pcbnew units and + // the global scalling ( GRID*DGRID) must be < 30000 to avoid + // overflow in calculations (made in long long in kbool) + if( GRID <= 1 ) // Cannot be null! + GRID = 1; + + aBooleng->SetMarge( MARGE ); + aBooleng->SetGrid( GRID ); + aBooleng->SetDGrid( DGRID ); + aBooleng->SetCorrectionFactor( CORRECTIONFACTOR ); + aBooleng->SetCorrectionAber( CORRECTIONABER ); + aBooleng->SetSmoothAber( SMOOTHABER ); + aBooleng->SetMaxlinemerge( MAXLINEMERGE ); + aBooleng->SetRoundfactor( ROUNDFACTOR ); + aBooleng->SetWindingRule( true ); // This is the default kbool value + + if( aConvertHoles ) + { +#if 1 // Can be set to 1 for kbool version >= 2.1, must be set to 0 for previous versions + // SetAllowNonTopHoleLinking() exists only in kbool >= 2.1 + aBooleng->SetAllowNonTopHoleLinking( false ); // Default = , but i have problems (filling errors) when true +#endif + aBooleng->SetLinkHoles( true ); // holes will be connected by double overlapping segments + aBooleng->SetOrientationEntryMode( false ); // all polygons are contours, not holes + } + else + { + aBooleng->SetLinkHoles( false ); // holes will not be connected by double overlapping segments + aBooleng->SetOrientationEntryMode( true ); // holes are entered counter clockwise + } +} + + +int main( int argc, char* argv[] ) +{ + potrace_bitmap_t* potrace_bitmap = NULL; + potrace_param_t* param; + potrace_state_t* st; + int error; + int fmt_option = '0'; + + + FILE* infile, * outfile = NULL; + + if( argc < 4 ) + { + printf( "Usage:\nbitmap2component <0,1,2>\n" ); + printf( " Allowed bitmap files formats are .bmp or .pgm\n" ); + printf( "output format:\n 0 = pcbnew.emp, 1 = eeschema.lib, 2 = ps\n" ); + return -1; + } + + infile = fopen( argv[1], "r" ); + if( infile == NULL ) + { + printf( "File %s could not be opened\n", argv[1] ); + return -2; + } + + outfile = fopen( argv[2], "w" ); + if( outfile == NULL ) + { + printf( "File %s could not be opened\n", argv[2] ); + return -2; + } + + double threshold = 0.5; // = 0 to 1.0 + + error = bm_read( infile, threshold, &potrace_bitmap ); + if( error != 0 ) + { + printf( "Bitmap %s could not be read\n", argv[1] ); + return -2; + } + + if( !potrace_bitmap ) + { + fprintf( stderr, "Error allocating bitmap: %s\n", strerror( errno ) ); + return 1; + } + + /* set tracing parameters, starting from defaults */ + param = potrace_param_default(); + if( !param ) + { + fprintf( stderr, "Error allocating parameters: %s\n", strerror( errno ) ); + return 1; + } + param->turdsize = 0; + + /* convert the bitmap to curves */ + st = potrace_trace( param, potrace_bitmap ); + if( !st || st->status != POTRACE_STATUS_OK ) + { + fprintf( stderr, "Error tracing bitmap: %s\n", strerror( errno ) ); + return 1; + } + + BITMAPCONV_INFO info; + info.m_PixmapWidth = potrace_bitmap->w; + info.m_PixmapHeight = potrace_bitmap->h; // the bitmap size in pixels + info.m_Paths = st->plist; + info.m_Outfile = outfile; + + if( argc >= 4 ) + fmt_option = argv[3][0]; + switch( fmt_option ) + { + case '2': + info.m_Format = POSTSCRIPT_FMT; + info.m_ScaleX = info.m_ScaleY = 1.0; // the conversion scale + /* output vector data, e.g. as a rudimentary EPS file */ + break; + + case '1': + info.m_Format = EESCHEMA_FMT; + info.m_ScaleX = info.m_ScaleY = 1000.0 / 300; // the conversion scale + break; + + case '0': + info.m_Format = PCBNEW_FMT; + info.m_ScaleX = 10000.0 / 300; // the conversion scale + info.m_ScaleY = -info.m_ScaleX; // Y axis is top to bottom in modedit + break; + + default: + printf( "Unknown output format\n" ); + break; + } + + CreateOutputFile( info ); + + bm_free( potrace_bitmap ); + + potrace_state_free( st ); + potrace_param_free( param ); + + return 0; +} + + +static void OuputHeader( BITMAPCONV_INFO& aInfo ) +{ + int Ypos = (int) ( aInfo.m_PixmapHeight / 2 * aInfo.m_ScaleY ); + int fieldSize; // fields text size = 60 mils + + switch( aInfo.m_Format ) + { + case POSTSCRIPT_FMT: + /* output vector data, e.g. as a rudimentary EPS file */ + fprintf( aInfo.m_Outfile, "%%!PS-Adobe-3.0 EPSF-3.0\n" ); + fprintf( aInfo.m_Outfile, "%%%%BoundingBox: 0 0 %d %d\n", + aInfo.m_PixmapWidth, aInfo.m_PixmapHeight ); + fprintf( aInfo.m_Outfile, "gsave\n" ); + break; + + case PCBNEW_FMT: + #define FIELD_LAYER 21 + fieldSize = 600; // fields text size = 60 mils + Ypos += fieldSize / 2; + fprintf( aInfo.m_Outfile, "PCBNEW-LibModule-V1\n" ); + fprintf( aInfo.m_Outfile, "$INDEX\n%s\n$EndINDEX\n", CmpName ); + + fprintf( aInfo.m_Outfile, "#\n# %s\n", CmpName ); + fprintf( aInfo.m_Outfile, "# pixmap w = %d, h = %d\n#\n", + aInfo.m_PixmapWidth, aInfo.m_PixmapHeight ); + fprintf( aInfo.m_Outfile, "$MODULE %s\n", CmpName ); + fprintf( aInfo.m_Outfile, "Po 0 0 0 15 00000000 00000000 ~~\n"); + fprintf( aInfo.m_Outfile, "T0 0 %d %d %d 0 %d N I %d \"G***\"\n", + Ypos, fieldSize, fieldSize, fieldSize/5, FIELD_LAYER ); + fprintf( aInfo.m_Outfile, "T1 0 %d %d %d 0 %d N I %d \"%s\"\n", + -Ypos, fieldSize, fieldSize, fieldSize/5, FIELD_LAYER, CmpName ); + break; + + case EESCHEMA_FMT: + fprintf( aInfo.m_Outfile, "EESchema-LIBRARY Version 2.3\n" ); + fprintf( aInfo.m_Outfile, "#\n# %s\n", CmpName ); + fprintf( aInfo.m_Outfile, "# pixmap size w = %d, h = %d\n#\n", + aInfo.m_PixmapWidth, aInfo.m_PixmapHeight ); + + // print reference and value + fieldSize = 60; // fields text size = 60 mils + Ypos += fieldSize / 2; + fprintf( aInfo.m_Outfile, "DEF %s G 0 40 Y Y 1 F N\n", CmpName ); + fprintf( aInfo.m_Outfile, "F0 \"#G\" 0 %d %d H I C CNN\n", Ypos, fieldSize ); + fprintf( aInfo.m_Outfile, "F1 \"%s\" 0 %d %d H I C CNN\n", CmpName, -Ypos, fieldSize ); + fprintf( aInfo.m_Outfile, "DRAW\n" ); + break; + } +} + + +static void OuputEnd( BITMAPCONV_INFO& aInfo ) +{ + switch( aInfo.m_Format ) + { + case POSTSCRIPT_FMT: + fprintf( aInfo.m_Outfile, "grestore\n" ); + fprintf( aInfo.m_Outfile, "%%EOF\n" ); + break; + + case PCBNEW_FMT: + fprintf( aInfo.m_Outfile, "$EndMODULE %s\n", CmpName ); + fprintf( aInfo.m_Outfile, "$EndLIBRARY\n" ); + break; + + case EESCHEMA_FMT: + fprintf( aInfo.m_Outfile, "ENDDRAW\n" ); + fprintf( aInfo.m_Outfile, "ENDDEF\n" ); + break; + } +} + + +static void OuputOnePolygon( BITMAPCONV_INFO& aInfo, + std::vector & aPolygonBuffer ) +{ + unsigned ii; + + double offsetX = aInfo.m_PixmapWidth / 2 * aInfo.m_ScaleX; + double offsetY = aInfo.m_PixmapHeight / 2 * aInfo.m_ScaleY; + + switch( aInfo.m_Format ) + { + case POSTSCRIPT_FMT: + fprintf( aInfo.m_Outfile, "%f %f moveto\n", + aPolygonBuffer[0].x * aInfo.m_ScaleX, + aPolygonBuffer[0].y * aInfo.m_ScaleY ); + for( ii = 1; ii < aPolygonBuffer.size(); ii++ ) + fprintf( aInfo.m_Outfile, "%f %f lineto\n", + aPolygonBuffer[ii].x * aInfo.m_ScaleX, + aPolygonBuffer[ii].y * aInfo.m_ScaleY ); + + fprintf( aInfo.m_Outfile, "0 setgray fill\n" ); + break; + + case PCBNEW_FMT: + { + #define SILKSCREEN_N_FRONT 21 + int layer = SILKSCREEN_N_FRONT; + int width = 1; + fprintf( aInfo.m_Outfile, "DP %d %d %d %d %d %d %d\n", + 0, 0, 0, 0, + aPolygonBuffer.size()+1, + width, layer ); + + for( ii = 0; ii < aPolygonBuffer.size(); ii++ ) + fprintf( aInfo.m_Outfile, "Dl %d %d\n", + (int) ( aPolygonBuffer[ii].x * aInfo.m_ScaleX - offsetX ), + (int) ( aPolygonBuffer[ii].y * aInfo.m_ScaleY - offsetY ) ); + // Close polygon + fprintf( aInfo.m_Outfile, "Dl %d %d\n", + (int) ( aPolygonBuffer[0].x * aInfo.m_ScaleX - offsetX ), + (int) ( aPolygonBuffer[0].y * aInfo.m_ScaleY - offsetY ) ); + } + break; + + case EESCHEMA_FMT: + fprintf( aInfo.m_Outfile, "P %d 0 0 1", aPolygonBuffer.size()+1 ); + for( ii = 0; ii < aPolygonBuffer.size(); ii++ ) + fprintf( aInfo.m_Outfile, " %d %d", + (int) ( aPolygonBuffer[ii].x * aInfo.m_ScaleX - offsetX ), + (int) ( aPolygonBuffer[ii].y * aInfo.m_ScaleY - offsetY ) ); + // Close polygon + fprintf( aInfo.m_Outfile, " %d %d", + (int) ( aPolygonBuffer[0].x * aInfo.m_ScaleX - offsetX ), + (int) ( aPolygonBuffer[0].y * aInfo.m_ScaleY - offsetY ) ); + + fprintf( aInfo.m_Outfile, " F\n" ); + break; + } +} + + +static void CreateOutputFile( BITMAPCONV_INFO& aInfo ) +{ + unsigned int i, n; + int* tag; + + std::vector cornersBuffer; + + potrace_dpoint_t( *c )[3]; + OuputHeader( aInfo ); + + bool main_outline = true; + Bool_Engine* booleng = NULL; + + /* draw each as a polygon with no hole. + * Bezier curves are approximated by a polyline + */ + potrace_path_t* paths = aInfo.m_Paths; // the list of paths + while( paths != NULL ) + { + n = paths->curve.n; + tag = paths->curve.tag; + c = paths->curve.c; + potrace_dpoint_t startpoint = c[n - 1][2]; + cornersBuffer.push_back( startpoint ); + if( booleng == NULL ) + { + booleng = new Bool_Engine(); + ArmBoolEng( booleng, true ); + } + for( i = 0; i < n; i++ ) + { + switch( tag[i] ) + { + case POTRACE_CORNER: + cornersBuffer.push_back( c[i][1] ); + cornersBuffer.push_back( c[i][2] ); + startpoint = c[i][2]; + break; + + case POTRACE_CURVETO: + BezierToPolyline( cornersBuffer, startpoint, c[i][0], c[i][1], c[i][2] ); + startpoint = c[i][2]; + break; + } + } + + // Store current path + if( main_outline ) + { + main_outline = false; + booleng->StartPolygonAdd( GROUP_A ); + for( i = 1; i < cornersBuffer.size(); i++ ) + booleng->AddPoint( cornersBuffer[i].x, cornersBuffer[i].y ); + + booleng->EndPolygonAdd(); + } + else + { + booleng->StartPolygonAdd( GROUP_B ); + for( i = 1; i < cornersBuffer.size(); i++ ) + booleng->AddPoint( cornersBuffer[i].x, cornersBuffer[i].y ); + + booleng->EndPolygonAdd(); + } + cornersBuffer.clear(); + + /* at the end of a group of a positive path and its negative + * children, fill. */ + if( paths->next == NULL || paths->next->sign == '+' ) + { + booleng->Do_Operation( BOOL_A_SUB_B ); + std::vector PolygonBuffer; + while( booleng->StartPolygonGet() ) + { + potrace_dpoint_t corner; + PolygonBuffer.clear(); + while( booleng->PolygonHasMorePoints() ) + { + corner.x = booleng->GetPolygonXPoint(); + corner.y = booleng->GetPolygonYPoint(); + PolygonBuffer.push_back( corner ); + } + + booleng->EndPolygonGet(); + OuputOnePolygon( aInfo, PolygonBuffer ); + PolygonBuffer.clear(); + } + + delete booleng; + booleng = NULL; + main_outline = true; + } + paths = paths->next; + } + + OuputEnd( aInfo ); +} + + +/* render a Bezier curve. */ +void BezierToPolyline( std::vector & aCornersBuffer, + potrace_dpoint_t p1, + potrace_dpoint_t p2, + potrace_dpoint_t p3, + potrace_dpoint_t p4 ) +{ + double dd0, dd1, dd, delta, e2, epsilon, t; + + // p1 = starting point + + /* we approximate the curve by small line segments. The interval + * size, epsilon, is determined on the fly so that the distance + * between the true curve and its approximation does not exceed the + * desired accuracy delta. */ + + delta = 0.5; /* desired accuracy, in pixels */ + + /* let dd = maximal value of 2nd derivative over curve - this must + * occur at an endpoint. */ + dd0 = sq( p1.x - 2 * p2.x + p3.x ) + sq( p1.y - 2 * p2.y + p3.y ); + dd1 = sq( p2.x - 2 * p3.x + p4.x ) + sq( p2.y - 2 * p3.y + p4.y ); + dd = 6 * sqrt( max( dd0, dd1 ) ); + e2 = 8 * delta <= dd ? 8 * delta / dd : 1; + epsilon = sqrt( e2 ); /* necessary interval size */ + + for( t = epsilon; t<1; t += epsilon ) + { + potrace_dpoint_t intermediate_point; + intermediate_point.x = p1.x * cu( 1 - t ) + + 3* p2.x* sq( 1 - t ) * t + + 3 * p3.x * (1 - t) * sq( t ) + + p4.x* cu( t ); + + intermediate_point.y = p1.y * cu( 1 - t ) + + 3* p2.y* sq( 1 - t ) * t + + 3 * p3.y * (1 - t) * sq( t ) + p4.y* cu( t ); + + aCornersBuffer.push_back( intermediate_point ); + } + + aCornersBuffer.push_back( p4 ); +} diff --git a/eeschema/class_libentry.cpp b/eeschema/class_libentry.cpp index 798250cd1d..072a9ed7fd 100644 --- a/eeschema/class_libentry.cpp +++ b/eeschema/class_libentry.cpp @@ -676,7 +676,7 @@ bool LIB_COMPONENT::Load( FILE* aFile, char* aLine, int* aLineNum, { aErrorMsg.Printf( wxT( "Wrong DEF format in line %d, skipped." ), *aLineNum ); - while( GetLine( aFile, aLine, aLineNum, 1024 ) ) + while( GetLine( aFile, aLine, aLineNum, LINE_BUFFER_LEN_LARGE ) ) { p = strtok( aLine, " \t\n" ); if( stricmp( p, "ENDDEF" ) == 0 ) @@ -726,7 +726,7 @@ bool LIB_COMPONENT::Load( FILE* aFile, char* aLine, int* aLineNum, m_options = ENTRY_POWER; /* Read next lines */ - while( GetLine( aFile, aLine, aLineNum, 1024 ) ) + while( GetLine( aFile, aLine, aLineNum, LINE_BUFFER_LEN_LARGE ) ) { p = strtok( aLine, " \t\n" ); @@ -775,7 +775,7 @@ bool LIB_COMPONENT::LoadDrawEntries( FILE* aFile, char* aLine, while( true ) { - if( GetLine( aFile, aLine, aLineNum, 1024 ) == NULL ) + if( GetLine( aFile, aLine, aLineNum, LINE_BUFFER_LEN_LARGE ) == NULL ) { aErrorMsg = wxT( "file ended prematurely loading component draw element" ); return false; @@ -830,7 +830,7 @@ bool LIB_COMPONENT::LoadDrawEntries( FILE* aFile, char* aLine, /* Flush till end of draw section */ do { - if( GetLine( aFile, aLine, aLineNum, 1024 ) == NULL ) + if( GetLine( aFile, aLine, aLineNum, LINE_BUFFER_LEN_LARGE ) == NULL ) { aErrorMsg = wxT( "file ended prematurely while attempting \ to flush to end of drawing section." ); @@ -899,7 +899,7 @@ bool LIB_COMPONENT::LoadFootprints( FILE* aFile, char* aLine, { while( true ) { - if( GetLine( aFile, aLine, aLineNum, 1024 ) == NULL ) + if( GetLine( aFile, aLine, aLineNum, LINE_BUFFER_LEN_LARGE ) == NULL ) { aErrorMsg = wxT( "file ended prematurely while loading footprints" ); return false; diff --git a/eeschema/class_library.cpp b/eeschema/class_library.cpp index a23ed94c7c..4943f253f9 100644 --- a/eeschema/class_library.cpp +++ b/eeschema/class_library.cpp @@ -551,7 +551,7 @@ bool CMP_LIBRARY::Load( wxString& aErrorMsg ) { FILE* file; int lineNumber = 0; - char line[1024]; + char line[LINE_BUFFER_LEN_LARGE]; // Use a very large buffer to load data LIB_COMPONENT* libEntry; wxString msg; @@ -716,7 +716,7 @@ void CMP_LIBRARY::LoadAliases( LIB_COMPONENT* component ) bool CMP_LIBRARY::LoadHeader( FILE* libfile, int* LineNum ) { - char Line[1024], * text, * data; + char Line[LINE_BUFFER_LEN], * text, * data; while( GetLine( libfile, Line, LineNum, sizeof(Line) ) ) { @@ -735,7 +735,7 @@ bool CMP_LIBRARY::LoadHeader( FILE* libfile, int* LineNum ) bool CMP_LIBRARY::LoadDocs( wxString& aErrorMsg ) { int lineNumber = 0; - char line[1024], * name, * text; + char line[LINE_BUFFER_LEN_LARGE], * name, * text; CMP_LIB_ENTRY* entry; FILE* file; wxString msg; diff --git a/eeschema/classes_body_items.cpp b/eeschema/classes_body_items.cpp index 5c97ecff6b..be42231978 100644 --- a/eeschema/classes_body_items.cpp +++ b/eeschema/classes_body_items.cpp @@ -1369,6 +1369,8 @@ bool LIB_POLYLINE::Load( char* aLine, wxString& aErrorMsg ) i = sscanf( &aLine[2], "%d %d %d %d", &ccount, &m_Unit, &m_Convert, &m_Width ); + m_Fill = NO_FILL; + if( i < 4 ) { aErrorMsg.Printf( _( "polyline only had %d parameters of the required 4" ), i ); @@ -1389,13 +1391,13 @@ bool LIB_POLYLINE::Load( char* aLine, wxString& aErrorMsg ) { wxPoint point; p = strtok( NULL, " \t\n" ); - if( sscanf( p, "%d", &pt.x ) != 1 ) + if( p == NULL || sscanf( p, "%d", &pt.x ) != 1 ) { aErrorMsg.Printf( _( "polyline point %d X position not defined" ), i ); return false; } p = strtok( NULL, " \t\n" ); - if( sscanf( p, "%d", &pt.y ) != 1 ) + if( p == NULL || sscanf( p, "%d", &pt.y ) != 1 ) { aErrorMsg.Printf( _( "polyline point %d Y position not defined" ), i ); return false; @@ -1403,8 +1405,6 @@ bool LIB_POLYLINE::Load( char* aLine, wxString& aErrorMsg ) AddPoint( pt ); } - m_Fill = NO_FILL; - if( ( p = strtok( NULL, " \t\n" ) ) != NULL ) { if( p[0] == 'F' ) diff --git a/eeschema/general.h b/eeschema/general.h index 58d445f6dd..dc3a77bca8 100644 --- a/eeschema/general.h +++ b/eeschema/general.h @@ -12,6 +12,10 @@ #define EESCHEMA_FILE_STAMP "EESchema" #define NULL_STRING "_NONAME_" +// Define the char buffer size used to read library files +#define LINE_BUFFER_LEN_LARGE 8000 +#define LINE_BUFFER_LEN 1024 + #define MAX_PIN_INFO 10 #define TXTMARGE 10 /* Offset in mils for placement of labels diff --git a/gerbview/gerber_test_files/octogonal.gbr b/gerbview/gerber_test_files/octogonal.gbr new file mode 100644 index 0000000000..909872d74b --- /dev/null +++ b/gerbview/gerber_test_files/octogonal.gbr @@ -0,0 +1,27 @@ +* +%FSLAX26Y26*% +%MOIN*% +G04 A4 - i274x.oc8.d36 * +%AMA4top* +4,1,8, +0.034500,0.014290, +0.034500,-0.014290, +0.014290,-0.034500, +-0.014290,-0.034500, +-0.034500,-0.014290, +-0.034500,0.014290, +-0.014290,0.034500, +0.014290,0.034500, +0.034500,0.014290, +0.0000* +% +%ADD40A4top*% +%IPPOS*% +%LNfp0149448top.gbx*% +%LPD*% +G75* +G54D40* +X04750000Y00344900D03* +X04856300Y00246400D03* +X04750000Y00148000D03* +M02* diff --git a/pcbnew/plot_rtn.cpp b/pcbnew/plot_rtn.cpp index 036dbc666c..befa22fc52 100644 --- a/pcbnew/plot_rtn.cpp +++ b/pcbnew/plot_rtn.cpp @@ -487,7 +487,7 @@ void Plot_1_EdgeModule( PLOTTER* plotter, EDGE_MODULE* PtEdge, *ptr++ = y; } - plotter->poly( PtEdge->m_PolyPoints.size(), ptr_base, NO_FILL, + plotter->poly( PtEdge->m_PolyPoints.size(), ptr_base, FILLED_SHAPE, thickness ); free( ptr_base ); } diff --git a/potrace/AUTHORS b/potrace/AUTHORS new file mode 100644 index 0000000000..0a8b8e0a0c --- /dev/null +++ b/potrace/AUTHORS @@ -0,0 +1,5 @@ +Known contributors are listed here, in alphabetical order by their +abbreviations (which are used in Changelog). + + PS1 Peter Selinger (author) + TA1 Tor Andersson diff --git a/potrace/CMakeLists.txt b/potrace/CMakeLists.txt new file mode 100644 index 0000000000..f54886384d --- /dev/null +++ b/potrace/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ) + +set(POTRACE_SRCS + bitmap_io.cpp + curve.cpp + decompose.cpp + greymap.cpp + potracelib.cpp + render.cpp + trace.cpp + ) + +add_library(potrace ${POTRACE_SRCS}) diff --git a/potrace/auxiliary.h b/potrace/auxiliary.h new file mode 100644 index 0000000000..041e2a30b7 --- /dev/null +++ b/potrace/auxiliary.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* This header file collects some general-purpose macros (and static + * inline functions) that are used in various places. */ + +#ifndef AUXILIARY_H +#define AUXILIARY_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* ---------------------------------------------------------------------- */ +/* point arithmetic */ + +#include "potracelib.h" + +struct point_s +{ + long x; + long y; +}; +typedef struct point_s point_t; + +typedef potrace_dpoint_t dpoint_t; + +/* convert point_t to dpoint_t */ +static inline dpoint_t dpoint( point_t p ) +{ + dpoint_t res; + + res.x = p.x; + res.y = p.y; + return res; +} + + +/* range over the straight line segment [a,b] when lambda ranges over [0,1] */ +static inline dpoint_t interval( double lambda, dpoint_t a, dpoint_t b ) +{ + dpoint_t res; + + res.x = a.x + lambda * (b.x - a.x); + res.y = a.y + lambda * (b.y - a.y); + return res; +} + + +/* ---------------------------------------------------------------------- */ + +/* some useful macros. Note: the "mod" macro works correctly for + * negative a. Also note that the test for a>=n, while redundant, + * speeds up the mod function by 70% in the average case (significant + * since the program spends about 16% of its time here - or 40% + * without the test). The "floordiv" macro returns the largest integer + * <= a/n, and again this works correctly for negative a, as long as + * a,n are integers and n>0. */ + +/* integer arithmetic */ + +static inline int mod( int a, int n ) +{ + return a>=n ? a % n : a>=0 ? a : n - 1 - (-1 - a) % n; +} + + +static inline int floordiv( int a, int n ) +{ + return a>=0 ? a / n : -1 - (-1 - a) / n; +} + + +/* Note: the following work for integers and other numeric types. */ +#undef sign +#undef abs +#undef min +#undef max +#undef sq +#undef cu +#define sign( x ) ( (x)>0 ? 1 : (x)<0 ? -1 : 0 ) +#define abs( a ) ( (a)>0 ? (a) : -(a) ) +#define min( a, b ) ( (a)<(b) ? (a) : (b) ) +#define max( a, b ) ( (a)>(b) ? (a) : (b) ) +#define sq( a ) ( (a) * (a) ) +#define cu( a ) ( (a) * (a) * (a) ) + +#endif /* AUXILIARY_H */ diff --git a/potrace/bitmap.h b/potrace/bitmap.h new file mode 100644 index 0000000000..b3c17ed7ec --- /dev/null +++ b/potrace/bitmap.h @@ -0,0 +1,118 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +#ifndef BITMAP_H +#define BITMAP_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +/* The bitmap type is defined in potracelib.h */ +#include "potracelib.h" + +/* The present file defines some convenient macros and static inline + * functions for accessing bitmaps. Since they only produce inline + * code, they can be conveniently shared by the library and frontends, + * if desired */ + +/* ---------------------------------------------------------------------- */ +/* some measurements */ + +#define BM_WORDSIZE ( (int) sizeof(potrace_word) ) +#define BM_WORDBITS (8 * BM_WORDSIZE) +#define BM_HIBIT ( ( (potrace_word) 1 ) << (BM_WORDBITS - 1) ) +#define BM_ALLBITS (~(potrace_word) 0) + +/* macros for accessing pixel at index (x,y). U* macros omit the + * bounds check. */ + +#define bm_scanline( bm, y ) ( (bm)->map + (y) * (bm)->dy ) +#define bm_index( bm, x, y ) (&bm_scanline( bm, y )[(x) / BM_WORDBITS]) +#define bm_mask( x ) ( BM_HIBIT >> ( (x) & (BM_WORDBITS - 1) ) ) +#define bm_range( x, a ) ( (int) (x) >= 0 && (int) (x) < (a) ) +#define bm_safe( bm, x, y ) ( bm_range( x, (bm)->w ) && bm_range( y, (bm)->h ) ) +#define BM_UGET( bm, x, y ) ( ( *bm_index( bm, x, y ) & bm_mask( x ) ) != 0 ) +#define BM_USET( bm, x, y ) ( *bm_index( bm, x, y ) |= bm_mask( x ) ) +#define BM_UCLR( bm, x, y ) ( *bm_index( bm, x, y ) &= ~bm_mask( x ) ) +#define BM_UINV( bm, x, y ) ( *bm_index( bm, x, y ) ^= bm_mask( x ) ) +#define BM_UPUT( bm, x, y, b ) ( (b) ? BM_USET( bm, x, y ) : BM_UCLR( bm, x, y ) ) +#define BM_GET( bm, x, y ) (bm_safe( bm, x, y ) ? BM_UGET( bm, x, y ) : 0) +#define BM_SET( bm, x, y ) (bm_safe( bm, x, y ) ? BM_USET( bm, x, y ) : 0) +#define BM_CLR( bm, x, y ) (bm_safe( bm, x, y ) ? BM_UCLR( bm, x, y ) : 0) +#define BM_INV( bm, x, y ) (bm_safe( bm, x, y ) ? BM_UINV( bm, x, y ) : 0) +#define BM_PUT( bm, x, y, b ) (bm_safe( bm, x, y ) ? BM_UPUT( bm, x, y, b ) : 0) + +/* free the given bitmap. Leaves errno untouched. */ +static inline void bm_free( potrace_bitmap_t* bm ) +{ + if( bm ) + { + free( bm->map ); + } + free( bm ); +} + + +/* return new un-initialized bitmap. NULL with errno on error */ +static inline potrace_bitmap_t* bm_new( int w, int h ) +{ + potrace_bitmap_t* bm; + int dy = (w + BM_WORDBITS - 1) / BM_WORDBITS; + + bm = (potrace_bitmap_t*) malloc( sizeof(potrace_bitmap_t) ); + if( !bm ) + { + return NULL; + } + bm->w = w; + bm->h = h; + bm->dy = dy; + bm->map = (potrace_word*) malloc( dy * h * BM_WORDSIZE ); + if( !bm->map ) + { + free( bm ); + return NULL; + } + return bm; +} + + +/* clear the given bitmap. Set all bits to c. */ +static inline void bm_clear( potrace_bitmap_t* bm, int c ) +{ + memset( bm->map, c ? -1 : 0, bm->dy * bm->h * BM_WORDSIZE ); +} + + +/* duplicate the given bitmap. Return NULL on error with errno set. */ +static inline potrace_bitmap_t* bm_dup( const potrace_bitmap_t* bm ) +{ + potrace_bitmap_t* bm1 = bm_new( bm->w, bm->h ); + + if( !bm1 ) + { + return NULL; + } + memcpy( bm1->map, bm->map, bm->dy * bm->h * BM_WORDSIZE ); + return bm1; +} + + +/* invert the given bitmap. */ +static inline void bm_invert( potrace_bitmap_t* bm ) +{ + int i; + + for( i = 0; i < bm->dy * bm->h; i++ ) + { + bm->map[i] ^= BM_ALLBITS; + } +} + + +#endif /* BITMAP_H */ diff --git a/potrace/bitmap_io.cpp b/potrace/bitmap_io.cpp new file mode 100644 index 0000000000..d628661f16 --- /dev/null +++ b/potrace/bitmap_io.cpp @@ -0,0 +1,969 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: bitmap_io.c 147 2007-04-09 00:44:09Z selinger $ */ + +/* Routines for manipulating bitmaps, including reading pbm files. */ + +#include + +#include "bitmap.h" + +#define INTBITS ( 8 * sizeof(int) ) + +static int bm_readbody_bmp( FILE* f, double threshold, potrace_bitmap_t** bmp ); +static int bm_readbody_pnm( FILE* f, double threshold, potrace_bitmap_t** bmp, int magic ); + +/* ---------------------------------------------------------------------- */ +/* routines for reading pnm streams */ + +/* read next character after whitespace and comments. Return EOF on + * end of file or error. */ +static int fgetc_ws( FILE* f ) +{ + int c; + + while( 1 ) + { + c = fgetc( f ); + if( c=='#' ) + { + while( 1 ) + { + c = fgetc( f ); + if( c=='\n' || c==EOF ) + { + break; + } + } + } + /* space, tab, line feed, carriage return, form-feed */ + if( c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=12 ) + { + return c; + } + } +} + + +/* skip whitespace and comments, then read a non-negative decimal + * number from a stream. Return -1 on EOF. Tolerate other errors (skip + * bad characters). Do not the read any characters following the + * number (put next character back into the stream) */ + +static int readnum( FILE* f ) +{ + int c; + int acc; + + /* skip whitespace and comments */ + while( 1 ) + { + c = fgetc_ws( f ); + if( c==EOF ) + { + return -1; + } + if( c>='0' && c<='9' ) + { + break; + } + } + + /* first digit is already in c */ + acc = c - '0'; + while( 1 ) + { + c = fgetc( f ); + if( c==EOF ) + { + break; + } + if( c<'0' || c>'9' ) + { + ungetc( c, f ); + break; + } + acc *= 10; + acc += c - '0'; + } + + return acc; +} + + +/* similar to readnum, but read only a single 0 or 1, and do not read + * any characters after it. */ + +static int readbit( FILE* f ) +{ + int c; + + /* skip whitespace and comments */ + while( 1 ) + { + c = fgetc_ws( f ); + if( c==EOF ) + { + return -1; + } + if( c>='0' && c<='1' ) + { + break; + } + } + + return c - '0'; +} + + +/* ---------------------------------------------------------------------- */ + +/* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and + * convert the output to a bitmap. Return bitmap in *bmp. Return 0 on + * success, -1 on error with errno set, -2 on bad file format (with + * error message in bm_read_error), and 1 on premature end of file, -3 + * on empty file (including files which contain only whitespace and + * comments), -4 if wrong magic number. If the return value is >=0, + *bmp is valid. */ + +const char* bm_read_error = NULL; + +int bm_read( FILE* f, double threshold, potrace_bitmap_t** bmp ) +{ + int magic[2]; + + /* read magic number. We ignore whitespace and comments before the + * magic, for the benefit of concatenated files in P1-P3 format. + * Multiple P1-P3 images in a single file are not formally allowed + * by the PNM standard, but there is no harm in being lenient. */ + + magic[0] = fgetc_ws( f ); + if( magic[0] == EOF ) + { + return -3; + } + magic[1] = fgetc( f ); + if( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' ) + { + return bm_readbody_pnm( f, threshold, bmp, magic[1] ); + } + if( magic[0] == 'B' && magic[1] == 'M' ) + { + return bm_readbody_bmp( f, threshold, bmp ); + } + return -4; +} + + +/* ---------------------------------------------------------------------- */ +/* read PNM format */ + +/* read PNM stream after magic number. Return values as for bm_read */ +static int bm_readbody_pnm( FILE* f, double threshold, potrace_bitmap_t** bmp, int magic ) +{ + potrace_bitmap_t* bm; + int x, y, i, b, b1, sum; + int bpr; /* bytes per row (as opposed to 4*bm->c) */ + int w, h, max; + + bm = NULL; + + w = readnum( f ); + if( w<0 ) + { + goto format_error; + } + + h = readnum( f ); + if( h<0 ) + { + goto format_error; + } + + /* allocate bitmap */ + bm = bm_new( w, h ); + if( !bm ) + { + return -1; + } + + /* zero it out */ + bm_clear( bm, 0 ); + + switch( magic ) + { + default: + /* not reached */ + goto format_error; + + case '1': + /* read P1 format: PBM ascii */ + + for( y = h - 1; y>=0; y-- ) + { + for( x = 0; x=0; y-- ) + { + for( x = 0; x threshold * max ? 0 : 1 ); + } + } + + break; + + case '3': + /* read P3 format: PPM ascii */ + + max = readnum( f ); + if( max<1 ) + { + goto format_error; + } + + for( y = h - 1; y>=0; y-- ) + { + for( x = 0; x 3 * threshold * max ? 0 : 1 ); + } + } + + break; + + case '4': + /* read P4 format: PBM raw */ + + b = fgetc( f ); /* read single white-space character after height */ + if( b==EOF ) + { + goto format_error; + } + + bpr = (w + 7) / 8; + + for( y = h - 1; y>=0; y-- ) + { + for( i = 0; i=0; y-- ) + { + for( x = 0; x=256 ) + { + b <<= 8; + b1 = fgetc( f ); + if( b1==EOF ) + goto eof; + b |= b1; + } + BM_UPUT( bm, x, y, b > threshold * max ? 0 : 1 ); + } + } + + break; + + case '6': + /* read P6 format: PPM raw */ + + max = readnum( f ); + if( max<1 ) + { + goto format_error; + } + + b = fgetc( f ); /* read single white-space character after max */ + if( b==EOF ) + { + goto format_error; + } + + for( y = h - 1; y>=0; y-- ) + { + for( x = 0; x=256 ) + { + b <<= 8; + b1 = fgetc( f ); + if( b1==EOF ) + goto eof; + b |= b1; + } + sum += b; + } + + BM_UPUT( bm, x, y, sum > 3 * threshold * max ? 0 : 1 ); + } + } + + break; + } + + *bmp = bm; + return 0; + +eof: + *bmp = bm; + return 1; + +format_error: + bm_free( bm ); + if( magic == '1' || magic == '4' ) + { + bm_read_error = "invalid pbm file"; + } + else if( magic == '2' || magic == '5' ) + { + bm_read_error = "invalid pgm file"; + } + else + { + bm_read_error = "invalid ppm file"; + } + return -2; +} + + +/* ---------------------------------------------------------------------- */ +/* read BMP format */ + +struct bmp_info_s +{ + unsigned int FileSize; + unsigned int reserved; + unsigned int DataOffset; + unsigned int InfoSize; + unsigned int w; /* width */ + unsigned int h; /* height */ + unsigned int Planes; + unsigned int bits; /* bits per sample */ + unsigned int comp; /* compression mode */ + unsigned int ImageSize; + unsigned int XpixelsPerM; + unsigned int YpixelsPerM; + unsigned int ncolors; /* number of colors in palette */ + unsigned int ColorsImportant; + unsigned int ctbits; /* sample size for color table */ +}; +typedef struct bmp_info_s bmp_info_t; + +/* auxiliary */ + +static int bmp_count = 0; /* counter for byte padding */ +static int bmp_pos = 0; /* counter from start of BMP data */ + +/* read n-byte little-endian integer. Return 1 on EOF or error, else + * 0. Assume n<=4. */ +static int bmp_readint( FILE* f, int n, unsigned int* p ) +{ + int i; + unsigned int sum = 0; + int b; + + for( i = 0; i> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff); + coltable[i] = (c > 3 * threshold * 255 ? 0 : 1); + if( i<2 ) + { + col1[i] = c; + } + } + } + + /* forward to data */ + if( bmpinfo.InfoSize != 12 ) /* not old OS/2 format */ + { + TRY( bmp_forward( f, bmpinfo.DataOffset ) ); + } + + /* allocate bitmap */ + bm = bm_new( bmpinfo.w, bmpinfo.h ); + if( !bm ) + { + goto std_error; + } + + /* zero it out */ + bm_clear( bm, 0 ); + + switch( bmpinfo.bits + 0x100 * bmpinfo.comp ) + { + default: + goto format_error; + break; + + case 0x001: /* monochrome palette */ + if( col1[0] < col1[1] ) /* make the darker color black */ + { + mask = 0xff; + } + else + { + mask = 0; + } + + /* raster data */ + for( y = 0; y> (INTBITS - bmpinfo.bits); + bitbuf <<= bmpinfo.bits; + n -= bmpinfo.bits; + BM_UPUT( bm, x, y, coltable[b] ); + } + + TRY( bmp_pad( f ) ); + } + + break; + + case 0x010: /* 16-bit encoding */ + + /* can't do this format because it is not well-documented and I + * don't have any samples */ + bm_read_error = "cannot handle bmp 16-bit coding"; + goto format_error; + break; + + case 0x018: /* 24-bit encoding */ + case 0x020: /* 32-bit encoding */ + for( y = 0; y> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff); + BM_UPUT( bm, x, y, c > 3 * threshold * 255 ? 0 : 1 ); + } + + TRY( bmp_pad( f ) ); + } + + break; + + case 0x204: /* 4-bit runlength compressed encoding (RLE4) */ + x = 0; + y = 0; + while( 1 ) + { + TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */ + if( b>0 ) + { + /* repeat count */ + col[0] = coltable[(c >> 4) & 0xf]; + col[1] = coltable[c & 0xf]; + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + BM_UPUT( bm, x, y, col[i & 1] ); + x++; + } + } + else if( c == 0 ) + { + /* end of line */ + y++; + x = 0; + } + else if( c == 1 ) + { + /* end of bitmap */ + break; + } + else if( c == 2 ) + { + /* "delta": skip pixels in x and y directions */ + TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */ + x += b; + y += c; + } + else + { + /* verbatim segment */ + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + BM_PUT( bm, x, y, coltable[( b >> ( 4 - 4 * (i & 1) ) ) & 0xf] ); + x++; + } + + if( (c + 1) & 2 ) + { + /* pad to 16-bit boundary */ + TRY_EOF( bmp_readint( f, 1, &b ) ); + } + } + } + + break; + + case 0x108: /* 8-bit runlength compressed encoding (RLE8) */ + x = 0; + y = 0; + while( 1 ) + { + TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */ + if( b>0 ) + { + /* repeat count */ + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + BM_UPUT( bm, x, y, coltable[c] ); + x++; + } + } + else if( c == 0 ) + { + /* end of line */ + y++; + x = 0; + } + else if( c == 1 ) + { + /* end of bitmap */ + break; + } + else if( c == 2 ) + { + /* "delta": skip pixels in x and y directions */ + TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */ + x += b; + y += c; + } + else + { + /* verbatim segment */ + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + BM_PUT( bm, x, y, coltable[b] ); + x++; + } + + if( c & 1 ) + { + /* pad input to 16-bit boundary */ + TRY_EOF( bmp_readint( f, 1, &b ) ); + } + } + } + + break; + } /* switch */ + + /* skip any potential junk after the data section, but don't + * complain in case EOF is encountered */ + bmp_forward( f, bmpinfo.FileSize ); + + free( coltable ); + *bmp = bm; + return 0; + +eof: + free( coltable ); + *bmp = bm; + return 1; + +format_error: +try_error: + free( coltable ); + free( bm ); + if( !bm_read_error ) + { + bm_read_error = "invalid bmp file"; + } + return -2; + +std_error: + free( coltable ); + free( bm ); + return -1; +} + + +/* ---------------------------------------------------------------------- */ +/* output pbm format */ + +void bm_writepbm( FILE* f, potrace_bitmap_t* bm ) +{ + int w, h, bpr, y, i, c; + + w = bm->w; + h = bm->h; + + bpr = (w + 7) / 8; + + fprintf( f, "P4\n%d %d\n", w, h ); + for( y = h - 1; y>=0; y-- ) + { + for( i = 0; i> ( 8 * ( BM_WORDSIZE - 1 - (i % BM_WORDSIZE) ) ) ) & 0xff; + fputc( c, f ); + } + } + + return; +} + + +/* ---------------------------------------------------------------------- */ +/* output - for primitive debugging purposes only! */ + +/* print bitmap to screen */ +int bm_print( FILE* f, potrace_bitmap_t* bm ) +{ + int x, y; + int xx, yy; + int d; + int sw, sh; + + sw = bm->w < 79 ? bm->w : 79; + sh = bm->w < 79 ? bm->h : bm->h * sw * 44 / (79 * bm->w); + + for( yy = sh - 1; yy>=0; yy-- ) + { + for( xx = 0; xxw / sw; x<(xx + 1) * bm->w / sw; x++ ) + { + for( y = yy * bm->h / sh; y<(yy + 1) * bm->h / sh; y++ ) + { + if( BM_GET( bm, x, y ) ) + { + d++; + } + } + } + + fputc( d ? '*' : ' ', f ); + } + + fputc( '\n', f ); + } + + return 0; +} diff --git a/potrace/bitmap_io.h b/potrace/bitmap_io.h new file mode 100644 index 0000000000..2f5689d893 --- /dev/null +++ b/potrace/bitmap_io.h @@ -0,0 +1,23 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: bitmap_io.h 147 2007-04-09 00:44:09Z selinger $ */ +/* bitmap input/output functions */ + +#ifndef BITMAP_IO_H +#define BITMAP_IO_H + +#include +#include "bitmap.h" + +/* Note that bitmaps are stored bottom to top, i.e., the first + * scanline is the bottom-most one */ + +extern char* bm_read_error; + +int bm_read( FILE* f, double blacklevel, potrace_bitmap_t** bmp ); +void bm_writepbm( FILE* f, potrace_bitmap_t* bm ); +int bm_print( FILE* f, potrace_bitmap_t* bm ); + +#endif /* BITMAP_IO_H */ diff --git a/potrace/bitops.h b/potrace/bitops.h new file mode 100644 index 0000000000..85b017bbe4 --- /dev/null +++ b/potrace/bitops.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: bitops.h 147 2007-04-09 00:44:09Z selinger $ */ + +/* bits.h: this file defines some macros for bit manipulations. We + provide a generic implementation */ + +/* lobit: return the position of the rightmost "1" bit of an int, or + 32 if none. hibit: return 1 + the position of the leftmost "1" bit + of an int, or 0 if none. Note: these functions work on 32-bit + integers. */ + +#ifndef BITOPS_H +#define BITOPS_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* generic macros */ + +static inline unsigned int lobit(unsigned int x) { + unsigned int res = 32; + while (x & 0xffffff) { + x <<= 8; + res -= 8; + } + while (x) { + x <<= 1; + res -= 1; + } + return res; +} + +static inline unsigned int hibit(unsigned int x) { + unsigned int res = 0; + while (x > 0xff) { + x >>= 8; + res += 8; + } + while (x) { + x >>= 1; + res += 1; + } + return res; +} + +#endif /* BITOPS_H */ diff --git a/potrace/curve.cpp b/potrace/curve.cpp new file mode 100644 index 0000000000..2984ecbde9 --- /dev/null +++ b/potrace/curve.cpp @@ -0,0 +1,122 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: curve.c 147 2007-04-09 00:44:09Z selinger $ */ +/* private part of the path and curve data structures */ + +#include +#include +#include + +#include "potracelib.h" +#include "lists.h" +#include "curve.h" + +#define SAFE_MALLOC( var, n, typ ) \ + if( ( var = (typ*) malloc( (n)* sizeof(typ) ) ) == NULL ) \ + goto malloc_error + +/* ---------------------------------------------------------------------- */ +/* allocate and free path objects */ + +path_t* path_new( void ) +{ + path_t* p = NULL; + privpath_t* priv = NULL; + + SAFE_MALLOC( p, 1, path_t ); + memset( p, 0, sizeof(path_t) ); + SAFE_MALLOC( priv, 1, privpath_t ); + memset( priv, 0, sizeof(privpath_t) ); + p->priv = priv; + return p; + +malloc_error: + free( p ); + free( priv ); + return NULL; +} + + +/* free the members of the given curve structure. Leave errno unchanged. */ +static void privcurve_free_members( privcurve_t* curve ) +{ + free( curve->tag ); + free( curve->c ); + free( curve->vertex ); + free( curve->alpha ); + free( curve->alpha0 ); + free( curve->beta ); +} + + +/* free a path. Leave errno untouched. */ +void path_free( path_t* p ) +{ + if( p ) + { + if( p->priv ) + { + free( p->priv->pt ); + free( p->priv->lon ); + free( p->priv->sums ); + free( p->priv->po ); + privcurve_free_members( &p->priv->curve ); + privcurve_free_members( &p->priv->ocurve ); + } + free( p->priv ); + /* do not free p->fcurve ! */ + } + free( p ); +} + + +/* free a pathlist, leaving errno untouched. */ +void pathlist_free( path_t* plist ) +{ + path_t* p; + + list_forall_unlink( p, plist ) { + path_free( p ); + } +} + + +/* ---------------------------------------------------------------------- */ +/* initialize and finalize curve structures */ + +typedef dpoint_t dpoint3_t[3]; + +/* initialize the members of the given curve structure to size m. + * Return 0 on success, 1 on error with errno set. */ +int privcurve_init( privcurve_t* curve, int n ) +{ + memset( curve, 0, sizeof(privcurve_t) ); + curve->n = n; + SAFE_MALLOC( curve->tag, n, int ); + SAFE_MALLOC( curve->c, n, dpoint3_t ); + SAFE_MALLOC( curve->vertex, n, dpoint_t ); + SAFE_MALLOC( curve->alpha, n, double ); + SAFE_MALLOC( curve->alpha0, n, double ); + SAFE_MALLOC( curve->beta, n, double ); + return 0; + +malloc_error: + free( curve->tag ); + free( curve->c ); + free( curve->vertex ); + free( curve->alpha ); + free( curve->alpha0 ); + free( curve->beta ); + return 1; +} + + +/* copy private to public curve structure */ +void privcurve_to_curve( privcurve_t* pc, potrace_curve_t* c ) +{ + c->n = pc->n; + c->tag = pc->tag; + c->c = pc->c; +} diff --git a/potrace/curve.h b/potrace/curve.h new file mode 100644 index 0000000000..a4712fea1f --- /dev/null +++ b/potrace/curve.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +#ifndef CURVE_H +#define CURVE_H + +#include "auxiliary.h" + +/* vertex is c[1] for tag=POTRACE_CORNER, and the intersection of + * .c[-1][2]..c[0] and c[1]..c[2] for tag=POTRACE_CURVETO. alpha is only + * defined for tag=POTRACE_CURVETO and is the alpha parameter of the curve: + * .c[-1][2]..c[0] = alpha*(.c[-1][2]..vertex), and + * c[2]..c[1] = alpha*(c[2]..vertex). + * Beta is so that (.beta[i])[.vertex[i],.vertex[i+1]] = .c[i][2]. + */ + +struct privcurve_s +{ + int n; /* number of segments */ + int* tag; /* tag[n]: POTRACE_CORNER or POTRACE_CURVETO */ + dpoint_t( * c )[3]; /* c[n][i]: control points. + * c[n][0] is unused for tag[n]=POTRACE_CORNER */ + + /* the remainder of this structure is special to privcurve, and is + * used in EPS debug output and special EPS "short coding". These + * fields are valid only if "alphacurve" is set. */ + int alphacurve; /* have the following fields been initialized? */ + dpoint_t* vertex; /* for POTRACE_CORNER, this equals c[1] */ + double* alpha; /* only for POTRACE_CURVETO */ + double* alpha0; /* "uncropped" alpha parameter - for debug output only */ + double* beta; +}; +typedef struct privcurve_s privcurve_t; + +struct sums_s +{ + double x; + double y; + double x2; + double xy; + double y2; +}; +typedef struct sums_s sums_t; + +/* the path structure is filled in with information about a given path + * as it is accumulated and passed through the different stages of the + * Potrace algorithm. Backends only need to read the fcurve and fm + * fields of this data structure, but debugging backends may read + * other fields. */ +struct potrace_privpath_s +{ + int len; + point_t* pt; /* pt[len]: path as extracted from bitmap */ + int* lon; /* lon[len]: (i,lon[i]) = longest straight line from i */ + + int x0, y0; /* origin for sums */ + sums_t* sums; /* sums[len+1]: cache for fast summing */ + + int m; /* length of optimal polygon */ + int* po; /* po[m]: optimal polygon */ + + privcurve_t curve; /* curve[m]: array of curve elements */ + privcurve_t ocurve; /* ocurve[om]: array of curve elements */ + privcurve_t* fcurve; /* final curve: this points to either curve or + * ocurve. Do not free this separately. */ +}; +typedef struct potrace_privpath_s potrace_privpath_t; + +/* shorter names */ +typedef potrace_privpath_t privpath_t; +typedef potrace_path_t path_t; + +path_t* path_new( void ); +void path_free( path_t* p ); +void pathlist_free( path_t* plist ); +int privcurve_init( privcurve_t* curve, int n ); +void privcurve_to_curve( privcurve_t* pc, potrace_curve_t* c ); + +#endif /* CURVE_H */ diff --git a/potrace/decompose.cpp b/potrace/decompose.cpp new file mode 100644 index 0000000000..c957d2a33d --- /dev/null +++ b/potrace/decompose.cpp @@ -0,0 +1,600 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: decompose.c 146 2007-04-09 00:43:46Z selinger $ */ + +#include +#include +#include +#include + +#include "potracelib.h" +#include "curve.h" +#include "lists.h" +#include "auxiliary.h" +#include "bitmap.h" +#include "decompose.h" +#include "progress.h" + +/* ---------------------------------------------------------------------- */ +/* auxiliary bitmap manipulations */ + +/* set the excess padding to 0 */ +static void bm_clearexcess( potrace_bitmap_t* bm ) +{ + potrace_word mask; + int y; + + if( bm->w % BM_WORDBITS != 0 ) + { + mask = BM_ALLBITS << ( BM_WORDBITS - (bm->w % BM_WORDBITS) ); + for( y = 0; yh; y++ ) + { + *bm_index( bm, bm->w, y ) &= mask; + } + } +} + + +struct bbox_s +{ + int x0, x1, y0, y1; /* bounding box */ +}; +typedef struct bbox_s bbox_t; + +/* clear the bm, assuming the bounding box is set correctly (faster + * than clearing the whole bitmap) */ +static void clear_bm_with_bbox( potrace_bitmap_t* bm, bbox_t* bbox ) +{ + int imin = (bbox->x0 / BM_WORDBITS); + int imax = ( (bbox->x1 + BM_WORDBITS - 1) / BM_WORDBITS ); + int i, y; + + for( y = bbox->y0; yy1; y++ ) + { + for( i = imin; i> 8) & 0xff] ^ t[(z >> 16) & 0xff] ^ t[(z >> 24) & 0xff]; + return z & 1; +} + + +/* return the "majority" value of bitmap bm at intersection (x,y). We + * assume that the bitmap is balanced at "radius" 1. */ +static int majority( potrace_bitmap_t* bm, int x, int y ) +{ + int i, a, ct; + + for( i = 2; i<5; i++ ) /* check at "radius" i */ + { + ct = 0; + for( a = -i + 1; a<=i - 1; a++ ) + { + ct += BM_GET( bm, x + a, y + i - 1 ) ? 1 : -1; + ct += BM_GET( bm, x + i - 1, y + a - 1 ) ? 1 : -1; + ct += BM_GET( bm, x + a - 1, y - i ) ? 1 : -1; + ct += BM_GET( bm, x - i, y + a ) ? 1 : -1; + } + + if( ct>0 ) + { + return 1; + } + else if( ct<0 ) + { + return 0; + } + } + + return 0; +} + + +/* ---------------------------------------------------------------------- */ +/* decompose image into paths */ + +/* efficiently invert bits [x,infty) and [xa,infty) in line y. Here xa + * must be a multiple of BM_WORDBITS. */ +static void xor_to_ref( potrace_bitmap_t* bm, int x, int y, int xa ) +{ + int xhi = x & - BM_WORDBITS; + int xlo = x & (BM_WORDBITS - 1); /* = x % BM_WORDBITS */ + int i; + + if( xhipriv->len <= 0 ) /* a path of length 0 is silly, but legal */ + { + return; + } + + y1 = p->priv->pt[p->priv->len - 1].y; + + xa = p->priv->pt[0].x & - BM_WORDBITS; + for( k = 0; kpriv->len; k++ ) + { + x = p->priv->pt[k].x; + y = p->priv->pt[k].y; + + if( y != y1 ) + { + /* efficiently invert the rectangle [x,xa] x [y,y1] */ + xor_to_ref( bm, x, min( y, y1 ), xa ); + y1 = y; + } + } +} + + +/* Find the bounding box of a given path. Path is assumed to be of + * non-zero length. */ +static void setbbox_path( bbox_t* bbox, path_t* p ) +{ + int x, y; + int k; + + bbox->y0 = INT_MAX; + bbox->y1 = 0; + bbox->x0 = INT_MAX; + bbox->x1 = 0; + + for( k = 0; kpriv->len; k++ ) + { + x = p->priv->pt[k].x; + y = p->priv->pt[k].y; + + if( x < bbox->x0 ) + { + bbox->x0 = x; + } + if( x > bbox->x1 ) + { + bbox->x1 = x; + } + if( y < bbox->y0 ) + { + bbox->y0 = y; + } + if( y > bbox->y1 ) + { + bbox->y1 = y; + } + } +} + + +/* compute a path in the given pixmap, separating black from white. + * Start path at the point (x0,x1), which must be an upper left corner + * of the path. Also compute the area enclosed by the path. Return a + * new path_t object, or NULL on error (note that a legitimate path + * cannot have length 0). Sign is required for correct interpretation + * of turnpolicies. */ +static path_t* findpath( potrace_bitmap_t* bm, int x0, int y0, int sign, int turnpolicy ) +{ + int x, y, dirx, diry, len, size, area; + int c, d, tmp; + point_t* pt, * pt1; + path_t* p = NULL; + + x = x0; + y = y0; + dirx = 0; + diry = -1; + + len = size = 0; + pt = NULL; + area = 0; + + while( 1 ) + { + /* add point to path */ + if( len>=size ) + { + size += 100; + size = (int) ( 1.3 * size ); + pt1 = (point_t*) realloc( pt, size * sizeof(point_t) ); + if( !pt1 ) + { + goto error; + } + pt = pt1; + } + pt[len].x = x; + pt[len].y = y; + len++; + + /* move to next point */ + x += dirx; + y += diry; + area += x * diry; + + /* path complete? */ + if( x==x0 && y==y0 ) + { + break; + } + + /* determine next direction */ + c = BM_GET( bm, x + (dirx + diry - 1) / 2, y + (diry - dirx - 1) / 2 ); + d = BM_GET( bm, x + (dirx - diry - 1) / 2, y + (diry + dirx - 1) / 2 ); + + if( c && !d ) /* ambiguous turn */ + { + if( turnpolicy == POTRACE_TURNPOLICY_RIGHT + || (turnpolicy == POTRACE_TURNPOLICY_BLACK && sign == '+') + || (turnpolicy == POTRACE_TURNPOLICY_WHITE && sign == '-') + || ( turnpolicy == POTRACE_TURNPOLICY_RANDOM && detrand( x, y ) ) + || ( turnpolicy == POTRACE_TURNPOLICY_MAJORITY && majority( bm, x, y ) ) + || ( turnpolicy == POTRACE_TURNPOLICY_MINORITY && !majority( bm, x, y ) ) ) + { + tmp = dirx; /* right turn */ + dirx = diry; + diry = -tmp; + } + else + { + tmp = dirx; /* left turn */ + dirx = -diry; + diry = tmp; + } + } + else if( c ) /* right turn */ + { + tmp = dirx; + dirx = diry; + diry = -tmp; + } + else if( !d ) /* left turn */ + { + tmp = dirx; + dirx = -diry; + diry = tmp; + } + } /* while this path */ + + /* allocate new path object */ + p = path_new(); + if( !p ) + { + goto error; + } + + p->priv->pt = pt; + p->priv->len = len; + p->area = area; + p->sign = sign; + + return p; + +error: + free( pt ); + return NULL; +} + + +/* Give a tree structure to the given path list, based on "insideness" + * testing. I.e., path A is considered "below" path B if it is inside + * path B. The input pathlist is assumed to be ordered so that "outer" + * paths occur before "inner" paths. The tree structure is stored in + * the "childlist" and "sibling" components of the path_t + * structure. The linked list structure is also changed so that + * negative path components are listed immediately after their + * positive parent. Note: some backends may ignore the tree + * structure, others may use it e.g. to group path components. We + * assume that in the input, point 0 of each path is an "upper left" + * corner of the path, as returned by bm_to_pathlist. This makes it + * easy to find an "interior" point. The bm argument should be a + * bitmap of the correct size (large enough to hold all the paths), + * and will be used as scratch space. Return 0 on success or -1 on + * error with errno set. */ + +static void pathlist_to_tree( path_t* plist, potrace_bitmap_t* bm ) +{ + path_t* p, * p1; + path_t* heap, * heap1; + path_t* cur; + path_t* head; + path_t** hook, ** hook_in, ** hook_out; /* for fast appending to linked list */ + bbox_t bbox; + + bm_clear( bm, 0 ); + + /* save original "next" pointers */ + list_forall( p, plist ) { + p->sibling = p->next; + p->childlist = NULL; + } + + heap = plist; + + /* the heap holds a list of lists of paths. Use "childlist" field + * for outer list, "next" field for inner list. Each of the sublists + * is to be turned into a tree. This code is messy, but it is + * actually fast. Each path is rendered exactly once. We use the + * heap to get a tail recursive algorithm: the heap holds a list of + * pathlists which still need to be transformed. */ + + while( heap ) + { + /* unlink first sublist */ + cur = heap; + heap = heap->childlist; + cur->childlist = NULL; + + /* unlink first path */ + head = cur; + cur = cur->next; + head->next = NULL; + + /* render path */ + xor_path( bm, head ); + setbbox_path( &bbox, head ); + + /* now do insideness test for each element of cur; append it to + * head->childlist if it's inside head, else append it to + * head->next. */ + hook_in = &head->childlist; + hook_out = &head->next; + list_forall_unlink( p, cur ) { + if( p->priv->pt[0].y <= bbox.y0 ) + { + list_insert_beforehook( p, hook_out ); + /* append the remainder of the list to hook_out */ + *hook_out = cur; + break; + } + if( BM_GET( bm, p->priv->pt[0].x, p->priv->pt[0].y - 1 ) ) + { + list_insert_beforehook( p, hook_in ); + } + else + { + list_insert_beforehook( p, hook_out ); + } + } + + /* clear bm */ + clear_bm_with_bbox( bm, &bbox ); + + /* now schedule head->childlist and head->next for further + * processing */ + if( head->next ) + { + head->next->childlist = heap; + heap = head->next; + } + if( head->childlist ) + { + head->childlist->childlist = heap; + heap = head->childlist; + } + } + + /* copy sibling structure from "next" to "sibling" component */ + p = plist; + while( p ) + { + p1 = p->sibling; + p->sibling = p->next; + p = p1; + } + + /* reconstruct a new linked list ("next") structure from tree + * ("childlist", "sibling") structure. This code is slightly messy, + * because we use a heap to make it tail recursive: the heap + * contains a list of childlists which still need to be + * processed. */ + heap = plist; + if( heap ) + { + heap->next = NULL; /* heap is a linked list of childlists */ + } + plist = NULL; + hook = &plist; + while( heap ) + { + heap1 = heap->next; + for( p = heap; p; p = p->sibling ) + { + /* p is a positive path */ + /* append to linked list */ + list_insert_beforehook( p, hook ); + + /* go through its children */ + for( p1 = p->childlist; p1; p1 = p1->sibling ) + { + /* append to linked list */ + list_insert_beforehook( p1, hook ); + /* append its childlist to heap, if non-empty */ + if( p1->childlist ) + { + list_append( path_t, heap1, p1->childlist ); + } + } + } + + heap = heap1; + } + + return; +} + + +/* find the next set pixel in a row <= y. Pixels are searched first + * left-to-right, then top-down. In other words, (x,y)<(x',y') if y>y' + * or y=y' and x=0; y-- ) + { + for( x = 0; xw; x += BM_WORDBITS ) + { + if( *bm_index( bm, x, y ) ) + { + while( !BM_GET( bm, x, y ) ) + { + x++; + } + + /* found */ + *xp = x; + *yp = y; + return 0; + } + } + } + + /* not found */ + return 1; +} + + +/* Decompose the given bitmap into paths. Returns a linked list of + * path_t objects with the fields len, pt, area, sign filled + * in. Returns 0 on success with plistp set, or -1 on error with errno + * set. */ + +int bm_to_pathlist( const potrace_bitmap_t* bm, + path_t** plistp, + const potrace_param_t* param, + progress_t* progress ) +{ + int x; + int y; + path_t* p; + path_t* plist = NULL; /* linked list of path objects */ + path_t** hook = &plist; /* used to speed up appending to linked list */ + potrace_bitmap_t* bm1 = NULL; + int sign; + + bm1 = bm_dup( bm ); + if( !bm1 ) + { + goto error; + } + + /* be sure the byte padding on the right is set to 0, as the fast + * pixel search below relies on it */ + bm_clearexcess( bm1 ); + + /* iterate through components */ + y = bm1->h - 1; + while( findnext( bm1, &x, &y ) == 0 ) + { + /* calculate the sign by looking at the original */ + sign = BM_GET( bm, x, y ) ? '+' : '-'; + + /* calculate the path */ + p = findpath( bm1, x, y + 1, sign, param->turnpolicy ); + if( p==NULL ) + { + goto error; + } + + /* update buffered image */ + xor_path( bm1, p ); + + /* if it's a turd, eliminate it, else append it to the list */ + if( p->area <= param->turdsize ) + { + path_free( p ); + } + else + { + list_insert_beforehook( p, hook ); + } + + if( bm1->h > 0 ) /* to be sure */ + { + progress_update( 1 - y / (double) bm1->h, progress ); + } + } + + pathlist_to_tree( plist, bm1 ); + bm_free( bm1 ); + *plistp = plist; + + progress_update( 1.0, progress ); + + return 0; + +error: + bm_free( bm1 ); + list_forall_unlink( p, plist ) { + path_free( p ); + } + return -1; +} diff --git a/potrace/decompose.h b/potrace/decompose.h new file mode 100644 index 0000000000..1da8847c34 --- /dev/null +++ b/potrace/decompose.h @@ -0,0 +1,18 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: decompose.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef DECOMPOSE_H +#define DECOMPOSE_H + +#include "potracelib.h" +#include "progress.h" + +int bm_to_pathlist( const potrace_bitmap_t* bm, + path_t** plistp, + const potrace_param_t* param, + progress_t* progress ); + +#endif /* DECOMPOSE_H */ diff --git a/potrace/greymap.cpp b/potrace/greymap.cpp new file mode 100644 index 0000000000..4a45e7b01c --- /dev/null +++ b/potrace/greymap.cpp @@ -0,0 +1,1107 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: greymap.c 147 2007-04-09 00:44:09Z selinger $ */ + +/* Routines for manipulating greymaps, including reading pgm files. We + * only deal with greymaps of depth 8 bits. */ + +#include +#include +#include +#include + +#include "greymap.h" + +#define INTBITS ( 8 * sizeof(int) ) + +#define mod( a, n ) ( (a)>=(n) ? (a) % (n) : (a)>=0 ? (a) : (n) - 1 - ( -1 - (a) ) % (n) ) + +static int gm_readbody_pnm( FILE* f, greymap_t** gmp, int magic ); +static int gm_readbody_bmp( FILE* f, greymap_t** gmp ); + +/* ---------------------------------------------------------------------- */ +/* basic greymap routines */ + +/* return new un-initialized greymap. NULL with errno on error */ + +greymap_t* gm_new( int w, int h ) +{ + greymap_t* gm; + int errno_save; + + gm = (greymap_t*) malloc( sizeof(greymap_t) ); + if( !gm ) + { + return NULL; + } + gm->w = w; + gm->h = h; + gm->map = (signed short int*) malloc( w * h * sizeof(signed short int) ); + if( !gm->map ) + { + errno_save = errno; + free( gm ); + errno = errno_save; + return NULL; + } + return gm; +} + + +/* free the given greymap */ +void gm_free( greymap_t* gm ) +{ + if( gm ) + { + free( gm->map ); + } + free( gm ); +} + + +/* duplicate the given greymap. Return NULL on error with errno set. */ +greymap_t* gm_dup( greymap_t* gm ) +{ + greymap_t* gm1 = gm_new( gm->w, gm->h ); + + if( !gm1 ) + { + return NULL; + } + memcpy( gm1->map, gm->map, gm->w * gm->h * 2 ); + return gm1; +} + + +/* clear the given greymap to color b. */ +void gm_clear( greymap_t* gm, int b ) +{ + int i; + + if( b==0 ) + { + memset( gm->map, 0, gm->w * gm->h * 2 ); + } + else + { + for( i = 0; iw * gm->h; i++ ) + { + gm->map[i] = b; + } + } +} + + +/* ---------------------------------------------------------------------- */ +/* routines for reading pnm streams */ + +/* read next character after whitespace and comments. Return EOF on + * end of file or error. */ +static int fgetc_ws( FILE* f ) +{ + int c; + + while( 1 ) + { + c = fgetc( f ); + if( c=='#' ) + { + while( 1 ) + { + c = fgetc( f ); + if( c=='\n' || c==EOF ) + { + break; + } + } + } + /* space, tab, line feed, carriage return, form-feed */ + if( c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=12 ) + { + return c; + } + } +} + + +/* skip whitespace and comments, then read a non-negative decimal + * number from a stream. Return -1 on EOF. Tolerate other errors (skip + * bad characters). Do not the read any characters following the + * number (put next character back into the stream) */ + +static int readnum( FILE* f ) +{ + int c; + int acc; + + /* skip whitespace and comments */ + while( 1 ) + { + c = fgetc_ws( f ); + if( c==EOF ) + { + return -1; + } + if( c>='0' && c<='9' ) + { + break; + } + } + + /* first digit is already in c */ + acc = c - '0'; + while( 1 ) + { + c = fgetc( f ); + if( c==EOF ) + { + break; + } + if( c<'0' || c>'9' ) + { + ungetc( c, f ); + break; + } + acc *= 10; + acc += c - '0'; + } + + return acc; +} + + +/* similar to readnum, but read only a single 0 or 1, and do not read + * any characters after it. */ + +static int readbit( FILE* f ) +{ + int c; + + /* skip whitespace and comments */ + while( 1 ) + { + c = fgetc_ws( f ); + if( c==EOF ) + { + return -1; + } + if( c>='0' && c<='1' ) + { + break; + } + } + + return c - '0'; +} + + +/* ---------------------------------------------------------------------- */ + +/* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and + * convert the output to a greymap. Return greymap in *gmp. Return 0 + * on success, -1 on error with errno set, -2 on bad file format (with + * error message in gm_read_error), and 1 on premature end of file, -3 + * on empty file (including files with only whitespace and comments), + * -4 if wrong magic number. If the return value is >=0, *gmp is + * valid. */ + +const char* gm_read_error = NULL; + +int gm_read( FILE* f, greymap_t** gmp ) +{ + int magic[2]; + + /* read magic number. We ignore whitespace and comments before the + * magic, for the benefit of concatenated files in P1-P3 format. + * Multiple P1-P3 images in a single file are not formally allowed + * by the PNM standard, but there is no harm in being lenient. */ + + magic[0] = fgetc_ws( f ); + if( magic[0] == EOF ) + { + /* files which contain only comments and whitespace count as "empty" */ + return -3; + } + magic[1] = fgetc( f ); + if( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' ) + { + return gm_readbody_pnm( f, gmp, magic[1] ); + } + if( magic[0] == 'B' && magic[1] == 'M' ) + { + return gm_readbody_bmp( f, gmp ); + } + return -4; +} + + +/* ---------------------------------------------------------------------- */ +/* read PNM format */ + +/* read PNM stream after magic number. Return values as for gm_read */ +static int gm_readbody_pnm( FILE* f, greymap_t** gmp, int magic ) +{ + greymap_t* gm; + int x, y, i, j, b, b1, sum; + int bpr; /* bytes per row (as opposed to 4*gm->c) */ + int w, h, max; + + gm = NULL; + + w = readnum( f ); + if( w<0 ) + { + goto format_error; + } + + h = readnum( f ); + if( h<0 ) + { + goto format_error; + } + + /* allocate greymap */ + gm = gm_new( w, h ); + if( !gm ) + { + return -1; + } + + /* zero it out */ + gm_clear( gm, 0 ); + + switch( magic ) + { + default: + /* not reached */ + goto format_error; + + case '1': + /* read P1 format: PBM ascii */ + + for( y = h - 1; y>=0; y-- ) + { + for( x = 0; x=0; y-- ) + { + for( x = 0; x=0; y-- ) + { + for( x = 0; x=0; y-- ) + { + for( i = 0; i> j) ? 0 : 255 ); + } + } + } + + break; + + case '5': + /* read P5 format: PGM raw */ + + max = readnum( f ); + if( max<1 ) + { + goto format_error; + } + + b = fgetc( f ); /* read single white-space character after max */ + if( b==EOF ) + { + goto format_error; + } + + for( y = h - 1; y>=0; y-- ) + { + for( x = 0; x=256 ) + { + b <<= 8; + b1 = fgetc( f ); + if( b1==EOF ) + goto eof; + b |= b1; + } + GM_UPUT( gm, x, y, b * 255 / max ); + } + } + + break; + + case '6': + /* read P6 format: PPM raw */ + + max = readnum( f ); + if( max<1 ) + { + goto format_error; + } + + b = fgetc( f ); /* read single white-space character after max */ + if( b==EOF ) + { + goto format_error; + } + + for( y = h - 1; y>=0; y-- ) + { + for( x = 0; x=256 ) + { + b <<= 8; + b1 = fgetc( f ); + if( b1==EOF ) + goto eof; + b |= b1; + } + sum += b; + } + + GM_UPUT( gm, x, y, sum * (255 / 3) / max ); + } + } + + break; + } + + *gmp = gm; + return 0; + +eof: + *gmp = gm; + return 1; + +format_error: + gm_free( gm ); + if( magic == '1' || magic == '4' ) + { + gm_read_error = "invalid pbm file"; + } + else if( magic == '2' || magic == '5' ) + { + gm_read_error = "invalid pgm file"; + } + else + { + gm_read_error = "invalid ppm file"; + } + return -2; +} + + +/* ---------------------------------------------------------------------- */ +/* read BMP format */ + +struct bmp_info_s +{ + unsigned int FileSize; + unsigned int reserved; + unsigned int DataOffset; + unsigned int InfoSize; + unsigned int w; /* width */ + unsigned int h; /* height */ + unsigned int Planes; + unsigned int bits; /* bits per sample */ + unsigned int comp; /* compression mode */ + unsigned int ImageSize; + unsigned int XpixelsPerM; + unsigned int YpixelsPerM; + unsigned int ncolors; /* number of colors in palette */ + unsigned int ColorsImportant; + unsigned int ctbits; /* sample size for color table */ +}; +typedef struct bmp_info_s bmp_info_t; + +/* auxiliary */ + +static int bmp_count = 0; /* counter for byte padding */ +static int bmp_pos = 0; /* counter from start of BMP data */ + +/* read n-byte little-endian integer. Return 1 on EOF or error, else + * 0. Assume n<=4. */ +static int bmp_readint( FILE* f, int n, unsigned int* p ) +{ + int i; + unsigned int sum = 0; + int b; + + for( i = 0; i> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff); + coltable[i] = c / 3; + } + } + + /* forward to data */ + if( bmpinfo.InfoSize != 12 ) /* not old OS/2 format */ + { + TRY( bmp_forward( f, bmpinfo.DataOffset ) ); + } + + /* allocate greymap */ + gm = gm_new( bmpinfo.w, bmpinfo.h ); + if( !gm ) + { + goto std_error; + } + + /* zero it out */ + gm_clear( gm, 0 ); + + switch( bmpinfo.bits + 0x100 * bmpinfo.comp ) + { + default: + goto format_error; + break; + + case 0x001: /* monochrome palette */ + + /* raster data */ + for( y = 0; y> j) ? coltable[1] : coltable[0] ); + } + } + + TRY( bmp_pad( f ) ); + } + + break; + + case 0x002: /* 2-bit to 8-bit palettes */ + case 0x003: + case 0x004: + case 0x005: + case 0x006: + case 0x007: + case 0x008: + for( y = 0; y> (INTBITS - bmpinfo.bits); + bitbuf <<= bmpinfo.bits; + n -= bmpinfo.bits; + GM_UPUT( gm, x, y, coltable[b] ); + } + + TRY( bmp_pad( f ) ); + } + + break; + + case 0x010: /* 16-bit encoding */ + + /* can't do this format because it is not well-documented and I + * don't have any samples */ + gm_read_error = "cannot handle bmp 16-bit coding"; + goto format_error; + break; + + case 0x018: /* 24-bit encoding */ + case 0x020: /* 32-bit encoding */ + for( y = 0; y> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff); + GM_UPUT( gm, x, y, c / 3 ); + } + + TRY( bmp_pad( f ) ); + } + + break; + + case 0x204: /* 4-bit runlength compressed encoding (RLE4) */ + x = 0; + y = 0; + while( 1 ) + { + TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */ + if( b>0 ) + { + /* repeat count */ + col[0] = coltable[(c >> 4) & 0xf]; + col[1] = coltable[c & 0xf]; + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + GM_UPUT( gm, x, y, col[i & 1] ); + x++; + } + } + else if( c == 0 ) + { + /* end of line */ + y++; + x = 0; + } + else if( c == 1 ) + { + /* end of greymap */ + break; + } + else if( c == 2 ) + { + /* "delta": skip pixels in x and y directions */ + TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */ + x += b; + y += c; + } + else + { + /* verbatim segment */ + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + GM_PUT( gm, x, y, coltable[( b >> ( 4 - 4 * (i & 1) ) ) & 0xf] ); + x++; + } + + if( (c + 1) & 2 ) + { + /* pad to 16-bit boundary */ + TRY_EOF( bmp_readint( f, 1, &b ) ); + } + } + } + + break; + + case 0x108: /* 8-bit runlength compressed encoding (RLE8) */ + x = 0; + y = 0; + while( 1 ) + { + TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */ + if( b>0 ) + { + /* repeat count */ + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + GM_UPUT( gm, x, y, coltable[c] ); + x++; + } + } + else if( c == 0 ) + { + /* end of line */ + y++; + x = 0; + } + else if( c == 1 ) + { + /* end of greymap */ + break; + } + else if( c == 2 ) + { + /* "delta": skip pixels in x and y directions */ + TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */ + TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */ + x += b; + y += c; + } + else + { + /* verbatim segment */ + for( i = 0; i=bmpinfo.w ) + { + x = 0; + y++; + } + if( y>=bmpinfo.h ) + { + break; + } + GM_PUT( gm, x, y, coltable[b] ); + x++; + } + + if( c & 1 ) + { + /* pad input to 16-bit boundary */ + TRY_EOF( bmp_readint( f, 1, &b ) ); + } + } + } + + break; + } /* switch */ + + /* skip any potential junk after the data section, but don't + * complain in case EOF is encountered */ + bmp_forward( f, bmpinfo.FileSize ); + + free( coltable ); + *gmp = gm; + return 0; + +eof: + free( coltable ); + *gmp = gm; + return 1; + +format_error: +try_error: + free( coltable ); + free( gm ); + if( !gm_read_error ) + { + gm_read_error = "invalid bmp file"; + } + return -2; + +std_error: + free( coltable ); + free( gm ); + return -1; +} + + +/* ---------------------------------------------------------------------- */ + +/* write a pgm stream, either P2 or (if raw != 0) P5 format. Include + * one-line comment if non-NULL. Mode determines how out-of-range + * color values are converted. Gamma is the desired gamma correction, + * if any (set to 2.2 if the image is to look optimal on a CRT monitor, + * 2.8 for LCD). Set to 1.0 for no gamma correction */ + +int gm_writepgm( FILE* f, greymap_t* gm, char* comment, int raw, int mode, double gamma ) +{ + int x, y, v; + int gammatable[256]; + + /* prepare gamma correction lookup table */ + if( gamma != 1.0 ) + { + gammatable[0] = 0; + for( v = 1; v<256; v++ ) + { + gammatable[v] = (int) ( 255 * exp( log( v / 255.0 ) / gamma ) + 0.5 ); + } + } + else + { + for( v = 0; v<256; v++ ) + { + gammatable[v] = v; + } + } + + fprintf( f, raw ? "P5\n" : "P2\n" ); + if( comment && *comment ) + { + fprintf( f, "# %s\n", comment ); + } + fprintf( f, "%d %d 255\n", gm->w, gm->h ); + for( y = gm->h - 1; y>=0; y-- ) + { + for( x = 0; xw; x++ ) + { + v = GM_UGET( gm, x, y ); + if( mode == GM_MODE_NONZERO ) + { + if( v > 255 ) + { + v = 510 - v; + } + if( v < 0 ) + { + v = 0; + } + } + else if( mode == GM_MODE_ODD ) + { + v = mod( v, 510 ); + if( v > 255 ) + { + v = 510 - v; + } + } + else if( mode == GM_MODE_POSITIVE ) + { + if( v < 0 ) + { + v = 0; + } + else if( v > 255 ) + { + v = 255; + } + } + else if( mode == GM_MODE_NEGATIVE ) + { + v = 510 - v; + if( v < 0 ) + { + v = 0; + } + else if( v > 255 ) + { + v = 255; + } + } + v = gammatable[v]; + + if( raw ) + { + fputc( v, f ); + } + else + { + fprintf( f, x == gm->w - 1 ? "%d\n" : "%d ", v ); + } + } + } + + return 0; +} + + +/* ---------------------------------------------------------------------- */ +/* output - for primitive debugging purposes only! */ + +/* print greymap to screen */ +int gm_print( FILE* f, greymap_t* gm ) +{ + int x, y; + int xx, yy; + int d, t; + int sw, sh; + + sw = gm->w < 79 ? gm->w : 79; + sh = gm->w < 79 ? gm->h : gm->h * sw * 44 / (79 * gm->w); + + for( yy = sh - 1; yy>=0; yy-- ) + { + for( xx = 0; xxw / sw; x<(xx + 1) * gm->w / sw; x++ ) + { + for( y = yy * gm->h / sh; y<(yy + 1) * gm->h / sh; y++ ) + { + d += GM_GET( gm, x, y ); + t += 256; + } + } + + fputc( "*#=- "[5 * d / t], f ); /* what a cute trick :) */ + } + + fputc( '\n', f ); + } + + return 0; +} diff --git a/potrace/greymap.h b/potrace/greymap.h new file mode 100644 index 0000000000..dcb926a145 --- /dev/null +++ b/potrace/greymap.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: greymap.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef PGM_H +#define PGM_H + +#include + +/* internal format for greymaps. Note: in this format, rows are + ordered from bottom to top. The pixels in each row are given from + left to right. */ + +struct greymap_s { + int w; /* width, in pixels */ + int h; /* height, in pixels */ + signed short int *map; /* raw data, w*h values */ +}; +typedef struct greymap_s greymap_t; + +/* macros for accessing pixel at index (x,y). Note that the origin is + in the *lower* left corner. U* macros omit the bounds check. */ + +#define gm_index(gm, x, y) (&(gm)->map[(x)+(y)*(gm)->w]) +#define gm_safe(gm, x, y) ((int)(x)>=0 && (int)(x)<(gm)->w && (int)(y)>=0 && (int)(y)<(gm)->h) +#define gm_bound(x, m) ((x)<0 ? 0 : (x)>=(m) ? (m)-1 : (x)) +#define GM_UGET(gm, x, y) (*gm_index(gm, x, y)) +#define GM_UINC(gm, x, y, b) (*gm_index(gm, x, y) += (short int)(b)) +#define GM_UINV(gm, x, y) (*gm_index(gm, x, y) = 255 - *gm_index(gm, x, y)) +#define GM_UPUT(gm, x, y, b) (*gm_index(gm, x, y) = (short int)(b)) +#define GM_GET(gm, x, y) (gm_safe(gm, x, y) ? GM_UGET(gm, x, y) : 0) +#define GM_INC(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UINC(gm, x, y, b) : 0) +#define GM_INV(gm, x, y) (gm_safe(gm, x, y) ? GM_UINV(gm, x, y) : 0) +#define GM_PUT(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UPUT(gm, x, y, b) : 0) +#define GM_BGET(gm, x, y) GM_UGET(gm, gm_bound(x, gm->w), gm_bound(y, gm->h)) + +/* modes for cutting off out-of-range values. The following names + refer to winding numbers. I.e., make a pixel black if winding + number is nonzero, odd, or positive, respectively. We assume that 0 + winding number corresponds to white (255). */ +#define GM_MODE_NONZERO 1 +#define GM_MODE_ODD 2 +#define GM_MODE_POSITIVE 3 +#define GM_MODE_NEGATIVE 4 + +extern const char *gm_read_error; + +greymap_t *gm_new(int w, int h); +greymap_t *gm_dup(greymap_t *gm); +void gm_free(greymap_t *gm); +void gm_clear(greymap_t *gm, int b); +int gm_read(FILE *f, greymap_t **gmp); +int gm_writepgm(FILE *f, greymap_t *gm, char *comment, int raw, int mode, double gamma); +int gm_print(FILE *f, greymap_t *gm); + +#endif /* PGM_H */ diff --git a/potrace/lists.h b/potrace/lists.h new file mode 100644 index 0000000000..53478205ae --- /dev/null +++ b/potrace/lists.h @@ -0,0 +1,293 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: lists.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef _PS_LISTS_H +#define _PS_LISTS_H + +/* here we define some general list macros. Because they are macros, + * they should work on any datatype with a "->next" component. Some of + * them use a "hook". If elt and list are of type t* then hook is of + * type t**. A hook stands for an insertion point in the list, i.e., + * either before the first element, or between two elements, or after + * the last element. If an operation "sets the hook" for an element, + * then the hook is set to just before the element. One can insert + * something at a hook. One can also unlink at a hook: this means, + * unlink the element just after the hook. By "to unlink", we mean the + * element is removed from the list, but not deleted. Thus, it and its + * components still need to be freed. */ + +/* Note: these macros are somewhat experimental. Only the ones that + * are actually *used* have been tested. So be careful to test any + * that you use. Looking at the output of the preprocessor, "gcc -E" + * (possibly piped though "indent"), might help too. Also: these + * macros define some internal (local) variables that start with + * "_". */ + +/* we enclose macro definitions whose body consists of more than one + * statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'. The + * reason is that we want to be able to use the macro in a context + * such as "if (...) macro(...); else ...". If we didn't use this obscure + * trick, we'd have to omit the ";" in such cases. */ + +#define MACRO_BEGIN do { +#define MACRO_END } while( 0 ) + +/* ---------------------------------------------------------------------- */ +/* macros for singly-linked lists */ + +/* traverse list. At the end, elt is set to NULL. */ +#define list_forall( elt, list ) for( elt = list; elt!=NULL; elt = elt->next ) + +/* set elt to the first element of list satisfying boolean condition + * c, or NULL if not found */ +#define list_find( elt, list, c ) \ + MACRO_BEGIN list_forall( elt, list ) if( c ) \ + break;MACRO_END + +/* like forall, except also set hook for elt. */ +#define list_forall2( elt, list, hook ) \ + for( elt = list, hook = &list; elt!=NULL; hook = &elt->next, elt = elt->next ) + +/* same as list_find, except also set hook for elt. */ +#define list_find2( elt, list, c, hook ) \ + MACRO_BEGIN list_forall2( elt, list, hook ) if( c ) \ + break;MACRO_END + +/* same, except only use hook. */ +#define _list_forall_hook( list, hook ) \ + for( hook = &list; *hook!=NULL; hook = &(*hook)->next ) + +/* same, except only use hook. Note: c may only refer to *hook, not elt. */ +#define _list_find_hook( list, c, hook ) \ + MACRO_BEGIN _list_forall_hook( list, hook ) if( c ) \ + break;MACRO_END + +/* insert element after hook */ +#define list_insert_athook( elt, hook ) \ + MACRO_BEGIN elt->next = *hook; *hook = elt; MACRO_END + +/* insert element before hook */ +#define list_insert_beforehook( elt, hook ) \ + MACRO_BEGIN elt->next = *hook; *hook = elt; hook = &elt->next; MACRO_END + +/* unlink element after hook, let elt be unlinked element, or NULL. + * hook remains. */ +#define list_unlink_athook( list, elt, hook ) \ + MACRO_BEGIN \ + elt = hook ? *hook : NULL; if( elt ) { *hook = elt->next; elt->next = NULL; } \ + MACRO_END + +/* unlink the specific element, if it is in the list. Otherwise, set + * elt to NULL */ +#define list_unlink( listtype, list, elt ) \ + MACRO_BEGIN \ + listtype * *_hook; \ + _list_find_hook( list, *_hook==elt, _hook ); \ + list_unlink_athook( list, elt, _hook ); \ + MACRO_END + +/* prepend elt to list */ +#define list_prepend( list, elt ) \ + MACRO_BEGIN elt->next = list; list = elt; MACRO_END + +/* append elt to list. */ +#define list_append( listtype, list, elt ) \ + MACRO_BEGIN \ + listtype * *_hook; \ + _list_forall_hook( list, _hook ) {} \ + list_insert_athook( elt, _hook ); \ + MACRO_END + +/* unlink the first element that satisfies the condition. */ +#define list_unlink_cond( listtype, list, elt, c ) \ + MACRO_BEGIN \ + listtype * *_hook; \ + list_find2( elt, list, c, _hook ); \ + list_unlink_athook( list, elt, _hook ); \ + MACRO_END + +/* let elt be the nth element of the list, starting to count from 0. + * Return NULL if out of bounds. */ +#define list_nth( elt, list, n ) \ + MACRO_BEGIN \ + int _x; /* only evaluate n once */ \ + for( _x = (n), elt = list; _x && elt; _x--, elt = elt->next ) {} \ + MACRO_END + +/* let elt be the nth element of the list, starting to count from 0. + * Return NULL if out of bounds. */ +#define list_nth_hook( elt, list, n, hook ) \ + MACRO_BEGIN \ + int _x; /* only evaluate n once */ \ + for( _x = (n), elt = list, hook = &list; _x && elt; _x--, hook = &elt->next, elt =\ + elt->next ) {} \ + MACRO_END + +/* set n to the length of the list */ +#define list_length( listtype, list, n ) \ + MACRO_BEGIN \ + listtype * _elt; \ + n = 0; \ + list_forall( _elt, list ) \ + n++; \ + MACRO_END + +/* set n to the index of the first element satisfying cond, or -1 if + * none found. Also set elt to the element, or NULL if none found. */ +#define list_index( list, n, elt, c ) \ + MACRO_BEGIN \ + n = 0; \ + list_forall( elt, list ) { \ + if( c ) \ + break;\ + n++; \ + } \ + if( !elt ) \ + n = -1;\ + MACRO_END + +/* set n to the number of elements in the list that satisfy condition c */ +#define list_count( list, n, elt, c ) \ + MACRO_BEGIN \ + n = 0; \ + list_forall( elt, list ) { \ + if( c ) \ + n++;\ + } \ + MACRO_END + +/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ +#define list_forall_unlink( elt, list ) \ + for( elt = list; elt ? (list = elt->next, elt->next = NULL), 1 : 0; elt = list ) + +/* reverse a list (efficient) */ +#define list_reverse( listtype, list ) \ + MACRO_BEGIN \ + listtype * _list1 = NULL, *elt; \ + list_forall_unlink( elt, list ) \ + list_prepend( _list1, elt ); \ + list = _list1; \ + MACRO_END + +/* insert the element ELT just before the first element TMP of the + * list for which COND holds. Here COND must be a condition of ELT and + * TMP. Typical usage is to insert an element into an ordered list: + * for instance, list_insert_ordered(listtype, list, elt, tmp, + * elt->size <= tmp->size). Note: if we give a "less than or equal" + * condition, the new element will be inserted just before a sequence + * of equal elements. If we give a "less than" condition, the new + * element will be inserted just after a list of equal elements. + * Note: it is much more efficient to construct a list with + * list_prepend and then order it with list_merge_sort, than to + * construct it with list_insert_ordered. */ +#define list_insert_ordered( listtype, list, elt, tmp, cond ) \ + MACRO_BEGIN \ + listtype * *_hook; \ + _list_find_hook( list, ( tmp = *_hook, (cond) ), _hook ); \ + list_insert_athook( elt, _hook ); \ + MACRO_END + +/* sort the given list, according to the comparison condition. + * Typical usage is list_sort(listtype, list, a, b, a->size < + * b->size). Note: if we give "less than or equal" condition, each + * segment of equal elements will be reversed in order. If we give a + * "less than" condition, each segment of equal elements will retain + * the original order. The latter is slower but sometimes + * prettier. Average running time: n*n/2. */ +#define list_sort( listtype, list, a, b, cond ) \ + MACRO_BEGIN \ + listtype * _newlist = NULL; \ + list_forall_unlink( a, list ) \ + list_insert_ordered( listtype, _newlist, a, b, cond ); \ + list = _newlist; \ + MACRO_END + +/* a much faster sort algorithm (merge sort, n log n worst case). It + * is required that the list type has an additional, unused next1 + * component. Note there is no curious reversal of order of equal + * elements as for list_sort. */ + +#define list_mergesort( listtype, list, a, b, cond ) \ + MACRO_BEGIN \ + listtype * _elt, **_hook1; \ + \ + for( _elt = list; _elt; _elt = _elt->next1 ) { \ + _elt->next1 = _elt->next; \ + _elt->next = NULL; \ + } \ + do { \ + _hook1 = &(list); \ + while( (a = *_hook1) != NULL && (b = a->next1) != NULL ) { \ + _elt = b->next1; \ + _list_merge_cond( listtype, a, b, cond, *_hook1 ); \ + _hook1 = &( (*_hook1)->next1 ); \ + *_hook1 = _elt; \ + } \ + } while( _hook1 != &(list) ); \ + MACRO_END + +/* merge two sorted lists. Store result at &result */ +#define _list_merge_cond( listtype, a, b, cond, result ) \ + MACRO_BEGIN \ + listtype * *_hook; \ + _hook = &(result); \ + while( 1 ) { \ + if( a==NULL ) { \ + *_hook = b; \ + break; \ + } else if( b==NULL ) { \ + *_hook = a; \ + break; \ + } else if( cond ) { \ + *_hook = a; \ + _hook = &(a->next); \ + a = a->next; \ + } else { \ + *_hook = b; \ + _hook = &(b->next); \ + b = b->next; \ + } \ + } \ + MACRO_END + +/* ---------------------------------------------------------------------- */ +/* macros for doubly-linked lists */ + +#define dlist_append( head, end, elt ) \ + MACRO_BEGIN \ + elt->prev = end; \ + elt->next = NULL; \ + if( end ) { \ + end->next = elt; \ + } else { \ + head = elt; \ + } \ + end = elt; \ + MACRO_END + +/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ +#define dlist_forall_unlink( elt, head, end ) \ + for( elt = head;\ + elt ? (head = elt->next, elt->next = NULL, elt->prev = NULL), 1 : (end = NULL, 0); \ + elt = head ) + +/* unlink the first element of the list */ +#define dlist_unlink_first( head, end, elt ) \ + MACRO_BEGIN \ + elt = head; \ + if( head ) { \ + head = head->next; \ + if( head ) { \ + head->prev = NULL; \ + } else { \ + end = NULL; \ + } \ + elt->prev = NULL; \ + elt->next = NULL; \ + } \ + MACRO_END + +#endif /* _PS_LISTS_H */ diff --git a/potrace/platform.h b/potrace/platform.h new file mode 100644 index 0000000000..466f309279 --- /dev/null +++ b/potrace/platform.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* this header file contains some platform dependent stuff */ + +#ifndef PLATFORM_H +#define PLATFORM_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* in Windows, set all file i/o to binary */ +#ifdef __MINGW32__ +#include +unsigned int _CRT_fmode = _O_BINARY; +#endif + +#ifdef __CYGWIN__ +#include +#include +static inline void platform_init( void ) +{ + setmode( 0, O_BINARY ); + setmode( 1, O_BINARY ); +} + + +#else +static inline void platform_init( void ) +{ + /* NOP */ +} + + +#endif + +#endif /* PLATFORM_H */ diff --git a/potrace/potrace_version.h b/potrace/potrace_version.h new file mode 100644 index 0000000000..71cdbaf0d5 --- /dev/null +++ b/potrace/potrace_version.h @@ -0,0 +1 @@ +#define POTRACELIB_VERSION "potracelib 1.8" diff --git a/potrace/potracelib.cpp b/potrace/potracelib.cpp new file mode 100644 index 0000000000..e99c5b17a0 --- /dev/null +++ b/potrace/potracelib.cpp @@ -0,0 +1,129 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. + */ + +#include +#include + +#include "potracelib.h" +#include "curve.h" +#include "decompose.h" +#include "trace.h" +#include "progress.h" +#include "potrace_version.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* default parameters */ +static const potrace_param_t param_default = +{ + 2, /* turdsize */ + POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */ + 1.0, /* alphamax */ + 1, /* opticurve */ + 0.2, /* opttolerance */ + { + NULL, /* callback function */ + NULL, /* callback data */ + 0.0, 1.0, /* progress range */ + 0.0, /* granularity */ + }, +}; + +/* Return a fresh copy of the set of default parameters, or NULL on + * failure with errno set. */ +potrace_param_t* potrace_param_default( void ) +{ + potrace_param_t* p; + + p = (potrace_param_t*) malloc( sizeof(potrace_param_t) ); + if( !p ) + { + return NULL; + } + memcpy( p, ¶m_default, sizeof(potrace_param_t) ); + return p; +} + + +/* On success, returns a Potrace state st with st->status == + * POTRACE_STATUS_OK. On failure, returns NULL if no Potrace state + * could be created (with errno set), or returns an incomplete Potrace + * state (with st->status == POTRACE_STATUS_INCOMPLETE). Complete or + * incomplete Potrace state can be freed with potrace_state_free(). */ +potrace_state_t* potrace_trace( const potrace_param_t* param, const potrace_bitmap_t* bm ) +{ + int r; + path_t* plist = NULL; + potrace_state_t* st; + progress_t prog; + progress_t subprog; + + /* prepare private progress bar state */ + prog.callback = param->progress.callback; + prog.data = param->progress.data; + prog.min = param->progress.min; + prog.max = param->progress.max; + prog.epsilon = param->progress.epsilon; + prog.d_prev = param->progress.min; + + /* allocate state object */ + st = (potrace_state_t*) malloc( sizeof(potrace_state_t) ); + if( !st ) + { + return NULL; + } + + progress_subrange_start( 0.0, 0.1, &prog, &subprog ); + + /* process the image */ + r = bm_to_pathlist( bm, &plist, param, &subprog ); + if( r ) + { + free( st ); + return NULL; + } + + st->status = POTRACE_STATUS_OK; + st->plist = plist; + st->priv = NULL; /* private state currently unused */ + + progress_subrange_end( &prog, &subprog ); + + progress_subrange_start( 0.1, 1.0, &prog, &subprog ); + + /* partial success. */ + r = process_path( plist, param, &subprog ); + if( r ) + { + st->status = POTRACE_STATUS_INCOMPLETE; + } + + progress_subrange_end( &prog, &subprog ); + + return st; +} + + +/* free a Potrace state, without disturbing errno. */ +void potrace_state_free( potrace_state_t* st ) +{ + pathlist_free( st->plist ); + free( st ); +} + + +/* free a parameter list, without disturbing errno. */ +void potrace_param_free( potrace_param_t* p ) +{ + free( p ); +} + + +const char* potrace_version( void ) +{ + return POTRACELIB_VERSION; +} diff --git a/potrace/potracelib.h b/potrace/potracelib.h new file mode 100644 index 0000000000..e7474f831f --- /dev/null +++ b/potrace/potracelib.h @@ -0,0 +1,138 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +#ifndef POTRACELIB_H +#define POTRACELIB_H + +/* this file defines the API for the core Potrace library. For a more + * detailed description of the API, see doc/potracelib.txt */ + +/* ---------------------------------------------------------------------- */ +/* tracing parameters */ + +/* turn policies */ +#define POTRACE_TURNPOLICY_BLACK 0 +#define POTRACE_TURNPOLICY_WHITE 1 +#define POTRACE_TURNPOLICY_LEFT 2 +#define POTRACE_TURNPOLICY_RIGHT 3 +#define POTRACE_TURNPOLICY_MINORITY 4 +#define POTRACE_TURNPOLICY_MAJORITY 5 +#define POTRACE_TURNPOLICY_RANDOM 6 + +/* structure to hold progress bar callback data */ +struct potrace_progress_s +{ + void (* callback)( double progress, void* privdata ); /* callback fn */ + void* data; /* callback function's private data */ + double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */ + double epsilon; /* granularity: can skip smaller increments */ +}; +typedef struct potrace_progress_s potrace_progress_t; + +/* structure to hold tracing parameters */ +struct potrace_param_s +{ + int turdsize; /* area of largest path to be ignored */ + int turnpolicy; /* resolves ambiguous turns in path decomposition */ + double alphamax; /* corner threshold */ + int opticurve; /* use curve optimization? */ + double opttolerance; /* curve optimization tolerance */ + potrace_progress_t progress; /* progress callback function */ +}; +typedef struct potrace_param_s potrace_param_t; + +/* ---------------------------------------------------------------------- */ +/* bitmaps */ + +/* native word size */ +typedef unsigned long potrace_word; + +/* Internal bitmap format. The n-th scanline starts at scanline(n) = + * (map + n*dy). Raster data is stored as a sequence of potrace_words + * (NOT bytes). The leftmost bit of scanline n is the most significant + * bit of scanline(n)[0]. */ +struct potrace_bitmap_s +{ + int w, h; /* width and height, in pixels */ + int dy; /* words per scanline (not bytes) */ + potrace_word* map; /* raw data, dy*h words */ +}; +typedef struct potrace_bitmap_s potrace_bitmap_t; + +/* ---------------------------------------------------------------------- */ +/* curves */ + +/* point */ +struct potrace_dpoint_s +{ + double x, y; +}; +typedef struct potrace_dpoint_s potrace_dpoint_t; + +/* segment tags */ +#define POTRACE_CURVETO 1 +#define POTRACE_CORNER 2 + +/* closed curve segment */ +struct potrace_curve_s +{ + int n; /* number of segments */ + int* tag; /* tag[n]: POTRACE_CURVETO or POTRACE_CORNER */ + potrace_dpoint_t( * c )[3]; /* c[n][3]: control points. + * c[n][0] is unused for tag[n]=POTRACE_CORNER */ +}; +typedef struct potrace_curve_s potrace_curve_t; + +/* Linked list of signed curve segments. Also carries a tree structure. */ +struct potrace_path_s +{ + int area; /* area of the bitmap path */ + int sign; /* '+' or '-', depending on orientation */ + potrace_curve_t curve; /* this path's vector data */ + + struct potrace_path_s* next; /* linked list structure */ + + struct potrace_path_s* childlist; /* tree structure */ + struct potrace_path_s* sibling; /* tree structure */ + + struct potrace_privpath_s* priv; /* private state */ +}; +typedef struct potrace_path_s potrace_path_t; + +/* ---------------------------------------------------------------------- */ +/* Potrace state */ + +#define POTRACE_STATUS_OK 0 +#define POTRACE_STATUS_INCOMPLETE 1 + +struct potrace_state_s +{ + int status; + potrace_path_t* plist; /* vector data */ + + struct potrace_privstate_s* priv; /* private state */ +}; +typedef struct potrace_state_s potrace_state_t; + +/* ---------------------------------------------------------------------- */ +/* API functions */ + +/* get default parameters */ +potrace_param_t* potrace_param_default( void ); + +/* free parameter set */ +void potrace_param_free( potrace_param_t* p ); + +/* trace a bitmap*/ +potrace_state_t* potrace_trace( const potrace_param_t* param, + const potrace_bitmap_t* bm ); + +/* free a Potrace state */ +void potrace_state_free( potrace_state_t* st ); + +/* return a static plain text version string identifying this version + * of potracelib */ +const char* potrace_version( void ); + +#endif /* POTRACELIB_H */ diff --git a/potrace/progress.h b/potrace/progress.h new file mode 100644 index 0000000000..5010481ab2 --- /dev/null +++ b/potrace/progress.h @@ -0,0 +1,96 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* operations on potrace_progress_t objects, which are defined in + * potracelib.h. Note: the code attempts to minimize runtime overhead + * when no progress monitoring was requested. It also tries to + * minimize excessive progress calculations beneath the "epsilon" + * threshold. */ + +#ifndef PROGRESS_H +#define PROGRESS_H + +/* structure to hold progress bar callback data */ +struct progress_s +{ + void (* callback)( double progress, void* privdata ); /* callback fn */ + void* data; /* callback function's private data */ + double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */ + double epsilon; /* granularity: can skip smaller increments */ + double b; /* upper limit of subrange in superrange units */ + double d_prev; /* previous value of d */ +}; +typedef struct progress_s progress_t; + +/* notify given progress object of current progress. Note that d is + * given in the 0.0-1.0 range, which will be scaled and translated to + * the progress object's range. */ +static inline void progress_update( double d, progress_t* prog ) +{ + double d_scaled; + + if( prog->callback != NULL ) + { + d_scaled = prog->min * (1 - d) + prog->max * d; + if( d == 1.0 || d_scaled >= prog->d_prev + prog->epsilon ) + { + prog->callback( prog->min * (1 - d) + prog->max * d, prog->data ); + prog->d_prev = d_scaled; + } + } +} + + +/* start a subrange of the given progress object. The range is + * narrowed to [a..b], relative to 0.0-1.0 coordinates. If new range + * is below granularity threshold, disable further subdivisions. */ +static inline void progress_subrange_start( double a, + double b, + const progress_t* prog, + progress_t* sub ) +{ + double min, max; + + if( prog->callback == NULL ) + { + sub->callback = NULL; + return; + } + + min = prog->min * (1 - a) + prog->max * a; + max = prog->min * (1 - b) + prog->max * b; + + if( max - min < prog->epsilon ) + { + sub->callback = NULL; /* no further progress info in subrange */ + sub->b = b; + return; + } + sub->callback = prog->callback; + sub->data = prog->data; + sub->epsilon = prog->epsilon; + sub->min = min; + sub->max = max; + sub->d_prev = prog->d_prev; + return; +} + + +static inline void progress_subrange_end( progress_t* prog, progress_t* sub ) +{ + if( prog->callback != NULL ) + { + if( sub->callback == NULL ) + { + progress_update( sub->b, prog ); + } + else + { + prog->d_prev = sub->d_prev; + } + } +} + + +#endif /* PROGRESS_H */ diff --git a/potrace/render.cpp b/potrace/render.cpp new file mode 100644 index 0000000000..9f310c399d --- /dev/null +++ b/potrace/render.cpp @@ -0,0 +1,294 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: render.c 147 2007-04-09 00:44:09Z selinger $ */ + +#include +#include +#include +#include + +#include "render.h" +#include "greymap.h" +#include "auxiliary.h" + +/* ---------------------------------------------------------------------- */ +/* routines for anti-aliased rendering of curves */ + +/* we use the following method. Given a point (x,y) (with real-valued + * coordinates) in the plane, let (xi,yi) be the integer part of the + * coordinates, i.e., xi=floor(x), yi=floor(y). Define a path from + * (x,y) to infinity as follows: path(x,y) = + * (x,y)--(xi+1,y)--(xi+1,yi)--(+infty,yi). Now as the point (x,y) + * moves smoothly across the plane, the path path(x,y) sweeps + * (non-smoothly) across a certain area. We proportionately blacken + * the area as the path moves "downward", and we whiten the area as + * the path moves "upward". This way, after the point has traversed a + * closed curve, the interior of the curve has been darkened + * (counterclockwise movement) or lightened (clockwise movement). (The + * "grey shift" is actually proportional to the winding number). By + * choosing the above path with mostly integer coordinates, we achieve + * that only pixels close to (x,y) receive grey values and are subject + * to round-off errors. The grey value of pixels far away from (x,y) + * is always in "integer" (where 0=black, 1=white). As a special + * trick, we keep an accumulator rm->a1, which holds a double value to + * be added to the grey value to be added to the current pixel + * (xi,yi). Only when changing "current" pixels, we convert this + * double value to an integer. This way we avoid round-off errors at + * the meeting points of line segments. Another speedup measure is + * that we sometimes use the rm->incrow_buf array to postpone + * incrementing or decrementing an entire row. If incrow_buf[y]=x+1!=0, + * then all the pixels (x,y),(x+1,y),(x+2,y),... are scheduled to be + * incremented/decremented (which one is the case will be clear from + * context). This keeps the greymap operations reasonably local. */ + +/* allocate a new rendering state */ +render_t* render_new( greymap_t* gm ) +{ + render_t* rm; + + rm = (render_t*) malloc( sizeof(render_t) ); + if( !rm ) + { + return NULL; + } + memset( rm, 0, sizeof(render_t) ); + rm->gm = gm; + rm->incrow_buf = (int*) malloc( gm->h * sizeof(int) ); + if( !rm->incrow_buf ) + { + free( rm ); + return NULL; + } + memset( rm->incrow_buf, 0, gm->h * sizeof(int) ); + return rm; +} + + +/* free a given rendering state. Note: this does not free the + * underlying greymap. */ +void render_free( render_t* rm ) +{ + free( rm->incrow_buf ); + free( rm ); +} + + +/* close path */ +void render_close( render_t* rm ) +{ + if( rm->x0 != rm->x1 || rm->y0 != rm->y1 ) + { + render_lineto( rm, rm->x0, rm->y0 ); + } + GM_INC( rm->gm, rm->x0i, rm->y0i, (rm->a0 + rm->a1) * 255 ); + + /* assert (rm->x0i != rm->x1i || rm->y0i != rm->y1i); */ + + /* the persistent state is now undefined */ +} + + +/* move point */ +void render_moveto( render_t* rm, double x, double y ) +{ + /* close the previous path */ + render_close( rm ); + + rm->x0 = rm->x1 = x; + rm->y0 = rm->y1 = y; + rm->x0i = (int) floor( rm->x0 ); + rm->x1i = (int) floor( rm->x1 ); + rm->y0i = (int) floor( rm->y0 ); + rm->y1i = (int) floor( rm->y1 ); + rm->a0 = rm->a1 = 0; +} + + +/* add b to pixels (x,y) and all pixels to the right of it. However, + * use rm->incrow_buf as a buffer to economize on multiple calls */ +static void incrow( render_t* rm, int x, int y, int b ) +{ + int i, x0; + + if( y < 0 || y >= rm->gm->h ) + { + return; + } + + if( x < 0 ) + { + x = 0; + } + else if( x > rm->gm->w ) + { + x = rm->gm->w; + } + if( rm->incrow_buf[y] == 0 ) + { + rm->incrow_buf[y] = x + 1; /* store x+1 so that we can use 0 for "vacant" */ + return; + } + x0 = rm->incrow_buf[y] - 1; + rm->incrow_buf[y] = 0; + if( x0 < x ) + { + for( i = x0; igm, i, y, -b ); + } + } + else + { + for( i = x; igm, i, y, b ); + } + } +} + + +/* render a straight line */ +void render_lineto( render_t* rm, double x2, double y2 ) +{ + int x2i, y2i; + double t0 = 2, s0 = 2; + int sn, tn; + double ss = 2, ts = 2; + double r0, r1; + int i, j; + int rxi, ryi; + int s; + + x2i = (int) floor( x2 ); + y2i = (int) floor( y2 ); + + sn = abs( x2i - rm->x1i ); + tn = abs( y2i - rm->y1i ); + + if( sn ) + { + s0 = ( (x2>rm->x1 ? rm->x1i + 1 : rm->x1i) - rm->x1 ) / (x2 - rm->x1); + ss = fabs( 1.0 / (x2 - rm->x1) ); + } + if( tn ) + { + t0 = ( (y2>rm->y1 ? rm->y1i + 1 : rm->y1i) - rm->y1 ) / (y2 - rm->y1); + ts = fabs( 1.0 / (y2 - rm->y1) ); + } + + r0 = 0; + + i = 0; + j = 0; + + rxi = rm->x1i; + ryi = rm->y1i; + + while( i=tn || (ix1,rm->y1)..(x2,y2) */ + + /* move point to r1 */ + rm->a1 += + (r1 - r0) * (y2 - rm->y1) * ( rxi + 1 - ( (r0 + r1) / 2.0 * (x2 - rm->x1) + rm->x1 ) ); + + /* move point across pixel boundary */ + if( s && x2>rm->x1 ) + { + GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 ); + rm->a1 = 0; + rxi++; + rm->a1 += rm->y1 + r1 * (y2 - rm->y1) - ryi; + } + else if( !s && y2>rm->y1 ) + { + GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 ); + rm->a1 = 0; + incrow( rm, rxi + 1, ryi, 255 ); + ryi++; + } + else if( s && x2<=rm->x1 ) + { + rm->a1 -= rm->y1 + r1 * (y2 - rm->y1) - ryi; + GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 ); + rm->a1 = 0; + rxi--; + } + else if( !s && y2<=rm->y1 ) + { + GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 ); + rm->a1 = 0; + ryi--; + incrow( rm, rxi + 1, ryi, -255 ); + } + + r0 = r1; + } + + /* move point to (x2,y2) */ + + r1 = 1; + rm->a1 += (r1 - r0) * (y2 - rm->y1) * ( rxi + 1 - ( (r0 + r1) / 2.0 * (x2 - rm->x1) + rm->x1 ) ); + + rm->x1i = x2i; + rm->y1i = y2i; + rm->x1 = x2; + rm->y1 = y2; + + /* assert (rxi != rm->x1i || ryi != rm->y1i); */ +} + + +/* render a Bezier curve. */ +void render_curveto( render_t* rm, + double x2, + double y2, + double x3, + double y3, + double x4, + double y4 ) +{ + double x1, y1, dd0, dd1, dd, delta, e2, epsilon, t; + + x1 = rm->x1; /* starting point */ + y1 = rm->y1; + + /* we approximate the curve by small line segments. The interval + * size, epsilon, is determined on the fly so that the distance + * between the true curve and its approximation does not exceed the + * desired accuracy delta. */ + + delta = .1; /* desired accuracy, in pixels */ + + /* let dd = maximal value of 2nd derivative over curve - this must + * occur at an endpoint. */ + dd0 = sq( x1 - 2 * x2 + x3 ) + sq( y1 - 2 * y2 + y3 ); + dd1 = sq( x2 - 2 * x3 + x4 ) + sq( y2 - 2 * y3 + y4 ); + dd = 6 * sqrt( max( dd0, dd1 ) ); + e2 = 8 * delta <= dd ? 8 * delta / dd : 1; + epsilon = sqrt( e2 ); /* necessary interval size */ + + for( t = epsilon; t<1; t += epsilon ) + { + render_lineto( rm, x1 * cu( 1 - t ) + 3 * x2 * sq( 1 - t ) * t + 3 * x3 * (1 - t) * sq( + t ) + x4 * cu( t ), + y1 * cu( 1 - t ) + 3 * y2 * sq( 1 - t ) * t + 3 * y3 * (1 - t) * sq( + t ) + y4 * cu( t ) ); + } + + render_lineto( rm, x4, y4 ); +} diff --git a/potrace/render.h b/potrace/render.h new file mode 100644 index 0000000000..f9a3d797c2 --- /dev/null +++ b/potrace/render.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: render.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef RENDER_H +#define RENDER_H + +#include "greymap.h" + +struct render_s +{ + greymap_t* gm; + double x0, y0, x1, y1; + int x0i, y0i, x1i, y1i; + double a0, a1; + int* incrow_buf; +}; +typedef struct render_s render_t; + +render_t* render_new( greymap_t* gm ); +void render_free( render_t* rm ); +void render_close( render_t* rm ); +void render_moveto( render_t* rm, double x, double y ); +void render_lineto( render_t* rm, double x, double y ); +void render_curveto( render_t* rm, + double x2, + double y2, + double x3, + double y3, + double x4, + double y4 ); + +#endif /* RENDER_H */ diff --git a/potrace/trace.cpp b/potrace/trace.cpp new file mode 100644 index 0000000000..9d110fba12 --- /dev/null +++ b/potrace/trace.cpp @@ -0,0 +1,1456 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: trace.c 147 2007-04-09 00:44:09Z selinger $ */ +/* transform jaggy paths into smooth curves */ + +#include +#include +#include +#include + +#include "potracelib.h" +#include "curve.h" +#include "lists.h" +#include "auxiliary.h" +#include "trace.h" +#include "progress.h" + +#define INFTY 10000000 /* it suffices that this is longer than any + * path; it need not be really infinite */ +#define COS179 -0.999847695156 /* the cosine of 179 degrees */ + +/* ---------------------------------------------------------------------- */ +#define SAFE_MALLOC( var, n, typ ) \ + if( ( var = (typ*) malloc( (n) * sizeof(typ) ) ) == NULL ) \ + goto malloc_error + +/* ---------------------------------------------------------------------- */ +/* auxiliary functions */ + +/* return a direction that is 90 degrees counterclockwise from p2-p0, + * but then restricted to one of the major wind directions (n, nw, w, etc) */ +static inline point_t dorth_infty( dpoint_t p0, dpoint_t p2 ) +{ + point_t r; + + r.y = sign( p2.x - p0.x ); + r.x = -sign( p2.y - p0.y ); + + return r; +} + + +/* return (p1-p0)x(p2-p0), the area of the parallelogram */ +static inline double dpara( dpoint_t p0, dpoint_t p1, dpoint_t p2 ) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; + + return x1 * y2 - x2 * y1; +} + + +/* ddenom/dpara have the property that the square of radius 1 centered + * at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ +static inline double ddenom( dpoint_t p0, dpoint_t p2 ) +{ + point_t r = dorth_infty( p0, p2 ); + + return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); +} + + +/* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ +static inline int cyclic( int a, int b, int c ) +{ + if( a<=c ) + { + return a<=b && blen; + sums_t* sums = pp->sums; + + double x, y, x2, xy, y2; + double k; + double a, b, c, lambda2, l; + int r = 0; /* rotations from i to j */ + + while( j>=n ) + { + j -= n; + r += 1; + } + + while( i>=n ) + { + i -= n; + r -= 1; + } + + while( j<0 ) + { + j += n; + r -= 1; + } + + while( i<0 ) + { + i += n; + r += 1; + } + + x = sums[j + 1].x - sums[i].x + r * sums[n].x; + y = sums[j + 1].y - sums[i].y + r * sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; + k = j + 1 - i + r * n; + + ctr->x = x / k; + ctr->y = y / k; + + a = (x2 - (double) x * x / k) / k; + b = (xy - (double) x * y / k) / k; + c = (y2 - (double) y * y / k) / k; + + lambda2 = ( a + c + sqrt( (a - c) * (a - c) + 4 * b * b ) ) / 2; /* larger e.value */ + + /* now find e.vector for lambda2 */ + a -= lambda2; + c -= lambda2; + + if( fabs( a ) >= fabs( c ) ) + { + l = sqrt( a * a + b * b ); + if( l!=0 ) + { + dir->x = -b / l; + dir->y = a / l; + } + } + else + { + l = sqrt( c * c + b * b ); + if( l!=0 ) + { + dir->x = -c / l; + dir->y = b / l; + } + } + if( l==0 ) + { + dir->x = dir->y = 0; /* sometimes this can happen when k=4: + * the two eigenvalues coincide */ + } +} + + +/* the type of (affine) quadratic forms, represented as symmetric 3x3 + * matrices. The value of the quadratic form at a vector (x,y) is v^t + * Q v, where v = (x,y,1)^t. */ +typedef double quadform_t[3][3]; + +/* Apply quadratic form Q to vector w = (w.x,w.y) */ +static inline double quadform( quadform_t Q, dpoint_t w ) +{ + double v[3]; + int i, j; + double sum; + + v[0] = w.x; + v[1] = w.y; + v[2] = 1; + sum = 0.0; + + for( i = 0; i<3; i++ ) + { + for( j = 0; j<3; j++ ) + { + sum += v[i] *Q[i][j] *v[j]; + } + } + + return sum; +} + + +/* calculate p1 x p2 */ +static inline int xprod( point_t p1, point_t p2 ) +{ + return p1.x * p2.y - p1.y * p2.x; +} + + +/* calculate (p1-p0)x(p3-p2) */ +static inline double cprod( dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3 ) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; + + return x1 * y2 - x2 * y1; +} + + +/* calculate (p1-p0)*(p2-p0) */ +static inline double iprod( dpoint_t p0, dpoint_t p1, dpoint_t p2 ) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; + + return x1 * x2 + y1 * y2; +} + + +/* calculate (p1-p0)*(p3-p2) */ +static inline double iprod1( dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3 ) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; + + return x1 * x2 + y1 * y2; +} + + +/* calculate distance between two points */ +static inline double ddist( dpoint_t p, dpoint_t q ) +{ + return sqrt( sq( p.x - q.x ) + sq( p.y - q.y ) ); +} + + +/* calculate point of a bezier curve */ +static inline dpoint_t bezier( double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3 ) +{ + double s = 1 - t; + dpoint_t res; + + /* Note: a good optimizing compiler (such as gcc-3) reduces the + * following to 16 multiplications, using common subexpression + * elimination. */ + + res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x; + res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y; + + return res; +} + + +/* calculate the point t in [0..1] on the (convex) bezier curve + * (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no + * solution in [0..1]. */ +static double tangent( dpoint_t p0, + dpoint_t p1, + dpoint_t p2, + dpoint_t p3, + dpoint_t q0, + dpoint_t q1 ) +{ + double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ + double a, b, c; /* a t^2 + b t + c = 0 */ + double d, s, r1, r2; + + A = cprod( p0, p1, q0, q1 ); + B = cprod( p1, p2, q0, q1 ); + C = cprod( p2, p3, q0, q1 ); + + a = A - 2 * B + C; + b = -2 * A + 2 * B; + c = A; + + d = b * b - 4 * a * c; + + if( a==0 || d<0 ) + { + return -1.0; + } + + s = sqrt( d ); + + r1 = (-b + s) / (2 * a); + r2 = (-b - s) / (2 * a); + + if( r1 >= 0 && r1 <= 1 ) + { + return r1; + } + else if( r2 >= 0 && r2 <= 1 ) + { + return r2; + } + else + { + return -1.0; + } +} + + +/* ---------------------------------------------------------------------- */ + +/* Preparation: fill in the sum* fields of a path (used for later + * rapid summing). Return 0 on success, 1 with errno set on + * failure. */ +static int calc_sums( privpath_t* pp ) +{ + int i, x, y; + int n = pp->len; + + SAFE_MALLOC( pp->sums, pp->len + 1, sums_t ); + + /* origin */ + pp->x0 = pp->pt[0].x; + pp->y0 = pp->pt[0].y; + + /* preparatory computation for later fast summing */ + pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; + for( i = 0; ipt[i].x - pp->x0; + y = pp->pt[i].y - pp->y0; + pp->sums[i + 1].x = pp->sums[i].x + x; + pp->sums[i + 1].y = pp->sums[i].y + y; + pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x; + pp->sums[i + 1].xy = pp->sums[i].xy + x * y; + pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y; + } + + return 0; + +malloc_error: + return 1; +} + + +/* ---------------------------------------------------------------------- */ + +/* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the + * "lon" component of a path object (based on pt/len). For each i, + * lon[i] is the furthest index such that a straight line can be drawn + * from i to lon[i]. Return 1 on error with errno set, else 0. */ + +/* this algorithm depends on the fact that the existence of straight + * subpaths is a triplewise property. I.e., there exists a straight + * line through squares i0,...,in iff there exists a straight line + * through i,j,k, for all i0<=i= 0 and xprod(constraint[1], + * cur) <= 0. */ + +/* Remark for Potrace 1.1: the current implementation of calc_lon is + * more complex than the implementation found in Potrace 1.0, but it + * is considerably faster. The introduction of the "nc" data structure + * means that we only have to test the constraints for "corner" + * points. On a typical input file, this speeds up the calc_lon + * function by a factor of 31.2, thereby decreasing its time share + * within the overall Potrace algorithm from 72.6% to 7.82%, and + * speeding up the overall algorithm by a factor of 3.36. On another + * input file, calc_lon was sped up by a factor of 6.7, decreasing its + * time share from 51.4% to 13.61%, and speeding up the overall + * algorithm by a factor of 1.78. In any case, the savings are + * substantial. */ + +/* returns 0 on success, 1 on error with errno set */ +static int calc_lon( privpath_t* pp ) +{ + point_t* pt = pp->pt; + int n = pp->len; + int i, j, k, k1; + int ct[4], dir; + point_t constraint[2]; + point_t cur; + point_t off; + int* pivk = NULL; /* pivk[n] */ + int* nc = NULL; /* nc[n]: next corner */ + point_t dk; /* direction of k-k1 */ + int a, b, c, d; + + SAFE_MALLOC( pivk, n, int ); + SAFE_MALLOC( nc, n, int ); + + /* initialize the nc data structure. Point from each point to the + * furthest future point to which it is connected by a vertical or + * horizontal segment. We take advantage of the fact that there is + * always a direction change at 0 (due to the path decomposition + * algorithm). But even if this were not so, there is no harm, as + * in practice, correctness does not depend on the word "furthest" + * above. */ + k = 0; + for( i = n - 1; i>=0; i-- ) + { + if( pt[i].x != pt[k].x && pt[i].y != pt[k].y ) + { + k = i + 1; /* necessarily ilon, n, int ); + + /* determine pivot points: for each i, let pivk[i] be the furthest k + * such that all j with i=0; i-- ) + { + ct[0] = ct[1] = ct[2] = ct[3] = 0; + + /* keep track of "directions" that have occurred */ + dir = + ( 3 + 3 * (pt[mod( i + 1, n )].x - pt[i].x) + (pt[mod( i + 1, n )].y - pt[i].y) ) / 2; + ct[dir]++; + + constraint[0].x = 0; + constraint[0].y = 0; + constraint[1].x = 0; + constraint[1].y = 0; + + /* find the next k such that no straight line from i to k */ + k = nc[i]; + k1 = i; + while( 1 ) + { + dir = ( 3 + 3 * sign( pt[k].x - pt[k1].x ) + sign( pt[k].y - pt[k1].y ) ) / 2; + ct[dir]++; + + /* if all four "directions" have occurred, cut this path */ + if( ct[0] && ct[1] && ct[2] && ct[3] ) + { + pivk[i] = k1; + goto foundk; + } + + cur.x = pt[k].x - pt[i].x; + cur.y = pt[k].y - pt[i].y; + + /* see if current constraint is violated */ + if( xprod( constraint[0], cur ) < 0 || xprod( constraint[1], cur ) > 0 ) + { + goto constraint_viol; + } + + /* else, update constraint */ + if( abs( cur.x ) <= 1 && abs( cur.y ) <= 1 ) + { + /* no constraint */ + } + else + { + off.x = cur.x + ( ( cur.y>=0 && (cur.y>0 || cur.x<0) ) ? 1 : -1 ); + off.y = cur.y + ( ( cur.x<=0 && (cur.x<0 || cur.y<0) ) ? 1 : -1 ); + if( xprod( constraint[0], off ) >= 0 ) + { + constraint[0] = off; + } + off.x = cur.x + ( ( cur.y<=0 && (cur.y<0 || cur.x<0) ) ? 1 : -1 ); + off.y = cur.y + ( ( cur.x>=0 && (cur.x>0 || cur.y<0) ) ? 1 : -1 ); + if( xprod( constraint[1], off ) <= 0 ) + { + constraint[1] = off; + } + } + k1 = k; + k = nc[k1]; + if( !cyclic( k, i, k1 ) ) + { + break; + } + } + +constraint_viol: + + /* k1 was the last "corner" satisfying the current constraint, and + * k is the first one violating it. We now need to find the last + * point along k1..k which satisfied the constraint. */ + dk.x = sign( pt[k].x - pt[k1].x ); + dk.y = sign( pt[k].y - pt[k1].y ); + cur.x = pt[k1].x - pt[i].x; + cur.y = pt[k1].y - pt[i].y; + + /* find largest integer j such that xprod(constraint[0], cur+j*dk) + * >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity + * of xprod. */ + a = xprod( constraint[0], cur ); + b = xprod( constraint[0], dk ); + c = xprod( constraint[1], cur ); + d = xprod( constraint[1], dk ); + + /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This + * can be solved with integer arithmetic. */ + j = INFTY; + if( b<0 ) + { + j = floordiv( a, -b ); + } + if( d>0 ) + { + j = min( j, floordiv( -c, d ) ); + } + pivk[i] = mod( k1 + j, n ); +foundk: + ; + } /* for i */ + + /* clean up: for each i, let lon[i] be the largest k such that for + * all i' with i<=i'lon[n - 1] = j; + for( i = n - 2; i>=0; i-- ) + { + if( cyclic( i + 1, pivk[i], j ) ) + { + j = pivk[i]; + } + pp->lon[i] = j; + } + + for( i = n - 1; cyclic( mod( i + 1, n ), j, pp->lon[i] ); i-- ) + { + pp->lon[i] = j; + } + + free( pivk ); + free( nc ); + return 0; + +malloc_error: + free( pivk ); + free( nc ); + return 1; +} + + +/* ---------------------------------------------------------------------- */ +/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ + +/* Auxiliary function: calculate the penalty of an edge from i to j in + * the given path. This needs the "lon" and "sum*" data. */ + +static double penalty3( privpath_t* pp, int i, int j ) +{ + int n = pp->len; + point_t* pt = pp->pt; + sums_t* sums = pp->sums; + + /* assume 0<=i=n ) + { + j -= n; + r += 1; + } + + x = sums[j + 1].x - sums[i].x + r * sums[n].x; + y = sums[j + 1].y - sums[i].y + r * sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; + k = j + 1 - i + r * n; + + px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; + py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; + ey = (pt[j].x - pt[i].x); + ex = -(pt[j].y - pt[i].y); + + a = ( (x2 - 2 * x * px) / k + px * px ); + b = ( (xy - x * py - y * px) / k + px * py ); + c = ( (y2 - 2 * y * py) / k + py * py ); + + s = ex * ex * a + 2 * ex * ey * b + ey * ey * c; + + return sqrt( s ); +} + + +/* find the optimal polygon. Fill in the m and po components. Return 1 + * on failure with errno set, else 0. Non-cyclic version: assumes i=0 + * is in the polygon. Fixme: ### implement cyclic version. */ +static int bestpolygon( privpath_t* pp ) +{ + int i, j, m, k; + int n = pp->len; + double* pen = NULL; /* pen[n+1]: penalty vector */ + int* prev = NULL; /* prev[n+1]: best path pointer vector */ + int* clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ + int* clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ + int* seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ + int* seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ + double thispen; + double best; + int c; + + SAFE_MALLOC( pen, n + 1, double ); + SAFE_MALLOC( prev, n + 1, int ); + SAFE_MALLOC( clip0, n, int ); + SAFE_MALLOC( clip1, n + 1, int ); + SAFE_MALLOC( seg0, n + 1, int ); + SAFE_MALLOC( seg1, n + 1, int ); + + /* calculate clipped paths */ + for( i = 0; ilon[mod( i - 1, n )] - 1, n ); + if( c == i ) + { + c = mod( i + 1, n ); + } + if( c < i ) + { + clip0[i] = n; + } + else + { + clip0[i] = c; + } + } + + /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff + * clip1[j] <= i, for i,j=0..n. */ + j = 1; + for( i = 0; i0; j-- ) + { + seg1[j] = i; + i = clip1[i]; + } + + seg1[0] = 0; + + /* now find the shortest path with m segments, based on penalty3 */ + + /* note: the outer 2 loops jointly have at most n interations, thus + * the worst-case behavior here is quadratic. In practice, it is + * close to linear since the inner loop tends to be short. */ + pen[0] = 0; + for( j = 1; j<=m; j++ ) + { + for( i = seg1[j]; i<=seg0[j]; i++ ) + { + best = -1; + for( k = seg0[j - 1]; k>=clip1[i]; k-- ) + { + thispen = penalty3( pp, k, i ) + pen[k]; + if( best < 0 || thispen < best ) + { + prev[i] = k; + best = thispen; + } + } + + pen[i] = best; + } + } + + pp->m = m; + SAFE_MALLOC( pp->po, m, int ); + + /* read off shortest path */ + for( i = n, j = m - 1; i>0; j-- ) + { + i = prev[i]; + pp->po[j] = i; + } + + free( pen ); + free( prev ); + free( clip0 ); + free( clip1 ); + free( seg0 ); + free( seg1 ); + return 0; + +malloc_error: + free( pen ); + free( prev ); + free( clip0 ); + free( clip1 ); + free( seg0 ); + free( seg1 ); + return 1; +} + + +/* ---------------------------------------------------------------------- */ +/* Stage 3: vertex adjustment (Sec. 2.3.1). */ + +/* Adjust vertices of optimal polygon: calculate the intersection of + * the two "optimal" line segments, then move it into the unit square + * if it lies outside. Return 1 with errno set on error; 0 on + * success. */ + +static int adjust_vertices( privpath_t* pp ) +{ + int m = pp->m; + int* po = pp->po; + int n = pp->len; + point_t* pt = pp->pt; + int x0 = pp->x0; + int y0 = pp->y0; + + dpoint_t* ctr = NULL; /* ctr[m] */ + dpoint_t* dir = NULL; /* dir[m] */ + quadform_t* q = NULL; /* q[m] */ + double v[3]; + double d; + int i, j, k, l; + dpoint_t s; + int r; + + SAFE_MALLOC( ctr, m, dpoint_t ); + SAFE_MALLOC( dir, m, dpoint_t ); + SAFE_MALLOC( q, m, quadform_t ); + + r = privcurve_init( &pp->curve, m ); + if( r ) + { + goto malloc_error; + } + + /* calculate "optimal" point-slope representation for each line + * segment */ + for( i = 0; iQ[1][1] ) + { + v[0] = -Q[0][1]; + v[1] = Q[0][0]; + } + else if( Q[1][1] ) + { + v[0] = -Q[1][1]; + v[1] = Q[1][0]; + } + else + { + v[0] = 1; + v[1] = 0; + } + d = sq( v[0] ) + sq( v[1] ); + v[2] = -v[1] *s.y - v[0] *s.x; + for( l = 0; l<3; l++ ) + { + for( k = 0; k<3; k++ ) + { + Q[l][k] += v[l] *v[k] / d; + } + } + } + + dx = fabs( w.x - s.x ); + dy = fabs( w.y - s.y ); + if( dx <= .5 && dy <= .5 ) + { + pp->curve.vertex[i].x = w.x + x0; + pp->curve.vertex[i].y = w.y + y0; + continue; + } + + /* the minimum was not in the unit square; now minimize quadratic + * on boundary of square */ + min = quadform( Q, s ); + xmin = s.x; + ymin = s.y; + + if( Q[0][0] == 0.0 ) + { + goto fixx; + } + for( z = 0; z<2; z++ ) /* value of the y-coordinate */ + { + w.y = s.y - 0.5 + z; + w.x = -(Q[0][1] *w.y + Q[0][2]) / Q[0][0]; + dx = fabs( w.x - s.x ); + cand = quadform( Q, w ); + if( dx <= .5 && cand < min ) + { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + +fixx: + if( Q[1][1] == 0.0 ) + { + goto corners; + } + for( z = 0; z<2; z++ ) /* value of the x-coordinate */ + { + w.x = s.x - 0.5 + z; + w.y = -(Q[1][0] *w.x + Q[1][2]) / Q[1][1]; + dy = fabs( w.y - s.y ); + cand = quadform( Q, w ); + if( dy <= .5 && cand < min ) + { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + +corners: + /* check four corners */ + for( l = 0; l<2; l++ ) + { + for( k = 0; k<2; k++ ) + { + w.x = s.x - 0.5 + l; + w.y = s.y - 0.5 + k; + cand = quadform( Q, w ); + if( cand < min ) + { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + } + + pp->curve.vertex[i].x = xmin + x0; + pp->curve.vertex[i].y = ymin + y0; + continue; + } + + free( ctr ); + free( dir ); + free( q ); + return 0; + +malloc_error: + free( ctr ); + free( dir ); + free( q ); + return 1; +} + + +/* ---------------------------------------------------------------------- */ +/* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ + +/* Always succeeds and returns 0 */ +static int smooth( privcurve_t* curve, int sign, double alphamax ) +{ + int m = curve->n; + + int i, j, k; + double dd, denom, alpha; + dpoint_t p2, p3, p4; + + if( sign == '-' ) + { + /* reverse orientation of negative paths */ + for( i = 0, j = m - 1; ivertex[i]; + curve->vertex[i] = curve->vertex[j]; + curve->vertex[j] = tmp; + } + } + + /* examine each vertex and find its best fit */ + for( i = 0; ivertex[k], curve->vertex[j] ); + + denom = ddenom( curve->vertex[i], curve->vertex[k] ); + if( denom != 0.0 ) + { + dd = dpara( curve->vertex[i], curve->vertex[j], curve->vertex[k] ) / denom; + dd = fabs( dd ); + alpha = dd>1 ? (1 - 1.0 / dd) : 0; + alpha = alpha / 0.75; + } + else + { + alpha = 4 / 3.0; + } + curve->alpha0[j] = alpha; /* remember "original" value of alpha */ + + if( alpha > alphamax ) /* pointed corner */ + { + curve->tag[j] = POTRACE_CORNER; + curve->c[j][1] = curve->vertex[j]; + curve->c[j][2] = p4; + } + else + { + if( alpha < 0.55 ) + { + alpha = 0.55; + } + else if( alpha > 1 ) + { + alpha = 1; + } + p2 = interval( .5 + .5 * alpha, curve->vertex[i], curve->vertex[j] ); + p3 = interval( .5 + .5 * alpha, curve->vertex[k], curve->vertex[j] ); + curve->tag[j] = POTRACE_CURVETO; + curve->c[j][0] = p2; + curve->c[j][1] = p3; + curve->c[j][2] = p4; + } + curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ + curve->beta[j] = 0.5; + } + + curve->alphacurve = 1; + + return 0; +} + + +/* ---------------------------------------------------------------------- */ +/* Stage 5: Curve optimization (Sec. 2.4) */ + +/* a private type for the result of opti_penalty */ +struct opti_s +{ + double pen; /* penalty */ + dpoint_t c[2]; /* curve parameters */ + double t, s; /* curve parameters */ + double alpha; /* curve parameter */ +}; +typedef struct opti_s opti_t; + +/* calculate best fit from i+.5 to j+.5. Assume icurve.n; + int k, k1, k2, conv, i1; + double area, alpha, d, d1, d2; + dpoint_t p0, p1, p2, p3, pt; + double A, R, A1, A2, A3, A4; + double s, t; + + /* check convexity, corner-freeness, and maximum bend < 179 degrees */ + + if( i==j ) /* sanity - a full loop can never be an opticurve */ + { + return 1; + } + + k = i; + i1 = mod( i + 1, m ); + k1 = mod( k + 1, m ); + conv = convc[k1]; + if( conv == 0 ) + { + return 1; + } + d = ddist( pp->curve.vertex[i], pp->curve.vertex[i1] ); + for( k = k1; k!=j; k = k1 ) + { + k1 = mod( k + 1, m ); + k2 = mod( k + 2, m ); + if( convc[k1] != conv ) + { + return 1; + } + if( sign( cprod( pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], + pp->curve.vertex[k2] ) ) != conv ) + { + return 1; + } + if( iprod1( pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], + pp->curve.vertex[k2] ) < d * + ddist( pp->curve.vertex[k1], pp->curve.vertex[k2] ) * COS179 ) + { + return 1; + } + } + + /* the curve we're working in: */ + p0 = pp->curve.c[mod( i, m )][2]; + p1 = pp->curve.vertex[mod( i + 1, m )]; + p2 = pp->curve.vertex[mod( j, m )]; + p3 = pp->curve.c[mod( j, m )][2]; + + /* determine its area */ + area = areac[j] - areac[i]; + area -= dpara( pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2] ) / 2; + if( i>=j ) + { + area += areac[m]; + } + + /* find intersection o of p0p1 and p2p3. Let t,s such that o = + * interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the + * triangle (p0,o,p3). */ + + A1 = dpara( p0, p1, p2 ); + A2 = dpara( p0, p1, p3 ); + A3 = dpara( p0, p2, p3 ); + /* A4 = dpara(p1, p2, p3); */ + A4 = A1 + A3 - A2; + + if( A2 == A1 ) /* this should never happen */ + { + return 1; + } + + t = A3 / (A3 - A4); + s = A2 / (A2 - A1); + A = A2 * t / 2.0; + + if( A == 0.0 ) /* this should never happen */ + { + return 1; + } + + R = area / A; /* relative area */ + alpha = 2 - sqrt( 4 - R / 0.3 ); /* overall alpha for p0-o-p3 curve */ + + res->c[0] = interval( t * alpha, p0, p1 ); + res->c[1] = interval( s * alpha, p3, p2 ); + res->alpha = alpha; + res->t = t; + res->s = s; + + p1 = res->c[0]; + p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ + + res->pen = 0; + + /* calculate penalty */ + /* check tangency with edges */ + for( k = mod( i + 1, m ); k!=j; k = k1 ) + { + k1 = mod( k + 1, m ); + t = tangent( p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1] ); + if( t<-.5 ) + { + return 1; + } + pt = bezier( t, p0, p1, p2, p3 ); + d = ddist( pp->curve.vertex[k], pp->curve.vertex[k1] ); + if( d == 0.0 ) /* this should never happen */ + { + return 1; + } + d1 = dpara( pp->curve.vertex[k], pp->curve.vertex[k1], pt ) / d; + if( fabs( d1 ) > opttolerance ) + { + return 1; + } + if( iprod( pp->curve.vertex[k], pp->curve.vertex[k1], + pt ) < 0 || iprod( pp->curve.vertex[k1], pp->curve.vertex[k], pt ) < 0 ) + { + return 1; + } + res->pen += sq( d1 ); + } + + /* check corners */ + for( k = i; k!=j; k = k1 ) + { + k1 = mod( k + 1, m ); + t = tangent( p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2] ); + if( t<-.5 ) + { + return 1; + } + pt = bezier( t, p0, p1, p2, p3 ); + d = ddist( pp->curve.c[k][2], pp->curve.c[k1][2] ); + if( d == 0.0 ) /* this should never happen */ + { + return 1; + } + d1 = dpara( pp->curve.c[k][2], pp->curve.c[k1][2], pt ) / d; + d2 = dpara( pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1] ) / d; + d2 *= 0.75 * pp->curve.alpha[k1]; + if( d2 < 0 ) + { + d1 = -d1; + d2 = -d2; + } + if( d1 < d2 - opttolerance ) + { + return 1; + } + if( d1 < d2 ) + { + res->pen += sq( d1 - d2 ); + } + } + + return 0; +} + + +/* optimize the path p, replacing sequences of Bezier segments by a + * single segment when possible. Return 0 on success, 1 with errno set + * on failure. */ +static int opticurve( privpath_t* pp, double opttolerance ) +{ + int m = pp->curve.n; + int* pt = NULL; /* pt[m+1] */ + double* pen = NULL; /* pen[m+1] */ + int* len = NULL; /* len[m+1] */ + opti_t* opt = NULL; /* opt[m+1] */ + int om; + int i, j, r; + opti_t o; + dpoint_t p0; + int i1; + double area; + double alpha; + double* s = NULL; + double* t = NULL; + + int* convc = NULL; /* conv[m]: pre-computed convexities */ + double* areac = NULL; /* cumarea[m+1]: cache for fast area computation */ + + SAFE_MALLOC( pt, m + 1, int ); + SAFE_MALLOC( pen, m + 1, double ); + SAFE_MALLOC( len, m + 1, int ); + SAFE_MALLOC( opt, m + 1, opti_t ); + SAFE_MALLOC( convc, m, int ); + SAFE_MALLOC( areac, m + 1, double ); + + /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ + for( i = 0; icurve.tag[i] == POTRACE_CURVETO ) + { + convc[i] = + sign( dpara( pp->curve.vertex[mod( i - 1, + m )], pp->curve.vertex[i], + pp->curve.vertex[mod( i + 1, m )] ) ); + } + else + { + convc[i] = 0; + } + } + + /* pre-calculate areas */ + area = 0.0; + areac[0] = 0.0; + p0 = pp->curve.vertex[0]; + for( i = 0; icurve.tag[i1] == POTRACE_CURVETO ) + { + alpha = pp->curve.alpha[i1]; + area += 0.3 * alpha * (4 - alpha) * dpara( pp->curve.c[i][2], + pp->curve.vertex[i1], + pp->curve.c[i1][2] ) / 2; + area += dpara( p0, pp->curve.c[i][2], pp->curve.c[i1][2] ) / 2; + } + areac[i + 1] = area; + } + + pt[0] = -1; + pen[0] = 0; + len[0] = 0; + + /* Fixme: we always start from a fixed point -- should find the best + * curve cyclically ### */ + + for( j = 1; j<=m; j++ ) + { + /* calculate best path from 0 to j */ + pt[j] = j - 1; + pen[j] = pen[j - 1]; + len[j] = len[j - 1] + 1; + + for( i = j - 2; i>=0; i-- ) + { + r = opti_penalty( pp, i, mod( j, m ), &o, opttolerance, convc, areac ); + if( r ) + { + break; + } + if( len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen) ) + { + pt[j] = i; + pen[j] = pen[i] + o.pen; + len[j] = len[i] + 1; + opt[j] = o; + } + } + } + + om = len[m]; + r = privcurve_init( &pp->ocurve, om ); + if( r ) + { + goto malloc_error; + } + SAFE_MALLOC( s, om, double ); + SAFE_MALLOC( t, om, double ); + + j = m; + for( i = om - 1; i>=0; i-- ) + { + if( pt[j]==j - 1 ) + { + pp->ocurve.tag[i] = pp->curve.tag[mod( j, m )]; + pp->ocurve.c[i][0] = pp->curve.c[mod( j, m )][0]; + pp->ocurve.c[i][1] = pp->curve.c[mod( j, m )][1]; + pp->ocurve.c[i][2] = pp->curve.c[mod( j, m )][2]; + pp->ocurve.vertex[i] = pp->curve.vertex[mod( j, m )]; + pp->ocurve.alpha[i] = pp->curve.alpha[mod( j, m )]; + pp->ocurve.alpha0[i] = pp->curve.alpha0[mod( j, m )]; + pp->ocurve.beta[i] = pp->curve.beta[mod( j, m )]; + s[i] = t[i] = 1.0; + } + else + { + pp->ocurve.tag[i] = POTRACE_CURVETO; + pp->ocurve.c[i][0] = opt[j].c[0]; + pp->ocurve.c[i][1] = opt[j].c[1]; + pp->ocurve.c[i][2] = pp->curve.c[mod( j, m )][2]; + pp->ocurve.vertex[i] = interval( opt[j].s, pp->curve.c[mod( j, + m )][2], + pp->curve.vertex[mod( j, m )] ); + pp->ocurve.alpha[i] = opt[j].alpha; + pp->ocurve.alpha0[i] = opt[j].alpha; + s[i] = opt[j].s; + t[i] = opt[j].t; + } + j = pt[j]; + } + + /* calculate beta parameters */ + for( i = 0; iocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + + pp->ocurve.alphacurve = 1; + + free( pt ); + free( pen ); + free( len ); + free( opt ); + free( s ); + free( t ); + free( convc ); + free( areac ); + return 0; + +malloc_error: + free( pt ); + free( pen ); + free( len ); + free( opt ); + free( s ); + free( t ); + free( convc ); + free( areac ); + return 1; +} + + +/* ---------------------------------------------------------------------- */ + +#define TRY( x ) if( x ) \ + goto try_error + +/* return 0 on success, 1 on error with errno set. */ +int process_path( path_t* plist, const potrace_param_t* param, progress_t* progress ) +{ + path_t* p; + double nn = 0, cn = 0; + + if( progress->callback ) + { + /* precompute task size for progress estimates */ + nn = 0; + list_forall( p, plist ) { + nn += p->priv->len; + } + cn = 0; + } + + /* call downstream function with each path */ + list_forall( p, plist ) { + TRY( calc_sums( p->priv ) ); + TRY( calc_lon( p->priv ) ); + TRY( bestpolygon( p->priv ) ); + TRY( adjust_vertices( p->priv ) ); + TRY( smooth( &p->priv->curve, p->sign, param->alphamax ) ); + if( param->opticurve ) + { + TRY( opticurve( p->priv, param->opttolerance ) ); + p->priv->fcurve = &p->priv->ocurve; + } + else + { + p->priv->fcurve = &p->priv->curve; + } + privcurve_to_curve( p->priv->fcurve, &p->curve ); + + if( progress->callback ) + { + cn += p->priv->len; + progress_update( cn / nn, progress ); + } + } + + progress_update( 1.0, progress ); + + return 0; + +try_error: + return 1; +} diff --git a/potrace/trace.h b/potrace/trace.h new file mode 100644 index 0000000000..7db3afc66c --- /dev/null +++ b/potrace/trace.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + * This file is part of Potrace. It is free software and it is covered + * by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: trace.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef TRACE_H +#define TRACE_H + +#include "potracelib.h" +#include "progress.h" + +int process_path( path_t* plist, const potrace_param_t* param, progress_t* progress ); + +#endif /* TRACE_H */