Added an experimental tool to create logos from .bmp bitmaps. Added Potrace library to convert bitmaps to polygons
This commit is contained in:
parent
82aadca861
commit
ac11d86c2f
|
@ -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 #
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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' )
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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*
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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})
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -0,0 +1 @@
|
||||||
|
#define POTRACELIB_VERSION "potracelib 1.8"
|
|
@ -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, ¶m_default, sizeof(potrace_param_t) );
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* On success, returns a Potrace state st with st->status ==
|
||||||
|
* POTRACE_STATUS_OK. On failure, returns NULL if no Potrace state
|
||||||
|
* could be created (with errno set), or returns an incomplete Potrace
|
||||||
|
* state (with st->status == POTRACE_STATUS_INCOMPLETE). Complete or
|
||||||
|
* incomplete Potrace state can be freed with potrace_state_free(). */
|
||||||
|
potrace_state_t* potrace_trace( const potrace_param_t* param, const potrace_bitmap_t* bm )
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
path_t* plist = NULL;
|
||||||
|
potrace_state_t* st;
|
||||||
|
progress_t prog;
|
||||||
|
progress_t subprog;
|
||||||
|
|
||||||
|
/* prepare private progress bar state */
|
||||||
|
prog.callback = param->progress.callback;
|
||||||
|
prog.data = param->progress.data;
|
||||||
|
prog.min = param->progress.min;
|
||||||
|
prog.max = param->progress.max;
|
||||||
|
prog.epsilon = param->progress.epsilon;
|
||||||
|
prog.d_prev = param->progress.min;
|
||||||
|
|
||||||
|
/* allocate state object */
|
||||||
|
st = (potrace_state_t*) malloc( sizeof(potrace_state_t) );
|
||||||
|
if( !st )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress_subrange_start( 0.0, 0.1, &prog, &subprog );
|
||||||
|
|
||||||
|
/* process the image */
|
||||||
|
r = bm_to_pathlist( bm, &plist, param, &subprog );
|
||||||
|
if( r )
|
||||||
|
{
|
||||||
|
free( st );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->status = POTRACE_STATUS_OK;
|
||||||
|
st->plist = plist;
|
||||||
|
st->priv = NULL; /* private state currently unused */
|
||||||
|
|
||||||
|
progress_subrange_end( &prog, &subprog );
|
||||||
|
|
||||||
|
progress_subrange_start( 0.1, 1.0, &prog, &subprog );
|
||||||
|
|
||||||
|
/* partial success. */
|
||||||
|
r = process_path( plist, param, &subprog );
|
||||||
|
if( r )
|
||||||
|
{
|
||||||
|
st->status = POTRACE_STATUS_INCOMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress_subrange_end( &prog, &subprog );
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* free a Potrace state, without disturbing errno. */
|
||||||
|
void potrace_state_free( potrace_state_t* st )
|
||||||
|
{
|
||||||
|
pathlist_free( st->plist );
|
||||||
|
free( st );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* free a parameter list, without disturbing errno. */
|
||||||
|
void potrace_param_free( potrace_param_t* p )
|
||||||
|
{
|
||||||
|
free( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* potrace_version( void )
|
||||||
|
{
|
||||||
|
return POTRACELIB_VERSION;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 );
|
||||||
|
}
|
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
Loading…
Reference in New Issue