Browse Source
Added an experimental tool to create logos from .bmp bitmaps. Added Potrace library to convert bitmaps to polygons
pull/1/head
Added an experimental tool to create logos from .bmp bitmaps. Added Potrace library to convert bitmaps to polygons
pull/1/head
32 changed files with 6387 additions and 13 deletions
-
2CMakeLists.txt
-
22bitmap2component/CMakeLists.txt
-
570bitmap2component/bitmap2component.cpp
-
10eeschema/class_libentry.cpp
-
6eeschema/class_library.cpp
-
8eeschema/classes_body_items.cpp
-
4eeschema/general.h
-
27gerbview/gerber_test_files/octogonal.gbr
-
2pcbnew/plot_rtn.cpp
-
5potrace/AUTHORS
-
14potrace/CMakeLists.txt
-
89potrace/auxiliary.h
-
118potrace/bitmap.h
-
969potrace/bitmap_io.cpp
-
23potrace/bitmap_io.h
-
50potrace/bitops.h
-
122potrace/curve.cpp
-
80potrace/curve.h
-
600potrace/decompose.cpp
-
18potrace/decompose.h
-
1107potrace/greymap.cpp
-
58potrace/greymap.h
-
293potrace/lists.h
-
39potrace/platform.h
-
1potrace/potrace_version.h
-
129potrace/potracelib.cpp
-
138potrace/potracelib.h
-
96potrace/progress.h
-
294potrace/render.cpp
-
35potrace/render.h
-
1456potrace/trace.cpp
-
15potrace/trace.h
@ -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 ); |
|||
} |
@ -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* |
@ -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 */ |
1107
potrace/greymap.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,58 @@ |
|||
/* Copyright (C) 2001-2007 Peter Selinger. |
|||
This file is part of Potrace. It is free software and it is covered |
|||
by the GNU General Public License. See the file COPYING for details. */ |
|||
|
|||
/* $Id: greymap.h 147 2007-04-09 00:44:09Z selinger $ */ |
|||
|
|||
#ifndef PGM_H |
|||
#define PGM_H |
|||
|
|||
#include <stdio.h> |
|||
|
|||
/* internal format for greymaps. Note: in this format, rows are |
|||
ordered from bottom to top. The pixels in each row are given from |
|||
left to right. */ |
|||
|
|||
struct greymap_s { |
|||
int w; /* width, in pixels */ |
|||
int h; /* height, in pixels */ |
|||
signed short int *map; /* raw data, w*h values */ |
|||
}; |
|||
typedef struct greymap_s greymap_t; |
|||
|
|||
/* macros for accessing pixel at index (x,y). Note that the origin is |
|||
in the *lower* left corner. U* macros omit the bounds check. */ |
|||
|
|||
#define gm_index(gm, x, y) (&(gm)->map[(x)+(y)*(gm)->w]) |
|||
#define gm_safe(gm, x, y) ((int)(x)>=0 && (int)(x)<(gm)->w && (int)(y)>=0 && (int)(y)<(gm)->h) |
|||
#define gm_bound(x, m) ((x)<0 ? 0 : (x)>=(m) ? (m)-1 : (x)) |
|||
#define GM_UGET(gm, x, y) (*gm_index(gm, x, y)) |
|||
#define GM_UINC(gm, x, y, b) (*gm_index(gm, x, y) += (short int)(b)) |
|||
#define GM_UINV(gm, x, y) (*gm_index(gm, x, y) = 255 - *gm_index(gm, x, y)) |
|||
#define GM_UPUT(gm, x, y, b) (*gm_index(gm, x, y) = (short int)(b)) |
|||
#define GM_GET(gm, x, y) (gm_safe(gm, x, y) ? GM_UGET(gm, x, y) : 0) |
|||
#define GM_INC(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UINC(gm, x, y, b) : 0) |
|||
#define GM_INV(gm, x, y) (gm_safe(gm, x, y) ? GM_UINV(gm, x, y) : 0) |
|||
#define GM_PUT(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UPUT(gm, x, y, b) : 0) |
|||
#define GM_BGET(gm, x, y) GM_UGET(gm, gm_bound(x, gm->w), gm_bound(y, gm->h)) |
|||
|
|||
/* modes for cutting off out-of-range values. The following names |
|||
refer to winding numbers. I.e., make a pixel black if winding |
|||
number is nonzero, odd, or positive, respectively. We assume that 0 |
|||
winding number corresponds to white (255). */ |
|||
#define GM_MODE_NONZERO 1 |
|||
#define GM_MODE_ODD 2 |
|||
#define GM_MODE_POSITIVE 3 |
|||
#define GM_MODE_NEGATIVE 4 |
|||
|
|||
extern const char *gm_read_error; |
|||
|
|||
greymap_t *gm_new(int w, int h); |
|||
greymap_t *gm_dup(greymap_t *gm); |
|||
void gm_free(greymap_t *gm); |
|||
void gm_clear(greymap_t *gm, int b); |
|||
int gm_read(FILE *f, greymap_t **gmp); |
|||
int gm_writepgm(FILE *f, greymap_t *gm, char *comment, int raw, int mode, double gamma); |
|||
int gm_print(FILE *f, greymap_t *gm); |
|||
|
|||
#endif /* PGM_H */ |
@ -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 */ |
1456
potrace/trace.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,15 @@ |
|||
/* Copyright (C) 2001-2007 Peter Selinger. |
|||
* This file is part of Potrace. It is free software and it is covered |
|||
* by the GNU General Public License. See the file COPYING for details. */ |
|||
|
|||
/* $Id: trace.h 147 2007-04-09 00:44:09Z selinger $ */ |
|||
|
|||
#ifndef TRACE_H |
|||
#define TRACE_H |
|||
|
|||
#include "potracelib.h" |
|||
#include "progress.h" |
|||
|
|||
int process_path( path_t* plist, const potrace_param_t* param, progress_t* progress ); |
|||
|
|||
#endif /* TRACE_H */ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue