pax_global_header00006660000000000000000000000064141443412460014515gustar00rootroot0000000000000052 comment=e05345f8d25e775d5ae71aeb27c7ef33a63a8a54 gimp-texturize-2.2/000077500000000000000000000000001414434124600143555ustar00rootroot00000000000000gimp-texturize-2.2/.gitignore000066400000000000000000000001741414434124600163470ustar00rootroot00000000000000*~ build/ intltool-extract intltool-merge intltool-update po/Makefile po/Makefile.in po/Makefile.in.in po/POTFILES po/*.gmo gimp-texturize-2.2/AUTHORS000066400000000000000000000001221414434124600154200ustar00rootroot00000000000000Manu Cornet Jean-Baptiste Rouquier gimp-texturize-2.2/COPYING000066400000000000000000000431311414434124600154120ustar00rootroot00000000000000 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-2.2/LOG000066400000000000000000000362531414434124600147320ustar00rootroot00000000000000This 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. ############################ ########################### ############################ 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-2.2/README.md000066400000000000000000000026671414434124600156470ustar00rootroot00000000000000# 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 Also install `meson`: sudo apt install meson Under a different OS, install - `gimp` (you probably already have that) - `libgimp2.0-dev` (libraries for developping with GIMP) - `intltool` (an internationalization tool). - `meson` (the build tool we use) # Installation To build the plugin: meson setup build cd build meson compile There should now be an executable `texturize` in your current directory. Copy it to your GIMP plugins: mkdir -p ~/.config/GIMP/2.10/plug-ins cp texturize ~/.config/GIMP/2.10/plug-ins 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-2005 * Manu Cornet * Jean-Baptiste Rouquier gimp-texturize-2.2/config.h000066400000000000000000000052621414434124600160000ustar00rootroot00000000000000/* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ /* always defined to indicate that i18n is enabled */ #define ENABLE_NLS 1 /* The gettext translation domain. */ #define GETTEXT_PACKAGE "gimp-texturize" /* Define to 1 if you have the `bind_textdomain_codeset' function. */ #define HAVE_BIND_TEXTDOMAIN_CODESET 1 /* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the CoreFoundation framework. */ /* #undef HAVE_CFLOCALECOPYCURRENT */ /* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in the CoreFoundation framework. */ /* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ /* Define to 1 if you have the `dcgettext' function. */ #define HAVE_DCGETTEXT 1 /* Define if the GNU gettext() function is already present or preinstalled. */ #define HAVE_GETTEXT 1 /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define if your file defines LC_MESSAGES. */ #define HAVE_LC_MESSAGES 1 /* Define to 1 if you have the header file. */ #define HAVE_LOCALE_H 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ #define PACKAGE_NAME "gimp-texturize" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "gimp-texturize 2.2.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "gimp-texturize" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "2.2.1" /* Plug-In major version */ #define PLUGIN_MAJOR_VERSION 2 /* Plug-In micro version */ #define PLUGIN_MICRO_VERSION 1 /* Plug-In minor version */ #define PLUGIN_MINOR_VERSION 2 /* Plug-In name */ #define PLUGIN_NAME PACKAGE_NAME /* Plug-In version */ #define PLUGIN_VERSION PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 gimp-texturize-2.2/extra_config.h000066400000000000000000000002251414434124600171750ustar00rootroot00000000000000// TODO: This should probably live elsewhere and not be hardcoded like that. #define DATADIR "/usr/share" #define LOCALEDIR "/usr/share/locale" gimp-texturize-2.2/help/000077500000000000000000000000001414434124600153055ustar00rootroot00000000000000gimp-texturize-2.2/help/en/000077500000000000000000000000001414434124600157075ustar00rootroot00000000000000gimp-texturize-2.2/help/en/gimp-help.xml000066400000000000000000000002601414434124600203110ustar00rootroot00000000000000 gimp-texturize-2.2/help/en/index.html000066400000000000000000000015541414434124600177110ustar00rootroot00000000000000 Help Page for Texturize
Texturize help page

You are being redirected to the Texturize plugin help page.

gimp-texturize-2.2/help/images/000077500000000000000000000000001414434124600165525ustar00rootroot00000000000000gimp-texturize-2.2/help/images/wilber.png000066400000000000000000000116361414434124600205530ustar00rootroot00000000000000PNG  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-2.2/intltool-extract.in000066400000000000000000000000001414434124600202070ustar00rootroot00000000000000gimp-texturize-2.2/intltool-merge.in000066400000000000000000000000001414434124600176340ustar00rootroot00000000000000gimp-texturize-2.2/intltool-update.in000066400000000000000000000000001414434124600200170ustar00rootroot00000000000000gimp-texturize-2.2/meson.build000066400000000000000000000014171414434124600165220ustar00rootroot00000000000000project('texturize', ['c', 'cpp']) gimp_dep = dependency('gimp-2.0') gtk_dep = dependency('gtk+-2.0') glib_dep = dependency('glib-2.0') libgimpui = meson.get_compiler('c').find_library('libgimpui-2.0') libgimpwidgets = meson.get_compiler('c').find_library('libgimpwidgets-2.0') executable('texturize', [ 'extra_config.h', 'src/compter.c', 'src/compter.h', 'src/graph.cpp', 'src/graphcut.cpp', 'src/initialiser.c', 'src/interface.c', 'src/maxflow.cpp', 'src/main.c', 'src/offset.c', 'src/render.c' ], dependencies: [ gimp_dep, gtk_dep, libgimpui, libgimpwidgets, ]) gimp-texturize-2.2/po/000077500000000000000000000000001414434124600147735ustar00rootroot00000000000000gimp-texturize-2.2/po/ChangeLog000066400000000000000000000031721414434124600165500ustar00rootroot000000000000002003-11-29 Sven Neumann * POTFILES.in: added src/render.c * de.po: updated. 2003-03-05 Sven Neumann * de.po: updated. 2002-06-03 Stanislav Visnovsky * README.translators: Added explanation that this module should not be translated. 2002-04-16 Sven Neumann * Makefile.in.in: updated by intltoolize. * po2tbl.sed.in: removed. 2002-01-15 Sven Neumann * Makefile.in.in * po2tbl.sed.in: new files needed for glib-gettext. * de.po: converted to UTF-8. 2001-12-26 Pablo Saratxaga * az.po: Updated Azeri file 2001-12-21 Stanislav Visnovsky * sk.po: Added Slovak translation from Zdenko Podobny . 2001-11-09 Christian Rose * sv.po: Added Swedish translation. * update.sh: Removed this file, it should be obsoleted by intltool long ago. 2001-11-05 Abel Cheung * zh_TW.po: Updated traditional Chinese translation. 2001-10-20 Christophe Merlet * fr.po: Aded French translation from work of Xavier Delatour . 2001-08-31 Abel Cheung * zh_TW.Big5.po: Rename to ...... * zh_TW.po: This. 2001-08-07 Abel Cheung * zh_TW.Big5.po: New traditional Chinese translation. 2001-07-12 Michael Natterer * de.po: updated translation. 2000-05-24 Michael Natterer * de.po: updated translation. 2000-05-24 Michael Natterer * initial revision. gimp-texturize-2.2/po/POTFILES.in000066400000000000000000000001411414434124600165440ustar00rootroot00000000000000# List of source files containing translatable strings. src/interface.c src/main.c src/render.c gimp-texturize-2.2/po/az.po000066400000000000000000000047761414434124600157630ustar00rootroot00000000000000# 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: 2004-12-29 16:41+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" "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:85 msgid "GIMP Plug-In Template" msgstr "GIMP Əlavə Nümunəsi" #. gimp_scale_entry_new() examples #: src/interface.c:100 msgid "ScaleEntry Examples" msgstr "Şkala Girişi Nümunələri" #: src/interface.c:113 msgid "Dummy 1:" msgstr "Maket 1:" #: src/interface.c:116 msgid "Dummy scale entry 1" msgstr "Maket şkala girişi 1" #: src/interface.c:122 msgid "Dummy 2:" msgstr "Maket 2:" #: src/interface.c:125 msgid "Dummy scale entry 2" msgstr "Maket şkala girişi 2" #: src/interface.c:131 msgid "Dummy 3:" msgstr "Maket 3:" #: src/interface.c:134 msgid "Dummy scale entry 3" msgstr "Maket şkala girişi 3" #. gimp_random_seed_new() example #: src/interface.c:141 msgid "A Random Seed Entry" msgstr "TƏsadüfi Ox Girişi" #. gimp_coordinates_new() example #: src/interface.c:156 msgid "" "A GimpCoordinates Widget\n" "Initialized with the Drawable's Size" msgstr "" "GimpCoordinates Pəncərəziyi\n" "Çəkən Böyüklüyü ilə hazırlandı" #: src/interface.c:174 msgid "Width:" msgstr "En :" #: src/interface.c:178 msgid "Height:" msgstr "Hündürlük :" #. Image and drawable menus #: src/interface.c:186 msgid "Image and Drawable Menu Examples" msgstr "Şəkil və Çəkilən Rəsm Menü Nümunələri" #: src/interface.c:205 msgid "Layers:" msgstr "Laylar:" #: src/interface.c:213 msgid "RGB Images:" msgstr "RGB Şəkillər:" #: src/main.c:137 #, fuzzy msgid "Plug-In Template..." msgstr "GIMP Əlavə Nümunəsi" #: src/render.c:49 msgid "This plug-in is just a dummy. It has now finished doing nothing." msgstr "" #~ msgid "Pixmap Examples" #~ msgstr "Şəkil Nümunələri" #~ msgid "Pixmap Buttons:" #~ msgstr "Şəkil Düymələri:" #~ msgid "With Text" #~ msgstr "Mətnli" #~ msgid "Plain Pixmap:" #~ msgstr "Düz Şəkil:" #, fuzzy #~ msgid "/Filters/Misc/Plug-In Template..." #~ msgstr "/Süzgəclər/Müxtəlif/Əlavə Nümunəsi..." #~ msgid "OK" #~ msgstr "Oldu" #~ msgid "Cancel" #~ msgstr "Ləğv Et" gimp-texturize-2.2/po/de.po000066400000000000000000000041631414434124600157270ustar00rootroot00000000000000# 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: 2004-12-29 16:41+0100\n" "PO-Revision-Date: 2003-11-29 13:36+0100\n" "Last-Translator: Sven Neumann \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:85 msgid "GIMP Plug-In Template" msgstr "GIMP Plug-In Vorlage" #. gimp_scale_entry_new() examples #: src/interface.c:100 msgid "ScaleEntry Examples" msgstr "ScaleEntry Beispiele" #: src/interface.c:113 msgid "Dummy 1:" msgstr "Beispiel 1:" #: src/interface.c:116 msgid "Dummy scale entry 1" msgstr "Scale Entry Beispiel 1" #: src/interface.c:122 msgid "Dummy 2:" msgstr "Beispiel 2:" #: src/interface.c:125 msgid "Dummy scale entry 2" msgstr "Scale Entry Beispiel 2" #: src/interface.c:131 msgid "Dummy 3:" msgstr "Beispiel 3:" #: src/interface.c:134 msgid "Dummy scale entry 3" msgstr "Scale Entry Beispiel 3" #. gimp_random_seed_new() example #: src/interface.c:141 msgid "A Random Seed Entry" msgstr "Eingabefeld für den Anfangswert des Zufallsgenerators" #. gimp_coordinates_new() example #: src/interface.c:156 msgid "" "A GimpCoordinates Widget\n" "Initialized with the Drawable's Size" msgstr "" "Ein GimpCoordinates Widget\n" "(Initialisiert mit der Größe des Drawables)" #: src/interface.c:174 msgid "Width:" msgstr "Breite:" #: src/interface.c:178 msgid "Height:" msgstr "Höhe:" #. Image and drawable menus #: src/interface.c:186 msgid "Image and Drawable Menu Examples" msgstr "Bild und Drawable Menü Beispiele" #: src/interface.c:205 msgid "Layers:" msgstr "Ebenen:" #: src/interface.c:213 msgid "RGB Images:" msgstr "RGB Bilder:" #: src/main.c:137 msgid "Plug-In Template..." msgstr "GIMP Plug-In Vorlage..." #: src/render.c:49 msgid "This plug-in is just a dummy. It has now finished doing nothing." msgstr "Dieses Plug-In tut absolut nichts und damit ist es jetzt fertig." gimp-texturize-2.2/po/fr.po000066400000000000000000000047001414434124600157430ustar00rootroot00000000000000# 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: 2004-12-29 16:41+0100\n" "PO-Revision-Date: 2001-10-20 12:33+0200\n" "Last-Translator: Emmanuel Cornet \n" "Language-Team: GNOME French Team \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:85 msgid "Texturize Plug-in for GIMP" msgstr "Greffon Texturize pour GIMP" msgid "Please set the size of the new image\nand the maximum overlap between patches." msgstr "Veuillez rgler la taille de la nouvelle image et le\nrecouvrement maximal entre les morceaux de texture." msgid "Set the new image's width" msgstr "Rglez la largeur de la nouvelle image" msgid "Set the new image's height" msgstr "Rglez la hauteur de la nouvelle image" msgid "Set the overlap between patches (larger values make better but longer texturizing and tend to make periodic results)" msgstr "Rglez le degr de chevauchement entre les morceaux de texture (plus cette valeur est leve, meilleure sera la texture, mais plus le calcul sera long et le rsultat aura tendance tre priodique)" msgid "Width :" msgstr "Largeur :" msgid "Height :" msgstr "Hauteur :" msgid "Overlap (pixels) :" msgstr "Recouvrement (pixels) :" msgid "Tileable" msgstr "Auto-similaire" msgid "Texturizing image..." msgstr "Calcul de la texture..." msgid "Sorry, the Texturize plugin only supports RGB and grayscale images. Please convert your image to RGB mode first." msgstr "Dsol, 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." msgid "Sorry, the Texturize plugin doesn't support images with an alpha (ie transparency) channel yet. Please flatten your image first." msgstr "Dsol, le plugin Texturize ne fonctionne pas encore avec les images pourvues d'une couche alpha (images transparentes). Veuillez commencer par aplatir l'image." msgid "There was a problem when opening the new image." msgstr "Il y a eu un problme au moment d'ouvrir la nouvelle image." msgid "There was a problem when filling the new image" msgstr "Il y a eu un problme pendant le remplissage de l'image" #~ msgid "OK" #~ msgstr "Valider" #~ msgid "Cancel" #~ msgstr "Annuler" gimp-texturize-2.2/po/gimp20-plugin-template.pot000066400000000000000000000033551414434124600217300ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2004-12-29 16:41+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:85 msgid "GIMP Plug-In Template" msgstr "" #. gimp_scale_entry_new() examples #: src/interface.c:100 msgid "ScaleEntry Examples" msgstr "" #: src/interface.c:113 msgid "Dummy 1:" msgstr "" #: src/interface.c:116 msgid "Dummy scale entry 1" msgstr "" #: src/interface.c:122 msgid "Dummy 2:" msgstr "" #: src/interface.c:125 msgid "Dummy scale entry 2" msgstr "" #: src/interface.c:131 msgid "Dummy 3:" msgstr "" #: src/interface.c:134 msgid "Dummy scale entry 3" msgstr "" #. gimp_random_seed_new() example #: src/interface.c:141 msgid "A Random Seed Entry" msgstr "" #. gimp_coordinates_new() example #: src/interface.c:156 msgid "" "A GimpCoordinates Widget\n" "Initialized with the Drawable's Size" msgstr "" #: src/interface.c:174 msgid "Width:" msgstr "" #: src/interface.c:178 msgid "Height:" msgstr "" #. Image and drawable menus #: src/interface.c:186 msgid "Image and Drawable Menu Examples" msgstr "" #: src/interface.c:205 msgid "Layers:" msgstr "" #: src/interface.c:213 msgid "RGB Images:" msgstr "" #: src/main.c:137 msgid "Plug-In Template..." msgstr "" #: src/render.c:49 msgid "This plug-in is just a dummy. It has now finished doing nothing." msgstr "" gimp-texturize-2.2/po/potfiles000066400000000000000000000000701414434124600165400ustar00rootroot00000000000000 ../src/interface.c \ ../src/main.c \ ../src/render.c gimp-texturize-2.2/po/sk.po000066400000000000000000000047121414434124600157540ustar00rootroot00000000000000# 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: 2004-12-29 16:41+0100\n" "PO-Revision-Date: 2001-12-20 17:22GMT+0100\n" "Last-Translator: Zdenko Podobn \n" "Language-Team: Slovak \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-2\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 0.9.5\n" #: src/interface.c:85 msgid "GIMP Plug-In Template" msgstr "ablna zsuvnch modulov GIMP" #. gimp_scale_entry_new() examples #: src/interface.c:100 msgid "ScaleEntry Examples" msgstr "Prklad na vekos poloky" #: src/interface.c:113 msgid "Dummy 1:" msgstr "Figurna 1:" #: src/interface.c:116 msgid "Dummy scale entry 1" msgstr "Poloka vekosti figurny 1" #: src/interface.c:122 msgid "Dummy 2:" msgstr "Figurna 2" #: src/interface.c:125 msgid "Dummy scale entry 2" msgstr "Poloka vekosti figurny 2" #: src/interface.c:131 msgid "Dummy 3:" msgstr "Figurna 3:" #: src/interface.c:134 msgid "Dummy scale entry 3" msgstr "Poloka vekosti figurny 3" #. gimp_random_seed_new() example #: src/interface.c:141 msgid "A Random Seed Entry" msgstr "Poloka nhodnho semena" #. gimp_coordinates_new() example #: src/interface.c:156 msgid "" "A GimpCoordinates Widget\n" "Initialized with the Drawable's Size" msgstr "" "Prvok GimpCoordinates\n" "Inicializovan s vekosou obrzka" #: src/interface.c:174 msgid "Width:" msgstr "rka:" #: src/interface.c:178 msgid "Height:" msgstr "Vka:" #. Image and drawable menus #: src/interface.c:186 msgid "Image and Drawable Menu Examples" msgstr "Prklady obrzkov a kreslench ponk" #: src/interface.c:205 msgid "Layers:" msgstr "Vrstvy:" #: src/interface.c:213 msgid "RGB Images:" msgstr "RGB Obrzky:" #: src/main.c:137 #, fuzzy msgid "Plug-In Template..." msgstr "ablna zsuvnch modulov GIMP" #: src/render.c:49 msgid "This plug-in is just a dummy. It has now finished doing nothing." msgstr "" #~ msgid "Pixmap Examples" #~ msgstr "Obrzky prkladov" #~ msgid "Pixmap Buttons:" #~ msgstr "Obrzky tlatok:" #~ msgid "With Text" #~ msgstr "S textom" #~ msgid "Plain Pixmap:" #~ msgstr "ist obrzok:" #, fuzzy #~ msgid "/Filters/Misc/Plug-In Template..." #~ msgstr "/Filters/Misc/PlugIn Template..." #~ msgid "OK" #~ msgstr "Ok" #~ msgid "Cancel" #~ msgstr "Zrui" gimp-texturize-2.2/po/sv.po000066400000000000000000000047001414434124600157640ustar00rootroot00000000000000# 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: 2004-12-29 16:41+0100\n" "PO-Revision-Date: 2001-11-09 15:12+0100\n" "Last-Translator: Christian Rose \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:85 msgid "GIMP Plug-In Template" msgstr "Mall fr GIMP-insticksmodul" #. gimp_scale_entry_new() examples #: src/interface.c:100 msgid "ScaleEntry Examples" msgstr "Skalfltexempel" #: src/interface.c:113 msgid "Dummy 1:" msgstr "Exempel 1:" #: src/interface.c:116 msgid "Dummy scale entry 1" msgstr "Exempelskalflt 1" #: src/interface.c:122 msgid "Dummy 2:" msgstr "Exempel 2:" #: src/interface.c:125 msgid "Dummy scale entry 2" msgstr "Exempelskalflt 2" #: src/interface.c:131 msgid "Dummy 3:" msgstr "Exempel 3:" #: src/interface.c:134 msgid "Dummy scale entry 3" msgstr "Exempelskalflt 3" #. gimp_random_seed_new() example #: src/interface.c:141 msgid "A Random Seed Entry" msgstr "Ett slumpfrsflt" #. gimp_coordinates_new() example #: src/interface.c:156 msgid "" "A GimpCoordinates Widget\n" "Initialized with the Drawable's Size" msgstr "" "En GimpCoordinates-widget\n" "Initierad med den ritbara ytans storlek" #: src/interface.c:174 msgid "Width:" msgstr "Bredd:" #: src/interface.c:178 msgid "Height:" msgstr "Hjd:" #. Image and drawable menus #: src/interface.c:186 msgid "Image and Drawable Menu Examples" msgstr "Menyexempel fr bild och ritbara ytan" #: src/interface.c:205 msgid "Layers:" msgstr "Lager:" #: src/interface.c:213 msgid "RGB Images:" msgstr "RGB-bilder:" #: src/main.c:137 #, fuzzy msgid "Plug-In Template..." msgstr "Mall fr GIMP-insticksmodul" #: src/render.c:49 msgid "This plug-in is just a dummy. It has now finished doing nothing." msgstr "" #~ msgid "Pixmap Examples" #~ msgstr "Bildexempel" #~ msgid "Pixmap Buttons:" #~ msgstr "Bildknappar:" #~ msgid "With Text" #~ msgstr "Med text" #~ msgid "Plain Pixmap:" #~ msgstr "Vanlig bild:" #, fuzzy #~ msgid "/Filters/Misc/Plug-In Template..." #~ msgstr "/Filter/Diverse/Mall fr insticksmodul..." #~ msgid "OK" #~ msgstr "OK" #~ msgid "Cancel" #~ msgstr "Avbryt" gimp-texturize-2.2/po/zh_TW.po000066400000000000000000000046611414434124600163750ustar00rootroot00000000000000# 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: 2004-12-29 16:41+0100\n" "PO-Revision-Date: 2001-11-05 21:53+0800\n" "Last-Translator: Abel Cheung \n" "Language-Team: Traditional Chinese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/interface.c:85 msgid "GIMP Plug-In Template" msgstr "GIMP Plug-In 樣本" #. gimp_scale_entry_new() examples #: src/interface.c:100 msgid "ScaleEntry Examples" msgstr "縮放項目例子" #: src/interface.c:113 msgid "Dummy 1:" msgstr "範例 1:" #: src/interface.c:116 msgid "Dummy scale entry 1" msgstr "範例縮放項目 1:" #: src/interface.c:122 msgid "Dummy 2:" msgstr "範例 2:" #: src/interface.c:125 msgid "Dummy scale entry 2" msgstr "範例縮放項目 2:" #: src/interface.c:131 msgid "Dummy 3:" msgstr "範例 3:" #: src/interface.c:134 msgid "Dummy scale entry 3" msgstr "範例縮放項目 3:" #. gimp_random_seed_new() example #: src/interface.c:141 msgid "A Random Seed Entry" msgstr "亂數種子項目" #. gimp_coordinates_new() example #: src/interface.c:156 msgid "" "A GimpCoordinates Widget\n" "Initialized with the Drawable's Size" msgstr "" "一個 GimpCoordinates 視窗元件\n" "設定為可繪物件的尺寸" #: src/interface.c:174 msgid "Width:" msgstr "寬度:" #: src/interface.c:178 msgid "Height:" msgstr "高度:" #. Image and drawable menus #: src/interface.c:186 msgid "Image and Drawable Menu Examples" msgstr "圖像例子" #: src/interface.c:205 msgid "Layers:" msgstr "圖層:" #: src/interface.c:213 msgid "RGB Images:" msgstr "RGB 圖像:" #: src/main.c:137 #, fuzzy msgid "Plug-In Template..." msgstr "GIMP Plug-In 樣本" #: src/render.c:49 msgid "This plug-in is just a dummy. It has now finished doing nothing." msgstr "" #~ msgid "Pixmap Examples" #~ msgstr "Pixmap 例子" #~ msgid "Pixmap Buttons:" #~ msgstr "Pixmap 按鈕:" #~ msgid "With Text" #~ msgstr "配合文字" #~ msgid "Plain Pixmap:" #~ msgstr "普通 Pixmap:" #, fuzzy #~ msgid "/Filters/Misc/Plug-In Template..." #~ msgstr "/濾鏡/其它/增效模組樣本..." #~ msgid "OK" #~ msgstr "確定" #~ msgid "Cancel" #~ msgstr "取消" gimp-texturize-2.2/src/000077500000000000000000000000001414434124600151445ustar00rootroot00000000000000gimp-texturize-2.2/src/adjacency_list/000077500000000000000000000000001414434124600201205ustar00rootroot00000000000000gimp-texturize-2.2/src/adjacency_list/block.h000066400000000000000000000163601414434124600213710ustar00rootroot00000000000000/* 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-2.2/src/adjacency_list/graph.cpp000066400000000000000000000027261414434124600217340ustar00rootroot00000000000000/* 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-2.2/src/adjacency_list/graph.h000066400000000000000000000144211414434124600213740ustar00rootroot00000000000000/* 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-2.2/src/adjacency_list/maxflow.cpp000066400000000000000000000244101414434124600223020ustar00rootroot00000000000000/* 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-2.2/src/block.h000066400000000000000000000162571414434124600164220ustar00rootroot00000000000000/* 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-2.2/src/compter.c000066400000000000000000000017011414434124600167600ustar00rootroot00000000000000#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-2.2/src/compter.h000066400000000000000000000000601414434124600167620ustar00rootroot00000000000000#include gint modulo(gint x, gint m); gimp-texturize-2.2/src/forward_star/000077500000000000000000000000001414434124600176415ustar00rootroot00000000000000gimp-texturize-2.2/src/forward_star/block.h000066400000000000000000000163601414434124600211120ustar00rootroot00000000000000/* 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-2.2/src/forward_star/graph.cpp000066400000000000000000000234031414434124600214500ustar00rootroot00000000000000/* 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-2.2/src/forward_star/graph.h000066400000000000000000000220301414434124600211100ustar00rootroot00000000000000/* 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-2.2/src/forward_star/maxflow.cpp000066400000000000000000000471011414434124600220250ustar00rootroot00000000000000/* 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-2.2/src/graph.cpp000066400000000000000000000026731414434124600167610ustar00rootroot00000000000000/* 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) { 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-2.2/src/graph.h000066400000000000000000000144211414434124600164200ustar00rootroot00000000000000/* 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-2.2/src/graphcut.cpp000066400000000000000000000307051414434124600174720ustar00rootroot00000000000000#include "config.h" #include #include #include #include extern "C" { #include "compter.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 REMPLI 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 decoupe_graphe (int* patch_posn, int width_i, int height_i, int width_p, int height_p, int channels, guchar **rempli, 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 * graphe = new Graph(); // Le graphe à couper 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 = rempli[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); // Si le pixel de l'image n'est pas rempli, on ne fait rien et on passe au suivant if (rempli[x_i][y_i]) { node_of_pixel[x_p * height_p + y_p] = graphe->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 neighbord (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 neighbords (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 (!rempli[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 && rempli[x_i][y_i - 1]) || (make_tileable && y_p != 0 && rempli[x_i][modulo (y_i - 1, height_i)])) { poids = 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)); graphe->add_edge (node_sommet_courant, node_of_pixel[x_p * height_p + y_p - 1], poids, poids); } // 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 && rempli[x_i - 1][y_i]) || (make_tileable && x_p != 0 && rempli[modulo (x_i - 1, width_i)][y_i])) { poids = 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)); graphe->add_edge (node_sommet_courant, node_of_pixel[(x_p - 1) * height_p + y_p], poids, poids); } // 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)) { graphe->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 && !rempli[x_i][y_i - 1]) // North || (y_p != height_p - 1 && y_i != height_i - 1 && !rempli[x_i][y_i + 1]) // South || (x_p != width_p - 1 && x_i != width_i - 1 && !rempli[x_i + 1][y_i]) // East || (x_p != 0 && x_i != 0 && !rempli[x_i - 1][y_i]))) // West || ((make_tileable) && ( (y_p != 0 && !rempli[x_i][modulo (y_i - 1, height_i)]) // North || (y_p != height_p - 1 && !rempli[x_i][modulo (y_i + 1, height_i)]) // South || (x_p != width_p - 1 && !rempli[modulo (x_i + 1, width_i)][y_i]) // East || (x_p != 0 && !rempli[modulo (x_i - 1, width_i)][y_i])))) { // West graphe->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) { graphe->add_tweights (first_node, MAX_CAPACITY, 0); } //////////////////////////////////////////////////////////////////////////////// // Compute the cut. graphe->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 = rempli[x_i][y_i]; if (r) { if (graphe->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 { // (!rempli[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); rempli[x_i][y_i] = REMPLI; } } } //////////////////////////////////////////////////////////////////////////////// // Clean up. delete graphe; free (node_of_pixel); return; } gimp-texturize-2.2/src/initialiser.c000066400000000000000000000007331414434124600176270ustar00rootroot00000000000000#include #include #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; gint i, j; tab = (guchar**) malloc (x * sizeof (guchar*)); for (i = 0; i < x; i++) { tab[i] = (guchar*) malloc (y * sizeof (guchar)); } for (i = 0; i < x; i++) { for (j = 0; j < y; j++) tab[i][j] = 0; } return tab; } gimp-texturize-2.2/src/interface.c000066400000000000000000000116031414434124600172510ustar00rootroot00000000000000#include "config.h" #include #include #include #include "main.h" #include "interface.h" #include "texturize.h" #include "plugin-intl.h" /* Constants */ #define SCALE_WIDTH 180 #define SPIN_BUTTON_WIDTH 75 #define RANDOM_SEED_WIDTH 100 /* Local function prototypes */ /* Local variables */ static PlugInUIVals *ui_state = NULL; /* Public functions */ gboolean dialog (gint32 image_ID, GimpDrawable *drawable, PlugInVals *vals, PlugInImageVals *image_vals, PlugInDrawableVals *drawable_vals, PlugInUIVals *ui_vals) { GtkWidget *dlg; GtkWidget *main_vbox; GtkWidget *frame; GtkWidget *table; GtkWidget *tileable_checkbox; GtkObject *adj; gint row; gboolean run = FALSE; image_vals->width_p = gimp_image_width(image_ID); image_vals->height_p = gimp_image_height(image_ID); // Here are the default values of the dialog box if (vals->width_i < image_vals->width_p || vals->height_i < image_vals->height_p) { vals->width_i = 2 * image_vals->width_p; vals->height_i = 2 * image_vals->height_p; } vals->overlap = 100; vals->make_tileable = FALSE; ui_state = ui_vals; gimp_ui_init (PLUGIN_NAME, TRUE); dlg = gimp_dialog_new( _("Texturize Plug-in for GIMP"), PLUGIN_NAME, NULL, GTK_DIALOG_MODAL, gimp_standard_help_func, "plug-in-template", GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); main_vbox = gtk_vbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), main_vbox); // Size of the new image ? frame = gimp_frame_new (_("Please set the size of the new image\nand the maximum overlap between patches.")); gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); table = gtk_table_new (3, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_container_add (GTK_CONTAINER (frame), table); gtk_widget_show (table); row = 0; // Width of the new image? adj = gimp_scale_entry_new(GTK_TABLE (table), 0, row++,_("Width :"), SCALE_WIDTH, SPIN_BUTTON_WIDTH, vals->width_i, image_vals->width_p, 20 * image_vals->width_p, 1, 10, 0, TRUE, 0, 0, _("Set the new image's width"), NULL); g_signal_connect(adj, "value_changed", G_CALLBACK (gimp_int_adjustment_update), &vals->width_i); // Height of the new image? adj = gimp_scale_entry_new(GTK_TABLE (table), 0, row++,_("Height :"), SCALE_WIDTH, SPIN_BUTTON_WIDTH, vals->height_i, image_vals->height_p, 20*image_vals->height_p, 1, 10, 0, TRUE, 0, 0, _("Set the new image's height"), NULL); g_signal_connect(adj, "value_changed", G_CALLBACK (gimp_int_adjustment_update), &vals->height_i); // Patch overlap? adj = gimp_scale_entry_new(GTK_TABLE (table), 0, row++, _("Overlap (pixels) :"), SCALE_WIDTH, SPIN_BUTTON_WIDTH, vals->overlap, MIN(25, MIN(image_vals->width_p - 1 ,image_vals->height_p - 1)), MIN(image_vals->width_p, image_vals->height_p), 5, 10, 0, TRUE, 0, 0, _("Set the overlap between patches (larger values make better " "but longer texturizing " "and tend to make periodic results)"), NULL); g_signal_connect(adj, "value_changed", G_CALLBACK (gimp_int_adjustment_update), &vals->overlap); // Tileable texture? tileable_checkbox = gtk_check_button_new_with_mnemonic(_("_Tileable")); gtk_box_pack_start(GTK_BOX (main_vbox), tileable_checkbox, FALSE, FALSE, 0); gtk_widget_show(tileable_checkbox); gimp_help_set_help_data(tileable_checkbox, _("Selects if to create a tileable texture"), NULL); g_signal_connect(GTK_WIDGET (tileable_checkbox), "toggled", G_CALLBACK (gimp_toggle_button_update), &vals->make_tileable); // Show the main containers. gtk_widget_show(main_vbox); gtk_widget_show(dlg); run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK); gtk_widget_destroy (dlg); return run; } gimp-texturize-2.2/src/interface.h000066400000000000000000000005721414434124600172610ustar00rootroot00000000000000#ifndef __INTERFACE_H__ #define __INTERFACE_H__ /* Public functions */ gboolean dialog (gint32 image_ID, GimpDrawable *drawable, PlugInVals *vals, PlugInImageVals *image_vals, PlugInDrawableVals *drawable_vals, PlugInUIVals *ui_vals); #endif /* __INTERFACE_H__ */ gimp-texturize-2.2/src/main.c000066400000000000000000000131611414434124600162360ustar00rootroot00000000000000#include "config.h" #include "extra_config.h" #include #include #include #include "main.h" #include "interface.h" #include "render.h" #include "texturize.h" #include "plugin-intl.h" /* Constants */ #define PROCEDURE_NAME "gimp_plugin_texturize" #define DATA_KEY_VALS "plug_in_texturize" #define DATA_KEY_UI_VALS "plug_in_texturize_ui" #define PARASITE_KEY "plug-in-texturize-options" /* Local function prototypes */ static void query (void); static void run(const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); /* Local variables */ const PlugInVals default_vals = { 0, // width_i 0, // height_i 5, // overlap FALSE, // tileable }; const PlugInImageVals default_image_vals = { 0, // image_id 500, // width_p 500 // height_p }; const PlugInDrawableVals default_drawable_vals = { 0 }; const PlugInUIVals default_ui_vals = { TRUE }; static PlugInVals vals; static PlugInImageVals image_vals; static PlugInDrawableVals drawable_vals; static PlugInUIVals ui_vals; GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; MAIN () static void query(void) { gchar *help_path; gchar *help_uri; static GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }, { GIMP_PDB_INT32, "width_i", "New image width" }, { GIMP_PDB_INT32, "height_i", "New image height" }, { GIMP_PDB_INT32, "overlap", "Overlap" }, { GIMP_PDB_INT32, "tileable", "Tileable" }, }; gimp_plugin_domain_register(PLUGIN_NAME, LOCALEDIR); help_path = g_build_filename(DATADIR, "help", NULL); help_uri = g_filename_to_uri(help_path, NULL, NULL); g_free (help_path); gimp_plugin_help_register("http://www.manucornet.net/Informatique/Texturize.php", help_uri); gimp_install_procedure ( PROCEDURE_NAME, "Blurb", "Help", "Emmanuel Cornet ", "Jean-Baptiste Rouquier ", "2007", N_("Texturize..."), "RGB*, GRAY*, INDEXED*", GIMP_PLUGIN, G_N_ELEMENTS (args), 0, args, NULL); gimp_plugin_menu_register (PROCEDURE_NAME, "/Filters/Map/"); } static void run (const gchar *name, gint n_params, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpDrawable *drawable; gint32 image_ID; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint32 new_image_id=0; *nreturn_vals = 1; *return_vals = values; /* Initialize i18n support */ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); #ifdef HAVE_BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); #endif textdomain (GETTEXT_PACKAGE); run_mode = (GimpRunMode)param[0].data.d_int32; image_ID = param[1].data.d_int32; drawable = gimp_drawable_get (param[2].data.d_drawable); /* Initialize with default values */ vals = default_vals; image_vals = default_image_vals; drawable_vals = default_drawable_vals; ui_vals = default_ui_vals; image_vals.width_p = gimp_image_width(image_ID); image_vals.height_p = gimp_image_height(image_ID); if (strcmp (name, PROCEDURE_NAME) == 0) { switch (run_mode) { case GIMP_RUN_NONINTERACTIVE: if (n_params != 7) { status = GIMP_PDB_CALLING_ERROR; } else { vals.width_i = param[3].data.d_int32; vals.height_i = param[4].data.d_int32; vals.overlap = param[5].data.d_int32; vals.make_tileable = param[6].data.d_int32; // vals.random_seed = param[7].data.d_int32; // if (vals.random_seed) // vals.seed = g_random_int (); } break; case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data (DATA_KEY_VALS, &vals); gimp_get_data (DATA_KEY_UI_VALS, &ui_vals); if (! dialog(image_ID, drawable, &vals, &image_vals, &drawable_vals, &ui_vals)) { status = GIMP_PDB_CANCEL; } break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data (DATA_KEY_VALS, &vals); // if (vals.random_seed) // vals.seed = g_random_int (); break; default: break; } } else { status = GIMP_PDB_CALLING_ERROR; } if (status == GIMP_PDB_SUCCESS) { new_image_id = render (image_ID, drawable, &vals, &image_vals, &drawable_vals); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); if (run_mode == GIMP_RUN_INTERACTIVE) { gimp_set_data (DATA_KEY_VALS, &vals, sizeof (vals)); gimp_set_data (DATA_KEY_UI_VALS, &ui_vals, sizeof (ui_vals)); } gimp_drawable_detach (drawable); // If new_image_id = -1, there has been an error (indexed colors ?). if (new_image_id != -1) gimp_display_new(new_image_id); else { g_message(_("There was a problem when opening the new image.")); } } values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; } gimp-texturize-2.2/src/main.h000066400000000000000000000012041414434124600162360ustar00rootroot00000000000000#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-2.2/src/maxflow.cpp000066400000000000000000000253571414434124600173410ustar00rootroot00000000000000/* 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-2.2/src/offset.c000066400000000000000000000073571414434124600166120ustar00rootroot00000000000000#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-2.2/src/plugin-intl.h000066400000000000000000000005311414434124600175560ustar00rootroot00000000000000#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-2.2/src/render.c000066400000000000000000000241501414434124600165710ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include "plugin-intl.h" #include "main.h" #include "texturize.h" /* Public functions */ gint32 render(gint32 image_ID, GimpDrawable *drawable, PlugInVals *vals, PlugInImageVals *image_vals, PlugInDrawableVals *drawable_vals) { ///////////////////// //////////////////// ///////////////////// Variable declaration //////////////////// ///////////////////// //////////////////// gint32 new_image_id = 0; gint32 new_layer_id = 0; GimpDrawable* new_drawable; GimpImageBaseType image_type = GIMP_RGB; GimpImageType drawable_type = GIMP_RGB_IMAGE; gint32 drawable_id = drawable->drawable_id; GimpPixelRgn rgn_in, rgn_out; gint width_i, height_i, width_p, height_p; gint channels; // 3 for RVB, 1 for grayscale gint k, x_i, y_i; // Many counters guchar* patch; // To store the original image guchar* image; // Buffer to store the current image in a 3d array // 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 // line 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 line). guchar** filled; // To keep track of which pixels have been filled. // 0 iff the pixel isn't filled // 1 if the pixel is filled and wihout 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?". int cur_posn[2]; // The position of the pixel to be filled. int patch_posn[2]; // Where we'll paste the patch to fill this pixel. 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; // substracted from cur_posn to get patch_posn. float progress; // Progress bar displayed during computation. gimp_progress_init ("Texturizing image..."); /////////////////////// ////////////////////// /////////////////////// Image dimensions ////////////////////// /////////////////////// ////////////////////// width_i = vals->width_i; height_i = vals->height_i; width_p = image_vals->width_p; height_p = image_vals->height_p; channels = gimp_drawable_bpp (drawable->drawable_id); if (width_i == width_p && height_i == height_p) { g_message(_("New image size and original image size are the same.")); return -1; } else if (width_i <= width_p || height_i <= height_p) { g_message(_("New image size is too small.")); return -1; } //g_warning ("Tileable : %i\n", vals->make_tileable); /* Figure out the type of the new image according to the original image */ switch (gimp_drawable_type (drawable_id)) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: image_type = GIMP_RGB; drawable_type = GIMP_RGB_IMAGE; break; case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE: image_type = GIMP_GRAY; drawable_type = GIMP_GRAY_IMAGE; 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 mode first.")); return -1; } if (gimp_drawable_has_alpha (drawable_id)) { g_message (_("Sorry, the Texturize plugin doesn't support images" " with an alpha (ie transparency) channel yet." " Please flatten your image first.")); return -1; } //////////////////////////// /////////////////////////// //////////////////////////// Recouvrement /////////////////////////// //////////////////////////// /////////////////////////// /* 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. x_off_min = MIN (vals->overlap, width_p - 1); y_off_min = MIN (vals->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 */ ////////////////// ///////////////// ////////////////// New image, initializations ///////////////// ////////////////// ///////////////// // Create a new image with only one layer. new_image_id = gimp_image_new(width_i,height_i,image_type); new_layer_id = gimp_layer_new(new_image_id, "Texture", width_i, height_i, drawable_type, 100, GIMP_NORMAL_MODE); gimp_image_add_layer(new_image_id, new_layer_id, 0); new_drawable = gimp_drawable_get(new_layer_id); // Initialize in and out regions. gimp_pixel_rgn_init(&rgn_out, new_drawable, 0, 0, width_i, height_i, TRUE, TRUE); gimp_pixel_rgn_init(&rgn_in, drawable, 0, 0, width_p, height_p, FALSE, FALSE); // Allocate some memory for everyone. patch = g_new(guchar,width_p * height_p * channels); image = g_new(guchar,width_i * height_i * channels); filled = init_guchar_tab_2d (width_i, height_i); coupe_h_here = g_new(guchar, width_i * height_i * channels); coupe_h_west = g_new(guchar, width_i * height_i * channels); coupe_v_here = g_new(guchar, width_i * height_i * channels); coupe_v_north = g_new(guchar, width_i * height_i * channels); // For security, initialize everything to 0. for (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; ////////////////// ///////////////// ////////////////// Cleaning up of the new image ///////////////// ////////////////// ///////////////// // Retrieve the initial image into the patch. gimp_pixel_rgn_get_rect (&rgn_in, patch, 0, 0, width_p, height_p); // Then paste a first patch at position (0,0) of the out image. gimp_pixel_rgn_set_rect (&rgn_out, patch, 0, 0, width_p, height_p); // And declare we have already filled in the corresponding pixels. for (x_i = 0; x_i < width_p; x_i++) { for (y_i = 0; y_i < height_p; y_i++) filled[x_i][y_i] = 1; } // Retrieve all of the current image into image. gimp_pixel_rgn_get_rect (&rgn_out, image, 0, 0, width_i, height_i); ///////////////////////// //////////////////////// ///////////////////////// The big loop //////////////////////// ///////////////////////// //////////////////////// // The current position : (0,0) cur_posn[0] = 0; cur_posn[1] = 0; int count = count_filled_pixels (filled,width_i,height_i); int count_max = width_i * height_i; while (1) { if (count >= count_max) break; /* 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, width_p, height_p, width_i, height_i, 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, vals->make_tileable); decoupe_graphe(patch_posn, width_i, height_i, width_p, height_p, channels, filled, image, patch, coupe_h_here, coupe_h_west, coupe_v_here, coupe_v_north, vals->make_tileable, FALSE); // Display progress to the user. count = count_filled_pixels (filled, width_i, height_i); progress = ((float) count) / ((float) count_max); progress = (progress > 1.0f)? 1.0f : ((progress < 0.0f)? 0.0f : progress); gimp_progress_update(progress); } ////////////////////// ///////////////////// ////////////////////// Last clean up ///////////////////// ////////////////////// ///////////////////// /* // To see where cuts are. guchar * image_coupes; image_coupes = g_new(guchar, width_i*height_i*channels); for (k=0;kdrawable_id, TRUE); gimp_drawable_update(new_drawable->drawable_id, 0, 0, width_i, height_i); gimp_drawable_detach(new_drawable); gimp_displays_flush(); g_free(patch); g_free(coupe_h_here); g_free(coupe_h_west); g_free(coupe_v_here); g_free(coupe_v_north); /* Finally return the ID of the new image, for the main function to display it */ return new_image_id; } gimp-texturize-2.2/src/render.h000066400000000000000000000004671414434124600166030ustar00rootroot00000000000000#ifndef __RENDER_H__ #define __RENDER_H__ /* Public functions */ gint32 render(gint32 image_ID, GimpDrawable *drawable, PlugInVals *vals, PlugInImageVals *image_vals, PlugInDrawableVals *drawable_vals); #endif /* __RENDER_H__ */ gimp-texturize-2.2/src/texturize.h000066400000000000000000000030351414434124600173610ustar00rootroot00000000000000#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 decoupe_graphe( 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-2.2/todo000066400000000000000000000010301414434124600152370ustar00rootroot00000000000000Improve 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.