Added an experimental tool to create logos from .bmp bitmaps. Added Potrace library to convert bitmaps to polygons

This commit is contained in:
jean-pierre charras 2010-06-10 20:43:12 +02:00
parent 82aadca861
commit ac11d86c2f
32 changed files with 6387 additions and 13 deletions

View File

@ -187,6 +187,8 @@ add_subdirectory(kicad)
add_subdirectory(pcbnew) add_subdirectory(pcbnew)
add_subdirectory(polygon) add_subdirectory(polygon)
add_subdirectory(polygon/kbool/src) add_subdirectory(polygon/kbool/src)
add_subdirectory(potrace)
add_subdirectory(bitmap2component)
############# #############
# Resources # # Resources #

View File

@ -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)

View File

@ -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 <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <math.h>
#include <vector>
#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 <potrace_dpoint_t>& 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 <infile_bitmap.ext> <outfile.ext> <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 <potrace_dpoint_t>& 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 <potrace_dpoint_t> 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 <potrace_dpoint_t> 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 <potrace_dpoint_t>& 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 );
}

View File

@ -676,7 +676,7 @@ bool LIB_COMPONENT::Load( FILE* aFile, char* aLine, int* aLineNum,
{ {
aErrorMsg.Printf( wxT( "Wrong DEF format in line %d, skipped." ), aErrorMsg.Printf( wxT( "Wrong DEF format in line %d, skipped." ),
*aLineNum ); *aLineNum );
while( GetLine( aFile, aLine, aLineNum, 1024 ) ) while( GetLine( aFile, aLine, aLineNum, LINE_BUFFER_LEN_LARGE ) )
{ {
p = strtok( aLine, " \t\n" ); p = strtok( aLine, " \t\n" );
if( stricmp( p, "ENDDEF" ) == 0 ) if( stricmp( p, "ENDDEF" ) == 0 )
@ -726,7 +726,7 @@ bool LIB_COMPONENT::Load( FILE* aFile, char* aLine, int* aLineNum,
m_options = ENTRY_POWER; m_options = ENTRY_POWER;
/* Read next lines */ /* Read next lines */
while( GetLine( aFile, aLine, aLineNum, 1024 ) ) while( GetLine( aFile, aLine, aLineNum, LINE_BUFFER_LEN_LARGE ) )
{ {
p = strtok( aLine, " \t\n" ); p = strtok( aLine, " \t\n" );
@ -775,7 +775,7 @@ bool LIB_COMPONENT::LoadDrawEntries( FILE* aFile, char* aLine,
while( true ) 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" ); aErrorMsg = wxT( "file ended prematurely loading component draw element" );
return false; return false;
@ -830,7 +830,7 @@ bool LIB_COMPONENT::LoadDrawEntries( FILE* aFile, char* aLine,
/* Flush till end of draw section */ /* Flush till end of draw section */
do 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 \ aErrorMsg = wxT( "file ended prematurely while attempting \
to flush to end of drawing section." ); to flush to end of drawing section." );
@ -899,7 +899,7 @@ bool LIB_COMPONENT::LoadFootprints( FILE* aFile, char* aLine,
{ {
while( true ) 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" ); aErrorMsg = wxT( "file ended prematurely while loading footprints" );
return false; return false;

View File

@ -551,7 +551,7 @@ bool CMP_LIBRARY::Load( wxString& aErrorMsg )
{ {
FILE* file; FILE* file;
int lineNumber = 0; int lineNumber = 0;
char line[1024]; char line[LINE_BUFFER_LEN_LARGE]; // Use a very large buffer to load data
LIB_COMPONENT* libEntry; LIB_COMPONENT* libEntry;
wxString msg; wxString msg;
@ -716,7 +716,7 @@ void CMP_LIBRARY::LoadAliases( LIB_COMPONENT* component )
bool CMP_LIBRARY::LoadHeader( FILE* libfile, int* LineNum ) 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) ) ) 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 ) bool CMP_LIBRARY::LoadDocs( wxString& aErrorMsg )
{ {
int lineNumber = 0; int lineNumber = 0;
char line[1024], * name, * text; char line[LINE_BUFFER_LEN_LARGE], * name, * text;
CMP_LIB_ENTRY* entry; CMP_LIB_ENTRY* entry;
FILE* file; FILE* file;
wxString msg; wxString msg;

View File

@ -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, i = sscanf( &aLine[2], "%d %d %d %d", &ccount, &m_Unit, &m_Convert,
&m_Width ); &m_Width );
m_Fill = NO_FILL;
if( i < 4 ) if( i < 4 )
{ {
aErrorMsg.Printf( _( "polyline only had %d parameters of the required 4" ), i ); 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; wxPoint point;
p = strtok( NULL, " \t\n" ); 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 ); aErrorMsg.Printf( _( "polyline point %d X position not defined" ), i );
return false; return false;
} }
p = strtok( NULL, " \t\n" ); 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 ); aErrorMsg.Printf( _( "polyline point %d Y position not defined" ), i );
return false; return false;
@ -1403,8 +1405,6 @@ bool LIB_POLYLINE::Load( char* aLine, wxString& aErrorMsg )
AddPoint( pt ); AddPoint( pt );
} }
m_Fill = NO_FILL;
if( ( p = strtok( NULL, " \t\n" ) ) != NULL ) if( ( p = strtok( NULL, " \t\n" ) ) != NULL )
{ {
if( p[0] == 'F' ) if( p[0] == 'F' )

View File

@ -12,6 +12,10 @@
#define EESCHEMA_FILE_STAMP "EESchema" #define EESCHEMA_FILE_STAMP "EESchema"
#define NULL_STRING "_NONAME_" #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 MAX_PIN_INFO 10
#define TXTMARGE 10 /* Offset in mils for placement of labels #define TXTMARGE 10 /* Offset in mils for placement of labels

View File

@ -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*

View File

@ -487,7 +487,7 @@ void Plot_1_EdgeModule( PLOTTER* plotter, EDGE_MODULE* PtEdge,
*ptr++ = y; *ptr++ = y;
} }
plotter->poly( PtEdge->m_PolyPoints.size(), ptr_base, NO_FILL, plotter->poly( PtEdge->m_PolyPoints.size(), ptr_base, FILLED_SHAPE,
thickness ); thickness );
free( ptr_base ); free( ptr_base );
} }

5
potrace/AUTHORS Normal file
View File

@ -0,0 +1,5 @@
Known contributors are listed here, in alphabetical order by their
abbreviations (which are used in Changelog).
PS1 Peter Selinger <selinger at users.sourceforge.net> (author)
TA1 Tor Andersson <tor at ghostscript.com>

14
potrace/CMakeLists.txt Normal file
View File

@ -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})

89
potrace/auxiliary.h Normal file
View File

@ -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 */

118
potrace/bitmap.h Normal file
View File

@ -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 <string.h>
#include <stdlib.h>
/* 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 */

969
potrace/bitmap_io.cpp Normal file
View File

@ -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 <stdio.h>
#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<w; x++ )
{
b = readbit( f );
if( b<0 )
{
goto eof;
}
BM_UPUT( bm, x, y, b );
}
}
break;
case '2':
/* read P2 format: PGM ascii */
max = readnum( f );
if( max<1 )
{
goto format_error;
}
for( y = h - 1; y>=0; y-- )
{
for( x = 0; x<w; x++ )
{
b = readnum( f );
if( b<0 )
{
goto eof;
}
BM_UPUT( bm, x, y, b > 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<w; x++ )
{
sum = 0;
for( i = 0; i<3; i++ )
{
b = readnum( f );
if( b<0 )
{
goto eof;
}
sum += b;
}
BM_UPUT( bm, x, y, sum > 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<bpr; i++ )
{
b = fgetc( f );
if( b==EOF )
{
goto eof;
}
*bm_index( bm, i * 8,
y ) |= ( (potrace_word) b ) <<
( 8 * ( BM_WORDSIZE - 1 - (i % BM_WORDSIZE) ) );
}
}
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<w; x++ )
{
b = fgetc( f );
if( b==EOF )
goto eof;
if( max>=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<w; x++ )
{
sum = 0;
for( i = 0; i<3; i++ )
{
b = fgetc( f );
if( b==EOF )
{
goto eof;
}
if( max>=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<n; i++ )
{
b = fgetc( f );
if( b==EOF )
{
return 1;
}
sum += b << (8 * i);
}
bmp_count += n;
bmp_pos += n;
*p = sum;
return 0;
}
/* reset padding boundary */
static void bmp_pad_reset( void )
{
bmp_count = 0;
}
/* read padding bytes to 4-byte boundary. Return 1 on EOF or error,
* else 0. */
static int bmp_pad( FILE* f )
{
int c, i, b;
c = (-bmp_count) & 3;
for( i = 0; i<c; i++ )
{
b = fgetc( f );
if( b==EOF )
{
return 1;
}
}
bmp_pos += c;
bmp_count = 0;
return 0;
}
/* forward to the new file position. Return 1 on EOF or error, else 0 */
static int bmp_forward( FILE* f, int pos )
{
int b;
while( bmp_pos < pos )
{
b = fgetc( f );
if( b==EOF )
{
return 1;
}
bmp_pos++;
bmp_count++;
}
return 0;
}
#define TRY( x ) if( x ) \
goto try_error
#define TRY_EOF( x ) if( x ) \
goto eof
/* read BMP stream after magic number. Return values as for bm_read.
* We choose to be as permissive as possible, since there are many
* programs out there which produce BMP. For instance, ppmtobmp can
* produce codings with anywhere from 1-8 or 24 bits per sample,
* although most specifications only allow 1,4,8,24,32. We can also
* read both the old and new OS/2 BMP formats in addition to the
* Windows BMP format. */
static int bm_readbody_bmp( FILE* f, double threshold, potrace_bitmap_t** bmp )
{
bmp_info_t bmpinfo;
int* coltable;
unsigned int b, c;
unsigned int i;
potrace_bitmap_t* bm;
int mask;
unsigned int x, y;
int col[2];
unsigned int bitbuf;
unsigned int n;
int col1[2];
bm_read_error = NULL;
bm = NULL;
coltable = NULL;
bmp_pos = 2; /* set file position */
/* file header (minus magic number) */
TRY( bmp_readint( f, 4, &bmpinfo.FileSize ) );
TRY( bmp_readint( f, 4, &bmpinfo.reserved ) );
TRY( bmp_readint( f, 4, &bmpinfo.DataOffset ) );
/* info header */
TRY( bmp_readint( f, 4, &bmpinfo.InfoSize ) );
if( bmpinfo.InfoSize == 40 || bmpinfo.InfoSize == 64 )
{
/* Windows or new OS/2 format */
bmpinfo.ctbits = 32; /* sample size in color table */
TRY( bmp_readint( f, 4, &bmpinfo.w ) );
TRY( bmp_readint( f, 4, &bmpinfo.h ) );
TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
TRY( bmp_readint( f, 4, &bmpinfo.comp ) );
TRY( bmp_readint( f, 4, &bmpinfo.ImageSize ) );
TRY( bmp_readint( f, 4, &bmpinfo.XpixelsPerM ) );
TRY( bmp_readint( f, 4, &bmpinfo.YpixelsPerM ) );
TRY( bmp_readint( f, 4, &bmpinfo.ncolors ) );
TRY( bmp_readint( f, 4, &bmpinfo.ColorsImportant ) );
}
else if( bmpinfo.InfoSize == 12 )
{
/* old OS/2 format */
bmpinfo.ctbits = 24; /* sample size in color table */
TRY( bmp_readint( f, 2, &bmpinfo.w ) );
TRY( bmp_readint( f, 2, &bmpinfo.h ) );
TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
bmpinfo.comp = 0;
bmpinfo.ncolors = 0;
}
else
{
goto format_error;
}
/* forward to color table (i.e., if bmpinfo.InfoSize == 64) */
TRY( bmp_forward( f, 14 + bmpinfo.InfoSize ) );
if( bmpinfo.Planes != 1 )
{
bm_read_error = "cannot handle bmp planes";
goto format_error; /* can't handle planes */
}
if( bmpinfo.ncolors == 0 )
{
bmpinfo.ncolors = 1 << bmpinfo.bits;
}
/* color table, present only if bmpinfo.bits <= 8. */
if( bmpinfo.bits <= 8 )
{
coltable = (int*) malloc( bmpinfo.ncolors * sizeof(int) );
if( !coltable )
{
goto std_error;
}
/* NOTE: since we are reading a bitmap, we can immediately convert
* the color table entries to bits. */
for( i = 0; i<bmpinfo.ncolors; i++ )
{
TRY( bmp_readint( f, bmpinfo.ctbits / 8, &c ) );
c = ( (c >> 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<bmpinfo.h; y++ )
{
bmp_pad_reset();
for( i = 0; 8 * i<bmpinfo.w; i++ )
{
TRY_EOF( bmp_readint( f, 1, &b ) );
b ^= mask;
*bm_index( bm, i * 8,
y ) |= ( (potrace_word) b ) <<
( 8 * ( BM_WORDSIZE - 1 - (i % BM_WORDSIZE) ) );
}
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<bmpinfo.h; y++ )
{
bmp_pad_reset();
bitbuf = 0; /* bit buffer: bits in buffer are high-aligned */
n = 0; /* number of bits currently in bitbuffer */
for( x = 0; x<bmpinfo.w; x++ )
{
if( n < bmpinfo.bits )
{
TRY_EOF( bmp_readint( f, 1, &b ) );
bitbuf |= b << (INTBITS - 8 - n);
n += 8;
}
b = bitbuf >> (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<bmpinfo.h; y++ )
{
bmp_pad_reset();
for( x = 0; x<bmpinfo.w; x++ )
{
TRY_EOF( bmp_readint( f, bmpinfo.bits / 8, &c ) );
c = ( (c >> 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<b && x<bmpinfo.w; i++ )
{
if( x>=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<c; i++ )
{
if( (i & 1)==0 )
{
TRY_EOF( bmp_readint( f, 1, &b ) );
}
if( x>=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<b; i++ )
{
if( x>=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<c; i++ )
{
TRY_EOF( bmp_readint( f, 1, &b ) );
if( x>=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<bpr; i++ )
{
c =
( *bm_index( bm, i * 8,
y ) >> ( 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; xx<sw; xx++ )
{
d = 0;
for( x = xx * bm->w / 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;
}

23
potrace/bitmap_io.h Normal file
View File

@ -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 <stdio.h>
#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 */

50
potrace/bitops.h Normal file
View File

@ -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 */

122
potrace/curve.cpp Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

80
potrace/curve.h Normal file
View File

@ -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 */

600
potrace/decompose.cpp Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#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; y<bm->h; 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; y<bbox->y1; y++ )
{
for( i = imin; i<imax; i++ )
{
bm_scanline( bm, y )[i] = 0;
}
}
}
/* ---------------------------------------------------------------------- */
/* auxiliary functions */
/* deterministically and efficiently hash (x,y) into a pseudo-random bit */
static inline int detrand( int x, int y )
{
unsigned int z;
static const unsigned char t[256] =
{
/* non-linear sequence: constant term of inverse in GF(8),
* mod x^8+x^4+x^3+x+1 */
0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1,
0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0,
0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0,
0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1,
1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
};
/* 0x04b3e375 and 0x05a8ef93 are chosen to contain every possible
* 5-bit sequence */
z = ( (0x04b3e375 * x) ^ y ) * 0x05a8ef93;
z = t[z & 0xff] ^ t[(z >> 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( xhi<xa )
{
for( i = xhi; i < xa; i += BM_WORDBITS )
{
*bm_index( bm, i, y ) ^= BM_ALLBITS;
}
}
else
{
for( i = xa; i < xhi; i += BM_WORDBITS )
{
*bm_index( bm, i, y ) ^= BM_ALLBITS;
}
}
/* note: the following "if" is needed because x86 treats a<<b as
* a<<(b&31). I spent hours looking for this bug. */
if( xlo )
{
*bm_index( bm, xhi, y ) ^= ( BM_ALLBITS << (BM_WORDBITS - xlo) );
}
}
/* a path is represented as an array of points, which are thought to
* lie on the corners of pixels (not on their centers). The path point
* (x,y) is the lower left corner of the pixel (x,y). Paths are
* represented by the len/pt components of a path_t object (which
* also stores other information about the path) */
/* xor the given pixmap with the interior of the given path. Note: the
* path must be within the dimensions of the pixmap. */
static void xor_path( potrace_bitmap_t* bm, path_t* p )
{
int xa, x, y, k, y1;
if( p->priv->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; k<p->priv->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; k<p->priv->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<x'. If found, return 0 and store pixel in
* (*xp,*yp). Else return 1. Note that this function assumes that
* excess bytes have been cleared with bm_clearexcess. */
static int findnext( potrace_bitmap_t* bm, int* xp, int* yp )
{
int x;
int y;
for( y = *yp; y>=0; y-- )
{
for( x = 0; x<bm->w; 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;
}

18
potrace/decompose.h Normal file
View File

@ -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 */

1107
potrace/greymap.cpp Normal file

File diff suppressed because it is too large Load Diff

58
potrace/greymap.h Normal file
View File

@ -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 <stdio.h>
/* 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 */

293
potrace/lists.h Normal file
View File

@ -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 */

39
potrace/platform.h Normal file
View File

@ -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 <fcntl.h>
unsigned int _CRT_fmode = _O_BINARY;
#endif
#ifdef __CYGWIN__
#include <fcntl.h>
#include <io.h>
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 */

View File

@ -0,0 +1 @@
#define POTRACELIB_VERSION "potracelib 1.8"

129
potrace/potracelib.cpp Normal file
View File

@ -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 <stdlib.h>
#include <string.h>
#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, &param_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;
}

138
potrace/potracelib.h Normal file
View File

@ -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 */

96
potrace/progress.h Normal file
View File

@ -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 */

294
potrace/render.cpp Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#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; i<x; i++ )
{
GM_INC( rm->gm, i, y, -b );
}
}
else
{
for( i = x; i<x0; i++ )
{
GM_INC( rm->gm, 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<sn || j<tn )
{
if( j>=tn || (i<sn && s0 + i * ss < t0 + j * ts) )
{
r1 = s0 + i * ss;
i++;
s = 1;
}
else
{
r1 = t0 + j * ts;
j++;
s = 0;
}
/* render line from r0 to r1 segment of (rm->x1,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 );
}

35
potrace/render.h Normal file
View File

@ -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 */

1456
potrace/trace.cpp Normal file

File diff suppressed because it is too large Load Diff

15
potrace/trace.h Normal file
View File

@ -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 */