From 1442dce9911da9fc7b2978b8a86d667197a34603 Mon Sep 17 00:00:00 2001 From: Willard Nilges Date: Wed, 21 Sep 2022 02:47:01 -0400 Subject: [PATCH] Re-write Inkpath with OpenCV (#22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Re-write in C++ * upsampling * autotrace doesn't like images that come frm opencv * chom * a * I wanna try this with opencv * Cleanup and usability * Path parsing actually works™, Not super useful for the actual tool, but good for debugging lol * Contours * Why not just re-write the whole thing in c++ * Update build instructions 1/? * Clean up ipcv_obj and Makefile * Update documentation --- .gitignore | 2 + HACKING/Dockerfile | 34 +- HACKING/README.md | 32 +- HACKING/build-environment.sh | 4 + HACKING/build-opencv.sh | 27 + HACKING/build-xopp.sh | 9 +- HACKING/launch-environment.sh | 3 +- ImageTranscription/main.lua | 48 +- Makefile | 62 +- README.md | 23 +- src/autotrace/.dirstamp | 0 src/autotrace/.gitattributes | 1 - src/autotrace/autotrace.c | 389 --------- src/autotrace/autotrace.h | 413 ---------- src/autotrace/bitmap.c | 8 - src/autotrace/bitmap.h | 13 - src/autotrace/color.c | 114 --- src/autotrace/color.h | 53 -- src/autotrace/curve.c | 255 ------ src/autotrace/curve.h | 131 --- src/autotrace/despeckle.c | 710 ---------------- src/autotrace/despeckle.h | 54 -- src/autotrace/epsilon-equal.c | 22 - src/autotrace/epsilon-equal.h | 17 - src/autotrace/exception.c | 47 -- src/autotrace/exception.h | 39 - src/autotrace/filename.c | 45 - src/autotrace/filename.h | 33 - src/autotrace/fit.c | 1442 --------------------------------- src/autotrace/fit.h | 22 - src/autotrace/image-header.h | 18 - src/autotrace/image-proc.c | 493 ----------- src/autotrace/image-proc.h | 21 - src/autotrace/input-bmp.c | 865 -------------------- src/autotrace/input-bmp.h | 27 - src/autotrace/input-png.c | 193 ----- src/autotrace/input-png.h | 29 - src/autotrace/input.c | 232 ------ src/autotrace/input.h | 95 --- src/autotrace/intl.h | 22 - src/autotrace/logreport.c | 9 - src/autotrace/logreport.h | 32 - src/autotrace/median.c | 863 -------------------- src/autotrace/module.c | 73 -- src/autotrace/private.h | 43 - src/autotrace/pxl-outline.c | 889 -------------------- src/autotrace/pxl-outline.h | 58 -- src/autotrace/quantize.h | 52 -- src/autotrace/spline.c | 160 ---- src/autotrace/spline.h | 88 -- src/autotrace/thin-image.c | 353 -------- src/autotrace/thin-image.h | 36 - src/autotrace/types.h | 42 - src/autotrace/vector.c | 260 ------ src/autotrace/vector.h | 65 -- src/autotrace/xstd.h | 85 -- src/cv/.otsu.cpp.swp | Bin 0 -> 12288 bytes src/cv/debug/debug.cpp | 124 +++ src/cv/ipcv.cpp | 118 +++ src/cv/ipcv.h | 20 + src/ipcv_obj/ipcv_obj.cpp | 106 +++ src/ipcv_obj/ipcv_obj.h | 49 ++ src/lua_util.c | 140 ---- src/lua_util.h | 22 - src/spline.h | 86 -- 65 files changed, 571 insertions(+), 9249 deletions(-) create mode 100755 HACKING/build-environment.sh create mode 100755 HACKING/build-opencv.sh delete mode 100644 src/autotrace/.dirstamp delete mode 100644 src/autotrace/.gitattributes delete mode 100644 src/autotrace/autotrace.c delete mode 100644 src/autotrace/autotrace.h delete mode 100644 src/autotrace/bitmap.c delete mode 100644 src/autotrace/bitmap.h delete mode 100644 src/autotrace/color.c delete mode 100644 src/autotrace/color.h delete mode 100644 src/autotrace/curve.c delete mode 100644 src/autotrace/curve.h delete mode 100644 src/autotrace/despeckle.c delete mode 100644 src/autotrace/despeckle.h delete mode 100644 src/autotrace/epsilon-equal.c delete mode 100644 src/autotrace/epsilon-equal.h delete mode 100644 src/autotrace/exception.c delete mode 100644 src/autotrace/exception.h delete mode 100644 src/autotrace/filename.c delete mode 100644 src/autotrace/filename.h delete mode 100644 src/autotrace/fit.c delete mode 100644 src/autotrace/fit.h delete mode 100644 src/autotrace/image-header.h delete mode 100644 src/autotrace/image-proc.c delete mode 100644 src/autotrace/image-proc.h delete mode 100644 src/autotrace/input-bmp.c delete mode 100644 src/autotrace/input-bmp.h delete mode 100644 src/autotrace/input-png.c delete mode 100644 src/autotrace/input-png.h delete mode 100644 src/autotrace/input.c delete mode 100644 src/autotrace/input.h delete mode 100644 src/autotrace/intl.h delete mode 100644 src/autotrace/logreport.c delete mode 100644 src/autotrace/logreport.h delete mode 100644 src/autotrace/median.c delete mode 100644 src/autotrace/module.c delete mode 100644 src/autotrace/private.h delete mode 100644 src/autotrace/pxl-outline.c delete mode 100644 src/autotrace/pxl-outline.h delete mode 100644 src/autotrace/quantize.h delete mode 100644 src/autotrace/spline.c delete mode 100644 src/autotrace/spline.h delete mode 100644 src/autotrace/thin-image.c delete mode 100644 src/autotrace/thin-image.h delete mode 100644 src/autotrace/types.h delete mode 100644 src/autotrace/vector.c delete mode 100644 src/autotrace/vector.h delete mode 100644 src/autotrace/xstd.h create mode 100644 src/cv/.otsu.cpp.swp create mode 100644 src/cv/debug/debug.cpp create mode 100644 src/cv/ipcv.cpp create mode 100644 src/cv/ipcv.h create mode 100644 src/ipcv_obj/ipcv_obj.cpp create mode 100644 src/ipcv_obj/ipcv_obj.h delete mode 100644 src/lua_util.c delete mode 100644 src/lua_util.h delete mode 100644 src/spline.h diff --git a/.gitignore b/.gitignore index 32eca9d..fcec1a5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ build/ *.svg do.sh ImageTranscription/inkpath.so +ImageTranscription/ipcvobj.so +*.gch diff --git a/HACKING/Dockerfile b/HACKING/Dockerfile index 3d40e42..b11ee04 100644 --- a/HACKING/Dockerfile +++ b/HACKING/Dockerfile @@ -1,21 +1,21 @@ FROM docker.io/debian -RUN touch /root/.Xauthority && \ - apt-get update && \ - apt-get install -y tmux git gdb vim x11-apps #&& \ -# apt-get install -y build-essential pkg-config autoconf intltool autopoint libtool libglib2.0-dev build-essential libmagickcore-dev libpstoedit-dev imagemagick pstoedit && \ -# apt-get install -y cmake libgtk-3-dev libpoppler-glib-dev portaudio19-dev libsndfile-dev \ -# dvipng texlive libxml2-dev liblua5.3-dev libzip-dev librsvg2-dev gettext lua-lgi +RUN touch /root/.Xauthority +RUN apt-get update -# Disabled for testing -#RUN cd && git clone https://github.com/yy502/autotrace && \ -# cd autotrace && ./autogen.sh && LD_LIBRARY_PATH=/usr/local/lib ./configure --prefix=/usr && make && make install +# Line 10: Inkpath +# Line 11: Xournalpp +# Line 13: debugging tools +RUN apt-get install -y make liblua5.4-dev build-essential pkg-config libglib2.0-dev libpng-dev && \ + apt-get install -y cmake libgtk-3-dev libpoppler-glib-dev portaudio19-dev libsndfile-dev \ + dvipng texlive libxml2-dev libzip-dev librsvg2-dev gettext lua-lgi \ + libgtksourceview-4-dev +RUN apt-get install -y tmux git gdb vim x11-apps tree wget unzip -# Mount these yourself. -#git clone https://github.com/xournalpp/xournalpp && \ -#git clone https://gitub.com/willnilges/inkpath && \ -#RUN cd && git clone https://github.com/willnilges/xournalpp && \ -#cd xournalpp && mkdir build && cd build && cmake .. && cmake --build . -# -#RUN cd && git clone https://github.com/willnilges/inkpath && \ -#cd inkpath && make lua-plugin && cp -r ImageTranscription ../xournalpp/plugins && cp ImageTranscription/inkpath.so ../xournalpp/build/src +# Build and install OpenCV into the container +COPY ./build-opencv.sh . +RUN bash /build-opencv.sh 48 + +# Build and install Xournal++ +COPY ./build-xopp.sh . +RUN bash /build-xopp.sh 48 diff --git a/HACKING/README.md b/HACKING/README.md index 3020cf5..38608f1 100644 --- a/HACKING/README.md +++ b/HACKING/README.md @@ -1,28 +1,28 @@ +# Thanks for contributing :) -This Dockerfile should have everything you need (sans Xforwarding, TODO) to make a development container. +## Setting up the development environment -TODO: Not sure if I want these as separate layers. It makes sense to me at 01:36, though. +This guide will walk you through everything you need to work on this project, including building OpenCV and Xournalpp. -We got: -1. Packages required for the container -2. Packages to build AutoTrace -3. Packages to build Xournalpp and Inkpath -(Some of these overlap) +(You should have all relevant development files (including this repo) located at `~/Code/xopp-dev`. This can be configured in `launch-environment.sh` by the `$CODE_PATH` variable) -Notes: imagemagick, pstoedit are optional. - -You'll need to `xauth add ` to make Xforwarding work. Find it with `xauth list` on your host. - -Run this with: +- First, build the Dockerfile. This will download all the packages you need to compile inkpath and friends. OpenCV is the backend that now powers this project, and you might want Xournalpp as source code so you can debug more easily. +``` +./HACKING/build-environment.sh +``` +- Next, launch it. This container will do Xforwarding for you so that you can run Xournalpp on your desktop and do development. All subsequent build stuff should happen in here. ``` -podman build . --tag xopp-dev -podman run --name=xopp-dev -dit -e DISPLAY=$DISPLAY --network=host --cap-add=SYS_PTRACE -v /home/$USER/Code/inkpath_dev:/mnt/inkpath_dev -v /tmp/.X11-unix:/tmp/.X11-unix xopp-dev +./HACKING/launch-environment.sh ``` -Or use the provided script. +- If you did it right, you should have an `xopp-dev` directory in your container. +``` +cd /xopp-dev/inkpath/ +make dev-install +``` -## Debugging the Lua +## Debugging Lua You might find the following to be useful - https://github.com/kikito/inspect.lua diff --git a/HACKING/build-environment.sh b/HACKING/build-environment.sh new file mode 100755 index 0000000..aad357f --- /dev/null +++ b/HACKING/build-environment.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +cd HACKING +podman build . --tag xopp-dev diff --git a/HACKING/build-opencv.sh b/HACKING/build-opencv.sh new file mode 100755 index 0000000..cc53a49 --- /dev/null +++ b/HACKING/build-opencv.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -e + +cores=4 + +# For compiling faster +if [ -n "$1" ]; then + cores=$1 +fi + +# Create exterior directories +mkdir -p ../opencv +cd ../opencv + +wget -O opencv.zip https://github.com/opencv/opencv/archive/4.5.1.zip + +unzip opencv.zip +# Create opencv build directory +mkdir -p build && cd build +# Configure +cmake ../opencv-4.5.1 -DBUILD_SHARED_LIBS=OFF -DOPENCV_GENERATE_PKGCONFIG=YES -DWITH_GTK=OFF +# Build +cmake --build . -j$cores + +make install + diff --git a/HACKING/build-xopp.sh b/HACKING/build-xopp.sh index f280af3..9a1c104 100755 --- a/HACKING/build-xopp.sh +++ b/HACKING/build-xopp.sh @@ -1,8 +1,12 @@ #!/bin/bash +# It's assumed that xournalpp will be cloned outside this repo :) +cd .. +git clone https://github.com/xournalpp/xournalpp + if [[ "$1" == "clean" ]]; then echo 'Deleting build dir' - rm -rf ../../xournalpp/build + rm -rf ../xournalpp/build exit 0 fi @@ -13,8 +17,7 @@ if [ -n "$1" ]; then cores=$1 fi - -cd ../../xournalpp +cd ../xournalpp mkdir build cd build cmake .. diff --git a/HACKING/launch-environment.sh b/HACKING/launch-environment.sh index c66cfd8..0035c97 100755 --- a/HACKING/launch-environment.sh +++ b/HACKING/launch-environment.sh @@ -36,7 +36,8 @@ podman run --name=xopp-dev --rm -it \ --cap-add=SYS_PTRACE \ --group-add keep-groups \ --annotation io.crun.keep_original_groups=1 \ - -v "$xauth_path"/.Xauthority:/root/.Xauthority:Z \ + -v "$xauth_path"/.Xauthority:/root/.Xauthority:Z \ -v "$CODE_PATH":/xopp-dev:Z \ + -v /scratch/wilnil:/scratch:Z \ -v /tmp/.X11-unix:/tmp/.X11-unix xopp-dev rm -rf "$xauth_path" diff --git a/ImageTranscription/main.lua b/ImageTranscription/main.lua index 0d0501e..25256ba 100644 --- a/ImageTranscription/main.lua +++ b/ImageTranscription/main.lua @@ -6,31 +6,29 @@ end -- Callback if the menu item is executed function drawStroke() - inkpath = require 'inkpath' - -- path = app.getFilePath({'*.ppm', '*.png', '*.pbm', '*.pnm', '*.bmp', '*.tga', '*.yuv', '*.pgm', '*.gf'}) -- Autotrace 0.40.0 supports ppm, png, pbm, pnm, bmp, tga, yuv, pgm, gf - path = app.getFilePath({'*.png', '*.bmp'}) -- The current version of Autotrace I'm using only supports PNGs. - image_scale = app.msgbox("Select tracing scale", {[1] = "Small", [2] = "Medium", [3] = "Large"}) - output = inkpath.transcribe_image(path, image_scale) + print("Inkpath Activated. Transcribing image....") + inkpath = require "ipcvobj" + path = app.getFilePath({'*.jpg', '*.png', '*.bmp'}) -- The current version of Autotrace I'm using only supports PNGs. + --image_scale = app.msgbox("Select tracing scale", {[1] = "Small", [2] = "Medium", [3] = "Large"}) -- TODO: implement this again. + image_scale = 1 + scaling_factor = 10.0 -- THIS IS A NEW THING! HOW MUCH DO YOU WANT TO DIVIDE YOUR SHIT BY!? MUST BE FLOAT! + local obj = IPCVObj(path, 1) print("Strokes retrieved.") - strokes = {} - single_stroke = {} - for key, value in pairs(output) do - if value[1] == -1.0 and value[2] == -1.0 then -- If we get a delimiting pair, add our current stroke to the stroke table. - table.insert(strokes, { - ["coordinates"] = single_stroke, - }); - single_stroke = {} - else - table.insert(single_stroke, value[1]) -- Y coord - table.insert(single_stroke, value[2]) -- X coord - end + contourCt = obj:getLength() + print("Got ", contourCt, " strokes.") + -- TODO: This could be much, MUCH faster. + for i = 0,contourCt-1,1 do + pointCt = obj:getContourLength(i) + x_points, y_points = obj:getContour(i, scaling_factor) + app.addStrokes({ + ["strokes"] = { + { + ["x"] = x_points, + ["y"] = y_points, + }, + }, + ["allowUndoRedoAction"] = "grouped", + }) end - -- When we've assembled our table of strokes, call the addSplines function - -- Not going to pass any options since I want to use the current tool options. - app.addSplines({ - ["splines"] = strokes, - ["allowUndoRedoAction"] = "grouped", - }) - app.refreshPage() - print("done") + print("Image Transcription Complete. Exiting Inkpath.") end diff --git a/Makefile b/Makefile index 8be7899..92555d0 100644 --- a/Makefile +++ b/Makefile @@ -5,49 +5,67 @@ WARNINGS = -Wall -Wextra -Wpedantic -Wconversion -Wformat=2 -Winit-self \ -Wmissing-include-dirs -Wformat-nonliteral -Wnested-externs \ -Wno-unused-parameter -Wold-style-definition -Wredundant-decls -Wshadow \ -Wstrict-prototypes -Wwrite-strings -#LIGHT_WARNINGS = -Wall +LIGHT_WARNINGS = -Wall PLUGIN_NAME=ImageTranscription +SO_NAME=ipcvobj.so LUA_VERSION=5.4 SO_INSTALL_PATH=/usr/lib64/lua/$(LUA_VERSION)# Just one of many possible destinations :) - -# CFLAGS += -std=gnu99 +XOPP_DEV_INSTALL_PATH=/xournalpp .PHONY: clean install uninstall dev-install dev-uninstall -# TODO: `-g` is for debugging. Make a target that supports debugging separately from primary compilation +ip_source := $(wildcard src/ipcv_obj/*.cpp) +cv_source := $(wildcard src/cv/*.cpp) -at_source := $(wildcard src/autotrace/*.c src/autotrace/*.h) +lua_deps=`pkg-config --cflags --libs lua` +cv_deps=`pkg-config --cflags --libs --static opencv4` -lua-plugin: src/lua_util.c $(at_source) - mkdir -p build - $(CC) $(LIGHT_WARNINGS) $(CFLAGS) src/lua_util.c $(at_source) -g `pkg-config --cflags --libs lua glib-2.0` -fPIC -shared -o $(PLUGIN_NAME)/inkpath.so +.PHONY: build_dir +build_dir: + @mkdir -p build + +# Compiles and statically links Inkpath's OpenCV code to the necessary OpenCV libraries +ipcv: $(cv_source) + @mkdir -p build + g++ -c $(cv_source) $(lua_deps) $(cv_deps) -fPIC -static + @mv *.o build + ar -crsT build/libipcv.a build/*.o +# Compiles Inkpath's shared object library +lua-plugin: $(ip_source) ipcv + g++ $(LIGHT_WARNINGS) $(ip_source) -L/xopp-dev/inkpath/build -lipcv $(cv_deps) $(lua_deps) -g -fPIC -shared -o $(PLUGIN_NAME)/$(SO_NAME) + +# Installs the plugin into your Xournalpp installation install: lua-plugin cp -r $(PLUGIN_NAME) /usr/share/xournalpp/plugins/ mkdir -p $(SO_INSTALL_PATH) - cp -r $(PLUGIN_NAME)/inkpath.so $(SO_INSTALL_PATH)/inkpath.so + cp -r $(PLUGIN_NAME)/$(SO_NAME) $(SO_INSTALL_PATH)/$(SO_NAME) +# Remove the plugin files from the xournalpp install dir uninstall: rm -rf /usr/share/xournalpp/plugins/$(PLUGIN_NAME) - rm $(SO_INSTALL_PATH)/inkpath.so + rm $(SO_INSTALL_PATH)/$(SO_NAME) # Used to install the plugin into a source code repository of xournalpp -dev-install: - cp -r $(PLUGIN_NAME) ../xournalpp/plugins - cp -r HACKING/StrokeTest ../xournalpp/plugins - cp $(PLUGIN_NAME)/inkpath.so ../xournalpp/build/ +dev-install: lua-plugin + cp -r $(PLUGIN_NAME) $(XOPP_DEV_INSTALL_PATH)/plugins + cp -r HACKING/StrokeTest $(XOPP_DEV_INSTALL_PATH)/plugins + cp $(PLUGIN_NAME)/$(SO_NAME) $(XOPP_DEV_INSTALL_PATH)/build/ +# Remove the plugin from the development environment dev-uninstall: - rm -rf ../xournalpp/plugins/$(PLUGIN_NAME) - rm -rf HACKING/StrokeTest ../xournalpp/plugins - rm ../xournalpp/build/inkpath.so + rm -rf $(XOPP_DEV_INSTALL_PATH)/plugins/$(PLUGIN_NAME) + rm -rf HACKING/StrokeTest $(XOPP_DEV_INSTALL_PATH)/plugins + rm $(XOPP_DEV_INSTALL_PATH)/$(SO_NAME) -stroketest: - cp -r HACKING/StrokeTest ../xournalpp/plugins +# For generating a CV debugging binary +ipcv-debug: $(cv_source) + mkdir -p build + g++ src/cv/debug/debug.cpp $(cv_source) `pkg-config --cflags --libs --static opencv4` -static -o build/ipcv -stroketest-uninstall: - rm -rf HACKING/StrokeTest ../xournalpp/plugins +help: + @echo ipcv lua-plugin install uninstall dev-install dev-uninstall ipcv-debug clean: rm -rf build - rm $(PLUGIN_NAME)/inkpath.so + rm $(PLUGIN_NAME)/$(SO_NAME) diff --git a/README.md b/README.md index 4e13612..cab0a59 100644 --- a/README.md +++ b/README.md @@ -21,15 +21,23 @@ Inkpath is a project designed to crunch those whiteboard photos into easily editable [Xournalpp](https://github.com/xournalpp) note files so that you can drop your whiteboard scrawlings directly into your lecture notes. Convenient! -This uses [autotrace](https://github.com/autotrace/autotrace) to translate -whiteboard markings into splines. Those splines are then passed directly to the -Xournal++ API and rasterized, before being placed onto the working layer. +The project consists of a lua script and a shared object library written in C++ +statically linked with some OpenCV utils. The project now uses OpenCV in place +of Autotrace to perform an Otsu Threshold. The thresholded image is then inverted, +and skeletonized, producing the centerline of each individual object in the image +(which at this point should be just markings). The resulting image is then scanned +for contours. These contours are pushed onto the lua stack, and passed to the +Xournal++ API. Unlike the previous implementation, this operates purely on rasters. ## Installation and Usage -When you run `make lua-plugin`, it will compile inkpath and place `inkpath.so` in the ImageTranscription directory.`make install` will copy that directory to your Xournalpp plugins folder, and Inkpath will be installed. You can then use it from the 'Plugins' menu from within Xournalpp. +### Prerequisites + +You'll need to statically compile and install OpenCV (see `HACKING/build-opencv.sh`), then install Xournalpp using your package manager of choice (or compile from source). + +_I'm not 100% sure that all the API changes I've made have been released. They are merged, but might not be in your package manager._ -_Prerequisite: You must have xournalpp installed via your package manager of choice_ +### Installation 1. Download dependencies @@ -50,10 +58,13 @@ pacman -S base-devel pkg-config lua libpng 2. Compile and install Inkpath ``` -# Download and build Inkpath (Deps included in Xournalpp) git clone https://github.com/willnilges/inkpath.git cd inkpath make install ``` +When you run `make lua-plugin`, it will compile inkpath and place `inkpath.so` in the ImageTranscription directory.`make install` will copy that directory to your Xournalpp plugins folder, and Inkpath will be installed. You can then use it from the 'Plugins' menu from within Xournalpp. + +_Inkpath is coming to a package manager near you soon™!_ + C badge diff --git a/src/autotrace/.dirstamp b/src/autotrace/.dirstamp deleted file mode 100644 index e69de29..0000000 diff --git a/src/autotrace/.gitattributes b/src/autotrace/.gitattributes deleted file mode 100644 index 36eaad9..0000000 --- a/src/autotrace/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* linguist-vendored diff --git a/src/autotrace/autotrace.c b/src/autotrace/autotrace.c deleted file mode 100644 index ed41971..0000000 --- a/src/autotrace/autotrace.c +++ /dev/null @@ -1,389 +0,0 @@ -/* autotrace.c --- Autotrace API - - Copyright (C) 2000, 2001, 2002 Martin Weber - - The author can be contacted at - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ -#include "intl.h" - -#include "private.h" - -#include "autotrace.h" -#include "exception.h" - -#include "fit.h" -#include "bitmap.h" -#include "spline.h" - -#include "input.h" - -#include "xstd.h" -#include "image-header.h" -#include "image-proc.h" -#include "quantize.h" -#include "thin-image.h" -#include "despeckle.h" - -#include -#include -#include -#include - -#define AT_DEFAULT_DPI 72 - -#define AUTOTRACE_VERSION "willard" -#define AUTOTRACE_WEB "chom" - -at_fitting_opts_type *at_fitting_opts_new(void) -{ - at_fitting_opts_type *opts; - XMALLOC(opts, sizeof(at_fitting_opts_type)); - *opts = new_fitting_opts(); - return opts; -} - -at_fitting_opts_type *at_fitting_opts_copy(at_fitting_opts_type * original) -{ - at_fitting_opts_type *new_opts; - if (original == NULL) - return NULL; - - new_opts = at_fitting_opts_new(); - *new_opts = *original; - if (original->background_color) - new_opts->background_color = at_color_copy(original->background_color); - return new_opts; -} - -void at_fitting_opts_free(at_fitting_opts_type * opts) -{ - free(opts->background_color); - free(opts); -} - -at_input_opts_type *at_input_opts_new(void) -{ - at_input_opts_type *opts; - XMALLOC(opts, sizeof(at_input_opts_type)); - opts->background_color = NULL; - opts->charcode = 0; - return opts; -} - -at_input_opts_type *at_input_opts_copy(at_input_opts_type * original) -{ - at_input_opts_type *opts; - opts = at_input_opts_new(); - *opts = *original; - if (original->background_color) - opts->background_color = at_color_copy(original->background_color); - return opts; -} - -void at_input_opts_free(at_input_opts_type * opts) -{ - free(opts->background_color); - free(opts); -} - -at_output_opts_type *at_output_opts_new(void) -{ - at_output_opts_type *opts; - XMALLOC(opts, sizeof(at_output_opts_type)); - opts->dpi = AT_DEFAULT_DPI; - return opts; -} - -at_output_opts_type *at_output_opts_copy(at_output_opts_type * original) -{ - at_output_opts_type *opts = at_output_opts_new(); - *opts = *original; - return opts; -} - -void at_output_opts_free(at_output_opts_type * opts) -{ - free(opts); -} - -at_bitmap *at_bitmap_read(at_bitmap_reader * reader, gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data) -{ - gboolean new_opts = FALSE; - at_bitmap *bitmap; - XMALLOC(bitmap, sizeof(at_bitmap)); - if (opts == NULL) { - opts = at_input_opts_new(); - new_opts = TRUE; - } - *bitmap = (*reader->func) (filename, opts, msg_func, msg_data, reader->data); - if (new_opts) - at_input_opts_free(opts); - return bitmap; -} - -at_bitmap *at_bitmap_new(unsigned short width, unsigned short height, unsigned int planes) -{ - at_bitmap *bitmap; - XMALLOC(bitmap, sizeof(at_bitmap)); - *bitmap = at_bitmap_init(NULL, width, height, planes); - return bitmap; -} - -at_bitmap *at_bitmap_copy(const at_bitmap * src) -{ - at_bitmap *dist; - unsigned short width, height, planes; - - width = at_bitmap_get_width(src); - height = at_bitmap_get_height(src); - planes = at_bitmap_get_planes(src); - - dist = at_bitmap_new(width, height, planes); - memcpy(dist->bitmap, src->bitmap, width * height * planes * sizeof(unsigned char)); - return dist; -} - -at_bitmap at_bitmap_init(unsigned char *area, unsigned short width, unsigned short height, unsigned int planes) -{ - at_bitmap bitmap; - - if (area) - bitmap.bitmap = area; - else { - if (0 == (width * height)) - bitmap.bitmap = NULL; - else - XCALLOC(bitmap.bitmap, width * height * planes * sizeof(unsigned char)); - } - - bitmap.width = width; - bitmap.height = height; - bitmap.np = planes; - return bitmap; -} - -void at_bitmap_free(at_bitmap * bitmap) -{ - free(AT_BITMAP_BITS(bitmap)); - free(bitmap); -} - -unsigned short at_bitmap_get_width(const at_bitmap * bitmap) -{ - return bitmap->width; -} - -unsigned short at_bitmap_get_height(const at_bitmap * bitmap) -{ - return bitmap->height; -} - -unsigned short at_bitmap_get_planes(const at_bitmap * bitmap) -{ - /* Here we use cast rather changing the type definition of - at_bitmap::np to keep binary compatibility. */ - return (unsigned short)bitmap->np; -} - -void at_bitmap_get_color(const at_bitmap * bitmap, unsigned int row, unsigned int col, at_color * color) -{ - unsigned char *p; - g_return_if_fail(color); - g_return_if_fail(bitmap); - - p = AT_BITMAP_PIXEL(bitmap, row, col); - if (at_bitmap_get_planes(bitmap) >= 3) - at_color_set(color, p[0], p[1], p[2]); - else - at_color_set(color, p[0], p[0], p[0]); -} - -gboolean at_bitmap_equal_color(const at_bitmap * bitmap, unsigned int row, unsigned int col, at_color * color) -{ - at_color c; - - g_return_val_if_fail(bitmap, FALSE); - g_return_val_if_fail(color, FALSE); - - at_bitmap_get_color(bitmap, row, col, &c); - return at_color_equal(&c, color); -} - -at_splines_type *at_splines_new(at_bitmap * bitmap, at_fitting_opts_type * opts, at_msg_func msg_func, gpointer msg_data) -{ - return at_splines_new_full(bitmap, opts, msg_func, msg_data, NULL, NULL, NULL, NULL); -} - -/* at_splines_new_full modify its argument: BITMAP - when despeckle, quantize and/or thin_image are invoked. */ -at_splines_type *at_splines_new_full(at_bitmap * bitmap, at_fitting_opts_type * opts, at_msg_func msg_func, gpointer msg_data, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data) -{ - image_header_type image_header; - at_splines_type *splines = NULL; - pixel_outline_list_type pixels; - QuantizeObj *myQuant = NULL; /* curently not used */ - at_exception_type exp = at_exception_new(msg_func, msg_data); - at_distance_map dist_map, *dist = NULL; - -#define CANCELP (test_cancel && test_cancel(testcancel_data)) -#define FATALP (at_exception_got_fatal(&exp)) -#define FREE_SPLINE() do {if (splines) {at_splines_free(splines); splines = NULL;}} while(0) - -#define CANCEL_THEN_CLEANUP_DIST() if (CANCELP) goto cleanup_dist; -#define CANCEL_THEN_CLEANUP_PIXELS() if (CANCELP) {FREE_SPLINE(); goto cleanup_pixels;} - -#define FATAL_THEN_RETURN() if (FATALP) return splines; -#define FATAL_THEN_CLEANUP_DIST() if (FATALP) goto cleanup_dist; -#define FATAL_THEN_CLEANUP_PIXELS() if (FATALP) {FREE_SPLINE(); goto cleanup_pixels;} - - if (opts->despeckle_level > 0) { - despeckle(bitmap, opts->despeckle_level, opts->despeckle_tightness, opts->noise_removal, &exp); - FATAL_THEN_RETURN(); - } - - image_header.width = at_bitmap_get_width(bitmap); - image_header.height = at_bitmap_get_height(bitmap); - - if (opts->color_count > 0) { - quantize(bitmap, opts->color_count, opts->background_color, &myQuant, &exp); - if (myQuant) - quantize_object_free(myQuant); /* curently not used */ - FATAL_THEN_RETURN(); - } - - if (opts->centerline) { - if (opts->preserve_width) { - /* Preserve line width prior to thinning. */ - dist_map = new_distance_map(bitmap, 255, /*padded= */ TRUE, &exp); - dist = &dist_map; - FATAL_THEN_RETURN(); - } - /* Hereafter, dist is allocated. dist must be freed if - the execution is canceled or exception is raised; - use FATAL_THEN_CLEANUP_DIST. */ - thin_image(bitmap, opts->background_color, &exp); - FATAL_THEN_CLEANUP_DIST() - } - - /* Hereafter, pixels is allocated. pixels must be freed if - the execution is canceled; use CANCEL_THEN_CLEANUP_PIXELS. */ - if (opts->centerline) { - at_color background_color = { 0xff, 0xff, 0xff }; - if (opts->background_color) - background_color = *opts->background_color; - - pixels = find_centerline_pixels(bitmap, background_color, notify_progress, progress_data, test_cancel, testcancel_data, &exp); - } else - pixels = find_outline_pixels(bitmap, opts->background_color, notify_progress, progress_data, test_cancel, testcancel_data, &exp); - FATAL_THEN_CLEANUP_DIST(); - CANCEL_THEN_CLEANUP_DIST(); - - XMALLOC(splines, sizeof(at_splines_type)); - *splines = fitted_splines(pixels, opts, dist, image_header.width, image_header.height, &exp, notify_progress, progress_data, test_cancel, testcancel_data); - FATAL_THEN_CLEANUP_PIXELS(); - CANCEL_THEN_CLEANUP_PIXELS(); - - if (notify_progress) - notify_progress(1.0, progress_data); - -cleanup_pixels: - free_pixel_outline_list(&pixels); -cleanup_dist: - if (dist) - free_distance_map(dist); - return splines; -#undef CANCELP -#undef FATALP -#undef FREE_SPLINE -#undef CANCEL_THEN_CLEANUP_DIST -#undef CANCEL_THEN_CLEANUP_PIXELS - -#undef FATAL_THEN_RETURN -#undef FATAL_THEN_CLEANUP_DIST -#undef FATAL_THEN_CLEANUP_PIXELS - -} - -void at_splines_write(at_spline_writer * writer, FILE * writeto, gchar * file_name, at_output_opts_type * opts, at_splines_type * splines, at_msg_func msg_func, gpointer msg_data) -{ - gboolean new_opts = FALSE; - int llx, lly, urx, ury; - llx = 0; - lly = 0; - urx = splines->width; - ury = splines->height; - - if (!file_name) - file_name = ""; - - if (opts == NULL) { - new_opts = TRUE; - opts = at_output_opts_new(); - } - - setlocale(LC_NUMERIC, "C"); - (*writer->func) (writeto, file_name, llx, lly, urx, ury, opts, *splines, msg_func, msg_data, writer->data); - if (new_opts) - at_output_opts_free(opts); -} - -void at_splines_free(at_splines_type * splines) -{ - free_spline_list_array(splines); - if (splines->background_color) - at_color_free(splines->background_color); - free(splines); -} - -const char *at_version(gboolean long_format) -{ - if (long_format) - return "AutoTrace version " AUTOTRACE_VERSION; - - return AUTOTRACE_VERSION; -} - -const char *at_home_site(void) -{ - return AUTOTRACE_WEB; -} - -void autotrace_init(void) -{ - static int initialized = 0; - if (!initialized) { -#ifdef ENABLE_NLS - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); -#endif /* Def: ENABLE_NLS */ - - /* Initialize subsystems */ - at_input_init(); - at_output_init(); - at_module_init(); - - initialized = 1; - } -} - -const char *at_fitting_opts_doc_func(char *string) -{ - return _(string); -} diff --git a/src/autotrace/autotrace.h b/src/autotrace/autotrace.h deleted file mode 100644 index dc49be1..0000000 --- a/src/autotrace/autotrace.h +++ /dev/null @@ -1,413 +0,0 @@ -/* autotrace.h --- Autotrace API - - Copyright (C) 2000, 2001, 2002 Martin Weber - - The author can be contacted at - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef AUTOTRACE_H -#define AUTOTRACE_H - -#include - -#include "types.h" -#include "color.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef N_ -#define N_(x) x -#endif /* Not def: N_ */ - -/* ===================================================================== * - * Typedefs - * ===================================================================== */ - -/* Third degree is the highest we deal with. */ - enum _at_polynomial_degree { - AT_LINEARTYPE = 1, - AT_QUADRATICTYPE = 2, - AT_CUBICTYPE = 3, - AT_PARALLELELLIPSETYPE = 4, - AT_ELLIPSETYPE = 5, - AT_CIRCLETYPE = 6 - /* not the real number of points to define a - circle but to distinguish between a cubic spline */ - }; - - enum _at_msg_type { - AT_MSG_NOT_SET = 0, /* is used in autotrace internally */ - AT_MSG_FATAL = 1, - AT_MSG_WARNING, - }; - - typedef struct _at_fitting_opts_type at_fitting_opts_type; - typedef struct _at_input_opts_type at_input_opts_type; - typedef struct _at_output_opts_type at_output_opts_type; - typedef struct _at_bitmap at_bitmap; - typedef enum _at_polynomial_degree at_polynomial_degree; - typedef struct _at_spline_type at_spline_type; - typedef struct _at_spline_list_type at_spline_list_type; - typedef struct _at_spline_list_array_type at_spline_list_array_type; -#define at_splines_type at_spline_list_array_type - typedef enum _at_msg_type at_msg_type; - -/* A Bezier spline can be represented as four points in the real plane: - a starting point, ending point, and two control points. The - curve always lies in the convex hull defined by the four points. It - is also convenient to save the divergence of the spline from the - straight line defined by the endpoints. */ - struct _at_spline_type { - at_real_coord v[4]; /* The control points. */ - at_polynomial_degree degree; - gfloat linearity; - }; - -/* Each outline in a character is typically represented by many - splines. So, here is a list structure for that: */ - struct _at_spline_list_type { - at_spline_type *data; - unsigned length; - gboolean clockwise; - at_color color; - gboolean open; - }; - -/* Each character is in general made up of many outlines. So here is one - more list structure. */ - struct _at_spline_list_array_type { - at_spline_list_type *data; - unsigned length; - - /* splines bbox */ - unsigned short height, width; - - /* the values for following members are inherited from - at_fitting_opts_type */ - at_color *background_color; - gboolean centerline; - gboolean preserve_width; - gfloat width_weight_factor; - - }; - -/* Fitting option. - With using at_fitting_opts_doc macro, the description of - each option could be get. e.g. at_fitting_opts_doc(background_color) */ - struct _at_fitting_opts_type { -#define at_doc__background_color \ -N_("background-color : the color of the background that " \ -"should be ignored, for example FFFFFF; " \ -"default is no background color.") - at_color *background_color; - -#define at_doc__charcode \ -N_("charcode : code of character to load from GF file, " \ -"allowed are 0..255; default is the first character in font.") - unsigned charcode; - -#define at_doc__color_count \ -N_("color-count : number of colors a color bitmap is reduced to, " \ -"it does not work on grayscale, allowed are 1..256; " \ -"default is 0, that means not color reduction is done.") - unsigned color_count; - -#define at_doc__corner_always_threshold \ -N_("corner-always-threshold : if the angle at a pixel is " \ -"less than this, it is considered a corner, even if it is within " \ -"`corner-surround' pixels of another corner; default is 60. ") - gfloat corner_always_threshold; - -#define at_doc__corner_surround \ -N_("corner-surround : number of pixels on either side of a " \ -"point to consider when determining if that point is a corner; " \ -"default is 4. ") - unsigned corner_surround; - -#define at_doc__corner_threshold \ -N_("corner-threshold : if a pixel, its predecessor(s), " \ -"and its successor(s) meet at an angle smaller than this, it's a " \ -"corner; default is 100. ") - gfloat corner_threshold; - -#define at_doc__error_threshold \ -N_("error-threshold : subdivide fitted curves that are off by " \ -"more pixels than this; default is 2.0. ") - gfloat error_threshold; - -#define at_doc__filter_iterations \ -N_("filter-iterations : smooth the curve this many times " \ -"before fitting; default is 4.") - unsigned filter_iterations; - -#define at_doc__line_reversion_threshold \ -N_("line-reversion-threshold : if a spline is closer to a straight " \ -"line than this, weighted by the square of the curve length, keep it a " \ -"straight line even if it is a list with curves; default is .01. ") - gfloat line_reversion_threshold; - -#define at_doc__line_threshold \ -N_("line-threshold : if the spline is not more than this far away " \ -"from the straight line defined by its endpoints," \ -"then output a straight line; default is 1. ") - gfloat line_threshold; - -#define at_doc__remove_adjacent_corners \ -N_("remove-adjacent-corners: remove corners that are adjacent; " \ -"default doesn't remove.") - gboolean remove_adjacent_corners; - -#define at_doc__tangent_surround \ -N_("tangent-surround : number of points on either side of a " \ -"point to consider when computing the tangent at that point; " \ -" default is 3.") - unsigned tangent_surround; - -#define at_doc__despeckle_level \ -N_("despeckle-level : 0..20; default is no despeckling. ") - unsigned despeckle_level; - -#define at_doc__despeckle_tightness \ -N_("despeckle-tightness : 0.0..8.0; default is 2.0. ") - gfloat despeckle_tightness; - -#define at_doc__noise_removal \ -N_("noise-removal : 1.0..0.0; default is 0.99. ") - gfloat noise_removal; - -#define at_doc__centerline \ -N_("centerline: trace a character's centerline, rather than its outline. ") - gboolean centerline; - -#define at_doc__preserve_width \ -N_("preserve-width: whether to preserve linewith with centerline fitting; " \ -"default doesn't preserve.") - gboolean preserve_width; - -#define at_doc__width_weight_factor \ -N_("width-weight-factor : weight factor for fitting the linewidth.") - gfloat width_weight_factor; - }; - - struct _at_input_opts_type { - at_color *background_color; - unsigned charcode; /* Character code used only in GF input. */ - }; - - struct _at_output_opts_type { - int dpi; /* DPI is used only in MIF output. */ - }; - - struct _at_bitmap { - unsigned short height; - unsigned short width; - unsigned char *bitmap; - unsigned int np; - }; - - typedef - void (*at_msg_func) (const gchar * msg, at_msg_type msg_type, gpointer client_data); - -/* - * Autotrace initializer - */ -#define AUTOTRACE_INIT - void autotrace_init(void); - -/* - * IO Handler typedefs - */ - - typedef struct _at_bitmap_reader at_bitmap_reader; - struct _at_bitmap_reader; - - typedef struct _at_spline_writer at_spline_writer; - struct _at_spline_writer; - -/* - * Progress handler typedefs - * 0.0 <= percentage <= 1.0 - */ - typedef void (*at_progress_func) (gfloat percentage, gpointer client_data); - -/* - * Test cancel - * Return TRUE if auto-tracing should be stopped. - */ - typedef gboolean(*at_testcancel_func) (gpointer client_data); - -/* ===================================================================== * - * Functions - * ===================================================================== */ - -/* --------------------------------------------------------------------- * - * Fitting option related - * - * TODO: internal data access, copy - * --------------------------------------------------------------------- */ - at_fitting_opts_type *at_fitting_opts_new(void); - at_fitting_opts_type *at_fitting_opts_copy(at_fitting_opts_type * original); - void at_fitting_opts_free(at_fitting_opts_type * opts); - -/* Gettextize */ -#define at_fitting_opts_doc(opt) at_fitting_opts_doc_func(at_doc__##opt) -/* Don't use next function directly from clients */ - const char *at_fitting_opts_doc_func(char *string); - -/* --------------------------------------------------------------------- * - * Input option related - * - * TODO: internal data access - * --------------------------------------------------------------------- */ - at_input_opts_type *at_input_opts_new(void); - at_input_opts_type *at_input_opts_copy(at_input_opts_type * original); - void at_input_opts_free(at_input_opts_type * opts); - -/* --------------------------------------------------------------------- * - * Output option related - * - * TODO: internal data access - * --------------------------------------------------------------------- */ - at_output_opts_type *at_output_opts_new(void); - at_output_opts_type *at_output_opts_copy(at_output_opts_type * original); - void at_output_opts_free(at_output_opts_type * opts); - -/* --------------------------------------------------------------------- * - * Bitmap related - * - * TODO: internal data access - * --------------------------------------------------------------------- */ - -/* There is two way to build at_bitmap. - 1. Using input reader - Use at_bitmap_read. - at_input_get_handler_by_suffix or - at_input_get_handler will help you to get at_bitmap_reader. - 2. Allocating a bitmap and rendering an image on it by yourself - Use at_bitmap_new. - - In both case, you have to call at_bitmap_free when at_bitmap * - data are no longer needed. */ - at_bitmap *at_bitmap_read(at_bitmap_reader * reader, gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data); - at_bitmap *at_bitmap_new(unsigned short width, unsigned short height, unsigned int planes); - at_bitmap *at_bitmap_copy(const at_bitmap * src); - -/* We have to export functions that supports internal datum - access. Such functions might be useful for - at_bitmap_new user. */ - unsigned short at_bitmap_get_width(const at_bitmap * bitmap); - unsigned short at_bitmap_get_height(const at_bitmap * bitmap); - unsigned short at_bitmap_get_planes(const at_bitmap * bitmap); - void at_bitmap_get_color(const at_bitmap * bitmap, unsigned int row, unsigned int col, at_color * color); - gboolean at_bitmap_equal_color(const at_bitmap * bitmap, unsigned int row, unsigned int col, at_color * color); - void at_bitmap_free(at_bitmap * bitmap); - -/* --------------------------------------------------------------------- * - * Spline related - * - * TODO: internal data access - * --------------------------------------------------------------------- */ -/* at_splines_new - - args: - - BITMAP is modified in at_splines_new according to opts. Therefore - if you need the original bitmap image, you have to make a backup of - BITMAP with using at_bitmap_copy. - - MSG_FUNC and MSG_DATA are used to notify a client errors and - warning from autotrace. NULL is valid value for MSG_FUNC if - the client is not interested in the notifications. */ - at_splines_type *at_splines_new(at_bitmap * bitmap, at_fitting_opts_type * opts, at_msg_func msg_func, gpointer msg_data); - -/* at_splines_new_full - - args: - - NOTIFY_PROGRESS is called repeatedly inside at_splines_new_full - to notify the progress of the execution. This might be useful for - interactive applications. NOTIFY_PROGRESS is called following - format: - - NOTIFY_PROGRESS (percentage, progress_data); - - test_cancel is called repeatedly inside at_splines_new_full - to test whether the execution is canceled or not. - If test_cancel returns TRUE, execution of at_splines_new_full - is stopped as soon as possible and returns NULL. If test_cancel - returns FALSE, nothing happens. test_cancel is called following - format: - - TEST_CANCEL (testcancel_data); - - NULL is valid value for notify_progress and/or test_cancel if - you don't need to know the progress of the execution and/or - cancel the execution */ - at_splines_type *at_splines_new_full(at_bitmap * bitmap, at_fitting_opts_type * opts, at_msg_func msg_func, gpointer msg_data, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data); - - void at_splines_write(at_spline_writer * writer, FILE * writeto, gchar * file_name, at_output_opts_type * opts, at_splines_type * splines, at_msg_func msg_func, gpointer msg_data); - - void at_splines_free(at_splines_type * splines); - -/* --------------------------------------------------------------------- * - * Input related - * --------------------------------------------------------------------- */ - at_bitmap_reader *at_input_get_handler(gchar * filename); - at_bitmap_reader *at_input_get_handler_by_suffix(gchar * suffix); - - const char **at_input_list_new(void); - void at_input_list_free(const char **list); - -/* at_input_shortlist - return value: Do free by yourself */ - char *at_input_shortlist(void); - -/* --------------------------------------------------------------------- * - * Output related - * --------------------------------------------------------------------- */ - at_spline_writer *at_output_get_handler(gchar * filename); - at_spline_writer *at_output_get_handler_by_suffix(gchar * suffix); - const char **at_output_list_new(void); - void at_output_list_free(const char **list); - -/* at_output_shortlist - return value: Do free by yourself */ - char *at_output_shortlist(void); - -/* --------------------------------------------------------------------- * - * Misc - * --------------------------------------------------------------------- */ - -/* at_version - - args: - LONG_FORMAT == TRUE: "AutoTrace version x.y" - LONG_FORMAT == FALSE: "x.y" - - return value: Don't free. It is allocated statically */ - const char *at_version(gboolean long_format); - -/* at_home_site - - return value: Don't free. It is allocated statically */ - const char *at_home_site(void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* AUTOTRACE_H */ diff --git a/src/autotrace/bitmap.c b/src/autotrace/bitmap.c deleted file mode 100644 index 759a0df..0000000 --- a/src/autotrace/bitmap.c +++ /dev/null @@ -1,8 +0,0 @@ -/* bitmap.c: operations on bitmaps. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "bitmap.h" -#include "xstd.h" diff --git a/src/autotrace/bitmap.h b/src/autotrace/bitmap.h deleted file mode 100644 index 0f9b45a..0000000 --- a/src/autotrace/bitmap.h +++ /dev/null @@ -1,13 +0,0 @@ -/* bitmap.h: definition for a bitmap type. No packing is done by - default; each pixel is represented by an entire byte. Among other - things, this means the type can be used for both grayscale and binary - images. */ - -#ifndef BITMAP_H -#define BITMAP_H - -#include "autotrace.h" -#include "input.h" -#include - -#endif /* not BITMAP_H */ diff --git a/src/autotrace/color.c b/src/autotrace/color.c deleted file mode 100644 index 14a70f4..0000000 --- a/src/autotrace/color.c +++ /dev/null @@ -1,114 +0,0 @@ -/* color.c: declarations for color handling. - - Copyright (C) 2000, 2001, 2002 Martin Weber - - The author can be contacted at - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "intl.h" -#include "color.h" -#include "exception.h" - -#include -#include - -at_color *at_color_new(unsigned char r, unsigned char g, unsigned char b) -{ - at_color *color; - color = g_new(at_color, 1); - color->r = r; - color->g = g; - color->b = b; - return color; -} - -at_color *at_color_parse(const gchar * string, GError ** err) -{ - GError *local_err = NULL; - unsigned char c[6]; - int i; - - if (!string) - return NULL; - else if (string[0] == '\0') - return NULL; - else if (strlen(string) != 6) { - g_set_error(err, AT_ERROR, AT_ERROR_WRONG_COLOR_STRING, _("color string is too short: %s"), string); - return NULL; - } - - for (i = 0; i < 6; i++) { - char ch = string[i]; - if (ch >= '0' && ch <= '9') - c[i] = ch - '0'; - else if (ch >= 'A' && ch <= 'F') - c[i] = ch - 'A' + 10; - else if (ch >= 'a' && ch <= 'f') - c[i] = ch - 'a' + 10; - else { - g_set_error(&local_err, AT_ERROR, AT_ERROR_WRONG_COLOR_STRING, _("wrong char in color string: %c"), string[i]); - g_propagate_error(err, local_err); - return NULL; - } - } - return at_color_new((unsigned char)(16 * c[0] + c[1]), (unsigned char)(16 * c[2] + c[3]), (unsigned char)(16 * c[4] + c[5])); -} - -at_color *at_color_copy(const at_color * original) -{ - if (original == NULL) - return NULL; - - return at_color_new(original->r, original->g, original->b); -} - -gboolean at_color_equal(const at_color * c1, const at_color * c2) -{ - if (c1 == c2 || ((c1->r == c2->r) && (c1->g == c2->g) && (c1->b == c2->b))) - return TRUE; - - return FALSE; -} - -void at_color_set(at_color * c, unsigned char r, unsigned char g, unsigned char b) -{ - g_return_if_fail(c); - c->r = r; - c->g = g; - c->b = b; -} - -unsigned char at_color_luminance(const at_color * color) -{ - return ((unsigned char)((color->r) * 0.30 + (color->g) * 0.59 + (color->b) * 0.11 + 0.5)); -} - -void at_color_free(at_color * color) -{ - g_free(color); -} - -GType at_color_get_type(void) -{ - static GType our_type = 0; - if (our_type == 0) - our_type = g_boxed_type_register_static("AtColor", (GBoxedCopyFunc) at_color_copy, (GBoxedFreeFunc) at_color_free); - return our_type; -} diff --git a/src/autotrace/color.h b/src/autotrace/color.h deleted file mode 100644 index 88651db..0000000 --- a/src/autotrace/color.h +++ /dev/null @@ -1,53 +0,0 @@ -/* color.h: declarations for color handling. - - Copyright (C) 2000, 2001, 2002 Martin Weber - - The author can be contacted at - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef AT_COLOR_H -#define AT_COLOR_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -typedef struct _at_color at_color; -struct _at_color { - guint8 r; - guint8 g; - guint8 b; -}; - -at_color *at_color_new(guint8 r, guint8 g, guint8 b); -at_color *at_color_parse(const gchar * string, GError ** err); -at_color *at_color_copy(const at_color * original); -gboolean at_color_equal(const at_color * c1, const at_color * c2); -void at_color_set(at_color * c1, guint8 r, guint8 g, guint8 b); -/* RGB to grayscale */ -unsigned char at_color_luminance(const at_color * color); -void at_color_free(at_color * color); - -GType at_color_get_type(void); -#define AT_TYPE_COLOR (at_color_get_type ()) - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* not AT_COLOR_H */ diff --git a/src/autotrace/curve.c b/src/autotrace/curve.c deleted file mode 100644 index 4ee2603..0000000 --- a/src/autotrace/curve.c +++ /dev/null @@ -1,255 +0,0 @@ -/* curve.c: operations on the lists of pixels and lists of curves. - - The code was partially derived from limn. - - Copyright (C) 1992 Free Software Foundation, Inc. - - 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, 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "logreport.h" -#include "curve.h" -#include "xstd.h" - -static at_real_coord int_to_real_coord(at_coord); - -/* Return an entirely empty curve. */ - -curve_type new_curve(void) -{ - curve_type curve; - XMALLOC(curve, sizeof(struct curve)); - curve->point_list = NULL; - CURVE_LENGTH(curve) = 0; - CURVE_CYCLIC(curve) = FALSE; - CURVE_START_TANGENT(curve) = CURVE_END_TANGENT(curve) = NULL; - PREVIOUS_CURVE(curve) = NEXT_CURVE(curve) = NULL; - - return curve; -} - -/* Don't copy the points or tangents, but copy everything else. */ - -curve_type copy_most_of_curve(curve_type old_curve) -{ - curve_type curve = new_curve(); - - CURVE_CYCLIC(curve) = CURVE_CYCLIC(old_curve); - PREVIOUS_CURVE(curve) = PREVIOUS_CURVE(old_curve); - NEXT_CURVE(curve) = NEXT_CURVE(old_curve); - - return curve; -} - -/* The length of CURVE will be zero if we ended up not being able to fit - it (which in turn implies a problem elsewhere in the program, but at - any rate, we shouldn't try here to free the nonexistent curve). */ - -void free_curve(curve_type curve) -{ - if (CURVE_LENGTH(curve) > 0) - free(curve->point_list); - if (CURVE_START_TANGENT(curve)) - free(CURVE_START_TANGENT(curve)); - if (CURVE_END_TANGENT(curve)) - free(CURVE_END_TANGENT(curve)); -} - -void append_pixel(curve_type curve, at_coord coord) -{ - append_point(curve, int_to_real_coord(coord)); -} - -void append_point(curve_type curve, at_real_coord coord) -{ - CURVE_LENGTH(curve)++; - XREALLOC(curve->point_list, CURVE_LENGTH(curve) * sizeof(point_type)); - LAST_CURVE_POINT(curve) = coord; - /* The t value does not need to be set. */ -} - -/* Print a curve in human-readable form. It turns out we never care - about most of the points on the curve, and so it is pointless to - print them all out umpteen times. What matters is that we have some - from the end and some from the beginning. */ - -#define NUM_TO_PRINT 3 - -#define LOG_CURVE_POINT(c, p, print_t) \ - do \ - { \ - LOG ("(%.3f,%.3f)", CURVE_POINT (c, p).x, CURVE_POINT (c, p).y); \ - if (print_t) \ - LOG ("/%.2f", CURVE_T (c, p)); \ - } \ - while (0) - -void log_curve(curve_type curve, gboolean print_t) -{ - unsigned this_point; - - LOG("curve id = %lx:\n", (unsigned long)(uintptr_t)curve); - LOG(" length = %u.\n", CURVE_LENGTH(curve)); - if (CURVE_CYCLIC(curve)) - LOG(" cyclic.\n"); - - /* It should suffice to check just one of the tangents for being null - -- either they both should be, or neither should be. */ - if (CURVE_START_TANGENT(curve) != NULL) - LOG(" tangents = (%.3f,%.3f) & (%.3f,%.3f).\n", CURVE_START_TANGENT(curve)->dx, CURVE_START_TANGENT(curve)->dy, CURVE_END_TANGENT(curve)->dx, CURVE_END_TANGENT(curve)->dy); - - LOG(" "); - - /* If the curve is short enough, don't use ellipses. */ - if (CURVE_LENGTH(curve) <= NUM_TO_PRINT * 2) { - for (this_point = 0; this_point < CURVE_LENGTH(curve); this_point++) { - LOG_CURVE_POINT(curve, this_point, print_t); - LOG(" "); - - if (this_point != CURVE_LENGTH(curve) - 1 && (this_point + 1) % NUM_TO_PRINT == 0) - LOG("\n "); - } - } else { - for (this_point = 0; this_point < NUM_TO_PRINT && this_point < CURVE_LENGTH(curve); this_point++) { - LOG_CURVE_POINT(curve, this_point, print_t); - LOG(" "); - } - - LOG("...\n ..."); - - for (this_point = CURVE_LENGTH(curve) - NUM_TO_PRINT; this_point < CURVE_LENGTH(curve); this_point++) { - LOG(" "); - LOG_CURVE_POINT(curve, this_point, print_t); - } - } - - LOG(".\n"); -} - -/* Like `log_curve', but write the whole thing. */ - -void log_entire_curve(curve_type curve) -{ - unsigned this_point; - - LOG("curve id = %lx:\n", (unsigned long)(uintptr_t)curve); - LOG(" length = %u.\n", CURVE_LENGTH(curve)); - if (CURVE_CYCLIC(curve)) - LOG(" cyclic.\n"); - - /* It should suffice to check just one of the tangents for being null - -- either they both should be, or neither should be. */ - if (CURVE_START_TANGENT(curve) != NULL) - LOG(" tangents = (%.3f,%.3f) & (%.3f,%.3f).\n", CURVE_START_TANGENT(curve)->dx, CURVE_START_TANGENT(curve)->dy, CURVE_END_TANGENT(curve)->dx, CURVE_END_TANGENT(curve)->dy); - - LOG(" "); - - for (this_point = 0; this_point < CURVE_LENGTH(curve); this_point++) { - LOG(" "); - LOG_CURVE_POINT(curve, this_point, TRUE); - /* Compiler warning `Condition is always true' can be ignored */ - } - - LOG(".\n"); -} - -/* Return an initialized but empty curve list. */ - -curve_list_type new_curve_list(void) -{ - curve_list_type curve_list; - - curve_list.length = 0; - curve_list.data = NULL; - - return curve_list; -} - -/* Free a curve list and all the curves it contains. */ - -void free_curve_list(curve_list_type * curve_list) -{ - unsigned this_curve; - - for (this_curve = 0; this_curve < curve_list->length; this_curve++) { - free_curve(curve_list->data[this_curve]); - free(curve_list->data[this_curve]); - } - - /* If the character was empty, it won't have any curves. */ - free(curve_list->data); -} - -/* Add an element to a curve list. */ - -void append_curve(curve_list_type * curve_list, curve_type curve) -{ - curve_list->length++; - XREALLOC(curve_list->data, curve_list->length * sizeof(curve_type)); - curve_list->data[curve_list->length - 1] = curve; -} - -/* Return an initialized but empty curve list array. */ - -curve_list_array_type new_curve_list_array(void) -{ - curve_list_array_type curve_list_array; - - CURVE_LIST_ARRAY_LENGTH(curve_list_array) = 0; - curve_list_array.data = NULL; - - return curve_list_array; -} - -/* Free a curve list array and all the curve lists it contains. */ - -void free_curve_list_array(curve_list_array_type * curve_list_array, at_progress_func notify_progress, gpointer client_data) -{ - unsigned this_list; - - for (this_list = 0; this_list < CURVE_LIST_ARRAY_LENGTH(*curve_list_array); this_list++) { - if (notify_progress) - notify_progress(((gfloat) this_list) / (CURVE_LIST_ARRAY_LENGTH(*curve_list_array) * (gfloat) 3.0) + (gfloat) 0.666, client_data); - free_curve_list(&CURVE_LIST_ARRAY_ELT(*curve_list_array, this_list)); - } - - /* If the character was empty, it won't have any curves. */ - free(curve_list_array->data); -} - -/* Add an element to a curve list array. */ - -void append_curve_list(curve_list_array_type * curve_list_array, curve_list_type curve_list) -{ - CURVE_LIST_ARRAY_LENGTH(*curve_list_array)++; - XREALLOC(curve_list_array->data, CURVE_LIST_ARRAY_LENGTH(*curve_list_array) * sizeof(curve_list_type)); - LAST_CURVE_LIST_ARRAY_ELT(*curve_list_array) = curve_list; -} - -/* Turn an integer point into a real one. */ - -static at_real_coord int_to_real_coord(at_coord int_coord) -{ - at_real_coord real_coord; - - real_coord.x = int_coord.x; - real_coord.y = int_coord.y; - real_coord.z = 0.0; - - return real_coord; -} diff --git a/src/autotrace/curve.h b/src/autotrace/curve.h deleted file mode 100644 index 1efcf4e..0000000 --- a/src/autotrace/curve.h +++ /dev/null @@ -1,131 +0,0 @@ -/* curve.h: data structures for the conversion from pixels to splines. */ - -#ifndef CURVE_H -#define CURVE_H - -#include "autotrace.h" -#include "vector.h" - -/* We are simultaneously manipulating two different representations of - the same outline: one based on (x,y) positions in the plane, and one - based on parametric splines. (We are trying to match the latter to - the former.) Although the original (x,y)'s are pixel positions, - i.e., integers, after filtering they are reals. */ - -typedef struct { - at_real_coord coord; - gfloat t; -} point_type; - -/* It turns out to be convenient to break the list of all the pixels in - the outline into sublists, divided at ``corners''. Then each of the - sublists is treated independently. Each of these sublists is a `curve'. */ - -struct curve { - point_type *point_list; - unsigned length; - gboolean cyclic; - vector_type *start_tangent; - vector_type *end_tangent; - struct curve *previous; - struct curve *next; -}; - -typedef struct curve *curve_type; - -/* Get at the coordinates and the t values. */ -#define CURVE_POINT(c, n) ((c)->point_list[n].coord) -#define LAST_CURVE_POINT(c) ((c)->point_list[(c)->length-1].coord) -#define CURVE_T(c, n) ((c)->point_list[n].t) -#define LAST_CURVE_T(c) ((c)->point_list[(c)->length-1].t) - -/* This is the length of `point_list'. */ -#define CURVE_LENGTH(c) ((c)->length) - -/* A curve is ``cyclic'' if it didn't have any corners, after all, so - the last point is adjacent to the first. */ -#define CURVE_CYCLIC(c) ((c)->cyclic) - -/* If the curve is cyclic, the next and previous points should wrap - around; otherwise, if we get to the end, we return CURVE_LENGTH and - -1, respectively. */ -#define CURVE_NEXT(c, n) \ - ((n) + 1 >= CURVE_LENGTH (c) \ - ? CURVE_CYCLIC (c) ? ((n) + 1) % CURVE_LENGTH (c) : CURVE_LENGTH (c) \ - : (n) + 1) -#define CURVE_PREV(c, n) \ - ((signed int) (n) - 1 < 0 \ - ? CURVE_CYCLIC (c) ? (signed int) CURVE_LENGTH (c) + (signed int) (n) - 1 : -1\ - : (signed int) (n) - 1) - -/* The tangents at the endpoints are computed using the neighboring curves. */ -#define CURVE_START_TANGENT(c) ((c)->start_tangent) -#define CURVE_END_TANGENT(c) ((c)->end_tangent) -#define PREVIOUS_CURVE(c) ((c)->previous) -#define NEXT_CURVE(c) ((c)->next) - -/* Return an entirely empty curve. */ -extern curve_type new_curve(void); - -/* Return a curve the same as C, except without any points. */ -extern curve_type copy_most_of_curve(curve_type c); - -/* Free the memory C uses. */ -extern void free_curve(curve_type c); - -/* Append the point P to the end of C's list. */ -extern void append_pixel(curve_type c, at_coord p); - -/* Like `append_pixel', for a point in real coordinates. */ -extern void append_point(curve_type c, at_real_coord p); - -/* Write some or all, respectively, of the curve C in human-readable - form to the log file, if logging is enabled. */ -extern void log_curve(curve_type c, gboolean print_t); -extern void log_entire_curve(curve_type c); - -/* Display the curve C online, if displaying is enabled. */ -extern void display_curve(curve_type); - -/* So, an outline is a list of curves. */ -typedef struct { - curve_type *data; - unsigned length; - gboolean clockwise; - gboolean open; -} curve_list_type; - -/* Number of curves in the list. */ -#define CURVE_LIST_LENGTH(c_l) ((c_l).length) - -/* Access the individual curves. */ -#define CURVE_LIST_ELT(c_l, n) ((c_l).data[n]) -#define LAST_CURVE_LIST_ELT(c_l) ((c_l).data[CURVE_LIST_LENGTH (c_l) - 1]) - -/* Says whether the outline that this curve list represents moves - clockwise or counterclockwise. */ -#define CURVE_LIST_CLOCKWISE(c_l) ((c_l).clockwise) - -extern curve_list_type new_curve_list(void); -extern void free_curve_list(curve_list_type *); -extern void append_curve(curve_list_type *, curve_type); - -/* And a character is a list of outlines. I named this - `curve_list_array_type' because `curve_list_list_type' seemed pretty - monstrous. */ -typedef struct { - curve_list_type *data; - unsigned length; -} curve_list_array_type; - -/* Turns out we can use the same definitions for lists of lists as for - just lists. But we define the usual names, just in case. */ -#define CURVE_LIST_ARRAY_LENGTH CURVE_LIST_LENGTH -#define CURVE_LIST_ARRAY_ELT CURVE_LIST_ELT -#define LAST_CURVE_LIST_ARRAY_ELT LAST_CURVE_LIST_ELT - -extern curve_list_array_type new_curve_list_array(void); -extern void free_curve_list_array(curve_list_array_type *, at_progress_func, gpointer); -extern void append_curve_list(curve_list_array_type *, curve_list_type); - -#endif /* not CURVE_H */ diff --git a/src/autotrace/despeckle.c b/src/autotrace/despeckle.c deleted file mode 100644 index 3ab66ac..0000000 --- a/src/autotrace/despeckle.c +++ /dev/null @@ -1,710 +0,0 @@ -/* despeckle.c: Bitmap despeckler - - Copyright (C) 2001 David A. Bartold / Martin Weber - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include "xstd.h" -#include "logreport.h" -#include "types.h" -#include "bitmap.h" -#include "despeckle.h" - -/* Calculate Error - compute the error between two colors - * - * Input parameters: - * Two 24 bit RGB colors - * - * Returns: - * The squared error between the two colors - */ - -static int calc_error(unsigned char *color1, unsigned char *color2) -{ - int the_error; - int temp; - - temp = color1[0] - color2[0]; - the_error = temp * temp; - temp = color1[1] - color2[1]; - the_error += temp * temp; - temp = color1[2] - color2[2]; - the_error += temp * temp; - - return the_error; -} - -/* Calculate Error - compute the error between two colors - * - * Input parameters: - * Two 8 bit gray scale colors - * - * Returns: - * The squared error between the two colors - */ - -static int calc_error_8(unsigned char *color1, unsigned char *color2) -{ - int the_error; - - the_error = abs(color1[0] - color2[0]); - - return the_error; -} - -/* Find Size - Find the number of adjacent pixels of the same color - * - * Input Parameters: - * An 24 bit image, the current location inside the image, and the palette - * index of the color we are looking for - * - * Modified Parameters: - * A mask array used to prevent backtracking over already counted pixels - * - * Returns: - * Number of adjacent pixels found having the same color - */ - -static int find_size( /* in */ unsigned char *index, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - int count; - int x1, x2; - - if (y < 0 || y >= height || mask[y * width + x] == 1 || bitmap[3 * (y * width + x)] != index[0] || bitmap[3 * (y * width + x) + 1] != index[1] || bitmap[3 * (y * width + x) + 2] != index[2]) - return 0; - - for (x1 = x; x1 >= 0 && bitmap[3 * (y * width + x1)] == index[0] && bitmap[3 * (y * width + x1) + 1] == index[1] && bitmap[3 * (y * width + x1) + 2] == index[2] && mask[y * width + x] != 1; x1--) ; - x1++; - - for (x2 = x; x2 < width && bitmap[3 * (y * width + x2)] == index[0] && bitmap[3 * (y * width + x2) + 1] == index[1] && bitmap[3 * (y * width + x2) + 2] == index[2] && mask[y * width + x] != 1; x2++) ; - x2--; - - count = x2 - x1 + 1; - for (x = x1; x <= x2; x++) - mask[y * width + x] = 1; - - for (x = x1; x <= x2; x++) { - count += find_size(index, x, y - 1, width, height, bitmap, mask); - count += find_size(index, x, y + 1, width, height, bitmap, mask); - } - - return count; -} - -/* Find Size - Find the number of adjacent pixels of the same color - * - * Input Parameters: - * An 8 bit image, the current location inside the image, and the palette - * index of the color we are looking for - * - * Modified Parameters: - * A mask array used to prevent backtracking over already counted pixels - * - * Returns: - * Number of adjacent pixels found having the same color - */ - -static int find_size_8( /* in */ unsigned char *index, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - int count; - int x1, x2; - - if (y < 0 || y >= height || mask[y * width + x] == 1 || bitmap[(y * width + x)] != index[0]) - return 0; - - for (x1 = x; x1 >= 0 && bitmap[(y * width + x1)] == index[0] && mask[y * width + x] != 1; x1--) ; - x1++; - - for (x2 = x; x2 < width && bitmap[(y * width + x2)] == index[0] && mask[y * width + x] != 1; x2++) ; - x2--; - - count = x2 - x1 + 1; - for (x = x1; x <= x2; x++) - mask[y * width + x] = 1; - - for (x = x1; x <= x2; x++) { - count += find_size_8(index, x, y - 1, width, height, bitmap, mask); - count += find_size_8(index, x, y + 1, width, height, bitmap, mask); - } - - return count; -} - -/* Find Most Similar Neighbor - Given a position in an 24 bit bitmap and a color - * index, traverse over a blob of adjacent pixels having the same value. - * Return the color index of the neighbor pixel that has the most similar - * color. - * - * Input parameters: - * 24 bit bitmap, the current location inside the image, - * and the color index of the blob - * - * Modified parameters: - * Mask used to prevent backtracking - * - * Output parameters: - * Closest index != index and the error between the two colors squared - */ - -static void find_most_similar_neighbor( /* in */ unsigned char *index, - /* in/out */ unsigned char **closest_index, - /* in/out */ int *error_amt, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - int x1, x2; - int temp_error; - unsigned char *value, *temp; - - if (y < 0 || y >= height || mask[y * width + x] == 2) - return; - - temp = &bitmap[3 * (y * width + x)]; - - assert(closest_index != NULL); - - if (temp[0] != index[0] || temp[1] != index[1] || temp[2] != index[2]) { - value = temp; - - temp_error = calc_error(index, value); - - if (*closest_index == NULL || temp_error < *error_amt) - *closest_index = value, *error_amt = temp_error; - - return; - } - - for (x1 = x; x1 >= 0 && bitmap[3 * (y * width + x1)] == index[0] && bitmap[3 * (y * width + x1) + 1] == index[1] && bitmap[3 * (y * width + x1) + 2] == index[2]; x1--) ; - x1++; - - for (x2 = x; x2 < width && bitmap[3 * (y * width + x2)] == index[0] && bitmap[3 * (y * width + x2) + 1] == index[1] && bitmap[3 * (y * width + x2) + 2] == index[2]; x2++) ; - x2--; - - if (x1 > 0) { - value = &bitmap[3 * (y * width + x1 - 1)]; - - temp_error = calc_error(index, value); - - if (*closest_index == NULL || temp_error < *error_amt) - *closest_index = value, *error_amt = temp_error; - } - - if (x2 < width - 1) { - value = &bitmap[3 * (y * width + x2 + 1)]; - - temp_error = calc_error(index, value); - - if (*closest_index == NULL || temp_error < *error_amt) - *closest_index = value, *error_amt = temp_error; - } - - for (x = x1; x <= x2; x++) - mask[y * width + x] = 2; - - for (x = x1; x <= x2; x++) { - find_most_similar_neighbor(index, closest_index, error_amt, x, y - 1, width, height, bitmap, mask); - find_most_similar_neighbor(index, closest_index, error_amt, x, y + 1, width, height, bitmap, mask); - } -} - -/* Find Most Similar Neighbor - Given a position in an 8 bit bitmap and a color - * index, traverse over a blob of adjacent pixels having the same value. - * Return the color index of the neighbor pixel that has the most similar - * color. - * - * Input parameters: - * 8 bit bitmap, the current location inside the image, - * and the color index of the blob - * - * Modified parameters: - * Mask used to prevent backtracking - * - * Output parameters: - * Closest index != index and the error between the two colors squared - */ - -static void find_most_similar_neighbor_8( /* in */ unsigned char *index, - /* in/out */ unsigned char **closest_index, - /* in/out */ int *error_amt, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - int x1, x2; - int temp_error; - unsigned char *value, *temp; - - if (y < 0 || y >= height || mask[y * width + x] == 2) - return; - - temp = &bitmap[(y * width + x)]; - - assert(closest_index != NULL); - - if (temp[0] != index[0]) { - value = temp; - - temp_error = calc_error_8(index, value); - - if (*closest_index == NULL || temp_error < *error_amt) - *closest_index = value, *error_amt = temp_error; - - return; - } - - for (x1 = x; x1 >= 0 && bitmap[(y * width + x1)] == index[0]; x1--) ; - x1++; - - for (x2 = x; x2 < width && bitmap[(y * width + x2)] == index[0]; x2++) ; - x2--; - - if (x1 > 0) { - value = &bitmap[(y * width + x1 - 1)]; - - temp_error = calc_error_8(index, value); - - if (*closest_index == NULL || temp_error < *error_amt) - *closest_index = value, *error_amt = temp_error; - } - - if (x2 < width - 1) { - value = &bitmap[(y * width + x2 + 1)]; - - temp_error = calc_error_8(index, value); - - if (*closest_index == NULL || temp_error < *error_amt) - *closest_index = value, *error_amt = temp_error; - } - - for (x = x1; x <= x2; x++) - mask[y * width + x] = 2; - - for (x = x1; x <= x2; x++) { - find_most_similar_neighbor_8(index, closest_index, error_amt, x, y - 1, width, height, bitmap, mask); - find_most_similar_neighbor_8(index, closest_index, error_amt, x, y + 1, width, height, bitmap, mask); - } -} - -/* Fill - change the color of a blob - * - * Input parameters: - * The new color - * - * Modified parameters: - * 24 bit pixbuf and its mask (used to prevent backtracking) - */ - -static void fill( /* in */ unsigned char *to_index, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in/out */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - int x1, x2; - - if (y < 0 || y >= height || mask[y * width + x] != 2) - return; - - for (x1 = x; x1 >= 0 && mask[y * width + x1] == 2; x1--) ; - x1++; - for (x2 = x; x2 < width && mask[y * width + x2] == 2; x2++) ; - x2--; - - assert(x1 >= 0 && x2 < width); - - for (x = x1; x <= x2; x++) { - bitmap[3 * (y * width + x)] = to_index[0]; - bitmap[3 * (y * width + x) + 1] = to_index[1]; - bitmap[3 * (y * width + x) + 2] = to_index[2]; - mask[y * width + x] = 3; - } - - for (x = x1; x <= x2; x++) { - fill(to_index, x, y - 1, width, height, bitmap, mask); - fill(to_index, x, y + 1, width, height, bitmap, mask); - } -} - -/* Fill - change the color of a blob - * - * Input parameters: - * The new color - * - * Modified parameters: - * 8 bit pixbuf and its mask (used to prevent backtracking) - */ - -static void fill_8( /* in */ unsigned char *to_index, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in/out */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - int x1, x2; - - if (y < 0 || y >= height || mask[y * width + x] != 2) - return; - - for (x1 = x; x1 >= 0 && mask[y * width + x1] == 2; x1--) ; - x1++; - for (x2 = x; x2 < width && mask[y * width + x2] == 2; x2++) ; - x2--; - - assert(x1 >= 0 && x2 < width); - - for (x = x1; x <= x2; x++) { - bitmap[(y * width + x)] = to_index[0]; - mask[y * width + x] = 3; - } - - for (x = x1; x <= x2; x++) { - fill_8(to_index, x, y - 1, width, height, bitmap, mask); - fill_8(to_index, x, y + 1, width, height, bitmap, mask); - } -} - -/* Ignore - blob is big enough, mask it off - * - * Modified parameters: - * its mask (used to prevent backtracking) - */ - -static void ignore( /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in/out */ unsigned char *mask) -{ - int x1, x2; - - if (y < 0 || y >= height || mask[y * width + x] != 1) - return; - - for (x1 = x; x1 >= 0 && mask[y * width + x1] == 1; x1--) ; - x1++; - for (x2 = x; x2 < width && mask[y * width + x2] == 1; x2++) ; - x2--; - - assert(x1 >= 0 && x2 < width); - - for (x = x1; x <= x2; x++) - mask[y * width + x] = 3; - - for (x = x1; x <= x2; x++) { - ignore(x, y - 1, width, height, mask); - ignore(x, y + 1, width, height, mask); - } -} - -/* Recolor - conditionally change a feature's color to the closest color of all - * neighboring pixels - * - * Input parameters: - * The color palette, current blob size, and adaptive tightness - * - * Adaptive Tightness: (integer 1 to 256) - * 1 = really tight - * 256 = turn off the feature - * - * Modified parameters: - * 24 bit pixbuf and its mask (used to prevent backtracking) - * - * Returns: - * TRUE - feature was recolored, thus coalesced - * FALSE - feature wasn't recolored - */ - -static gboolean recolor( /* in */ double adaptive_tightness, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in/out */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - unsigned char *index, *to_index; - int error_amt, max_error; - - index = &bitmap[3 * (y * width + x)]; - to_index = NULL; - error_amt = 0; - max_error = (int)(3.0 * adaptive_tightness * adaptive_tightness); - - find_most_similar_neighbor(index, &to_index, &error_amt, x, y, width, height, bitmap, mask); - - /* This condition only fails if the bitmap is all the same color */ - if (to_index != NULL) { - /* - * If the difference between the two colors is too great, - * don't coalesce the feature with its neighbor(s). This prevents a - * color from turning into its complement. - */ - - if (calc_error(index, to_index) > max_error) - fill(index, x, y, width, height, bitmap, mask); - else { - fill(to_index, x, y, width, height, bitmap, mask); - - return TRUE; - } - } - - return FALSE; -} - -/* Recolor - conditionally change a feature's color to the closest color of all - * neighboring pixels - * - * Input parameters: - * The color palette, current blob size, and adaptive tightness - * - * Adaptive Tightness: (integer 1 to 256) - * 1 = really tight - * 256 = turn off the feature - * - * Modified parameters: - * 8 bit pixbuf and its mask (used to prevent backtracking) - * - * Returns: - * TRUE - feature was recolored, thus coalesced - * FALSE - feature wasn't recolored - */ - -static gboolean recolor_8( /* in */ double adaptive_tightness, - /* in */ int x, - /* in */ int y, - /* in */ int width, - /* in */ int height, - /* in/out */ unsigned char *bitmap, - /* in/out */ unsigned char *mask) -{ - unsigned char *index, *to_index; - int error_amt; - - index = &bitmap[(y * width + x)]; - to_index = NULL; - error_amt = 0; - - find_most_similar_neighbor_8(index, &to_index, &error_amt, x, y, width, height, bitmap, mask); - - /* This condition only fails if the bitmap is all the same color */ - if (to_index != NULL) { - /* - * If the difference between the two colors is too great, - * don't coalesce the feature with its neighbor(s). This prevents a - * color from turning into its complement. - */ - - if (calc_error_8(index, to_index) > adaptive_tightness) - fill_8(index, x, y, width, height, bitmap, mask); - else { - fill_8(to_index, x, y, width, height, bitmap, mask); - - return TRUE; - } - } - - return FALSE; -} - -/* Despeckle Iteration - Despeckle all regions smaller than cur_size pixels - * - * Input Parameters: - * Current blob size, maximum blob size - * for all iterations (used to selectively recolor blobs), adaptive - * tightness and noise removal - * - * Modified Parameters: - * The 24 bit pixbuf is despeckled - */ - -static void despeckle_iteration( /* in */ int level, - /* in */ double adaptive_tightness, - /* in */ double noise_max, - /* in */ int width, - /* in */ int height, - /* in/out */ unsigned char *bitmap) -{ - unsigned char *mask; - int x, y; - int current_size; - int tightness; - - /* Size doubles each iteration level, so current_size = 2^level */ - current_size = 1 << level; - tightness = (int)(noise_max / (1.0 + adaptive_tightness * level)); - - mask = (unsigned char *)calloc(width * height, sizeof(unsigned char)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - if (mask[y * width + x] == 0) { - int size; - - size = find_size(&bitmap[3 * (y * width + x)], x, y, width, height, bitmap, mask); - - assert(size > 0); - - if (size < current_size) { - if (recolor(tightness, x, y, width, height, bitmap, mask)) - x--; - } else - ignore(x, y, width, height, mask); - } - } - } - - free(mask); -} - -/* Despeckle Iteration - Despeckle all regions smaller than cur_size pixels - * - * Input Parameters: - * Current blob size, maximum blob size - * for all iterations (used to selectively recolor blobs), adaptive - * tightness and noise removal - * - * Modified Parameters: - * The 8 bit pixbuf is despeckled - */ - -static void despeckle_iteration_8( /* in */ int level, - /* in */ double adaptive_tightness, - /* in */ double noise_max, - /* in */ int width, - /* in */ int height, - /* in/out */ unsigned char *bitmap) -{ - unsigned char *mask; - int x, y; - int current_size; - int tightness; - - /* Size doubles each iteration level, so current_size = 2^level */ - current_size = 1 << level; - tightness = (int)(noise_max / (1.0 + adaptive_tightness * level)); - - mask = (unsigned char *)calloc(width * height, sizeof(unsigned char)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - if (mask[y * width + x] == 0) { - int size; - - size = find_size_8(&bitmap[(y * width + x)], x, y, width, height, bitmap, mask); - - assert(size > 0); - - if (size < current_size) { - if (recolor_8(tightness, x, y, width, height, bitmap, mask)) - x--; - } else - ignore(x, y, width, height, mask); - } - } - } - - free(mask); -} - -/* Despeckle - Despeckle a 8 or 24 bit image - * - * Input Parameters: - * Adaptive feature coalescing value, the despeckling level and noise removal - * - * Despeckling level (level): Integer from 0 to ~20 - * 0 = perform no despeckling - * An increase of the despeckling level by one doubles the size of features. - * The Maximum value must be smaller then the logarithm base two of the number - * of pixels. - * - * Feature coalescing (tightness): Real from 0.0 to ~8.0 - * 0 = Turn it off (whites may turn black and vice versa, etc) - * 3 = Good middle value - * 8 = Really tight - * - * Noise removal (noise_removal): Real from 1.0 to 0.0 - * 1 = Maximum noise removal - * You should always use the highest value, only if certain parts of the image - * disappear you should lower it. - * - * Modified Parameters: - * The bitmap is despeckled. - */ - -void despeckle( /* in/out */ at_bitmap * bitmap, - /* in */ int level, - /* in */ gfloat tightness, - /* in */ gfloat noise_removal, - /* exception handling */ at_exception_type * excep) -{ - int i, planes, max_level; - short width, height; - unsigned char *bits; - double noise_max, adaptive_tightness; - - planes = AT_BITMAP_PLANES(bitmap); - noise_max = noise_removal * 255.0; - width = AT_BITMAP_WIDTH(bitmap); - height = AT_BITMAP_HEIGHT(bitmap); - bits = AT_BITMAP_BITS(bitmap); - max_level = (int)(log(width * height) / log(2.0) - 0.5); - if (level > max_level) - level = max_level; - adaptive_tightness = (noise_removal * (1.0 + tightness * level) - 1.0) / level; - - if (planes == 3) { - for (i = 0; i < level; i++) - despeckle_iteration(i, adaptive_tightness, noise_max, width, height, bits); - } else if (planes == 1) { - for (i = 0; i < level; i++) - despeckle_iteration_8(i, adaptive_tightness, noise_max, width, height, bits); - } else { - LOG("despeckle: %u-plane images are not supported", planes); - at_exception_fatal(excep, "despeckle: wrong plane images are passed"); - return; - } - -} diff --git a/src/autotrace/despeckle.h b/src/autotrace/despeckle.h deleted file mode 100644 index 3b206e7..0000000 --- a/src/autotrace/despeckle.h +++ /dev/null @@ -1,54 +0,0 @@ -/* despeckle.h: Bitmap despeckler for AutoTrace - - Copyright (C) 2001 David A. Bartold / Martin Weber - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifndef DESPECKLE_H -#define DESPECKLE_H - -#include "types.h" -#include "bitmap.h" -#include "exception.h" - -/* Despeckle - Despeckle a 8 or 24 bit image - * - * Input Parameters: - * Adaptive feature coalescing value, the despeckling level and noise removal - * - * Despeckling level (level): Integer from 0 to ~20 - * 0 = perform no despeckling - * An increase of the despeckling level by one doubles the size of features. - * The Maximum value must be smaller then the logarithm base two of the number - * of pixels. - * - * Feature coalescing (tightness): Real from 0.0 to ~8.0 - * 0 = Turn it off (whites may turn black and vice versa, etc) - * 3 = Good middle value - * 8 = Really tight - * - * Noise removal (noise_removal): Real from 1.0 to 0.0 - * 1 = Maximum noise removal - * You should always use the highest value, only if certain parts of the image - * disappear you should lower it. - * - * Modified Parameters: - * The bitmap is despeckled. - */ - -extern void despeckle(at_bitmap * bitmap, int level, gfloat tightness, gfloat noise_removal, at_exception_type * exp); - -#endif /* not DESPECKLE_H */ diff --git a/src/autotrace/epsilon-equal.c b/src/autotrace/epsilon-equal.c deleted file mode 100644 index 8d26886..0000000 --- a/src/autotrace/epsilon-equal.c +++ /dev/null @@ -1,22 +0,0 @@ -/* epsilon-equal.c: define a error resist compare. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "types.h" -#include "epsilon-equal.h" -#include - -/* Numerical errors sometimes make a floating point number just slightly - larger or smaller than its TRUE value. When it matters, we need to - compare with some tolerance, REAL_EPSILON, defined in kbase.h. */ - -gboolean epsilon_equal(gfloat v1, gfloat v2) -{ - if (v1 == v2 /* Usually they'll be exactly equal, anyway. */ - || fabs(v1 - v2) <= REAL_EPSILON) - return TRUE; - - return FALSE; -} diff --git a/src/autotrace/epsilon-equal.h b/src/autotrace/epsilon-equal.h deleted file mode 100644 index 8b547ba..0000000 --- a/src/autotrace/epsilon-equal.h +++ /dev/null @@ -1,17 +0,0 @@ -/* epsilon-equal.h: define an error resist compare. */ - -#ifndef EPSILON_EQUAL_H -#define EPSILON_EQUAL_H - -#include "types.h" - -/* Says whether V1 and V2 are within REAL_EPSILON of each other. - Fixed-point arithmetic would be better, to guarantee machine - independence, but it's so much more painful to work with. The value - here is smaller than can be represented in either a `fix_word' or a - `scaled_num', so more precision than this will be lost when we - output, anyway. */ -extern gboolean epsilon_equal(gfloat v1, gfloat v2); -#define REAL_EPSILON 0.00001 - -#endif /* not EPSILON_EQUAL_H */ diff --git a/src/autotrace/exception.c b/src/autotrace/exception.c deleted file mode 100644 index c98777e..0000000 --- a/src/autotrace/exception.c +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "exception.h" - -at_exception_type at_exception_new(at_msg_func client_func, gpointer client_data) -{ - at_exception_type e; - e.msg_type = AT_MSG_NOT_SET; - e.client_func = client_func; - e.client_data = client_data; - return e; -} - -gboolean at_exception_got_fatal(at_exception_type * exception) -{ - return (exception->msg_type == AT_MSG_FATAL) ? TRUE : FALSE; -} - -void at_exception_fatal(at_exception_type * exception, const gchar * message) -{ - if (!exception) - return; - exception->msg_type = AT_MSG_FATAL; - if (exception->client_func) { - exception->client_func(message, AT_MSG_FATAL, exception->client_data); - } -} - -void at_exception_warning(at_exception_type * exception, const gchar * message) -{ - if (!exception) - return; - exception->msg_type = AT_MSG_WARNING; - if (exception->client_func) { - exception->client_func(message, AT_MSG_WARNING, exception->client_data); - } -} - -GQuark at_error_quark(void) -{ - static GQuark q = 0; - if (q == 0) - q = g_quark_from_static_string("at-error-quark"); - return q; -} diff --git a/src/autotrace/exception.h b/src/autotrace/exception.h deleted file mode 100644 index 793e879..0000000 --- a/src/autotrace/exception.h +++ /dev/null @@ -1,39 +0,0 @@ -/* exception.h: facility to handle error in autotrace */ - -#ifndef AT_EXCEPTION_H -#define AT_EXCEPTION_H - -#include "autotrace.h" -#include "types.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Protocol: - If a function raises a FATAL(including propagation), - the function must release resources allocated by the - function itself. */ - typedef struct _at_exception_type at_exception_type; - struct _at_exception_type { - at_msg_type msg_type; - at_msg_func client_func; - gpointer client_data; - }; - - at_exception_type at_exception_new(at_msg_func client_func, gpointer client_data); - gboolean at_exception_got_fatal(at_exception_type * exception); - void at_exception_fatal(at_exception_type * exception, const gchar * message); - void at_exception_warning(at_exception_type * exception, const gchar * message); - -#define AT_ERROR at_error_quark() - GQuark at_error_quark(void); - typedef enum { - AT_ERROR_WRONG_COLOR_STRING, - } AtError; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* Not def: AT_EXCEPTION_H */ diff --git a/src/autotrace/filename.c b/src/autotrace/filename.c deleted file mode 100644 index 3ae8c58..0000000 --- a/src/autotrace/filename.c +++ /dev/null @@ -1,45 +0,0 @@ -/* filename.c: Function manipulate file names - Was: find-suffix, remove-suffix */ - -/* remove-suffx.c: remove any suffix. - -Copyright (C) 1999 Free Software Foundation, Inc. - -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, 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, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "filename.h" -#include "xstd.h" -#include -#include - -gchar *find_suffix(gchar * name) -{ - gchar *dot_pos = strrchr(name, '.'); - gchar *slash_pos = strrchr(name, G_DIR_SEPARATOR); - - /* If the name is `foo' or `/foo.bar/baz', we have no extension. */ - return dot_pos == NULL || dot_pos < slash_pos ? NULL : dot_pos + 1; -} - -gchar *remove_suffix(gchar * s) -{ - gchar *suffix = find_suffix(s); - - return suffix == NULL ? s : suffix - 2 - s < 0 ? NULL : g_strndup(s, (unsigned)(suffix - 2 - s)); -} diff --git a/src/autotrace/filename.h b/src/autotrace/filename.h deleted file mode 100644 index baf373d..0000000 --- a/src/autotrace/filename.h +++ /dev/null @@ -1,33 +0,0 @@ -/* filename.h: Function manipulate file names - Was: find-suffix, remove-suffix */ - -/* remove-suffx.h: declarations for shared routines. - -Copyright (C) 1992 Free Software Foundation, Inc. - -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, 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, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef FILENAME_H -#define FILENAME_H -#include "types.h" - -/* If NAME has a suffix, return a pointer to its first character (i.e., - the one after the `.'); otherwise, return NULL. */ -extern gchar *find_suffix(gchar * name); - -/* Return NAME with any suffix removed. */ -extern gchar *remove_suffix(gchar * name); - -#endif /* Not def: FILENAME_H */ diff --git a/src/autotrace/fit.c b/src/autotrace/fit.c deleted file mode 100644 index ffc0556..0000000 --- a/src/autotrace/fit.c +++ /dev/null @@ -1,1442 +0,0 @@ -/* fit.c: turn a bitmap representation of a curve into a list of splines. - Some of the ideas, but not the code, comes from the Phoenix thesis. - See README for the reference. - - The code was partially derived from limn. - - Copyright (C) 1992 Free Software Foundation, Inc. - - 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, 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "autotrace.h" -#include "fit.h" -#include "logreport.h" -#include "spline.h" -#include "vector.h" -#include "curve.h" -#include "pxl-outline.h" -#include "epsilon-equal.h" -#include "xstd.h" -#include -#ifndef FLT_MAX -#include -#include -#endif -#ifndef FLT_MIN -#include -#include -#endif -#include -#include - -#define SQUARE(x) ((x) * (x)) -#define CUBE(x) ((x) * (x) * (x)) - -/* We need to manipulate lists of array indices. */ - -typedef struct index_list { - unsigned *data; - unsigned length; -} index_list_type; - -/* The usual accessor macros. */ -#define GET_INDEX(i_l, n) ((i_l).data[n]) -#define INDEX_LIST_LENGTH(i_l) ((i_l).length) -#define GET_LAST_INDEX(i_l) ((i_l).data[INDEX_LIST_LENGTH (i_l) - 1]) - -static void append_index(index_list_type *, unsigned); -static void free_index_list(index_list_type *); -static index_list_type new_index_list(void); -static void remove_adjacent_corners(index_list_type *, unsigned, gboolean, at_exception_type * exception); -static void change_bad_lines(spline_list_type *, fitting_opts_type *); -static void filter(curve_type, fitting_opts_type *); -static void find_vectors(unsigned, pixel_outline_type, vector_type *, vector_type *, unsigned); -static index_list_type find_corners(pixel_outline_type, fitting_opts_type *, at_exception_type * exception); -static gfloat find_error(curve_type, spline_type, unsigned *, at_exception_type * exception); -static vector_type find_half_tangent(curve_type, gboolean start, unsigned *, unsigned); -static void find_tangent(curve_type, gboolean, gboolean, unsigned); -static spline_type fit_one_spline(curve_type, at_exception_type * exception); -static spline_list_type *fit_curve(curve_type, fitting_opts_type *, at_exception_type * exception); -static spline_list_type fit_curve_list(curve_list_type, fitting_opts_type *, at_distance_map *, at_exception_type * exception); -static spline_list_type *fit_with_least_squares(curve_type, fitting_opts_type *, at_exception_type * exception); -static spline_list_type *fit_with_line(curve_type); -static void remove_knee_points(curve_type, gboolean); -static void set_initial_parameter_values(curve_type); -static gboolean spline_linear_enough(spline_type *, curve_type, fitting_opts_type *); -static curve_list_array_type split_at_corners(pixel_outline_list_type, fitting_opts_type *, at_exception_type * exception); -static at_coord real_to_int_coord(at_real_coord); -static gfloat distance(at_real_coord, at_real_coord); - -/* Get a new set of fitting options */ -fitting_opts_type new_fitting_opts(void) -{ - fitting_opts_type fitting_opts; - - fitting_opts.background_color = NULL; - fitting_opts.charcode = 0; - fitting_opts.color_count = 0; - fitting_opts.corner_always_threshold = (gfloat) 60.0; - fitting_opts.corner_surround = 4; - fitting_opts.corner_threshold = (gfloat) 100.0; - fitting_opts.error_threshold = (gfloat) 2.0; - fitting_opts.filter_iterations = 4; - fitting_opts.line_reversion_threshold = (gfloat) .01; - fitting_opts.line_threshold = (gfloat) 1.0; - fitting_opts.remove_adjacent_corners = FALSE; - fitting_opts.tangent_surround = 3; - fitting_opts.despeckle_level = 0; - fitting_opts.despeckle_tightness = 2.0; - fitting_opts.noise_removal = (gfloat) 0.99; - fitting_opts.centerline = FALSE; - fitting_opts.preserve_width = FALSE; - fitting_opts.width_weight_factor = 6.0; - - return (fitting_opts); -} - -/* The top-level call that transforms the list of pixels in the outlines - of the original character to a list of spline lists fitted to those - pixels. */ - -spline_list_array_type fitted_splines(pixel_outline_list_type pixel_outline_list, fitting_opts_type * fitting_opts, at_distance_map * dist, unsigned short width, unsigned short height, at_exception_type * exception, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data) -{ - unsigned this_list; - - spline_list_array_type char_splines = new_spline_list_array(); - curve_list_array_type curve_array = split_at_corners(pixel_outline_list, - fitting_opts, - exception); - - char_splines.centerline = fitting_opts->centerline; - char_splines.preserve_width = fitting_opts->preserve_width; - char_splines.width_weight_factor = fitting_opts->width_weight_factor; - - if (fitting_opts->background_color) - char_splines.background_color = at_color_copy(fitting_opts->background_color); - else - char_splines.background_color = NULL; - /* Set dummy values. Real value is set in upper context. */ - char_splines.width = width; - char_splines.height = height; - - for (this_list = 0; this_list < CURVE_LIST_ARRAY_LENGTH(curve_array); this_list++) { - spline_list_type curve_list_splines; - curve_list_type curves = CURVE_LIST_ARRAY_ELT(curve_array, this_list); - - if (notify_progress) - notify_progress((((gfloat) this_list) / ((gfloat) CURVE_LIST_ARRAY_LENGTH(curve_array) * (gfloat) 3.0) + (gfloat) 0.333), progress_data); - if (test_cancel && test_cancel(testcancel_data)) - goto cleanup; - - LOG("\nFitting curve list #%u:\n", this_list); - - curve_list_splines = fit_curve_list(curves, fitting_opts, dist, exception); - if (at_exception_got_fatal(exception)) { - if (char_splines.background_color) - at_color_free(char_splines.background_color); - goto cleanup; - } - curve_list_splines.clockwise = curves.clockwise; - - memcpy(&(curve_list_splines.color), &(O_LIST_OUTLINE(pixel_outline_list, this_list).color), sizeof(at_color)); - append_spline_list(&char_splines, curve_list_splines); - } -cleanup: - free_curve_list_array(&curve_array, notify_progress, progress_data); - - return char_splines; -} - -/* Fit the list of curves CURVE_LIST to a list of splines, and return - it. CURVE_LIST represents a single closed paths, e.g., either the - inside or outside outline of an `o'. */ - -static spline_list_type fit_curve_list(curve_list_type curve_list, fitting_opts_type * fitting_opts, at_distance_map * dist, at_exception_type * exception) -{ - curve_type curve; - unsigned this_curve, this_spline; - unsigned curve_list_length = CURVE_LIST_LENGTH(curve_list); - spline_list_type curve_list_splines = empty_spline_list(); - - curve_list_splines.open = curve_list.open; - - /* Remove the extraneous ``knee'' points before filtering. Since the - corners have already been found, we don't need to worry about - removing a point that should be a corner. */ - - LOG("\nRemoving knees:\n"); - for (this_curve = 0; this_curve < curve_list_length; this_curve++) { - LOG("#%u:", this_curve); - remove_knee_points(CURVE_LIST_ELT(curve_list, this_curve), CURVE_LIST_CLOCKWISE(curve_list)); - } - - if (dist != NULL) { - unsigned this_point; - unsigned height = dist->height; - for (this_curve = 0; this_curve < curve_list_length; this_curve++) { - curve = CURVE_LIST_ELT(curve_list, this_curve); - for (this_point = 0; this_point < CURVE_LENGTH(curve); this_point++) { - unsigned x, y; - float width, w; - at_real_coord *coord = &CURVE_POINT(curve, this_point); - x = (unsigned)(coord->x); - y = height - (unsigned)(coord->y) - 1; - - /* Each (x, y) is a point on the skeleton of the curve, which - might be offset from the TRUE centerline, where the width - is maximal. Therefore, use as the local line width the - maximum distance over the neighborhood of (x, y). */ - width = dist->d[y][x]; - if (y >= 1) { - if ((w = dist->d[y - 1][x]) > width) - width = w; - if (x >= 1) { - if ((w = dist->d[y][x - 1]) > width) - width = w; - if ((w = dist->d[y - 1][x - 1]) > width) - width = w; - } - if (x + 1 < dist->width) { - if ((w = dist->d[y][x + 1]) > width) - width = w; - if ((w = dist->d[y - 1][x + 1]) > width) - width = w; - } - } - if (y + 1 < height) { - if ((w = dist->d[y + 1][x]) > width) - width = w; - if (x >= 1 && (w = dist->d[y + 1][x - 1]) > width) - width = w; - if (x + 1 < dist->width && (w = dist->d[y + 1][x + 1]) > width) - width = w; - } - coord->z = width * (fitting_opts->width_weight_factor); - } - } - } - - /* We filter all the curves in CURVE_LIST at once; otherwise, we would - look at an unfiltered curve when computing tangents. */ - - LOG("\nFiltering curves:\n"); - for (this_curve = 0; this_curve < curve_list.length; this_curve++) { - LOG("#%u: ", this_curve); - filter(CURVE_LIST_ELT(curve_list, this_curve), fitting_opts); - } - - /* Make the first point in the first curve also be the last point in - the last curve, so the fit to the whole curve list will begin and - end at the same point. This may cause slight errors in computing - the tangents and t values, but it's worth it for the continuity. - Of course we don't want to do this if the two points are already - the same, as they are if the curve is cyclic. (We don't append it - earlier, in `split_at_corners', because that confuses the - filtering.) Finally, we can't append the point if the curve is - exactly three points long, because we aren't adding any more data, - and three points isn't enough to determine a spline. Therefore, - the fitting will fail. */ - curve = CURVE_LIST_ELT(curve_list, 0); - if (CURVE_CYCLIC(curve) == TRUE) - append_point(curve, CURVE_POINT(curve, 0)); - - /* Finally, fit each curve in the list to a list of splines. */ - for (this_curve = 0; this_curve < curve_list_length; this_curve++) { - spline_list_type *curve_splines; - curve_type current_curve = CURVE_LIST_ELT(curve_list, this_curve); - - LOG("\nFitting curve #%u:\n", this_curve); - - curve_splines = fit_curve(current_curve, fitting_opts, exception); - if (at_exception_got_fatal(exception)) - goto cleanup; - else if (curve_splines == NULL) { - LOG("Could not fit curve #%u", this_curve); - at_exception_warning(exception, "Could not fit curve"); - } else { - LOG("Fitted splines for curve #%u:\n", this_curve); - for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH(*curve_splines); this_spline++) { - LOG(" %u: ", this_spline); - if (logging) - print_spline(SPLINE_LIST_ELT(*curve_splines, this_spline)); - } - - /* After fitting, we may need to change some would-be lines - back to curves, because they are in a list with other - curves. */ - change_bad_lines(curve_splines, fitting_opts); - - concat_spline_lists(&curve_list_splines, *curve_splines); - free_spline_list(*curve_splines); - free(curve_splines); - } - } - - if (logging) { - LOG("\nFitted splines are:\n"); - for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH(curve_list_splines); this_spline++) { - LOG(" %u: ", this_spline); - print_spline(SPLINE_LIST_ELT(curve_list_splines, this_spline)); - } - } -cleanup: - return curve_list_splines; -} - -/* Transform a set of locations to a list of splines (the fewer the - better). We are guaranteed that CURVE does not contain any corners. - We return NULL if we cannot fit the points at all. */ - -static spline_list_type *fit_curve(curve_type curve, fitting_opts_type * fitting_opts, at_exception_type * exception) -{ - spline_list_type *fittedsplines; - - if (CURVE_LENGTH(curve) < 2) { - LOG("Tried to fit curve with less than two points"); - at_exception_warning(exception, "Tried to fit curve with less than two points"); - return NULL; - } - - /* Do we have enough points to fit with a spline? */ - fittedsplines = CURVE_LENGTH(curve) < 4 ? fit_with_line(curve) - : fit_with_least_squares(curve, fitting_opts, exception); - - return fittedsplines; -} - -/* As mentioned above, the first step is to find the corners in - PIXEL_LIST, the list of points. (Presumably we can't fit a single - spline around a corner.) The general strategy is to look through all - the points, remembering which we want to consider corners. Then go - through that list, producing the curve_list. This is dictated by the - fact that PIXEL_LIST does not necessarily start on a corner---it just - starts at the character's first outline pixel, going left-to-right, - top-to-bottom. But we want all our splines to start and end on real - corners. - - For example, consider the top of a capital `C' (this is in cmss20): - x - *********** - ****************** - - PIXEL_LIST will start at the pixel below the `x'. If we considered - this pixel a corner, we would wind up matching a very small segment - from there to the end of the line, probably as a straight line, which - is certainly not what we want. - - PIXEL_LIST has one element for each closed outline on the character. - To preserve this information, we return an array of curve_lists, one - element (which in turn consists of several curves, one between each - pair of corners) for each element in PIXEL_LIST. */ - -static curve_list_array_type split_at_corners(pixel_outline_list_type pixel_list, fitting_opts_type * fitting_opts, at_exception_type * exception) -{ - unsigned this_pixel_o; - curve_list_array_type curve_array = new_curve_list_array(); - - LOG("\nFinding corners:\n"); - - for (this_pixel_o = 0; this_pixel_o < O_LIST_LENGTH(pixel_list); this_pixel_o++) { - curve_type curve, first_curve; - index_list_type corner_list; - unsigned p, this_corner; - curve_list_type curve_list = new_curve_list(); - pixel_outline_type pixel_o = O_LIST_OUTLINE(pixel_list, this_pixel_o); - - CURVE_LIST_CLOCKWISE(curve_list) = O_CLOCKWISE(pixel_o); - curve_list.open = pixel_o.open; - - LOG("#%u:", this_pixel_o); - - /* If the outline does not have enough points, we can't do - anything. The endpoints of the outlines are automatically - corners. We need at least `corner_surround' more pixels on - either side of a point before it is conceivable that we might - want another corner. */ - if (O_LENGTH(pixel_o) > fitting_opts->corner_surround * 2 + 2) - corner_list = find_corners(pixel_o, fitting_opts, exception); - - else { - int surround; - if ((surround = (int)(O_LENGTH(pixel_o) - 3) / 2) >= 2) { - unsigned save_corner_surround = fitting_opts->corner_surround; - fitting_opts->corner_surround = surround; - corner_list = find_corners(pixel_o, fitting_opts, exception); - fitting_opts->corner_surround = save_corner_surround; - } else { - corner_list.length = 0; - corner_list.data = NULL; - } - } - - /* Remember the first curve so we can make it be the `next' of the - last one. (And vice versa.) */ - first_curve = new_curve(); - - curve = first_curve; - - if (corner_list.length == 0) { /* No corners. Use all of the pixel outline as the curve. */ - for (p = 0; p < O_LENGTH(pixel_o); p++) - append_pixel(curve, O_COORDINATE(pixel_o, p)); - - if (curve_list.open == TRUE) - CURVE_CYCLIC(curve) = FALSE; - else - CURVE_CYCLIC(curve) = TRUE; - } else { /* Each curve consists of the points between (inclusive) each pair - of corners. */ - for (this_corner = 0; this_corner < corner_list.length - 1; this_corner++) { - curve_type previous_curve = curve; - unsigned corner = GET_INDEX(corner_list, this_corner); - unsigned next_corner = GET_INDEX(corner_list, this_corner + 1); - - for (p = corner; p <= next_corner; p++) - append_pixel(curve, O_COORDINATE(pixel_o, p)); - - append_curve(&curve_list, curve); - curve = new_curve(); - NEXT_CURVE(previous_curve) = curve; - PREVIOUS_CURVE(curve) = previous_curve; - } - - /* The last curve is different. It consists of the points - (inclusive) between the last corner and the end of the list, - and the beginning of the list and the first corner. */ - for (p = GET_LAST_INDEX(corner_list); p < O_LENGTH(pixel_o); p++) - append_pixel(curve, O_COORDINATE(pixel_o, p)); - - if (!pixel_o.open) { - for (p = 0; p <= GET_INDEX(corner_list, 0); p++) - append_pixel(curve, O_COORDINATE(pixel_o, p)); - } else { - curve_type last_curve = PREVIOUS_CURVE(curve); - PREVIOUS_CURVE(first_curve) = NULL; - if (last_curve) - NEXT_CURVE(last_curve) = NULL; - } - } - - LOG(" [%u].\n", corner_list.length); - free_index_list(&corner_list); - - /* Add `curve' to the end of the list, updating the pointers in - the chain. */ - append_curve(&curve_list, curve); - NEXT_CURVE(curve) = first_curve; - PREVIOUS_CURVE(first_curve) = curve; - - /* And now add the just-completed curve list to the array. */ - append_curve_list(&curve_array, curve_list); - } /* End of considering each pixel outline. */ - - return curve_array; -} - -/* We consider a point to be a corner if (1) the angle defined by the - `corner_surround' points coming into it and going out from it is less - than `corner_threshold' degrees, and no point within - `corner_surround' points has a smaller angle; or (2) the angle is less - than `corner_always_threshold' degrees. - - Because of the different cases, it is convenient to have the - following macro to append a corner on to the list we return. The - character argument C is simply so that the different cases can be - distinguished in the log file. */ - -#define APPEND_CORNER(index, angle, c) \ - do \ - { \ - append_index (&corner_list, index); \ - LOG (" (%d,%d)%c%.3f", \ - O_COORDINATE (pixel_outline, index).x, \ - O_COORDINATE (pixel_outline, index).y, \ - c, angle); \ - } \ - while (0) - -static index_list_type find_corners(pixel_outline_type pixel_outline, fitting_opts_type * fitting_opts, at_exception_type * exception) -{ - unsigned p, start_p, end_p; - index_list_type corner_list = new_index_list(); - - start_p = 0; - end_p = O_LENGTH(pixel_outline) - 1; - if (pixel_outline.open) { - if (end_p <= fitting_opts->corner_surround * 2) - return corner_list; - APPEND_CORNER(0, 0.0, '@'); - start_p += fitting_opts->corner_surround; - end_p -= fitting_opts->corner_surround; - } - - /* Consider each pixel on the outline in turn. */ - for (p = start_p; p <= end_p; p++) { - gfloat corner_angle; - vector_type in_vector, out_vector; - - /* Check if the angle is small enough. */ - find_vectors(p, pixel_outline, &in_vector, &out_vector, fitting_opts->corner_surround); - corner_angle = Vangle(in_vector, out_vector, exception); - if (at_exception_got_fatal(exception)) - goto cleanup; - - if (fabs(corner_angle) <= fitting_opts->corner_threshold) { - /* We want to keep looking, instead of just appending the - first pixel we find with a small enough angle, since there - might be another corner within `corner_surround' pixels, with - a smaller angle. If that is the case, we want that one. */ - gfloat best_corner_angle = corner_angle; - unsigned best_corner_index = p; - index_list_type equally_good_list = new_index_list(); - /* As we come into the loop, `p' is the index of the point - that has an angle less than `corner_angle'. We use `i' to - move through the pixels next to that, and `q' for moving - through the adjacent pixels to each `p'. */ - unsigned q = p; - unsigned i = p + 1; - - while (TRUE) { - /* Perhaps the angle is sufficiently small that we want to - consider this a corner, even if it's not the best - (unless we've already wrapped around in the search, - i.e., `qcorner_always_threshold && q >= p) - APPEND_CORNER(q, corner_angle, '\\'); - - /* Exit the loop if we've looked at `corner_surround' - pixels past the best one we found, or if we've looked - at all the pixels. */ - if (i >= best_corner_index + fitting_opts->corner_surround || i >= O_LENGTH(pixel_outline)) - break; - - /* Check the angle. */ - q = i % O_LENGTH(pixel_outline); - find_vectors(q, pixel_outline, &in_vector, &out_vector, fitting_opts->corner_surround); - corner_angle = Vangle(in_vector, out_vector, exception); - if (at_exception_got_fatal(exception)) - goto cleanup; - - /* If we come across a corner that is just as good as the - best one, we should make it a corner, too. This - happens, for example, at the points on the `W' in some - typefaces, where the ``points'' are flat. */ - if (epsilon_equal(corner_angle, best_corner_angle)) - append_index(&equally_good_list, q); - - else if (corner_angle < best_corner_angle) { - best_corner_angle = corner_angle; - /* We want to check `corner_surround' pixels beyond the - new best corner. */ - i = best_corner_index = q; - free_index_list(&equally_good_list); - equally_good_list = new_index_list(); - } - - i++; - } - - /* After we exit the loop, `q' is the index of the last point - we checked. We have already added the corner if - `best_corner_angle' is less than `corner_always_threshold'. - Again, if we've already wrapped around, we don't want to - add the corner again. */ - if (best_corner_angle > fitting_opts->corner_always_threshold && best_corner_index >= p) { - unsigned j; - - APPEND_CORNER(best_corner_index, best_corner_angle, '/'); - - for (j = 0; j < INDEX_LIST_LENGTH(equally_good_list); j++) - APPEND_CORNER(GET_INDEX(equally_good_list, j), best_corner_angle, '@'); - } - free_index_list(&equally_good_list); - - /* If we wrapped around in our search, we're done; otherwise, - we don't want the outer loop to look at the pixels that we - already looked at in searching for the best corner. */ - p = (q < p) ? O_LENGTH(pixel_outline) : q; - } /* End of searching for the best corner. */ - } /* End of considering each pixel. */ - - if (INDEX_LIST_LENGTH(corner_list) > 0) - /* We never want two corners next to each other, since the - only way to fit such a ``curve'' would be with a straight - line, which usually interrupts the continuity dreadfully. */ - remove_adjacent_corners(&corner_list, O_LENGTH(pixel_outline) - (pixel_outline.open ? 2 : 1), fitting_opts->remove_adjacent_corners, exception); -cleanup: - return corner_list; -} - -/* Return the difference vectors coming in and going out of the outline - OUTLINE at the point whose index is TEST_INDEX. In Phoenix, - Schneider looks at a single point on either side of the point we're - considering. That works for him because his points are not touching. - But our points *are* touching, and so we have to look at - `corner_surround' points on either side, to get a better picture of - the outline's shape. */ - -static void find_vectors(unsigned test_index, pixel_outline_type outline, vector_type * in, vector_type * out, unsigned corner_surround) -{ - int i; - unsigned n_done; - at_coord candidate = O_COORDINATE(outline, test_index); - - in->dx = in->dy = in->dz = 0.0; - out->dx = out->dy = out->dz = 0.0; - - /* Add up the differences from p of the `corner_surround' points - before p. */ - for (i = O_PREV(outline, test_index), n_done = 0; n_done < corner_surround; i = O_PREV(outline, i), n_done++) - *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate)); - - /* And the points after p. */ - for (i = O_NEXT(outline, test_index), n_done = 0; n_done < corner_surround; i = O_NEXT(outline, i), n_done++) - *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate)); -} - -/* Remove adjacent points from the index list LIST. We do this by first - sorting the list and then running through it. Since these lists are - quite short, a straight selection sort (e.g., p.139 of the Art of - Computer Programming, vol.3) is good enough. LAST_INDEX is the index - of the last pixel on the outline, i.e., the next one is the first - pixel. We need this for checking the adjacency of the last corner. - - We need to do this because the adjacent corners turn into - two-pixel-long curves, which can only be fit by straight lines. */ - -static void remove_adjacent_corners(index_list_type * list, unsigned last_index, gboolean remove_adj_corners, at_exception_type * exception) -{ - unsigned j; - unsigned last; - index_list_type new_list = new_index_list(); - - for (j = INDEX_LIST_LENGTH(*list) - 1; j > 0; j--) { - unsigned search; - unsigned temp; - /* Find maximal element below `j'. */ - unsigned max_index = j; - - for (search = 0; search < j; search++) - if (GET_INDEX(*list, search) > GET_INDEX(*list, max_index)) - max_index = search; - - if (max_index != j) { - temp = GET_INDEX(*list, j); - GET_INDEX(*list, j) = GET_INDEX(*list, max_index); - GET_INDEX(*list, max_index) = temp; - - /* xx -- really have to sort? */ - LOG("needed exchange"); - at_exception_warning(exception, "needed exchange"); - } - } - - /* The list is sorted. Now look for adjacent entries. Each time - through the loop we insert the current entry and, if appropriate, - the next entry. */ - for (j = 0; j < INDEX_LIST_LENGTH(*list) - 1; j++) { - unsigned current = GET_INDEX(*list, j); - unsigned next = GET_INDEX(*list, j + 1); - - /* We should never have inserted the same element twice. */ - /* assert (current != next); */ - - if ((remove_adj_corners) && ((next == current + 1) || (next == current))) - j++; - - append_index(&new_list, current); - } - - /* Don't append the last element if it is 1) adjacent to the previous - one; or 2) adjacent to the very first one. */ - last = GET_LAST_INDEX(*list); - if (INDEX_LIST_LENGTH(new_list) == 0 || !(last == GET_LAST_INDEX(new_list) + 1 || (last == last_index && GET_INDEX(*list, 0) == 0))) - append_index(&new_list, last); - - free_index_list(list); - *list = new_list; -} - -/* A ``knee'' is a point which forms a ``right angle'' with its - predecessor and successor. See the documentation (the `Removing - knees' section) for an example and more details. - - The argument CLOCKWISE tells us which direction we're moving. (We - can't figure that information out from just the single segment with - which we are given to work.) - - We should never find two consecutive knees. - - Since the first and last points are corners (unless the curve is - cyclic), it doesn't make sense to remove those. */ - -/* This evaluates to TRUE if the vector V is zero in one direction and - nonzero in the other. */ -#define ONLY_ONE_ZERO(v) \ - (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0)) - -/* There are four possible cases for knees, one for each of the four - corners of a rectangle; and then the cases differ depending on which - direction we are going around the curve. The tests are listed here - in the order of upper left, upper right, lower right, lower left. - Perhaps there is some simple pattern to the - clockwise/counterclockwise differences, but I don't see one. */ -#define CLOCKWISE_KNEE(prev_delta, next_delta) \ - ((prev_delta.dx == -1.0 && next_delta.dy == 1.0) \ - || (prev_delta.dy == 1.0 && next_delta.dx == 1.0) \ - || (prev_delta.dx == 1.0 && next_delta.dy == -1.0) \ - || (prev_delta.dy == -1.0 && next_delta.dx == -1.0)) - -#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta) \ - ((prev_delta.dy == 1.0 && next_delta.dx == -1.0) \ - || (prev_delta.dx == 1.0 && next_delta.dy == 1.0) \ - || (prev_delta.dy == -1.0 && next_delta.dx == 1.0) \ - || (prev_delta.dx == -1.0 && next_delta.dy == -1.0)) - -static void remove_knee_points(curve_type curve, gboolean clockwise) -{ - unsigned i; - unsigned offset = (CURVE_CYCLIC(curve) == TRUE) ? 0 : 1; - at_coord previous = real_to_int_coord(CURVE_POINT(curve, CURVE_PREV(curve, offset))); - curve_type trimmed_curve = copy_most_of_curve(curve); - - if (CURVE_CYCLIC(curve) == FALSE) - append_pixel(trimmed_curve, real_to_int_coord(CURVE_POINT(curve, 0))); - - for (i = offset; i < CURVE_LENGTH(curve) - offset; i++) { - at_coord current = real_to_int_coord(CURVE_POINT(curve, i)); - at_coord next = real_to_int_coord(CURVE_POINT(curve, CURVE_NEXT(curve, i))); - vector_type prev_delta = IPsubtract(previous, current); - vector_type next_delta = IPsubtract(next, current); - - if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta) - && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta)) - || (!clockwise && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)))) - LOG(" (%d,%d)", current.x, current.y); - else { - previous = current; - append_pixel(trimmed_curve, current); - } - } - - if (CURVE_CYCLIC(curve) == FALSE) - append_pixel(trimmed_curve, real_to_int_coord(LAST_CURVE_POINT(curve))); - - if (CURVE_LENGTH(trimmed_curve) == CURVE_LENGTH(curve)) - LOG(" (none)"); - - LOG(".\n"); - - free_curve(curve); - *curve = *trimmed_curve; - free(trimmed_curve); /* free_curve? --- Masatake */ -} - -/* Smooth the curve by adding in neighboring points. Do this - `filter_iterations' times. But don't change the corners. */ - -static void filter(curve_type curve, fitting_opts_type * fitting_opts) -{ - unsigned iteration, this_point; - unsigned offset = (CURVE_CYCLIC(curve) == TRUE) ? 0 : 1; - at_real_coord prev_new_point; - - /* We must have at least three points---the previous one, the current - one, and the next one. But if we don't have at least five, we will - probably collapse the curve down onto a single point, which means - we won't be able to fit it with a spline. */ - if (CURVE_LENGTH(curve) < 5) { - LOG("Length is %u, not enough to filter.\n", CURVE_LENGTH(curve)); - return; - } - - prev_new_point.x = FLT_MAX; - prev_new_point.y = FLT_MAX; - prev_new_point.z = FLT_MAX; - - for (iteration = 0; iteration < fitting_opts->filter_iterations; iteration++) { - curve_type newcurve = copy_most_of_curve(curve); - gboolean collapsed = FALSE; - - /* Keep the first point on the curve. */ - if (offset) - append_point(newcurve, CURVE_POINT(curve, 0)); - - for (this_point = offset; this_point < CURVE_LENGTH(curve) - offset; this_point++) { - vector_type in, out, sum; - at_real_coord new_point; - - /* Calculate the vectors in and out, computed by looking at n points - on either side of this_point. Experimental it was found that 2 is - optimal. */ - - signed int prev, prevprev; /* have to be signed */ - unsigned int next, nextnext; - at_real_coord candidate = CURVE_POINT(curve, this_point); - - prev = CURVE_PREV(curve, this_point); - prevprev = CURVE_PREV(curve, prev); - next = CURVE_NEXT(curve, this_point); - nextnext = CURVE_NEXT(curve, next); - - /* Add up the differences from p of the `surround' points - before p. */ - in.dx = in.dy = in.dz = 0.0; - - in = Vadd(in, Psubtract(CURVE_POINT(curve, prev), candidate)); - if (prevprev >= 0) - in = Vadd(in, Psubtract(CURVE_POINT(curve, prevprev), candidate)); - - /* And the points after p. Don't use more points after p than we - ended up with before it. */ - out.dx = out.dy = out.dz = 0.0; - - out = Vadd(out, Psubtract(CURVE_POINT(curve, next), candidate)); - if (nextnext < CURVE_LENGTH(curve)) - out = Vadd(out, Psubtract(CURVE_POINT(curve, nextnext), candidate)); - - /* Start with the old point. */ - new_point = candidate; - sum = Vadd(in, out); - /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */ - new_point.x += sum.dx / 6; - new_point.y += sum.dy / 6; - new_point.z += sum.dz / 6; - if (fabs(prev_new_point.x - new_point.x) < 0.3 && fabs(prev_new_point.y - new_point.y) < 0.3 && fabs(prev_new_point.z - new_point.z) < 0.3) { - collapsed = TRUE; - break; - } - - /* Put the newly computed point into a separate curve, so it - doesn't affect future computation (on this iteration). */ - append_point(newcurve, prev_new_point = new_point); - } - - if (collapsed) - free_curve(newcurve); - else { - /* Just as with the first point, we have to keep the last point. */ - if (offset) - append_point(newcurve, LAST_CURVE_POINT(curve)); - - /* Set the original curve to the newly filtered one, and go again. */ - free_curve(curve); - *curve = *newcurve; - } - free(newcurve); - } - - if (logging) - log_curve(curve, FALSE); -} - -/* This routine returns the curve fitted to a straight line in a very - simple way: make the first and last points on the curve be the - endpoints of the line. This simplicity is justified because we are - called only on very short curves. */ - -static spline_list_type *fit_with_line(curve_type curve) -{ - spline_type line; - - LOG("Fitting with straight line:\n"); - - SPLINE_DEGREE(line) = LINEARTYPE; - START_POINT(line) = CONTROL1(line) = CURVE_POINT(curve, 0); - END_POINT(line) = CONTROL2(line) = LAST_CURVE_POINT(curve); - - /* Make sure that this line is never changed to a cubic. */ - SPLINE_LINEARITY(line) = 0; - - if (logging) { - LOG(" "); - print_spline(line); - } - - return new_spline_list_with_spline(line); -} - -/* The least squares method is well described in Schneider's thesis. - Briefly, we try to fit the entire curve with one spline. If that - fails, we subdivide the curve. */ - -static spline_list_type *fit_with_least_squares(curve_type curve, fitting_opts_type * fitting_opts, at_exception_type * exception) -{ - gfloat error = 0, best_error = FLT_MAX; - spline_type spline, best_spline; - spline_list_type *spline_list = NULL; - unsigned worst_point = 0; - gfloat previous_error = FLT_MAX; - - LOG("\nFitting with least squares:\n"); - - /* Phoenix reduces the number of points with a ``linear spline - technique''. But for fitting letterforms, that is - inappropriate. We want all the points we can get. */ - - /* It makes no difference whether we first set the `t' values or - find the tangents. This order makes the documentation a little - more coherent. */ - - LOG("Finding tangents:\n"); - find_tangent(curve, /* to_start */ TRUE, /* cross_curve */ FALSE, - fitting_opts->tangent_surround); - find_tangent(curve, /* to_start */ FALSE, /* cross_curve */ FALSE, - fitting_opts->tangent_surround); - - set_initial_parameter_values(curve); - - /* Now we loop, subdividing, until CURVE has - been fit. */ - while (TRUE) { - spline = best_spline = fit_one_spline(curve, exception); - if (at_exception_got_fatal(exception)) - goto cleanup; - - if (SPLINE_DEGREE(spline) == LINEARTYPE) - LOG(" fitted to line:\n"); - else - LOG(" fitted to spline:\n"); - - if (logging) { - LOG(" "); - print_spline(spline); - } - - if (SPLINE_DEGREE(spline) == LINEARTYPE) - break; - - error = find_error(curve, spline, &worst_point, exception); - if (error <= previous_error) { - best_error = error; - best_spline = spline; - } - break; - } - - if (SPLINE_DEGREE(spline) == LINEARTYPE) { - spline_list = new_spline_list_with_spline(spline); - LOG("Accepted error of %.3f.\n", error); - return (spline_list); - } - - /* Go back to the best fit. */ - spline = best_spline; - error = best_error; - - if (error < fitting_opts->error_threshold && CURVE_CYCLIC(curve) == FALSE) { - /* The points were fitted with a - spline. We end up here whenever a fit is accepted. We have - one more job: see if the ``curve'' that was fit should really - be a straight line. */ - if (spline_linear_enough(&spline, curve, fitting_opts)) { - SPLINE_DEGREE(spline) = LINEARTYPE; - LOG("Changed to line.\n"); - } - spline_list = new_spline_list_with_spline(spline); - LOG("Accepted error of %.3f.\n", error); - } else { - /* We couldn't fit the curve acceptably, so subdivide. */ - unsigned subdivision_index; - spline_list_type *left_spline_list; - spline_list_type *right_spline_list; - curve_type left_curve = new_curve(); - curve_type right_curve = new_curve(); - - /* Keep the linked list of curves intact. */ - NEXT_CURVE(right_curve) = NEXT_CURVE(curve); - PREVIOUS_CURVE(right_curve) = left_curve; - NEXT_CURVE(left_curve) = right_curve; - PREVIOUS_CURVE(left_curve) = curve; - NEXT_CURVE(curve) = left_curve; - - LOG("\nSubdividing (error %.3f):\n", error); - LOG(" Original point: (%.3f,%.3f), #%u.\n", CURVE_POINT(curve, worst_point).x, CURVE_POINT(curve, worst_point).y, worst_point); - subdivision_index = worst_point; - LOG(" Final point: (%.3f,%.3f), #%u.\n", CURVE_POINT(curve, subdivision_index).x, CURVE_POINT(curve, subdivision_index).y, subdivision_index); - - /* The last point of the left-hand curve will also be the first - point of the right-hand curve. */ - CURVE_LENGTH(left_curve) = subdivision_index + 1; - CURVE_LENGTH(right_curve) = CURVE_LENGTH(curve) - subdivision_index; - left_curve->point_list = curve->point_list; - right_curve->point_list = curve->point_list + subdivision_index; - - /* We want to use the tangents of the curve which we are - subdividing for the start tangent for left_curve and the - end tangent for right_curve. */ - CURVE_START_TANGENT(left_curve) = CURVE_START_TANGENT(curve); - CURVE_END_TANGENT(right_curve) = CURVE_END_TANGENT(curve); - - /* We have to set up the two curves before finding the tangent at - the subdivision point. The tangent at that point must be the - same for both curves, or noticeable bumps will occur in the - character. But we want to use information on both sides of the - point to compute the tangent, hence cross_curve = true. */ - find_tangent(left_curve, /* to_start_point: */ FALSE, - /* cross_curve: */ TRUE, fitting_opts->tangent_surround); - CURVE_START_TANGENT(right_curve) = CURVE_END_TANGENT(left_curve); - - /* Now that we've set up the curves, we can fit them. */ - left_spline_list = fit_curve(left_curve, fitting_opts, exception); - if (at_exception_got_fatal(exception)) - /* TODO: Memory allocated for left_curve and right_curve - will leak. */ - goto cleanup; - - right_spline_list = fit_curve(right_curve, fitting_opts, exception); - /* TODO: Memory allocated for left_curve and right_curve - will leak. */ - if (at_exception_got_fatal(exception)) - goto cleanup; - - /* Neither of the subdivided curves could be fit, so fail. */ - if (left_spline_list == NULL && right_spline_list == NULL) - return NULL; - - /* Put the two together (or whichever of them exist). */ - spline_list = new_spline_list(); - - if (left_spline_list == NULL) { - LOG("Could not fit spline to left curve (%lx).\n", (unsigned long)(uintptr_t)left_curve); - at_exception_warning(exception, "Could not fit left spline list"); - } else { - concat_spline_lists(spline_list, *left_spline_list); - free_spline_list(*left_spline_list); - free(left_spline_list); - } - - if (right_spline_list == NULL) { - LOG("Could not fit spline to right curve (%lx).\n", (unsigned long)(uintptr_t)right_curve); - at_exception_warning(exception, "Could not fit right spline list"); - } else { - concat_spline_lists(spline_list, *right_spline_list); - free_spline_list(*right_spline_list); - free(right_spline_list); - } - if (CURVE_END_TANGENT(left_curve)) - free(CURVE_END_TANGENT(left_curve)); - free(left_curve); - free(right_curve); - } -cleanup: - return spline_list; -} - -/* Our job here is to find alpha1 (and alpha2), where t1_hat (t2_hat) is - the tangent to CURVE at the starting (ending) point, such that: - - control1 = alpha1*t1_hat + starting point - control2 = alpha2*t2_hat + ending_point - - and the resulting spline (starting_point .. control1 and control2 .. - ending_point) minimizes the least-square error from CURVE. - - See pp.57--59 of the Phoenix thesis. - - The B?(t) here corresponds to B_i^3(U_i) there. - The Bernshte\u in polynomials of degree n are defined by - B_i^n(t) = { n \choose i } t^i (1-t)^{n-i}, i = 0..n */ - -#define B0(t) CUBE ((gfloat) 1.0 - (t)) -#define B1(t) ((gfloat) 3.0 * (t) * SQUARE ((gfloat) 1.0 - (t))) -#define B2(t) ((gfloat) 3.0 * SQUARE (t) * ((gfloat) 1.0 - (t))) -#define B3(t) CUBE (t) - -static spline_type fit_one_spline(curve_type curve, at_exception_type * exception) -{ - /* Since our arrays are zero-based, the `C0' and `C1' here correspond - to `C1' and `C2' in the paper. */ - gfloat X_C1_det, C0_X_det, C0_C1_det; - gfloat alpha1, alpha2; - spline_type spline; - vector_type start_vector, end_vector; - unsigned i; - vector_type *A; - vector_type t1_hat = *CURVE_START_TANGENT(curve); - vector_type t2_hat = *CURVE_END_TANGENT(curve); - gfloat C[2][2] = { {0.0, 0.0}, {0.0, 0.0} }; - gfloat X[2] = { 0.0, 0.0 }; - - XMALLOC(A, CURVE_LENGTH(curve) * 2 * sizeof(vector_type)); /* A dynamically allocated array. */ - - START_POINT(spline) = CURVE_POINT(curve, 0); - END_POINT(spline) = LAST_CURVE_POINT(curve); - start_vector = make_vector(START_POINT(spline)); - end_vector = make_vector(END_POINT(spline)); - - for (i = 0; i < CURVE_LENGTH(curve); i++) { - A[(i << 1) + 0] = Vmult_scalar(t1_hat, B1(CURVE_T(curve, i))); - A[(i << 1) + 1] = Vmult_scalar(t2_hat, B2(CURVE_T(curve, i))); - } - - for (i = 0; i < CURVE_LENGTH(curve); i++) { - vector_type temp, temp0, temp1, temp2, temp3; - vector_type *Ai = A + (i << 1); - - C[0][0] += Vdot(Ai[0], Ai[0]); - C[0][1] += Vdot(Ai[0], Ai[1]); - /* C[1][0] = C[0][1] (this is assigned outside the loop) */ - C[1][1] += Vdot(Ai[1], Ai[1]); - - /* Now the right-hand side of the equation in the paper. */ - temp0 = Vmult_scalar(start_vector, B0(CURVE_T(curve, i))); - temp1 = Vmult_scalar(start_vector, B1(CURVE_T(curve, i))); - temp2 = Vmult_scalar(end_vector, B2(CURVE_T(curve, i))); - temp3 = Vmult_scalar(end_vector, B3(CURVE_T(curve, i))); - - temp = make_vector(Vsubtract_point(CURVE_POINT(curve, i), Vadd(temp0, Vadd(temp1, Vadd(temp2, temp3))))); - - X[0] += Vdot(temp, Ai[0]); - X[1] += Vdot(temp, Ai[1]); - } - free(A); - - C[1][0] = C[0][1]; - - X_C1_det = X[0] * C[1][1] - X[1] * C[0][1]; - C0_X_det = C[0][0] * X[1] - C[0][1] * X[0]; - C0_C1_det = C[0][0] * C[1][1] - C[1][0] * C[0][1]; - if (C0_C1_det == 0.0) { - /* Zero determinant */ - alpha1 = 0; - alpha2 = 0; - } else { - alpha1 = X_C1_det / C0_C1_det; - alpha2 = C0_X_det / C0_C1_det; - } - CONTROL1(spline) = Vadd_point(START_POINT(spline), Vmult_scalar(t1_hat, alpha1)); - CONTROL2(spline) = Vadd_point(END_POINT(spline), Vmult_scalar(t2_hat, alpha2)); - SPLINE_DEGREE(spline) = CUBICTYPE; - - return spline; -} - -/* Find reasonable values for t for each point on CURVE. The method is - called chord-length parameterization, which is described in Plass & - Stone. The basic idea is just to use the distance from one point to - the next as the t value, normalized to produce values that increase - from zero for the first point to one for the last point. */ - -static void set_initial_parameter_values(curve_type curve) -{ - unsigned p; - - LOG("\nAssigning initial t values:\n "); - - CURVE_T(curve, 0) = 0.0; - - for (p = 1; p < CURVE_LENGTH(curve); p++) { - at_real_coord point = CURVE_POINT(curve, p), previous_p = CURVE_POINT(curve, p - 1); - gfloat d = distance(point, previous_p); - CURVE_T(curve, p) = CURVE_T(curve, p - 1) + d; - } - - if (LAST_CURVE_T(curve) == 0.0) - LAST_CURVE_T(curve) = 1.0; - - for (p = 1; p < CURVE_LENGTH(curve); p++) - CURVE_T(curve, p) = CURVE_T(curve, p) / LAST_CURVE_T(curve); - - if (logging) - log_entire_curve(curve); -} - -/* Find an approximation to the tangent to an endpoint of CURVE (to the - first point if TO_START_POINT is TRUE, else the last). If - CROSS_CURVE is TRUE, consider points on the adjacent curve to CURVE. - - It is important to compute an accurate approximation, because the - control points that we eventually decide upon to fit the curve will - be placed on the half-lines defined by the tangents and - endpoints...and we never recompute the tangent after this. */ - -static void find_tangent(curve_type curve, gboolean to_start_point, gboolean cross_curve, unsigned tangent_surround) -{ - vector_type tangent; - vector_type **curve_tangent = (to_start_point == TRUE) ? &(CURVE_START_TANGENT(curve)) - : &(CURVE_END_TANGENT(curve)); - unsigned n_points = 0; - - LOG(" tangent to %s: ", (to_start_point == TRUE) ? "start" : "end"); - - if (*curve_tangent == NULL) { - XMALLOC(*curve_tangent, sizeof(vector_type)); - do { - tangent = find_half_tangent(curve, to_start_point, &n_points, tangent_surround); - - if ((cross_curve == TRUE) || (CURVE_CYCLIC(curve) == TRUE)) { - curve_type adjacent_curve = (to_start_point == TRUE) ? PREVIOUS_CURVE(curve) : NEXT_CURVE(curve); - vector_type tangent2 = (to_start_point == FALSE) ? find_half_tangent(adjacent_curve, TRUE, &n_points, - tangent_surround) : find_half_tangent(adjacent_curve, TRUE, &n_points, - tangent_surround); - - LOG("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ", tangent2.dx, tangent2.dy, tangent2.dz); - tangent = Vadd(tangent, tangent2); - } - tangent_surround--; - - } - while (tangent.dx == 0.0 && tangent.dy == 0.0); - - assert(n_points > 0); - **curve_tangent = Vmult_scalar(tangent, (gfloat) (1.0 / n_points)); - if ((CURVE_CYCLIC(curve) == TRUE) && CURVE_START_TANGENT(curve)) - *CURVE_START_TANGENT(curve) = **curve_tangent; - if ((CURVE_CYCLIC(curve) == TRUE) && CURVE_END_TANGENT(curve)) - *CURVE_END_TANGENT(curve) = **curve_tangent; - } else - LOG("(already computed) "); - - LOG("(%.3f,%.3f,%.3f).\n", (*curve_tangent)->dx, (*curve_tangent)->dy, (*curve_tangent)->dz); -} - -/* Find the change in y and change in x for `tangent_surround' (a global) - points along CURVE. Increment N_POINTS by the number of points we - actually look at. */ - -static vector_type find_half_tangent(curve_type c, gboolean to_start_point, unsigned *n_points, unsigned tangent_surround) -{ - unsigned p; - int factor = to_start_point ? 1 : -1; - unsigned tangent_index = to_start_point ? 0 : c->length - 1; - at_real_coord tangent_point = CURVE_POINT(c, tangent_index); - vector_type tangent = { 0.0, 0.0 }; - unsigned int surround; - - if ((surround = CURVE_LENGTH(c) / 2) > tangent_surround) - surround = tangent_surround; - - for (p = 1; p <= surround; p++) { - int this_index = p * factor + tangent_index; - at_real_coord this_point; - - if (this_index < 0 || this_index >= (int)c->length) - break; - - this_point = CURVE_POINT(c, p * factor + tangent_index); - - /* Perhaps we should weight the tangent from `this_point' by some - factor dependent on the distance from the tangent point. */ - tangent = Vadd(tangent, Vmult_scalar(Psubtract(this_point, tangent_point), (gfloat) factor)); - (*n_points)++; - } - - return tangent; -} - -/* When this routine is called, we have computed a spline representation - for the digitized curve. The question is, how good is it? If the - fit is very good indeed, we might have an error of zero on each - point, and then WORST_POINT becomes irrelevant. But normally, we - return the error at the worst point, and the index of that point in - WORST_POINT. The error computation itself is the Euclidean distance - from the original curve CURVE to the fitted spline SPLINE. */ - -static gfloat find_error(curve_type curve, spline_type spline, unsigned *worst_point, at_exception_type * exception) -{ - unsigned this_point; - gfloat total_error = 0.0; - gfloat worst_error = FLT_MIN; - - *worst_point = CURVE_LENGTH(curve) + 1; /* A sentinel value. */ - - for (this_point = 0; this_point < CURVE_LENGTH(curve); this_point++) { - at_real_coord curve_point = CURVE_POINT(curve, this_point); - gfloat t = CURVE_T(curve, this_point); - at_real_coord spline_point = evaluate_spline(spline, t); - gfloat this_error = distance(curve_point, spline_point); - if (this_error >= worst_error) { - *worst_point = this_point; - worst_error = this_error; - } - total_error += this_error; - } - - if (*worst_point == CURVE_LENGTH(curve) + 1) { /* Didn't have any ``worst point''; the error should be zero. */ - if (epsilon_equal(total_error, 0.0)) - LOG(" Every point fit perfectly.\n"); - else { - LOG("No worst point found; something is wrong"); - at_exception_warning(exception, "No worst point found; something is wrong"); - } - } else { - if (epsilon_equal(total_error, 0.0)) - LOG(" Every point fit perfectly.\n"); - else { - LOG(" Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n", CURVE_POINT(curve, *worst_point).x, CURVE_POINT(curve, *worst_point).y, CURVE_POINT(curve, *worst_point).z, *worst_point, worst_error); - LOG(" Total error was %.3f.\n", total_error); - LOG(" Average error (over %u points) was %.3f.\n", CURVE_LENGTH(curve), total_error / CURVE_LENGTH(curve)); - } - } - - return worst_error; -} - -/* Supposing that we have accepted the error, another question arises: - would we be better off just using a straight line? */ - -static gboolean spline_linear_enough(spline_type * spline, curve_type curve, fitting_opts_type * fitting_opts) -{ - gfloat A, B, C; - unsigned this_point; - gfloat dist = 0.0, start_end_dist, threshold; - - LOG("Checking linearity:\n"); - - A = END_POINT(*spline).x - START_POINT(*spline).x; - B = END_POINT(*spline).y - START_POINT(*spline).y; - C = END_POINT(*spline).z - START_POINT(*spline).z; - - start_end_dist = (gfloat) (SQUARE(A) + SQUARE(B) + SQUARE(C)); - LOG("start_end_distance is %.3f.\n", sqrt(start_end_dist)); - - LOG(" Line endpoints are (%.3f, %.3f, %.3f) and ", START_POINT(*spline).x, START_POINT(*spline).y, START_POINT(*spline).z); - LOG("(%.3f, %.3f, %.3f)\n", END_POINT(*spline).x, END_POINT(*spline).y, END_POINT(*spline).z); - - /* LOG (" Line is %.3fx + %.3fy + %.3f = 0.\n", A, B, C); */ - - for (this_point = 0; this_point < CURVE_LENGTH(curve); this_point++) { - gfloat a, b, c, w; - gfloat t = CURVE_T(curve, this_point); - at_real_coord spline_point = evaluate_spline(*spline, t); - - a = spline_point.x - START_POINT(*spline).x; - b = spline_point.y - START_POINT(*spline).y; - c = spline_point.z - START_POINT(*spline).z; - w = (A * a + B * b + C * c) / start_end_dist; - - dist += (gfloat) sqrt(SQUARE(a - A * w) + SQUARE(b - B * w) + SQUARE(c - C * w)); - } - LOG(" Total distance is %.3f, ", dist); - - dist /= (CURVE_LENGTH(curve) - 1); - LOG("which is %.3f normalized.\n", dist); - - /* We want reversion of short curves to splines to be more likely than - reversion of long curves, hence the second division by the curve - length, for use in `change_bad_lines'. */ - SPLINE_LINEARITY(*spline) = dist; - LOG(" Final linearity: %.3f.\n", SPLINE_LINEARITY(*spline)); - if (start_end_dist * (gfloat) 0.5 > fitting_opts->line_threshold) - threshold = fitting_opts->line_threshold; - else - threshold = start_end_dist * (gfloat) 0.5; - LOG("threshold is %.3f .\n", threshold); - if (dist < threshold) - return TRUE; - else - return FALSE; -} - -/* Unfortunately, we cannot tell in isolation whether a given spline - should be changed to a line or not. That can only be known after the - entire curve has been fit to a list of splines. (The curve is the - pixel outline between two corners.) After subdividing the curve, a - line may very well fit a portion of the curve just as well as the - spline---but unless a spline is truly close to being a line, it - should not be combined with other lines. */ - -static void change_bad_lines(spline_list_type * spline_list, fitting_opts_type * fitting_opts) -{ - unsigned this_spline; - gboolean found_cubic = FALSE; - unsigned length = SPLINE_LIST_LENGTH(*spline_list); - - LOG("\nChecking for bad lines (length %u):\n", length); - - /* First see if there are any splines in the fitted shape. */ - for (this_spline = 0; this_spline < length; this_spline++) { - if (SPLINE_DEGREE(SPLINE_LIST_ELT(*spline_list, this_spline)) == CUBICTYPE) { - found_cubic = TRUE; - break; - } - } - - /* If so, change lines back to splines (we haven't done anything to - their control points, so we only have to change the degree) unless - the spline is close enough to being a line. */ - if (found_cubic) - for (this_spline = 0; this_spline < length; this_spline++) { - spline_type s = SPLINE_LIST_ELT(*spline_list, this_spline); - - if (SPLINE_DEGREE(s) == LINEARTYPE) { - LOG(" #%u: ", this_spline); - if (SPLINE_LINEARITY(s) > fitting_opts->line_reversion_threshold) { - LOG("reverted, "); - SPLINE_DEGREE(SPLINE_LIST_ELT(*spline_list, this_spline)) - = CUBICTYPE; - } - LOG("linearity %.3f.\n", SPLINE_LINEARITY(s)); - } - } else - LOG(" No lines.\n"); -} - -/* Lists of array indices (well, that is what we use it for). */ - -static index_list_type new_index_list(void) -{ - index_list_type index_list; - - index_list.data = NULL; - INDEX_LIST_LENGTH(index_list) = 0; - - return index_list; -} - -static void free_index_list(index_list_type * index_list) -{ - if (INDEX_LIST_LENGTH(*index_list) > 0) { - free(index_list->data); - index_list->data = NULL; - INDEX_LIST_LENGTH(*index_list) = 0; - } -} - -static void append_index(index_list_type * list, unsigned new_index) -{ - INDEX_LIST_LENGTH(*list)++; - XREALLOC(list->data, INDEX_LIST_LENGTH(*list) * sizeof(unsigned)); - list->data[INDEX_LIST_LENGTH(*list) - 1] = new_index; -} - -/* Turn an real point into a integer one. */ - -static at_coord real_to_int_coord(at_real_coord real_coord) -{ - at_coord int_coord; - - int_coord.x = lround(real_coord.x); - int_coord.y = lround(real_coord.y); - - return int_coord; -} - -/* Return the Euclidean distance between P1 and P2. */ - -static gfloat distance(at_real_coord p1, at_real_coord p2) -{ - gfloat x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z; - return (gfloat) sqrt(SQUARE(x) - + SQUARE(y) + SQUARE(z)); -} diff --git a/src/autotrace/fit.h b/src/autotrace/fit.h deleted file mode 100644 index 1875887..0000000 --- a/src/autotrace/fit.h +++ /dev/null @@ -1,22 +0,0 @@ -/* fit.h: convert the pixel representation to splines. */ - -#ifndef FIT_H -#define FIT_H - -#include "autotrace.h" -#include "image-proc.h" -#include "pxl-outline.h" -#include "spline.h" -#include "exception.h" - -/* See fit.c for descriptions of these variables, all of which can be - set using options. */ -typedef at_fitting_opts_type fitting_opts_type; - -/* Fit splines and lines to LIST. */ -extern spline_list_array_type fitted_splines(pixel_outline_list_type, fitting_opts_type *, at_distance_map *, unsigned short width, unsigned short height, at_exception_type * exception, at_progress_func, gpointer, at_testcancel_func, gpointer); - -/* Get a new set of fitting options */ -extern fitting_opts_type new_fitting_opts(void); - -#endif /* not FIT_H */ diff --git a/src/autotrace/image-header.h b/src/autotrace/image-header.h deleted file mode 100644 index c685735..0000000 --- a/src/autotrace/image-header.h +++ /dev/null @@ -1,18 +0,0 @@ -/* image-header.h: declarations for a generic image header. */ - -#ifndef IMAGE_HEADER_H -#define IMAGE_HEADER_H - -#include "types.h" - -/* The important general information about the image data. - See `get_{img,pbm}_header' for the full details of the headers for - the particular formats. */ -typedef struct { - unsigned short hres, vres; /* In pixels per inch. */ - unsigned short width, height; /* In bits. */ - unsigned short depth; /* Perhaps the depth? */ - unsigned format; /* (for pbm) Whether packed or not. */ -} image_header_type; - -#endif /* not IMAGE_HEADER_H */ diff --git a/src/autotrace/image-proc.c b/src/autotrace/image-proc.c deleted file mode 100644 index 7709175..0000000 --- a/src/autotrace/image-proc.c +++ /dev/null @@ -1,493 +0,0 @@ -/* image-proc.c: image processing routines */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include -#include -#include "xstd.h" -#include "logreport.h" -#include "image-proc.h" - -#define BLACK 0 -#define WHITE 0xff -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237 -#endif - -/* Threshold for binarizing a monochrome image */ -#define GRAY_THRESHOLD 225 - -/* RGB to grayscale */ -#define LUMINANCE(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11 + 0.5) - -#if 0 -struct etyp { - int t00, t11, t01, t01s; -}; - -static gboolean get_edge(bitmap_type, int y, int x, struct etyp *t); -static void check(int v1, int v2, int v3, struct etyp *t); -#endif - -/* Allocate storage for a new distance map with the same dimensions - as BITMAP and initialize it so that pixels in BITMAP with value - TARGET_VALUE are at distance zero and all other pixels are at - distance infinity. Then compute the gray-weighted distance from - every non-target point to the nearest target point. */ - -at_distance_map new_distance_map(at_bitmap * bitmap, unsigned char target_value, gboolean padded, at_exception_type * exp) -{ - signed x, y; - float d, min; - at_distance_map dist; - unsigned char *b = AT_BITMAP_BITS(bitmap); - unsigned w = AT_BITMAP_WIDTH(bitmap); - unsigned h = AT_BITMAP_HEIGHT(bitmap); - unsigned spp = AT_BITMAP_PLANES(bitmap); - - dist.height = h; - dist.width = w; - XMALLOC(dist.d, h * sizeof(float *)); - XMALLOC(dist.weight, h * sizeof(float *)); - for (y = 0; y < (signed)h; y++) { - XCALLOC(dist.d[y], w * sizeof(float)); - XMALLOC(dist.weight[y], w * sizeof(float)); - } - - if (spp == 3) { - for (y = 0; y < (signed)h; y++) { - for (x = 0; x < (signed)w; x++, b += spp) { - int gray; - float fgray; - gray = (int)LUMINANCE(b[0], b[1], b[2]); - dist.d[y][x] = (gray == target_value ? 0.0F : 1.0e10F); - fgray = gray * 0.0039215686F; /* = gray / 255.0F */ - dist.weight[y][x] = 1.0F - fgray; -/* dist.weight[y][x] = 1.0F - (fgray * fgray);*/ -/* dist.weight[y][x] = (fgray < 0.5F ? 1.0F - fgray : -2.0F * fgray * (fgray - 1.0F));*/ - } - } - } else { - for (y = 0; y < (signed)h; y++) { - for (x = 0; x < (signed)w; x++, b += spp) { - int gray; - float fgray; - gray = b[0]; - dist.d[y][x] = (gray == target_value ? 0.0F : 1.0e10F); - fgray = gray * 0.0039215686F; /* = gray / 255.0F */ - dist.weight[y][x] = 1.0F - fgray; -/* dist.weight[y][x] = 1.0F - (fgray * fgray);*/ -/* dist.weight[y][x] = (fgray < 0.5F ? 1.0F - fgray : -2.0F * fgray * (fgray - 1.0F)); */ - } - } - } - - /* If the image is padded then border points are all at most - one unit away from the nearest target point. */ - if (padded) { - for (y = 0; y < (signed)h; y++) { - if (dist.d[y][0] > dist.weight[y][0]) - dist.d[y][0] = dist.weight[y][0]; - if (dist.d[y][w - 1] > dist.weight[y][w - 1]) - dist.d[y][w - 1] = dist.weight[y][w - 1]; - } - for (x = 0; x < (signed)w; x++) { - if (dist.d[0][x] > dist.weight[0][x]) - dist.d[0][x] = dist.weight[0][x]; - if (dist.d[h - 1][x] > dist.weight[h - 1][x]) - dist.d[h - 1][x] = dist.weight[h - 1][x]; - } - } - - /* Scan the image from left to right, top to bottom. - Examine the already-visited neighbors of each point (those - situated above or to the left of it). Each neighbor knows - the distance to its nearest target point; add to this distance - the distance from the central point to the neighbor (either - sqrt(2) or one) multiplied by the central point's weight - (derived from its gray level). Replace the distance already - stored at the central point if the new distance is smaller. */ - for (y = 1; y < (signed)h; y++) { - for (x = 1; x < (signed)w; x++) { - if (dist.d[y][x] == 0.0F) - continue; - - min = dist.d[y][x]; - - /* upper-left neighbor */ - d = dist.d[y - 1][x - 1] + (float)M_SQRT2 *dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - - /* upper neighbor */ - d = dist.d[y - 1][x] + dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - - /* left neighbor */ - d = dist.d[y][x - 1] + dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - - /* upper-right neighbor (except at the last column) */ - if (x + 1 < (signed)w) { - d = dist.d[y - 1][x + 1] + (float)M_SQRT2 *dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - } - } - } - - /* Same as above, but now scanning right to left, bottom to top. */ - for (y = h - 2; y >= 0; y--) { - for (x = w - 2; x >= 0; x--) { - min = dist.d[y][x]; - - /* lower-right neighbor */ - d = dist.d[y + 1][x + 1] + (float)M_SQRT2 *dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - - /* lower neighbor */ - d = dist.d[y + 1][x] + dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - - /* right neighbor */ - d = dist.d[y][x + 1] + dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - - /* lower-left neighbor (except at the first column) */ - if (x - 1 >= 0) { - d = dist.d[y + 1][x - 1] + (float)M_SQRT2 *dist.weight[y][x]; - if (d < min) - min = dist.d[y][x] = d; - } - } - } - return dist; -} - -/* Free the dynamically-allocated storage associated with a distance map. */ - -void free_distance_map(at_distance_map * dist) -{ - unsigned y, h; - - if (!dist) - return; - - h = dist->height; - - if (dist->d != NULL) { - for (y = 0; y < h; y++) - free((gpointer *) dist->d[y]); - free((gpointer *) dist->d); - } - if (dist->weight != NULL) { - for (y = 0; y < h; y++) - free((gpointer *) dist->weight[y]); - free((gpointer *) dist->weight); - } -} - -#if 0 -void medial_axis(bitmap_type * bitmap, at_distance_map * dist, const at_color * bg_color) -{ - unsigned x, y, test; - unsigned w, h; - unsigned char *b; - float **d, f; - at_color bg; - - assert(bitmap != NULL); - - assert(AT_BITMAP_PLANES(*bitmap) == 1); - - b = AT_BITMAP_BITS(*bitmap); - assert(b != NULL); - assert(dist != NULL); - d = dist->d; - assert(d != NULL); - - h = AT_BITMAP_HEIGHT(*dist); - w = AT_BITMAP_WIDTH(*dist); - assert(AT_BITMAP_WIDTH(*bitmap) == w && AT_BITMAP_HEIGHT(*bitmap) == h); - - if (bg_color) - bg = *bg_color; - else - bg.r = bg.g = bg.b = 255; - - f = d[0][0] + 0.5; - test = (f < d[1][0]) + (f < d[1][1]) + (f < d[0][1]); - if (test > 1) - b[0] = bg.r; - - f = d[0][w - 1] + 0.5; - test = (f < d[1][w - 1]) + (f < d[1][w - 2]) + (f < d[0][w - 2]); - if (test > 1) - b[w - 1] = bg.r; - - for (x = 1; x < w - 1; x++) { - f = d[0][x] + 0.5; - test = (f < d[0][x - 1]) + (f < d[0][x + 1]) + (f < d[1][x - 1]) - + (f < d[1][x]) + (f < d[1][x + 1]); - if (test > 1) - b[x] = bg.r; - } - b += w; - - for (y = 1; y < h - 1; y++) { - f = d[y][0] + 0.5; - test = (f < d[y - 1][0]) + (f < d[y - 1][1]) + (f < d[y][1]) - + (f < d[y + 1][0]) + (f < d[y + 1][1]); - if (test > 1) - b[0] = bg.r; - - for (x = 1; x < w - 1; x++) { - f = d[y][x] + 0.5; - test = (f < d[y - 1][x - 1]) + (f < d[y - 1][x]) + (f < d[y - 1][x + 1]) - + (f < d[y][x - 1]) + (f < d[y][x + 1]) - + (f < d[y + 1][x - 1]) + (f < d[y + 1][x]) + (f < d[y + 1][x + 1]); - if (test > 1) - b[x] = bg.r; - } - - f = d[y][w - 1] + 0.5; - test = (f < d[y - 1][w - 1]) + (f < d[y - 1][w - 2]) + (f < d[y][w - 2]) - + (f < d[y + 1][w - 1]) + (f < d[y + 1][w - 2]); - if (test > 1) - b[w - 1] = bg.r; - - b += w; - } - - for (x = 1; x < w - 1; x++) { - f = d[h - 1][x] + 0.5; - test = (f < d[h - 1][x - 1]) + (f < d[h - 1][x + 1]) - + (f < d[h - 2][x - 1]) + (f < d[h - 2][x]) + (f < d[h - 2][x + 1]); - if (test > 1) - b[x] = bg.r; - } - - f = d[h - 1][0] + 0.5; - test = (f < d[h - 2][0]) + (f < d[h - 2][1]) + (f < d[h - 1][1]); - if (test > 1) - b[0] = bg.r; - - f = d[h - 1][w - 1] + 0.5; - test = (f < d[h - 2][w - 1]) + (f < d[h - 2][w - 2]) + (f < d[h - 1][w - 2]); - if (test > 1) - b[w - 1] = bg.r; -} -#endif - -/* Binarize a grayscale or color image. */ - -void binarize(at_bitmap * bitmap) -{ - unsigned i, npixels, spp; - unsigned char *b; - - assert(bitmap != NULL); - assert(AT_BITMAP_BITS(bitmap) != NULL); - - b = AT_BITMAP_BITS(bitmap); - spp = AT_BITMAP_PLANES(bitmap); - npixels = AT_BITMAP_WIDTH(bitmap) * AT_BITMAP_HEIGHT(bitmap); - - if (spp == 1) { - for (i = 0; i < npixels; i++) - b[i] = (b[i] > GRAY_THRESHOLD ? WHITE : BLACK); - } else if (spp == 3) { - unsigned char *rgb = b; - for (i = 0; i < npixels; i++, rgb += 3) { - b[i] = (LUMINANCE(rgb[0], rgb[1], rgb[2]) > GRAY_THRESHOLD ? WHITE : BLACK); - } - XREALLOC(AT_BITMAP_BITS(bitmap), npixels); - AT_BITMAP_PLANES(bitmap) = 1; - } else { - WARNING("binarize: %u-plane images are not supported", spp); - } -} - -#if 0 -/* Thin a binary image, replacing the original image with the thinned one. */ - -at_bitmap ip_thin(bitmap_type input_b) -{ - unsigned y, x, i; - gboolean k, again; - struct etyp t; - unsigned w = AT_BITMAP_WIDTH(input_b); - unsigned h = AT_BITMAP_HEIGHT(input_b); - size_t num_bytes = w * h; - bitmap_type b = input_b; - - if (AT_BITMAP_PLANES(input_b) != 1) { - FATAL("thin: single-plane image required; " "%u-plane images cannot be thinned", AT_BITMAP_PLANES(input_b)); - return b; - } - - /* Process and return a copy of the input image. */ - XMALLOC(b.bitmap, num_bytes); - memcpy(b.bitmap, input_b.bitmap, num_bytes); - - /* Set background pixels to zero, foreground pixels to one. */ - for (i = 0; i < num_bytes; i++) - b.bitmap[i] = (b.bitmap[i] == BLACK ? 1 : 0); - - again = TRUE; - while (again) { - again = FALSE; - - for (y = 1; y < h - 1; y++) { - for (x = 1; x < w - 1; x++) { - /* During processing, pixels are used to store edge - type codes, so we can't just test for WHITE or BLACK. */ - if (*AT_BITMAP_PIXEL(b, y, x) == 0) - continue; - - k = (!get_edge(b, y, x, &t) - || (get_edge(b, y, x + 1, &t) && *AT_BITMAP_PIXEL(b, y - 1, x) - && *AT_BITMAP_PIXEL(b, y + 1, x)) - || (get_edge(b, y + 1, x, &t) && *AT_BITMAP_PIXEL(b, y, x - 1) - && *AT_BITMAP_PIXEL(b, y, x + 1)) - || (get_edge(b, y, x + 1, &t) && get_edge(b, y + 1, x + 1, &t) - && get_edge(b, y + 1, x, &t))); - if (k) - continue; - - get_edge(b, y, x, &t); - if (t.t01) - *AT_BITMAP_PIXEL(b, y, x) |= 4; - *AT_BITMAP_PIXEL(b, y, x) |= 2; - again = TRUE; - } - } - - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - if (*AT_BITMAP_PIXEL(b, y, x) & 02) - *AT_BITMAP_PIXEL(b, y, x) = 0; - - for (y = 1; y < h - 1; y++) { - for (x = 1; x < w - 1; x++) { - if (*AT_BITMAP_PIXEL(b, y, x) == 0) - continue; - - k = (!get_edge(b, y, x, &t) - || ((*AT_BITMAP_PIXEL(b, y, x) & 04) == 0) - || (get_edge(b, y + 1, x, &t) && (*AT_BITMAP_PIXEL(b, y, x - 1)) - && *AT_BITMAP_PIXEL(b, y, x + 1)) - || (get_edge(b, y, x + 1, &t) && *AT_BITMAP_PIXEL(b, y - 1, x) - && *AT_BITMAP_PIXEL(b, y + 1, x)) - || (get_edge(b, y + 1, x, &t) && get_edge(b, y, x + 1, &t) - && get_edge(b, y + 1, x + 1, &t))); - if (k) - continue; - - *AT_BITMAP_PIXEL(b, y, x) |= 02; - again = TRUE; - } - } - - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - if (*AT_BITMAP_PIXEL(b, y, x) & 02) - *AT_BITMAP_PIXEL(b, y, x) = 0; - else if (*AT_BITMAP_PIXEL(b, y, x) > 0) - *AT_BITMAP_PIXEL(b, y, x) = 1; - } - } - } - - /* Staircase removal; northward bias. */ - for (y = 1; y < h - 1; y++) { - for (x = 1; x < w - 1; x++) { - if (*AT_BITMAP_PIXEL(b, y, x) == 0) - continue; - - k = !(*AT_BITMAP_PIXEL(b, y - 1, x) - && ((*AT_BITMAP_PIXEL(b, y, x + 1) && !*AT_BITMAP_PIXEL(b, y - 1, x + 1) - && !*AT_BITMAP_PIXEL(b, y + 1, x - 1) - && (!*AT_BITMAP_PIXEL(b, y, x - 1) || !*AT_BITMAP_PIXEL(b, y + 1, x))) - || (*AT_BITMAP_PIXEL(b, y, x - 1) && !*AT_BITMAP_PIXEL(b, y - 1, x - 1) - && !*AT_BITMAP_PIXEL(b, y + 1, x + 1) && (!*AT_BITMAP_PIXEL(b, y, x + 1) || !*AT_BITMAP_PIXEL(b, y + 1, x))))); - if (k) - continue; - - *AT_BITMAP_PIXEL(b, y, x) |= 02; - } - } - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - if (*AT_BITMAP_PIXEL(b, y, x) & 02) - *AT_BITMAP_PIXEL(b, y, x) = 0; - else if (*AT_BITMAP_PIXEL(b, y, x) > 0) - *AT_BITMAP_PIXEL(b, y, x) = 1; - } - } - - /* Southward bias */ - for (y = 1; y < h - 1; y++) { - for (x = 1; x < w - 1; x++) { - if (*AT_BITMAP_PIXEL(b, y, x) == 0) - continue; - - k = !(*AT_BITMAP_PIXEL(b, y + 1, x) - && ((*AT_BITMAP_PIXEL(b, y, x + 1) && !*AT_BITMAP_PIXEL(b, y + 1, x + 1) - && !*AT_BITMAP_PIXEL(b, y - 1, x - 1) && (!*AT_BITMAP_PIXEL(b, y, x - 1) - || !*AT_BITMAP_PIXEL(b, y - 1, x))) || (*AT_BITMAP_PIXEL(b, y, x - 1) - && !*AT_BITMAP_PIXEL(b, y + 1, x - 1) && !*AT_BITMAP_PIXEL(b, y - 1, x + 1) - && (!*AT_BITMAP_PIXEL(b, y, x + 1) || !*AT_BITMAP_PIXEL(b, y - 1, x))))); - if (k) - continue; - - *AT_BITMAP_PIXEL(b, y, x) |= 02; - } - } - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - if (*AT_BITMAP_PIXEL(b, y, x) & 02) - *AT_BITMAP_PIXEL(b, y, x) = 0; - else if (*AT_BITMAP_PIXEL(b, y, x) > 0) - *AT_BITMAP_PIXEL(b, y, x) = 1; - } - } - - /* Set background pixels to WHITE, foreground pixels to BLACK. */ - for (i = 0; i < num_bytes; i++) - b.bitmap[i] = (b.bitmap[i] == 0 ? WHITE : BLACK); - return b; -} - -gboolean get_edge(bitmap_type b, int y, int x, struct etyp * t) -{ - t->t00 = 0; - t->t01 = 0; - t->t01s = 0; - t->t11 = 0; - check(*AT_BITMAP_PIXEL(b, y - 1, x - 1), *AT_BITMAP_PIXEL(b, y - 1, x), *AT_BITMAP_PIXEL(b, y - 1, x + 1), t); - check(*AT_BITMAP_PIXEL(b, y - 1, x + 1), *AT_BITMAP_PIXEL(b, y, x + 1), *AT_BITMAP_PIXEL(b, y + 1, x + 1), t); - check(*AT_BITMAP_PIXEL(b, y + 1, x + 1), *AT_BITMAP_PIXEL(b, y + 1, x), *AT_BITMAP_PIXEL(b, y + 1, x - 1), t); - check(*AT_BITMAP_PIXEL(b, y + 1, x - 1), *AT_BITMAP_PIXEL(b, y, x - 1), *AT_BITMAP_PIXEL(b, y - 1, x - 1), t); - return *AT_BITMAP_PIXEL(b, y, x) && t->t00 && t->t11 && !t->t01s; -} - -void check(int v1, int v2, int v3, struct etyp *t) -{ - if (!v2 && (!v1 || !v3)) - t->t00 = 1; - if (v2 && (v1 || v3)) - t->t11 = 1; - if ((!v1 && v2) || (!v2 && v3)) { - t->t01s = t->t01; - t->t01 = 1; - } -} -#endif diff --git a/src/autotrace/image-proc.h b/src/autotrace/image-proc.h deleted file mode 100644 index 5c131a7..0000000 --- a/src/autotrace/image-proc.h +++ /dev/null @@ -1,21 +0,0 @@ -/* image-proc.h: image processing routines */ - -#ifndef IMAGE_PROC_H -#define IMAGE_PROC_H - -#include "bitmap.h" -#include "color.h" - -typedef struct { - unsigned height, width; - float **weight; - float **d; -} at_distance_map; - -/* Allocate and compute a new distance map. */ -extern at_distance_map new_distance_map(at_bitmap *, unsigned char target_value, gboolean padded, at_exception_type * exp); - -/* Free the dynamically-allocated storage associated with a distance map. */ -extern void free_distance_map(at_distance_map *); - -#endif /* not IMAGE_PROC_H */ diff --git a/src/autotrace/input-bmp.c b/src/autotrace/input-bmp.c deleted file mode 100644 index ca7c88e..0000000 --- a/src/autotrace/input-bmp.c +++ /dev/null @@ -1,865 +0,0 @@ -/* input-bmp.c: reads any bitmap I could get for testing - - Copyright (C) 1999, 2000, 2001 Martin Weber. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include -#include -#include - -#include "types.h" -#include "bitmap.h" -#include "logreport.h" -#include "xstd.h" -#include "input-bmp.h" - - -#define BI_RGB 0 -#define BI_RLE8 1 -#define BI_RLE4 2 -#define BI_BITFIELDS 3 -#define BI_ALPHABITFIELDS 4 - -#define BitSet(byte, bit) (((byte) & (bit)) == (bit)) - -#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0) - -struct Bitmap_File_Head_Struct { - char zzMagic[2]; /* 00 "BM" */ - unsigned long bfSize; /* 02 */ - unsigned short zzHotX; /* 06 */ - unsigned short zzHotY; /* 08 */ - unsigned long bfOffs; /* 0A */ - unsigned long biSize; /* 0E */ -} Bitmap_File_Head; - -struct Bitmap_Head_Struct { - unsigned long biWidth; /* 12 */ - unsigned long biHeight; /* 16 */ - unsigned short biPlanes; /* 1A */ - unsigned short biBitCnt; /* 1C */ - unsigned long biCompr; /* 1E */ - unsigned long biSizeIm; /* 22 */ - unsigned long biXPels; /* 26 */ - unsigned long biYPels; /* 2A */ - unsigned long biClrUsed; /* 2E */ - unsigned long biClrImp; /* 32 */ - unsigned long masks[4]; /* 36 */ - /* 3A */ -} Bitmap_Head; - -typedef struct -{ - unsigned long mask; - unsigned long shiftin; - float max_value; -} Bitmap_Channel; - -static void -setMasksDefault (unsigned short biBitCnt, - Bitmap_Channel *masks) -{ - switch (biBitCnt) - { - case 32: - masks[0].mask = 0x00ff0000; - masks[0].shiftin = 16; - masks[0].max_value = (float)255.0; - masks[1].mask = 0x0000ff00; - masks[1].shiftin = 8; - masks[1].max_value = (float)255.0; - masks[2].mask = 0x000000ff; - masks[2].shiftin = 0; - masks[2].max_value = (float)255.0; - masks[3].mask = 0x00000000; - masks[3].shiftin = 0; - masks[3].max_value = (float)0.0; - break; - - case 24: - masks[0].mask = 0xff0000; - masks[0].shiftin = 16; - masks[0].max_value = (float)255.0; - masks[1].mask = 0x00ff00; - masks[1].shiftin = 8; - masks[1].max_value = (float)255.0; - masks[2].mask = 0x0000ff; - masks[2].shiftin = 0; - masks[2].max_value = (float)255.0; - masks[3].mask = 0x0; - masks[3].shiftin = 0; - masks[3].max_value = (float)0.0; - break; - - case 16: - masks[0].mask = 0x7c00; - masks[0].shiftin = 10; - masks[0].max_value = (float)31.0; - masks[1].mask = 0x03e0; - masks[1].shiftin = 5; - masks[1].max_value = (float)31.0; - masks[2].mask = 0x001f; - masks[2].shiftin = 0; - masks[2].max_value = (float)31.0; - masks[3].mask = 0x0; - masks[3].shiftin = 0; - masks[3].max_value = (float)0.0; - break; - - default: - break; - } -} - -static long ToL(unsigned char *); -static short ToS(unsigned char *); -static int ReadColorMap(FILE *, unsigned char[256][3], int, int, gboolean *, at_exception_type *); -static gboolean ReadChannelMasks(unsigned int *, Bitmap_Channel *, unsigned int); -static unsigned char *ReadImage(FILE *, int, int, unsigned char[256][3], int, int, int, int, gboolean, const Bitmap_Channel *, at_exception_type *); - -at_bitmap input_bmp_reader(gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data, gpointer user_data) -{ - FILE *fd; - unsigned char buffer[128]; - int ColormapSize, rowbytes, Maps; - gboolean Grey = FALSE; - unsigned char ColorMap[256][3]; - at_bitmap image = at_bitmap_init(0, 0, 0, 1); - unsigned char *image_storage; - at_exception_type exp = at_exception_new(msg_func, msg_data); - char magick[2]; - Bitmap_Channel masks[4]; - - fd = fopen(filename, "rb"); - - if (!fd) { - LOG("Can't open \"%s\"\n", filename); - at_exception_fatal(&exp, "bmp: cannot open input file"); - goto cleanup; - } - - /* It is a File. Now is it a Bitmap? Read the shortest possible header. */ - - if (!ReadOK(fd, magick, 2) || - !(!strncmp(magick, "BA", 2) || - !strncmp(magick, "BM", 2) || - !strncmp(magick, "IC", 2) || - !strncmp(magick, "PT", 2) || - !strncmp(magick, "CI", 2) || - !strncmp(magick, "CP", 2))) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - while (!strncmp(magick, "BA", 2)) - { - if (!ReadOK(fd, buffer, 12)) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - if (!ReadOK(fd, magick, 2)) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - } - - if (!ReadOK(fd, buffer, 12))//// - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - /* bring them to the right byteorder. Not too nice, but it should work */ - - Bitmap_File_Head.bfSize = ToL(&buffer[0x00]); - Bitmap_File_Head.zzHotX = ToS(&buffer[0x04]); - Bitmap_File_Head.zzHotY = ToS(&buffer[0x06]); - Bitmap_File_Head.bfOffs = ToL(&buffer[0x08]); - - if (!ReadOK(fd, buffer, 4)) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - Bitmap_File_Head.biSize = ToL(&buffer[0x00]); - - /* What kind of bitmap is it? */ - - if (Bitmap_File_Head.biSize == 12) { /* OS/2 1.x ? */ - if (!ReadOK(fd, buffer, 8)) { - LOG("Error reading BMP file header\n"); - at_exception_fatal(&exp, "Error reading BMP file header"); - goto cleanup; - } - - Bitmap_Head.biWidth = ToS(&buffer[0x00]); /* 12 */ - Bitmap_Head.biHeight = ToS(&buffer[0x02]); /* 14 */ - Bitmap_Head.biPlanes = ToS(&buffer[0x04]); /* 16 */ - Bitmap_Head.biBitCnt = ToS(&buffer[0x06]); /* 18 */ - Bitmap_Head.biCompr = 0; - Bitmap_Head.biSizeIm = 0; - Bitmap_Head.biXPels = Bitmap_Head.biYPels = 0; - Bitmap_Head.biClrUsed = 0; - Bitmap_Head.biClrImp = 0; - Bitmap_Head.masks[0] = 0; - Bitmap_Head.masks[1] = 0; - Bitmap_Head.masks[2] = 0; - Bitmap_Head.masks[3] = 0; - - memset(masks, 0, sizeof(masks)); - Maps = 3; - - } else if (Bitmap_File_Head.biSize == 40) { /* Windows 3.x */ - if (!ReadOK(fd, buffer, 36)) - { - LOG ("Error reading BMP file header\n"); - at_exception_fatal(&exp, "Error reading BMP file header"); - goto cleanup; - } - - - Bitmap_Head.biWidth = ToL(&buffer[0x00]); /* 12 */ - Bitmap_Head.biHeight = ToL(&buffer[0x04]); /* 16 */ - Bitmap_Head.biPlanes = ToS(&buffer[0x08]); /* 1A */ - Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]); /* 1C */ - Bitmap_Head.biCompr = ToL(&buffer[0x0C]); /* 1E */ - Bitmap_Head.biSizeIm = ToL(&buffer[0x10]); /* 22 */ - Bitmap_Head.biXPels = ToL(&buffer[0x14]); /* 26 */ - Bitmap_Head.biYPels = ToL(&buffer[0x18]); /* 2A */ - Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]); /* 2E */ - Bitmap_Head.biClrImp = ToL(&buffer[0x20]); /* 32 */ - Bitmap_Head.masks[0] = 0; - Bitmap_Head.masks[1] = 0; - Bitmap_Head.masks[2] = 0; - Bitmap_Head.masks[3] = 0; - - Maps = 4; - memset(masks, 0, sizeof(masks)); - - if (Bitmap_Head.biCompr == BI_BITFIELDS) - { - if (!ReadOK(fd, buffer, 3 * sizeof(unsigned long))) - { - LOG("Error reading BMP file header\n"); - at_exception_fatal(&exp, "Error reading BMP file header"); - goto cleanup; - } - - Bitmap_Head.masks[0] = ToL(&buffer[0x00]); - Bitmap_Head.masks[1] = ToL(&buffer[0x04]); - Bitmap_Head.masks[2] = ToL(&buffer[0x08]); - - ReadChannelMasks(&Bitmap_Head.masks[0], masks, 3); - } - else if (Bitmap_Head.biCompr == BI_RGB) - { - setMasksDefault(Bitmap_Head.biBitCnt, masks); - } - else if ((Bitmap_Head.biCompr != BI_RLE4) && - (Bitmap_Head.biCompr != BI_RLE8)) - { - /* BI_ALPHABITFIELDS, etc. */ - LOG("Unsupported compression in BMP file\n"); - at_exception_fatal(&exp, "Unsupported compression in BMP file"); - goto cleanup; - } - } - else if (Bitmap_File_Head.biSize >= 56 && - Bitmap_File_Head.biSize <= 64) - { - /* enhanced Windows format with bit masks */ - - if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4)) - { - - LOG("Error reading BMP file header\n"); - at_exception_fatal(&exp, "Error reading BMP file header"); - goto cleanup; - } - - Bitmap_Head.biWidth = ToL(&buffer[0x00]); /* 12 */ - Bitmap_Head.biHeight = ToL(&buffer[0x04]); /* 16 */ - Bitmap_Head.biPlanes = ToS(&buffer[0x08]); /* 1A */ - Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]); /* 1C */ - Bitmap_Head.biCompr = ToL(&buffer[0x0C]); /* 1E */ - Bitmap_Head.biSizeIm = ToL(&buffer[0x10]); /* 22 */ - Bitmap_Head.biXPels = ToL(&buffer[0x14]); /* 26 */ - Bitmap_Head.biYPels = ToL(&buffer[0x18]); /* 2A */ - Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]); /* 2E */ - Bitmap_Head.biClrImp = ToL(&buffer[0x20]); /* 32 */ - Bitmap_Head.masks[0] = ToL(&buffer[0x24]); /* 36 */ - Bitmap_Head.masks[1] = ToL(&buffer[0x28]); /* 3A */ - Bitmap_Head.masks[2] = ToL(&buffer[0x2C]); /* 3E */ - Bitmap_Head.masks[3] = ToL(&buffer[0x30]); /* 42 */ - - Maps = 4; - ReadChannelMasks(&Bitmap_Head.masks[0], masks, 4); - } - else if (Bitmap_File_Head.biSize == 108 || - Bitmap_File_Head.biSize == 124) - { - /* BMP Version 4 or 5 */ - - if (!ReadOK(fd, buffer, Bitmap_File_Head.biSize - 4)) - { - LOG("Error reading BMP file header\n"); - at_exception_fatal(&exp, "Error reading BMP file header"); - goto cleanup; - } - - Bitmap_Head.biWidth = ToL(&buffer[0x00]); - Bitmap_Head.biHeight = ToL(&buffer[0x04]); - Bitmap_Head.biPlanes = ToS(&buffer[0x08]); - Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]); - Bitmap_Head.biCompr = ToL(&buffer[0x0C]); - Bitmap_Head.biSizeIm = ToL(&buffer[0x10]); - Bitmap_Head.biXPels = ToL(&buffer[0x14]); - Bitmap_Head.biYPels = ToL(&buffer[0x18]); - Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]); - Bitmap_Head.biClrImp = ToL(&buffer[0x20]); - Bitmap_Head.masks[0] = ToL(&buffer[0x24]); - Bitmap_Head.masks[1] = ToL(&buffer[0x28]); - Bitmap_Head.masks[2] = ToL(&buffer[0x2C]); - Bitmap_Head.masks[3] = ToL(&buffer[0x30]); - - Maps = 4; - - if (Bitmap_Head.biCompr == BI_BITFIELDS) - { - ReadChannelMasks(&Bitmap_Head.masks[0], masks, 4); - } - else if (Bitmap_Head.biCompr == BI_RGB) - { - setMasksDefault(Bitmap_Head.biBitCnt, masks); - } - } else { - LOG("Error reading BMP file header\n"); - at_exception_fatal(&exp, "Error reading BMP file header"); - goto cleanup; - } - - /* Valid options 1, 4, 8, 16, 24, 32 */ - /* 16 is awful, we should probably shoot whoever invented it */ - - switch (Bitmap_Head.biBitCnt) - { - case 1: - case 2: - case 4: - case 8: - case 16: - case 24: - case 32: - break; - default: - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - /* There should be some colors used! */ - - ColormapSize = (Bitmap_File_Head.bfOffs - Bitmap_File_Head.biSize - 14) / Maps; - - if ((Bitmap_Head.biClrUsed == 0) && - (Bitmap_Head.biBitCnt <= 8)) - { - ColormapSize = Bitmap_Head.biClrUsed = 1 << Bitmap_Head.biBitCnt; - } - - if (ColormapSize > 256) - ColormapSize = 256; - - /* Sanity checks */ - - if (Bitmap_Head.biHeight == 0 || - Bitmap_Head.biWidth == 0) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - /* biHeight may be negative, but -2147483648 is dangerous because: - -2147483648 == -(-2147483648) */ - if (Bitmap_Head.biWidth < 0 || - Bitmap_Head.biHeight == -2147483648) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - if (Bitmap_Head.biPlanes != 1) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - if (Bitmap_Head.biClrUsed > 256 && - Bitmap_Head.biBitCnt <= 8) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - /* protect against integer overflows caused by malicious BMPs */ - /* use divisions in comparisons to avoid type overflows */ - - if (((unsigned long)Bitmap_Head.biWidth) > (unsigned int)0x7fffffff / Bitmap_Head.biBitCnt || - ((unsigned long)Bitmap_Head.biWidth) > ((unsigned int)0x7fffffff /abs(Bitmap_Head.biHeight)) / 4) - { - LOG("%s is not a valid BMP file", filename); - at_exception_fatal(&exp, "bmp: invalid input file"); - goto cleanup; - } - - /* Windows and OS/2 declare filler so that rows are a multiple of - * word length (32 bits == 4 bytes) - */ - - unsigned long overflowTest = Bitmap_Head.biWidth * Bitmap_Head.biBitCnt; - if (overflowTest / Bitmap_Head.biWidth != Bitmap_Head.biBitCnt) { - LOG("Error reading BMP file header. Width is too large\n"); - at_exception_fatal(&exp, "Error reading BMP file header. Width is too large"); - goto cleanup; - } - - rowbytes = ((Bitmap_Head.biWidth * Bitmap_Head.biBitCnt - 1) / 32) * 4 + 4; - -#ifdef DEBUG - printf("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n", Bitmap_File_Head.bfSize, Bitmap_Head.biClrUsed, Bitmap_Head.biBitCnt, Bitmap_Head.biWidth, Bitmap_Head.biHeight, Bitmap_Head.biCompr, rowbytes); -#endif - - - if (Bitmap_Head.biBitCnt <= 8) - { -#ifdef DEBUG - printf("Colormap read\n"); -#endif - /* Get the Colormap */ - if (!ReadColorMap(fd, ColorMap, ColormapSize, Maps, &Grey, &exp)) - goto cleanup; - } - - fseek(fd, Bitmap_File_Head.bfOffs, SEEK_SET); - - /* Get the Image and return the ID or -1 on error */ - image_storage = ReadImage(fd, - Bitmap_Head.biWidth, Bitmap_Head.biHeight, - ColorMap, - Bitmap_Head.biClrUsed, - Bitmap_Head.biBitCnt, Bitmap_Head.biCompr, rowbytes, - Grey, - masks, - &exp); - - image = at_bitmap_init(image_storage, (unsigned short)Bitmap_Head.biWidth, (unsigned short)Bitmap_Head.biHeight, Grey ? 1 : 3); -cleanup: - fclose(fd); - return (image); -} - -static gboolean ReadColorMap(FILE * fd, unsigned char buffer[256][3], int number, int size, - gboolean *Grey, - at_exception_type *exp) -{ - int i; - unsigned char rgb[4]; - - *Grey = (number > 2); - for (i = 0; i < number; i++) { - if (!ReadOK(fd, rgb, size)) { - LOG ("Bad colormap\n"); - at_exception_fatal (exp, "Bad colormap"); - return FALSE; - } - - /* Bitmap save the colors in another order! But change only once! */ - - buffer[i][0] = rgb[2]; - buffer[i][1] = rgb[1]; - buffer[i][2] = rgb[0]; - *Grey = ((*Grey) && (rgb[0] == rgb[1]) && (rgb[1] == rgb[2])); - } -cleanup: - return TRUE; -} - -static gboolean -ReadChannelMasks(unsigned int *tmp, - Bitmap_Channel *masks, - unsigned int channels) -{ - unsigned int i; - - for (i = 0; i < channels; i++) - { - unsigned int mask; - int nbits, offset, bit; - - mask = tmp[i]; - masks[i].mask = mask; - nbits = 0; - offset = -1; - - for (bit = 0; bit < 32; bit++) - { - if (mask & 1) - { - nbits++; - if (offset == -1) - offset = bit; - } - - mask = mask >> 1; - } - - masks[i].shiftin = offset; - masks[i].max_value = (float)((1 << nbits) - 1); - -#ifdef _DEBUG - LOG4("Channel %d mask %08x in %d max_val %d\n", - i, masks[i].mask, masks[i].shiftin, (int)masks[i].max_value); -#endif - } - - return TRUE; -} - -/*static gint32 -ReadImage(FILE *fd, - const gchar *filename, - gint width, - gint height, - guchar cmap[256][3], - gint ncols, - gint bpp, - gint compression, - gint rowbytes, - gboolean gray, - const BitmapChannel *masks, - GError **error)*/ - - -static unsigned char *ReadImage(FILE * fd, int width, int height, - unsigned char cmap[256][3], - int ncols, - int bpp, int compression, int rowbytes, - gboolean Grey, - const Bitmap_Channel * masks, - at_exception_type * exp) -{ - unsigned char v, n; - int xpos = 0; - int ypos = 0; - unsigned char * image; - unsigned char *dest, *temp, *row_buf; - long rowstride, channels; - unsigned short rgb; - int i, i_max, j; - int total_bytes_read; - unsigned int px32; - - if (!(compression == BI_RGB || - (bpp == 8 && compression == BI_RLE8) || - (bpp == 4 && compression == BI_RLE4) || - (bpp == 16 && compression == BI_BITFIELDS) || - (bpp == 32 && compression == BI_BITFIELDS))) - { - LOG("Unrecognized or invalid BMP compression format.\n"); - at_exception_fatal(exp, "Unrecognized or invalid BMP compression format."); - return NULL; - } - - if (bpp >= 16) { /* color image */ - XMALLOC(image, width * height * 3 * sizeof(unsigned char)); - if (masks[3].mask != 0) - { - channels = 4; - } - else - { - channels = 3; - } - } - else if (Grey) /* Grey image */ - { - XMALLOC(image, width * height * 1 * sizeof(unsigned char)); - channels = 1; - } else { /* indexed image */ - - XMALLOC(image, width * height * 1 * sizeof(unsigned char)); - channels = 1; - } - - /* use XCALLOC to initialize the dest row_buf so that unspecified - pixels in RLE bitmaps show up as the zeroth element in the palette. - */ - XCALLOC(dest, width * height * channels); - XMALLOC (row_buf, rowbytes); - rowstride = width * channels; - - ypos = height - 1; /* Bitmaps begin in the lower left corner */ - - switch (bpp) { - - case 32: - { - while (ReadOK (fd, row_buf, rowbytes)) - { - temp = image + (ypos * rowstride); - for (xpos = 0; xpos < width; ++xpos) { - px32 = ToL(&row_buf[xpos * 4]); - unsigned char red = *(temp++) = ((px32 & masks[0].mask) >> masks[0].shiftin) * 255.0 / masks[0].max_value + 0.5; - unsigned char green = *(temp++) = ((px32 & masks[1].mask) >> masks[1].shiftin) * 255.0 / masks[1].max_value + 0.5; - unsigned char blue = *(temp++) = ((px32 & masks[2].mask) >> masks[2].shiftin) * 255.0 / masks[2].max_value + 0.5; - /* currently alpha channels are not supported by AutoTrace, thus simply ignored */ - /*if (channels > 3) - *(temp++) = ((px32 & masks[3].mask) >> masks[3].shiftin) * 255.0 / masks[3].max_value + 0.5;*/ - } - - if (ypos == 0) - break; - - --ypos; /* next line */ - } - } - break; - - case 24: - { - while (ReadOK (fd, row_buf, rowbytes)) - { - temp = image + (ypos * rowstride); - for (xpos = 0; xpos < width; ++xpos) { - *(temp++) = row_buf[xpos * 3 + 2]; - *(temp++) = row_buf[xpos * 3 + 1]; - *(temp++) = row_buf[xpos * 3]; - } - - if (ypos == 0) - break; - - --ypos; /* next line */ - } - } - break; - - case 16: - { - while (ReadOK (fd, row_buf, rowbytes)) - { - temp = image + (ypos * rowstride); - for (xpos = 0; xpos < width; ++xpos) - { - rgb = ToS(&row_buf[xpos * 2]); - *(temp++) = ((rgb & masks[0].mask) >> masks[0].shiftin) * 255.0 / masks[0].max_value + 0.5; - *(temp++) = ((rgb & masks[1].mask) >> masks[1].shiftin) * 255.0 / masks[1].max_value + 0.5; - *(temp++) = ((rgb & masks[2].mask) >> masks[2].shiftin) * 255.0 / masks[2].max_value + 0.5; - /* currently alpha channels are not supported by AutoTrace, thus simply ignored */ - /*if (channels > 3) - *(temp++) = ((rgb & masks[3].mask) >> masks[3].shiftin) * 255.0 / masks[3].max_value + 0.5;*/ - } - - if (ypos == 0) - break; - - --ypos; /* next line */ - } - } - break; - - case 8: - case 4: - case 1: - { - if (compression == 0) { - while (ReadOK(fd, &v, 1)) { - for (i = 1; (i <= (8 / bpp)) && (xpos < width); i++, xpos++) { - temp = image + (ypos * rowstride) + (xpos * channels); - *temp = (v & (((1 << bpp) - 1) << (8 - (i*bpp)))) >> (8 - (i*bpp)); - if (Grey) - *temp = cmap[*temp][0]; - } - - if (xpos == width) { - ReadOK (fd, row_buf, rowbytes - 1 - (width * bpp - 1) / 8); - ypos--; - xpos = 0; - - } - if (ypos < 0) - break; - } - break; - } else { - /* compressed image (either RLE8 or RLE4) */ - while (ypos >= 0 && xpos <= width) { - if (!ReadOK(fd, row_buf, 2)) - { - LOG("The bitmap ends unexpectedly."); - break; - } - - if ((unsigned char) row_buf[0] != 0) - /* Count + Color - record */ - { - /* encoded mode run - - * row_buf[0] == run_length - * row_buf[1] == pixel data - */ - for (j = 0; ((unsigned char) j < (unsigned char) row_buf[0]) && (xpos < width);) - { -#ifdef DEBUG2 - printf("%u %u | ", xpos, width); -#endif - for (i = 1; - ((i <= (8 / bpp)) && - (xpos < width) && - ((unsigned char) j < (unsigned char) row_buf[0])); - i++, xpos++, j++) - { - temp = dest + (ypos * rowstride) + (xpos * channels); - *temp = (unsigned char) ((row_buf[1] & (((1<> (8 - (i * bpp))); - if (Grey) - *temp = cmap[*temp][0]; - } - } - } - if ((row_buf[0] == 0) && (row_buf[1] > 2)) - /* uncompressed record */ - { - n = row_buf[1]; - total_bytes_read = 0; - - for (j = 0; j < n; j += (8 / bpp)) - { - /* read the next byte in the record */ - if (!ReadOK(fd, &v, 1)) - { - LOG("The bitmap ends unexpectedly."); - break; - } - - total_bytes_read++; - - /* read all pixels from that byte */ - i_max = 8 / bpp; - if (n - j < i_max) - { - i_max = n - j; - } - - i = 1; - while ((i <= i_max) && (xpos < width)) - { - temp = - dest + (ypos * rowstride) + (xpos * channels); - *temp = (v >> (8 - (i*bpp))) & ((1 << bpp) - 1); - if (Grey) - *temp = cmap[*temp][0]; - i++; - xpos++; - } - } - - /* absolute mode runs are padded to 16-bit alignment */ - if (total_bytes_read % 2) - fread(&v, 1, 1, fd); //ReadOk - } - if (((unsigned char) row_buf[0] == 0) && ((unsigned char) row_buf[1]==0)) - /* Line end */ - { - ypos--; - xpos = 0; - } - if (((unsigned char)row_buf[0] == 0) && ((unsigned char)row_buf[1] == 1)) - /* Bitmap end */ - { - break; - } - if (((unsigned char)row_buf[0] == 0) && ((unsigned char)row_buf[1] == 2)) - /* Deltarecord */ - { - if (!ReadOK(fd, row_buf, 2)) - { - LOG("The bitmap ends unexpectedly."); - break; - } - xpos += (unsigned char) row_buf[0]; - ypos -= (unsigned char) row_buf[1]; - } - } - break; - } - } - break; - default: - /* This is very bad, we should not be here */ - break; - } - - if (bpp <= 8) { - unsigned char *temp2, *temp3; - unsigned char index; - temp2 = temp = image; - XMALLOC (image, width * height * 3 * sizeof (unsigned char)); //??? - temp3 = image; - for (ypos = 0; ypos < height; ypos++) { - for (xpos = 0; xpos < width; xpos++) { - index = *temp2++; - *temp3++ = cmap[index][0]; - if (!Grey) { - *temp3++ = cmap[index][1]; - *temp3++ = cmap[index][2]; - } - } - } - free(temp); - } - - free (row_buf); - free(dest); - return image; -} - -static long ToL(unsigned char *puffer) -{ - return (puffer[0] | puffer[1] << 8 | puffer[2] << 16 | puffer[3] << 24); -} - -static short ToS(unsigned char *puffer) -{ - return ((short)(puffer[0] | puffer[1] << 8)); -} diff --git a/src/autotrace/input-bmp.h b/src/autotrace/input-bmp.h deleted file mode 100644 index 6aa77d1..0000000 --- a/src/autotrace/input-bmp.h +++ /dev/null @@ -1,27 +0,0 @@ -/* input-bmp.h: import bmp files - - Copyright (C) 1999, 2000, 2001 Martin Weber. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifndef INPUT_BMP_H -#define INPUT_BMP_H - -#include "input.h" - -at_bitmap input_bmp_reader(gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data, gpointer user_data); - -#endif /* not INPUT_BMP_H */ diff --git a/src/autotrace/input-png.c b/src/autotrace/input-png.c deleted file mode 100644 index 7064b97..0000000 --- a/src/autotrace/input-png.c +++ /dev/null @@ -1,193 +0,0 @@ -/* input-png.c: PNG loader for autotrace - - Copyright (C) 2000 MenTaLguY - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include -#include -#include - -#include "types.h" -#include "bitmap.h" -#include "logreport.h" -#include "xstd.h" -#include -#include "input-png.h" - -static png_bytep *read_png(png_structp png_ptr, png_infop info_ptr, at_input_opts_type * opts); - -/* for pre-1.0.6 versions of libpng */ -#ifndef png_jmpbuf -# define png_jmpbuf(png_ptr) (png_ptr)->jmpbuf -#endif - -static void handle_warning(png_structp png, const gchar * message) -{ - LOG("PNG warning: %s", message); - at_exception_warning((at_exception_type *) png_get_error_ptr(png), message); - /* at_exception_fatal((at_exception_type *)at_png->error_ptr, - "PNG warning"); */ -} - -static void handle_error(png_structp png, const gchar * message) -{ - LOG("PNG error: %s", message); - at_exception_fatal((at_exception_type *) png_get_error_ptr(png), message); - /* at_exception_fatal((at_exception_type *)at_png->error_ptr, - "PNG error"); */ - -} - -static void finalize_structs(png_structp png, png_infop info, png_infop end_info) -{ - png_destroy_read_struct(png ? &png : NULL, info ? &info : NULL, end_info ? &end_info : NULL); -} - -static int init_structs(png_structp * png, png_infop * info, png_infop * end_info, at_exception_type * exp) -{ - *png = NULL; - *info = *end_info = NULL; - - *png = png_create_read_struct(PNG_LIBPNG_VER_STRING, exp, (png_error_ptr) handle_error, (png_error_ptr) handle_warning); - - if (*png) { - *info = png_create_info_struct(*png); - if (*info) { - *end_info = png_create_info_struct(*png); - if (*end_info) - return 1; - } - finalize_structs(*png, *info, *end_info); - } - return 0; -} - -#define CHECK_ERROR() do { if (at_exception_got_fatal(exp)) \ - { \ - result = 0; \ - goto cleanup; \ - } } while (0) - -static int load_image(at_bitmap * image, FILE * stream, at_input_opts_type * opts, at_exception_type * exp) -{ - png_structp png; - png_infop info, end_info; - png_bytep *rows; - unsigned short width, height, row; - int pixel_size; - int result = 1; - - if (!init_structs(&png, &info, &end_info, exp)) - return 0; - - png_init_io(png, stream); - CHECK_ERROR(); - - rows = read_png(png, info, opts); - - width = (unsigned short)png_get_image_width(png, info); - height = (unsigned short)png_get_image_height(png, info); - if (png_get_color_type(png, info) == PNG_COLOR_TYPE_GRAY) { - pixel_size = 1; - } else { - pixel_size = 3; - } - - *image = at_bitmap_init(NULL, width, height, pixel_size); - for (row = 0; row < height; row++, rows++) { - memcpy(AT_BITMAP_PIXEL(image, row, 0), *rows, width * pixel_size * sizeof(unsigned char)); - } -cleanup: - finalize_structs(png, info, end_info); - return result; -} - -at_bitmap input_png_reader(gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data, gpointer user_data) -{ - FILE *stream; - at_bitmap image = at_bitmap_init(0, 0, 0, 1); - at_exception_type exp = at_exception_new(msg_func, msg_data); - - stream = fopen(filename, "rb"); - if (!stream) { - LOG("Can't open \"%s\"\n", filename); - at_exception_fatal(&exp, "Cannot open input png file"); - return image; - } - - load_image(&image, stream, opts, &exp); - fclose(stream); - - return image; -} - -static png_bytep *read_image(png_structp png_ptr, png_infop info_ptr) -{ - unsigned width, height, y; - png_bytep *rows; - - width = png_get_rowbytes(png_ptr, info_ptr); - height = png_get_image_height(png_ptr, info_ptr); - rows = (png_bytep *) png_malloc(png_ptr, height * sizeof(png_bytep)); - for (y = 0; y < height; y++) { - rows[y] = (png_bytep) png_malloc(png_ptr, width); - } - - png_read_image(png_ptr, rows); - return rows; -} - -static png_bytep *read_png(png_structp png_ptr, png_infop info_ptr, at_input_opts_type * opts) -{ - png_color_16p original_bg; - png_color_16 my_bg; - png_bytep *rows; - - png_read_info(png_ptr, info_ptr); - - png_set_strip_16(png_ptr); - png_set_packing(png_ptr); - if ((png_get_bit_depth(png_ptr, info_ptr) < 8) || (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) || (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) - png_set_expand(png_ptr); - - if (png_get_bKGD(png_ptr, info_ptr, &original_bg)) { - /* Fill transparent region with ... */ - my_bg.index = 0; - - if (opts && opts->background_color) { - my_bg.red = 256 * opts->background_color->r; - my_bg.green = 256 * opts->background_color->g; - my_bg.blue = 256 * opts->background_color->b; - my_bg.gray = 256 * ((opts->background_color->r + opts->background_color->g + opts->background_color->b) / 3); - } else - /* else, use white */ - my_bg.red = my_bg.green = my_bg.blue = my_bg.gray = 0xFFFF; - - png_set_background(png_ptr, &my_bg, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); - } else - png_set_strip_alpha(png_ptr); - png_set_interlace_handling(png_ptr); - png_read_update_info(png_ptr, info_ptr); - - rows = read_image(png_ptr, info_ptr); - png_read_end(png_ptr, info_ptr); - return rows; -} diff --git a/src/autotrace/input-png.h b/src/autotrace/input-png.h deleted file mode 100644 index 7de166d..0000000 --- a/src/autotrace/input-png.h +++ /dev/null @@ -1,29 +0,0 @@ -/* input-png.h: import png files. - - Copyright (C) 2000 MenTaLguY - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -/* $Id: input-png.h,v 1.10 2003/08/17 10:18:27 masata-y Exp $ */ - -#ifndef INPUT_PNG_H -#define INPUT_PNG_H - -#include "input.h" - -at_bitmap input_png_reader(gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data, gpointer user_data); - -#endif /* not INPUT_PNG_H */ diff --git a/src/autotrace/input.c b/src/autotrace/input.c deleted file mode 100644 index 42ca359..0000000 --- a/src/autotrace/input.c +++ /dev/null @@ -1,232 +0,0 @@ -/* input.c: interface for input handlers - - Copyright (C) 1999, 2000, 2001 Bernhard Herzog. - Copyright (C) 2003 Masatake YAMATO - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "autotrace.h" -#include "private.h" -#include "input.h" -#include "xstd.h" -#include "filename.h" -#include -#include - -typedef struct _at_input_format_entry at_input_format_entry; -struct _at_input_format_entry { - at_bitmap_reader reader; - const gchar *descr; - GDestroyNotify user_data_destroy_func; -}; - -static GHashTable *at_input_formats = NULL; -static at_input_format_entry *at_input_format_new(const char *descr, at_input_func reader, gpointer user_data, GDestroyNotify user_data_destroy_func); -static void at_input_format_free(at_input_format_entry * entry); - -/* - * Helper functions - */ -static void input_list_set(gpointer key, gpointer value, gpointer user_data); -static void input_list_strlen(gpointer key, gpointer value, gpointer user_data); -static void input_list_strcat(gpointer key, gpointer value, gpointer user_data); - -/** - * at_input_init: - * Initialize at_input input plugin sub system. - * - * Return value: 1 for success, else for failure - **/ -int at_input_init(void) -{ - if (at_input_formats) - return 1; - - at_input_formats = g_hash_table_new_full(g_str_hash, (GEqualFunc) g_str_equal, g_free, (GDestroyNotify) at_input_format_free); - if (!at_input_formats) - return 0; - return 1; -} - -static at_input_format_entry *at_input_format_new(const gchar * descr, at_input_func reader, gpointer user_data, GDestroyNotify user_data_destroy_func) -{ - at_input_format_entry *entry; - entry = g_malloc(sizeof(at_input_format_entry)); - if (entry) { - entry->reader.func = reader; - entry->reader.data = user_data; - entry->descr = g_strdup(descr); - entry->user_data_destroy_func = user_data_destroy_func; - } - return entry; -} - -static void at_input_format_free(at_input_format_entry * entry) -{ - g_free((gpointer) entry->descr); - if (entry->user_data_destroy_func) - entry->user_data_destroy_func(entry->reader.data); - g_free(entry); - -} - -int at_input_add_handler(const gchar * suffix, const gchar * description, at_input_func reader) -{ - return at_input_add_handler_full(suffix, description, reader, 0, NULL, NULL); -} - -int at_input_add_handler_full(const gchar * suffix, const gchar * description, at_input_func reader, gboolean override, gpointer user_data, GDestroyNotify user_data_destroy_func) -{ - gchar *gsuffix_raw; - gchar *gsuffix; - const gchar *gdescription; - at_input_format_entry *old_entry; - at_input_format_entry *new_entry; - - g_return_val_if_fail(suffix, 0); - g_return_val_if_fail(description, 0); - g_return_val_if_fail(reader, 0); - - gsuffix_raw = g_strdup((gchar *) suffix); - g_return_val_if_fail(gsuffix_raw, 0); - gsuffix = g_ascii_strdown(gsuffix_raw, strlen(gsuffix_raw)); - g_free(gsuffix_raw); - - gdescription = (const gchar *)description; - - old_entry = g_hash_table_lookup(at_input_formats, gsuffix); - if (old_entry && !override) { - g_free(gsuffix); - return 1; - } - - new_entry = at_input_format_new(gdescription, reader, user_data, user_data_destroy_func); - g_return_val_if_fail(new_entry, 0); - - g_hash_table_replace(at_input_formats, gsuffix, new_entry); - return 1; -} - -at_bitmap_reader *at_input_get_handler(gchar * filename) -{ - char *ext = find_suffix(filename); - if (ext == NULL) - ext = ""; - - printf("Extension: %s\n", ext); - return at_input_get_handler_by_suffix(ext); -} - -at_bitmap_reader *at_input_get_handler_by_suffix(gchar * suffix) -{ - at_input_format_entry *format; - gchar *gsuffix_raw; - gchar *gsuffix; - - if (!suffix || suffix[0] == '\0') - return NULL; - - gsuffix_raw = g_strdup(suffix); - g_return_val_if_fail(gsuffix_raw, NULL); - gsuffix = g_ascii_strdown(gsuffix_raw, strlen(gsuffix_raw)); - g_free(gsuffix_raw); - format = g_hash_table_lookup(at_input_formats, gsuffix); - g_free(gsuffix); - - if (format) - return &(format->reader); - else - return NULL; -} - -const char **at_input_list_new(void) -{ - char **list, **tmp; - gint format_count; - gint list_count; - - format_count = g_hash_table_size(at_input_formats); - list_count = 2 * format_count; - list = g_new(gchar *, list_count + 1); - list[list_count] = NULL; - - tmp = list; - g_hash_table_foreach(at_input_formats, input_list_set, &tmp); - return (const char **)list; -} - -void at_input_list_free(const char **list) -{ - free((char **)list); -} - -char *at_input_shortlist(void) -{ - gint length = 0, count; - char *list, *tmp; - g_hash_table_foreach(at_input_formats, input_list_strlen, &length); - count = g_hash_table_size(at_input_formats); - - /* 2 for ", " */ - length += (2 * count); - list = g_malloc(length + 1); - list[0] = '\0'; - - tmp = list; - g_hash_table_foreach(at_input_formats, input_list_strcat, &tmp); - - /* remove final ", " */ - g_return_val_if_fail(list[length - 2] == ',', NULL); - list[length - 2] = '\0'; - return list; -} - -static void input_list_set(gpointer key, gpointer value, gpointer user_data) -{ - at_input_format_entry *format = value; - const char ***list_ptr = user_data; - const char **list = *list_ptr; - list[0] = key; - list[1] = format->descr; - *list_ptr = &(list[2]); -} - -static void input_list_strlen(gpointer key, gpointer value, gpointer user_data) -{ - gint *length; - g_return_if_fail(key); - g_return_if_fail(user_data); - - length = user_data; - *length += strlen(key); -} - -static void input_list_strcat(gpointer key, gpointer value, gpointer user_data) -{ - gchar **list_ptr; - gchar *list; - list_ptr = user_data; - list = *list_ptr; - strcat(list, key); - strcat(list, ", "); - - /* 2 for ", " */ - *list_ptr = list + strlen(key) + 2; -} diff --git a/src/autotrace/input.h b/src/autotrace/input.h deleted file mode 100644 index b4a7d9b..0000000 --- a/src/autotrace/input.h +++ /dev/null @@ -1,95 +0,0 @@ -/* input.h: interface for input handlers - - Copyright (C) 1999, 2000, 2001 Bernhard Herzog. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifndef INPUT_H -#define INPUT_H -#include "types.h" -#include "autotrace.h" -#include "exception.h" - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Input handler should be implemented with using - following functions and macros. */ - - typedef - at_bitmap(*at_input_func) (gchar * name, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data, gpointer user_data); - -/* at_input_add_handler - Register an input handler to autotrace. - If a handler for the suffix is already existed, do nothing. */ - extern int at_input_add_handler(const gchar * suffix, const gchar * description, at_input_func reader); -/* at_input_add_handler_full - If OVERRIDE is TRUE and if the old handler for suffix is existed, - remove the old handler first then add new handler. - If OVERRIDE is false, do nothing. */ - extern int at_input_add_handler_full(const gchar * suffix, const gchar * description, at_input_func reader, gboolean override, gpointer user_data, GDestroyNotify user_data_destroy_func); - -/* at_bitmap_init - Return initialized at_bitmap value. - - args: - AREA is used as a storage of returned value. - If AREA is NULL, the storage is newly allocated - by at_bitmap_init. In such case the size of storage - is automatically calculated by WIDTH, HEIGHT and PLANES. - - PLANES must be 1(gray scale) or 3(RGB color). - - return value: - The return value is not newly allocated. - Only the storage is allocated if AREA is NULL. - On the other hand, at_bitmap_new allocates - mem for at_bitmap; and returns a pointer - for the mem. - at_bitmap_new is for autotrace library user. - at_bitmap_init is for input-handler developer. - Don't use at_bitmap_new in your input-handler. */ - extern at_bitmap at_bitmap_init(unsigned char *area, unsigned short width, unsigned short height, unsigned int planes); - -/* TODO: free storage */ - -/* The number of color planes of each pixel */ -#define AT_BITMAP_PLANES(b) ((b)->np) - -/* The pixels, represented as an array of bytes (in contiguous storage). - Each pixel is represented by np bytes. */ -#define AT_BITMAP_BITS(b) ((b)->bitmap) - -/* These are convenient abbreviations for geting inside the members. */ -#define AT_BITMAP_WIDTH(b) ((b)->width) -#define AT_BITMAP_HEIGHT(b) ((b)->height) - -/* This is the pixel at [ROW,COL]. */ -#define AT_BITMAP_PIXEL(b, row, col) \ - ((AT_BITMAP_BITS (b) + (row) * AT_BITMAP_PLANES (b) * AT_BITMAP_WIDTH (b) \ - + (col) * AT_BITMAP_PLANES(b))) - -/* at_ prefix removed version */ -#define AT_BITMAP_VALID_PIXEL(b, row, col) \ - ((row) < AT_BITMAP_HEIGHT (b) && (col) < AT_BITMAP_WIDTH (b)) - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* Not def: INPUT_H */ diff --git a/src/autotrace/intl.h b/src/autotrace/intl.h deleted file mode 100644 index 7cc6f81..0000000 --- a/src/autotrace/intl.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __INTL_H__ -#define __INTL_H__ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#ifdef ENABLE_NLS -#include -#define _(String) dgettext(PACKAGE,String) -#define N_(String) (String) -#else /* NLS is disabled */ -#define _(String) (String) -#define N_(String) (String) -#define textdomain(String) (String) -#define gettext(String) (String) -#define dgettext(Domain,String) (String) -#define dcgettext(Domain,String,Type) (String) -#define bindtextdomain(Domain,Directory) (Domain) -#endif - -#endif diff --git a/src/autotrace/logreport.c b/src/autotrace/logreport.c deleted file mode 100644 index 7784a7c..0000000 --- a/src/autotrace/logreport.c +++ /dev/null @@ -1,9 +0,0 @@ -/* logreport.c: showing information to the user. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "logreport.h" - -gboolean logging = FALSE; diff --git a/src/autotrace/logreport.h b/src/autotrace/logreport.h deleted file mode 100644 index 04a548c..0000000 --- a/src/autotrace/logreport.h +++ /dev/null @@ -1,32 +0,0 @@ -/* logreport.h: status reporting routines. */ - -#ifndef LOGREPORT_H -#define LOGREPORT_H - -#include -#include "types.h" -#include - -#ifdef _EXPORTING -#define DECLSPEC __declspec(dllexport) -#elif _IMPORTING -#define DECLSPEC __declspec(dllimport) -#else -#define DECLSPEC -#endif - -/* Whether to write a log */ -extern gboolean logging; - -#define LOG(...) \ - do { if (logging) fprintf (stdout, __VA_ARGS__); } while (0) - -/* Define common sorts of messages. */ - -#define FATAL(...) \ - do { fputs ("fatal: ", stderr); LOG("fatal: "); fprintf (stderr, __VA_ARGS__); LOG (__VA_ARGS__); fputs (".\n", stderr); exit (1); } while (0) - -#define WARNING(...) \ - do { fputs ("warning: ", stderr); LOG ("warning: "); fprintf (stderr, __VA_ARGS__); LOG (__VA_ARGS__); fputs (".\n", stderr); } while (0) - -#endif /* not LOGREPORT_H */ diff --git a/src/autotrace/median.c b/src/autotrace/median.c deleted file mode 100644 index 60e8c7f..0000000 --- a/src/autotrace/median.c +++ /dev/null @@ -1,863 +0,0 @@ -/* median.c: median cut - reducing a high color bitmap to certain number of colors - - Copyright (C) 2001, 2002 Martin Weber - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include -#include -#include -#include "logreport.h" -#include "xstd.h" -#include "quantize.h" - -#define MAXNUMCOLORS 256 - -#if 0 -#define R_SCALE -#define G_SCALE -#define B_SCALE -#else - -/* scale RGB distances by *2,*3,*1 */ -#define R_SCALE <<1 -#define G_SCALE *3 -#define B_SCALE -#endif - -#define BITS_IN_SAMPLE 8 - -#define R_SHIFT (BITS_IN_SAMPLE - PRECISION_R) -#define G_SHIFT (BITS_IN_SAMPLE - PRECISION_G) -#define B_SHIFT (BITS_IN_SAMPLE - PRECISION_B) - -typedef struct { - /* The bounds of the box (inclusive); expressed as histogram indexes */ - int Rmin, Rmax; - int Gmin, Gmax; - int Bmin, Bmax; - /* The volume (actually 2-norm) of the box */ - int volume; - /* The number of nonzero histogram cells within this box */ - long colorcount; -} box, *boxptr; - -static void zero_histogram_rgb(Histogram histogram) -{ - int r, g, b; - for (r = 0; r < HIST_R_ELEMS; r++) - for (g = 0; g < HIST_G_ELEMS; g++) - for (b = 0; b < HIST_B_ELEMS; b++) - histogram[r * MR + g * MG + b] = 0; -} - -static void generate_histogram_rgb(Histogram histogram, at_bitmap * image, const at_color * ignoreColor) -{ - unsigned char *src = image->bitmap; - int num_elems; - ColorFreq *col; - - num_elems = AT_BITMAP_WIDTH(image) * AT_BITMAP_HEIGHT(image); - zero_histogram_rgb(histogram); - - switch (AT_BITMAP_PLANES(image)) { - case 3: - while (num_elems--) { - /* If we have an ignorecolor, skip it. */ - if (ignoreColor) { - if ((src[0] == ignoreColor->r) - && (src[1] == ignoreColor->g) - && (src[2] == ignoreColor->b)) { - src += 3; - continue; - } - } - col = &histogram[(src[0] >> R_SHIFT) * MR + (src[1] >> G_SHIFT) * MG + (src[2] >> B_SHIFT)]; - (*col)++; - src += 3; - } - break; - - case 1: - while (--num_elems >= 0) { - if (ignoreColor && src[num_elems] == ignoreColor->r) - continue; - col = &histogram[(src[num_elems] >> R_SHIFT) * MR + (src[num_elems] >> G_SHIFT) * MG + (src[num_elems] >> B_SHIFT)]; - (*col)++; - } - break; - default: - /* To avoid compiler warning */ ; - } -} - -static boxptr find_biggest_volume(boxptr boxlist, int numboxes) -/* Find the splittable box with the largest (scaled) volume */ -/* Returns 0 if no splittable boxes remain */ -{ - boxptr boxp; - int i; - int maxv = 0; - boxptr which = 0; - - for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { - if (boxp->volume > maxv) { - which = boxp; - maxv = boxp->volume; - } - } - - return which; -} - -static void update_box_rgb(Histogram histogram, boxptr boxp) -/* Shrink the min/max bounds of a box to enclose only nonzero elements, */ -/* and recompute its volume and population */ -{ - ColorFreq *histp; - int R, G, B; - int Rmin, Rmax, Gmin, Gmax, Bmin, Bmax; - int dist0, dist1, dist2; - long ccount; - - Rmin = boxp->Rmin; - Rmax = boxp->Rmax; - Gmin = boxp->Gmin; - Gmax = boxp->Gmax; - Bmin = boxp->Bmin; - Bmax = boxp->Bmax; - - if (Rmax > Rmin) - for (R = Rmin; R <= Rmax; R++) - for (G = Gmin; G <= Gmax; G++) { - histp = histogram + R * MR + G * MG + Bmin; - for (B = Bmin; B <= Bmax; B++) - if (*histp++ != 0) { - boxp->Rmin = Rmin = R; - goto have_Rmin; - } - } -have_Rmin: - if (Rmax > Rmin) - for (R = Rmax; R >= Rmin; R--) - for (G = Gmin; G <= Gmax; G++) { - histp = histogram + R * MR + G * MG + Bmin; - for (B = Bmin; B <= Bmax; B++) - if (*histp++ != 0) { - boxp->Rmax = Rmax = R; - goto have_Rmax; - } - } -have_Rmax: - if (Gmax > Gmin) - for (G = Gmin; G <= Gmax; G++) - for (R = Rmin; R <= Rmax; R++) { - histp = histogram + R * MR + G * MG + Bmin; - for (B = Bmin; B <= Bmax; B++) - if (*histp++ != 0) { - boxp->Gmin = Gmin = G; - goto have_Gmin; - } - } -have_Gmin: - if (Gmax > Gmin) - for (G = Gmax; G >= Gmin; G--) - for (R = Rmin; R <= Rmax; R++) { - histp = histogram + R * MR + G * MG + Bmin; - for (B = Bmin; B <= Bmax; B++) - if (*histp++ != 0) { - boxp->Gmax = Gmax = G; - goto have_Gmax; - } - } -have_Gmax: - if (Bmax > Bmin) - for (B = Bmin; B <= Bmax; B++) - for (R = Rmin; R <= Rmax; R++) { - histp = histogram + R * MR + Gmin * MG + B; - for (G = Gmin; G <= Gmax; G++, histp += MG) - if (*histp != 0) { - boxp->Bmin = Bmin = B; - goto have_Bmin; - } - } -have_Bmin: - if (Bmax > Bmin) - for (B = Bmax; B >= Bmin; B--) - for (R = Rmin; R <= Rmax; R++) { - histp = histogram + R * MR + Gmin * MG + B; - for (G = Gmin; G <= Gmax; G++, histp += MG) - if (*histp != 0) { - boxp->Bmax = Bmax = B; - goto have_Bmax; - } - } -have_Bmax: - - /* Update box volume. - * We use 2-norm rather than real volume here; this biases the method - * against making long narrow boxes, and it has the side benefit that - * a box is splittable iff norm > 0. - * Since the differences are expressed in histogram-cell units, - * we have to shift back to JSAMPLE units to get consistent distances; - * after which, we scale according to the selected distance scale factors. - */ - dist0 = Rmax - Rmin; - dist1 = Gmax - Gmin; - dist2 = Bmax - Bmin; - boxp->volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2; - - /* Now scan remaining volume of box and compute population */ - ccount = 0; - for (R = Rmin; R <= Rmax; R++) - for (G = Gmin; G <= Gmax; G++) { - histp = histogram + R * MR + G * MG + Bmin; - for (B = Bmin; B <= Bmax; B++, histp++) - if (*histp != 0) { - ccount++; - } - } - - boxp->colorcount = ccount; -} - -static int median_cut_rgb(Histogram histogram, boxptr boxlist, int numboxes, int desired_colors) -/* Repeatedly select and split the largest box until we have enough boxes */ -{ - int n, lb; - int R, G, B, cmax; - boxptr b1, b2; - - while (numboxes < desired_colors) { - /* Select box to split. - * Current algorithm: by population for first half, then by volume. - */ - b1 = find_biggest_volume(boxlist, numboxes); - - if (b1 == 0) /* no splittable boxes left! */ - break; - b2 = boxlist + numboxes; /* where new box will go */ - /* Copy the color bounds to the new box. */ - b2->Rmax = b1->Rmax; - b2->Gmax = b1->Gmax; - b2->Bmax = b1->Bmax; - b2->Rmin = b1->Rmin; - b2->Gmin = b1->Gmin; - b2->Bmin = b1->Bmin; - /* Choose which axis to split the box on. - * Current algorithm: longest scaled axis. - * See notes in update_box about scaling distances. - */ - R = b1->Rmax - b1->Rmin; - G = b1->Gmax - b1->Gmin; - B = b1->Bmax - b1->Bmin; - /* We want to break any ties in favor of green, then red, blue last. - */ - cmax = G; - n = 1; - if (R > cmax) { - cmax = R; - n = 0; - } - if (B > cmax) { - n = 2; - } - /* Choose split point along selected axis, and update box bounds. - * Current algorithm: split at halfway point. - * (Since the box has been shrunk to minimum volume, - * any split will produce two nonempty subboxes.) - * Note that lb value is max for lower box, so must be < old max. - */ - switch (n) { - case 0: - lb = (b1->Rmax + b1->Rmin) / 2; - b1->Rmax = lb; - b2->Rmin = lb + 1; - break; - case 1: - lb = (b1->Gmax + b1->Gmin) / 2; - b1->Gmax = lb; - b2->Gmin = lb + 1; - break; - case 2: - lb = (b1->Bmax + b1->Bmin) / 2; - b1->Bmax = lb; - b2->Bmin = lb + 1; - break; - } - /* Update stats for boxes */ - update_box_rgb(histogram, b1); - update_box_rgb(histogram, b2); - numboxes++; - } - return numboxes; -} - -static void compute_color_rgb(QuantizeObj * quantobj, Histogram histogram, boxptr boxp, int icolor) -/* Compute representative color for a box, put it in colormap[icolor] */ -{ - /* Current algorithm: mean weighted by pixels (not colors) */ - /* Note it is important to get the rounding correct! */ - ColorFreq *histp; - int R, G, B; - int Rmin, Rmax; - int Gmin, Gmax; - int Bmin, Bmax; - unsigned long count; - unsigned long total = 0; - unsigned long Rtotal = 0; - unsigned long Gtotal = 0; - unsigned long Btotal = 0; - - Rmin = boxp->Rmin; - Rmax = boxp->Rmax; - Gmin = boxp->Gmin; - Gmax = boxp->Gmax; - Bmin = boxp->Bmin; - Bmax = boxp->Bmax; - - for (R = Rmin; R <= Rmax; R++) - for (G = Gmin; G <= Gmax; G++) { - histp = histogram + R * MR + G * MG + Bmin; - for (B = Bmin; B <= Bmax; B++) { - if ((count = *histp++) != 0) { - total += count; - Rtotal += ((R << R_SHIFT) + ((1 << R_SHIFT) >> 1)) * count; - Gtotal += ((G << G_SHIFT) + ((1 << G_SHIFT) >> 1)) * count; - Btotal += ((B << B_SHIFT) + ((1 << B_SHIFT) >> 1)) * count; - } - } - } - - quantobj->cmap[icolor].r = (unsigned char)((Rtotal + (total >> 1)) / total); - quantobj->cmap[icolor].g = (unsigned char)((Gtotal + (total >> 1)) / total); - quantobj->cmap[icolor].b = (unsigned char)((Btotal + (total >> 1)) / total); - quantobj->freq[icolor] = total; -} - -static void select_colors_rgb(QuantizeObj * quantobj, Histogram histogram) -/* Master routine for color selection */ -{ - boxptr boxlist; - int numboxes; - int desired = quantobj->desired_number_of_colors; - int i; - - /* Allocate workspace for box list */ - XMALLOC(boxlist, desired * sizeof(box)); - - /* Initialize one box containing whole space */ - numboxes = 1; - boxlist[0].Rmin = 0; - boxlist[0].Rmax = (1 << PRECISION_R) - 1; - boxlist[0].Gmin = 0; - boxlist[0].Gmax = (1 << PRECISION_G) - 1; - boxlist[0].Bmin = 0; - boxlist[0].Bmax = (1 << PRECISION_B) - 1; - /* Shrink it to actually-used volume and set its statistics */ - update_box_rgb(histogram, boxlist); - /* Perform median-cut to produce final box list */ - numboxes = median_cut_rgb(histogram, boxlist, numboxes, desired); - quantobj->actual_number_of_colors = numboxes; - /* Compute the representative color for each box, fill colormap */ - for (i = 0; i < numboxes; i++) - compute_color_rgb(quantobj, histogram, boxlist + i, i); - free(boxlist); -} - -/* - * These routines are concerned with the time-critical task of mapping input - * colors to the nearest color in the selected colormap. - * - * We re-use the histogram space as an "inverse color map", essentially a - * cache for the results of nearest-color searches. All colors within a - * histogram cell will be mapped to the same colormap entry, namely the one - * closest to the cell's center. This may not be quite the closest entry to - * the actual input color, but it's almost as good. A zero in the cache - * indicates we haven't found the nearest color for that cell yet; the array - * is cleared to zeroes before starting the mapping pass. When we find the - * nearest color for a cell, its colormap index plus one is recorded in the - * cache for future use. The pass2 scanning routines call fill_inverse_cmap - * when they need to use an unfilled entry in the cache. - * - * Our method of efficiently finding nearest colors is based on the "locally - * sorted search" idea described by Heckbert and on the incremental distance - * calculation described by Spencer W. Thomas in chapter III.1 of Graphics - * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that - * the distances from a given colormap entry to each cell of the histogram can - * be computed quickly using an incremental method: the differences between - * distances to adjacent cells themselves differ by a constant. This allows a - * fairly fast implementation of the "brute force" approach of computing the - * distance from every colormap entry to every histogram cell. Unfortunately, - * it needs a work array to hold the best-distance-so-far for each histogram - * cell (because the inner loop has to be over cells, not colormap entries). - * The work array elements have to be ints, so the work array would need - * 256Kb at our recommended precision. This is not feasible in DOS machines. - -[ 256*1024/4 = 65,536 ] - - * To get around these problems, we apply Thomas' method to compute the - * nearest colors for only the cells within a small subbox of the histogram. - * The work array need be only as big as the subbox, so the memory usage - * problem is solved. Furthermore, we need not fill subboxes that are never - * referenced in pass2; many images use only part of the color gamut, so a - * fair amount of work is saved. An additional advantage of this - * approach is that we can apply Heckbert's locality criterion to quickly - * eliminate colormap entries that are far away from the subbox; typically - * three-fourths of the colormap entries are rejected by Heckbert's criterion, - * and we need not compute their distances to individual cells in the subbox. - * The speed of this approach is heavily influenced by the subbox size: too - * small means too much overhead, too big loses because Heckbert's criterion - * can't eliminate as many colormap entries. Empirically the best subbox - * size seems to be about 1/512th of the histogram (1/8th in each direction). - * - * Thomas' article also describes a refined method which is asymptotically - * faster than the brute-force method, but it is also far more complex and - * cannot efficiently be applied to small subboxes. It is therefore not - * useful for programs intended to be portable to DOS machines. On machines - * with plenty of memory, filling the whole histogram in one shot with Thomas' - * refined method might be faster than the present code --- but then again, - * it might not be any faster, and it's certainly more complicated. - */ - -/* log2(histogram cells in update box) for each axis; this can be adjusted */ -#define BOX_R_LOG (PRECISION_R-3) -#define BOX_G_LOG (PRECISION_G-3) -#define BOX_B_LOG (PRECISION_B-3) - -#define BOX_R_ELEMS (1<actual_number_of_colors; - int maxR, maxG, maxB; - int centerR, centerG, centerB; - int i, x, ncolors; - int minmaxdist, min_dist = 0, max_dist, tdist; - int mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ - - /* Compute TRUE coordinates of update box's upper corner and center. - * Actually we compute the coordinates of the center of the upper-corner - * histogram cell, which are the upper bounds of the volume we care about. - * Note that since ">>" rounds down, the "center" values may be closer to - * min than to max; hence comparisons to them must be "<=", not "<". - */ - maxR = minR + ((1 << BOX_R_SHIFT) - (1 << R_SHIFT)); - centerR = (minR + maxR) >> 1; - maxG = minG + ((1 << BOX_G_SHIFT) - (1 << G_SHIFT)); - centerG = (minG + maxG) >> 1; - maxB = minB + ((1 << BOX_B_SHIFT) - (1 << B_SHIFT)); - centerB = (minB + maxB) >> 1; - - /* For each color in colormap, find: - * 1. its minimum squared-distance to any point in the update box - * (zero if color is within update box); - * 2. its maximum squared-distance to any point in the update box. - * Both of these can be found by considering only the corners of the box. - * We save the minimum distance for each color in mindist[]; - * only the smallest maximum distance is of interest. - */ - minmaxdist = 0x7FFFFFFFL; - - for (i = 0; i < numcolors; i++) { - /* We compute the squared-R-distance term, then add in the other two. */ - x = quantobj->cmap[i].r; - if (x < minR) { - tdist = (x - minR) R_SCALE; - min_dist = tdist * tdist; - tdist = (x - maxR) R_SCALE; - max_dist = tdist * tdist; - } else if (x > maxR) { - tdist = (x - maxR) R_SCALE; - min_dist = tdist * tdist; - tdist = (x - minR) R_SCALE; - max_dist = tdist * tdist; - } else { - /* within cell range so no contribution to min_dist */ - min_dist = 0; - if (x <= centerR) { - tdist = (x - maxR) R_SCALE; - max_dist = tdist * tdist; - } else { - tdist = (x - minR) R_SCALE; - max_dist = tdist * tdist; - } - } - - x = quantobj->cmap[i].g; - if (x < minG) { - tdist = (x - minG) G_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxG) G_SCALE; - max_dist += tdist * tdist; - } else if (x > maxG) { - tdist = (x - maxG) G_SCALE; - min_dist += tdist * tdist; - tdist = (x - minG) G_SCALE; - max_dist += tdist * tdist; - } else { - /* within cell range so no contribution to min_dist */ - if (x <= centerG) { - tdist = (x - maxG) G_SCALE; - max_dist += tdist * tdist; - } else { - tdist = (x - minG) G_SCALE; - max_dist += tdist * tdist; - } - } - - x = quantobj->cmap[i].b; - if (x < minB) { - tdist = (x - minB) B_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxB) B_SCALE; - max_dist += tdist * tdist; - } else if (x > maxB) { - tdist = (x - maxB) B_SCALE; - min_dist += tdist * tdist; - tdist = (x - minB) B_SCALE; - max_dist += tdist * tdist; - } else { - /* within cell range so no contribution to min_dist */ - if (x <= centerB) { - tdist = (x - maxB) B_SCALE; - max_dist += tdist * tdist; - } else { - tdist = (x - minB) B_SCALE; - max_dist += tdist * tdist; - } - } - - mindist[i] = min_dist; /* save away the results */ - if (max_dist < minmaxdist) - minmaxdist = max_dist; - } - - /* Now we know that no cell in the update box is more than minmaxdist - * away from some colormap entry. Therefore, only colors that are - * within minmaxdist of some part of the box need be considered. - */ - ncolors = 0; - for (i = 0; i < numcolors; i++) { - if (mindist[i] <= minmaxdist) - colorlist[ncolors++] = i; - } - return ncolors; -} - -static void find_best_colors(QuantizeObj * quantobj, int minR, int minG, int minB, int numcolors, int *colorlist, int *bestcolor) -/* Find the closest colormap entry for each cell in the update box, - given the list of candidate colors prepared by find_nearby_colors. - Return the indexes of the closest entries in the bestcolor[] array. - This routine uses Thomas' incremental distance calculation method to - find the distance from a colormap entry to successive cells in the box. - */ -{ - int iR, iG, iB; - int i, icolor; - int *bptr; /* pointer into bestdist[] array */ - int *cptr; /* pointer into bestcolor[] array */ - int dist0, dist1; /* initial distance values */ - int dist2; /* current distance in inner loop */ - int xx0, xx1; /* distance increments */ - int xx2; - int inR, inG, inB; /* initial values for increments */ - - /* This array holds the distance to the nearest-so-far color for each cell */ - int bestdist[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS]; - - /* Initialize best-distance for each cell of the update box */ - bptr = bestdist; - for (i = BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS - 1; i >= 0; i--) - *bptr++ = 0x7FFFFFFFL; - - /* For each color selected by find_nearby_colors, - * compute its distance to the center of each cell in the box. - * If that's less than best-so-far, update best distance and color number. - */ - - /* Nominal steps between cell centers ("x" in Thomas article) */ -#define STEP_R ((1 << R_SHIFT) R_SCALE) -#define STEP_G ((1 << G_SHIFT) G_SCALE) -#define STEP_B ((1 << B_SHIFT) B_SCALE) - - for (i = 0; i < numcolors; i++) { - icolor = colorlist[i]; - /* Compute (square of) distance from minR/G/B to this color */ - inR = (minR - quantobj->cmap[icolor].r) R_SCALE; - dist0 = inR * inR; - inG = (minG - quantobj->cmap[icolor].g) G_SCALE; - dist0 += inG * inG; - inB = (minB - quantobj->cmap[icolor].b) B_SCALE; - dist0 += inB * inB; - /* Form the initial difference increments */ - inR = inR * (2 * STEP_R) + STEP_R * STEP_R; - inG = inG * (2 * STEP_G) + STEP_G * STEP_G; - inB = inB * (2 * STEP_B) + STEP_B * STEP_B; - /* Now loop over all cells in box, updating distance per Thomas method */ - bptr = bestdist; - cptr = bestcolor; - xx0 = inR; - for (iR = BOX_R_ELEMS - 1; iR >= 0; iR--) { - dist1 = dist0; - xx1 = inG; - for (iG = BOX_G_ELEMS - 1; iG >= 0; iG--) { - dist2 = dist1; - xx2 = inB; - for (iB = BOX_B_ELEMS - 1; iB >= 0; iB--) { - if (dist2 < *bptr) { - *bptr = dist2; - *cptr = icolor; - } - dist2 += xx2; - xx2 += 2 * STEP_B * STEP_B; - bptr++; - cptr++; - } - dist1 += xx1; - xx1 += 2 * STEP_G * STEP_G; - } - dist0 += xx0; - xx0 += 2 * STEP_R * STEP_R; - } - } -} - -static void fill_inverse_cmap_rgb(QuantizeObj * quantobj, Histogram histogram, int R, int G, int B) -/* Fill the inverse-colormap entries in the update box that contains - histogram cell R/G/B. (Only that one cell MUST be filled, but - we can fill as many others as we wish.) */ -{ - int minR, minG, minB; /* lower left corner of update box */ - int iR, iG, iB; - int *cptr; /* pointer into bestcolor[] array */ - ColorFreq *cachep; /* pointer into main cache array */ - /* This array lists the candidate colormap indexes. */ - int colorlist[MAXNUMCOLORS]; - int numcolors; /* number of candidate colors */ - /* This array holds the actually closest colormap index for each cell. */ - int bestcolor[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS]; - - /* Convert cell coordinates to update box ID */ - R >>= BOX_R_LOG; - G >>= BOX_G_LOG; - B >>= BOX_B_LOG; - - /* Compute TRUE coordinates of update box's origin corner. - * Actually we compute the coordinates of the center of the corner - * histogram cell, which are the lower bounds of the volume we care about. - */ - minR = (R << BOX_R_SHIFT) + ((1 << R_SHIFT) >> 1); - minG = (G << BOX_G_SHIFT) + ((1 << G_SHIFT) >> 1); - minB = (B << BOX_B_SHIFT) + ((1 << B_SHIFT) >> 1); - - /* Determine which colormap entries are close enough to be candidates - * for the nearest entry to some cell in the update box. - */ - numcolors = find_nearby_colors(quantobj, minR, minG, minB, colorlist); - - /* Determine the actually nearest colors. */ - find_best_colors(quantobj, minR, minG, minB, numcolors, colorlist, bestcolor); - - /* Save the best color numbers (plus 1) in the main cache array */ - R <<= BOX_R_LOG; /* convert ID back to base cell indexes */ - G <<= BOX_G_LOG; - B <<= BOX_B_LOG; - cptr = bestcolor; - for (iR = 0; iR < BOX_R_ELEMS; iR++) { - for (iG = 0; iG < BOX_G_ELEMS; iG++) { - cachep = &histogram[(R + iR) * MR + (G + iG) * MG + B]; - for (iB = 0; iB < BOX_B_ELEMS; iB++) { - *cachep++ = (*cptr++) + 1; - } - } - } -} - -/* This is pass 1 */ -static void median_cut_pass1_rgb(QuantizeObj * quantobj, at_bitmap * image, const at_color * ignoreColor) -{ - generate_histogram_rgb(quantobj->histogram, image, ignoreColor); - select_colors_rgb(quantobj, quantobj->histogram); -} - -/* Map some rows of pixels to the output colormapped representation. */ -static void median_cut_pass2_rgb(QuantizeObj * quantobj, at_bitmap * image, const at_color * bgColor) - /* This version performs no dithering */ -{ - Histogram histogram = quantobj->histogram; - ColorFreq *cachep; - int R, G, B; - int origR, origG, origB; - int row, col; - int spp = AT_BITMAP_PLANES(image); - int width = AT_BITMAP_WIDTH(image); - int height = AT_BITMAP_HEIGHT(image); - unsigned char *src, *dest; - at_color bg_color = { 0xff, 0xff, 0xff }; - - zero_histogram_rgb(histogram); - - if (bgColor) { - /* Find the nearest colormap entry for the background color. */ - R = bgColor->r >> R_SHIFT; - G = bgColor->g >> G_SHIFT; - B = bgColor->b >> B_SHIFT; - cachep = &histogram[R * MR + G * MG + B]; - if (*cachep == 0) - fill_inverse_cmap_rgb(quantobj, histogram, R, G, B); - bg_color = quantobj->cmap[*cachep - 1]; - } - - src = dest = image->bitmap; - if (spp == 3) { - for (row = 0; row < height; row++) { - for (col = 0; col < width; col++) { - /* get pixel value and index into the cache */ - origR = (*src++); - origG = (*src++); - origB = (*src++); - - /* - if (origR > 253 && origG > 253 && origB > 253) - { - (*dest++) = 255; (*dest++) = 255; (*dest++) = 255; - continue; - } - */ - - /* get pixel value and index into the cache */ - R = origR >> R_SHIFT; - G = origG >> G_SHIFT; - B = origB >> B_SHIFT; - cachep = &histogram[R * MR + G * MG + B]; - /* If we have not seen this color before, find nearest - colormap entry and update the cache */ - if (*cachep == 0) { - fill_inverse_cmap_rgb(quantobj, histogram, R, G, B); - } - /* Now emit the colormap index for this cell */ - dest[0] = quantobj->cmap[*cachep - 1].r; - dest[1] = quantobj->cmap[*cachep - 1].g; - dest[2] = quantobj->cmap[*cachep - 1].b; - - /* If the colormap entry for this pixel is the same as the - background's colormap entry, set the pixel to the - background color. */ - if (bgColor && (dest[0] == bg_color.r && dest[1] == bg_color.g && dest[2] == bg_color.b)) { - dest[0] = bgColor->r; - dest[1] = bgColor->g; - dest[2] = bgColor->b; - } - dest += 3; - } - } - } else if (spp == 1) { - long idx = width * height; - while (--idx >= 0) { - origR = src[idx]; - R = origR >> R_SHIFT; - G = origR >> G_SHIFT; - B = origR >> B_SHIFT; - cachep = &histogram[R * MR + G * MG + B]; - if (*cachep == 0) - fill_inverse_cmap_rgb(quantobj, histogram, R, G, B); - - dest[idx] = quantobj->cmap[*cachep - 1].r; - - /* If the colormap entry for this pixel is the same as the - background's colormap entry, set the pixel to the - background color. */ - if (bgColor && dest[idx] == bg_color.r) - dest[idx] = bgColor->r; - } - } -} - -static QuantizeObj *initialize_median_cut(int num_colors) -{ - QuantizeObj *quantobj; - - /* Initialize the data structures */ - XMALLOC(quantobj, sizeof(QuantizeObj)); - - XMALLOC(quantobj->histogram, sizeof(ColorFreq) * HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS); - quantobj->desired_number_of_colors = num_colors; - - return quantobj; -} - -void quantize(at_bitmap * image, long ncolors, const at_color * bgColor, QuantizeObj ** iQuant, at_exception_type * exp) -{ - QuantizeObj *quantobj; - unsigned int spp = AT_BITMAP_PLANES(image); - - if (spp != 3 && spp != 1) { - LOG("quantize: %u-plane images are not supported", spp); - at_exception_fatal(exp, "quantize: wrong plane images are passed"); - return; - } - - /* If a pointer was sent in, let's use it. */ - if (iQuant) { - if (*iQuant == NULL) { - quantobj = initialize_median_cut(ncolors); - median_cut_pass1_rgb(quantobj, image, bgColor); - *iQuant = quantobj; - } else - quantobj = *iQuant; - } else { - quantobj = initialize_median_cut(ncolors); - median_cut_pass1_rgb(quantobj, image, NULL); - } - - median_cut_pass2_rgb(quantobj, image, bgColor); - - if (iQuant == NULL) - quantize_object_free(quantobj); -} - -void quantize_object_free(QuantizeObj * quantobj) -{ - free(quantobj->histogram); - free(quantobj); -} diff --git a/src/autotrace/module.c b/src/autotrace/module.c deleted file mode 100644 index ad3e8fb..0000000 --- a/src/autotrace/module.c +++ /dev/null @@ -1,73 +0,0 @@ -/* module.c --- Autotrace plugin module management subsystem - - Copyright (C) 2003 Martin Weber - Copyright (C) 2003 Masatake YAMATO - - The author can be contacted at - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#define HAVE_LIBPNG true -//#define HAVE_MAGICK_READERS true -//#define HAVE_CONFIG_H true - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ -#include "intl.h" - -#include "private.h" - -#include "input.h" -#if !HAVE_MAGICK_READERS -#include "input-bmp.h" -#endif /* HAVE_MAGICK_READERS */ -#if !HAVE_MAGICK_READERS || HAVE_GRAPHICSMAGICK -#endif /* !HAVE_MAGICK_READERS || HAVE_GRAPHICSMAGICK */ - -#ifdef HAVE_LIBPNG -#include "input-png.h" -#endif /* HAVE_LIBPNG */ - -static int install_input_readers(void); -static int install_output_writers(void); - -int at_module_init(void) -{ - int r, w; - /* TODO: Loading every thing in dynamic. - For a while, these are staticly added. */ - r = install_input_readers(); - w = install_output_writers(); - return (int)(r << 2 | w); -} - -static int install_input_readers(void) -{ -#ifdef HAVE_LIBPNG - at_input_add_handler("PNG", "Portable network graphics (native)", input_png_reader); -#endif - -#if !HAVE_MAGICK_READERS - at_input_add_handler("BMP", "Microsoft Windows bitmap image (native)", input_bmp_reader); -#endif /* HAVE_MAGICK_READERS */ - - return 0; // ((0 << 1) || install_input_magick_readers()); // fuck you :) -} - -static int install_output_writers(void) -{ - return 1; // fuck you :) -} diff --git a/src/autotrace/private.h b/src/autotrace/private.h deleted file mode 100644 index acb6815..0000000 --- a/src/autotrace/private.h +++ /dev/null @@ -1,43 +0,0 @@ -/* private.h --- Autotrace library private decls - - Copyright (C) 2003 Martin Weber - Copyright (C) 2003 Masatake YAMATO - - The author can be contacted at - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef PRIVATE_H -#define PRIVATE_H - -#include "autotrace.h" -#include "input.h" -#include "output.h" - -struct _at_bitmap_reader { - at_input_func func; - gpointer data; -}; - -struct _at_spline_writer { - at_output_func func; - gpointer data; -}; - -int at_input_init(void); -int at_output_init(void); -int at_param_init(void); -int at_module_init(void); -#endif /* Not def: PRIVATE_H */ diff --git a/src/autotrace/pxl-outline.c b/src/autotrace/pxl-outline.c deleted file mode 100644 index d51f8ef..0000000 --- a/src/autotrace/pxl-outline.c +++ /dev/null @@ -1,889 +0,0 @@ -/* pxl-outline.c: find the outlines of a bitmap image; each outline is made up of one or more pixels; - and each pixel participates via one or more edges. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "logreport.h" -#include "types.h" -#include "bitmap.h" -#include "color.h" -#include "bitmap.h" -#include "xstd.h" -#include "pxl-outline.h" -#include - -/* We consider each pixel to consist of four edges, and we travel along - edges, instead of through pixel centers. This is necessary for those - unfortunate times when a single pixel is on both an inside and an - outside outline. - - The numbers chosen here are not arbitrary; the code that figures out - which edge to move to depends on particular values. See the - `TRY_PIXEL' macro in `edge.c'. To emphasize this, I've written in the - numbers we need for each edge value. */ - -typedef enum { - TOP = 1, LEFT = 2, BOTTOM = 3, RIGHT = 0, NO_EDGE = 4 -} edge_type; - -/* This choice is also not arbitrary: starting at the top edge makes the - code find outside outlines before inside ones, which is certainly - what we want. */ -#define START_EDGE top - -typedef enum { - NORTH = 0, NORTHWEST = 1, WEST = 2, SOUTHWEST = 3, SOUTH = 4, - SOUTHEAST = 5, EAST = 6, NORTHEAST = 7 -} direction_type; - -#define NUM_EDGES NO_EDGE - -#define COMPUTE_DELTA(axis, dir) \ - ((dir) % 2 != 0 \ - ? COMPUTE_##axis##_DELTA ((dir) - 1) \ - + COMPUTE_##axis##_DELTA (((dir) + 1) % 8) \ - : COMPUTE_##axis##_DELTA (dir) \ - ) - -#define COMPUTE_ROW_DELTA(dir) \ - ((dir) == NORTH ? -1 : (dir) == SOUTH ? +1 : 0) - -#define COMPUTE_COL_DELTA(dir) \ - ((dir) == WEST ? -1 : (dir) == EAST ? +1 : 0) - -static pixel_outline_type find_one_outline(at_bitmap *, edge_type, unsigned short, unsigned short, at_bitmap *, gboolean, gboolean, at_exception_type *); -static pixel_outline_type find_one_centerline(at_bitmap *, direction_type, unsigned short, unsigned short, at_bitmap *); -static void append_pixel_outline(pixel_outline_list_type *, pixel_outline_type); -static pixel_outline_type new_pixel_outline(void); -static void free_pixel_outline(pixel_outline_type *); -static void concat_pixel_outline(pixel_outline_type *, const pixel_outline_type *); -static void append_outline_pixel(pixel_outline_type *, at_coord); -static gboolean is_marked_edge(edge_type, unsigned short, unsigned short, at_bitmap *); -static gboolean is_outline_edge(edge_type, at_bitmap *, unsigned short, unsigned short, at_color, at_exception_type *); -static gboolean is_unmarked_outline_edge(unsigned short, unsigned short, edge_type, at_bitmap *, at_bitmap *, at_color, at_exception_type *); - -static void mark_edge(edge_type e, unsigned short, unsigned short, at_bitmap *); -/* static edge_type opposite_edge(edge_type); */ - -static gboolean is_marked_dir(unsigned short, unsigned short, direction_type, at_bitmap *); -static gboolean is_other_dir_marked(unsigned short, unsigned short, direction_type, at_bitmap *); -static void mark_dir(unsigned short, unsigned short, direction_type, at_bitmap *); -static gboolean next_unmarked_pixel(unsigned short *, unsigned short *, direction_type *, at_bitmap *, at_bitmap *); - -gboolean is_valid_dir(unsigned short, unsigned short, direction_type, at_bitmap *, at_bitmap *); - -static at_coord next_point(at_bitmap *, edge_type *, unsigned short *, unsigned short *, at_color, gboolean, at_bitmap *, at_exception_type *); -static unsigned num_neighbors(unsigned short, unsigned short, at_bitmap *); - -#define CHECK_FATAL() if (at_exception_got_fatal(exp)) goto cleanup; - -/* We go through a bitmap TOP to BOTTOM, LEFT to RIGHT, looking for each pixel with an unmarked edge - that we consider a starting point of an outline. */ - -pixel_outline_list_type find_outline_pixels(at_bitmap * bitmap, at_color * bg_color, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data, at_exception_type * exp) -{ - pixel_outline_list_type outline_list; - unsigned short row, col; - at_bitmap *marked = at_bitmap_new(AT_BITMAP_WIDTH(bitmap), AT_BITMAP_HEIGHT(bitmap), 1); - unsigned int max_progress = AT_BITMAP_HEIGHT(bitmap) * AT_BITMAP_WIDTH(bitmap); - - O_LIST_LENGTH(outline_list) = 0; - outline_list.data = NULL; - - for (row = 0; row < AT_BITMAP_HEIGHT(bitmap); row++) { - for (col = 0; col < AT_BITMAP_WIDTH(bitmap); col++) { - edge_type edge; - at_color color; - gboolean is_background; - - if (notify_progress) - notify_progress((gfloat) (row * AT_BITMAP_WIDTH(bitmap) + col) / ((gfloat) max_progress * (gfloat) 3.0), progress_data); - - /* A valid edge can be TOP for an outside outline. - Outside outlines are traced counterclockwise */ - at_bitmap_get_color(bitmap, row, col, &color); - if (!(is_background = (gboolean) (bg_color && at_color_equal(&color, bg_color))) - && is_unmarked_outline_edge(row, col, edge = TOP, bitmap, marked, color, exp)) { - pixel_outline_type outline; - - CHECK_FATAL(); /* FREE(DONE) outline_list */ - - LOG("#%u: (counterclockwise)", O_LIST_LENGTH(outline_list)); - - outline = find_one_outline(bitmap, edge, row, col, marked, FALSE, FALSE, exp); - CHECK_FATAL(); /* FREE(DONE) outline_list */ - - O_CLOCKWISE(outline) = FALSE; - append_pixel_outline(&outline_list, outline); - - LOG(" [%u].\n", O_LENGTH(outline)); - } else - CHECK_FATAL(); /* FREE(DONE) outline_list */ - - /* A valid edge can be BOTTOM for an inside outline. - Inside outlines are traced clockwise */ - if (row != 0) { - at_bitmap_get_color(bitmap, row - 1, col, &color); - if (!(bg_color && at_color_equal(&color, bg_color)) - && is_unmarked_outline_edge(row - 1, col, edge = BOTTOM, bitmap, marked, color, exp)) { - pixel_outline_type outline; - - CHECK_FATAL(); /* FREE(DONE) outline_list */ - - /* This lines are for debugging only: */ - if (is_background) { - LOG("#%u: (clockwise)", O_LIST_LENGTH(outline_list)); - - outline = find_one_outline(bitmap, edge, row - 1, col, marked, TRUE, FALSE, exp); - CHECK_FATAL(); /* FREE(DONE) outline_list */ - - O_CLOCKWISE(outline) = TRUE; - append_pixel_outline(&outline_list, outline); - - LOG(" [%u].\n", O_LENGTH(outline)); - } else { - outline = find_one_outline(bitmap, edge, row - 1, col, marked, TRUE, TRUE, exp); - CHECK_FATAL(); /* FREE(DONE) outline_list */ - } - } else - CHECK_FATAL(); /* FREE(DONE) outline_list */ - } - if (test_cancel && test_cancel(testcancel_data)) { - free_pixel_outline_list(&outline_list); - goto cleanup; - } - } - } -cleanup: - at_bitmap_free(marked); - if (at_exception_got_fatal(exp)) - free_pixel_outline_list(&outline_list); - return outline_list; -} - -/* We calculate one single outline here. We pass the position of the starting pixel and the - starting edge. All edges we track along will be marked and the outline pixels are appended - to the coordinate list. */ - -static pixel_outline_type find_one_outline(at_bitmap * bitmap, edge_type original_edge, unsigned short original_row, unsigned short original_col, at_bitmap * marked, gboolean clockwise, gboolean ignore, at_exception_type * exp) -{ - pixel_outline_type outline; - unsigned short row = original_row, col = original_col; - edge_type edge = original_edge; - at_coord pos; - - pos.x = col + ((edge == RIGHT) || (edge == BOTTOM) ? 1 : 0); - pos.y = AT_BITMAP_HEIGHT(bitmap) - row - 1 + ((edge == TOP) || (edge == RIGHT) ? 1 : 0); - - if (!ignore) - outline = new_pixel_outline(); - at_bitmap_get_color(bitmap, row, col, &outline.color); - - do { - /* Put this edge into the output list */ - if (!ignore) { - LOG(" (%d,%d)", pos.x, pos.y); - append_outline_pixel(&outline, pos); - } - - mark_edge(edge, row, col, marked); - pos = next_point(bitmap, &edge, &row, &col, outline.color, clockwise, marked, exp); - CHECK_FATAL(); - } - while (edge != NO_EDGE); - -cleanup: - if (at_exception_got_fatal(exp)) - free_pixel_outline(&outline); - return outline; -} - -gboolean is_valid_dir(unsigned short row, unsigned short col, direction_type dir, at_bitmap * bitmap, at_bitmap * marked) -{ - - at_color c; - int new_row = COMPUTE_DELTA(ROW, dir) + row; - int new_col = COMPUTE_DELTA(COL, dir) + col; - - if ((new_row < 0) || (new_col < 0) || (new_row >= AT_BITMAP_HEIGHT(bitmap)) || (new_col >= AT_BITMAP_WIDTH(bitmap))) - return FALSE; // Must not call at_bitmap_get_color() with negative row or col. - - at_bitmap_get_color(bitmap, COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, &c); - return ((gboolean) (!is_marked_dir(row, col, dir, marked) - && COMPUTE_DELTA(ROW, dir) + row > 0 && COMPUTE_DELTA(COL, dir) + col > 0 && AT_BITMAP_VALID_PIXEL(bitmap, COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col) - && at_bitmap_equal_color(bitmap, row, col, &c))); -} - -pixel_outline_list_type find_centerline_pixels(at_bitmap * bitmap, at_color bg_color, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data, at_exception_type * exp) -{ - pixel_outline_list_type outline_list; - signed short row, col; - at_bitmap *marked = at_bitmap_new(AT_BITMAP_WIDTH(bitmap), AT_BITMAP_HEIGHT(bitmap), 1); - unsigned int max_progress = AT_BITMAP_HEIGHT(bitmap) * AT_BITMAP_WIDTH(bitmap); - - O_LIST_LENGTH(outline_list) = 0; - outline_list.data = NULL; - - for (row = 0; row < AT_BITMAP_HEIGHT(bitmap); row++) { - for (col = 0; col < AT_BITMAP_WIDTH(bitmap);) { - direction_type dir = EAST; - pixel_outline_type outline; - gboolean clockwise = FALSE; - - if (notify_progress) - notify_progress((gfloat) (row * AT_BITMAP_WIDTH(bitmap) + col) / ((gfloat) max_progress * (gfloat) 3.0), progress_data); - - if (at_bitmap_equal_color(bitmap, row, col, &bg_color)) { - col++; - continue; - } - - if (!is_valid_dir(row, col, dir, bitmap, marked) - || (!is_valid_dir(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, dir, bitmap, marked) - && num_neighbors(row, col, bitmap) > 2) - || num_neighbors(row, col, bitmap) > 4 || num_neighbors(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, bitmap) > 4 || (is_other_dir_marked(row, col, dir, marked) - && is_other_dir_marked(row + COMPUTE_DELTA(ROW, dir), col + COMPUTE_DELTA(COL, dir), dir, marked))) { - dir = SOUTHEAST; - if (!is_valid_dir(row, col, dir, bitmap, marked) - || (!is_valid_dir(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, dir, bitmap, marked) - && num_neighbors(row, col, bitmap) > 2) - || num_neighbors(row, col, bitmap) > 4 || num_neighbors(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, bitmap) > 4 || (is_other_dir_marked(row, col, dir, marked) - && is_other_dir_marked(row + COMPUTE_DELTA(ROW, dir), col + COMPUTE_DELTA(COL, dir), dir, marked))) { - dir = SOUTH; - if (!is_valid_dir(row, col, dir, bitmap, marked) - || (!is_valid_dir(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, dir, bitmap, marked) - && num_neighbors(row, col, bitmap) > 2) - || num_neighbors(row, col, bitmap) > 4 || num_neighbors(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, bitmap) > 4 || (is_other_dir_marked(row, col, dir, marked) - && is_other_dir_marked(row + COMPUTE_DELTA(ROW, dir), col + COMPUTE_DELTA(COL, dir), dir, marked))) { - dir = SOUTHWEST; - if (!is_valid_dir(row, col, dir, bitmap, marked) - || (!is_valid_dir(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, dir, bitmap, marked) - && num_neighbors(row, col, bitmap) > 2) - || num_neighbors(row, col, bitmap) > 4 || num_neighbors(COMPUTE_DELTA(ROW, dir) + row, COMPUTE_DELTA(COL, dir) + col, bitmap) > 4 || (is_other_dir_marked(row, col, dir, marked) - && is_other_dir_marked(row + COMPUTE_DELTA(ROW, dir), col + COMPUTE_DELTA(COL, dir), dir, marked))) { - col++; - continue; - } - } - } - } - - LOG("#%u: (%sclockwise) ", O_LIST_LENGTH(outline_list), clockwise ? "" : "counter"); - - outline = find_one_centerline(bitmap, dir, row, col, marked); - - /* If the outline is open (i.e., we didn't return to the - starting pixel), search from the starting pixel in the - opposite direction and concatenate the two outlines. */ - - if (outline.open) { - pixel_outline_type partial_outline; - gboolean okay = FALSE; - - if (dir == EAST) { - dir = SOUTH; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = SOUTHWEST; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = SOUTHEAST; - okay = is_valid_dir(row, col, dir, bitmap, marked); - } - } - } else if (dir == SOUTHEAST) { - dir = SOUTHWEST; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = EAST; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = SOUTH; - okay = is_valid_dir(row, col, dir, bitmap, marked); - } - } - } else if (dir == SOUTH) { - dir = EAST; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = SOUTHEAST; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = SOUTHWEST; - okay = is_valid_dir(row, col, dir, bitmap, marked); - } - } - } else if (dir == SOUTHWEST) { - dir = SOUTHEAST; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = EAST; - if (!(okay = is_valid_dir(row, col, dir, bitmap, marked))) { - dir = SOUTH; - okay = is_valid_dir(row, col, dir, bitmap, marked); - } - } - } - if (okay) { - partial_outline = find_one_centerline(bitmap, dir, row, col, marked); - concat_pixel_outline(&outline, &partial_outline); - if (partial_outline.data) - free(partial_outline.data); - } else - col++; - } - - /* Outside outlines will start at a top edge, and move - counterclockwise, and inside outlines will start at a - bottom edge, and move clockwise. This happens because of - the order in which we look at the edges. */ - O_CLOCKWISE(outline) = clockwise; - if (O_LENGTH(outline) > 1) - append_pixel_outline(&outline_list, outline); - LOG("(%s)", (outline.open ? " open" : " closed")); - LOG(" [%u].\n", O_LENGTH(outline)); - if (O_LENGTH(outline) == 1) - free_pixel_outline(&outline); - } - } - if (test_cancel && test_cancel(testcancel_data)) { - if (O_LIST_LENGTH(outline_list) != 0) - free_pixel_outline_list(&outline_list); - goto cleanup; - } -cleanup: - at_bitmap_free(marked); - return outline_list; -} - -static pixel_outline_type find_one_centerline(at_bitmap * bitmap, direction_type search_dir, unsigned short original_row, unsigned short original_col, at_bitmap * marked) -{ - pixel_outline_type outline = new_pixel_outline(); - direction_type original_dir = search_dir; - unsigned short row = original_row, col = original_col; - unsigned short prev_row, prev_col; - at_coord pos; - - outline.open = FALSE; - at_bitmap_get_color(bitmap, row, col, &outline.color); - - /* Add the starting pixel to the output list, changing from bitmap - to Cartesian coordinates and specifying the left edge so that - the coordinates won't be adjusted. */ - pos.x = col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - row - 1; - LOG(" (%d,%d)", pos.x, pos.y); - append_outline_pixel(&outline, pos); - - for (;;) { - prev_row = row; - prev_col = col; - - /* If there is no adjacent, unmarked pixel, we can't proceed - any further, so return an open outline. */ - if (!next_unmarked_pixel(&row, &col, &search_dir, bitmap, marked)) { - outline.open = TRUE; - break; - } - - /* If we've moved to a new pixel, mark all edges of the previous - pixel so that it won't be revisited. */ - if (!(prev_row == original_row && prev_col == original_col)) - mark_dir(prev_row, prev_col, search_dir, marked); - mark_dir(row, col, (direction_type) ((search_dir + 4) % 8), marked); - - /* If we've returned to the starting pixel, we're done. */ - if (row == original_row && col == original_col) - break; - - /* Add the new pixel to the output list. */ - pos.x = col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - row - 1; - LOG(" (%d,%d)", pos.x, pos.y); - append_outline_pixel(&outline, pos); - } - mark_dir(original_row, original_col, original_dir, marked); - return outline; -} - -/* Add an outline to an outline list. */ - -static void append_pixel_outline(pixel_outline_list_type * outline_list, pixel_outline_type outline) -{ - O_LIST_LENGTH(*outline_list)++; - XREALLOC(outline_list->data, outline_list->length * sizeof(pixel_outline_type)); - O_LIST_OUTLINE(*outline_list, O_LIST_LENGTH(*outline_list) - 1) = outline; -} - -/* Free the list of outline lists. */ - -void free_pixel_outline_list(pixel_outline_list_type * outline_list) -{ - unsigned this_outline; - - for (this_outline = 0; this_outline < outline_list->length; this_outline++) { - pixel_outline_type o = outline_list->data[this_outline]; - free_pixel_outline(&o); - } - free(outline_list->data); - outline_list->data = NULL; - outline_list->length = 0; -} - -/* Return an empty list of pixels. */ - -static pixel_outline_type new_pixel_outline(void) -{ - pixel_outline_type pixel_outline; - - O_LENGTH(pixel_outline) = 0; - pixel_outline.data = NULL; - pixel_outline.open = FALSE; - - return pixel_outline; -} - -static void free_pixel_outline(pixel_outline_type * outline) -{ - free(outline->data); - outline->data = NULL; - outline->length = 0; -} - -/* Concatenate two pixel lists. The two lists are assumed to have the - same starting pixel and to proceed in opposite directions therefrom. */ - -static void concat_pixel_outline(pixel_outline_type * o1, const pixel_outline_type * o2) -{ - int src, dst; - unsigned o1_length, o2_length; - if (!o1 || !o2 || O_LENGTH(*o2) <= 1) - return; - - o1_length = O_LENGTH(*o1); - o2_length = O_LENGTH(*o2); - O_LENGTH(*o1) += o2_length - 1; - /* Resize o1 to the sum of the lengths of o1 and o2 minus one (because - the two lists are assumed to share the same starting pixel). */ - XREALLOC(o1->data, O_LENGTH(*o1) * sizeof(at_coord)); - /* Shift the contents of o1 to the end of the new array to make room - to prepend o2. */ - for (src = o1_length - 1, dst = O_LENGTH(*o1) - 1; src >= 0; src--, dst--) - O_COORDINATE(*o1, dst) = O_COORDINATE(*o1, src); - /* Prepend the contents of o2 (in reverse order) to o1. */ - for (src = o2_length - 1, dst = 0; src > 0; src--, dst++) - O_COORDINATE(*o1, dst) = O_COORDINATE(*o2, src); -} - -/* Add a point to the pixel list. */ - -static void append_outline_pixel(pixel_outline_type * o, at_coord c) -{ - O_LENGTH(*o)++; - XREALLOC(o->data, O_LENGTH(*o) * sizeof(at_coord)); - O_COORDINATE(*o, O_LENGTH(*o) - 1) = c; -} - -/* Is this really an edge and is it still unmarked? */ - -static gboolean is_unmarked_outline_edge(unsigned short row, unsigned short col, edge_type edge, at_bitmap * bitmap, at_bitmap * marked, at_color color, at_exception_type * exp) -{ - return (gboolean) (!is_marked_edge(edge, row, col, marked) - && is_outline_edge(edge, bitmap, row, col, color, exp)); -} - -/* We check to see if the edge of the pixel at position ROW and COL - is an outline edge */ - -static gboolean is_outline_edge(edge_type edge, at_bitmap * bitmap, unsigned short row, unsigned short col, at_color color, at_exception_type * exp) -{ - /* If this pixel isn't of the same color, it's not part of the outline. */ - if (!at_bitmap_equal_color(bitmap, row, col, &color)) - return FALSE; - - switch (edge) { - case LEFT: - return (gboolean) (col == 0 || !at_bitmap_equal_color(bitmap, row, col - 1, &color)); - case TOP: - return (gboolean) (row == 0 || !at_bitmap_equal_color(bitmap, row - 1, col, &color)); - - case RIGHT: - return (gboolean) (col == AT_BITMAP_WIDTH(bitmap) - 1 || !at_bitmap_equal_color(bitmap, row, col + 1, &color)); - - case BOTTOM: - return (gboolean) (row == AT_BITMAP_HEIGHT(bitmap) - 1 || !at_bitmap_equal_color(bitmap, row + 1, col, &color)); - - case NO_EDGE: - g_assert_not_reached(); - default: - g_assert_not_reached(); - } - return FALSE; /* NOT REACHED */ -} - -/* If EDGE is not already marked, we mark it; otherwise, it's a fatal error. - The position ROW and COL should be inside the bitmap MARKED. EDGE can be - NO_EDGE. */ - -static void mark_edge(edge_type edge, unsigned short row, unsigned short col, at_bitmap * marked) -{ - *AT_BITMAP_PIXEL(marked, row, col) |= 1 << edge; -} - -/* Mark the direction of the pixel ROW/COL in MARKED. */ - -static void mark_dir(unsigned short row, unsigned short col, direction_type dir, at_bitmap * marked) -{ - *AT_BITMAP_PIXEL(marked, row, col) |= 1 << dir; -} - -/* Test if the direction of pixel at ROW/COL in MARKED is marked. */ - -static gboolean is_marked_dir(unsigned short row, unsigned short col, direction_type dir, at_bitmap * marked) -{ - return (gboolean) ((*AT_BITMAP_PIXEL(marked, row, col) & 1 << dir) != 0); -} - -static gboolean is_other_dir_marked(unsigned short row, unsigned short col, direction_type dir, at_bitmap * marked) -{ - return (gboolean) ((*AT_BITMAP_PIXEL(marked, row, col) & (255 - (1 << dir) - (1 << ((dir + 4) % 8)))) != 0); -} - -static gboolean next_unmarked_pixel(unsigned short *row, unsigned short *col, direction_type * dir, at_bitmap * bitmap, at_bitmap * marked) -{ - unsigned short orig_row = *row, orig_col = *col; - direction_type orig_dir = *dir, test_dir = *dir; - - do { - if (is_valid_dir(orig_row, orig_col, test_dir, bitmap, marked)) { - *row = orig_row + COMPUTE_DELTA(ROW, test_dir); - *col = orig_col + COMPUTE_DELTA(COL, test_dir); - *dir = test_dir; - break; - } - - if (orig_dir == test_dir) - test_dir = (direction_type) ((orig_dir + 2) % 8); - else if ((orig_dir + 2) % 8 == test_dir) - test_dir = (direction_type) ((orig_dir + 6) % 8); - else if ((orig_dir + 6) % 8 == test_dir) - test_dir = (direction_type) ((orig_dir + 1) % 8); - else if ((orig_dir + 1) % 8 == test_dir) - test_dir = (direction_type) ((orig_dir + 7) % 8); - else if ((orig_dir + 7) % 8 == test_dir) - test_dir = (direction_type) ((orig_dir + 3) % 8); - else if ((orig_dir + 3) % 8 == test_dir) - test_dir = (direction_type) ((orig_dir + 5) % 8); - else if ((orig_dir + 5) % 8 == test_dir) - break; - } - while (1); - if ((*row != orig_row || *col != orig_col) && (!(is_other_dir_marked(orig_row, orig_col, test_dir, marked) - && is_other_dir_marked(orig_row + COMPUTE_DELTA(ROW, test_dir), orig_col + COMPUTE_DELTA(COL, test_dir), test_dir, marked)))) - return TRUE; - else - return FALSE; -} - -/* Return the number of pixels adjacent to pixel ROW/COL that are black. */ - -static unsigned num_neighbors(unsigned short row, unsigned short col, at_bitmap * bitmap) -{ - unsigned dir, count = 0; - at_color color; - - at_bitmap_get_color(bitmap, row, col, &color); - for (dir = NORTH; dir <= NORTHEAST; dir++) { - int delta_r = COMPUTE_DELTA(ROW, dir); - int delta_c = COMPUTE_DELTA(COL, dir); - unsigned int test_row = row + delta_r; - unsigned int test_col = col + delta_c; - if (AT_BITMAP_VALID_PIXEL(bitmap, test_row, test_col) - && at_bitmap_equal_color(bitmap, test_row, test_col, &color)) - ++count; - } - return count; -} - -/* Test if the edge EDGE at ROW/COL in MARKED is marked. */ - -static gboolean is_marked_edge(edge_type edge, unsigned short row, unsigned short col, at_bitmap * marked) -{ - return (gboolean) (edge == NO_EDGE ? FALSE : (*AT_BITMAP_PIXEL(marked, row, col) & (1 << edge)) != 0); -} - -static at_coord next_point(at_bitmap * bitmap, edge_type * edge, unsigned short *row, unsigned short *col, at_color color, gboolean clockwise, at_bitmap * marked, at_exception_type * exp) -{ - at_coord pos = { 0, 0 }; - - if (!clockwise) - switch (*edge) { - case TOP: - /* WEST */ - if ((*col >= 1 && !is_marked_edge(TOP, *row, *col - 1, marked) - && is_outline_edge(TOP, bitmap, *row, *col - 1, color, exp))) { - /**edge = TOP;*/ - (*col)--; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - /* NORTHWEST */ - if ((*col >= 1 && *row >= 1 && !is_marked_edge(RIGHT, *row - 1, *col - 1, marked) - && is_outline_edge(RIGHT, bitmap, *row - 1, *col - 1, color, exp)) && !(is_marked_edge(LEFT, *row - 1, *col, marked) && is_marked_edge(TOP, *row, *col - 1, marked)) && !(is_marked_edge(BOTTOM, *row - 1, *col, marked) && is_marked_edge(RIGHT, *row, *col - 1, marked))) { - *edge = RIGHT; - (*col)--; - (*row)--; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - if ((!is_marked_edge(LEFT, *row, *col, marked) - && is_outline_edge(LEFT, bitmap, *row, *col, color, exp))) { - *edge = LEFT; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - *edge = NO_EDGE; - break; - case RIGHT: - /* NORTH */ - if ((*row >= 1 && !is_marked_edge(RIGHT, *row - 1, *col, marked) - && is_outline_edge(RIGHT, bitmap, *row - 1, *col, color, exp))) { - /**edge = RIGHT;*/ - (*row)--; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - /* NORTHEAST */ - if ((*col + 1 < AT_BITMAP_WIDTH(marked) && *row >= 1 && !is_marked_edge(BOTTOM, *row - 1, *col + 1, marked) - && is_outline_edge(BOTTOM, bitmap, *row - 1, *col + 1, color, exp)) && !(is_marked_edge(LEFT, *row, *col + 1, marked) && is_marked_edge(BOTTOM, *row - 1, *col, marked)) && !(is_marked_edge(TOP, *row, *col + 1, marked) && is_marked_edge(RIGHT, *row - 1, *col, marked))) { - *edge = BOTTOM; - (*col)++; - (*row)--; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - if ((!is_marked_edge(TOP, *row, *col, marked) - && is_outline_edge(TOP, bitmap, *row, *col, color, exp))) { - *edge = TOP; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - *edge = NO_EDGE; - break; - case BOTTOM: - /* EAST */ - if ((*col + 1 < AT_BITMAP_WIDTH(marked) - && !is_marked_edge(BOTTOM, *row, *col + 1, marked) - && is_outline_edge(BOTTOM, bitmap, *row, *col + 1, color, exp))) { - /**edge = BOTTOM;*/ - (*col)++; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - /* SOUTHEAST */ - if ((*col + 1 < AT_BITMAP_WIDTH(marked) && *row + 1 < AT_BITMAP_HEIGHT(marked) - && !is_marked_edge(LEFT, *row + 1, *col + 1, marked) - && is_outline_edge(LEFT, bitmap, *row + 1, *col + 1, color, exp)) && !(is_marked_edge(TOP, *row + 1, *col, marked) && is_marked_edge(LEFT, *row, *col + 1, marked)) && !(is_marked_edge(RIGHT, *row + 1, *col, marked) && is_marked_edge(BOTTOM, *row, *col + 1, marked))) { - *edge = LEFT; - (*col)++; - (*row)++; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - if ((!is_marked_edge(RIGHT, *row, *col, marked) - && is_outline_edge(RIGHT, bitmap, *row, *col, color, exp))) { - *edge = RIGHT; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - *edge = NO_EDGE; - break; - case LEFT: - /* SOUTH */ - if ((*row + 1 < AT_BITMAP_HEIGHT(marked) - && !is_marked_edge(LEFT, *row + 1, *col, marked) - && is_outline_edge(LEFT, bitmap, *row + 1, *col, color, exp))) { - /**edge = LEFT;*/ - (*row)++; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - /* SOUTHWEST */ - if ((*col >= 1 && *row + 1 < AT_BITMAP_HEIGHT(marked) - && !is_marked_edge(TOP, *row + 1, *col - 1, marked) - && is_outline_edge(TOP, bitmap, *row + 1, *col - 1, color, exp)) && !(is_marked_edge(RIGHT, *row, *col - 1, marked) && is_marked_edge(TOP, *row + 1, *col, marked)) && !(is_marked_edge(BOTTOM, *row, *col - 1, marked) && is_marked_edge(LEFT, *row + 1, *col, marked))) { - *edge = TOP; - (*col)--; - (*row)++; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - if ((!is_marked_edge(BOTTOM, *row, *col, marked) - && is_outline_edge(BOTTOM, bitmap, *row, *col, color, exp))) { - *edge = BOTTOM; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - case NO_EDGE: - default: - *edge = NO_EDGE; - break; - } else - switch (*edge) { - case TOP: - if ((!is_marked_edge(LEFT, *row, *col, marked) - && is_outline_edge(LEFT, bitmap, *row, *col, color, exp))) { - *edge = LEFT; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - /* WEST */ - if ((*col >= 1 && !is_marked_edge(TOP, *row, *col - 1, marked) - && is_outline_edge(TOP, bitmap, *row, *col - 1, color, exp))) { - /**edge = TOP;*/ - (*col)--; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - /* NORTHWEST */ - if ((*col >= 1 && *row >= 1 && !is_marked_edge(RIGHT, *row - 1, *col - 1, marked) - && is_outline_edge(RIGHT, bitmap, *row - 1, *col - 1, color, exp))) { - *edge = RIGHT; - (*col)--; - (*row)--; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - *edge = NO_EDGE; - break; - case RIGHT: - if ((!is_marked_edge(TOP, *row, *col, marked) - && is_outline_edge(TOP, bitmap, *row, *col, color, exp))) { - *edge = TOP; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - /* NORTH */ - if ((*row >= 1 && !is_marked_edge(RIGHT, *row - 1, *col, marked) - && is_outline_edge(RIGHT, bitmap, *row - 1, *col, color, exp))) { - /**edge = RIGHT;*/ - (*row)--; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - /* NORTHEAST */ - if ((*col + 1 < AT_BITMAP_WIDTH(marked) && *row >= 1 && !is_marked_edge(BOTTOM, *row - 1, *col + 1, marked) - && is_outline_edge(BOTTOM, bitmap, *row - 1, *col + 1, color, exp))) { - *edge = BOTTOM; - (*col)++; - (*row)--; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - *edge = NO_EDGE; - break; - case BOTTOM: - if ((!is_marked_edge(RIGHT, *row, *col, marked) - && is_outline_edge(RIGHT, bitmap, *row, *col, color, exp))) { - *edge = RIGHT; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - /* EAST */ - if ((*col + 1 < AT_BITMAP_WIDTH(marked) - && !is_marked_edge(BOTTOM, *row, *col + 1, marked) - && is_outline_edge(BOTTOM, bitmap, *row, *col + 1, color, exp))) { - /**edge = BOTTOM;*/ - (*col)++; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - /* SOUTHEAST */ - if ((*col + 1 < AT_BITMAP_WIDTH(marked) && *row + 1 < AT_BITMAP_HEIGHT(marked) - && !is_marked_edge(LEFT, *row + 1, *col + 1, marked) - && is_outline_edge(LEFT, bitmap, *row + 1, *col + 1, color, exp))) { - *edge = LEFT; - (*col)++; - (*row)++; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - *edge = NO_EDGE; - break; - case LEFT: - if ((!is_marked_edge(BOTTOM, *row, *col, marked) - && is_outline_edge(BOTTOM, bitmap, *row, *col, color, exp))) { - *edge = BOTTOM; - pos.x = *col + 1; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - /* SOUTH */ - if ((*row + 1 < AT_BITMAP_HEIGHT(marked) - && !is_marked_edge(LEFT, *row + 1, *col, marked) - && is_outline_edge(LEFT, bitmap, *row + 1, *col, color, exp))) { - /**edge = LEFT;*/ - (*row)++; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row - 1; - break; - } - CHECK_FATAL(); - /* SOUTHWEST */ - if ((*col >= 1 && *row + 1 < AT_BITMAP_HEIGHT(marked) - && !is_marked_edge(TOP, *row + 1, *col - 1, marked) - && is_outline_edge(TOP, bitmap, *row + 1, *col - 1, color, exp))) { - *edge = TOP; - (*col)--; - (*row)++; - pos.x = *col; - pos.y = AT_BITMAP_HEIGHT(bitmap) - *row; - break; - } - CHECK_FATAL(); - case NO_EDGE: - default: - *edge = NO_EDGE; - break; - } -cleanup: - return (pos); -} diff --git a/src/autotrace/pxl-outline.h b/src/autotrace/pxl-outline.h deleted file mode 100644 index e00d802..0000000 --- a/src/autotrace/pxl-outline.h +++ /dev/null @@ -1,58 +0,0 @@ -/* pxl-outline.h: find a list of outlines which make up one character. */ - -#ifndef PXL_OUTLINE_H -#define PXL_OUTLINE_H - -#include "autotrace.h" -#include "exception.h" -#include "bitmap.h" -#include "color.h" - -/* This is a list of contiguous points on the bitmap. */ -typedef struct { - at_coord *data; - unsigned length; - gboolean clockwise; - at_color color; - gboolean open; -} pixel_outline_type; - -/* The Nth coordinate in the list. */ -#define O_COORDINATE(p_o, n) ((p_o).data[n]) - -/* The length of the list. */ -#define O_LENGTH(p_o) ((p_o).length) - -/* Whether the outline moves clockwise or counterclockwise. */ -#define O_CLOCKWISE(p_o) ((p_o).clockwise) - -/* Since a pixel outline is cyclic, the index of the next coordinate - after the last is the first, and the previous coordinate before the - first is the last. */ -#define O_NEXT(p_o, n) (((n) + 1) % O_LENGTH (p_o)) -#define O_PREV(p_o, n) ((n) == 0 \ - ? O_LENGTH (p_o) - 1 \ - : (n) - 1) - -/* And the character turns into a list of such lists. */ -typedef struct { - pixel_outline_type *data; - unsigned length; -} pixel_outline_list_type; - -/* The Nth list in the list of lists. */ -#define O_LIST_OUTLINE(p_o_l, n) ((p_o_l).data[n]) - -/* The length of the list of lists. */ -#define O_LIST_LENGTH(p_o_l) ((p_o_l).length) - -/* Find all pixels on the outline in the character C. */ -extern pixel_outline_list_type find_outline_pixels(at_bitmap * bitmap, at_color * bg_color, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data, at_exception_type * exp); - -/* Find all pixels on the center line of the character C. */ -extern pixel_outline_list_type find_centerline_pixels(at_bitmap * bitmap, at_color bg_color, at_progress_func notify_progress, gpointer progress_data, at_testcancel_func test_cancel, gpointer testcancel_data, at_exception_type * exp); - -/* Free the memory in the list. */ -extern void free_pixel_outline_list(pixel_outline_list_type *); - -#endif /* not PXL_OUTLINE_H */ diff --git a/src/autotrace/quantize.h b/src/autotrace/quantize.h deleted file mode 100644 index 3e62c2e..0000000 --- a/src/autotrace/quantize.h +++ /dev/null @@ -1,52 +0,0 @@ -/* quantize.h: Quantize a high color bitmap - - Copyright (C) 2001, 2002 Martin Weber - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#include "bitmap.h" -#include "color.h" -#include "exception.h" - -#ifndef QUANTIZE_H -#define QUANTIZE_H - -#define PRECISION_R 7 -#define PRECISION_G 7 -#define PRECISION_B 7 - -#define HIST_R_ELEMS (1< - -/* Print a spline in human-readable form. */ - -void print_spline(spline_type s) -{ - assert(SPLINE_DEGREE(s) == LINEARTYPE || SPLINE_DEGREE(s) == CUBICTYPE); - - if (SPLINE_DEGREE(s) == LINEARTYPE) - fprintf(stdout, "(%.3f,%.3f)--(%.3f,%.3f).\n", START_POINT(s).x, START_POINT(s).y, END_POINT(s).x, END_POINT(s).y); - - else if (SPLINE_DEGREE(s) == CUBICTYPE) - fprintf(stdout, "(%.3f,%.3f)..ctrls(%.3f,%.3f)&(%.3f,%.3f)..(%.3f,%.3f).\n", START_POINT(s).x, START_POINT(s).y, CONTROL1(s).x, CONTROL1(s).y, CONTROL2(s).x, CONTROL2(s).y, END_POINT(s).x, END_POINT(s).y); -} - -/* Evaluate the spline S at a given T value. This is an implementation - of de Casteljau's algorithm. See Schneider's thesis, p.37. - The variable names are taken from there. */ - -at_real_coord evaluate_spline(spline_type s, gfloat t) -{ - spline_type V[4]; /* We need degree+1 splines, but assert degree <= 3. */ - signed i, j; - gfloat one_minus_t = (gfloat) 1.0 - t; - polynomial_degree degree = SPLINE_DEGREE(s); - - for (i = 0; i <= degree; i++) { - V[0].v[i].x = s.v[i].x; - V[0].v[i].y = s.v[i].y; - V[0].v[i].z = s.v[i].z; - } - - for (j = 1; j <= degree; j++) - for (i = 0; i <= degree - j; i++) { - at_real_coord t1 = Pmult_scalar(V[j - 1].v[i], one_minus_t); - at_real_coord t2 = Pmult_scalar(V[j - 1].v[i + 1], t); - at_real_coord temp = Padd(t1, t2); - V[j].v[i].x = temp.x; - V[j].v[i].y = temp.y; - V[j].v[i].z = temp.z; - } - - return V[degree].v[0]; -} - -/* Return a new, empty, spline list. */ - -spline_list_type *new_spline_list(void) -{ - spline_list_type *answer; - - XMALLOC(answer, sizeof(spline_list_type)); - *answer = empty_spline_list(); - return answer; -} - -spline_list_type empty_spline_list(void) -{ - spline_list_type answer; - SPLINE_LIST_DATA(answer) = NULL; - SPLINE_LIST_LENGTH(answer) = 0; - return answer; -} - -/* Return a new spline list with SPLINE as the first element. */ - -spline_list_type *new_spline_list_with_spline(spline_type spline) -{ - spline_list_type *answer; - - answer = new_spline_list(); - XMALLOC(SPLINE_LIST_DATA(*answer), sizeof(spline_type)); - SPLINE_LIST_ELT(*answer, 0) = spline; - SPLINE_LIST_LENGTH(*answer) = 1; - - return answer; -} - -/* Free the storage in a spline list. We don't have to free the - elements, since they are arrays in automatic storage. And we don't - want to free the list if it was empty. */ - -void free_spline_list(spline_list_type spline_list) -{ - free(SPLINE_LIST_DATA(spline_list)); -} - -/* Append the spline S to the list SPLINE_LIST. */ - -void append_spline(spline_list_type * l, spline_type s) -{ - assert(l != NULL); - - SPLINE_LIST_LENGTH(*l)++; - XREALLOC(SPLINE_LIST_DATA(*l), SPLINE_LIST_LENGTH(*l) * sizeof(spline_type)); - LAST_SPLINE_LIST_ELT(*l) = s; -} - -/* Tack the elements in the list S2 onto the end of S1. - S2 is not changed. */ - -void concat_spline_lists(spline_list_type * s1, spline_list_type s2) -{ - unsigned this_spline; - unsigned new_length; - - assert(s1 != NULL); - - new_length = SPLINE_LIST_LENGTH(*s1) + SPLINE_LIST_LENGTH(s2); - - XREALLOC(SPLINE_LIST_DATA(*s1), new_length * sizeof(spline_type)); - - for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH(s2); this_spline++) - SPLINE_LIST_ELT(*s1, SPLINE_LIST_LENGTH(*s1)++) - = SPLINE_LIST_ELT(s2, this_spline); -} - -/* Return a new, empty, spline list array. */ - -spline_list_array_type new_spline_list_array(void) -{ - spline_list_array_type answer; - - SPLINE_LIST_ARRAY_DATA(answer) = NULL; - SPLINE_LIST_ARRAY_LENGTH(answer) = 0; - - return answer; -} - -/* Free the storage in a spline list array. We don't - want to free the list if it is empty. */ -void free_spline_list_array(spline_list_array_type * spline_list_array) -{ - unsigned this_list; - - for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH(*spline_list_array); this_list++) - free_spline_list(SPLINE_LIST_ARRAY_ELT(*spline_list_array, this_list)); - - free(SPLINE_LIST_ARRAY_DATA(*spline_list_array)); -} - -/* Append the spline S to the list SPLINE_LIST_ARRAY. */ - -void append_spline_list(spline_list_array_type * l, spline_list_type s) -{ - SPLINE_LIST_ARRAY_LENGTH(*l)++; - XREALLOC(SPLINE_LIST_ARRAY_DATA(*l), SPLINE_LIST_ARRAY_LENGTH(*l) * sizeof(spline_list_type)); - LAST_SPLINE_LIST_ARRAY_ELT(*l) = s; -} diff --git a/src/autotrace/spline.h b/src/autotrace/spline.h deleted file mode 100644 index 6cbd143..0000000 --- a/src/autotrace/spline.h +++ /dev/null @@ -1,88 +0,0 @@ -/* spline.h: manipulate the spline representation. - Some of macrs are only renamed macros in output.h. */ - -#ifndef SPLINE_H -#define SPLINE_H - -#include -#include "autotrace.h" -#include "output.h" - -typedef at_polynomial_degree polynomial_degree; -typedef at_spline_type spline_type; - -#define LINEARTYPE AT_LINEARTYPE -#define QUADRATICTYPE AT_QUADRATICTYPE -#define CUBICTYPE AT_CUBICTYPE -#define PARALLELELLIPSETYPE AT_PARALLELELLIPSETYPE -#define ELLIPSETYPE AT_ELLIPSETYPE -#define CIRCLETYPE AT_CIRCLETYPE - -#define START_POINT AT_SPLINE_START_POINT_VALUE -#define CONTROL1 AT_SPLINE_CONTROL1_VALUE -#define CONTROL2 AT_SPLINE_CONTROL2_VALUE -#define END_POINT AT_SPLINE_END_POINT_VALUE -#define SPLINE_DEGREE AT_SPLINE_DEGREE_VALUE -#define SPLINE_LINEARITY(spl) ((spl).linearity) - -#ifndef _IMPORTING -/* Print a spline on the given file. */ -extern void print_spline(spline_type); - -/* Evaluate SPLINE at the given T value. */ -extern at_real_coord evaluate_spline(spline_type spline, gfloat t); -#endif - -/* Each outline in a character is typically represented by many - splines. So, here is a list structure for that: */ -typedef at_spline_list_type spline_list_type; - -/* An empty list will have length zero (and null data). */ -#define SPLINE_LIST_LENGTH AT_SPLINE_LIST_LENGTH_VALUE - -/* The address of the beginning of the array of data. */ -#define SPLINE_LIST_DATA AT_SPLINE_LIST_DATA_VALUE - -/* The element INDEX in S_L. */ -#define SPLINE_LIST_ELT AT_SPLINE_LIST_ELT_VALUE - -/* The last element in S_L. */ -#define LAST_SPLINE_LIST_ELT(s_l) \ - (SPLINE_LIST_DATA (s_l)[SPLINE_LIST_LENGTH (s_l) - 1]) - -/* The previous and next elements to INDEX in S_L. */ -#define NEXT_SPLINE_LIST_ELT(s_l, index) \ - SPLINE_LIST_ELT (s_l, ((index) + 1) % SPLINE_LIST_LENGTH (s_l)) -#define PREV_SPLINE_LIST_ELT(s_l, index) \ - SPLINE_LIST_ELT (s_l, index == 0 \ - ? SPLINE_LIST_LENGTH (s_l) - 1 \ - : index - 1) - -#ifndef _IMPORTING -/* Construct and destroy new `spline_list_type' objects. */ -extern spline_list_type *new_spline_list(void); /* Allocate new memory */ -extern spline_list_type empty_spline_list(void); /* No allocation */ -extern spline_list_type *new_spline_list_with_spline(spline_type); -extern void free_spline_list(spline_list_type); - -/* Append the spline S to the list S_LIST. */ -extern void append_spline(spline_list_type * s_list, spline_type s); - -/* Append the elements in list S2 to S1, changing S1. */ -extern void concat_spline_lists(spline_list_type * s1, spline_list_type s2); -#endif - -typedef at_spline_list_array_type spline_list_array_type; - -/* Turns out we can use the same definitions for lists of lists as for - just lists. But we define the usual names, just in case. */ -#define SPLINE_LIST_ARRAY_LENGTH AT_SPLINE_LIST_ARRAY_LENGTH_VALUE -#define SPLINE_LIST_ARRAY_DATA SPLINE_LIST_DATA -#define SPLINE_LIST_ARRAY_ELT AT_SPLINE_LIST_ARRAY_ELT_VALUE -#define LAST_SPLINE_LIST_ARRAY_ELT LAST_SPLINE_LIST_ELT - -extern spline_list_array_type new_spline_list_array(void); -extern void append_spline_list(spline_list_array_type *, spline_list_type); -extern void free_spline_list_array(spline_list_array_type *); - -#endif /* not SPLINE_H */ diff --git a/src/autotrace/thin-image.c b/src/autotrace/thin-image.c deleted file mode 100644 index 2e220ec..0000000 --- a/src/autotrace/thin-image.c +++ /dev/null @@ -1,353 +0,0 @@ -/* thin-image.c: thin binary image - - Copyright (C) 2001, 2002 Martin Weber - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include -#include -#include "thin-image.h" -#include "logreport.h" -#include "types.h" -#include "bitmap.h" -#include "xstd.h" -#include - -#define PIXEL_SET(p, new) ((void)memcpy((p), (new), sizeof(Pixel))) -#define PIXEL_EQUAL(p1, p2) \ - ((p1)[0] == (p2)[0] && (p1)[1] == (p2)[1] && (p1)[2] == (p2)[2]) - -typedef unsigned char Pixel[3]; /* RGB pixel data type */ - -void thin3(at_bitmap * image, Pixel colour); -void thin1(at_bitmap * image, unsigned char colour); - -/* -------------------------------- ThinImage - Thin binary image. --------------------------- * - * - * Description: - * Thins the supplied binary image using Rosenfeld's parallel - * thinning algorithm. - * - * On Entry: - * image = Image to thin. - * - * -------------------------------------------------------------------------------------------- */ - -/* Direction masks: */ -/* N S W E */ -static unsigned int masks[] = { 0200, 0002, 0040, 0010 }; - -/* True if pixel neighbor map indicates the pixel is 8-simple and */ -/* not an end point and thus can be deleted. The neighborhood */ -/* map is defined as an integer of bits abcdefghi with a non-zero */ -/* bit representing a non-zero pixel. The bit assignment for the */ -/* neighborhood is: */ -/* */ -/* a b c */ -/* d e f */ -/* g h i */ - -static unsigned char todelete[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 -}; - -static at_color background = { 0xff, 0xff, 0xff }; - -void thin_image(at_bitmap * image, const at_color * bg, at_exception_type * exp) -{ - /* This is nasty as we need to call thin once for each - * colour in the image the way I do this is to keep a second - * copy of the bitmap and to use this to keep - * track of which colours have not yet been processed, - * trades time for pathological case memory.....*/ - long m, n, num_pixels; - at_bitmap bm; - unsigned int spp = AT_BITMAP_PLANES(image), width = AT_BITMAP_WIDTH(image), height = AT_BITMAP_HEIGHT(image); - - if (bg) - background = *bg; - - bm.height = image->height; - bm.width = image->width; - bm.np = image->np; - XMALLOC(bm.bitmap, height * width * spp); - memcpy(bm.bitmap, image->bitmap, height * width * spp); - /* that clones the image */ - - num_pixels = height * width; - switch (spp) { - case 3: - { - Pixel *ptr = (Pixel *) AT_BITMAP_BITS(&bm); - Pixel bg_color; - bg_color[0] = background.r; - bg_color[1] = background.g; - bg_color[2] = background.b; - - for (n = num_pixels - 1; n >= 0L; --n) { - Pixel p; - - PIXEL_SET(p, ptr[n]); - if (!PIXEL_EQUAL(p, bg_color)) { - /* we have a new colour in the image */ - LOG("Thinning colour (%x, %x, %x)\n", p[0], p[1], p[2]); - for (m = n - 1; m >= 0L; --m) { - if (PIXEL_EQUAL(ptr[m], p)) - PIXEL_SET(ptr[m], bg_color); - } - thin3(image, p); - } - } - break; - } - - case 1: - { - unsigned char *ptr = AT_BITMAP_BITS(&bm); - unsigned char bg_color; - - if (background.r == background.g && background.g == background.b) - bg_color = background.r; - else - bg_color = at_color_luminance(&background); - - for (n = num_pixels - 1; n >= 0L; --n) { - unsigned char c = ptr[n]; - if (c != bg_color) { - LOG("Thinning colour %x\n", c); - for (m = n - 1; m >= 0L; --m) - if (ptr[m] == c) - ptr[m] = bg_color; - thin1(image, c); - } - } - break; - } - - default: - { - LOG("thin_image: %u-plane images are not supported", spp); - at_exception_fatal(exp, "thin_image: wrong plane images are passed"); - goto cleanup; - } - } -cleanup: - free(bm.bitmap); -} - -void thin3(at_bitmap * image, Pixel colour) -{ - Pixel *ptr, *y_ptr, *y1_ptr; - Pixel bg_color; - unsigned int xsize, ysize; /* Image resolution */ - unsigned int x, y; /* Pixel location */ - unsigned int i; /* Pass index */ - unsigned int pc = 0; /* Pass count */ - unsigned int count = 1; /* Deleted pixel count */ - unsigned int p, q; /* Neighborhood maps of adjacent */ - /* cells */ - unsigned char *qb; /* Neighborhood maps of previous */ - /* scanline */ - unsigned int m; /* Deletion direction mask */ - - bg_color[0] = background.r; - bg_color[1] = background.g; - bg_color[2] = background.b; - - LOG(" Thinning image.....\n "); - xsize = AT_BITMAP_WIDTH(image); - ysize = AT_BITMAP_HEIGHT(image); - XMALLOC(qb, xsize * sizeof(unsigned char)); - qb[xsize - 1] = 0; /* Used for lower-right pixel */ - ptr = (Pixel *) AT_BITMAP_BITS(image); - - while (count) { /* Scan image while deletions */ - pc++; - count = 0; - - for (i = 0; i < 4; i++) { - - m = masks[i]; - - /* Build initial previous scan buffer. */ - p = PIXEL_EQUAL(ptr[0], colour); - for (x = 0; x < xsize - 1; x++) - qb[x] = (unsigned char)(p = ((p << 1) & 0006) | (unsigned int)PIXEL_EQUAL(ptr[x + 1], colour)); - - /* Scan image for pixel deletion candidates. */ - y_ptr = ptr; - y1_ptr = ptr + xsize; - for (y = 0; y < ysize - 1; y++, y_ptr += xsize, y1_ptr += xsize) { - q = qb[0]; - p = ((q << 2) & 0330) | (unsigned int)PIXEL_EQUAL(y1_ptr[0], colour); - - for (x = 0; x < xsize - 1; x++) { - q = qb[x]; - p = ((p << 1) & 0666) | ((q << 3) & 0110) | (unsigned int)PIXEL_EQUAL(y1_ptr[x + 1], colour); - qb[x] = (unsigned char)p; - if ((i != 2 || x != 0) && ((p & m) == 0) && todelete[p]) { - count++; /* delete the pixel */ - PIXEL_SET(y_ptr[x], bg_color); - } - } - - /* Process right edge pixel. */ - p = (p << 1) & 0666; - if (i != 3 && (p & m) == 0 && todelete[p]) { - count++; - PIXEL_SET(y_ptr[xsize - 1], bg_color); - } - } - - if (i != 1) { - /* Process bottom scan line. */ - q = qb[0]; - p = ((q << 2) & 0330); - - y_ptr = ptr + xsize * (ysize - 1); - for (x = 0; x < xsize; x++) { - q = qb[x]; - p = ((p << 1) & 0666) | ((q << 3) & 0110); - if ((i != 2 || x != 0) && (p & m) == 0 && todelete[p]) { - count++; - PIXEL_SET(y_ptr[x], bg_color); - } - } - } - } - LOG("ThinImage: pass %d, %d pixels deleted\n", pc, count); - } - free(qb); -} - -void thin1(at_bitmap * image, unsigned char colour) -{ - unsigned char *ptr, *y_ptr, *y1_ptr; - unsigned char bg_color; - unsigned int xsize, ysize; /* Image resolution */ - unsigned int x, y; /* Pixel location */ - unsigned int i; /* Pass index */ - unsigned int pc = 0; /* Pass count */ - unsigned int count = 1; /* Deleted pixel count */ - unsigned int p, q; /* Neighborhood maps of adjacent */ - /* cells */ - unsigned char *qb; /* Neighborhood maps of previous */ - /* scanline */ - unsigned int m; /* Deletion direction mask */ - - if (background.r == background.g && background.g == background.b) - bg_color = background.r; - else - bg_color = at_color_luminance(&background); - - LOG(" Thinning image.....\n "); - xsize = AT_BITMAP_WIDTH(image); - ysize = AT_BITMAP_HEIGHT(image); - XMALLOC(qb, xsize * sizeof(unsigned char)); - qb[xsize - 1] = 0; /* Used for lower-right pixel */ - ptr = AT_BITMAP_BITS(image); - - while (count) { /* Scan image while deletions */ - pc++; - count = 0; - - for (i = 0; i < 4; i++) { - - m = masks[i]; - - /* Build initial previous scan buffer. */ - p = (ptr[0] == colour); - for (x = 0; x < xsize - 1; x++) - qb[x] = (unsigned char)(p = ((p << 1) & 0006) | (unsigned int)(ptr[x + 1] == colour)); - - /* Scan image for pixel deletion candidates. */ - y_ptr = ptr; - y1_ptr = ptr + xsize; - for (y = 0; y < ysize - 1; y++, y_ptr += xsize, y1_ptr += xsize) { - q = qb[0]; - p = ((q << 2) & 0330) | (y1_ptr[0] == colour); - - for (x = 0; x < xsize - 1; x++) { - q = qb[x]; - p = ((p << 1) & 0666) | ((q << 3) & 0110) | (unsigned int)(y1_ptr[x + 1] == colour); - qb[x] = (unsigned char)p; - if (((p & m) == 0) && todelete[p]) { - count++; - y_ptr[x] = bg_color; /* delete the pixel */ - } - } - - /* Process right edge pixel. */ - p = (p << 1) & 0666; - if ((p & m) == 0 && todelete[p]) { - count++; - y_ptr[xsize - 1] = bg_color; - } - } - - /* Process bottom scan line. */ - q = qb[0]; - p = ((q << 2) & 0330); - - y_ptr = ptr + xsize * (ysize - 1); - for (x = 0; x < xsize; x++) { - q = qb[x]; - p = ((p << 1) & 0666) | ((q << 3) & 0110); - if ((p & m) == 0 && todelete[p]) { - count++; - y_ptr[x] = bg_color; - } - } - } - LOG("thin1: pass %d, %d pixels deleted\n", pc, count); - } - free(qb); -} diff --git a/src/autotrace/thin-image.h b/src/autotrace/thin-image.h deleted file mode 100644 index fdeba2a..0000000 --- a/src/autotrace/thin-image.h +++ /dev/null @@ -1,36 +0,0 @@ -/* thin-image.h: thin binary image - - Copyright (C) 2001, 2002 Martin Weber - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 of - the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. */ - -#ifndef THIN_IMAGE_H -#define THIN_IMAGE_H - -/* - * C code from the article - * "Efficient Binary Image Thinning using Neighborhood Maps" - * by Joseph M. Cychosz, 3ksnn64@ecn.purdue.edu - * in "Graphics Gems IV", Academic Press, 1994 - */ - -#include "bitmap.h" -#include "color.h" -#include "exception.h" - -void thin_image(at_bitmap * image, const at_color * bg_color, at_exception_type * exp); - -#endif /* not THIN_IMAGE_H */ diff --git a/src/autotrace/types.h b/src/autotrace/types.h deleted file mode 100644 index a7b6843..0000000 --- a/src/autotrace/types.h +++ /dev/null @@ -1,42 +0,0 @@ -/* types.h: general types - Copyright (C) 2000, 2001 Martin Weber - - The author can be contacted at - - 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#ifndef AT_TYPES_H -#define AT_TYPES_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Cartesian points. */ - typedef struct _at_coord { - gushort x, y; - } at_coord; - - typedef struct _at_real_coord { - gfloat x, y, z; - } at_real_coord; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* not AT_TYPES_H */ diff --git a/src/autotrace/vector.c b/src/autotrace/vector.c deleted file mode 100644 index 2b6375a..0000000 --- a/src/autotrace/vector.c +++ /dev/null @@ -1,260 +0,0 @@ -/* vector.c: vector/point operations. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* Def: HAVE_CONFIG_H */ - -#include "vector.h" -#include "logreport.h" -#include "epsilon-equal.h" -#include -#include -#include -#include - -static gfloat acos_d(gfloat, at_exception_type * excep); - -#define _USE_MATH_DEFINES -#include - -/* Given the point COORD, return the corresponding vector. */ - -vector_type make_vector(const at_real_coord c) -{ - vector_type v; - - v.dx = c.x; - v.dy = c.y; - v.dz = c.z; - - return v; -} - -/* And the converse: given a vector, return the corresponding point. */ - -at_real_coord vector_to_point(const vector_type v) -{ - at_real_coord coord; - - coord.x = v.dx; - coord.y = v.dy; - - return coord; -} - -gfloat magnitude(const vector_type v) -{ - return (gfloat) sqrt(v.dx * v.dx + v.dy * v.dy + v.dz * v.dz); -} - -vector_type normalize(const vector_type v) -{ - vector_type new_v; - gfloat m = magnitude(v); - - /* assert (m > 0.0); */ - - if (m > 0.0) { - new_v.dx = v.dx / m; - new_v.dy = v.dy / m; - new_v.dz = v.dz / m; - } else { - new_v.dx = v.dx; - new_v.dy = v.dy; - new_v.dz = v.dz; - } - - return new_v; -} - -vector_type Vadd(const vector_type v1, const vector_type v2) -{ - vector_type new_v; - - new_v.dx = v1.dx + v2.dx; - new_v.dy = v1.dy + v2.dy; - new_v.dz = v1.dz + v2.dz; - - return new_v; -} - -gfloat Vdot(const vector_type v1, const vector_type v2) -{ - return v1.dx * v2.dx + v1.dy * v2.dy + v1.dz * v2.dz; -} - -vector_type Vmult_scalar(const vector_type v, const gfloat r) -{ - vector_type new_v; - - new_v.dx = v.dx * r; - new_v.dy = v.dy * r; - new_v.dz = v.dz * r; - - return new_v; -} - -/* Given the IN_VECTOR and OUT_VECTOR, return the angle between them in - degrees, in the range zero to 180. */ - -gfloat Vangle(const vector_type in_vector, const vector_type out_vector, at_exception_type * exp) -{ - vector_type v1 = normalize(in_vector); - vector_type v2 = normalize(out_vector); - - return acos_d(Vdot(v2, v1), exp); -} - -at_real_coord Vadd_point(const at_real_coord c, const vector_type v) -{ - at_real_coord new_c; - - new_c.x = c.x + v.dx; - new_c.y = c.y + v.dy; - new_c.z = c.z + v.dz; - return new_c; -} - -at_real_coord Vsubtract_point(const at_real_coord c, const vector_type v) -{ - at_real_coord new_c; - - new_c.x = c.x - v.dx; - new_c.y = c.y - v.dy; - new_c.z = c.z - v.dz; - return new_c; -} - -at_coord Vadd_int_point(const at_coord c, const vector_type v) -{ - at_coord a; - - a.x = (unsigned short)lround((gfloat) c.x + v.dx); - a.y = (unsigned short)lround((gfloat) c.y + v.dy); - return a; -} - -vector_type Vabs(const vector_type v) -{ - vector_type new_v; - - new_v.dx = (gfloat) fabs(v.dx); - new_v.dy = (gfloat) fabs(v.dy); - new_v.dz = (gfloat) fabs(v.dz); - return new_v; -} - -/* Operations on points. */ - -at_real_coord Padd(const at_real_coord coord1, const at_real_coord coord2) -{ - at_real_coord sum; - - sum.x = coord1.x + coord2.x; - sum.y = coord1.y + coord2.y; - sum.z = coord1.z + coord2.z; - - return sum; -} - -at_real_coord Pmult_scalar(const at_real_coord coord, const gfloat r) -{ - at_real_coord answer; - - answer.x = coord.x * r; - answer.y = coord.y * r; - answer.z = coord.z * r; - - return answer; -} - -vector_type Psubtract(const at_real_coord c1, const at_real_coord c2) -{ - vector_type v; - - v.dx = c1.x - c2.x; - v.dy = c1.y - c2.y; - v.dz = c1.z - c2.z; - - return v; -} - -/* Operations on integer points. */ - -vector_type IPsubtract(const at_coord coord1, const at_coord coord2) -{ - vector_type v; - - v.dx = (gfloat) (coord1.x - coord2.x); - v.dy = (gfloat) (coord1.y - coord2.y); - v.dz = 0.0; - - return v; -} - -at_coord IPsubtractP(const at_coord c1, const at_coord c2) -{ - at_coord c; - - c.x = c1.x - c2.x; - c.y = c1.y - c2.y; - - return c; -} - -at_coord IPadd(const at_coord c1, const at_coord c2) -{ - at_coord c; - - c.x = c1.x + c2.x; - c.y = c1.y + c2.y; - - return c; -} - -at_coord IPmult_scalar(const at_coord c, const int i) -{ - at_coord a; - - a.x = (unsigned short)(c.x * i); - a.y = (unsigned short)(c.y * i); - - return a; -} - -at_real_coord IPmult_real(const at_coord c, const gfloat r) -{ - at_real_coord a; - - a.x = c.x * r; - a.y = c.y * r; - - return a; -} - -gboolean IPequal(const at_coord c1, const at_coord c2) -{ - if ((c1.x == c2.x) && (c1.y == c2.y)) - return TRUE; - else - return FALSE; -} - -static gfloat acos_d(gfloat v, at_exception_type * excep) -{ - gfloat a; - - if (epsilon_equal(v, 1.0)) - v = 1.0; - else if (epsilon_equal(v, -1.0)) - v = -1.0; - - errno = 0; - a = (gfloat) acos(v); - if (errno == ERANGE || errno == EDOM) { - at_exception_fatal(excep, strerror(errno)); - return 0.0; - } - - return a * (gfloat) 180.0 / (gfloat) M_PI; -} diff --git a/src/autotrace/vector.h b/src/autotrace/vector.h deleted file mode 100644 index fe26254..0000000 --- a/src/autotrace/vector.h +++ /dev/null @@ -1,65 +0,0 @@ -/* vector.h: operations on vectors and points. */ - -#ifndef VECTOR_H -#define VECTOR_H - -#include "types.h" -#include "exception.h" - -/* Our vectors are represented as displacements along the x and y axes. */ - -typedef struct { - gfloat dx, dy, dz; -} vector_type; - -/* Consider a point as a vector from the origin. */ -extern vector_type make_vector(const at_real_coord); - -/* And a vector as a point, i.e., a displacement from the origin. */ -extern at_real_coord vector_to_point(const vector_type); - -/* Definitions for these common operations can be found in any decent - linear algebra book, and most calculus books. */ - -extern gfloat magnitude(const vector_type); -extern vector_type normalize(const vector_type); - -extern vector_type Vadd(const vector_type, const vector_type); -extern gfloat Vdot(const vector_type, const vector_type); -extern vector_type Vmult_scalar(const vector_type, const gfloat); -extern gfloat Vangle(const vector_type in, const vector_type out, at_exception_type * exp); - -/* These operations could have been named `P..._vector' just as well as - V..._point, so we may as well allow both names. */ -#define Padd_vector Vadd_point -extern at_real_coord Vadd_point(const at_real_coord, const vector_type); - -#define Psubtract_vector Vsubtract_point -extern at_real_coord Vsubtract_point(const at_real_coord, const vector_type); - -/* This returns the rounded sum. */ -#define IPadd_vector Vadd_int_point -extern at_coord Vadd_int_point(const at_coord, const vector_type); - -/* Take the absolute value of both components. */ -extern vector_type Vabs(const vector_type); - -/* Operations on points with real coordinates. It is not orthogonal, - but more convenient, to have the subtraction operator return a - vector, and the addition operator return a point. */ -extern vector_type Psubtract(const at_real_coord, const at_real_coord); - -/* These are heavily used in spline fitting. */ -extern at_real_coord Padd(const at_real_coord, const at_real_coord); -extern at_real_coord Pmult_scalar(const at_real_coord, const gfloat); - -/* Similarly, for points with integer coordinates; here, a subtraction - operator that does return another point is useful. */ -extern vector_type IPsubtract(const at_coord, const at_coord); -extern at_coord IPsubtractP(const at_coord, const at_coord); -extern at_coord IPadd(const at_coord, const at_coord); -extern at_coord IPmult_scalar(const at_coord, const int); -extern at_real_coord IPmult_real(const at_coord, const gfloat); -extern gboolean IPequal(const at_coord, const at_coord); - -#endif /* not VECTOR_H */ diff --git a/src/autotrace/xstd.h b/src/autotrace/xstd.h deleted file mode 100644 index a8862e3..0000000 --- a/src/autotrace/xstd.h +++ /dev/null @@ -1,85 +0,0 @@ -/* xstd.h: Wrappers for functions in C standard library - Was: xmem, xfile */ - -/* These call the corresponding function in the standard library, and - abort if those routines fail. */ - -#ifndef XSTD_H -#define XSTD_H - -#include "types.h" -#include -#include - -/* - * XMEM - */ -#ifndef __cplusplus -#define XMALLOC(new_mem, size) \ -do \ - { \ - assert(size); \ - new_mem = (gpointer) malloc (size); \ - assert(new_mem); \ - } while (0) - -#define XCALLOC(new_mem, size) \ -do \ - { \ - assert(size); \ - new_mem = (gpointer) calloc (size, 1); \ - assert(new_mem); \ - } while (0) - -#define XREALLOC(old_ptr, size) \ -do \ - { \ - gpointer new_mem; \ - \ - if (old_ptr == NULL) \ - XMALLOC (new_mem, size); \ - else \ - { \ - new_mem = (gpointer) realloc (old_ptr, size); \ - assert(new_mem); \ - } \ - \ - old_ptr = new_mem; \ -} while (0) - -#else -/* Use templates if Cplusplus... */ -#define XMALLOC(new_mem, size) \ -do \ - { \ - assert(size); \ - (gpointer&)(new_mem) = (gpointer) malloc (size); \ - assert(new_mem); \ - } while (0) - -#define XCALLOC(new_mem, sizex) \ -do \ - { \ - assert(sizex); \ - (gpointer&)(new_mem) = (void *) calloc (sizex, 1); \ - assert(new_mem); \ - } while (0) - -#define XREALLOC(old_ptr, size) \ -do \ - { \ - gpointer new_mem; \ - \ - if (old_ptr == NULL) \ - XMALLOC (new_mem, (size)); \ - else \ - { \ - (gpointer&) new_mem = (gpointer) realloc ((old_ptr), (size)); \ - assert(new_mem); \ - } \ - \ - (gpointer&)old_ptr = new_mem; \ - } while (0) -#endif - -#endif /* Not XSTD_H */ diff --git a/src/cv/.otsu.cpp.swp b/src/cv/.otsu.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..39e1bdb07e0f8e6f1bb7cfc296f781febf1da456 GIT binary patch literal 12288 zcmeHN&1)n@6t8tJMmM{nh}G`+$s|E^W$5XybT{el>aDJx zF$uvByom=disH$Gc<|!IlSf4q{{oMG96abjyounis(OZ*b-~MCR;uCGJ^ku^{@$yG zkQyxR-`Junoh63rA;zk29t+?3Z2#=jc$GpZE&i|TKyn_5yc1SQN2aR7qxy~}-B(3f zJ{9p{H!Tj!KqcKuhTU-36{@N`VOieM$_X60yU@E!0i@G0;K@CJ|rG4MDr2b=}| zMD4o%zcdH#att^I90QI4$ADwNG4Ow5;PjLcE`8+EOY^wBYF@2SFG#LxSy-uluT-?M z5{g>U)vMH67j+gZ?TZ zDzVW^nHK86YZ=#+1QiW9FKC1Vs|rsgsH^hwX3=UGNpro#y_Ob_53KgXVP}w+Eq^8e zEKC_ToX8??kkxWl2X*+gbosJRyPG>3z0LmiZf~y%bzLW$2E{Y5^6Ih*@-pL*Pu5E> zJ?0+lF1L*j&_NT@w1zEDQ~2+qyXI)^lNB1JnLui35mRuWxU|}cN(X6%cP!W{$|Jc~ z>K^$tl?$0PbeB4z$f}D=zWFW12*0V_RHCz(7}>txyy@&9 zRim`xeGI+{;L}u?soJNcjpZqgk~HJwEkvT=jVdAkf@9phaA-DHp`I7TOI!-GC24KV zOg-t#P(_5bh)3YQJleHdmR=(5Vw|6lO?nFZ%O*Jqsr9OQpkxp#+g_`r?=wnKngtkq z8__4L#WSAsLSgvOP%T3CqU421xXDpRFjm(6*uliqsGXR5B+O$VL}s{Hotv{x%w0Ru zv(Q)~l$OL?^^~kbe4j9^8#W=s;MVre3!DA*o$X%FH>ze>mwj3u52mGY;@6OA2^`CG zO|uf+OkGK*(mN2lZ+*XiWpDjb6Cw< +#include "../ipcv.h" + +// A quick way to split strings separated via any character +// delimiter. +std::vector adv_tokenizer(std::string s, char del) +{ + std::stringstream ss(s); + std::string word; + std::vector tokens; + while (!ss.eof()) { + getline(ss, word, del); +// cout << word << endl; + tokens.push_back(word); + } + return tokens; +} + +void print_help() +{ + printf("-f : file : input file\n-o : output : output file\n-h : help : print help\n"); +} + +void print_points(Shapes shapes) +{ + int i = 0; + for (vector contour : shapes.contours) + { + cout << "CONTOUR #" << i << "\n"; + for (Point point : contour) + { + cout << "Point: " << point.x << ", " << point.y << "\n"; + } + i++; + } +} + +// Test function +int main(int argc, char *argv[]) +{ + // CLI arguments and such + int verbose = 0; + int order = 0; + std::string image_path; + std::string output_path; + int rc; + /* getopt_long stores the option index here. */ + int option_index = 0; + + /* This contains the short command line parameters list */ + const char* getoptOptions = "f:o:h"; /* add lots of stuff here */ + + /* This contains the long command line parameter list, it should mostly + match the short list */ + struct option long_options[] = { + /* These options don’t set a flag. + We distinguish them by their indices. */ + {"file", required_argument, 0, 'f'}, + {"output", required_argument, 0, 'o'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + + opterr = 1; /* Enable automatic error reporting */ + while ((rc = getopt_long_only( + argc, + argv, + getoptOptions, + long_options, + &option_index)) != -1) { + /* Detect the end of the options. */ + switch (rc) + { + case 'f': + image_path = optarg; + break; + case 'o': + output_path = optarg; + break; + case 'h': + print_help(); + break; + default: + fprintf (stderr, "Internal error: undefined option %0xX\n", rc); + exit(1); + } // End switch + } /* end while */ + + if ((optind < argc) || image_path.empty() || output_path.empty()){ + print_help(); + exit(1); + } + + Mat img = imread(image_path, 0); + if(img.empty()) + { + std::cout << "Could not read the image: " << image_path << std::endl; + return 1; + } + + // Separate the file title from the rest of the path + // FIXME: This feels awful, but I don't know enough about C++ to be sure. + std::vector path_vec = adv_tokenizer(output_path, '/'); + std::string file_title = path_vec.back(); + path_vec.pop_back(); + std::string path_string; + if (output_path[0] == '/') + path_string += '/'; + for (auto &dir : path_vec) + { + if (!dir.empty()) { + path_string += dir + '/'; + } + } + std::cout << "Using: " << path_string << file_title << "\n"; + Mat otsu_img = otsu(img, path_string + "otsu_" + file_title); + Mat skel_img = skeletonize(otsu_img, path_string + "skel_" + file_title); + Shapes shapes = find_shapes(skel_img, path_string + "shape_" + file_title); + print_points(shapes); + + + return 0; +} + diff --git a/src/cv/ipcv.cpp b/src/cv/ipcv.cpp new file mode 100644 index 0000000..618ba66 --- /dev/null +++ b/src/cv/ipcv.cpp @@ -0,0 +1,118 @@ +#include "ipcv.h" + +// Skeletonization algorithm. I might mess around with this +// more down the road. +// TODO: Where the hell did I find this? +Mat skeletonize(Mat img_inv, std::string output_path) { + Mat img; + bitwise_not(img_inv, img); + cv::Mat skel(img.size(), CV_8UC1, cv::Scalar(0)); + cv::Mat temp; + cv::Mat eroded; + + cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)); + + bool done; + do + { + cv::erode(img, eroded, element); + cv::dilate(eroded, temp, element); // temp = open(img) + cv::subtract(img, temp, temp); + cv::bitwise_or(skel, temp, skel); + eroded.copyTo(img); + + done = (cv::countNonZero(img) == 0); + } while (!done); + + Mat skel_invert; + bitwise_not(skel, skel_invert); + + //Mat downsampled; + //pyrDown(skel_invert, downsampled, Size( img.cols/2, img.rows/2 )); + if (output_path != "") { + imwrite(output_path, skel_invert); + std::cout << "Image has been written to " << output_path << "\n"; + } + return skel_invert; +} + +// Apply an Otsu's thresholding to the object. I found that this was +// the best function of the ones I tried +Mat otsu(Mat img, std::string output_path) +{ + int k; + // Upsample our image + Mat upsampled; + pyrUp(img, upsampled, Size( img.cols*2, img.rows*2 )); + + //otsu's thresholding after gaussian filtering + Mat gauss_thresh; + Mat blur; + GaussianBlur(upsampled, blur, Size(5, 5), 0, 0); + threshold(blur, gauss_thresh, 0, 255, THRESH_OTSU); + + if (output_path != "") { + imwrite(output_path, gauss_thresh); + std::cout << "Image has been written to " << output_path << "\n"; + } + return gauss_thresh; +} + +// Prereqs: Must be binary color image, target must be black +Shapes find_shapes(Mat img, std::string output_path) { + Mat src; + bitwise_not(img, src); + + Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3); + src = src > 1; + //namedWindow( "Source", 1 ); + //imshow( "Source", src ); + vector> contours; + vector hierarchy; + findContours( src, contours, hierarchy, + RETR_TREE, CHAIN_APPROX_SIMPLE ); + + // Remove contours that are too small. +// double min_area=10; // area threshold + int min_points=10; // area threshold + for(int i = 0; i< contours.size(); i++) // iterate through each contour. + { + /*double area=contourArea(contours[i],false); // Find the area of contour + if(area < min_area) + contours.erase(contours.begin() + i);*/ + + /*int points = contours[i].size(); + if (points < min_points) + cout << "Found short contour. Removing...\n"; + contours.erase(contours.begin() + i); + hierarchy.erase(hierarchy.begin() + i);*/ + } + + if (output_path != "") { + // iterate through all the top-level contours, + // draw each connected component with its own random color + int idx = 0; + for( ; idx >= 0; idx = hierarchy[idx][0] ) + { + Scalar color( rand()&255, rand()&255, rand()&255 ); + drawContours( dst, contours, idx, color, FILLED, 8, hierarchy ); + } + + imwrite(output_path, dst); + std::cout << "Image has been written to " << output_path << "\n"; + } + + return Shapes{contours, hierarchy}; +} + +// FIXME: This shouldn't be necessary when I'm done >:) +void prep_otsu(char* image_path) +{ + Mat img = imread(image_path, 0); + if(img.empty()) + { + std::cout << "Could not read the image: " << image_path << std::endl; + return; + } + otsu(img, image_path); +} diff --git a/src/cv/ipcv.h b/src/cv/ipcv.h new file mode 100644 index 0000000..82d834a --- /dev/null +++ b/src/cv/ipcv.h @@ -0,0 +1,20 @@ +#include + +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +typedef struct Shapes { + std::vector> contours; + vector hierarchy; +} Shapes; + +Mat skeletonize(Mat img_inv, std::string output_path); +Mat otsu(Mat img, std::string output_path); +Shapes find_shapes(Mat img, std::string output_path); +void prep_otsu(char* image_path); + diff --git a/src/ipcv_obj/ipcv_obj.cpp b/src/ipcv_obj/ipcv_obj.cpp new file mode 100644 index 0000000..5c89d2b --- /dev/null +++ b/src/ipcv_obj/ipcv_obj.cpp @@ -0,0 +1,106 @@ +#include "ipcv_obj.h" + +// Do OpenCV stuff +int cv_perform_processing(const char* image_path, IPCVObj* data) +{ + Mat img = imread(image_path, 0); + if(img.empty()) + { + std::cout << "Could not read the image: " << image_path << std::endl; + exit(1); + } + Mat otsu_img = otsu(img, ""); + std::cout << "Performing otsu filtering...\n"; + Mat skel_img = skeletonize(otsu_img, ""); + std::cout << "Performing Skeletonization...\n"; + Shapes shapes = find_shapes(skel_img, ""); + std::cout << "Looking for shapes...\n"; + data->set(shapes.contours); + return 0; +} + +// Create & return IPCVObj instance to Lua +static int ipcvobj_new(lua_State* L) +{ + const char* image_path = luaL_checkstring(L, 1); + int tracing_scale = luaL_checkinteger(L, 2); + + IPCVObj* object; // Declare pointer + object = new IPCVObj(); // Initialize pointer + *reinterpret_cast(lua_newuserdata(L, sizeof(IPCVObj*))) = object; // Make Lua aware of it + luaL_setmetatable(L, LUA_IPCVOBJ); // Metatable magic + + cv_perform_processing(image_path, object); // do CV stuff + + std::cout << "CV Processing complete!\n"; + return 1; +} + +// Free IPCVObj instance by Lua garbage collection +static int ipcvobj_delete(lua_State* L) +{ + delete *reinterpret_cast(lua_touserdata(L, 1)); + return 0; +} + +// Length stuff +static int ipcvobj_getLength(lua_State* L) +{ + lua_pushinteger(L, (*reinterpret_cast(luaL_checkudata(L, 1, LUA_IPCVOBJ)))->get().size()); + return 1; +} + +static int ipcvobj_getContourLength(lua_State* L) +{ + lua_pushinteger(L, (*reinterpret_cast(luaL_checkudata(L, 1, LUA_IPCVOBJ)))->get()[luaL_checkinteger(L, 2)].size()); + return 1; +} + +// Receiving data +static int ipcvobj_getContour(lua_State* L) +{ + int contourIdx = luaL_checkinteger(L, 2); + double scalingFactor = luaL_checknumber(L, 3); + + vector selectedContour = (*reinterpret_cast(luaL_checkudata(L, 1, LUA_IPCVOBJ)))->get()[contourIdx]; + + // Push all the X coords to the stack + lua_newtable(L); + for (int i = 0; i < selectedContour.size(); i++) + { + lua_pushnumber(L, selectedContour[i].x / scalingFactor); + lua_rawseti(L, -2, i); + } + + // Push all the Y coords to the stack + lua_newtable(L); + for (int i = 0; i < selectedContour.size(); i++) + { + lua_pushnumber(L, selectedContour[i].y / scalingFactor); + lua_rawseti(L, -2, i); + } + + return 2; // Returning two tables +} + +// Register IPCVObj to Lua +static void register_ipcvobj(lua_State* L){ + lua_register(L, LUA_IPCVOBJ, ipcvobj_new); + luaL_newmetatable(L, LUA_IPCVOBJ); + lua_pushcfunction(L, ipcvobj_delete); lua_setfield(L, -2, "__gc"); + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, ipcvobj_getLength); lua_setfield(L, -2, "getLength"); + lua_pushcfunction(L, ipcvobj_getContourLength); lua_setfield(L, -2, "getContourLength"); + lua_pushcfunction(L, ipcvobj_getContour); lua_setfield(L, -2, "getContour"); + lua_pop(L, 1); +} + +extern "C" { + // Program entry + int luaopen_ipcvobj(lua_State *L) + { + luaL_openlibs(L); + register_ipcvobj(L); + return 1; + } +} diff --git a/src/ipcv_obj/ipcv_obj.h b/src/ipcv_obj/ipcv_obj.h new file mode 100644 index 0000000..21e69ee --- /dev/null +++ b/src/ipcv_obj/ipcv_obj.h @@ -0,0 +1,49 @@ +#ifndef IPCV_OBJ +#define IPCV_OBJ + +/* + * This is a barebones-af interface for allowing Lua to get data directly™ from OpenCV + * */ + +// Lua C API +#include +// C++ input/output streams +#include +#include + +#include "../cv/ipcv.h" + +using namespace cv; +using namespace std; + +typedef vector> ContourList; + +// IPCVObj as C++ class +class IPCVObj +{ + private: + ContourList contours; + public: + IPCVObj(){} + void set(ContourList contours){this->contours = contours;} + ContourList get() const{return this->contours;} +}; + +int cv_perform_processing(const char* image_path, IPCVObj* data); + +// IPCVObj identifier for the Lua metatable +#define LUA_IPCVOBJ "IPCVObj" + +static int ipcvobj_new(lua_State* L); + +// Free IPCVObj instance by Lua garbage collection +static int ipcvobj_delete(lua_State* L); + +// Length stuff +static int ipcvobj_getLength(lua_State* L); +static int ipcvobj_getContourLength(lua_State* L); + +// Receiving data +static int ipcvobj_getContour(lua_State* L); + +#endif diff --git a/src/lua_util.c b/src/lua_util.c deleted file mode 100644 index 5cb17ff..0000000 --- a/src/lua_util.c +++ /dev/null @@ -1,140 +0,0 @@ -#include "lua_util.h" - -int transcribe_image(lua_State *L) -{ - int color_count = 2; - char* image_path = luaL_checkstring(L, 1); - char* background = "FFFFFF"; - int tracing_scale = luaL_checkinteger(L, 2); - - printf("Processing strokes for %s...\n", image_path); - - // AutoTrace Magic™ - at_fitting_opts_type* opts = at_fitting_opts_new(); - opts->color_count = color_count; - opts->centerline = 1; - if (background) - { - char s_red[3]; - char s_grn[3]; - char s_blu[3]; - - strncpy(s_red, background+0, 2); - strncpy(s_grn, background+2, 2); - strncpy(s_blu, background+4, 2); - - opts->background_color = at_color_new( - (char)strtol(s_red, NULL, 16), - (char)strtol(s_grn, NULL, 16), - (char)strtol(s_blu, NULL, 16) - ); - } - - // Prepare AutoTrace - autotrace_init(); - static at_bitmap_reader *rfunc = NULL; - rfunc = at_input_get_handler(image_path); - at_bitmap* bitmap; - at_splines_type* splines; - bitmap = at_bitmap_read(rfunc, image_path, NULL, NULL, NULL); - splines = at_splines_new(bitmap, opts, NULL, NULL); - - unsigned this_list; - spline_list_type list; - - // Each stroke is made up of a number of splines. - // I'm going to separate out the strokes with that -1, -1 delimiter still, - // and push each spline within that stroke one after the other. I think I still - // want to store each point in its own Lua table, because that'll just be - // so much easier to process after the fact - - // So anyway we create the stroke table - lua_newtable(L); - int point_count = 1; - - for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH(*splines); this_list++) { - unsigned this_spline; - //spline_type first; - - list = SPLINE_LIST_ARRAY_ELT(*splines, this_list); - //first = SPLINE_LIST_ELT(list, 0); - - // Dump the points of splines 4 at a time, delimit with -1, -1 - // Start "stroke" - for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH(list); this_spline++) { - - spline_type s = SPLINE_LIST_ELT(list, this_spline); - - // Push the transcribed points a little further onto the center of the page - double offset_x = 50.0; - double offset_y = 500.0; - - double scaling = 0.1; // Scale the points down. Default is 0.1 (Small) - switch (tracing_scale) { - case 1: - scaling = 0.1; - break; - case 2: - scaling = 0.3; - break; - case 3: - scaling = 0.5; - break; - } - - // So this is a spline. - lua_newtable(L); // One table per point - lua_pushnumber(L, START_POINT(s).x * scaling + offset_x); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, START_POINT(s).y * scaling * -1.0 + offset_y); - lua_rawseti(L, -2, 2); - lua_rawseti(L, -2, point_count++); - - lua_newtable(L); - lua_pushnumber(L, CONTROL1(s).x * scaling + offset_x); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, CONTROL1(s).y * scaling * -1.0 + offset_y); - lua_rawseti(L, -2, 2); - lua_rawseti(L, -2, point_count++); - - lua_newtable(L); - lua_pushnumber(L, CONTROL2(s).x * scaling + offset_x); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, CONTROL2(s).y * scaling * -1.0 + offset_y); - lua_rawseti(L, -2, 2); - lua_rawseti(L, -2, point_count++); - - lua_newtable(L); - lua_pushnumber(L, END_POINT(s).x * scaling + offset_x); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, END_POINT(s).y * scaling * -1.0 + offset_y); - lua_rawseti(L, -2, 2); - lua_rawseti(L, -2, point_count++); - // And that's it. Way less data processing and memory usage required - // on my part. - } - // End "stroke". We've got between 1 and N splines. - // I'm gonna say that a point with coords -1, -1 is the end of a "stroke". - lua_newtable(L); - lua_pushnumber(L, -1.0); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, -1.0); - lua_rawseti(L, -2, 2); - lua_rawseti(L, -2, point_count++); - } - - printf("Image transcription complete\n"); - - return 1; -} - -//library to be registered -static const struct luaL_Reg inkpath [] = { - {"transcribe_image", transcribe_image}, - {NULL, NULL} /* sentinel */ -}; - -int luaopen_inkpath (lua_State *L){ - luaL_newlib(L, inkpath); - return 1; -} diff --git a/src/lua_util.h b/src/lua_util.h deleted file mode 100644 index 8cfaab7..0000000 --- a/src/lua_util.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H -#include -#include -#include -#include - -#include "autotrace/autotrace.h" -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" - -#include "spline.h" - -typedef struct { - double x; - double y; -} inkpath_stroke_point; - -int transcribe_image(lua_State *L); - -#endif diff --git a/src/spline.h b/src/spline.h deleted file mode 100644 index ad8fb87..0000000 --- a/src/spline.h +++ /dev/null @@ -1,86 +0,0 @@ -/* spline.h: manipulate the spline representation. - Some of macrs are only renamed macros in output.h. */ - -#ifndef SPLINE_H -#define SPLINE_H - -#include "autotrace/output.h" - -typedef at_polynomial_degree polynomial_degree; -typedef at_spline_type spline_type; - -#define LINEARTYPE AT_LINEARTYPE -#define QUADRATICTYPE AT_QUADRATICTYPE -#define CUBICTYPE AT_CUBICTYPE -#define PARALLELELLIPSETYPE AT_PARALLELELLIPSETYPE -#define ELLIPSETYPE AT_ELLIPSETYPE -#define CIRCLETYPE AT_CIRCLETYPE - -#define START_POINT AT_SPLINE_START_POINT_VALUE -#define CONTROL1 AT_SPLINE_CONTROL1_VALUE -#define CONTROL2 AT_SPLINE_CONTROL2_VALUE -#define END_POINT AT_SPLINE_END_POINT_VALUE -#define SPLINE_DEGREE AT_SPLINE_DEGREE_VALUE -#define SPLINE_LINEARITY(spl) ((spl).linearity) - -#ifndef _IMPORTING -/* Print a spline on the given file. */ -extern void print_spline(spline_type); - -/* Evaluate SPLINE at the given T value. */ -extern at_real_coord evaluate_spline(spline_type spline, gfloat t); -#endif - -/* Each outline in a character is typically represented by many - splines. So, here is a list structure for that: */ -typedef at_spline_list_type spline_list_type; - -/* An empty list will have length zero (and null data). */ -#define SPLINE_LIST_LENGTH AT_SPLINE_LIST_LENGTH_VALUE - -/* The address of the beginning of the array of data. */ -#define SPLINE_LIST_DATA AT_SPLINE_LIST_DATA_VALUE - -/* The element INDEX in S_L. */ -#define SPLINE_LIST_ELT AT_SPLINE_LIST_ELT_VALUE - -/* The last element in S_L. */ -#define LAST_SPLINE_LIST_ELT(s_l) \ - (SPLINE_LIST_DATA (s_l)[SPLINE_LIST_LENGTH (s_l) - 1]) - -/* The previous and next elements to INDEX in S_L. */ -#define NEXT_SPLINE_LIST_ELT(s_l, index) \ - SPLINE_LIST_ELT (s_l, ((index) + 1) % SPLINE_LIST_LENGTH (s_l)) -#define PREV_SPLINE_LIST_ELT(s_l, index) \ - SPLINE_LIST_ELT (s_l, index == 0 \ - ? SPLINE_LIST_LENGTH (s_l) - 1 \ - : index - 1) - -#ifndef _IMPORTING -/* Construct and destroy new `spline_list_type' objects. */ -extern spline_list_type *new_spline_list(void); /* Allocate new memory */ -extern spline_list_type empty_spline_list(void); /* No allocation */ -extern spline_list_type *new_spline_list_with_spline(spline_type); -extern void free_spline_list(spline_list_type); - -/* Append the spline S to the list S_LIST. */ -extern void append_spline(spline_list_type * s_list, spline_type s); - -/* Append the elements in list S2 to S1, changing S1. */ -extern void concat_spline_lists(spline_list_type * s1, spline_list_type s2); -#endif - -typedef at_spline_list_array_type spline_list_array_type; - -/* Turns out we can use the same definitions for lists of lists as for - just lists. But we define the usual names, just in case. */ -#define SPLINE_LIST_ARRAY_LENGTH AT_SPLINE_LIST_ARRAY_LENGTH_VALUE -#define SPLINE_LIST_ARRAY_DATA SPLINE_LIST_DATA -#define SPLINE_LIST_ARRAY_ELT AT_SPLINE_LIST_ARRAY_ELT_VALUE -#define LAST_SPLINE_LIST_ARRAY_ELT LAST_SPLINE_LIST_ELT - -extern spline_list_array_type new_spline_list_array(void); -extern void append_spline_list(spline_list_array_type *, spline_list_type); -extern void free_spline_list_array(spline_list_array_type *); - -#endif /* not SPLINE_H */