gimp-texturize-3.0+ds/0000775000000000000000000000000014772315646013472 5ustar rootrootgimp-texturize-3.0+ds/meson.build0000664000000000000000000000366014771535253015636 0ustar rootrootproject( 'texturize', ['c', 'cpp'], meson_version: '>= 0.51.0', ) i18n = import('i18n') gettext_package = 'gimp-texturize' gimp_dep = dependency('gimp-3.0') gtk_dep = dependency('gtk+-3.0') glib_dep = dependency('glib-2.0') # TODO: Remove: gimpui_dep = dependency('gimpui-3.0') prefix = get_option('prefix') # Once we bump meson version to 0.58, we will be able to use positional argument # https://mesonbuild.com/Release-notes-for-0-58-0.html#depget_variablevarname gimp_libdir = gimp_dep.get_variable( pkgconfig: 'gimplibdir', pkgconfig_define: ['prefix', prefix], ) texturize_plugin_dir = gimp_libdir / 'plug-ins' / 'texturize' datadir = prefix / get_option('datadir') localedir = prefix / get_option('localedir') configure_include_dir = include_directories('.') conf_data = configuration_data() conf_data.set10( 'ENABLE_NLS', true, description: 'always defined to indicate that i18n is enabled', ) conf_data.set_quoted( 'GETTEXT_PACKAGE', gettext_package, description: 'The gettext translation domain.', ) conf_data.set10( 'HAVE_BIND_TEXTDOMAIN_CODESET', meson.get_compiler('c').has_function('bind_textdomain_codeset'), description: 'Define to 1 if you have the “bind_textdomain_codeset” function.', ) conf_data.set_quoted( 'PLUGIN_NAME', meson.project_name(), description: 'Plug-In name', ) conf_data.set_quoted( 'DATADIR', datadir, ) conf_data.set_quoted( 'LOCALEDIR', datadir, ) configure_file( output: 'config.h', configuration: conf_data, ) sources = [ 'src/counting.c', 'src/counting.h', 'src/graph.cpp', 'src/graphcut.cpp', 'src/maxflow.cpp', 'src/main.c', 'src/offset.c', 'src/render.c' ] executable( 'texturize', sources, dependencies: [ gimp_dep, gtk_dep, gimpui_dep, ], include_directories: configure_include_dir, install: true, install_dir: texturize_plugin_dir, ) install_subdir( 'help', install_dir: datadir / 'gimp-texturize', ) subdir('po') gimp-texturize-3.0+ds/compile0000775000000000000000000000200014771535253015035 0ustar rootroot#!/usr/bin/python import os BUILD_DIR = "build" PLUGINS_DIR_2 = os.path.expanduser("~/.config/GIMP/2.10/plug-ins") PLUGINS_DIR_3 = os.path.expanduser("~/.config/GIMP/3.0/plug-ins") def main(): if not os.path.exists(BUILD_DIR): print("Setting up...") os.system("meson setup " + BUILD_DIR) os.chdir(BUILD_DIR) print("Compiling...") os.system("meson compile") if not os.path.exists(PLUGINS_DIR_2): os.system("mkdir -p " + PLUGINS_DIR_2) print("Copying the 'texturize' binary to " + PLUGINS_DIR_2 + "...") os.system("cp texturize " + PLUGINS_DIR_2 + "/") if not os.path.exists(PLUGINS_DIR_3): os.system("mkdir -p " + PLUGINS_DIR_3) if os.path.exists(PLUGINS_DIR_3): subdir = os.path.join(PLUGINS_DIR_3, "texturize") if not os.path.exists(subdir): os.system("mkdir " + subdir) print("Copying the 'texturize' binary to " + subdir + "...") os.system("cp texturize " + subdir) if __name__ == "__main__": main() gimp-texturize-3.0+ds/clean-upstream0000775000000000000000000000015714771535253016340 0ustar rootroot#!/usr/bin/python import os def main(): os.system("rm -rf build") if __name__ == "__main__": main() gimp-texturize-3.0+ds/todo0000664000000000000000000000103014771535253014351 0ustar rootrootImprove the progress bar. Be compatible with images that have an alpha channel. Erase the very low frequency of luminosity before applying the plugin. Or advise to the homogenizer plugin (http://www.logarithmic.net/pfh/resynthesizer). Translate comments to English. Output the position of the cuts on a image, for checking purposes. Optionnaly, don't take into account the old cuts to check for bugs in this feature. Add http://adn.diwi.org/debian/ on the home page. Add GPL headers. Be even closer to Gimp and Debian standards. gimp-texturize-3.0+ds/README.md0000664000000000000000000000230114771535253014742 0ustar rootroot# GIMP Plug-In Texturize This plugin takes a small image and creates a texture out of it. The idea is to put several copies (patches) of the small image on the big canvas. The copies aren't complete: we cut the border so that the transition is invisible, and the texture seems natural. # Examples https://lmanul.github.io/gimp-texturize/examples.html # Dependencies If you're using a Debian-based system, install the build dependencies sudo apt build-dep gimp-texturize sudo apt install meson Under a different OS, install - `gimp` (you probably already have that) - `libgimp3.0-dev` (libraries for developping with GIMP) - `gettext` (an internationalization tool). - `meson` (the build tool we use) # Installation To build and install the plugin: ./compile Then (close and) reopen the GIMP. Texturize will be in the Filters->Map menu. This plugin is based on the article "Graphcut Textures: Image and Video Synthesis Using Graph Cuts" by Vivek Kwatra, Arno Schödl, Irfan Essa, Greg Turk and Aaron Bobick, available from www.cc.gatech.edu/cpl/projects/graphcuttextures. Copyright (C) 2004-2025 * Manu Cornet * Jean-Baptiste Rouquier gimp-texturize-3.0+ds/src/0000775000000000000000000000000014771535253014256 5ustar rootrootgimp-texturize-3.0+ds/src/graphcut.cpp0000664000000000000000000003062414771535253016604 0ustar rootroot#include "config.h" #include #include #include #include extern "C" { #include "counting.h" #include "main.h" #include "texturize.h" } #include "graph.h" #define MAX_CAPACITY 16383 // Half of the largest short, (captype is short in graph.h) #define FILLED 1 #define CUT_NORTH 2 #define CUT_WEST 4 #define HAS_CUT_NORTH(r) (r) & CUT_NORTH #define HAS_CUT_WEST(r) (r) & CUT_WEST // ||pixel1 - pixel2||^2 // From experience, squares seem to work better than another type of norm. inline Graph::captype cost (guchar * pixel1, guchar * pixel2, int channels) { int diff, result = 0; for (int c = 0; c < channels; c++){ diff = pixel1[c] - pixel2[c]; result += diff*diff; } return (result/24); // We need to divide at least by 24, or we might return more than // MAX_CAPACITY. } inline Graph::captype gradient (guchar * pixel1, guchar * pixel2, int channels) { int diff, result = 0; for (int c = 0; c < channels; c++){ diff = pixel1[c] - pixel2[c]; result += diff*diff; } return ((Graph::captype) sqrt(result)); } // When we write the four arguments to edge_weight on two lines of code, // we try to always align things (pixel VS image) so that it makes sense. inline Graph::captype edge_weight (int channels, guchar * im1_pix1, guchar * im2_pix1, guchar * im1_pix2, guchar * im2_pix2) { return ((cost(im1_pix1,im2_pix1,channels) + (cost(im1_pix2,im2_pix2,channels))) / (gradient(im1_pix1,im1_pix2,channels) + gradient(im2_pix1,im2_pix2,channels) +1)); } inline void paste_patch_pixel_to_image(int width_i, int height_i, int width_p, int height_p, int x_i, int y_i, int x_p, int y_p, int channels, guchar * image, guchar * patch) { int k; for (k = 0; k < channels; k++) { image[(y_i * width_i + x_i) * channels + k] = patch[(y_p * width_p + x_p) * channels + k]; /* Might become useful again if we start taking old cuts into account again. if (y_i < height_i - 1 && y_p < height_p - 1){ for(k = 0; k < channels; k++) coupe_v_here[((y_i + 1) * width_i + x_i) * channels + k] = patch[((y_p + 1) * width_p + x_p) * channels + k]; } if (x_i < width_i - 1 && x_p < width_p - 1) { for(k = 0; k < channels; k++) coupe_h_here[(y_i * width_i + x_i + 1) * channels + k] = patch[(y_p * width_p + x_p + 1) * channels + k]; } */ } } void cut_graph (int* patch_posn, int width_i, int height_i, int width_p, int height_p, int channels, guchar **filled, guchar *image, guchar * patch, guchar *coupe_h_here, guchar * coupe_h_west, guchar *coupe_v_here, guchar * coupe_v_north, gboolean make_tileable, gboolean invert) { //////////////////////////////////////////////////////////////////////////////// // Variable declaration. gint k, x_p, y_p, x_i, y_i;// nb_sommets, sommet_courant; // Compteurs gint real_x_i, real_y_i; gint x_inf, y_inf, x_sup, y_sup; Graph * graph = new Graph(); // The graph to cut Graph::node_id *node_of_pixel = (void **) calloc (width_p * height_p, sizeof (Graph::node_id)); // Le noeud du graph auquel correspond un pointeur. for (k=0; k| * | | * | | * <--------------|--------- real_x_i--|---------------> * | | * | | * ______________________ */ // We count the number of nodes by visiting the intersection between the // patch and the filled in image. // nb_sommets = 0; // for (real_x_i = x_inf; real_x_i < x_sup; real_x_i++) { // for (real_y_i = y_inf; real_y_i < y_sup; real_y_i++) { // x_i = modulo (real_x_i, width_i); // y_i = modulo (real_y_i, height_i); // r = filled[x_i][y_i]; // if (r) { // nb_sommets++; // We'll uncomment this when we start taking previous cuts into account again. // if (HAS_CUT_NORTH(r)) nb_sommets++; // if (HAS_CUT_WEST(r)) nb_sommets++; // } // } // } // Start by visiting the whole patch to create nodes and create links in // node_of_pixel. for (real_x_i = x_inf; real_x_i < x_sup; real_x_i++) { x_p = real_x_i - patch_posn[0]; x_i = modulo (real_x_i, width_i); for (real_y_i = y_inf; real_y_i < y_sup; real_y_i++) { y_p = real_y_i - patch_posn[1]; y_i = modulo (real_y_i, height_i); // If the pixel in the image isn't filled, do nothing and go to the next pixel. if (filled[x_i][y_i]) { node_of_pixel[x_p * height_p + y_p] = graph->add_node (); if (first_node == NULL) first_node = node_of_pixel[x_p * height_p + y_p]; } } } // Create the edges. /* We link to the source the pixels that are at the same time filled and also on the edges of the patch (and, for a non tileable texture, that are also not on the edge of the image). We link to the sink the pixels that have at least one neighbor that isn't filled yet. ********************************************** Loop summary: For each x of the patch (intersection with the image if !make_tileable). For each y of the patch (same note) If I am already filled Create the edges with my North and West neighbors (if they exist in the patch) (later we'll need to take previous cuts into account) If I am on the edge of the patch (i.e. there's no other pixel in the patch to the North OR South OR East OR West) And in the !make_tileable case, if I am also not on the edge of the image (1) Then link me to the source If one of my neighbors (North, South, East, West) exists (in the patch AND in the image) and hasn't been filled yet Then link me to the sink If I haven't been filled yet Don't do anything. // The test (1) above might cause the source to not be linked to any pixel. // The following line fixes that problem. If !make_tileable, link the top left pixel of the intersection (the first one that was created) to the source. */ for (real_x_i = x_inf; real_x_i < x_sup; real_x_i++) { x_p = real_x_i - patch_posn[0]; x_i = modulo (real_x_i, width_i); for (real_y_i = y_inf; real_y_i < y_sup; real_y_i++) { y_p = real_y_i - patch_posn[1]; y_i = modulo (real_y_i, height_i); // If the pixel in the image hasn't been filled, we do nothing and skip // to the next one. if (!filled[x_i][y_i]) { continue; } else { // Create the nodes and edges. node_sommet_courant = node_of_pixel[x_p * height_p + y_p]; // If the neighbord exists in the patch and if the pixel to the North // is filled in the image, create a link to it. if ((!make_tileable && y_p != 0 && y_i != 0 && filled[x_i][y_i - 1]) || (make_tileable && y_p != 0 && filled[x_i][modulo (y_i - 1, height_i)])) { weight = edge_weight (channels, image + ((y_i * width_i + x_i) * channels), patch + ((y_p * width_p + x_p) * channels), image + (((modulo (y_i - 1, height_i)) * width_i + x_i) * channels), patch + (((y_p - 1) * width_p + x_p) * channels)); graph->add_edge (node_sommet_courant, node_of_pixel[x_p * height_p + y_p - 1], weight, weight); } // If the West neighbor exists in the patch and if the West pixel is // filled in the image, we create a link to it. if ((!make_tileable && x_p != 0 && x_i != 0 && filled[x_i - 1][y_i]) || (make_tileable && x_p != 0 && filled[modulo (x_i - 1, width_i)][y_i])) { weight = edge_weight (channels, image + ((y_i * width_i + x_i) * channels), patch + ((y_p * width_p + x_p) * channels), image + ((y_i * width_i + (modulo (x_i, width_i) - 1)) * channels), patch + ((y_p * width_p + (x_p - 1)) * channels)); graph->add_edge (node_sommet_courant, node_of_pixel[(x_p - 1) * height_p + y_p], weight, weight); } // If I am on the edge of the patch and, if !make_tileable, I am not on // the edge of the image, link me to the source. if ( (make_tileable && (x_p == 0 || y_p == 0 || x_p == width_p - 1 || y_p == height_p - 1)) || (!make_tileable && (x_p == 0 || y_p == 0 || x_p == width_p - 1 || y_p == height_p - 1) && x_i != 0 && y_i != 0 && x_i != width_i - 1 && y_i != height_i - 1)) { graph->add_tweights (node_sommet_courant, MAX_CAPACITY, 0); } // If one of my neighbords exists and isn't filled, link me to the sink. if (((!make_tileable) && ( (y_p != 0 && y_i != 0 && !filled[x_i][y_i - 1]) // North || (y_p != height_p - 1 && y_i != height_i - 1 && !filled[x_i][y_i + 1]) // South || (x_p != width_p - 1 && x_i != width_i - 1 && !filled[x_i + 1][y_i]) // East || (x_p != 0 && x_i != 0 && !filled[x_i - 1][y_i]))) // West || ((make_tileable) && ( (y_p != 0 && !filled[x_i][modulo (y_i - 1, height_i)]) // North || (y_p != height_p - 1 && !filled[x_i][modulo (y_i + 1, height_i)]) // South || (x_p != width_p - 1 && !filled[modulo (x_i + 1, width_i)][y_i]) // East || (x_p != 0 && !filled[modulo (x_i - 1, width_i)][y_i])))) { // West graph->add_tweights (node_sommet_courant, 0, MAX_CAPACITY); } } } } // If !make_tileable, link the top left pixel in patch \cap image to the // source. if (!make_tileable) { graph->add_tweights (first_node, MAX_CAPACITY, 0); } //////////////////////////////////////////////////////////////////////////////// // Compute the cut. graph->maxflow(); //////////////////////////////////////////////////////////////////////////////// // Update the image. for (real_x_i = x_inf; real_x_i < x_sup; real_x_i++) { x_p = real_x_i - patch_posn[0]; x_i = modulo (real_x_i, width_i); for (real_y_i = y_inf; real_y_i < y_sup; real_y_i++) { y_p = real_y_i - patch_posn[1]; y_i = modulo (real_y_i, height_i); r = filled[x_i][y_i]; if (r) { if (graph->what_segment(node_of_pixel[x_p * height_p + y_p]) == Graph::SINK) { paste_patch_pixel_to_image (width_i, height_i, width_p, height_p, x_i, y_i, x_p, y_p, channels, image, patch); //, //coupe_h_here, coupe_v_here); } } else { // (!filled[x_i][y_i]) paste_patch_pixel_to_image (width_i, height_i, width_p, height_p, x_i, y_i, x_p, y_p, channels, image, patch); //, //coupe_h_here, coupe_v_here); filled[x_i][y_i] = FILLED; } } } //////////////////////////////////////////////////////////////////////////////// // Clean up. delete graph; free (node_of_pixel); return; } gimp-texturize-3.0+ds/src/counting.c0000664000000000000000000000170114771535253016247 0ustar rootroot#include "config.h" #include #include #include "main.h" #include "texturize.h" // Counts the number of pixels that are already filled. int count_filled_pixels(guchar **filled, int width_i, int height_i) { int x_i, y_i; int somme = 0; for (x_i = 0; x_i < width_i; x_i++) { for (y_i = 0; y_i < height_i; y_i++) { if (filled[x_i][y_i]) somme++; } } return somme; } // Finds the next pixel that needs to be filled. int* pixel_to_fill(guchar **filled, int width_i, int height_i, int *resultat) { int x_i, y_i; for (y_i = 0; y_i < height_i; y_i++) { for (x_i = 0; x_i < width_i; x_i++) { if (!filled[x_i][y_i]) { resultat[0] = x_i; resultat[1] = y_i; return resultat; } } } return NULL; } // Quick and dirty implementation of x mod m assuming x isn't greater than // 2 m. gint modulo(gint x, gint m) { int v = (x - (m * (x / m))); return (v >= 0)? v : v + m; } gimp-texturize-3.0+ds/src/graph.h0000664000000000000000000001442114771535253015532 0ustar rootroot/* graph.h */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ /* This software library is a modification of the maxflow algorithm described in An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Computer Vision. Yuri Boykov and Vladimir Kolmogorov. In Third International Workshop on Energy Minimization Methods in Computer Vision and Pattern Recognition, September 2001 This algorithm was originally developed at Siemens. The main modification is that two trees are used for finding augmenting paths - one grows from the source and the other from the sink. (The original algorithm used only the former one). Details will be described in my PhD thesis. This implementation uses an adjacency list graph representation. Memory allocation: Nodes: 22 bytes + one field to hold a residual capacity of t-links (by default it is 'short' - 2 bytes) Arcs: 12 bytes + one field to hold a residual capacity (by default it is 'short' - 2 bytes) (Note that arcs are always added in pairs - in forward and reverse directions) Example usage (computes a maxflow on the following graph): SOURCE / \ 1/ \2 / 3 \ node0 -----> node1 | <----- | | 4 | \ / 5\ /6 \ / SINK /////////////////////////////////////////////////// #include #include "graph.h" void main() { Graph::node_id nodes[2]; Graph *g = new Graph(); nodes[0] = g -> add_node(); nodes[1] = g -> add_node(); g -> set_tweights(nodes[0], 1, 5); g -> set_tweights(nodes[1], 2, 6); g -> add_edge(nodes[0], nodes[1], 3, 4); Graph::flowtype flow = g -> maxflow(); printf("Flow = %d\n", flow); printf("Minimum cut:\n"); if (g->what_segment(nodes[0]) == Graph::SOURCE) printf("node0 is in the SOURCE set\n"); else printf("node0 is in the SINK set\n"); if (g->what_segment(nodes[1]) == Graph::SOURCE) printf("node1 is in the SOURCE set\n"); else printf("node1 is in the SINK set\n"); delete g; } ////////////////////////////////////////////////// */ #ifndef __GRAPH_H__ #define __GRAPH_H__ #include "block.h" /* Nodes, arcs and pointers to nodes are added in blocks for memory and time efficiency. Below are numbers of items in blocks */ #define NODE_BLOCK_SIZE 512 #define ARC_BLOCK_SIZE 1024 #define NODEPTR_BLOCK_SIZE 128 class Graph { public: typedef enum { SOURCE = 0, SINK = 1 } termtype; /* terminals */ /* Type of edge weights. Can be changed to char, int, float, double, ... */ typedef short captype; /* Type of total flow */ typedef int flowtype; typedef void * node_id; /* interface functions */ /* Constructor. Optional argument is the pointer to the function which will be called if an error occurs; an error message is passed to this function. If this argument is omitted, exit(1) will be called. */ Graph(void (*err_function)(const char *) = NULL); /* Destructor */ ~Graph(); /* Adds a node to the graph */ node_id add_node(); /* Adds a bidirectional edge between 'from' and 'to' with the weights 'cap' and 'rev_cap' */ void add_edge(node_id from, node_id to, captype cap, captype rev_cap); /* Sets the weights of the edges 'SOURCE->i' and 'i->SINK' Can be called at most once for each node before any call to 'add_tweights'. Weights can be negative */ void set_tweights(node_id i, captype cap_source, captype cap_sink); /* Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights Can be called multiple times for each node. Weights can be negative */ void add_tweights(node_id i, captype cap_source, captype cap_sink); /* After the maxflow is computed, this function returns to which segment the node 'i' belongs (Graph::SOURCE or Graph::SINK) */ termtype what_segment(node_id i); /* Computes the maxflow. Can be called only once. */ flowtype maxflow(); /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ private: /* internal variables and functions */ struct arc_st; /* node structure */ typedef struct node_st { arc_st *first; /* first outcoming arc */ arc_st *parent; /* node's parent */ node_st *next; /* pointer to the next active node (or to itself if it is the last node in the list) */ int TS; /* timestamp showing when DIST was computed */ int DIST; /* distance to the terminal */ short is_sink; /* flag showing whether the node is in the source or in the sink tree */ captype tr_cap; /* if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node otherwise -tr_cap is residual capacity of the arc node->SINK */ } node; /* arc structure */ typedef struct arc_st { node_st *head; /* node the arc points to */ arc_st *next; /* next arc with the same originating node */ arc_st *sister; /* reverse arc */ captype r_cap; /* residual capacity */ } arc; /* 'pointer to node' structure */ typedef struct nodeptr_st { node_st *ptr; nodeptr_st *next; } nodeptr; Block *node_block; Block *arc_block; DBlock *nodeptr_block; void (*error_function)(const char *);/* this function is called if a error occurs, with a corresponding error message (or exit(1) is called if it's NULL) */ flowtype flow; /* total flow */ /***********************************************************************/ node *queue_first[2], *queue_last[2]; /* list of active nodes */ nodeptr *orphan_first, *orphan_last; /* list of pointers to orphans */ int TIME; /* monotonically increasing global counter */ /***********************************************************************/ /* functions for processing active list */ void set_active(node *i); node *next_active(); void maxflow_init(); void augment(arc *middle_arc); void process_source_orphan(node *i); void process_sink_orphan(node *i); }; #endif gimp-texturize-3.0+ds/src/render.c0000664000000000000000000002233214771535253015703 0ustar rootroot#include "config.h" #include #include #include #include #include #include #include "plugin-intl.h" #include "main.h" #include "texturize.h" // Allocates enough memory for a 2-dimensional table of guchars and // initializes all elements to zero. guchar** init_guchar_tab_2d (gint x, gint y) { guchar** tab; tab = (guchar**) malloc (x * sizeof (guchar*)); for (gint i = 0; i < x; i++) { tab[i] = (guchar*) malloc (y * sizeof (guchar)); } for (gint i = 0; i < x; i++) { for (gint j = 0; j < y; j++) { tab[i][j] = 0; } } return tab; } void debug_print_guchar_buffer(gpointer buffer, int width, int height) { for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { printf("%i ", ((guchar*)buffer)[row * width + col]); } } } void the_big_loop( guchar* image, guchar* patch, int width_i, int height_i, int width_p, int height_p, GeglRectangle rect_image, GeglRectangle rect_patch, guchar** filled, gboolean tileable, guchar* coupe_h_here, guchar* coupe_h_west, guchar* coupe_v_here, guchar* coupe_v_north, int x_off_min, int y_off_min, int x_off_max, int y_off_max, int channels) { float progress = 0; // Progress bar displayed during computation. int cur_posn[2]; // The position of the pixel to be filled. int patch_posn[2]; // The current position : (0,0) cur_posn[0] = 0; cur_posn[1] = 0; int count = count_filled_pixels (filled, rect_image.width, rect_image.height); int count_max = rect_image.width * rect_image.height; while (count < count_max) { // Update the current position: it's the next pixel to fill. if (pixel_to_fill (filled, width_i, height_i, cur_posn) == NULL) { g_message (_("There was a problem when filling the new image.")); exit(-1); }; offset_optimal(patch_posn, image, patch, rect_patch.width, rect_patch.height, rect_image.width, rect_image.height, cur_posn[0] - x_off_min, cur_posn[1] - y_off_min, cur_posn[0] - x_off_max, cur_posn[1] - y_off_max, channels, filled, tileable); cut_graph(patch_posn, rect_image.width, rect_image.height, rect_patch.width, rect_patch.height, channels, filled, image, patch, coupe_h_here, coupe_h_west, coupe_v_here, coupe_v_north, tileable, FALSE); // Display progress to the user. count = count_filled_pixels (filled, rect_image.width, rect_image.height); progress = ((float) count) / ((float) count_max); progress = (progress > 1.0f)? 1.0f : ((progress < 0.0f)? 0.0f : progress); gimp_progress_update(progress); } } GimpImage* render(GimpDrawable *drawable, gint width_i, gint height_i, gint overlap, gboolean tileable) { gint width_p = gimp_drawable_get_width(drawable); gint height_p = gimp_drawable_get_height(drawable); GimpImageBaseType image_type = GIMP_RGB; GimpImageType drawable_type = GIMP_RGB_IMAGE; gimp_progress_init(_("Texturizing image...")); gimp_progress_update(0); GeglRectangle rect_image = { 0, 0, width_i, height_i }; GeglRectangle rect_patch = { 0, 0, width_p, height_p }; const Babl* format; gint channels = gimp_drawable_get_bpp(drawable); // 3 for RGB, 1 for grayscale if (width_i == width_p && height_i == height_p) { g_message(_("New image size and original image size are the same.")); return NULL; } else if (width_i <= width_p || height_i <= height_p) { g_message(_("New image size is too small.")); return NULL; } // Figure out the type of the new image according to the original image switch (gimp_drawable_type(drawable)) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: image_type = GIMP_RGB; drawable_type = GIMP_RGB_IMAGE; format = babl_format("RGB u8"); break; case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE: image_type = GIMP_GRAY; drawable_type = GIMP_GRAY_IMAGE; format = babl_format("Y u8"); break; case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: g_message (_("Sorry, the Texturize plugin only supports RGB and " "grayscale images. Please convert your image to RGB first.")); return NULL; } if (gimp_drawable_has_alpha(drawable)) { g_message (_("Sorry, the Texturize plugin doesn't support images with " "an alpha (i.e. transparency) channel yet. Please convert your image " "first.")); return NULL; } //////////////////////////// /////////////////////////// //////////////////////////// Overlap /////////////////////////// //////////////////////////// /////////////////////////// // WARNING: our conventions here aren't necessarily intuitive. Given the way // that we detect the next pixel to fill, offsets are always negative values // (we paste the patch a little above and to the left). However, {x,y}_off_* // are positive values, and x_off_max < x_off_min. // Heuristic values, to refine when we get more experience. int x_off_min, y_off_min; // Max and min values of the offset, i.e. the vector int x_off_max, y_off_max; // subtracted from cur_posn to get patch_posn. x_off_min = MIN(overlap, width_p - 1); y_off_min = MIN(overlap, height_p - 1); x_off_max = CLAMP(20, x_off_min/3, width_p -1); // We know that x_off_min/5 < width_p -1 y_off_max = CLAMP(20, y_off_min/3, height_p - 1); // We know that y_off_min/5 < height_p-1 // Keeps track of which pixels have been filled. guchar** filled = init_guchar_tab_2d(rect_image.width, rect_image.height); // 0 iff the pixel isn't filled // 1 if the pixel is filled and without any cuts // 3 if there is an upwards cut // 5 if there is a cut towards the left // 7 if both // I.e. the weak bit is "filled?", the previous bit is "upwards_cut?". // These are for storing the pixels we have discarded along the cuts. guchar* coupe_h_here; // pixel (x,y) of the patch to which belongs the pixel // on the left (we will thus not use the first column of this array). guchar* coupe_h_west; // Pixel to the left of the patch to which belongs the // pixel (x,y) (same for the first column). guchar* coupe_v_here; // pixel (x,y) of the patch to which belongs the pixel // to the top (we will thus not use the first row of this array). guchar* coupe_v_north; // Pixel to the top of the patch to which belongs the // pixel (x,y) (same for the first row). // The initial image data, renamed to "patch". guchar* patch = g_new(guchar, rect_patch.width * rect_patch.height * channels); GeglBuffer* buffer_in = gimp_drawable_get_buffer(drawable); // patch = destination gegl_buffer_get(buffer_in, &rect_patch, 1.0, format, patch, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); g_object_unref(buffer_in); // Create a new image with only one layer. GimpImage* new_image = gimp_image_new(width_i, height_i, image_type); GimpLayer* new_layer = gimp_layer_new(new_image, _("Texture"), rect_image.width, rect_image.height, drawable_type, 100, GIMP_LAYER_MODE_NORMAL); GeglBuffer* dest_buffer = gimp_drawable_get_buffer(GIMP_DRAWABLE(new_layer)); // The destination image. guchar* image = g_new(guchar, rect_image.width * rect_image.height * channels); // Paste a first patch at position (0,0) of the out image. // TODO: is there a shortcut for this low-level operation? for (int row = 0; row < height_p; row++) { // Copy row by row memcpy(image + row * width_i * channels, patch + row * width_p * channels, channels * width_p); } // And declare we have already filled in the corresponding pixels. for (int x_i = 0; x_i < width_p; x_i++) { for (int y_i = 0; y_i < height_p; y_i++) { filled[x_i][y_i] = 1; } } coupe_h_here = g_new(guchar, rect_image.width * rect_image.height * channels); coupe_h_west = g_new(guchar, rect_image.width * rect_image.height * channels); coupe_v_here = g_new(guchar, rect_image.width * rect_image.height * channels); coupe_v_north = g_new(guchar, rect_image.width * rect_image.height * channels); // For security, initialize everything to 0. for (int k = 0; k < width_i * height_i * channels; k++) { coupe_h_here[k] = coupe_h_west[k] = coupe_v_here[k] = coupe_v_north[k] = 0; } the_big_loop( image, patch, width_i, height_i, width_p, height_p, rect_image, rect_patch, filled, tileable, coupe_h_here, coupe_h_west, coupe_v_here, coupe_v_north, x_off_min, y_off_min, x_off_max, y_off_max, channels ); gimp_progress_update(1.0); gegl_buffer_set(dest_buffer, &rect_image, 0, format, image, GEGL_AUTO_ROWSTRIDE); // Necessary for the drawable to update g_object_unref(dest_buffer); gimp_drawable_update(GIMP_DRAWABLE(new_layer), rect_patch.x, rect_patch.y, rect_patch.width, rect_patch.height); gimp_image_insert_layer(new_image, new_layer, NULL /* parent */, 0); gimp_displays_flush(); g_free(coupe_h_here); g_free(coupe_h_west); g_free(coupe_v_here); g_free(coupe_v_north); g_free(patch); g_free(filled); return new_image; } gimp-texturize-3.0+ds/src/counting.h0000664000000000000000000000006014771535253016251 0ustar rootroot#include gint modulo(gint x, gint m); gimp-texturize-3.0+ds/src/graph.cpp0000664000000000000000000000266214771535253016071 0ustar rootroot/* graph.cpp */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ #include #include "graph.h" Graph::Graph(void (*err_function)(const char *)) { error_function = err_function; node_block = new Block(NODE_BLOCK_SIZE, error_function); arc_block = new Block(NODE_BLOCK_SIZE, error_function); flow = 0; } Graph::~Graph() { delete node_block; delete arc_block; } Graph::node_id Graph::add_node() { node *i = node_block -> New(); i -> first = NULL; i -> tr_cap = 0; return (node_id) i; } void Graph::add_edge(node_id from, node_id to, captype cap, captype rev_cap) { arc *a, *a_rev; a = arc_block -> New(2); a_rev = a + 1; a -> sister = a_rev; a_rev -> sister = a; a -> next = ((node*)from) -> first; ((node*)from) -> first = a; a_rev -> next = ((node*)to) -> first; ((node*)to) -> first = a_rev; a -> head = (node*)to; a_rev -> head = (node*)from; a -> r_cap = cap; a_rev -> r_cap = rev_cap; } void Graph::set_tweights(node_id i, captype cap_source, captype cap_sink) { flow += (cap_source < cap_sink) ? cap_source : cap_sink; ((node*)i) -> tr_cap = cap_source - cap_sink; } void Graph::add_tweights(node_id i, captype cap_source, captype cap_sink) { captype delta = ((node*)i) -> tr_cap; if (delta > 0) cap_source += delta; else cap_sink -= delta; flow += (cap_source < cap_sink) ? cap_source : cap_sink; ((node*)i) -> tr_cap = cap_source - cap_sink; } gimp-texturize-3.0+ds/src/forward_star/0000775000000000000000000000000014771535253016753 5ustar rootrootgimp-texturize-3.0+ds/src/forward_star/graph.h0000664000000000000000000002203014771535253020222 0ustar rootroot/* graph.h */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ /* This software library is a modification of the maxflow algorithm described in An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Computer Vision. Yuri Boykov and Vladimir Kolmogorov. In Third International Workshop on Energy Minimization Methods in Computer Vision and Pattern Recognition, September 2001 This algorithm was originally developed at Siemens. The main modification is that two trees are used for finding augmenting paths - one grows from the source and the other from the sink. (The original algorithm used only the former one). Details will be described in my PhD thesis. This implementation uses a forward star graph representation. Memory allocation: Nodes: 26 bytes + one field to hold a residual capacity of t-links (by default it is 'short' - 2 bytes) Arcs: 4 bytes + one field to hold a residual capacity (by default it is 'short' - 2 bytes), assuming that the maximum number of arcs per node (except the source and the sink) is much less than ARC_BLOCK_SIZE (1024 by default). (Note that arcs are always added in pairs - in forward and reverse directions) Example usage (computes a maxflow on the following graph): SOURCE / \ 1/ \2 / 3 \ node0 -----> node1 | <----- | | 4 | \ / 5\ /6 \ / SINK /////////////////////////////////////////////////// #include #include "graph.h" void main() { Graph::node_id nodes[2]; Graph *g = new Graph(); nodes[0] = g -> add_node(); nodes[1] = g -> add_node(); g -> set_tweights(nodes[0], 1, 5); g -> set_tweights(nodes[1], 2, 6); g -> add_edge(nodes[0], nodes[1], 3, 4); Graph::flowtype flow = g -> maxflow(); printf("Flow = %d\n", flow); printf("Minimum cut:\n"); if (g->what_segment(nodes[0]) == Graph::SOURCE) printf("node0 is in the SOURCE set\n"); else printf("node0 is in the SINK set\n"); if (g->what_segment(nodes[1]) == Graph::SOURCE) printf("node1 is in the SOURCE set\n"); else printf("node1 is in the SINK set\n"); delete g; } /////////////////////////////////////////////////// */ #ifndef __GRAPH_H__ #define __GRAPH_H__ #include "block.h" /* Nodes, arcs and pointers to nodes are added in blocks for memory and time efficiency. Below are numbers of items in blocks */ #define NODE_BLOCK_SIZE 512 #define ARC_BLOCK_SIZE 1024 #define NODEPTR_BLOCK_SIZE 128 class Graph { public: typedef enum { SOURCE = 0, SINK = 1 } termtype; /* terminals */ /* Type of edge weights. Can be changed to char, int, float, double, ... */ typedef short captype; /* Type of total flow */ typedef int flowtype; typedef void * node_id; /* interface functions */ /* Constructor. Optional argument is the pointer to the function which will be called if an error occurs; an error message is passed to this function. If this argument is omitted, exit(1) will be called. */ Graph(void (*err_function)(char *) = NULL); /* Destructor */ ~Graph(); /* Adds a node to the graph */ node_id add_node(); /* Adds a bidirectional edge between 'from' and 'to' with the weights 'cap' and 'rev_cap' */ void add_edge(node_id from, node_id to, captype cap, captype rev_cap); /* Sets the weights of the edges 'SOURCE->i' and 'i->SINK' Can be called at most once for each node before any call to 'add_tweights'. Weights can be negative */ void set_tweights(node_id i, captype cap_source, captype cap_sink); /* Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights Can be called multiple times for each node. Weights can be negative */ void add_tweights(node_id i, captype cap_source, captype cap_sink); /* After the maxflow is computed, this function returns to which segment the node 'i' belongs (Graph::SOURCE or Graph::SINK) */ termtype what_segment(node_id i); /* Computes the maxflow. Can be called only once. */ flowtype maxflow(); /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ private: /* internal variables and functions */ struct arc_forward_st; struct arc_reverse_st; #define IS_ODD(a) ((int)(a) & 1) #define MAKE_ODD(a) ((arc_forward *) ((int)(a) | 1)) #define MAKE_EVEN(a) ((arc_forward *) ((int)(a) & (~1))) #define MAKE_ODD_REV(a) ((arc_reverse *) ((int)(a) | 1)) #define MAKE_EVEN_REV(a) ((arc_reverse *) ((int)(a) & (~1))) /* node structure */ typedef struct node_st { /* Usually i->first_out is the first outgoing arc, and (i+1)->first_out-1 is the last outgoing arc. However, it is not always possible, since arcs are allocated in blocks, so arcs corresponding to two consecutive nodes may be in different blocks. If outgoing arcs for i are last in the arc block, then a different mechanism is used. i->first_out is odd in this case; the first outgoing arc is (a+1), and the last outgoing arc is ((arc_forward *)(a->shift))-1, where a = (arc_forward *) (((char *)(i->first_out)) + 1); Similar mechanism is used for incoming arcs. */ arc_forward_st *first_out; /* first outcoming arc */ arc_reverse_st *first_in; /* first incoming arc */ arc_forward_st *parent; /* describes node's parent if IS_ODD(parent) then MAKE_EVEN(parent) points to 'arc_reverse', otherwise parent points to 'arc_forward' */ node_st *next; /* pointer to the next active node (or to itself if it is the last node in the list) */ int TS; /* timestamp showing when DIST was computed */ int DIST; /* distance to the terminal */ short is_sink; /* flag showing whether the node is in the source or in the sink tree */ captype tr_cap; /* if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node otherwise -tr_cap is residual capacity of the arc node->SINK */ } node; /* arc structures */ #define NEIGHBOR_NODE(i, shift) ((node *) ((char *)(i) + (shift))) #define NEIGHBOR_NODE_REV(i, shift) ((node *) ((char *)(i) - (shift))) typedef struct arc_forward_st { int shift; /* node_to = NEIGHBOR_NODE(node_from, shift) */ captype r_cap; /* residual capacity */ captype r_rev_cap; /* residual capacity of the reverse arc*/ } arc_forward; typedef struct arc_reverse_st { arc_forward *sister; /* reverse arc */ } arc_reverse; /* 'pointer to node' structure */ typedef struct nodeptr_st { node_st *ptr; nodeptr_st *next; } nodeptr; typedef struct node_block_st { node *current; struct node_block_st *next; node nodes[NODE_BLOCK_SIZE]; } node_block; #define last_node LAST_NODE.LAST_NODE typedef struct arc_for_block_st { char *start; /* the actual start address of this block. May be different from 'this' since 'this' must be at an even address. */ arc_forward *current; struct arc_for_block_st *next; arc_forward arcs_for[ARC_BLOCK_SIZE]; /* all arcs must be at even addresses */ union { arc_forward dummy; node *LAST_NODE; /* used in graph consruction */ } LAST_NODE; } arc_for_block; typedef struct arc_rev_block_st { char *start; /* the actual start address of this block. May be different from 'this' since 'this' must be at an even address. */ arc_reverse *current; struct arc_rev_block_st *next; arc_reverse arcs_rev[ARC_BLOCK_SIZE]; /* all arcs must be at even addresses */ union { arc_reverse dummy; node *LAST_NODE; /* used in graph consruction */ } LAST_NODE; } arc_rev_block; node_block *node_block_first; arc_for_block *arc_for_block_first; arc_rev_block *arc_rev_block_first; DBlock *nodeptr_block; void (*error_function)(char *); /* this function is called if a error occurs, with a corresponding error message (or exit(1) is called if it's NULL) */ flowtype flow; /* total flow */ /***********************************************************************/ node *queue_first[2], *queue_last[2]; /* list of active nodes */ nodeptr *orphan_first, *orphan_last; /* list of pointers to orphans */ int TIME; /* monotonically increasing global counter */ /***********************************************************************/ /* functions for processing active list */ void set_active(node *i); node *next_active(); void prepare_graph(); void maxflow_init(); void augment(node *s_start, node *t_start, captype *cap_middle, captype *rev_cap_middle); void process_source_orphan(node *i); void process_sink_orphan(node *i); }; #endif gimp-texturize-3.0+ds/src/forward_star/graph.cpp0000664000000000000000000002340314771535253020562 0ustar rootroot/* graph.cpp */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ #include #include "graph.h" Graph::Graph(void (*err_function)(char *)) { error_function = err_function; node_block_first = NULL; arc_for_block_first = NULL; arc_rev_block_first = NULL; flow = 0; } Graph::~Graph() { while (node_block_first) { node_block *next = node_block_first -> next; delete node_block_first; node_block_first = next; } while (arc_for_block_first) { arc_for_block *next = arc_for_block_first -> next; delete arc_for_block_first -> start; arc_for_block_first = next; } while (arc_rev_block_first) { arc_rev_block *next = arc_rev_block_first -> next; delete arc_rev_block_first -> start; arc_rev_block_first = next; } } Graph::node_id Graph::add_node() { node *i; if (!node_block_first || node_block_first->current+1 > &node_block_first->nodes[NODE_BLOCK_SIZE-1]) { node_block *next = node_block_first; node_block_first = (node_block *) new node_block; if (!node_block_first) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } node_block_first -> current = & ( node_block_first -> nodes[0] ); node_block_first -> next = next; } i = node_block_first -> current ++; i -> first_out = (arc_forward *) 0; i -> first_in = (arc_reverse *) 0; i -> tr_cap = 0; return (node_id) i; } void Graph::add_edge(node_id from, node_id to, captype cap, captype rev_cap) { arc_forward *a_for; arc_reverse *a_rev; if (!arc_for_block_first || arc_for_block_first->current+1 > &arc_for_block_first->arcs_for[ARC_BLOCK_SIZE]) { arc_for_block *next = arc_for_block_first; char *ptr = new char[sizeof(arc_for_block)+1]; if (!ptr) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } if ((int)ptr & 1) arc_for_block_first = (arc_for_block *) (ptr + 1); else arc_for_block_first = (arc_for_block *) ptr; arc_for_block_first -> start = ptr; arc_for_block_first -> current = & ( arc_for_block_first -> arcs_for[0] ); arc_for_block_first -> next = next; } if (!arc_rev_block_first || arc_rev_block_first->current+1 > &arc_rev_block_first->arcs_rev[ARC_BLOCK_SIZE]) { arc_rev_block *next = arc_rev_block_first; char *ptr = new char[sizeof(arc_rev_block)+1]; if (!ptr) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } if ((int)ptr & 1) arc_rev_block_first = (arc_rev_block *) (ptr + 1); else arc_rev_block_first = (arc_rev_block *) ptr; arc_rev_block_first -> start = ptr; arc_rev_block_first -> current = & ( arc_rev_block_first -> arcs_rev[0] ); arc_rev_block_first -> next = next; } a_for = arc_for_block_first -> current ++; a_rev = arc_rev_block_first -> current ++; a_rev -> sister = (arc_forward *) from; a_for -> shift = (int) to; a_for -> r_cap = cap; a_for -> r_rev_cap = rev_cap; ((node *)from) -> first_out = (arc_forward *) ((int)(((node *)from) -> first_out) + 1); ((node *)to) -> first_in = (arc_reverse *) ((int)(((node *)to) -> first_in) + 1); } void Graph::set_tweights(node_id i, captype cap_source, captype cap_sink) { flow += (cap_source < cap_sink) ? cap_source : cap_sink; ((node*)i) -> tr_cap = cap_source - cap_sink; } void Graph::add_tweights(node_id i, captype cap_source, captype cap_sink) { register captype delta = ((node*)i) -> tr_cap; if (delta > 0) cap_source += delta; else cap_sink -= delta; flow += (cap_source < cap_sink) ? cap_source : cap_sink; ((node*)i) -> tr_cap = cap_source - cap_sink; } /* Converts arcs added by 'add_edge()' calls to a forward star graph representation. Linear time algorithm. No or little additional memory is allocated during this process (it may be necessary to allocate additional arc blocks, since arcs corresponding to the same node must be contiguous, i.e. be in one arc block.) */ void Graph::prepare_graph() { node *i; arc_for_block *ab_for, *ab_for_first; arc_rev_block *ab_rev, *ab_rev_first, *ab_rev_scan; arc_forward *a_for; arc_reverse *a_rev, *a_rev_scan, a_rev_tmp; node_block *nb; bool for_flag = false, rev_flag = false; int k; if (!arc_rev_block_first) { node_id from = add_node(), to = add_node(); add_edge(from, to, 1, 0); } /* FIRST STAGE */ a_rev_tmp.sister = NULL; for (a_rev=arc_rev_block_first->current; a_rev<&arc_rev_block_first->arcs_rev[ARC_BLOCK_SIZE]; a_rev++) { a_rev -> sister = NULL; } ab_for = ab_for_first = arc_for_block_first; ab_rev = ab_rev_first = ab_rev_scan = arc_rev_block_first; a_for = &ab_for->arcs_for[0]; a_rev = a_rev_scan = &ab_rev->arcs_rev[0]; for (nb=node_block_first; nb; nb=nb->next) { for (i=&nb->nodes[0]; icurrent; i++) { /* outgoing arcs */ k = (int) i -> first_out; if (a_for + k > &ab_for->arcs_for[ARC_BLOCK_SIZE]) { if (k > ARC_BLOCK_SIZE) { if (error_function) (*error_function)("# of arcs per node exceeds block size!"); exit(1); } if (for_flag) ab_for = NULL; else { ab_for = ab_for -> next; ab_rev_scan = ab_rev_scan -> next; } if (ab_for == NULL) { arc_for_block *next = arc_for_block_first; char *ptr = new char[sizeof(arc_for_block)+1]; if (!ptr) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } if ((int)ptr & 1) arc_for_block_first = (arc_for_block *) (ptr + 1); else arc_for_block_first = (arc_for_block *) ptr; arc_for_block_first -> start = ptr; arc_for_block_first -> current = & ( arc_for_block_first -> arcs_for[0] ); arc_for_block_first -> next = next; ab_for = arc_for_block_first; for_flag = true; } else a_rev_scan = &ab_rev_scan->arcs_rev[0]; a_for = &ab_for->arcs_for[0]; } if (ab_rev_scan) { a_rev_scan += k; i -> parent = (arc_forward *) a_rev_scan; } else i -> parent = (arc_forward *) &a_rev_tmp; a_for += k; i -> first_out = a_for; ab_for -> last_node = i; /* incoming arcs */ k = (int) i -> first_in; if (a_rev + k > &ab_rev->arcs_rev[ARC_BLOCK_SIZE]) { if (k > ARC_BLOCK_SIZE) { if (error_function) (*error_function)("# of arcs per node exceeds block size!"); exit(1); } if (rev_flag) ab_rev = NULL; else ab_rev = ab_rev -> next; if (ab_rev == NULL) { arc_rev_block *next = arc_rev_block_first; char *ptr = new char[sizeof(arc_rev_block)+1]; if (!ptr) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } if ((int)ptr & 1) arc_rev_block_first = (arc_rev_block *) (ptr + 1); else arc_rev_block_first = (arc_rev_block *) ptr; arc_rev_block_first -> start = ptr; arc_rev_block_first -> current = & ( arc_rev_block_first -> arcs_rev[0] ); arc_rev_block_first -> next = next; ab_rev = arc_rev_block_first; rev_flag = true; } a_rev = &ab_rev->arcs_rev[0]; } a_rev += k; i -> first_in = a_rev; ab_rev -> last_node = i; } /* i is the last node in block */ i -> first_out = a_for; i -> first_in = a_rev; } /* SECOND STAGE */ for (ab_for=arc_for_block_first; ab_for; ab_for=ab_for->next) { ab_for -> current = ab_for -> last_node -> first_out; } for ( ab_for=ab_for_first, ab_rev=ab_rev_first; ab_for; ab_for=ab_for->next, ab_rev=ab_rev->next ) for ( a_for=&ab_for->arcs_for[0], a_rev=&ab_rev->arcs_rev[0]; a_for<&ab_for->arcs_for[ARC_BLOCK_SIZE]; a_for++, a_rev++ ) { arc_forward *af; arc_reverse *ar; node *from; int shift = 0, shift_new; captype r_cap, r_rev_cap, r_cap_new, r_rev_cap_new; if (!(from=(node *)(a_rev->sister))) continue; af = a_for; ar = a_rev; do { ar -> sister = NULL; shift_new = ((char *)(af->shift)) - (char *)from; r_cap_new = af -> r_cap; r_rev_cap_new = af -> r_rev_cap; if (shift) { af -> shift = shift; af -> r_cap = r_cap; af -> r_rev_cap = r_rev_cap; } shift = shift_new; r_cap = r_cap_new; r_rev_cap = r_rev_cap_new; af = -- from -> first_out; if ((arc_reverse *)(from->parent) != &a_rev_tmp) { from -> parent = (arc_forward *)(((arc_reverse *)(from -> parent)) - 1); ar = (arc_reverse *)(from -> parent); } } while (from=(node *)(ar->sister)); af -> shift = shift; af -> r_cap = r_cap; af -> r_rev_cap = r_rev_cap; } for (ab_for=arc_for_block_first; ab_for; ab_for=ab_for->next) { i = ab_for -> last_node; a_for = i -> first_out; ab_for -> current -> shift = a_for -> shift; ab_for -> current -> r_cap = a_for -> r_cap; ab_for -> current -> r_rev_cap = a_for -> r_rev_cap; a_for -> shift = (int) (ab_for -> current + 1); i -> first_out = (arc_forward *) (((char *)a_for) - 1); } /* THIRD STAGE */ for (ab_rev=arc_rev_block_first; ab_rev; ab_rev=ab_rev->next) { ab_rev -> current = ab_rev -> last_node -> first_in; } for (nb=node_block_first; nb; nb=nb->next) for (i=&nb->nodes[0]; icurrent; i++) { arc_forward *a_for_first, *a_for_last; a_for_first = i -> first_out; if (IS_ODD(a_for_first)) { a_for_first = (arc_forward *) (((char *)a_for_first) + 1); a_for_last = (arc_forward *) ((a_for_first ++) -> shift); } else a_for_last = (i + 1) -> first_out; for (a_for=a_for_first; a_for shift); a_rev = -- to -> first_in; a_rev -> sister = a_for; } } for (ab_rev=arc_rev_block_first; ab_rev; ab_rev=ab_rev->next) { i = ab_rev -> last_node; a_rev = i -> first_in; ab_rev -> current -> sister = a_rev -> sister; a_rev -> sister = (arc_forward *) (ab_rev -> current + 1); i -> first_in = (arc_reverse *) (((char *)a_rev) - 1); } } gimp-texturize-3.0+ds/src/forward_star/block.h0000664000000000000000000001636014771535253020224 0ustar rootroot/* block.h */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ /* Template classes Block and DBlock Implement adding and deleting items of the same type in blocks. If there there are many items then using Block or DBlock is more efficient than using 'new' and 'delete' both in terms of memory and time since (1) On some systems there is some minimum amount of memory that 'new' can allocate (e.g., 64), so if items are small that a lot of memory is wasted. (2) 'new' and 'delete' are designed for items of varying size. If all items has the same size, then an algorithm for adding and deleting can be made more efficient. (3) All Block and DBlock functions are inline, so there are no extra function calls. Differences between Block and DBlock: (1) DBlock allows both adding and deleting items, whereas Block allows only adding items. (2) Block has an additional operation of scanning items added so far (in the order in which they were added). (3) Block allows to allocate several consecutive items at a time, whereas DBlock can add only a single item. Note that no constructors or destructors are called for items. Example usage for items of type 'MyType': /////////////////////////////////////////////////// #include "block.h" #define BLOCK_SIZE 1024 typedef struct { int a, b; } MyType; MyType *ptr, *array[10000]; ... Block *block = new Block(BLOCK_SIZE); // adding items for (int i=0; i New(); ptr -> a = ptr -> b = rand(); } // reading items for (ptr=block->ScanFirst(); ptr; ptr=block->ScanNext()) { printf("%d %d\n", ptr->a, ptr->b); } delete block; ... DBlock *dblock = new DBlock(BLOCK_SIZE); // adding items for (int i=0; i New(); } // deleting items for (int i=0; i Delete(array[i]); } // adding items for (int i=0; i New(); } delete dblock; /////////////////////////////////////////////////// Note that DBlock deletes items by marking them as empty (i.e., by adding them to the list of free items), so that this memory could be used for subsequently added items. Thus, at each moment the memory allocated is determined by the maximum number of items allocated simultaneously at earlier moments. All memory is deallocated only when the destructor is called. */ #ifndef __BLOCK_H__ #define __BLOCK_H__ #include /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ template class Block { public: /* Constructor. Arguments are the block size and (optionally) the pointer to the function which will be called if allocation failed; the message passed to this function is "Not enough memory!" */ Block(int size, void (*err_function)(char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } /* Destructor. Deallocates all items added so far */ ~Block() { while (first) { block *next = first -> next; delete first; first = next; } } /* Allocates 'num' consecutive items; returns pointer to the first item. 'num' cannot be greater than the block size since items must fit in one block */ Type *New(int num = 1) { Type *t; if (!last || last->current + num > last->last) { if (last && last->next) last = last -> next; else { block *next = (block *) new char [sizeof(block) + (block_size-1)*sizeof(Type)]; if (!next) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } if (last) last -> next = next; else first = next; last = next; last -> current = & ( last -> data[0] ); last -> last = last -> current + block_size; last -> next = NULL; } } t = last -> current; last -> current += num; return t; } /* Returns the first item (or NULL, if no items were added) */ Type *ScanFirst() { scan_current_block = first; if (!scan_current_block) return NULL; scan_current_data = & ( scan_current_block -> data[0] ); return scan_current_data ++; } /* Returns the next item (or NULL, if all items have been read) Can be called only if previous ScanFirst() or ScanNext() call returned not NULL. */ Type *ScanNext() { if (scan_current_data >= scan_current_block -> current) { scan_current_block = scan_current_block -> next; if (!scan_current_block) return NULL; scan_current_data = & ( scan_current_block -> data[0] ); } return scan_current_data ++; } /* Marks all elements as empty */ void Reset() { block *b; if (!first) return; for (b=first; ; b=b->next) { b -> current = & ( b -> data[0] ); if (b == last) break; } last = first; } /***********************************************************************/ private: typedef struct block_st { Type *current, *last; struct block_st *next; Type data[1]; } block; int block_size; block *first; block *last; block *scan_current_block; Type *scan_current_data; void (*error_function)(char *); }; /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ template class DBlock { public: /* Constructor. Arguments are the block size and (optionally) the pointer to the function which will be called if allocation failed; the message passed to this function is "Not enough memory!" */ DBlock(int size, void (*err_function)(char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } /* Destructor. Deallocates all items added so far */ ~DBlock() { while (first) { block *next = first -> next; delete first; first = next; } } /* Allocates one item */ Type *New() { block_item *item; if (!first_free) { block *next = first; first = (block *) new char [sizeof(block) + (block_size-1)*sizeof(block_item)]; if (!first) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } first_free = & (first -> data[0] ); for (item=first_free; item next_free = item + 1; item -> next_free = NULL; first -> next = next; } item = first_free; first_free = item -> next_free; return (Type *) item; } /* Deletes an item allocated previously */ void Delete(Type *t) { ((block_item *) t) -> next_free = first_free; first_free = (block_item *) t; } /***********************************************************************/ private: typedef union block_item_st { Type t; block_item_st *next_free; } block_item; typedef struct block_st { struct block_st *next; block_item data[1]; } block; int block_size; block *first; block_item *first_free; void (*error_function)(char *); }; #endif gimp-texturize-3.0+ds/src/forward_star/maxflow.cpp0000664000000000000000000004710114771535253021137 0ustar rootroot/* maxflow.cpp */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ #include #include "graph.h" /* special constants for node->parent */ #define TERMINAL ( (arc_forward *) 1 ) /* to terminal */ #define ORPHAN ( (arc_forward *) 2 ) /* orphan */ #define INFINITE_D 1000000000 /* infinite distance to the terminal */ /***********************************************************************/ /* Functions for processing active list. i->next points to the next node in the list (or to i, if i is the last node in the list). If i->next is NULL iff i is not in the list. There are two queues. Active nodes are added to the end of the second queue and read from the front of the first queue. If the first queue is empty, it is replaced by the second queue (and the second queue becomes empty). */ inline void Graph::set_active(node *i) { if (!i->next) { /* it's not in the list yet */ if (queue_last[1]) queue_last[1] -> next = i; else queue_first[1] = i; queue_last[1] = i; i -> next = i; } } /* Returns the next active node. If it is connected to the sink, it stays in the list, otherwise it is removed from the list */ inline Graph::node * Graph::next_active() { node *i; while ( 1 ) { if (!(i=queue_first[0])) { queue_first[0] = i = queue_first[1]; queue_last[0] = queue_last[1]; queue_first[1] = NULL; queue_last[1] = NULL; if (!i) return NULL; } /* remove it from the active list */ if (i->next == i) queue_first[0] = queue_last[0] = NULL; else queue_first[0] = i -> next; i -> next = NULL; /* a node in the list is active iff it has a parent */ if (i->parent) return i; } } /***********************************************************************/ void Graph::maxflow_init() { node *i; node_block *nb; queue_first[0] = queue_last[0] = NULL; queue_first[1] = queue_last[1] = NULL; orphan_first = NULL; for (nb=node_block_first; nb; nb=nb->next) for (i=&nb->nodes[0]; icurrent; i++) { i -> next = NULL; i -> TS = 0; if (i->tr_cap > 0) { /* i is connected to the source */ i -> is_sink = 0; i -> parent = TERMINAL; set_active(i); i -> TS = 0; i -> DIST = 1; } else if (i->tr_cap < 0) { /* i is connected to the sink */ i -> is_sink = 1; i -> parent = TERMINAL; set_active(i); i -> TS = 0; i -> DIST = 1; } else { i -> parent = NULL; } } TIME = 0; } /***********************************************************************/ void Graph::augment(node *s_start, node *t_start, captype *cap_middle, captype *rev_cap_middle) { node *i; arc_forward *a; captype bottleneck; nodeptr *np; /* 1. Finding bottleneck capacity */ /* 1a - the source tree */ bottleneck = *cap_middle; for (i=s_start; ; ) { a = i -> parent; if (a == TERMINAL) break; if (IS_ODD(a)) { a = MAKE_EVEN(a); if (bottleneck > a->r_cap) bottleneck = a -> r_cap; i = NEIGHBOR_NODE_REV(i, a -> shift); } else { if (bottleneck > a->r_rev_cap) bottleneck = a -> r_rev_cap; i = NEIGHBOR_NODE(i, a -> shift); } } if (bottleneck > i->tr_cap) bottleneck = i -> tr_cap; /* 1b - the sink tree */ for (i=t_start; ; ) { a = i -> parent; if (a == TERMINAL) break; if (IS_ODD(a)) { a = MAKE_EVEN(a); if (bottleneck > a->r_rev_cap) bottleneck = a -> r_rev_cap; i = NEIGHBOR_NODE_REV(i, a -> shift); } else { if (bottleneck > a->r_cap) bottleneck = a -> r_cap; i = NEIGHBOR_NODE(i, a -> shift); } } if (bottleneck > - i->tr_cap) bottleneck = - i -> tr_cap; /* 2. Augmenting */ /* 2a - the source tree */ *rev_cap_middle += bottleneck; *cap_middle -= bottleneck; for (i=s_start; ; ) { a = i -> parent; if (a == TERMINAL) break; if (IS_ODD(a)) { a = MAKE_EVEN(a); a -> r_rev_cap += bottleneck; a -> r_cap -= bottleneck; if (!a->r_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } i = NEIGHBOR_NODE_REV(i, a -> shift); } else { a -> r_cap += bottleneck; a -> r_rev_cap -= bottleneck; if (!a->r_rev_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } i = NEIGHBOR_NODE(i, a -> shift); } } i -> tr_cap -= bottleneck; if (!i->tr_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } /* 2b - the sink tree */ for (i=t_start; ; ) { a = i -> parent; if (a == TERMINAL) break; if (IS_ODD(a)) { a = MAKE_EVEN(a); a -> r_cap += bottleneck; a -> r_rev_cap -= bottleneck; if (!a->r_rev_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } i = NEIGHBOR_NODE_REV(i, a -> shift); } else { a -> r_rev_cap += bottleneck; a -> r_cap -= bottleneck; if (!a->r_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } i = NEIGHBOR_NODE(i, a -> shift); } } i -> tr_cap += bottleneck; if (!i->tr_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } flow += bottleneck; } /***********************************************************************/ void Graph::process_source_orphan(node *i) { node *j; arc_forward *a0_for, *a0_for_first, *a0_for_last; arc_reverse *a0_rev, *a0_rev_first, *a0_rev_last; arc_forward *a0_min = NULL, *a; nodeptr *np; int d, d_min = INFINITE_D; /* trying to find a new parent */ a0_for_first = i -> first_out; if (IS_ODD(a0_for_first)) { a0_for_first = (arc_forward *) (((char *)a0_for_first) + 1); a0_for_last = (arc_forward *) ((a0_for_first ++) -> shift); } else a0_for_last = (i + 1) -> first_out; a0_rev_first = i -> first_in; if (IS_ODD(a0_rev_first)) { a0_rev_first = (arc_reverse *) (((char *)a0_rev_first) + 1); a0_rev_last = (arc_reverse *) ((a0_rev_first ++) -> sister); } else a0_rev_last = (i + 1) -> first_in; for (a0_for=a0_for_first; a0_forr_rev_cap) { j = NEIGHBOR_NODE(i, a0_for -> shift); if (!j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } if (dshift); j->TS!=TIME; ) { j -> TS = TIME; j -> DIST = d --; a = j->parent; if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } } } } for (a0_rev=a0_rev_first; a0_rev sister; if (a0_for->r_cap) { j = NEIGHBOR_NODE_REV(i, a0_for -> shift); if (!j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } if (dshift); j->TS!=TIME; ) { j -> TS = TIME; j -> DIST = d --; a = j->parent; if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } } } } } if (i->parent = a0_min) { i -> TS = TIME; i -> DIST = d_min + 1; } else { /* no parent is found */ i -> TS = 0; /* process neighbors */ for (a0_for=a0_for_first; a0_for shift); if (!j->is_sink && (a=j->parent)) { if (a0_for->r_rev_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && IS_ODD(a) && NEIGHBOR_NODE_REV(j, MAKE_EVEN(a)->shift)==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } for (a0_rev=a0_rev_first; a0_rev sister; j = NEIGHBOR_NODE_REV(i, a0_for -> shift); if (!j->is_sink && (a=j->parent)) { if (a0_for->r_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && !IS_ODD(a) && NEIGHBOR_NODE(j, a->shift)==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } } } void Graph::process_sink_orphan(node *i) { node *j; arc_forward *a0_for, *a0_for_first, *a0_for_last; arc_reverse *a0_rev, *a0_rev_first, *a0_rev_last; arc_forward *a0_min = NULL, *a; nodeptr *np; int d, d_min = INFINITE_D; /* trying to find a new parent */ a0_for_first = i -> first_out; if (IS_ODD(a0_for_first)) { a0_for_first = (arc_forward *) (((char *)a0_for_first) + 1); a0_for_last = (arc_forward *) ((a0_for_first ++) -> shift); } else a0_for_last = (i + 1) -> first_out; a0_rev_first = i -> first_in; if (IS_ODD(a0_rev_first)) { a0_rev_first = (arc_reverse *) (((char *)a0_rev_first) + 1); a0_rev_last = (arc_reverse *) ((a0_rev_first ++) -> sister); } else a0_rev_last = (i + 1) -> first_in; for (a0_for=a0_for_first; a0_forr_cap) { j = NEIGHBOR_NODE(i, a0_for -> shift); if (j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } if (dshift); j->TS!=TIME; ) { j -> TS = TIME; j -> DIST = d --; a = j->parent; if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } } } } for (a0_rev=a0_rev_first; a0_rev sister; if (a0_for->r_rev_cap) { j = NEIGHBOR_NODE_REV(i, a0_for -> shift); if (j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } if (dshift); j->TS!=TIME; ) { j -> TS = TIME; j -> DIST = d --; a = j->parent; if (IS_ODD(a)) j = NEIGHBOR_NODE_REV(j, MAKE_EVEN(a) -> shift); else j = NEIGHBOR_NODE(j, a -> shift); } } } } } if (i->parent = a0_min) { i -> TS = TIME; i -> DIST = d_min + 1; } else { /* no parent is found */ i -> TS = 0; /* process neighbors */ for (a0_for=a0_for_first; a0_for shift); if (j->is_sink && (a=j->parent)) { if (a0_for->r_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && IS_ODD(a) && NEIGHBOR_NODE_REV(j, MAKE_EVEN(a)->shift)==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } for (a0_rev=a0_rev_first; a0_rev sister; j = NEIGHBOR_NODE_REV(i, a0_for -> shift); if (j->is_sink && (a=j->parent)) { if (a0_for->r_rev_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && !IS_ODD(a) && NEIGHBOR_NODE(j, a->shift)==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } } } /***********************************************************************/ Graph::flowtype Graph::maxflow() { node *i, *j, *current_node = NULL, *s_start, *t_start; captype *cap_middle, *rev_cap_middle; arc_forward *a_for, *a_for_first, *a_for_last; arc_reverse *a_rev, *a_rev_first, *a_rev_last; nodeptr *np, *np_next; prepare_graph(); maxflow_init(); nodeptr_block = new DBlock(NODEPTR_BLOCK_SIZE, error_function); while ( 1 ) { if (i=current_node) { i -> next = NULL; /* remove active flag */ if (!i->parent) i = NULL; } if (!i) { if (!(i = next_active())) break; } /* growth */ s_start = NULL; a_for_first = i -> first_out; if (IS_ODD(a_for_first)) { a_for_first = (arc_forward *) (((char *)a_for_first) + 1); a_for_last = (arc_forward *) ((a_for_first ++) -> shift); } else a_for_last = (i + 1) -> first_out; a_rev_first = i -> first_in; if (IS_ODD(a_rev_first)) { a_rev_first = (arc_reverse *) (((char *)a_rev_first) + 1); a_rev_last = (arc_reverse *) ((a_rev_first ++) -> sister); } else a_rev_last = (i + 1) -> first_in; if (!i->is_sink) { /* grow source tree */ for (a_for=a_for_first; a_forr_cap) { j = NEIGHBOR_NODE(i, a_for -> shift); if (!j->parent) { j -> is_sink = 0; j -> parent = MAKE_ODD(a_for); j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (j->is_sink) { s_start = i; t_start = j; cap_middle = & ( a_for -> r_cap ); rev_cap_middle = & ( a_for -> r_rev_cap ); break; } else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the source shorter */ j -> parent = MAKE_ODD(a_for); j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } if (!s_start) for (a_rev=a_rev_first; a_rev sister; if (a_for->r_rev_cap) { j = NEIGHBOR_NODE_REV(i, a_for -> shift); if (!j->parent) { j -> is_sink = 0; j -> parent = a_for; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (j->is_sink) { s_start = i; t_start = j; cap_middle = & ( a_for -> r_rev_cap ); rev_cap_middle = & ( a_for -> r_cap ); break; } else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the source shorter */ j -> parent = a_for; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } } } else { /* grow sink tree */ for (a_for=a_for_first; a_forr_rev_cap) { j = NEIGHBOR_NODE(i, a_for -> shift); if (!j->parent) { j -> is_sink = 1; j -> parent = MAKE_ODD(a_for); j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (!j->is_sink) { s_start = j; t_start = i; cap_middle = & ( a_for -> r_rev_cap ); rev_cap_middle = & ( a_for -> r_cap ); break; } else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the sink shorter */ j -> parent = MAKE_ODD(a_for); j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } for (a_rev=a_rev_first; a_rev sister; if (a_for->r_cap) { j = NEIGHBOR_NODE_REV(i, a_for -> shift); if (!j->parent) { j -> is_sink = 1; j -> parent = a_for; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (!j->is_sink) { s_start = j; t_start = i; cap_middle = & ( a_for -> r_cap ); rev_cap_middle = & ( a_for -> r_rev_cap ); break; } else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the sink shorter */ j -> parent = a_for; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } } } TIME ++; if (s_start) { i -> next = i; /* set active flag */ current_node = i; /* augmentation */ augment(s_start, t_start, cap_middle, rev_cap_middle); /* augmentation end */ /* adoption */ while (np=orphan_first) { np_next = np -> next; np -> next = NULL; while (np=orphan_first) { orphan_first = np -> next; i = np -> ptr; nodeptr_block -> Delete(np); if (!orphan_first) orphan_last = NULL; if (i->is_sink) process_sink_orphan(i); else process_source_orphan(i); } orphan_first = np_next; } /* adoption end */ } else current_node = NULL; } delete nodeptr_block; return flow; } /***********************************************************************/ Graph::termtype Graph::what_segment(node_id i) { if (((node*)i)->parent && !((node*)i)->is_sink) return SOURCE; return SINK; } gimp-texturize-3.0+ds/src/plugin-intl.h0000664000000000000000000000053114771535253016670 0ustar rootroot#ifndef __PLUGIN_INTL_H__ #define __PLUGIN_INTL_H__ #ifndef GETTEXT_PACKAGE #error "config.h must be included prior to plugin-intl.h" #endif #include #define _(String) gettext (String) #ifdef gettext_noop # define N_(String) gettext_noop (String) #else # define N_(String) (String) #endif #endif /* __PLUGIN_INTL_H__ */ gimp-texturize-3.0+ds/src/adjacency_list/0000775000000000000000000000000014771535253017232 5ustar rootrootgimp-texturize-3.0+ds/src/adjacency_list/graph.h0000664000000000000000000001442114771535253020506 0ustar rootroot/* graph.h */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ /* This software library is a modification of the maxflow algorithm described in An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Computer Vision. Yuri Boykov and Vladimir Kolmogorov. In Third International Workshop on Energy Minimization Methods in Computer Vision and Pattern Recognition, September 2001 This algorithm was originally developed at Siemens. The main modification is that two trees are used for finding augmenting paths - one grows from the source and the other from the sink. (The original algorithm used only the former one). Details will be described in my PhD thesis. This implementation uses an adjacency list graph representation. Memory allocation: Nodes: 22 bytes + one field to hold a residual capacity of t-links (by default it is 'short' - 2 bytes) Arcs: 12 bytes + one field to hold a residual capacity (by default it is 'short' - 2 bytes) (Note that arcs are always added in pairs - in forward and reverse directions) Example usage (computes a maxflow on the following graph): SOURCE / \ 1/ \2 / 3 \ node0 -----> node1 | <----- | | 4 | \ / 5\ /6 \ / SINK /////////////////////////////////////////////////// #include #include "graph.h" void main() { Graph::node_id nodes[2]; Graph *g = new Graph(); nodes[0] = g -> add_node(); nodes[1] = g -> add_node(); g -> set_tweights(nodes[0], 1, 5); g -> set_tweights(nodes[1], 2, 6); g -> add_edge(nodes[0], nodes[1], 3, 4); Graph::flowtype flow = g -> maxflow(); printf("Flow = %d\n", flow); printf("Minimum cut:\n"); if (g->what_segment(nodes[0]) == Graph::SOURCE) printf("node0 is in the SOURCE set\n"); else printf("node0 is in the SINK set\n"); if (g->what_segment(nodes[1]) == Graph::SOURCE) printf("node1 is in the SOURCE set\n"); else printf("node1 is in the SINK set\n"); delete g; } /////////////////////////////////////////////////// */ #ifndef __GRAPH_H__ #define __GRAPH_H__ #include "block.h" /* Nodes, arcs and pointers to nodes are added in blocks for memory and time efficiency. Below are numbers of items in blocks */ #define NODE_BLOCK_SIZE 512 #define ARC_BLOCK_SIZE 1024 #define NODEPTR_BLOCK_SIZE 128 class Graph { public: typedef enum { SOURCE = 0, SINK = 1 } termtype; /* terminals */ /* Type of edge weights. Can be changed to char, int, float, double, ... */ typedef short captype; /* Type of total flow */ typedef int flowtype; typedef void * node_id; /* interface functions */ /* Constructor. Optional argument is the pointer to the function which will be called if an error occurs; an error message is passed to this function. If this argument is omitted, exit(1) will be called. */ Graph(void (*err_function)(char *) = NULL); /* Destructor */ ~Graph(); /* Adds a node to the graph */ node_id add_node(); /* Adds a bidirectional edge between 'from' and 'to' with the weights 'cap' and 'rev_cap' */ void add_edge(node_id from, node_id to, captype cap, captype rev_cap); /* Sets the weights of the edges 'SOURCE->i' and 'i->SINK' Can be called at most once for each node before any call to 'add_tweights'. Weights can be negative */ void set_tweights(node_id i, captype cap_source, captype cap_sink); /* Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights Can be called multiple times for each node. Weights can be negative */ void add_tweights(node_id i, captype cap_source, captype cap_sink); /* After the maxflow is computed, this function returns to which segment the node 'i' belongs (Graph::SOURCE or Graph::SINK) */ termtype what_segment(node_id i); /* Computes the maxflow. Can be called only once. */ flowtype maxflow(); /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ private: /* internal variables and functions */ struct arc_st; /* node structure */ typedef struct node_st { arc_st *first; /* first outcoming arc */ arc_st *parent; /* node's parent */ node_st *next; /* pointer to the next active node (or to itself if it is the last node in the list) */ int TS; /* timestamp showing when DIST was computed */ int DIST; /* distance to the terminal */ short is_sink; /* flag showing whether the node is in the source or in the sink tree */ captype tr_cap; /* if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node otherwise -tr_cap is residual capacity of the arc node->SINK */ } node; /* arc structure */ typedef struct arc_st { node_st *head; /* node the arc points to */ arc_st *next; /* next arc with the same originating node */ arc_st *sister; /* reverse arc */ captype r_cap; /* residual capacity */ } arc; /* 'pointer to node' structure */ typedef struct nodeptr_st { node_st *ptr; nodeptr_st *next; } nodeptr; Block *node_block; Block *arc_block; DBlock *nodeptr_block; void (*error_function)(char *); /* this function is called if a error occurs, with a corresponding error message (or exit(1) is called if it's NULL) */ flowtype flow; /* total flow */ /***********************************************************************/ node *queue_first[2], *queue_last[2]; /* list of active nodes */ nodeptr *orphan_first, *orphan_last; /* list of pointers to orphans */ int TIME; /* monotonically increasing global counter */ /***********************************************************************/ /* functions for processing active list */ void set_active(node *i); node *next_active(); void maxflow_init(); void augment(arc *middle_arc); void process_source_orphan(node *i); void process_sink_orphan(node *i); }; #endif gimp-texturize-3.0+ds/src/adjacency_list/graph.cpp0000664000000000000000000000272614771535253021046 0ustar rootroot/* graph.cpp */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ #include #include "graph.h" Graph::Graph(void (*err_function)(char *)) { error_function = err_function; node_block = new Block(NODE_BLOCK_SIZE, error_function); arc_block = new Block(NODE_BLOCK_SIZE, error_function); flow = 0; } Graph::~Graph() { delete node_block; delete arc_block; } Graph::node_id Graph::add_node() { node *i = node_block -> New(); i -> first = NULL; i -> tr_cap = 0; return (node_id) i; } void Graph::add_edge(node_id from, node_id to, captype cap, captype rev_cap) { arc *a, *a_rev; a = arc_block -> New(2); a_rev = a + 1; a -> sister = a_rev; a_rev -> sister = a; a -> next = ((node*)from) -> first; ((node*)from) -> first = a; a_rev -> next = ((node*)to) -> first; ((node*)to) -> first = a_rev; a -> head = (node*)to; a_rev -> head = (node*)from; a -> r_cap = cap; a_rev -> r_cap = rev_cap; } void Graph::set_tweights(node_id i, captype cap_source, captype cap_sink) { flow += (cap_source < cap_sink) ? cap_source : cap_sink; ((node*)i) -> tr_cap = cap_source - cap_sink; } void Graph::add_tweights(node_id i, captype cap_source, captype cap_sink) { register captype delta = ((node*)i) -> tr_cap; if (delta > 0) cap_source += delta; else cap_sink -= delta; flow += (cap_source < cap_sink) ? cap_source : cap_sink; ((node*)i) -> tr_cap = cap_source - cap_sink; } gimp-texturize-3.0+ds/src/adjacency_list/block.h0000664000000000000000000001636014771535253020503 0ustar rootroot/* block.h */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ /* Template classes Block and DBlock Implement adding and deleting items of the same type in blocks. If there there are many items then using Block or DBlock is more efficient than using 'new' and 'delete' both in terms of memory and time since (1) On some systems there is some minimum amount of memory that 'new' can allocate (e.g., 64), so if items are small that a lot of memory is wasted. (2) 'new' and 'delete' are designed for items of varying size. If all items has the same size, then an algorithm for adding and deleting can be made more efficient. (3) All Block and DBlock functions are inline, so there are no extra function calls. Differences between Block and DBlock: (1) DBlock allows both adding and deleting items, whereas Block allows only adding items. (2) Block has an additional operation of scanning items added so far (in the order in which they were added). (3) Block allows to allocate several consecutive items at a time, whereas DBlock can add only a single item. Note that no constructors or destructors are called for items. Example usage for items of type 'MyType': /////////////////////////////////////////////////// #include "block.h" #define BLOCK_SIZE 1024 typedef struct { int a, b; } MyType; MyType *ptr, *array[10000]; ... Block *block = new Block(BLOCK_SIZE); // adding items for (int i=0; i New(); ptr -> a = ptr -> b = rand(); } // reading items for (ptr=block->ScanFirst(); ptr; ptr=block->ScanNext()) { printf("%d %d\n", ptr->a, ptr->b); } delete block; ... DBlock *dblock = new DBlock(BLOCK_SIZE); // adding items for (int i=0; i New(); } // deleting items for (int i=0; i Delete(array[i]); } // adding items for (int i=0; i New(); } delete dblock; /////////////////////////////////////////////////// Note that DBlock deletes items by marking them as empty (i.e., by adding them to the list of free items), so that this memory could be used for subsequently added items. Thus, at each moment the memory allocated is determined by the maximum number of items allocated simultaneously at earlier moments. All memory is deallocated only when the destructor is called. */ #ifndef __BLOCK_H__ #define __BLOCK_H__ #include /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ template class Block { public: /* Constructor. Arguments are the block size and (optionally) the pointer to the function which will be called if allocation failed; the message passed to this function is "Not enough memory!" */ Block(int size, void (*err_function)(char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } /* Destructor. Deallocates all items added so far */ ~Block() { while (first) { block *next = first -> next; delete first; first = next; } } /* Allocates 'num' consecutive items; returns pointer to the first item. 'num' cannot be greater than the block size since items must fit in one block */ Type *New(int num = 1) { Type *t; if (!last || last->current + num > last->last) { if (last && last->next) last = last -> next; else { block *next = (block *) new char [sizeof(block) + (block_size-1)*sizeof(Type)]; if (!next) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } if (last) last -> next = next; else first = next; last = next; last -> current = & ( last -> data[0] ); last -> last = last -> current + block_size; last -> next = NULL; } } t = last -> current; last -> current += num; return t; } /* Returns the first item (or NULL, if no items were added) */ Type *ScanFirst() { scan_current_block = first; if (!scan_current_block) return NULL; scan_current_data = & ( scan_current_block -> data[0] ); return scan_current_data ++; } /* Returns the next item (or NULL, if all items have been read) Can be called only if previous ScanFirst() or ScanNext() call returned not NULL. */ Type *ScanNext() { if (scan_current_data >= scan_current_block -> current) { scan_current_block = scan_current_block -> next; if (!scan_current_block) return NULL; scan_current_data = & ( scan_current_block -> data[0] ); } return scan_current_data ++; } /* Marks all elements as empty */ void Reset() { block *b; if (!first) return; for (b=first; ; b=b->next) { b -> current = & ( b -> data[0] ); if (b == last) break; } last = first; } /***********************************************************************/ private: typedef struct block_st { Type *current, *last; struct block_st *next; Type data[1]; } block; int block_size; block *first; block *last; block *scan_current_block; Type *scan_current_data; void (*error_function)(char *); }; /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ template class DBlock { public: /* Constructor. Arguments are the block size and (optionally) the pointer to the function which will be called if allocation failed; the message passed to this function is "Not enough memory!" */ DBlock(int size, void (*err_function)(char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } /* Destructor. Deallocates all items added so far */ ~DBlock() { while (first) { block *next = first -> next; delete first; first = next; } } /* Allocates one item */ Type *New() { block_item *item; if (!first_free) { block *next = first; first = (block *) new char [sizeof(block) + (block_size-1)*sizeof(block_item)]; if (!first) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } first_free = & (first -> data[0] ); for (item=first_free; item next_free = item + 1; item -> next_free = NULL; first -> next = next; } item = first_free; first_free = item -> next_free; return (Type *) item; } /* Deletes an item allocated previously */ void Delete(Type *t) { ((block_item *) t) -> next_free = first_free; first_free = (block_item *) t; } /***********************************************************************/ private: typedef union block_item_st { Type t; block_item_st *next_free; } block_item; typedef struct block_st { struct block_st *next; block_item data[1]; } block; int block_size; block *first; block_item *first_free; void (*error_function)(char *); }; #endif gimp-texturize-3.0+ds/src/adjacency_list/maxflow.cpp0000664000000000000000000002441014771535253021414 0ustar rootroot/* maxflow.cpp */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ #include #include "graph.h" /* special constants for node->parent */ #define TERMINAL ( (arc *) 1 ) /* to terminal */ #define ORPHAN ( (arc *) 2 ) /* orphan */ #define INFINITE_D 1000000000 /* infinite distance to the terminal */ /***********************************************************************/ /* Functions for processing active list. i->next points to the next node in the list (or to i, if i is the last node in the list). If i->next is NULL iff i is not in the list. There are two queues. Active nodes are added to the end of the second queue and read from the front of the first queue. If the first queue is empty, it is replaced by the second queue (and the second queue becomes empty). */ inline void Graph::set_active(node *i) { if (!i->next) { /* it's not in the list yet */ if (queue_last[1]) queue_last[1] -> next = i; else queue_first[1] = i; queue_last[1] = i; i -> next = i; } } /* Returns the next active node. If it is connected to the sink, it stays in the list, otherwise it is removed from the list */ inline Graph::node * Graph::next_active() { node *i; while ( 1 ) { if (!(i=queue_first[0])) { queue_first[0] = i = queue_first[1]; queue_last[0] = queue_last[1]; queue_first[1] = NULL; queue_last[1] = NULL; if (!i) return NULL; } /* remove it from the active list */ if (i->next == i) queue_first[0] = queue_last[0] = NULL; else queue_first[0] = i -> next; i -> next = NULL; /* a node in the list is active iff it has a parent */ if (i->parent) return i; } } /***********************************************************************/ void Graph::maxflow_init() { node *i; queue_first[0] = queue_last[0] = NULL; queue_first[1] = queue_last[1] = NULL; orphan_first = NULL; for (i=node_block->ScanFirst(); i; i=node_block->ScanNext()) { i -> next = NULL; i -> TS = 0; if (i->tr_cap > 0) { /* i is connected to the source */ i -> is_sink = 0; i -> parent = TERMINAL; set_active(i); i -> TS = 0; i -> DIST = 1; } else if (i->tr_cap < 0) { /* i is connected to the sink */ i -> is_sink = 1; i -> parent = TERMINAL; set_active(i); i -> TS = 0; i -> DIST = 1; } else { i -> parent = NULL; } } TIME = 0; } /***********************************************************************/ void Graph::augment(arc *middle_arc) { node *i; arc *a; captype bottleneck; nodeptr *np; /* 1. Finding bottleneck capacity */ /* 1a - the source tree */ bottleneck = middle_arc -> r_cap; for (i=middle_arc->sister->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; if (bottleneck > a->sister->r_cap) bottleneck = a -> sister -> r_cap; } if (bottleneck > i->tr_cap) bottleneck = i -> tr_cap; /* 1b - the sink tree */ for (i=middle_arc->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; if (bottleneck > a->r_cap) bottleneck = a -> r_cap; } if (bottleneck > - i->tr_cap) bottleneck = - i -> tr_cap; /* 2. Augmenting */ /* 2a - the source tree */ middle_arc -> sister -> r_cap += bottleneck; middle_arc -> r_cap -= bottleneck; for (i=middle_arc->sister->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; a -> r_cap += bottleneck; a -> sister -> r_cap -= bottleneck; if (!a->sister->r_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } } i -> tr_cap -= bottleneck; if (!i->tr_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } /* 2b - the sink tree */ for (i=middle_arc->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; a -> sister -> r_cap += bottleneck; a -> r_cap -= bottleneck; if (!a->r_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } } i -> tr_cap += bottleneck; if (!i->tr_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } flow += bottleneck; } /***********************************************************************/ void Graph::process_source_orphan(node *i) { node *j; arc *a0, *a0_min = NULL, *a; nodeptr *np; int d, d_min = INFINITE_D; /* trying to find a new parent */ for (a0=i->first; a0; a0=a0->next) if (a0->sister->r_cap) { j = a0 -> head; if (!j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } j = a -> head; } if (dhead; j->TS!=TIME; j=j->parent->head) { j -> TS = TIME; j -> DIST = d --; } } } } if (i->parent = a0_min) { i -> TS = TIME; i -> DIST = d_min + 1; } else { /* no parent is found */ i -> TS = 0; /* process neighbors */ for (a0=i->first; a0; a0=a0->next) { j = a0 -> head; if (!j->is_sink && (a=j->parent)) { if (a0->sister->r_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && a->head==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } } } void Graph::process_sink_orphan(node *i) { node *j; arc *a0, *a0_min = NULL, *a; nodeptr *np; int d, d_min = INFINITE_D; /* trying to find a new parent */ for (a0=i->first; a0; a0=a0->next) if (a0->r_cap) { j = a0 -> head; if (j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } j = a -> head; } if (dhead; j->TS!=TIME; j=j->parent->head) { j -> TS = TIME; j -> DIST = d --; } } } } if (i->parent = a0_min) { i -> TS = TIME; i -> DIST = d_min + 1; } else { /* no parent is found */ i -> TS = 0; /* process neighbors */ for (a0=i->first; a0; a0=a0->next) { j = a0 -> head; if (j->is_sink && (a=j->parent)) { if (a0->r_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && a->head==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } } } /***********************************************************************/ Graph::flowtype Graph::maxflow() { node *i, *j, *current_node = NULL; arc *a; nodeptr *np, *np_next; maxflow_init(); nodeptr_block = new DBlock(NODEPTR_BLOCK_SIZE, error_function); while ( 1 ) { if (i=current_node) { i -> next = NULL; /* remove active flag */ if (!i->parent) i = NULL; } if (!i) { if (!(i = next_active())) break; } /* growth */ if (!i->is_sink) { /* grow source tree */ for (a=i->first; a; a=a->next) if (a->r_cap) { j = a -> head; if (!j->parent) { j -> is_sink = 0; j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (j->is_sink) break; else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the source shorter */ j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } } else { /* grow sink tree */ for (a=i->first; a; a=a->next) if (a->sister->r_cap) { j = a -> head; if (!j->parent) { j -> is_sink = 1; j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (!j->is_sink) { a = a -> sister; break; } else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the sink shorter */ j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } } TIME ++; if (a) { i -> next = i; /* set active flag */ current_node = i; /* augmentation */ augment(a); /* augmentation end */ /* adoption */ while (np=orphan_first) { np_next = np -> next; np -> next = NULL; while (np=orphan_first) { orphan_first = np -> next; i = np -> ptr; nodeptr_block -> Delete(np); if (!orphan_first) orphan_last = NULL; if (i->is_sink) process_sink_orphan(i); else process_source_orphan(i); } orphan_first = np_next; } /* adoption end */ } else current_node = NULL; } delete nodeptr_block; return flow; } /***********************************************************************/ Graph::termtype Graph::what_segment(node_id i) { if (((node*)i)->parent && !((node*)i)->is_sink) return SOURCE; return SINK; } gimp-texturize-3.0+ds/src/block.h0000664000000000000000000001625714771535253015534 0ustar rootroot/* block.h */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ /* Template classes Block and DBlock Implement adding and deleting items of the same type in blocks. If there there are many items then using Block or DBlock is more efficient than using 'new' and 'delete' both in terms of memory and time since (1) On some systems there is some minimum amount of memory that 'new' can allocate (e.g., 64), so if items are small that a lot of memory is wasted. (2) 'new' and 'delete' are designed for items of varying size. If all items has the same size, then an algorithm for adding and deleting can be made more efficient. (3) All Block and DBlock functions are inline, so there are no extra function calls. Differences between Block and DBlock: (1) DBlock allows both adding and deleting items, whereas Block allows only adding items. (2) Block has an additional operation of scanning items added so far (in the order in which they were added). (3) Block allows to allocate several consecutive items at a time, whereas DBlock can add only a single item. Note that no constructors or destructors are called for items. Example usage for items of type 'MyType': /////////////////////////////////////////////////// #include "block.h" #define BLOCK_SIZE 1024 typedef struct { int a, b; } MyType; MyType *ptr, *array[10000]; ... Block *block = new Block(BLOCK_SIZE); // adding items for (int i=0; i New(); ptr -> a = ptr -> b = rand(); } // reading items for (ptr=block->ScanFirst(); ptr; ptr=block->ScanNext()) { printf("%d %d\n", ptr->a, ptr->b); } delete block; ... DBlock *dblock = new DBlock(BLOCK_SIZE); // adding items for (int i=0; i New(); } // deleting items for (int i=0; i Delete(array[i]); } // adding items for (int i=0; i New(); } delete dblock; /////////////////////////////////////////////////// Note that DBlock deletes items by marking them as empty (i.e., by adding them to the list of free items), so that this memory could be used for subsequently added items. Thus, at each moment the memory allocated is determined by the maximum number of items allocated simultaneously at earlier moments. All memory is deallocated only when the destructor is called. */ #ifndef __BLOCK_H__ #define __BLOCK_H__ #include /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ template class Block { public: /* Constructor. Arguments are the block size and (optionally) the pointer to the function which will be called if allocation failed; the message passed to this function is "Not enough memory!" */ Block(int size, void (*err_function)(const char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } /* Destructor. Deallocates all items added so far */ ~Block() { while (first) { block *next = first -> next; delete first; first = next; } } /* Allocates 'num' consecutive items; returns pointer to the first item. 'num' cannot be greater than the block size since items must fit in one block */ Type *New(int num = 1) { Type *t; if (!last || last->current + num > last->last) { if (last && last->next) last = last -> next; else { block *next = (block *) new char [sizeof(block) + (block_size-1)*sizeof(Type)]; if (!next) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } if (last) last -> next = next; else first = next; last = next; last -> current = & ( last -> data[0] ); last -> last = last -> current + block_size; last -> next = NULL; } } t = last -> current; last -> current += num; return t; } /* Returns the first item (or NULL, if no items were added) */ Type *ScanFirst() { scan_current_block = first; if (!scan_current_block) return NULL; scan_current_data = & ( scan_current_block -> data[0] ); return scan_current_data ++; } /* Returns the next item (or NULL, if all items have been read) Can be called only if previous ScanFirst() or ScanNext() call returned not NULL. */ Type *ScanNext() { if (scan_current_data >= scan_current_block -> current) { scan_current_block = scan_current_block -> next; if (!scan_current_block) return NULL; scan_current_data = & ( scan_current_block -> data[0] ); } return scan_current_data ++; } /* Marks all elements as empty */ void Reset() { block *b; if (!first) return; for (b=first; ; b=b->next) { b -> current = & ( b -> data[0] ); if (b == last) break; } last = first; } /***********************************************************************/ private: typedef struct block_st { Type *current, *last; struct block_st *next; Type data[1]; } block; int block_size; block *first; block *last; block *scan_current_block; Type *scan_current_data; void(*error_function)(const char *); }; /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ template class DBlock { public: /* Constructor. Arguments are the block size and (optionally) the pointer to the function which will be called if allocation failed; the message passed to this function is "Not enough memory!" */ DBlock(int size, void (*err_function)(const char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } /* Destructor. Deallocates all items added so far */ ~DBlock() { while (first) { block *next = first -> next; delete first; first = next; } } /* Allocates one item */ Type *New() { block_item *item; if (!first_free) { block *next = first; first = (block *) new char [sizeof(block) + (block_size-1)*sizeof(block_item)]; if (!first) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } first_free = & (first -> data[0] ); for (item=first_free; item next_free = item + 1; item -> next_free = NULL; first -> next = next; } item = first_free; first_free = item -> next_free; return (Type *) item; } /* Deletes an item allocated previously */ void Delete(Type *t) { ((block_item *) t) -> next_free = first_free; first_free = (block_item *) t; } /***********************************************************************/ private: typedef union block_item_st { Type t; block_item_st *next_free; } block_item; typedef struct block_st { struct block_st *next; block_item data[1]; } block; int block_size; block *first; block_item *first_free; void (*error_function)(const char *); }; #endif gimp-texturize-3.0+ds/src/main.c0000664000000000000000000001313514771535253015351 0ustar rootroot/* Always include this in all plug-ins */ #include #include #include "main.h" #include "render.h" /* The name of my PDB procedure */ #define PLUG_IN_PROC "plug-in-texturize-c-texturize" #define PLUG_IN_NAME_CAPITALIZED "Texturize" #define DEFAULT_NEW_IMAGE_WIDTH 600; #define DEFAULT_NEW_IMAGE_HEIGHT 600; #define DEFAULT_OVERLAP 100; /* Our custom class Texturize is derived from GimpPlugIn. */ struct _Texturize { GimpPlugIn parent_instance; }; #define TEXTURIZE_TYPE (texturize_get_type()) G_DECLARE_FINAL_TYPE (Texturize, texturize, TEXTURIZE,, GimpPlugIn) /* Declarations */ static GList * texturize_query_procedures (GimpPlugIn *plug_in); static GimpProcedure * texturize_create_procedure (GimpPlugIn *plug_in, const gchar *name); static GimpValueArray * texturize_run(GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GimpDrawable **drawables, GimpProcedureConfig *config, gpointer run_data); G_DEFINE_TYPE (Texturize, texturize, GIMP_TYPE_PLUG_IN) static void texturize_class_init (TexturizeClass *klass) { GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass); plug_in_class->query_procedures = texturize_query_procedures; plug_in_class->create_procedure = texturize_create_procedure; } static void texturize_init (Texturize *texturize) { } static GList * texturize_query_procedures (GimpPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_PROC)); } static GimpProcedure * texturize_create_procedure (GimpPlugIn *plug_in, const gchar *name) { GimpProcedure *procedure = NULL; if (g_strcmp0 (name, PLUG_IN_PROC) == 0) { procedure = gimp_image_procedure_new (plug_in, name, GIMP_PDB_PROC_TYPE_PLUGIN, texturize_run, NULL, NULL); gimp_procedure_set_sensitivity_mask (procedure, GIMP_PROCEDURE_SENSITIVE_DRAWABLE); gimp_procedure_set_menu_label (procedure, "Texturize..."); gimp_procedure_add_menu_path (procedure, "/Filters/Map/"); gimp_procedure_set_documentation (procedure, "Texturize Plugin for the GIMP", "Creates seamless textures from existing images. " "", NULL); gimp_procedure_set_attribution (procedure, "Manu Cornet & Jean-Baptiste Rouquier", "", "2007"); gimp_procedure_add_int_argument(procedure, "width_i", "New image _width", NULL, 1, 100000, 1000, G_PARAM_READWRITE); gimp_procedure_add_int_argument(procedure, "height_i", "New image _height", NULL, 1, 100000, 1000, G_PARAM_READWRITE); gimp_procedure_add_int_argument(procedure, "overlap", "O_verlap", "Higher: slower, more seamless but potentially more visible repetition", 5, 1000, 300, G_PARAM_READWRITE); gimp_procedure_add_boolean_argument(procedure, "tileable", "_Tileable", "Whether the new image should be tileable", 0, G_PARAM_READWRITE); } return procedure; } #define PLUG_IN_BINARY "texturize" static GimpValueArray * texturize_run (GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GimpDrawable **drawables, GimpProcedureConfig *config, gpointer run_data) { gint n_drawables; gint width_i = DEFAULT_NEW_IMAGE_WIDTH; gint height_i = DEFAULT_NEW_IMAGE_HEIGHT; gint overlap = DEFAULT_OVERLAP; gboolean tileable = 0; n_drawables = gimp_core_object_array_get_length ((GObject **) drawables); gimp_message ("Texturizing..."); if (n_drawables > 1 || n_drawables == 0) { GError *error = NULL; g_set_error (&error, GIMP_PLUG_IN_ERROR, 0, "'%s' works only with a single image.", PLUG_IN_NAME_CAPITALIZED); return gimp_procedure_new_return_values(procedure, GIMP_PDB_CALLING_ERROR, error); } else if (n_drawables == 1) { GimpDrawable *drawable = drawables[0]; if (!GIMP_IS_LAYER (drawable)) { GError *error = NULL; g_set_error(&error, GIMP_PLUG_IN_ERROR, 0, "Procedure '%s' works with layers only.", PLUG_IN_NAME_CAPITALIZED); return gimp_procedure_new_return_values(procedure, GIMP_PDB_CALLING_ERROR, error); } } if (run_mode == GIMP_RUN_INTERACTIVE) { GtkWidget *dialog; gimp_ui_init(PLUG_IN_BINARY); dialog = gimp_procedure_dialog_new(procedure, GIMP_PROCEDURE_CONFIG(config), PLUG_IN_NAME_CAPITALIZED); gimp_procedure_dialog_fill(GIMP_PROCEDURE_DIALOG(dialog), NULL); if (!gimp_procedure_dialog_run(GIMP_PROCEDURE_DIALOG(dialog))) { return gimp_procedure_new_return_values(procedure, GIMP_PDB_CANCEL, NULL); } } g_object_get (config, "width_i", &width_i, "height_i", &height_i, "overlap", &overlap, "tileable", &tileable, NULL); GimpImage* new_image = render(drawables[0], width_i, height_i, overlap, tileable); if (new_image == NULL) { return gimp_procedure_new_return_values(procedure, GIMP_PDB_EXECUTION_ERROR, NULL); } else { gimp_display_new(new_image); gimp_displays_flush(); return gimp_procedure_new_return_values(procedure, GIMP_PDB_SUCCESS, NULL); } } GIMP_MAIN (TEXTURIZE_TYPE) gimp-texturize-3.0+ds/src/texturize.h0000664000000000000000000000303014771535253016466 0ustar rootroot#include // Counts number of cells != 0 in filled. int count_filled_pixels(guchar ** filled, int width_i, int height_i); // Compute the graph, cuts it and updates the image. void cut_graph( int* patch_posn, // Where to put the patch. int width_i, int height_i, int width_p, int height_p, int channels, guchar **filled, //see render.c. Tells whether the the pixel is filled and if there is a cut here. guchar *image, guchar * patch, guchar *coupe_h_here, guchar * coupe_h_west, // Pixels lost along an old horizontal cut guchar *coupe_v_here, guchar * coupe_v_north, // idem for vertical cuts gboolean make_tileable, gboolean invert); // Allocates the memory (with malloc) and fills with 0. guchar ** init_guchar_tab_2d(gint x, gint y); /* Compute the best position to put the patch, * between (x_patch_posn_min, y_patch_posn_min) * and (x_patch_posn_max, y_patch_posn_max). */ void offset_optimal( gint *resultat, // The position where the patch will have to be put. guchar *image, guchar *patch, gint width_p, gint height_p, gint width_i, gint height_i, gint x_patch_posn_min, gint y_patch_posn_min, gint x_patch_posn_max, gint y_patch_posn_max, // Admissible positions for the patch, this function determines the best one. gint channels, guchar ** filled, gboolean make_tileable); // Returns the minimal unfilled pixel under lexicographical order (y,x). int * pixel_to_fill(guchar ** filled, int width_i, int height_i, int* resultat); gint modulo(gint x, gint m); gimp-texturize-3.0+ds/src/render.h0000664000000000000000000000031514771535253015705 0ustar rootroot#ifndef __RENDER_H__ #define __RENDER_H__ /* Public functions */ GimpImage* render(GimpDrawable *drawable, gint new_width, gint new_height, gint overlap, gboolean tileable); #endif /* __RENDER_H__ */ gimp-texturize-3.0+ds/src/maxflow.cpp0000664000000000000000000002535714771535253016453 0ustar rootroot/* maxflow.cpp */ /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2001. */ #include #include "graph.h" /* special constants for node->parent */ #define TERMINAL ( (arc *) 1 ) /* to terminal */ #define ORPHAN ( (arc *) 2 ) /* orphan */ #define INFINITE_D 1000000000 /* infinite distance to the terminal */ /***********************************************************************/ /* Functions for processing active list. i->next points to the next node in the list (or to i, if i is the last node in the list). If i->next is NULL iff i is not in the list. There are two queues. Active nodes are added to the end of the second queue and read from the front of the first queue. If the first queue is empty, it is replaced by the second queue (and the second queue becomes empty). */ inline void Graph::set_active(node *i) { if (!i->next) { /* it's not in the list yet */ if (queue_last[1]) queue_last[1] -> next = i; else queue_first[1] = i; queue_last[1] = i; i -> next = i; } } /* Returns the next active node. If it is connected to the sink, it stays in the list, otherwise it is removed from the list */ inline Graph::node * Graph::next_active() { node *i; while ( 1 ) { if (!(i=queue_first[0])) { queue_first[0] = i = queue_first[1]; queue_last[0] = queue_last[1]; queue_first[1] = NULL; queue_last[1] = NULL; if (!i) return NULL; } /* remove it from the active list */ if (i->next == i) queue_first[0] = queue_last[0] = NULL; else queue_first[0] = i -> next; i -> next = NULL; /* a node in the list is active iff it has a parent */ if (i->parent) return i; } } /***********************************************************************/ void Graph::maxflow_init() { node *i; queue_first[0] = queue_last[0] = NULL; queue_first[1] = queue_last[1] = NULL; orphan_first = NULL; for (i=node_block->ScanFirst(); i; i=node_block->ScanNext()) { i -> next = NULL; i -> TS = 0; if (i->tr_cap > 0) { /* i is connected to the source */ i -> is_sink = 0; i -> parent = TERMINAL; set_active(i); i -> TS = 0; i -> DIST = 1; } else if (i->tr_cap < 0) { /* i is connected to the sink */ i -> is_sink = 1; i -> parent = TERMINAL; set_active(i); i -> TS = 0; i -> DIST = 1; } else { i -> parent = NULL; } } TIME = 0; } /***********************************************************************/ void Graph::augment(arc *middle_arc) { node *i; arc *a; captype bottleneck; nodeptr *np; /* 1. Finding bottleneck capacity */ /* 1a - the source tree */ bottleneck = middle_arc -> r_cap; for (i=middle_arc->sister->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; if (bottleneck > a->sister->r_cap) bottleneck = a -> sister -> r_cap; } if (bottleneck > i->tr_cap) bottleneck = i -> tr_cap; /* 1b - the sink tree */ for (i=middle_arc->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; if (bottleneck > a->r_cap) bottleneck = a -> r_cap; } if (bottleneck > - i->tr_cap) bottleneck = - i -> tr_cap; /* 2. Augmenting */ /* 2a - the source tree */ middle_arc -> sister -> r_cap += bottleneck; middle_arc -> r_cap -= bottleneck; for (i=middle_arc->sister->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; a -> r_cap += bottleneck; a -> sister -> r_cap -= bottleneck; if (!a->sister->r_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } } i -> tr_cap -= bottleneck; if (!i->tr_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } /* 2b - the sink tree */ for (i=middle_arc->head; ; i=a->head) { a = i -> parent; if (a == TERMINAL) break; a -> sister -> r_cap += bottleneck; a -> r_cap -= bottleneck; if (!a->r_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } } i -> tr_cap += bottleneck; if (!i->tr_cap) { /* add i to the adoption list */ i -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = i; np -> next = orphan_first; orphan_first = np; } flow += bottleneck; } /***********************************************************************/ void Graph::process_source_orphan(node *i) { node *j; arc *a0, *a0_min = NULL, *a; nodeptr *np; int d, d_min = INFINITE_D; /* trying to find a new parent */ for (a0=i->first; a0; a0=a0->next) if (a0->sister->r_cap) { j = a0 -> head; if (!j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } j = a -> head; } if (dhead; j->TS!=TIME; j=j->parent->head) { j -> TS = TIME; j -> DIST = d --; } } } } if ((i->parent = a0_min)) { i -> TS = TIME; i -> DIST = d_min + 1; } else { /* no parent is found */ i -> TS = 0; /* process neighbors */ for (a0=i->first; a0; a0=a0->next) { j = a0 -> head; if (!j->is_sink && (a=j->parent)) { if (a0->sister->r_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && a->head==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } } } void Graph::process_sink_orphan(node *i) { node *j; arc *a0, *a0_min = NULL, *a; nodeptr *np; int d, d_min = INFINITE_D; /* trying to find a new parent */ for (a0=i->first; a0; a0=a0->next) if (a0->r_cap) { j = a0 -> head; if (j->is_sink && (a=j->parent)) { /* checking the origin of j */ d = 0; while ( 1 ) { if (j->TS == TIME) { d += j -> DIST; break; } a = j -> parent; d ++; if (a==TERMINAL) { j -> TS = TIME; j -> DIST = 1; break; } if (a==ORPHAN) { d = INFINITE_D; break; } j = a -> head; } if (dhead; j->TS!=TIME; j=j->parent->head) { j -> TS = TIME; j -> DIST = d --; } } } } if ((i->parent = a0_min)) { i -> TS = TIME; i -> DIST = d_min + 1; } else { /* no parent is found */ i -> TS = 0; /* process neighbors */ for (a0=i->first; a0; a0=a0->next) { j = a0 -> head; if (j->is_sink && (a=j->parent)) { if (a0->r_cap) set_active(j); if (a!=TERMINAL && a!=ORPHAN && a->head==i) { /* add j to the adoption list */ j -> parent = ORPHAN; np = nodeptr_block -> New(); np -> ptr = j; if (orphan_last) orphan_last -> next = np; else orphan_first = np; orphan_last = np; np -> next = NULL; } } } } } /***********************************************************************/ Graph::flowtype Graph::maxflow() { node *i, *j, *current_node = NULL; arc *a; nodeptr *np, *np_next; maxflow_init(); nodeptr_block = new DBlock(NODEPTR_BLOCK_SIZE, error_function); while ( 1 ) { if ((i = current_node)) { i -> next = NULL; /* remove active flag */ if (!i->parent) i = NULL; } if (!i) { if (!(i = next_active())) break; } /* growth */ if (!i->is_sink) { /* grow source tree */ for (a = i->first; a; a = a->next) if (a->r_cap) { j = a -> head; if (!j->parent) { j -> is_sink = 0; j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (j->is_sink) break; else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the source shorter */ j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } } else { /* grow sink tree */ for (a=i->first; a; a=a->next) if (a->sister->r_cap) { j = a -> head; if (!j->parent) { j -> is_sink = 1; j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; set_active(j); } else if (!j->is_sink) { a = a -> sister; break; } else if (j->TS <= i->TS && j->DIST > i->DIST) { /* heuristic - trying to make the distance from j to the sink shorter */ j -> parent = a -> sister; j -> TS = i -> TS; j -> DIST = i -> DIST + 1; } } } TIME ++; if (a) { i -> next = i; /* set active flag */ current_node = i; /* augmentation */ augment(a); /* augmentation end */ /* adoption */ while ((np = orphan_first)) { np_next = np -> next; np -> next = NULL; while ((np = orphan_first)) { orphan_first = np -> next; i = np -> ptr; nodeptr_block -> Delete(np); if (!orphan_first) orphan_last = NULL; if (i->is_sink) process_sink_orphan(i); else process_source_orphan(i); } orphan_first = np_next; } /* adoption end */ } else current_node = NULL; } delete nodeptr_block; return flow; } /***********************************************************************/ Graph::termtype Graph::what_segment(node_id i) { if (((node*)i)->parent && !((node*)i)->is_sink) return SOURCE; return SINK; } gimp-texturize-3.0+ds/src/main.h0000664000000000000000000000120414771535253015350 0ustar rootroot#ifndef __MAIN_H__ #define __MAIN_H__ #include typedef struct { gint width_i; gint height_i; gint overlap; gboolean make_tileable; } PlugInVals; typedef struct { gint32 image_id; gint width_p; gint height_p; } PlugInImageVals; typedef struct { gint32 drawable_id; } PlugInDrawableVals; typedef struct { gboolean chain_active; } PlugInUIVals; /* Default values */ extern const PlugInVals default_vals; extern const PlugInImageVals default_image_vals; extern const PlugInDrawableVals default_drawable_vals; extern const PlugInUIVals default_ui_vals; #endif /* __MAIN_H__ */ gimp-texturize-3.0+ds/src/offset.c0000664000000000000000000000735714771535253015724 0ustar rootroot#include "config.h" #include #include #include #include #include "main.h" #include "texturize.h" #include "plugin-intl.h" // Computes the distance between image_tab and patch_tab for the zone that's // been filled in: // (x_min,y_min) -> (x_max,y_max) in image_tab // (x_min,y_min)-posn -> (x_max,y_max) - posn in patch_tab float difference(gint width_i, gint height_i, gint width_p, gint height_p, guchar * image, guchar * patch, gint posn_x, gint posn_y, gint x_min, gint y_min, gint x_max, gint y_max, gint channels, guchar ** filled) { gint somme = 0, zone=0; gint x_i, y_i, k; guchar *image_ptr, *patch_ptr; gint x_p, y_p; gint x_i_start, x_p_start; gint xcount, ycount; gint iy, ix; guchar *image_ptr_x, *patch_ptr_x; gint image_add_y, patch_add_y; // source image edges is looping ycount = y_max - y_min; xcount = x_max - x_min; y_i = modulo(y_min, height_i); x_i_start = modulo(x_min, width_i); y_p = modulo(y_i - posn_y, height_p); x_p_start = modulo(x_i_start - posn_x, width_p); image_add_y = width_i * channels; patch_add_y = width_p * channels; image_ptr_x = image + y_i * image_add_y; patch_ptr_x = patch + y_p * patch_add_y; for (iy = 0; iy < ycount; iy++) { x_i = x_i_start; x_p = x_p_start; image_ptr = image_ptr_x + x_i * channels; patch_ptr = patch_ptr_x + x_p * channels; for (ix = 0; ix < xcount; ix++) { if (filled[x_i][y_i]) { for (k = 0 ; k < channels; k++) { somme += abs (*image_ptr - *patch_ptr); image_ptr++; patch_ptr++; zone++; } } else { image_ptr += channels; patch_ptr += channels; } if (++x_i >= width_i) { x_i = 0; image_ptr = image_ptr_x; } if (++x_p >= width_p) { x_p = 0; patch_ptr = patch_ptr_x; } } image_ptr_x += image_add_y; patch_ptr_x += patch_add_y; if (++y_i >= height_i) { y_i = 0; image_ptr_x = image; } if (++y_p >= height_p) { y_p = 0; patch_ptr_x = patch; } } if (zone == 0) {g_message(_("Bug: Zone = 0")); exit(-1);} return (((float) somme) / ((float) zone)); } void offset_optimal(gint *resultat, guchar *image, guchar *patch, gint width_p, gint height_p, gint width_i, gint height_i, gint x_patch_posn_min, gint y_patch_posn_min, gint x_patch_posn_max, gint y_patch_posn_max, gint channels, guchar **filled, gboolean tileable) { gint x_i, y_i; float best_difference = INFINITY, tmp_difference; if (tileable) { for (x_i = x_patch_posn_min; x_i < x_patch_posn_max; x_i++) { for (y_i = y_patch_posn_min; y_i < y_patch_posn_max; y_i++) { tmp_difference = difference ( width_i, height_i, width_p, height_p, image, patch, x_i, y_i, MAX (0, x_i), MAX (0, y_i), x_i + width_p, y_i + height_p, channels, filled); if (tmp_difference < best_difference) { best_difference = tmp_difference; resultat[0] = x_i; resultat[1] = y_i; } } } } else { for (x_i = x_patch_posn_min; x_i < x_patch_posn_max; x_i++) { for (y_i = y_patch_posn_min; y_i < y_patch_posn_max; y_i++) { tmp_difference = difference ( width_i, height_i, width_p, height_p, image, patch, x_i, y_i, MAX (0,x_i), MAX (0,y_i), MIN (x_i + width_p, width_i), MIN (y_i + height_p, height_i), channels, filled); if (tmp_difference < best_difference) { best_difference = tmp_difference; resultat[0] = x_i; resultat[1] = y_i; } } } } return; } gimp-texturize-3.0+ds/po/0000775000000000000000000000000014771535253014105 5ustar rootrootgimp-texturize-3.0+ds/po/meson.build0000664000000000000000000000006414771535253016247 0ustar rootrooti18n.gettext( gettext_package, preset: 'glib' ) gimp-texturize-3.0+ds/po/az.po0000664000000000000000000000446014771535253015063 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: gimp-plugin-template 1.2.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-21 02:34+0100\n" "PO-Revision-Date: 2001-11-25 15:19GMT+0200\n" "Last-Translator: Vasif İsmayıloğlu MD \n" "Language-Team: Azerbaijani Turkic \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 0.9.5\n" #: src/interface.c:62 msgid "Texturize Plug-in for GIMP" msgstr "" #: src/interface.c:66 #, fuzzy msgid "_Cancel" msgstr "Ləğv Et" #: src/interface.c:67 #, fuzzy msgid "_OK" msgstr "Oldu" #. Size of the new image ? #: src/interface.c:75 msgid "" "Please set the size of the new image\n" "and the maximum overlap between patches." msgstr "" #. Width of the new image? #: src/interface.c:88 #, fuzzy msgid "Width :" msgstr "En :" #: src/interface.c:94 msgid "Set the new image's width" msgstr "" #. Height of the new image? #: src/interface.c:101 #, fuzzy msgid "Height :" msgstr "Hündürlük :" #: src/interface.c:107 msgid "Set the new image's height" msgstr "" #: src/interface.c:115 msgid "Overlap (pixels) :" msgstr "" #: src/interface.c:121 msgid "" "Set the overlap between patches (larger values make better but longer " "texturizing and tend to make periodic results)" msgstr "" #. Tileable texture? #: src/interface.c:132 msgid "_Tileable" msgstr "" #: src/interface.c:136 msgid "Selects if to create a tileable texture" msgstr "" #: src/main.c:96 msgid "Texturize..." msgstr "" #: src/main.c:202 msgid "There was a problem when opening the new image." msgstr "" #: src/render.c:83 msgid "New image size and original image size are the same." msgstr "" #: src/render.c:86 msgid "New image size is too small." msgstr "" #: src/render.c:106 msgid "" "Sorry, the Texturize plugin only supports RGB and grayscale images. Please " "convert your image to RGB mode first." msgstr "" #: src/render.c:112 msgid "" "Sorry, the Texturize plugin doesn't support images with an alpha (ie " "transparency) channel yet. Please flatten your image first." msgstr "" #: src/render.c:197 msgid "There was a problem when filling the new image." msgstr "" gimp-texturize-3.0+ds/po/fr.po0000664000000000000000000000631014771535253015054 0ustar rootroot# This is the French locale definition for the Texturize GIMP Plug-In # Emmanuel Cornet , 2004 # msgid "" msgstr "" "Project-Id-Version: gimp-plugin-template 1.2.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-21 02:34+0100\n" "PO-Revision-Date: 2001-10-20 12:33+0200\n" "Last-Translator: Emmanuel Cornet \n" "Language-Team: GNOME French Team \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:62 msgid "Texturize Plug-in for GIMP" msgstr "Greffon Texturize pour GIMP" #: src/interface.c:66 #, fuzzy msgid "_Cancel" msgstr "Annuler" #: src/interface.c:67 #, fuzzy msgid "_OK" msgstr "Valider" #. Size of the new image ? #: src/interface.c:75 msgid "" "Please set the size of the new image\n" "and the maximum overlap between patches." msgstr "" "Veuillez régler la taille de la nouvelle image et le\n" "recouvrement maximal entre les morceaux de texture." #. Width of the new image? #: src/interface.c:88 msgid "Width :" msgstr "Largeur :" #: src/interface.c:94 msgid "Set the new image's width" msgstr "Réglez la largeur de la nouvelle image" #. Height of the new image? #: src/interface.c:101 msgid "Height :" msgstr "Hauteur :" #: src/interface.c:107 msgid "Set the new image's height" msgstr "Réglez la hauteur de la nouvelle image" #: src/interface.c:115 msgid "Overlap (pixels) :" msgstr "Recouvrement (pixels) :" #: src/interface.c:121 msgid "" "Set the overlap between patches (larger values make better but longer " "texturizing and tend to make periodic results)" msgstr "" "Réglez le degré de chevauchement entre les morceaux de texture (plus cette " "valeur est élevée, meilleure sera la texture, mais plus le calcul sera long " "et le résultat aura tendance à être périodique)" #. Tileable texture? #: src/interface.c:132 #, fuzzy msgid "_Tileable" msgstr "Auto-similaire" #: src/interface.c:136 msgid "Selects if to create a tileable texture" msgstr "" #: src/main.c:96 #, fuzzy msgid "Texturize..." msgstr "Calcul de la texture..." #: src/main.c:202 msgid "There was a problem when opening the new image." msgstr "Il y a eu un problème au moment d'ouvrir la nouvelle image." #: src/render.c:83 msgid "New image size and original image size are the same." msgstr "" #: src/render.c:86 msgid "New image size is too small." msgstr "" #: src/render.c:106 msgid "" "Sorry, the Texturize plugin only supports RGB and grayscale images. Please " "convert your image to RGB mode first." msgstr "" "Désolé, le plugin Texturize ne fonctionne que sur les images en RVB ou en " "niveaux de gris. Veuillez convertir votre image en mode RVB avant " "d'appliquer le filtre." #: src/render.c:112 msgid "" "Sorry, the Texturize plugin doesn't support images with an alpha (ie " "transparency) channel yet. Please flatten your image first." msgstr "" "Désolé, le plugin Texturize ne fonctionne pas encore avec les images " "pourvues d'une couche alpha (images transparentes). Veuillez commencer par " "aplatir l'image." #: src/render.c:197 #, fuzzy msgid "There was a problem when filling the new image." msgstr "Il y a eu un problème pendant le remplissage de l'image" gimp-texturize-3.0+ds/po/zh_TW.po0000664000000000000000000000444514771535253015507 0ustar rootroot# Traditional chinese translation for gimp-plugin-template. # Copyright (C) 2001 Free Software Foundation, Inc. # Abel Cheung , 2001. # msgid "" msgstr "" "Project-Id-Version: gimp-plugin-template 1.2.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-21 02:34+0100\n" "PO-Revision-Date: 2001-11-05 21:53+0800\n" "Last-Translator: Abel Cheung \n" "Language-Team: Traditional Chinese \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:62 msgid "Texturize Plug-in for GIMP" msgstr "" #: src/interface.c:66 #, fuzzy msgid "_Cancel" msgstr "取消" #: src/interface.c:67 #, fuzzy msgid "_OK" msgstr "確定" #. Size of the new image ? #: src/interface.c:75 msgid "" "Please set the size of the new image\n" "and the maximum overlap between patches." msgstr "" #. Width of the new image? #: src/interface.c:88 #, fuzzy msgid "Width :" msgstr "寬度:" #: src/interface.c:94 msgid "Set the new image's width" msgstr "" #. Height of the new image? #: src/interface.c:101 #, fuzzy msgid "Height :" msgstr "高度:" #: src/interface.c:107 msgid "Set the new image's height" msgstr "" #: src/interface.c:115 msgid "Overlap (pixels) :" msgstr "" #: src/interface.c:121 msgid "" "Set the overlap between patches (larger values make better but longer " "texturizing and tend to make periodic results)" msgstr "" #. Tileable texture? #: src/interface.c:132 msgid "_Tileable" msgstr "" #: src/interface.c:136 msgid "Selects if to create a tileable texture" msgstr "" #: src/main.c:96 msgid "Texturize..." msgstr "" #: src/main.c:202 msgid "There was a problem when opening the new image." msgstr "" #: src/render.c:83 msgid "New image size and original image size are the same." msgstr "" #: src/render.c:86 msgid "New image size is too small." msgstr "" #: src/render.c:106 msgid "" "Sorry, the Texturize plugin only supports RGB and grayscale images. Please " "convert your image to RGB mode first." msgstr "" #: src/render.c:112 msgid "" "Sorry, the Texturize plugin doesn't support images with an alpha (ie " "transparency) channel yet. Please flatten your image first." msgstr "" #: src/render.c:197 msgid "There was a problem when filling the new image." msgstr "" gimp-texturize-3.0+ds/po/LINGUAS0000664000000000000000000000010414771535253015125 0ustar rootroot# Please keep this list sorted alphabetically. az de fr sk sv zh_TW gimp-texturize-3.0+ds/po/sv.po0000664000000000000000000000446114771535253015102 0ustar rootroot# Swedish messages for gimp-plugin-template. # Copyright (C) 2001 Free Software Foundation, Inc. # Christian Rose , 2001. # # $Id: sv.po,v 1.1 2005/04/08 12:05:14 lllmanulll Exp $ # msgid "" msgstr "" "Project-Id-Version: gimp-plugin-template\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-21 02:34+0100\n" "PO-Revision-Date: 2001-11-09 15:12+0100\n" "Last-Translator: Christian Rose \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:62 msgid "Texturize Plug-in for GIMP" msgstr "" #: src/interface.c:66 #, fuzzy msgid "_Cancel" msgstr "Avbryt" #: src/interface.c:67 #, fuzzy msgid "_OK" msgstr "OK" #. Size of the new image ? #: src/interface.c:75 msgid "" "Please set the size of the new image\n" "and the maximum overlap between patches." msgstr "" #. Width of the new image? #: src/interface.c:88 #, fuzzy msgid "Width :" msgstr "Bredd:" #: src/interface.c:94 msgid "Set the new image's width" msgstr "" #. Height of the new image? #: src/interface.c:101 #, fuzzy msgid "Height :" msgstr "Höjd:" #: src/interface.c:107 msgid "Set the new image's height" msgstr "" #: src/interface.c:115 msgid "Overlap (pixels) :" msgstr "" #: src/interface.c:121 msgid "" "Set the overlap between patches (larger values make better but longer " "texturizing and tend to make periodic results)" msgstr "" #. Tileable texture? #: src/interface.c:132 msgid "_Tileable" msgstr "" #: src/interface.c:136 msgid "Selects if to create a tileable texture" msgstr "" #: src/main.c:96 msgid "Texturize..." msgstr "" #: src/main.c:202 msgid "There was a problem when opening the new image." msgstr "" #: src/render.c:83 msgid "New image size and original image size are the same." msgstr "" #: src/render.c:86 msgid "New image size is too small." msgstr "" #: src/render.c:106 msgid "" "Sorry, the Texturize plugin only supports RGB and grayscale images. Please " "convert your image to RGB mode first." msgstr "" #: src/render.c:112 msgid "" "Sorry, the Texturize plugin doesn't support images with an alpha (ie " "transparency) channel yet. Please flatten your image first." msgstr "" #: src/render.c:197 msgid "There was a problem when filling the new image." msgstr "" gimp-texturize-3.0+ds/po/de.po0000664000000000000000000000435014771535253015037 0ustar rootroot# This is the German locale definition for the GIMP Plug-In Template. # Copyright (C) 2000 Free Software Foundation, Inc. # Michael Natterer # msgid "" msgstr "" "Project-Id-Version: gimp-plugin-template 2.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-21 02:34+0100\n" "PO-Revision-Date: 2003-11-29 13:36+0100\n" "Last-Translator: Sven Neumann \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:62 msgid "Texturize Plug-in for GIMP" msgstr "" #: src/interface.c:66 msgid "_Cancel" msgstr "" #: src/interface.c:67 msgid "_OK" msgstr "" #. Size of the new image ? #: src/interface.c:75 msgid "" "Please set the size of the new image\n" "and the maximum overlap between patches." msgstr "" #. Width of the new image? #: src/interface.c:88 #, fuzzy msgid "Width :" msgstr "Breite:" #: src/interface.c:94 msgid "Set the new image's width" msgstr "" #. Height of the new image? #: src/interface.c:101 #, fuzzy msgid "Height :" msgstr "Höhe:" #: src/interface.c:107 msgid "Set the new image's height" msgstr "" #: src/interface.c:115 msgid "Overlap (pixels) :" msgstr "" #: src/interface.c:121 msgid "" "Set the overlap between patches (larger values make better but longer " "texturizing and tend to make periodic results)" msgstr "" #. Tileable texture? #: src/interface.c:132 msgid "_Tileable" msgstr "" #: src/interface.c:136 msgid "Selects if to create a tileable texture" msgstr "" #: src/main.c:96 msgid "Texturize..." msgstr "" #: src/main.c:202 msgid "There was a problem when opening the new image." msgstr "" #: src/render.c:83 msgid "New image size and original image size are the same." msgstr "" #: src/render.c:86 msgid "New image size is too small." msgstr "" #: src/render.c:106 msgid "" "Sorry, the Texturize plugin only supports RGB and grayscale images. Please " "convert your image to RGB mode first." msgstr "" #: src/render.c:112 msgid "" "Sorry, the Texturize plugin doesn't support images with an alpha (ie " "transparency) channel yet. Please flatten your image first." msgstr "" #: src/render.c:197 msgid "There was a problem when filling the new image." msgstr "" gimp-texturize-3.0+ds/po/POTFILES.in0000664000000000000000000000014114771535253015656 0ustar rootroot# List of source files containing translatable strings. src/interface.c src/main.c src/render.c gimp-texturize-3.0+ds/po/sk.po0000664000000000000000000000441514771535253015066 0ustar rootroot# gimp-plugin-template # Copyright (C) 2001 Free Software Foundation, Inc. # Zdenko Podobny , 2001. # msgid "" msgstr "" "Project-Id-Version: gimp-plugin-template\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-21 02:34+0100\n" "PO-Revision-Date: 2001-12-20 17:22GMT+0100\n" "Last-Translator: Zdenko Podobný \n" "Language-Team: Slovak \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 0.9.5\n" #: src/interface.c:62 msgid "Texturize Plug-in for GIMP" msgstr "" #: src/interface.c:66 #, fuzzy msgid "_Cancel" msgstr "Zrušiť" #: src/interface.c:67 #, fuzzy msgid "_OK" msgstr "Ok" #. Size of the new image ? #: src/interface.c:75 msgid "" "Please set the size of the new image\n" "and the maximum overlap between patches." msgstr "" #. Width of the new image? #: src/interface.c:88 #, fuzzy msgid "Width :" msgstr "Šírka:" #: src/interface.c:94 msgid "Set the new image's width" msgstr "" #. Height of the new image? #: src/interface.c:101 #, fuzzy msgid "Height :" msgstr "Výška:" #: src/interface.c:107 msgid "Set the new image's height" msgstr "" #: src/interface.c:115 msgid "Overlap (pixels) :" msgstr "" #: src/interface.c:121 msgid "" "Set the overlap between patches (larger values make better but longer " "texturizing and tend to make periodic results)" msgstr "" #. Tileable texture? #: src/interface.c:132 msgid "_Tileable" msgstr "" #: src/interface.c:136 msgid "Selects if to create a tileable texture" msgstr "" #: src/main.c:96 msgid "Texturize..." msgstr "" #: src/main.c:202 msgid "There was a problem when opening the new image." msgstr "" #: src/render.c:83 msgid "New image size and original image size are the same." msgstr "" #: src/render.c:86 msgid "New image size is too small." msgstr "" #: src/render.c:106 msgid "" "Sorry, the Texturize plugin only supports RGB and grayscale images. Please " "convert your image to RGB mode first." msgstr "" #: src/render.c:112 msgid "" "Sorry, the Texturize plugin doesn't support images with an alpha (ie " "transparency) channel yet. Please flatten your image first." msgstr "" #: src/render.c:197 msgid "There was a problem when filling the new image." msgstr "" gimp-texturize-3.0+ds/.github/0000775000000000000000000000000014771535253015027 5ustar rootrootgimp-texturize-3.0+ds/.github/workflows/0000775000000000000000000000000014771535253017064 5ustar rootrootgimp-texturize-3.0+ds/.github/workflows/build.yml0000664000000000000000000000110014771535253020676 0ustar rootrootname: build-check on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install_dependencies # Attempt to fix 'unable to fetch some archives' failures during this step run: sudo apt update && sudo apt install meson - name: build run: | meson setup build cd build # For newest Ubuntu: meson compile ls texturize && \ { echo "texturize found. SUCCESS."; } || \ { echo "texturize not found"; exit 1; } gimp-texturize-3.0+ds/COPYING0000664000000000000000000004313114771535253014524 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. gimp-texturize-3.0+ds/AUTHORS0000664000000000000000000000012214771535253014532 0ustar rootrootManu Cornet Jean-Baptiste Rouquier gimp-texturize-3.0+ds/help/0000775000000000000000000000000014771535253014417 5ustar rootrootgimp-texturize-3.0+ds/help/images/0000775000000000000000000000000014771535253015664 5ustar rootrootgimp-texturize-3.0+ds/help/images/wilber.png0000664000000000000000000001163614771535253017665 0ustar rootrootPNG  IHDRJ;S̋BgAMA a pHYs @S@IDATx՛ۓ\y>3]VH+BXmRʹ;TH $r*<2T\~b0c 2, iڕbwg>Oߗ9ͮ aW{ؚt}$8%b?:LEƶRԺu\vѷh̜icf|0u&U+oHD}o_/e KyC2?>:+"BL*=Zۼ}族f.{ce"&$D$>>.^ۮŵ|A'5f؈/>8}a2cOgf΁fu3IMַx/DLD} J2|1بW^ۭ5sUuȾ"w}>cU*_o؁ĵϟYZ*Ddnz,RƠ1Ơ!Œ`n޾ C^}ժƘ4M3Bc̊!.bHij2¸v3Fcةکazcm~0o DD6"ނB._[܆7lh+?{N]u$Zk]l]B5`Wvcc^YG"kԠu#tM/yUMJm9fJ*JHS[8rR,Wji蹧8~mc !4֏lZ !hˏMx}ӯY,H #5L>G!.DZx몞H)Hd "L{'iݺiJ)J9y˴r''O:NSqA5~/{뺎c &_9"^YB4@[JT)x ҵhe<;}'oq䉷}WQz,@7^~̉׏lq]̌ Nx11gHĈֶHĴv݆a4 7a.uǟNJQ"tBT~bi7lrۆҠRf_ZZZ8y|bԄz2#3 ""d[lb޴Rjl7"Dٷvi7=r1iĤI:{Q 80t߫V*BBI! zLIAFcf xwj=3&%krTyR)GB*EL@R*!AIV\B4D'dRÂX 0@ R c "F&F`k<߲Yoe`k\UIxBzuq;~Dy{߸y{\(6ӓǏxmCd!] cjY(0 ~X$fEŞ:Q\;*6ly1ޗ]w˾;wپ;wɏ[# ABF29BFdH6J%^qiI gI83W_UBw-xԩ'N5 IҮGq&# o_H)G-ֺ^/--4M]Wԩ)"bDmL$:ڤڤ6+F)wæZ-IlS`z>66/^tIJY,w#'7l=I"NMM-,,lڴi;vkԫ4mZRJuY_uۮO `K3cRC)EHBI!rS\ bcrf^zgժnONN>|k_mwկkZxlll~;ߑR>wߎ;پ[TT*EQn׏A&2hRBB6i#JanR(GL,wTW_~q3ŋ.]tͻo.-pR?(LLL|x0q<99yÇ=70X3  @6dHI LD9cvݳ㸶+U^{- ý{w}Zq?I\<Sw,=z… <555y;?;pmFFFǟzꩵk>íV $20"cLl! NAJVuq<}}<ϳxI :Α#G0,˥R)j.+$W277$I\?9NTay뭷:v[0Ƙ'|ZiDI D"AIBOZ'}?0 0(-aZV \-..ř4M$jD~w,ߟo^yC_X<ֺ [︛ƃ>xСmFGGO?rͣ $ L BP`}?88( /ۑb[._n$Y\\L'֍F#]cP([7zzhwco}[=RNvm `d@&&Pb6QvBq}00(^9WbڵZWZ-"t:=G8fPc%@03+?<8p [߿oKSΝOx֩d[l) 40.HFb)ZNeVo::uKBQ!(Q*%+f S+W@8q qX,bP"f]XP.W{5<{?yoYY$|O9ɂV)։6J)s==&88X,(WwXFRʍX(k ]vxxxxxxhhhppT*q_߁M1Fkn[˗~sj{!J)R]0/ l> NsCFHtzj2P,ųCCCk֬)Jֆ)gZNnz^j8򻙙jn m |/ s( 8 ЏP9aL:??_TZ:Z@P(ąB\.b -T#:'2l$Ii6f^ojZZVkzV;{չR!< 08 ]ϕJř+F3n1z%R\. rXRKmxdN;%ŖNqJ.b%mdgL l;aD'ZiiBـtaEQVAl%ճA_~io A̽SVR:78$I3[:w9ADQwZIP=VsZߞ !Rr\ǵm7u:us]vqo;qۈO6#5xJ)p]73Bz˦6코\Tv6;AQyMG̣>*fo+NcKe@/W ?mov?2,Ϝ )%Ti$${#ۜvB%v+>}WGRߖKesIF\9 ;loi[g[rwe|sbmeS&+%A߉3c!-gfgjŃ[d\_ -rJX=~'puik^V꓏;uE]32>ޤKÊo;2 +7*F'{BWwIENDB`gimp-texturize-3.0+ds/help/en/0000775000000000000000000000000014771535253015021 5ustar rootrootgimp-texturize-3.0+ds/help/en/gimp-help.xml0000664000000000000000000000026014771535253017423 0ustar rootroot gimp-texturize-3.0+ds/help/en/index.html0000664000000000000000000000153714771535253017024 0ustar rootroot Help Page for Texturize
Texturize help page

You are being redirected to the Texturize plugin help page.

gimp-texturize-3.0+ds/LOG0000664000000000000000000003664214771535253014046 0ustar rootrootThis is a log file created for the "Texturize" plugin for the GIMP. Since this plugin is the result of a project for a Computer Vision course in a French university (and since its LOG file shouldn't be useful for anybody except the two students who worked on this project), this file is written in French. Ceci est le fichier de LOG de la création du greffon "Texturize". Les changements sont dans l'ordre chronologique inverse. ############################ ########################### ############################ 2025/03/28 ########################### ############################ ########################### + Fixed plug-in for GIMP-3. ############################ ########################### ############################ 2005/02/01 ########################### ############################ ########################### + Réalisation de la page web dédiée au plugin. + Nettoyage global du code (versions précédentes, débuggage). + Finalisation de la traduction en français. + Nettoyage de graphcut.cpp. Les lignes sont moins longues (mais toujours plus que 80 caractères). ############################ ########################### ############################ 2005/01/31 ########################### ############################ ########################### * On affiche un message d'erreur quand on rencontre une image avec une couche alpha (au lieu de traiter l'image tout de même, incorrectement). * Retour à la version précédente de la barre de progression. +* La page web du plugin commence à s'étoffer de quelques explications, d'exemples et d'une capture d'écran. + Le patch et l'image final sont désormais des guchar* (et non des guchar***). Le greffon tourne environ 5 fois plus vite. J'ai donc enlevé la recommandation d'aller boire un café :-) + Le poids d'une arrête tient compte du gradient local : le greffon coupe maintenant préférentiellement le long des bords des objets. + L'image finale n'est plus initialisée en blanc. Le code est toujours présent pour les prochaines sessions de débuggage. ############################ ########################### ############################ 2005/01/30 ########################### ############################ ########################### + La barre de progression est plus régulière (on utilise l'ordonnée du dernier pixel rempli au lieu du pourcentages de pixels remplis). ############################ ########################### ############################ 2005/01/29 ########################### ############################ ########################### + La coupe min tient désormais compte des anciennes coupes, même sur la première passe (remplissage de l'image avant de chercher à cacher les mauvaises coutures). + rempli est maintenant un guchar** (et non un int**). + Nettoyage de offset.c. ############################ ########################### ############################ 2005/01/27 ########################### ############################ ########################### + J'ai (péniblement) ajouté la détermination de la coupe de poids minimal. La construction des sommets est une petite horreur de gestion des indices si l'on tente de rester propre. + Corrigé un bug pour que la taille par défaut de la nouvelle image soit par défaut 2 fois la taille de l'image initiale, même si on ne touche pas à la boîte de dialogue. ############################ ########################### ############################ 2005/01/25 ########################### ############################ ########################### + Rationalisation des noms des variables. Celles concernant l'image d'origine (le "patch") se terminent par _p, celle concernant l'image finale par _i. Corrigé le bug qui faisait coller les patch du haut tous à la même ordonnée (ie collés contre le bord). Maintenant ils peuvent déborder. +* Journée de travail à deux physiquement à côté. Nettoyages divers dans le code. ############################ ########################### ############################ 2005/01/18 ########################### ############################ ########################### * C'est bon, l'offset marche ! J'ai aussi implémenté la méthode de JB qui consiste non plus à remplir l'image ligne par ligne mais à remplir toujours à partir du pixel vide le plus en haut à gauche : effectivement, ça marche mieux ! Bon, on approche du but, mais il reste encore les graph cut à implémenter (ou plutôt faire le lien avec le code déjà disponible) et essayer de se souvenir des coutures déjà faites (ça, ce sera le plus chaud). ############################ ########################### ############################ 2005/01/16 ########################### ############################ ########################### * J'ai commencé à implémenter la fonction d'offset (ajout du fichier offset.c). C'est un calcul bête et méchant de similarité entre deux images (somme des différences de chaque couple de pixel, en valeur absolue). Mais il y a pas mal de sous-cas à traiter pour vérifier qu'on ne fait pas de segmentation fault, qu'on ne sort pas de l'image, etc. ############################ ########################### ############################ 2005/01/15 ########################### ############################ ########################### * Voilà, à ce stade j'ai un greffon qui ne fait rien de super intéressant (il se contente de répéter l'image de départ jusqu'à remplir la nouvelle image) mais dont le résultat est propre, qui marche aussi pour de très grandes images et qui affiche une belle barre de progression. Demain, j'essaierai de trouver du temps pour rendre la fonction "offset" plus intéressante (calcul des corrélations, ie du produit de convolution, en fait). * La fonction gimp_pixel_rgn_set_rect, qui sert à placer une zone de mémoire dans un rectangle de l'image, n'est pas très futée : on lui dit quel rectangle de l'image de destination il faut mettre à jour, mais si la zone de mémoire n'a pas exactement la même taille, il essaie quand même de placer tous les pixels et ça fait du coup des effets bizarres (les sortes de distorsions que j'observais tout à l'heure). Il faut donc à chaque fois tronquer la zone de mémoire en fonction de la zone de l'image de destination qu'on va occuper. C'est réparé. * J'ai ajouté une barre de progression qui renseigne sur le traitement de l'image : ça fait plus classe :) * La plupart des fonctions utiles sont posées, mais encore à l'état d'esquisse. Ce que fait le greffon pour l'instant, c'est simplement recopier l'image de départ dans la nouvelle jusqu'à remplissage total. Il y a des distorsions curieuses à droit de l'image, il faut que je règle ça. Prochaine grande étape : faire une fonction de calcul d'offset intelligente. * Maintenant que les basses sont bien posées et que je comprends comment interagir avec le Gimp, ça devient un peu plus amusant :) Je commence à poser les bases des fonctions dont j'aurai besoin, les différentes boucles, l'architecture du projet. * En plus de placer l'image de départ en (0,0), je colore le reste en tout blanc pour partir sur un bon point de départ (sinon les autres pixels sont aléatoires et c'est un peu dégueu). Ça été l'occasion de comprendre (à la dure, et avec moult essais/erreurs) comment sont organisées les structures de données qui représentent des images : où sont les deux coordonnées, où sont les canaux de couleur. Bref, encore une fois ce n'était pas du tout, mais vraiment pas du tout documenté ni expliqué nulle part, donc il m'a fallu un certain nombre d'essais et d'images immondes pour comprendre comment ça marchait, et avant d'arriver à faire du blanc bien pur ! * J'ai trouvé le problème : je travaillais sur une image GIF, à couleurs indexées (qui provenait d'ailleurs de la page web de l'article sur lequel on se base). L'algo serait censé marcher pour tous les types d'images (je gère un nombre de canaux quelconque), mais ici ça ne marche pas, je ne sais pas pourquoi. J'ai donc mis un message d'erreur quand on essaie d'appliquer le greffon sur une image à couleurs indexées, je ne vois pas comment résoudre le problème pour l'instant. * J'ai donc bien une nouvelle image qui s'ouvre, mais j'ai maintenant des problèmes pour récupérer les données de l'image de départ et les refourguer à la nouvelle. Plus précisément, j'arrive à récupérer les données (en les affichant avec des printf, c'est bon), mais je n'arrive pas à les remettre dans la nouvelle image. La gestion de la mémoire n'est vraiment pas très sympa : si on veut aller chercher les pixels dans un rectangle de mémoire, c'est à base de tableau[i*taille_ligne + j*taille_colonne + k] avec k variant de 0 au nombre de canaux (RGB, etc.). Il y a bien des procédures toutes faites pour lire un rectangle d'une région initialisée, et l'écrire dans une autre, mais la gestion de la mémoire derrière est telle que ce n'est vraiment pas évident de savoir quel type de zone mémoire il faut utiliser pour stocker ces données (ça, ça marche pour l'instant) et donner à manger à la procédure d'écriture (ça, ça ne marche pas)... Je sens que je vais m'empêtrer dans les pointeurs et les malloc exotiques :-p ############################ ########################### ############################ 2005/01/14 ########################### ############################ ########################### * Dernière modification de la journée : j'ai finalement choisi de demander à Gimp d'ouvrir une nouvelle image, au lieu de m'emmerder avec l'image actuelle. C'était vraiment une journée galère, j'ai un peu pété les plombs, et tout ça n'est vraiment pas bien documenté (probablement à cause de la nouvelle version -- 2.2 -- qui vient de sortir). J'essaierai de faire un bon tutoriel d'écriture d'un greffon Gimp, ça manque *vraiment*. J'ai donc implémenté mon idée d'ouvrir une nouvelle image avec directement la bonne taille, c'est un peu plus subtil à coder mais ça marche : pour l'instant une nouvelle image (toute noire) s'ouvre. * Bon, la compilation remarche (automake, autoconf, etc., c'est bon). Mais pour une raison que je ne comprends pas, il ne veut absolument pas me laisser écrire en C++ un fichier déjà présent dans l'archive (le fichier render.c que j'ai essayé de renommer en render.cpp), même en changeant tout ce qu'il faut dans les Makefile et autre configure.in... Bon, ben il faudra que je me passe du C++ pour ce fichier-là, j'ai déjà passé trop de temps à essayer de lui faire manger mon render.cpp, sans succès. * Les fichiers qui calculent le flot max (ou la coupe de poids min) sont en C++ alors que le greffon est fait pour être programmé en C. J'ai passé beaucoup de temps à changer ce qu'il faut (et à comprendre quoi changer !) dans les fichiers configure.in, Makefile.am, etc. C'est compliqué ces trucs-là, et automake, autoconf sont encore bien obscurs pour moi (sans compter le script autogen.sh inclus dans le template). Bref, pour que ça marche chez tout le monde quand on fait ./configure && make && make install, y a toute une machinerie derrière, très complexe, et j'ai un peu les doigts coincés dedans :) * Ce qui est vraiment difficile, c'est qu'aucun des greffons que j'ai trouvés (y compris tous les greffons livrés avec The Gimp) ne redimensionnent l'image de départ : l'image d'arrivée a exactement la même taille. Or c'est la principale difficulté à laquelle je suis confronté : le redimensionnement apporte des problèmes d'initialisation des "drawable" que j'essaie de résoudre par plusieurs moyens (agrandissement de l'image pour ensuite effacer des pixels, agrandissement de l'image mais en gardant l'original dans un coin -- dans ce dernier cas j'ai un mal fou à demander à Gimp de me laisser écrire dans les autres pixels, etc.), mais pour l'instant sans succès. ############################ ########################### ############################ 2005/01/13 ########################### ############################ ########################### * J'ai passé pas mal de temps pour comprendre comment marchaient les choses sous Gimp (et je n'ai pas fini !). Bref, j'ai enfin réussi à demander à mon greffon de redimensionner l'image (il faut aller fouiller dans les librairies de Gimp, c'est mal documenté, la doc correspond à une version antérieure et plein de trucs ont changé depuis... bref ce n'est pas évident). ############################ ########################### ############################ 2005/01/11 ########################### ############################ ########################### * Je crée le fichier "texturize.h" pour regrouper le prototype de toutes les fonctions et les trucs communs à tous les fichiers source (en particulier pour faire le lien entre le greffon lui-même et l'algo de graph cut que j'utilise). * Il y a aussi les problèmes d'internationalisation : dans le sous-dossier "po", il y a la traduction de tous les messages dans plein de langues. Je vais faire l'anglais (fichier de départ) et le français. * Je modifie la fenêtre de dialogue de base (avec plein d'exemples de curseurs) pour mettre juste la taille de la nouvelle image et poser la bonne question. J'enlève les définitions de variables inutiles. * J'inclus les sources de "maxflow" (algo de calcul du flot max) dans le répertoire du greffon ; je mets les deux sous-répertoires (adjacency-list et forward-star) et je fais des liens symboliques vers les éléments de adjacency-list (c'est le plus gourmand en RAM, mais le plus rapide). Je compile... Ah, c'est du code C++ donc j'installe le paquet g++, je recompile... C'est bon, il fallait ajouter la ligne "AC_PROG_CXX" au fichier configure.in. Je ne pige pas trop comment fonctionne automake, configure, etc. mais maintenant que ça marche, je ne touche plus :) ############################ ########################### ############################ 2005/01/07 ########################### ############################ ########################### * Pour commencer à me familiariser avec la structure des fichiers main.c, render.c, interface.c, etc., je change déjà les textes de façade (nom du greffon, nom des auteurs, etc.). Je commence à voir un peu comment ça marche. * Ça ne fonctionne toujours pas (./config râle : ma version de GIMP est trop ancienne). J'installe donc tous ces paquets (et gimp) en version unstable. Bon, ça va mieux : j'ai fait ./configure, make, sudo make install et tout va bien. Le nouveau greffon "plug-in template" apparaît bien dans la loiste des plug-ins disponibles, dans le GIMP, avec un exemple de boîte de dialogue très bien fait. * Installation de plusieurs paquets liés au développement sous GIMP (je ne sais pas lesquels sont vraiment utiles mais ça ne marchait pas dans au moins l'un d'entre eux) : sudo apt-get install libgimp2.0-dev libglib2.0-dev libgtk1.2-dev libgtkgl2.0-dev * J'ai commencé par changer la licence (donnée dans le fichier COPYING) en la GPL, un peu plus adaptée à notre projet, il me semble. * J'ai donc téléchargé le "plugin template" depuis le site officiel des développeurs de GIMP. LocalWords: guchar débuggage offset.c ie JB fault gimp rgn rect LocalWords: printf RGB malloc gimp-texturize-3.0+ds/.gitignore0000664000000000000000000000005014771535253015452 0ustar rootroot*~ build/ po/POTFILES po/*.pot po/*.gmo