<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>C++, Qt et GPU &#187; C++</title>
	<atom:link href="https://blog.developpez.com/gpu/?cat=5&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/gpu</link>
	<description></description>
	<lastBuildDate>Fri, 24 May 2013 17:02:21 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Développez en natif pour Android avec Qt 5.1</title>
		<link>https://blog.developpez.com/gpu/?p=522</link>
		<comments>https://blog.developpez.com/gpu/?p=522#comments</comments>
		<pubDate>Thu, 23 May 2013 11:24:04 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Facile]]></category>
		<category><![CDATA[Qt5]]></category>
		<category><![CDATA[QtQuick]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=522</guid>
		<description><![CDATA[La sortie de Qt 5.1 est prévue en release candidat pour le 27 mai. Cette version propose le support d&#8217;Android et iOS, bien qu&#8217;il était déjà possible de porter sur Android avec Qt 4.8 et le projet Necessitas. Je vais &#8230; <a href="https://blog.developpez.com/gpu/?p=522">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>La sortie de Qt 5.1 est prévue en release candidat pour le 27 mai. Cette version propose le support d&rsquo;Android et iOS, bien qu&rsquo;il était  déjà possible de porter sur Android avec Qt 4.8 et le projet <a href="http://www.developpez.net/forums/d1278983/plateformes/android-necessitas-port-android-qt-integre-au-qt-project/">Necessitas</a>. Je vais vous présenter ici comment créer une application Qt pour Android sous Ubuntu 13.04.</p>
<p><span id="more-522"></span></p>
<h1>Vue d&rsquo;ensemble</h1>
<p>Avant de commencer, une remarque sur Windows. À priori, il est possible de travailler sur Windows. Cependant, les binaires Qt pour Android ne sont disponibles que pour Linux. Comme la compilation de Qt pour Windows est pénible et que je ne l&rsquo;ai pas faite depuis longtemps, je présente seulement l&rsquo;utilisation de Linux. Pour ceux qui aimeraient pouvoir utiliser Windows&#8230; tant pis pour vous <img src="https://blog.developpez.com/gpu/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>Je n&rsquo;avais pas testé Qt sur Android depuis quelques temps, en fait depuis Qt 4.8 et Necessitas. Du coup, j&rsquo;ai découvert le portage Android dans Qt 5.1 en même temps que je prenais des notes pour cet article. Comme base de travail, je suis parti des pages des pages de documentation de Qt : <a href="http://doc-snapshot.qt-project.org/qt5-stable/qtdoc/android-support.html">Qt &#8211; Android support</a>.</p>
<p>L&rsquo;utilisation de Qt sur Android nécessite l&rsquo;installation de plusieurs outils :</p>
<ul>
<li>la version de <a href="http://download.qt-project.org/development_releases/qt/5.1/5.1.0-beta1/">Qt 5.1 pour Android</a> : ok, on s&rsquo;y attendait un peu. Contient la bibliothèque complète et Qt Creator ;</li>
<li>le <a href="http://developer.android.com/sdk/index.html">kit de développement (SDK) pour Android</a> : permet de créer des applications Java pour Android et un simulateur pour tester ses applications ;</li>
<li>le <a href="http://developer.android.com/tools/sdk/ndk/index.html">kit de développement natif (NDK) pour Android</a> : permet de créer des applications C++ pour Android ;</li>
<li>l&rsquo;<a href="http://openjdk.java.net/install/index.html">environnement d&rsquo;exécution de Java (JRE)</a> : nécessaire pour le SDK Android ;</li>
<li><a href="http://ant.apache.org/bindownload.cgi">Apache Ant</a> : pour faire beau. (bon ok, ce n&rsquo;est pas que pour faire beau, ça sert au déploiement. Mais comme c&rsquo;est un outil Java, je ne connais pas du tout).</li>
</ul>
<p>Dans ce tutoriel, je vais montrer l&rsquo;utilisation de Qt Creator, mais il doit être possible d&rsquo;utiliser d&rsquo;autres outils.</p>
<h1>Installations</h1>
<h2>Installation de OpenJDK 7</h2>
<p>Pour commencer, il faut installer l&rsquo;environnement d&rsquo;exécution de Java. Il est fort possible qu&rsquo;il soit déjà installé. Dans la documentation, il est indiqué qu&rsquo;il faut par exemple OpenJDK en version 6 ou plus récente.</p>
<p><del datetime="2013-05-24T14:58:09+00:00">Personnellement, j&rsquo;avais la version 7. Cependant, j&rsquo;ai eu des problème avec Apache Ant, qui me retournait une erreur. J&rsquo;ai donc dû installer la version 6 en plus. Comme j&rsquo;ai pas mal bidouillé, je ne suis pas sûr de la procédure. Si des personnes testent et remarquent des problèmes, n&rsquo;hésitez pas à me le signaler.</del></p>
<p><em>Merci à <a href="http://www.developpez.net/forums/u657013/rotoom/">rotoOm</a> qui m&rsquo;a indiqué l&rsquo;origine du problème. Il suffit en fait d&rsquo;installer le JDK en plus du JRE. Merci à lui pour l&rsquo;information.</em></p>
<p>Vous pouvez tester si Java est installé avec la ligne de commande suivante :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">java -version</div></div>
<p>Pour l&rsquo;installation, j&rsquo;ai donc utilisé les paquets fournis par Ubuntu. Il faut donc juste taper ces lignes de commande :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">sudo apt-get install openjdk-6-jre<br />
sudo apt-get install openjdk-7-jdk</div></div>
<p>Pour les autres systèmes Linux, vous pouvez consulter la page de documentation d&rsquo;OpenJDK : <a href="http://openjdk.java.net/install/index.html">How to download and install prebuilt OpenJDK packages</a>.</p>
<h2>Installation d&rsquo;Apache Ant</h2>
<p>Pour l&rsquo;installation d&rsquo;Apache Ant, même méthode, j&rsquo;utilise les paquets Ubuntu :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">sudo apt-get -u install ant</div></div>
<p>Pour tester l&rsquo;installation d&rsquo;Apache Ant, vous pouvez taper la ligne de commande suivante :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ant -version</div></div>
<h2>Installation du SDK Android</h2>
<h3>Pourquoi installer le kit de développement Java ?</h3>
<p>Pour commencer, on ne peut se poser une question : pourquoi installer le kit de développement Java et le kit de développement natif et pas simplement ce dernier ? La réponse est simple : parce qu&rsquo;on ne peut développer que en Java pour Android !</p>
<p>Ok&#8230;</p>
<p>Donc cela veut dire qu&rsquo;il faudra utiliser le binding Java de Qt ? On parlait pas de développement C++ dans ce tutoriel ?</p>
<p>Oui, pas d&rsquo;inquiétude <img src="https://blog.developpez.com/gpu/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley" /> En fait, il y a un point à savoir sur les applications natives C++ sur Android. C&rsquo;est une application Java qui appelle un &laquo;&nbsp;module&nbsp;&raquo; écrit en C++ (on utilise le terme &laquo;&nbsp;activité&nbsp;&raquo; en Java). Avec Qt, on va donc simplement créer notre code C++/Qt et Qt Creator va s&rsquo;occuper de créer l&rsquo;application Java qui appelle notre code.</p>
<p>Ouf !</p>
<h3>Installation</h3>
<p>L&rsquo;installation du SDK Android est simple aussi&#8230; puisqu&rsquo;il n&rsquo;y a pas d&rsquo;installation à faire. Vous devez simplement télécharger le <a href="http://developer.android.com/sdk/index.html">kit de développement pour Android</a> et décompresser l&rsquo;archive. Ce SDK contient les outils de développement Android, des plateformes de compilation, l&rsquo;éditeur Eclipse ADT (Android Developer Tools, configuré spécialement pour Android), un simulateur Android pour tester ses applications.</p>
<p>Pour installer d&rsquo;autres outils, par exemple d&rsquo;autres versions de plateforme, vous pouvez utiliser le SDK Manager. Dans Eclipse ADT, allez dans le menu Windows puis Android SDK Manager.</p>
<h3>Tester Eclipse</h3>
<p>Comme je suis curieux et que j&rsquo;ai envie de voir un peu comment fonctionne une application Java sur Android, j&rsquo;ai testé un peu Eclipse et Java. Ceux qui ne sont pas intéressés, vous pouvez passer directement à l&rsquo;installation du NDK.</p>
<p>Pour cela, j&rsquo;ai simplement suivi le tutoriel : <a href="http://developer.android.com/training/basics/firstapp/index.html">Building Your First App</a> (il faut reconnaître que la documentation est très claire et simple à suivre, même pour un développeur C++ comme moi).</p>
<p>Allez dans le répertoire d&rsquo;installation du SDK Android puis dans le répertoire eclipse/. Celui-ci contient un binaire eclipse, que vous lancez. Si c&rsquo;est la première utilisation d&rsquo;Eclipse, il va falloir faire quelques réglages de configuration. En premier le Workspace (l&rsquo;espace de travail d&rsquo;Eclipse, dans lequel il range les fichiers). Personnellement, j&rsquo;ai accepté toutes les valeurs par défaut.</p>
<p>Une fois qu&rsquo;Eclipse est lancé, créez un nouveau projet de type &laquo;&nbsp;Android Application Project&nbsp;&raquo; dans Fichier puis Créer un projet. Un dialogue permet de donner un nom à votre application (c&rsquo;est le nom qui apparaîtra sur le téléphone), la version minimale d&rsquo;Android à prendre en charge et la version Android de destination. J&rsquo;ai laissé par défaut. Vous pouvez ensuite choisir l&rsquo;icône de l&rsquo;application et le type d&rsquo;activité. Choisissez BlanckActivity, qui va créer une activité &laquo;&nbsp;hello world&nbsp;&raquo;.</p>
<p>Après quelques minutes (je vais rien dire que la lenteur d&rsquo;Eclipse&#8230;), un projet est créé, qui contient quelques fichiers et répertoires. Les plus importants sont :</p>
<ul>
<li>le fichier AndroidManifest.xml : contient les informations sur l&rsquo;application, en particulier les versions minimale et ciblée de la plateforme ;</li>
<li>le répertoire res/layout/ : contient les interfaces de l&rsquo;application, décrites dans des fichiers XML ;</li>
<li>le répertoire src/ : contient les sources Java du projet. Dans ce projet par défaut, il lance simplement une activité, qui affiche l&rsquo;interface décrite dans le fichier XML de layouts/ ;</li>
<li>les répertoires res/drawable-xxx/ : contient les images de l&rsquo;application.</li>
</ul>
<p>Comme c&rsquo;est une application Android, vous ne pouvez pas simplement la lancer comme une application de Bureau classique. Il existe deux méthodes pour la lancer : utiliser un simulateur ou déployer sur un téléphone.</p>
<h3>Tester l&rsquo;application sur simulateur</h3>
<p>Pour cela, il faut dans un premier temps créer un simulateur. Le SDK Android est fournit avec un gestionnaire, permettant de créer différentes configurations pour le simulateur, permettant ainsi de tester l&rsquo;application sur plusieurs types de téléphone.</p>
<p>Allez dans le menu Windows puis Android Virtual Device Manager. Dans le dialogue, créez un nouveau périphérique en cliquant sur &laquo;&nbsp;New&nbsp;&raquo;, puis configurez selon le type de téléphone que vous voulez tester. Par exemple, comme j&rsquo;ai un téléphone Samsung Galaxy S1, j&rsquo;ai choisit un écran 4&Prime; en 480*800. Après la création du périphérique, lancez-le.</p>
<p>Lancez ensuite l&rsquo;application en ouvrant le fichier java (dans src/) puis en cliquant sur le bouton Run. Un dialogue &laquo;&nbsp;Run As&#8230;&nbsp;&raquo; s&rsquo;ouvre pour choisir comment exécuter l&rsquo;application. Choisissez Android Application. Normalement, l&rsquo;application devrait se lancer.</p>
<h3>Tester l&rsquo;application sur téléphone</h3>
<p>Bon, un simulateur, c&rsquo;est bien, mais on aimerait avoir l&rsquo;application sur un vrai téléphone pour tester. Ce n&rsquo;est pas très compliqué non plus, mais il va falloir faire une manipulation sur le téléphone. Celle-ci permet d&rsquo;activer les fonctionnalités de développement du téléphone, en particulier le Debug Mode USB, qui permet de déployer des applications via un cable USB.</p>
<p>Pour Android 4.2, allez dans les paramètres puis dans le menu À propos du téléphone. Cliquez plusieurs fois sur Numéro de build par activer le mode développeur. Vous allez avoir un message confirmant le passage en mode Debug. Revenez ensuite à l&rsquo;écran précédent et allez dans les options de développeur. Activez le mode Debug USB. Pour les autres versions d&rsquo;Android, vous pouvez consulter la page suivante : <a href="http://developer.android.com/training/basics/firstapp/running-app.html#RealDevice">Run on a Real Device</a>.</p>
<p>Pour terminer, il suffit de brancher le téléphone sur l&rsquo;ordinateur avec un câble USB puis de lancer l&rsquo;application comme précédemment. L&rsquo;application devrait se lancer sur le téléphone.</p>
<h2>Installation du NDK Android</h2>
<p>Rien de compliqué ici non plus, il n&rsquo;y a pas d&rsquo;installation à faire, il faut simplement décompresser l&rsquo;archive.</p>
<h2>Installation de Qt Android</h2>
<p>Le plus simple, comme indiqué au début de ce tutoriel, est d&rsquo;utiliser les binaires fournis de <a href="http://download.qt-project.org/development_releases/qt/5.1/5.1.0-beta1/">Qt 5.1 pour Android</a>. Si vous n&rsquo;utilisez pas Ubuntu, il vous faudra compiler Qt vous-même. Bon courage <img src="https://blog.developpez.com/gpu/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>Lancez le binaire Qt pour Android. Le processus d&rsquo;installation est classique, avec une particularité : le SDK propose par défaut l&rsquo;installation de Qt Desktop, mais pour Android (versions ARM ou x86), il faut l&rsquo;activer soi-même. Une fois l&rsquo;installation finie, allez dans les répertoires Tools, QtCreator, bin puis lancez qtcreator.</p>
<p>La première étape va être de configurer Android. Pour cela, allez dans le menu Outils puis Options. Dans la liste de gauche, allez dans Android, puis configurez les répertoires du SDK et du NDK. Cochez la case pour laisser Qt Creator créer automatiquement les kits de compilation.</p>
<p><a href="http://blog.developpez.com/gpu/files/2013/05/android.png"><img src="http://blog.developpez.com/gpu/files/2013/05/android-300x188.png" alt="android" width="300" height="188" class="aligncenter size-medium wp-image-555" /></a></p>
<p>Il faut ensuite créer un périphérique pour le simulateur si ce n&rsquo;est pas encore fait. Pour cela, cliquez sur le bouton Start Android AVD Manager. </p>
<p><a href="http://blog.developpez.com/gpu/files/2013/05/avd-manager.png"><img src="http://blog.developpez.com/gpu/files/2013/05/avd-manager.png" alt="avd manager" width="680" height="500" class="aligncenter size-full wp-image-556" /></a></p>
<p>Cliquez sur New pour créer un nouveau périphérique. Choisissez le type de Device (pour simuler mon Galaxy S, j&rsquo;ai donc choisit un 4&Prime; en 480*800). N&rsquo;oubliez pas d&rsquo;activer la prise en charge du GPU (Use Host GPU) pour que Qt puisse utiliser OpenGL ES.</p>
<p><a href="http://blog.developpez.com/gpu/files/2013/05/new-device.png"><img src="http://blog.developpez.com/gpu/files/2013/05/new-device.png" alt="new device" width="480" height="709" class="aligncenter size-full wp-image-558" /></a></p>
<p>Pour créer une application Qt, allez dans le menu Fichier puis Nouveau projet. Choisissez un projet compatible avec Android (par exemple Application Graphique Qt ou Application Qt Quick 2 (élément de base)).</p>
<p><a href="http://blog.developpez.com/gpu/files/2013/05/new-project.png"><img src="http://blog.developpez.com/gpu/files/2013/05/new-project.png" alt="new project" width="1440" height="900" class="aligncenter size-full wp-image-559" /></a></p>
<p>Dans un premier temps, le simulateur se lance.</p>
<p><a href="http://blog.developpez.com/gpu/files/2013/05/simulator.png"><img src="http://blog.developpez.com/gpu/files/2013/05/simulator.png" alt="simulator" width="481" height="827" class="aligncenter size-full wp-image-561" /></a></p>
<p>Puis l&rsquo;application à la fin du déploiement.</p>
<p><a href="http://blog.developpez.com/gpu/files/2013/05/run-simulator.png"><img src="http://blog.developpez.com/gpu/files/2013/05/run-simulator.png" alt="run simulator" width="714" height="829" class="aligncenter size-full wp-image-560" /></a></p>
<p>Pour déployer sur un téléphone (préalablement passé en mode Debug USB, voir au début), il suffit simplement de le brancher, Qt Creator lancera alors l&rsquo;application dessus.</p>
<p><a href="http://blog.developpez.com/gpu/files/2013/05/IMG_4736.jpg"><img src="http://blog.developpez.com/gpu/files/2013/05/IMG_4736.jpg" alt="IMG_4736" width="477" height="900" class="aligncenter size-full wp-image-557" /></a></p>
<h1>Conclusion</h1>
<p>Rien de très compliqué finalement. Le déploiement est relativement simple une fois que les outils sont configurés. Reste plus qu&rsquo;à tester les performances. <img src="https://blog.developpez.com/gpu/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley" /></p>
<p>Pour ceux qui sont intéressés par Qt Quick, je rappelle le livre auquel je participe : <a href="http://www.d-booker.fr/110-qt-5-les-essentiels.html">Créer des applications avec Qt 5 &#8211; Les essentiels</a>. J&rsquo;ai en particulier participé à la rédaction de Qt Quick. C&rsquo;est encore une pré-version du livre final, mais il y a déjà pas mal de choses sur Qt Quick. Et n&rsquo;hésitez pas à me dire s&rsquo;il y a des points que vous souhaiteriez voir abordés dans ce livre.</p>
<p>Si vous êtes intéressé par Qt Quick sur mobile, je fais une présentation le 5 juin à Paris. Contactez-moi par MP pour plus d&rsquo;informations.</p>
<p>Merci à <a href="http://www.developpez.net/forums/u39401/prgasp77/">prgasp77</a> et <a href="http://www.developpez.net/forums/u329028/winjerome/">winjerome</a> pour leur relecture orthographique.</p>
<p><a href="http://www.developpez.net/forums/d1346162/qt/developpez-en-natif-pour-android-avec-qt-51/">N&rsquo;hésitez pas à commenter cet article sur le forum.</a></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lambdas T() et std::function&lt;const T&amp;&gt;, un mélange dangereux</title>
		<link>https://blog.developpez.com/gpu/?p=309</link>
		<comments>https://blog.developpez.com/gpu/?p=309#comments</comments>
		<pubDate>Wed, 15 May 2013 00:49:31 +0000</pubDate>
		<dc:creator><![CDATA[germinolegrand]]></dc:creator>
				<category><![CDATA[Avancé]]></category>
		<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=309</guid>
		<description><![CDATA[Le chat de Dvp est le lieu de rendez-vous quotidien des devs C++ et de nombreuses discussions techniques sur le C++ soulèvent des interrogations sur des points particuliers du langage. Il nous est apparu qu&#8217;une faille dangereuse et non détectée &#8230; <a href="https://blog.developpez.com/gpu/?p=309">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Le chat de Dvp est le lieu de rendez-vous quotidien des devs C++ et de nombreuses discussions techniques sur le C++ soulèvent des interrogations sur des points particuliers du langage.</p>
<p>Il nous est apparu qu&rsquo;une faille dangereuse et non détectée par le compilateur résultait de l&rsquo;utilisation conjointe de la déduction automatique de type retour des lambdas et des std::function&lt;const A&amp;()&gt; qui retournent une référence constante.</p>
<p><span id="more-309"></span></p>
<p>Le code suivant compile parfaitement sans aucun warning sous g++4.8 et clang++3.2 avec les flags <code class="codecolorer text blackboard"><span class="text">-pedantic -Wall -Wextra</span></code>.</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &lt;iostream&gt;</span><br />
<span style="color: #339900;">#include &lt;functional&gt;</span><br />
<br />
<span style="color: #0000ff;">struct</span> A<br />
<span style="color: #008000;">&#123;</span> <br />
&nbsp; &nbsp; A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A()&quot;</span> <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; ~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;~A()&quot;</span> <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> <span style="color: #0000dd;">32</span><span style="color: #008080;">;</span><span style="color: #666666;">//variable témoin</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
&nbsp;<br />
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; A a<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #007788;">function</span><span style="color: #000080;">&lt;</span><span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #000080;">&gt;</span> f <span style="color: #000080;">=</span> <span style="color: #008000;">&#91;</span><span style="color: #000040;">&amp;</span>a<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#123;</span><span style="color: #0000ff;">return</span> a<span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span> aref <span style="color: #000080;">=</span> f<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;checkpoint_1&quot;</span> <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> aref.<span style="color: #007788;">i</span> <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Or le résultat obtenu en commentant cette ligne est :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;`<br />
~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
checkpoint_1<br />
<span style="color: #0000dd;">1</span><br />
~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Pour les rapides, oui aref est totalement corrompue, et son utilisation résulte en un crash sauvage et sans pitié du programme lorsqu&rsquo;on a de la chance. Pour les autres, voici l&rsquo;explication.</p>
<p>Savez-vous quel est le type de retour implicite de la lambda ci-dessous ?</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">A a<span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">auto</span> lm <span style="color: #000080;">=</span> <span style="color: #008000;">&#91;</span><span style="color: #000040;">&amp;</span>a<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#123;</span><span style="color: #0000ff;">return</span> a<span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Si comme moi, à première vue vous répondez A&amp;, vous vous en mordrez les doigts.<br />
La réponse est A.</p>
<p>En effet, comment se déduit automatiquement le type de retour d&#039;une lambda ? Eh bien de la même façon que decltype. C&#039;est à dire tel que la variable a été déclarée. Ici, a étant déclaré comme A, c&#039;est donc A qui est le type du retour de la lambda.</p>
<p>Si l&#039;on capture cette fois une référence vers A :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">A a<span style="color: #008080;">;</span><br />
A<span style="color: #000040;">&amp;</span> refTo_a<span style="color: #008000;">&#40;</span>a<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">auto</span> lm <span style="color: #000080;">=</span> <span style="color: #008000;">&#91;</span><span style="color: #000040;">&amp;</span>refTo_a<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#123;</span><span style="color: #0000ff;">return</span> refTo_a<span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Ici refTo_a est déclarée comme une A&amp;, c&#039;est donc A&amp; qui sera déduit.</p>
<p>Reprenons notre cas initial. Bon, à priori ce n&#039;est pas une grande perte, on fait une simple copie.</p>
<p>Ainsi, le code suivant est parfaitement sain si on reconnait la légitimité de la copie :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &lt;iostream&gt;</span><br />
<span style="color: #339900;">#include &lt;functional&gt;</span><br />
<br />
<span style="color: #0000ff;">static</span> <span style="color: #0000ff;">int</span> ID <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">struct</span> A<br />
<span style="color: #008000;">&#123;</span> <br />
&nbsp; &nbsp; A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> id <span style="color: #000080;">=</span> ID<span style="color: #000040;">++</span><span style="color: #008080;">;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A() &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; A<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> id <span style="color: #000080;">=</span> ID<span style="color: #000040;">++</span><span style="color: #008080;">;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A(const A&amp;) &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; ~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;~A() &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> <span style="color: #0000dd;">32</span><span style="color: #008080;">;</span><span style="color: #666666;">//variable témoin</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> id <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
&nbsp;<br />
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; A a<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">auto</span> lm <span style="color: #000080;">=</span> <span style="color: #008000;">&#91;</span><span style="color: #000040;">&amp;</span>a<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#123;</span><span style="color: #0000ff;">return</span> a<span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span> aref <span style="color: #000080;">=</span> lm<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;checkpoint_1&quot;</span> <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> aref.<span style="color: #007788;">i</span> <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">0</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;`<br />
A<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">1</span><br />
checkpoint_1<br />
<span style="color: #0000dd;">32</span><br />
~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">1</span><br />
~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">0</span></div></td></tr></tbody></table></div>
<p>La prise par référence constante allonge bien la durée de vie de la temporaire retournée par la lambda jusqu&#039;à la fin de la vie de la référence, ainsi que la norme le demande.</p>
<p>Oui mais&#8230;</p>
<p>Que se passe-t-il lorsque notre lambda est encapsulée dans une std::function&lt;const A&amp;()&gt; ?</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &lt;iostream&gt;</span><br />
<span style="color: #339900;">#include &lt;functional&gt;</span><br />
<br />
<span style="color: #0000ff;">static</span> <span style="color: #0000ff;">int</span> ID <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">struct</span> A<br />
<span style="color: #008000;">&#123;</span> <br />
&nbsp; &nbsp; A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> id <span style="color: #000080;">=</span> ID<span style="color: #000040;">++</span><span style="color: #008080;">;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A() &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; A<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> id <span style="color: #000080;">=</span> ID<span style="color: #000040;">++</span><span style="color: #008080;">;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A(const A&amp;) &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; ~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;~A() &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> <span style="color: #0000dd;">32</span><span style="color: #008080;">;</span><span style="color: #666666;">//variable témoin</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> id <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
&nbsp;<br />
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; A a<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #007788;">function</span><span style="color: #000080;">&lt;</span><span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #000080;">&gt;</span> f <span style="color: #000080;">=</span> <span style="color: #008000;">&#91;</span><span style="color: #000040;">&amp;</span>a<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#123;</span><span style="color: #0000ff;">return</span> a<span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span> aref <span style="color: #000080;">=</span> f<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;checkpoint_1&quot;</span> <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Ainsi que cela a été révélé plus haut, mais en ajoutant l&#039;indication du constructeur de copie et un id aux objets la sortie est :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">0</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;`<br />
A<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">1</span><br />
~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">1</span><br />
checkpoint_1<br />
~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">0</span></div></td></tr></tbody></table></div>
<p>Lors de l&#039;appel à f(), une copie est donc faite, conformément à ce qu&#039;on a vu précédemment avec la déduction automatique du type de la lambda. Mais elle est détruite immédiatement !</p>
<p>Pourtant on récupère bien une const A&amp;, pourquoi ne fait-elle pas prolonger de la copie temporaire comme précédemment ?</p>
<p>Un petit code montrant le mécanisme du avec std::function&lt;const A&amp;()&gt; en détail :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">int</span> ID <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">struct</span> A<br />
<span style="color: #008000;">&#123;</span> <br />
&nbsp; &nbsp; A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> id <span style="color: #000080;">=</span> ID<span style="color: #000040;">++</span><span style="color: #008080;">;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A() &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; A<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> id <span style="color: #000080;">=</span> ID<span style="color: #000040;">++</span><span style="color: #008080;">;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A(const A&amp;) &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; ~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;~A() &quot;</span> <span style="color: #000080;">&lt;&lt;</span> id <span style="color: #000080;">&lt;&lt;</span> std<span style="color: #008080;">::</span><span style="color: #007788;">endl</span><span style="color: #008080;">;</span><span style="color: #008000;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> <span style="color: #0000dd;">32</span><span style="color: #008080;">;</span><span style="color: #666666;">//variable témoin</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> id <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
<br />
A lambda<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; A a<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">return</span> a<span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><br />
<br />
<span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span> function<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> A<span style="color: #000040;">&amp;</span> aret <span style="color: #000080;">=</span> lambda<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;checkpoint_1&quot;</span><span style="color: #008080;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #0000ff;">return</span> aret<span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><br />
<br />
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">const</span> A <span style="color: #000040;">&amp;</span>aref <span style="color: #000080;">=</span> function<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
&nbsp; &nbsp; std<span style="color: #008080;">::</span><span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;checkpoint_2&quot;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>On note à nouveau un silence total du compilateur. Le résultat est :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">0</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;`<br />
checkpoint_1<br />
~A<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">0</span><br />
checkpoint_2</div></td></tr></tbody></table></div>
<p>Là où ça devient tordu c&#039;est que l&#039;extension de durée de vie d&#039;une temporaire par référence constante est limitée à une seule fois.</p>
<p>En effet, une fois récupérée dans une const A&amp;, la copie temporaire n&#039;a plus du tout le statut de temporaire : aussi on ne peut donc plus étendre sa durée de vie de cette façon !</p>
<p>Voici donc le pourquoi du comment. En conclusion il est donc dangereux soit de jouer avec les std::function&lt;const A&amp;()&gt; soit de laisser faire la déduction automatique du type retour des lambdas. En l&#039;absence d&#039;arguments forts pour pencher la balance, la vigilance est donc de mise lorsque l&#039;on joue avec les deux règles responsables du problème ici :</p>
<ol>
<li>Un type de retour automatiquement déduit est équivalent à un decltype(<em>expression_de_return</em>), soit le type de l&rsquo;expression telle qu&rsquo;elle a été déclarée, la capture par référence n&rsquo;y fait rien, ça reste le type de la variable avant sa capture.</li>
<li>Si une lambda a vocation à être stockée dans une std::function, alors il faut veiller à ce qu&rsquo;elle ne renvoie pas une temporaire et que le type de la std::function corresponde au comportement souhaité.</li>
</ol>
<p>Un grand merci à Flob90 qui a pris le temps de faire beaucoup de tests que vous pouvez consulter ici : <a href="http://pastebin.com/TR7f8rB2" title="lien pastebin" target="_blank">lien pastebin</a>. Merci à gbdivers pour sa relecture attentive.</p>
<p>Vous pouvez commenter ce billet de blog sur le <a href="http://www.developpez.net/forums/d1343028/cpp/billet-lambdas-t-stdfunctionltconst-tampgt-melange-dangereux/">forum</a>.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Un ColorPicker avec Qt – Benchmark et optimisations</title>
		<link>https://blog.developpez.com/gpu/?p=390</link>
		<comments>https://blog.developpez.com/gpu/?p=390#comments</comments>
		<pubDate>Fri, 15 Feb 2013 18:52:59 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Intermédiaire]]></category>
		<category><![CDATA[OpenGL]]></category>
		<category><![CDATA[Qt]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=390</guid>
		<description><![CDATA[Le sujet du premier exercice proposé par la rubrique Qt de Developpez.com consistait à créer un widget permettant de sélectionner les différentes nuances de gris à partir d&#8217;une teinte définie. Deux méthodes ont été proposer pour générer ces nuances de &#8230; <a href="https://blog.developpez.com/gpu/?p=390">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Le sujet du premier exercice proposé par la rubrique Qt de Developpez.com consistait à créer un widget permettant de sélectionner les différentes nuances de gris à partir d&rsquo;une teinte définie.<br />
<span id="more-390"></span><br />
Deux méthodes ont été proposer pour générer ces nuances de gris :</p>
<ul>
<li>utiliser deux boucles for imbriquées pour parcourir chaque pixel du widget et calculer la couleur correspondante, en faisant varier les paramètres S et V dans l&rsquo;espace colorimétrique HSV ;</li>
<li>utiliser deux gradient (à l&rsquo;aide de la classe QLinearGradient), le premier, horizontal, allant du blanc à gauche à la teinte choisie à droite, et le second, vertical, allant du transparent en haut au noir en bas.</li>
</ul>
<p>Je vais présenter ici comment implémenter un benchmark avec Qt pour tester la rapidité de ces différentes méthodes et présenter deux autres méthodes pour accélérer le rendu : l&rsquo;accès direct au tampon mémoire contenant l&rsquo;image finale et l&rsquo;accélération matériel sur carte graphique.</p>
<h1>Création du programme Benchmark</h1>
<p>Qt offre différents outils facilitant la création de tests unitaires et de benchmarks, rassemblés dans le module <a href="http://qt.developpez.com/doc/4.6/QtTest/">QtTest</a>. Différents tutoriels permettent de se familiariser progressivement avec ce module.</p>
<h2>Benchmark.pro</h2>
<p>Pour créer notre programme Benchmark, commençons par créer un fichier de projet Benchmark.pro. Ce fichier de projet contient les déclarations de base suivantes : la liste des modules utilisés (core, gui puisse que l&rsquo;on teste les fonctions de dessin et testlib) et le nom et le type de binaire généré (une application Benchmark dans notre cas) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">QT <span style="color: #000040;">+</span><span style="color: #000080;">=</span> core gui testlib<br />
TARGET <span style="color: #000080;">=</span> Benchmark<br />
TEMPLATE <span style="color: #000080;">=</span> app</div></td></tr></tbody></table></div>
<p>On ajoute ensuite la liste des fichiers à inclure dans notre projet. On va simplement créer une classe Benchmark : on ajoute donc les fichiers d&rsquo;en-tête et d&rsquo;implémentation :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SOURCES <span style="color: #000040;">+</span><span style="color: #000080;">=</span> benchmark.<span style="color: #007788;">cpp</span><br />
HEADERS &nbsp;<span style="color: #000040;">+</span><span style="color: #000080;">=</span> benchmark.<span style="color: #007788;">h</span></div></td></tr></tbody></table></div>
<h2>benchmark.h</h2>
<p>La classe Benchmark hérite de QObject (pour pourvoir utiliser le système de signaux et slots) et contient deux slots privés, un pour chaque méthode que l&rsquo;on souhaite tester. Ces slots seront automatiquement appelés à l&rsquo;exécution du programme. A part cela, le contenu de la classe est minimaliste :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#ifndef BENCHMARK_H</span><br />
<span style="color: #339900;">#define BENCHMARK_H</span><br />
<span style="color: #339900;">#include &lt;QtTest/QtTest&gt;</span><br />
<span style="color: #0000ff;">class</span> Benchmark <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> QObject<br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; Q_OBJECT<br />
<span style="color: #0000ff;">private</span> slots<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> hsvGradient<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> doubleLinearGradient<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span><br />
<span style="color: #339900;">#endif // BENCHMARK_H</span></div></td></tr></tbody></table></div>
<h2>benchmark.cpp</h2>
<p>Dans le fichier d&rsquo;implémentation, on ajoute chaque méthode dans les slots correspondants :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &quot;benchmark.h&quot;</span><br />
<br />
<span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
<span style="color: #008000;">&#125;</span><br />
<span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Il faut également ajouter la macro QTEST_MAIN, qui crée une fonction main, qui aura pour fonction de créer une instance de la classe Benchmark et d&rsquo;exécuter les slots privés :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">QTEST_MAIN<span style="color: #008000;">&#40;</span>Benchmark<span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<h2>Méthode des deux boucles for</h2>
<p>Pour mesurer le temps d&rsquo;exécution d&rsquo;une méthode, on utilise la macro BENCHMARK. Le temps du reste du code contenu dans le slot mais en dehors du bloc précédent la macro n&rsquo;est pas évalué. Pour vérifier que le code généré correctement les nuances de gris, on enregistre les images produites :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339900;">#include &lt;QtGui/QPainter&gt;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> width <span style="color: #000080;">=</span> <span style="color: #0000dd;">500</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> height <span style="color: #000080;">=</span> <span style="color: #0000dd;">500</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> QColor m_main_color <span style="color: #000080;">=</span> Qt<span style="color: #008080;">::</span><span style="color: #007788;">red</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; QImage image <span style="color: #000080;">=</span> QImage<span style="color: #008000;">&#40;</span>QSize<span style="color: #008000;">&#40;</span>width, height<span style="color: #008000;">&#41;</span>, QImage<span style="color: #008080;">::</span><span style="color: #007788;">Format_ARGB32</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QBENCHMARK<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">float</span> h <span style="color: #000080;">=</span> m_main_color.<span style="color: #007788;">hsvHueF</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> s<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> s<span style="color: #000080;">&lt;</span>width<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>s<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> v<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> v<span style="color: #000080;">&lt;</span>height<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>v<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; QColor color <span style="color: #000080;">=</span> QColor<span style="color: #008080;">::</span><span style="color: #007788;">fromHsvF</span><span style="color: #008000;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; h,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> s <span style="color: #000040;">/</span> <span style="color: #008000;">&#40;</span>width<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color:#800080;">1.0</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> v <span style="color: #000040;">/</span> <span style="color: #008000;">&#40;</span>height<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image.<span style="color: #007788;">setPixel</span><span style="color: #008000;">&#40;</span>s, v, color.<span style="color: #007788;">rgb</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;hsvGradient.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Méthode des deux gradients linéaires</h2>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; QImage image<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QPixmap pixmap<span style="color: #008000;">&#40;</span>QSize<span style="color: #008000;">&#40;</span>width, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QPainter painter<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span>pixmap<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">setPen</span><span style="color: #008000;">&#40;</span>QPen<span style="color: #008000;">&#40;</span>Qt<span style="color: #008080;">::</span><span style="color: #007788;">NoPen</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QBENCHMARK<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; QLinearGradient h_gradient<span style="color: #008000;">&#40;</span>QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span>, QPointF<span style="color: #008000;">&#40;</span>width, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; h_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">white</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; h_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span>, m_main_color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">setBrush</span><span style="color: #008000;">&#40;</span>QBrush<span style="color: #008000;">&#40;</span>h_gradient<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">drawRect</span><span style="color: #008000;">&#40;</span>QRect<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, width, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; QLinearGradient v_gradient<span style="color: #008000;">&#40;</span>QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span>, QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">0.0</span>, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; v_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">transparent</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; v_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">black</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">setBrush</span><span style="color: #008000;">&#40;</span>QBrush<span style="color: #008000;">&#40;</span>v_gradient<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; painter.<span style="color: #007788;">drawRect</span><span style="color: #008000;">&#40;</span>QRect<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, width, height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; image <span style="color: #000080;">=</span> pixmap.<span style="color: #007788;">toImage</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;doubleLinearGradient.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Le code étant expliqué dans la solution de l&rsquo;exercice, je n&rsquo;entre pas dans le détail ici.</p>
<h2>Résultat du benchmark</h2>
<p>Lorsque le programme est exécuté, des messages sont générés automatiquement à chaque étape du processus. L&rsquo;en-tête décrit simplement le nom du programme et les versions de QtTest et de Qt utilisées :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000040;">*********</span> Start testing of Benchmark <span style="color: #000040;">*********</span><br />
Config<span style="color: #008080;">:</span> Using QTest library 4.7.0, Qt 4.7.0</div></td></tr></tbody></table></div>
<p>Le premier slot que lance le programme est un slot spécifique, initTestCase, généré par défaut et ne faisant rien. Ce slot permet, si on le souhaite, d&rsquo;initialiser des objets pour l&rsquo;ensemble du programme :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">initTestCase</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Vient ensuite les différents slots que l&rsquo;on a créé. Le premier est hsvGradient. Lorsque le temps d&rsquo;exécution du slots est trop petit, la mesure risque de ne pas être précise. Pour palier à cet inconvénient, le code contenu dans le bloc de code suivant la macro BENCHMARK peut être exécuté plusieurs fois, le temps indiqué correspondant au temps moyen mesuré. Ici, le slot hsvGradient a été exécuté deux fois et le temps moyen est de 47 millisecondes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color: #0000dd;">47</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">94</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">2</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">hsvGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Pour le slot doubleLinearGradient, le code a été exécuté une seule fois et le temps mesuré est de 57 millisecondes. On voit donc ici que cette méthode est plus lente que la première version (sur le système testé ! C&rsquo;est à dire Linux dans mon cas. Sous Windows ou Max OS X, il est possible que les résultats soient différents). Cette différence peut s&rsquo;expliquer par le fait que la génération de deux gradients et la gestion de la transparence est plus lente que l&rsquo;accès directe aux pixels, sur le système de rendu par défaut (le système Raster) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color: #0000dd;">57</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">57</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">doubleLinearGradient</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Le programme exécute ensuite un slot pour libérer les ressources initialisées dans la fonction initTestCase :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">cleanupTestCase</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>Pour terminer, le programme indique le nombre de slot exécutés sans incident (passed), ceux qui ont échoué (failed) et ceux qui n&rsquo;ont pas été exécutés (skipped) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Totals<span style="color: #008080;">:</span> <span style="color: #0000dd;">4</span> passed, <span style="color: #0000dd;">0</span> failed, <span style="color: #0000dd;">0</span> skipped<br />
<span style="color: #000040;">*********</span> Finished testing of Benchmark <span style="color: #000040;">*********</span></div></td></tr></tbody></table></div>
<h1>Rendu dans un tampon mémoire</h1>
<p>Dans la première méthode, on utilise la fonction Qimage::setPixel pour modifier chaque pixel un par un. Cette méthode permet d&rsquo;utiliser une interface sécurisée et simple. Malheureusement, les calculs (calcul de l&rsquo;adresse mémoire correspondant au pixel, conversion de l&rsquo;espace colorimétrique HSV en RGB) et tests effectués en interne (la position du pixel est bien dans l&rsquo;image ? Les valeurs de la couleurs sont-elles correctes ?) peuvent ralentir fortement l&rsquo;exécution.<br />
Il est alors possible d&rsquo;optimiser le rendu en accédant directement à la mémoire à l&rsquo;aide de pointeurs : on créer un bloc mémoire de taille suffisante que l&rsquo;on alloue à une image puis on accède à chaque octet un par un.</p>
<p>ATTENTION : ce type d&rsquo;approche peut comporter certains risques : en cas d&rsquo;erreur dans le code, il est possible que l&rsquo;on écrive dans une zone mémoire qui ne correspond pas à l&rsquo;image. Aucun test n&rsquo;est effectué pour vérifier que le pointeur pointe vers une zone valide. L&rsquo;application peut devenir instable. De plus, il est possible que le code ne soit pas portable et donne des résultats différents en fonction des systèmes d&rsquo;exploitation utilisés (je l&rsquo;ai testé uniquement sur Linux, pensez à bien enregistrer tous vos documents avant de lancer le programme sur un autre système:) ).</p>
<p>La première chose a faire est de créer une zone mémoire de taille suffisante : un pixel étant codé sur 32 bits (un octet par composante de la couleur et quatre composantes : rouge, vert, bleu et alpha), il faudra donc réserver un bloc mémoire de width * height * 4 pour contenir toute l&rsquo;image :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">drawInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; uchar buffer<span style="color: #008000;">&#91;</span>width <span style="color: #000040;">*</span> height <span style="color: #000040;">*</span> <span style="color: #0000dd;">4</span><span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QImage image<span style="color: #008000;">&#40;</span>buffer, width, height, QImage<span style="color: #008080;">::</span><span style="color: #007788;">Format_ARGB32</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Il n&rsquo;est pas nécessaire d&rsquo;initialiser chaque octet du bloc mémoire (avec memset par exemple) puisque l&rsquo;algorithme utilisé garanti que chaque octet sera initialisé durant l&rsquo;exécution de celui-ci.</p>
<p>Pour commencer, on crée un pointeur pointant vers le premier octet du bloc mémoire :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; QBENCHMARK<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; uchar<span style="color: #000040;">*</span> pbuffer <span style="color: #000080;">=</span> <span style="color: #000040;">&amp;</span>buffer<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>On utilise deux boucle for, comme dans la première méthode, pour parcourir chaque pixel (en fait, on parcourt le bloc mémoire linéairement, en « déplaçant » le pointeur octet par octet et non en accédant aux coordonnées (x, y) comme dans le cas d&rsquo;une image) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> i<span style="color: #000080;">&lt;</span>width<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>i<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666;">//uchar green_blue = red;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> j<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> j<span style="color: #000080;">&lt;</span>height<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>j<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span></div></td></tr></tbody></table></div>
<p>Les valeurs de composantes RGB sont calculés par la méthode présentée dans la version QML de la solution de l&#039;exercice :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">float</span> a <span style="color: #000080;">=</span> <span style="color:#800080;">255.0</span> <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> i <span style="color: #000040;">/</span> width<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">float</span> b <span style="color: #000080;">=</span> a <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span> <span style="color: #000040;">*</span> j <span style="color: #000040;">/</span> height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Ensuite, pour chaque composante, on affecte la valeur calculée (sous forme de uchar pour être sur qu&#039;elle occupe bien à un octet en mémoire) à l&#039;octet pointé par le pointeur (en dé-référençant celui-ci) puis on « avance » le pointeur d&rsquo;un octet :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> b<span style="color: #008080;">;</span> <span style="color: #666666;">// blue</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>On fait de même pour les autres composantes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> b<span style="color: #008080;">;</span> <span style="color: #666666;">// green</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> a<span style="color: #008080;">;</span> <span style="color: #666666;">// red</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">*</span>pbuffer <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>uchar<span style="color: #008000;">&#41;</span> <span style="color: #0000dd;">255</span><span style="color: #008080;">;</span> <span style="color: #666666;">// alpha</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>pbuffer<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour terminer, on enregistre l&rsquo;image générée :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;buffured.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Résultat du benchmark</h2>
<p>Les messages de sortie d&rsquo;application générés par le slot drawInBuffer indique que cette méthode est plus rapide d&rsquo;un facteur 10 que les deux autres méthodes, avec un temps d&rsquo;exécution moyen de 3,9 millisecondes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">drawInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color:#800080;">3.9</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">63</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">16</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">drawInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<h1>Optimisation de l&rsquo;algorithme</h1>
<p>Jusqu&rsquo;à présent, nous avons simplement travailler sur l&rsquo;implémentation pour optimiser le rendu des nuances de gris. Une autre approche consiste à travailler sur l&rsquo;algorithme utilisé et d&rsquo;essayer de diminuer les calculs effectués.<br />
Quelque soit l&rsquo;approche utilisée, il sera nécessaire de parcourir l&rsquo;ensemble des pixels du widget, soit explicitement, comme dans le cas de la première méthode présentée, soit implicitement, comme dans le cas des deux QLinearGradient. On ne pourra donc pas optimiser cette partie (on peut au mieux éviter de parcourir plusieurs fois chaque pixel, comme on fait avec la méthode avec les deux QLinearGradient).<br />
On peut remarquer que, dans chaque méthode présentée jusqu&rsquo;ici, on travaille avec des nombres réels, ce qui n&rsquo;est pas optimal. L&rsquo;idéal serait de travailler qu&rsquo;avec des nombres entiers, mieux gérés par les processeurs, et d&rsquo;effectuer que des opérations élémentaires : addition, soustraction et multiplication.</p>
<p>Regardons en détail ce que l&rsquo;on souhaite faire lorsque l&rsquo;on parcourt un ligne pour une composante en particulier. La composante est une valeur entière allant de 0 à 255 (ou de 255 à 0) pour les pixels allant de 0 à N (avec N &gt; 256 pour être sur d&rsquo;afficher toutes les nuances). Il faut donc parcourir chaque pixel et incrémenter, quand c&rsquo;est nécessaire, la composante. Si on représente cela avec un graphique 2D, on obtient :</p>
<p><img src="http://blog.developpez.com/gpu/files/2013/02/droite.png" class="aligncenter" /></p>
<p>avec x, l&rsquo;index des pixels, et y, la composante de la couleur (source : <a href="http://commons.wikimedia.org/wiki/File:Bresenham_run-based.svg">Wikipédia</a>).</p>
<p>Le lecteur averti aura compris l&rsquo;idée sous-jacente : calculer la composante de la couleur revient à dessiner une ligne dans un espace (x, y) discret. Or, il existe un algorithme très performant pour réaliser cela : <a href="http://fr.wikipedia.org/wiki/Algorithme_de_trac.%C3%A9_de_segment_de_Bresenham">l&rsquo;algorithme de Bresenham</a>. Il suffit juste de remplacer y par la composante de la couleur. Le lecteur se reportera à l&rsquo;article de Wikipédia pour l&rsquo;explication du principe de cet algorithme.</p>
<p>L&rsquo;implémentation de l&rsquo;algorithme est assez triviale à partir du pseudo-code. On fera attention aux notations utilisées, qui correspondent au pseudo-code, c&rsquo;est à dire que y1 correspondent à la composante de la couleur et non à la position verticale du pixel. La variable i est utilisée pour parcourir verticalement l&rsquo;image dans une boucle for :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">QImage image<span style="color: #008000;">&#40;</span>width, height, Qimage<span style="color: #008080;">::</span><span style="color: #007788;">Format_ARGB32</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">int</span> x1 <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> <span style="color: #666666;">// premier pixel</span><br />
<span style="color: #0000ff;">int</span> y1 <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> <span style="color: #666666;">// composante dans le premier pixel</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> x2 <span style="color: #000080;">=</span> width <span style="color: #000040;">-</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span> <span style="color: #666666;">// dernier pixel</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> y2 <span style="color: #000080;">=</span> <span style="color: #0000dd;">255</span><span style="color: #008080;">;</span> <span style="color: #666666;">// composante dans le dernier pixel</span><br />
<span style="color: #0000ff;">int</span> e <span style="color: #000080;">=</span> x2 <span style="color: #000040;">-</span> x1<span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> dx <span style="color: #000080;">=</span> <span style="color: #0000dd;">2</span> <span style="color: #000040;">*</span> e<span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> dy <span style="color: #000080;">=</span> <span style="color: #0000dd;">2</span> <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span>y2 <span style="color: #000040;">-</span> y1<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>x1 <span style="color: #000080;">&lt;=</span> x2<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> i<span style="color: #000080;">&lt;</span>height<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>i<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; image.<span style="color: #007788;">setPixel</span><span style="color: #008000;">&#40;</span>QPoint<span style="color: #008000;">&#40;</span>x1, i<span style="color: #008000;">&#41;</span>, qRgb<span style="color: #008000;">&#40;</span>y1, y1, y1<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #000040;">++</span>x1<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; e <span style="color: #000040;">-</span><span style="color: #000080;">=</span> dy<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>e <span style="color: #000080;">&lt;=</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000040;">++</span>y1<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; e <span style="color: #000040;">+</span><span style="color: #000080;">=</span> dx<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
<span style="color: #008000;">&#125;</span><br />
image.<span style="color: #007788;">save</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;bresenham.png&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>L&#039;image générée est un gradient allant du noir au blanc :</p>
<p><img src="http://blog.developpez.com/gpu/files/2013/02/gradient.png" class="aligncenter" /></p>
<p>Pour générer toutes les nuances, il va falloir imbriquer deux fois algorithme, le premier allant du blanc à la teinte choisie (première ligne de pixels) et le second allant de la teinte (pixel de  la première ligne) au noir (pixel de la dernière ligne). Il faut également gérer chaque composante indépendamment.</p>
<p>Pour terminer, il est possible de combiner cette méthode avec la méthode utilisant un tampon mémoire. Le code est très proche du précédent.</p>
<h2>Résultat du benchmark</h2>
<p>La méthode utilisant l&rsquo;algorithme amélioré permet de gagner un facteur 3 à 4 par rapport aux premières méthodes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenham</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color: #0000dd;">14</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">58</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">4</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenham</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>La version combinant les deux approches permet un gain de performance d&rsquo;un facteur 4 par rapport à la méthode utilisant un tampon mémoire et un facteur 50 par rapport au premières méthodes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">RESULT <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenhamInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">:</span><br />
<span style="color:#800080;">1.1</span> msecs per iteration <span style="color: #008000;">&#40;</span>total<span style="color: #008080;">:</span> <span style="color: #0000dd;">72</span>, iterations<span style="color: #008080;">:</span> <span style="color: #0000dd;">64</span><span style="color: #008000;">&#41;</span><br />
PASS <span style="color: #008080;">:</span> Benchmark<span style="color: #008080;">::</span><span style="color: #007788;">bresenhamInBuffer</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<h1>Accélération matériel sur carte graphique</h1>
<p>Pour réaliser le rendu des nuances de gris sur carte graphique, on utilise une classe dérivée de QGLWidget. Il suffit de dessiner un rectangle s&rsquo;adaptant à la taille du widget, le reste du travail de rendu est réalisé dans le fragment shader : on calcule le rapport entre la position du pixel et la largeur du widget (passé en paramètre) pour calculer la nuance de gris correspondante :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #FF0000;">&quot;uniform vec4 main_color;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;uniform vec2 size;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;vec4 pixel_color;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;void main(void)<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;{<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.r = (gl_FragCoord.y / size.y) * (1.0 + (gl_FragCoord.x / size.x) * (main_color.r - 1.0));<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.g = (gl_FragCoord.y / size.y) * (1.0 + (gl_FragCoord.x / size.x) * (main_color.g - 1.0));<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.b = (gl_FragCoord.y / size.y) * (1.0 + (gl_FragCoord.x / size.x) * (main_color.b - 1.0));<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;pixel_color.a = main_color.a;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot; &nbsp; &nbsp;gl_FragColor = pixel_color;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><br />
<span style="color: #FF0000;">&quot;}<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Pour que le fragment shader soit appliqué sur chaque pixel du widget, il suffit de dessiner un quadrilatère sur la totalité de la surface :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">glClear<span style="color: #008000;">&#40;</span>GL_COLOR_BUFFER_BIT<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
glBegin<span style="color: #008000;">&#40;</span>GL_QUADS<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color: #000040;">-</span><span style="color:#800080;">1.0</span>, <span style="color: #000040;">-</span><span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color: #000040;">-</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; glVertex2f<span style="color: #008000;">&#40;</span><span style="color:#800080;">1.0</span>, <span style="color: #000040;">-</span><span style="color:#800080;">1.0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
glEnd<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Les différents paramètres utilisés dans le fragment shader sont définis à l&rsquo;aide des fonctions uniformLocation et setUniformValue. Par exemple, pour mettre à jour la taille du widget :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">position_location <span style="color: #000080;">=</span> program<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>uniformLocation<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;position&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
QVector2D s <span style="color: #000080;">=</span> QVector2D<span style="color: #008000;">&#40;</span>width, height<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
program<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>setUniformValue<span style="color: #008000;">&#40;</span>size_location, s<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Par contre, je n&rsquo;ai pas réussit à utiliser les fonctions de benchmarks de Qt Test pour tester la rapidité du rendu par cette méthode. Pour pouvoir mesurer le temps, j&rsquo;ai ajouté un calcul du nombre de FPS (frame par seconde) avec un simple compteur, incrémenté à chaque cycle et remis à zéro toutes les secondes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">int</span> delay <span style="color: #000080;">=</span> m_time.<span style="color: #007788;">msecsTo</span><span style="color: #008000;">&#40;</span>QTime<span style="color: #008080;">::</span><span style="color: #007788;">currentTime</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span> delay <span style="color: #000080;">&gt;=</span> <span style="color: #0000dd;">1000</span> <span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>m_fps <span style="color: #000080;">&gt;</span> m_fps_max<span style="color: #008000;">&#41;</span> m_fps_max <span style="color: #000080;">=</span> m_fps<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; setWindowTitle<span style="color: #008000;">&#40;</span>QString<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;fps:%1 - max:%2 - w:%3 - h:%4&quot;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; .<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>m_fps<span style="color: #008000;">&#41;</span>.<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>m_fps_max<span style="color: #008000;">&#41;</span>.<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>width<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">arg</span><span style="color: #008000;">&#40;</span>height<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; m_fps <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; m_time <span style="color: #000080;">=</span> QTime<span style="color: #008080;">::</span><span style="color: #007788;">currentTime</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><br />
<span style="color: #0000ff;">else</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000040;">++</span>m_fps<span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour mettre à jour la widget, j&rsquo;utilise un QTimer avec un délai de 0.</p>
<p>ATTENTION : Il en faut pas laisser le programme tourner trop longtemps avec cette méthode. Le programme tourne en boucle et utilise la carte graphique au maximum de ses capacités, ce qui augmente progressivement sa température. Personnellement, je n&rsquo;ai pas pris le risque de tester si cela pouvait endommager la carte graphique.</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">m_timer <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> QTimer<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">this</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
connect<span style="color: #008000;">&#40;</span>m_timer, SIGNAL<span style="color: #008000;">&#40;</span>timeout<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>, <span style="color: #0000dd;">this</span>, SLOT<span style="color: #008000;">&#40;</span>timerUpdate<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
m_timer<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>start<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">20</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">timerUpdate</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; updateGL<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Performances</h2>
<p>J&rsquo;ai réalisé le test sur un ordinateur portable équipé d&rsquo;une carte Nvidia 8600M GT avec 512 Mo et un widget de 256 x 256 pixels. Avec cette configuration, les mesures donnent un FPS moyen de 5000 fps avec de pointes à 5200 fps, c&rsquo;est à dire des temps de rendu de l&rsquo;ordre de 0,2 millisecondes (soit 250 fois plus rapide que les premières versions).</p>
<h1>Les sources</h1>
<p><a href="http://www.developpez.net/forums/attachments/p67224d1284552523/c-cpp/bibliotheques/qt/exercice-qt-color-picker-deuxieme-partie-lancee/benchmark.zip/">Télécharger les sources des benchmarks.</a></p>
<p><a href="http://www.developpez.net/forums/attachments/p67225d1284552535/c-cpp/bibliotheques/qt/exercice-qt-color-picker-deuxieme-partie-lancee/colorpickeropengl.zip/">Télécharger les sources de la version OpenGL.</a></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Un ColorPicker avec Qt – Version QML</title>
		<link>https://blog.developpez.com/gpu/?p=366</link>
		<comments>https://blog.developpez.com/gpu/?p=366#comments</comments>
		<pubDate>Fri, 15 Feb 2013 18:31:20 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Facile]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[QtQuick]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=366</guid>
		<description><![CDATA[Cet article est la solution que j’avais proposé à l’exercice Qt sur la création d’un ColorPicker (voir le première article de cette série). Dans cette partie, j&#8217;utilise le QML pour créer le ColorPicker. L&#8217;objet QML ColorPicker L&#8217;objet ColorPicker permet d&#8217;afficher &#8230; <a href="https://blog.developpez.com/gpu/?p=366">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Cet article est la solution que j’avais proposé à l’exercice Qt sur la création d’un ColorPicker (<a href="http://blog.developpez.com/gpu/p11791/difficulte/facile/un-colorpicker-avec-qt-enonce-de-lexercice">voir le première article de cette série</a>). Dans cette partie, j&rsquo;utilise le QML pour créer le ColorPicker.<br />
<span id="more-366"></span></p>
<h1>L&rsquo;objet QML ColorPicker</h1>
<p>L&rsquo;objet ColorPicker permet d&rsquo;afficher les nuances de gris d&rsquo;une couleur et de sélectionner une nuance directement en cliquant dans l&rsquo;item. Les composantes des couleurs sont manipulées directement puisqu&rsquo;il n&rsquo;est pas possible d&rsquo;extraire les composantes d&rsquo;une couleur donnée en QML.<br />
La couleur principale est définie par les variables main_red, main_green et main_blue. La couleur sélectionnée est récupérée grâce aux variables selected_red, selected_green et selected_blue dans le contexte ColorPickerContext.</p>
<p>La taille de l&rsquo;item est fixée à 256&#215;256 :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">import Qt <span style="color:#800080;">4.7</span><br />
Item<br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; width<span style="color: #008080;">:</span> <span style="color: #0000dd;">256</span><br />
&nbsp; &nbsp; height<span style="color: #008080;">:</span> <span style="color: #0000dd;">256</span></div></td></tr></tbody></table></div>
<p>Pour dessiner les nuances de gris, on va utiliser deux gradients linéaires :</p>
<ul>
<li>le premier, horizontal, va du blanc à la couleur principale ;</li>
<li>le second, vertical, va du transparent au noir.</li>
</ul>
<p>Pour créer les gradients, on commence par définir un rectangle ayant les mêmes dimensions et position que l&rsquo;item :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; Rectangle<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; anchors.<span style="color: #007788;">fill</span><span style="color: #008080;">:</span> parent<br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>On utilise ensuite l&rsquo;objet gradient pour remplir le rectangle avec le dégradé. Par exemple pour le second gradient :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; Rectangle<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; anchors.<span style="color: #007788;">fill</span><span style="color: #008080;">:</span> parent<br />
&nbsp; &nbsp; &nbsp; &nbsp; gradient<span style="color: #008080;">:</span> Gradient<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GradientStop <span style="color: #008000;">&#123;</span> position<span style="color: #008080;">:</span> <span style="color:#800080;">0.0</span><span style="color: #008080;">;</span> color<span style="color: #008080;">:</span> Qt.<span style="color: #007788;">rgba</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GradientStop <span style="color: #008000;">&#123;</span> position<span style="color: #008080;">:</span> <span style="color:#800080;">1.0</span><span style="color: #008080;">;</span> color<span style="color: #008080;">:</span> Qt.<span style="color: #007788;">rgba</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>On obtient ainsi un gradient vertical allant du transparent au noir. Pour le premier gradient, il faut donc effectuer une rotation du rectangle de 90° après l&rsquo;avoir correctement dimensionné et positionné :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; Rectangle<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; width<span style="color: #008080;">:</span> parent.<span style="color: #007788;">height</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; height<span style="color: #008080;">:</span> parent.<span style="color: #007788;">width</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; transform<span style="color: #008080;">:</span> Rotation <span style="color: #008000;">&#123;</span> angle<span style="color: #008080;">:</span> <span style="color: #0000dd;">90</span><span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; x<span style="color: #008080;">:</span> parent.<span style="color: #007788;">width</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; y<span style="color: #008080;">:</span> <span style="color: #0000dd;">0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; gradient<span style="color: #008080;">:</span> Gradient<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GradientStop <span style="color: #008000;">&#123;</span> position<span style="color: #008080;">:</span> <span style="color:#800080;">0.0</span><span style="color: #008080;">;</span> color<span style="color: #008080;">:</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Qt.<span style="color: #007788;">rgba</span><span style="color: #008000;">&#40;</span>main_red, main_green, main_blue, <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GradientStop <span style="color: #008000;">&#123;</span> position<span style="color: #008080;">:</span> <span style="color:#800080;">1.0</span><span style="color: #008080;">;</span> color<span style="color: #008080;">:</span> <span style="color: #FF0000;">&quot;white&quot;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour visualiser la position de la couleur sélectionnée, on va afficher un cercle entourant la dernière position connue. Cependant, dessiner un cercle en QML est un peu complexe (il faut créer des objets path pour dessiner des courbes quadratiques et constituer le cercle). Pour simplifier, on va afficher une image de taille 8&#215;8 pixels représentant un cercle avec le fond transparent :</p>
<p><img src="http://blog.developpez.com/gpu/files/2013/02/curseur.png" class="aligncenter" /></p>
<p>On crée un objet image en indiquant la source du fichier. Par défaut, la taille de l&rsquo;image est celle du fichier chargé :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; Image<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; source<span style="color: #008080;">:</span> <span style="color: #FF0000;">&quot;cursor.png&quot;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Par défaut, on souhaite que le curseur soit placé au centre de l&rsquo;item. Pour cela, on utilise les variables x et y :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; Image<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; x<span style="color: #008080;">:</span> width<span style="color: #000040;">/</span><span style="color: #0000dd;">2</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; y<span style="color: #008080;">:</span> height<span style="color: #000040;">/</span><span style="color: #0000dd;">2</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; source<span style="color: #008080;">:</span> <span style="color: #FF0000;">&quot;cursor.png&quot;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour déplacer, le curseur en fonction de la position de la souris, il faut pouvoir identifier cette image :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; Image<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; id<span style="color: #008080;">:</span> cursor<br />
&nbsp; &nbsp; &nbsp; &nbsp; x<span style="color: #008080;">:</span> width<span style="color: #000040;">/</span><span style="color: #0000dd;">2</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; y<span style="color: #008080;">:</span> height<span style="color: #000040;">/</span><span style="color: #0000dd;">2</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; source<span style="color: #008080;">:</span> <span style="color: #FF0000;">&quot;cursor.png&quot;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>On pourra alors déplacer l&rsquo;image en modifiant les variables x et y, en ajoutant le code suivant n&rsquo;importe où dans notre item :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; cursor.<span style="color: #007788;">x</span> <span style="color: #000080;">=</span> <span style="color: #0000dd;">100</span><br />
&nbsp; &nbsp; cursor.<span style="color: #007788;">y</span> <span style="color: #000080;">=</span> <span style="color: #0000dd;">100</span></div></td></tr></tbody></table></div>
<p>Pour terminer, il faut pouvoir récupérer les évènements souris survenant sur notre item. Cela est réalisé en créer un objet MouseArea de même dimension que l&rsquo;item :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; MouseArea<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; anchors.<span style="color: #007788;">fill</span><span style="color: #008080;">:</span> parent<br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Il faut ensuite demander à cette MouseArea de récupérer les clics sur le bouton gauche de la souris :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; MouseArea<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; anchors.<span style="color: #007788;">fill</span><span style="color: #008080;">:</span> parent<br />
&nbsp; &nbsp; &nbsp; &nbsp; acceptedButtons<span style="color: #008080;">:</span> Qt.<span style="color: #007788;">LeftButton</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>On va réagir à deux types d’évènements : un clic sur le bouton gauche, qui déclenche un évènement de type onPressed, et le déplacement de la souris avec le bouton enfoncé, qui déclenche un évènement de type onPositionChanged.<br />
Lors de ces évènements, on récupère la position de la souris grâce aux variables mouseX et mouseY que l&rsquo;on affecte aux positions de l&rsquo;image avec cursor.x et cursor.y. Cependant, il ne faut pas oublier que la position de cursor correspond au coin en haut à gauche de l&rsquo;image alors que la souris correspond au centre de l&rsquo;image. Il faut donc corriger en fonction des dimensions de l&rsquo;image :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; cursor.<span style="color: #007788;">x</span> <span style="color: #000080;">=</span> mouseX <span style="color: #000040;">-</span> <span style="color: #0000dd;">4</span><br />
&nbsp; &nbsp; cursor.<span style="color: #007788;">y</span> <span style="color: #000080;">=</span> mouseY – <span style="color: #0000dd;">4</span></div></td></tr></tbody></table></div>
<p>Pour terminer, après un changement de la couleur sélectionnée, il faut recalculer les variables selected_red, selected_green et selected_blue. Pour cela, on appelle un fonction JavaScript updateSelectedColor(), qu&rsquo;il faudra créer :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; MouseArea<br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; anchors.<span style="color: #007788;">fill</span><span style="color: #008080;">:</span> parent<br />
&nbsp; &nbsp; &nbsp; &nbsp; acceptedButtons<span style="color: #008080;">:</span> Qt.<span style="color: #007788;">LeftButton</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; onPressed<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cursor.<span style="color: #007788;">x</span> <span style="color: #000080;">=</span> mouseX <span style="color: #000040;">-</span> <span style="color: #0000dd;">4</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cursor.<span style="color: #007788;">y</span> <span style="color: #000080;">=</span> mouseY <span style="color: #000040;">-</span> <span style="color: #0000dd;">4</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; onPositionChanged<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cursor.<span style="color: #007788;">x</span> <span style="color: #000080;">=</span> mouseX <span style="color: #000040;">-</span> <span style="color: #0000dd;">4</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cursor.<span style="color: #007788;">y</span> <span style="color: #000080;">=</span> mouseY <span style="color: #000040;">-</span> <span style="color: #0000dd;">4</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour connaître la couleur sélectionnée, on ne dispose pas de fonction équivalente à Qimage::pixel(). Les seuls éléments que l&rsquo;on dispose sont : la position (x, y) de la couleur sélectionnée dans le widget de taille (width, height) et les composantes RGB de la couleur principale.<br />
Il faut donc calculer les composantes RGB de la couleur sélectionnée à partir de ces éléments et de la méthode utilisée pour dessiner l&rsquo;item.</p>
<p>Lorsque l&rsquo;on dessine le premier gradient, on va du blanc (1, 1, 1) à la couleur principale (R, G, B). Le rapport entre la position cursor.x et la largeur est équivalent au rapport entre la composante de la couleur principale et le blanc :</p>
<p><img src="http://blog.developpez.com/gpu/files/2013/02/composantes.png" class="alignnone" /></p>
<p>On obtient alors l&rsquo;équivalence suivante :</p>
<p>{r &#8211; 1} over {X} ~ = ~ {R &#8211; 1} over {width} ~ = ~ pente de la droite</p>
<p>Alors :</p>
<p>r ~ = ~ 1 ~ + ~ (R-1) {X} over {width} </p>
<p>De même pour les autres composantes :</p>
<p>g ~ = ~ 1 ~ + ~ (G-1) {X} over {width} newline<br />
b ~ = ~ 1 ~ + ~ (B-1) {X} over {width} </p>
<p>Lorsque l&rsquo;on dessine le second gradient, on va du transparent (alpha = 0) au noir (0, 0, 0, 1). La couleur final s&rsquo;écrit :</p>
<p>couleur finale ~ = ~ couleur initiale * (1 &#8211; alpha) ~ + ~ noir * alpha ~ = ~ couleur initiale * (1-alpha)</p>
<p>La transparence alpha est liée à la position cursor.y par :</p>
<p>alpha ~ = ~ {Y} over {height} </p>
<p>On obtient donc la formule suivante pour la composante rouge :</p>
<p>r&rsquo; ~ = ~ r * (1 &#8211; {Y} over {height} ) newline<br />
r&rsquo; ~ = ~ (1 + (R-1) {X} over {width}) * (1 &#8211; {Y} over {height} )</p>
<p>De même pour les autres composantes :</p>
<p>g&rsquo; ~ = ~ (1 + (G-1) {X} over {width}) * (1 &#8211; {Y} over {height} ) newline<br />
b&rsquo; ~ = ~ (1 + (B-1) {X} over {width}) * (1 &#8211; {Y} over {height} )</p>
<p>La fonction JavaScript s&rsquo;écrit alors, en utilisant ces formules :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; function updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ColorPickerContext.<span style="color: #007788;">selected_red</span> <span style="color: #000080;">=</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span>cursor.<span style="color: #007788;">y</span> <span style="color: #000040;">/</span> height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">*</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span> <span style="color: #000040;">+</span> <span style="color: #008000;">&#40;</span>cursor.<span style="color: #007788;">x</span> <span style="color: #000040;">/</span> width<span style="color: #008000;">&#41;</span> <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span>main_red <span style="color: #000040;">-</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ColorPickerContext.<span style="color: #007788;">selected_green</span> <span style="color: #000080;">=</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span>cursor.<span style="color: #007788;">y</span> <span style="color: #000040;">/</span> height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">*</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span> <span style="color: #000040;">+</span> <span style="color: #008000;">&#40;</span>cursor.<span style="color: #007788;">x</span> <span style="color: #000040;">/</span> width<span style="color: #008000;">&#41;</span> <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span>main_green <span style="color: #000040;">-</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; ColorPickerContext.<span style="color: #007788;">selected_blue</span> <span style="color: #000080;">=</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span> <span style="color: #000040;">-</span> <span style="color: #008000;">&#40;</span>cursor.<span style="color: #007788;">y</span> <span style="color: #000040;">/</span> height<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">*</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span> <span style="color: #000040;">+</span> <span style="color: #008000;">&#40;</span>cursor.<span style="color: #007788;">x</span> <span style="color: #000040;">/</span> width<span style="color: #008000;">&#41;</span> <span style="color: #000040;">*</span> <span style="color: #008000;">&#40;</span>main_blue <span style="color: #000040;">-</span> <span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h1>La classe GradientWidget</h1>
<p>Pour utiliser cet item QML dans du code C++, il est nécessaire d&rsquo;écrire un wrapper pour récupérer les signaux et slots de l&rsquo;item. Cette classe n&rsquo;est pas un widget et dérive donc de QObject (pour pouvoir utiliser le système de signaux et slots) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">class</span> GradientWidget <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> QObject<br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; Q_OBJECT</div></td></tr></tbody></table></div>
<p>Pour récupérer la couleur sélectionnée, il faut créer une variable pour chaque composante et utiliser la macro Q_PROPERTY pour la rendre accessible en QML. Il faut également définir les fonctions d&rsquo;écriture (pour modifier la variable depuis le QML) et de lecture (pour lire la variable à l&rsquo;extérieur de la classe) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">public</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; Q_PROPERTY<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span> &nbsp; &nbsp;selected_red<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;READ &nbsp; &nbsp; selectedRed<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;WRITE &nbsp; &nbsp;setSelectedRed<span style="color: #008000;">&#41;</span><br />
<br />
<span style="color: #0000ff;">private</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">float</span> &nbsp; selected_red<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">float</span> &nbsp; selectedRed<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">const</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000ff;">return</span> selected_red<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;setSelectedRed<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">float</span> red<span style="color: #008000;">&#41;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">&#123;</span> selected_red <span style="color: #000080;">=</span> red<span style="color: #008080;">;</span> selectedColorChanged<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Après avoir mis à jour la couleur avec la fonction setSelectedColor(), il faut émettre un signal contenant la couleur créée à partir des composantes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">signals<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;colorSelected<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> QColor <span style="color: #000040;">&amp;</span>color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">private</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;selectedColorChanged<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; emit colorSelected<span style="color: #008000;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; QColor<span style="color: #008080;">::</span><span style="color: #007788;">fromRgbF</span><span style="color: #008000;">&#40;</span>selected_red, selected_green, selected_blue<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Il faut également définir les variables et fonctions pour les deux autres composantes :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; Q_PROPERTY<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span> &nbsp; &nbsp;selected_green<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;READ &nbsp; &nbsp; selectedGreen<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;WRITE &nbsp; &nbsp;setSelectedGreen<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; Q_PROPERTY<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span> &nbsp; &nbsp;selected_blue<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;READ &nbsp; &nbsp; selectedBlue<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;WRITE &nbsp; &nbsp;setSelectedBlue<span style="color: #008000;">&#41;</span></div></td></tr></tbody></table></div>
<p>La création de l&rsquo;item ColorPicker est réalisée dans le constructeur de la classe :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">public</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; GradientWidget<span style="color: #008000;">&#40;</span>QWidget <span style="color: #000040;">*</span>parent <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Dans ce constructeur, il faut créer un objet QDeclarativeView pour afficher l&rsquo;item QML puis fournir le code QML à l&rsquo;aide la fonction setSource. La taille est fixée à 256&#215;256, comme dans le code QML. Dans cet exemple, le code QML est fournit dans un fichier .qml et référencé dans un fichier ressource .qrc :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">GradientWidget</span><span style="color: #008000;">&#40;</span>QWidget <span style="color: #000040;">*</span>parent<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008080;">:</span> QObject<span style="color: #008000;">&#40;</span>parent<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; view <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> QdeclarativeView<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; view<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>resize<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">256</span>, <span style="color: #0000dd;">256</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; view<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>setSource<span style="color: #008000;">&#40;</span>QUrl<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;qrc:/qml/colorpicker.qml&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Pour permettre au code QML de transmettre les variables contenant les composantes de la couleur sélectionnée, il faut récupérer le contexte par défaut de la vue et définir une propriété ColorPickerContext dans celui-ci. Cette propriété permet d&rsquo;accéder à l&rsquo;objet dans le code QML à partir de la variable ColorPickerContext et permettre ainsi d&rsquo;accéder aux propriétés définies ci-dessus :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; context <span style="color: #000080;">=</span> view<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>rootContext<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; context<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>setContextProperty<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;ColorPickerContext&quot;</span>, <span style="color: #0000dd;">this</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Les variables view et context sont définies dans l&rsquo;en-tête de la classe par :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">private</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; QDeclarativeView<span style="color: #000040;">*</span> &nbsp; &nbsp; &nbsp; view<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QDeclarativeContext<span style="color: #000040;">*</span> &nbsp; &nbsp;context<span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Lors de la création de l&rsquo;item, on attribue également une couleur principale par défaut à l&rsquo;aide de la fonction setMainColor() :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; setMainColor<span style="color: #008000;">&#40;</span>Qt<span style="color: #008080;">::</span><span style="color: #007788;">red</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Cette fonction setMainColor() est un slot ayant un paramètre, la couleur a attribuer :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">public</span> slots<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;setMainColor<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> QColor <span style="color: #000040;">&amp;</span>color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Pour transmettre cette couleur au code QML, on utilise la fonction setContextProperty(), qui permet d&rsquo;attribuer une valeur à une variable QML :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">setMainColor</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> QColor <span style="color: #000040;">&amp;</span>color<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; context<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>setContextProperty<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;main_red&quot;</span>, color.<span style="color: #007788;">redF</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; context<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>setContextProperty<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;main_green&quot;</span>, color.<span style="color: #007788;">greenF</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; context<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>setContextProperty<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;main_blue&quot;</span>, color.<span style="color: #007788;">blueF</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Pour terminer, il faut créer les fonctions show() et move() pour pouvoir afficher et déplacer la vue :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">public</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;show<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;move<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<br />
<span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">show</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; view<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>show<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><br />
<span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">move</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; view<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>move<span style="color: #008000;">&#40;</span>x, y<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h1>Le résultat final</h1>
<p>Voici le rendu obtenu :</p>
<p><img src="http://qt.developpez.com/exercices/01-colorpicker/1-degrade/gbdivers_qml/gbdivers_qml_capture.png" class="aligncenter" /></p>
<p><a href="http://qt.developpez.com/exercices/01-colorpicker/1-degrade/gbdivers_qml/gbdivers_qml_sources.zip">Télécharger les sources de cet article.</a></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Un ColorPicker avec Qt &#8211; Version Qt</title>
		<link>https://blog.developpez.com/gpu/?p=353</link>
		<comments>https://blog.developpez.com/gpu/?p=353#comments</comments>
		<pubDate>Fri, 15 Feb 2013 18:04:33 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Facile]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[QtQuick]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=353</guid>
		<description><![CDATA[Cet article est la solution que j&#8217;avais proposé à l&#8217;exercice Qt sur la création d&#8217;un ColorPicker (voir l&#8217;article précédent du blog). Il décrit la création d&#8217;un widget permettant d&#8217;afficher et sélectionner les nuances de gris d&#8217;une couleur. Conception Le widget &#8230; <a href="https://blog.developpez.com/gpu/?p=353">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Cet article est la solution que j&rsquo;avais proposé à l&rsquo;exercice Qt sur la création d&rsquo;un ColorPicker (voir <a href="http://blog.developpez.com/gpu/p11791/difficulte/facile/un-colorpicker-avec-qt-enonce-de-lexercice">l&rsquo;article précédent du blog</a>). Il décrit la création d&rsquo;un widget permettant d&rsquo;afficher et sélectionner les nuances de gris d&rsquo;une couleur.<br />
<span id="more-353"></span></p>
<h1>Conception</h1>
<p>Le widget devra simplement afficher un fond (les nuances de gris d&rsquo;une couleur) et gérer les évènements de la souris (clique et déplacement pour sélectionner une nuance de gris).</p>
<p>Il est possible de partir de plusieurs classes différentes pour créer le nouveau widget. Voyons les avantages et inconvénients de chacune :</p>
<ol>
<li>QWidget est la classe parent de tous les widgets de Qt. Elle fournit l&rsquo;interface de base commune de tous les objets visibles. Par défaut, elle n&rsquo;affiche rien et ne fait rien.</li>
<li>QScrollArea est destinée à afficher le contenu d&rsquo;un widget dans un autre, en gérant les barres de déplacement horizontale et verticale. Dans cet exercice, la présence de barre de déplacement n&rsquo;est pas souhaitée et il est préférable d&rsquo;ajuster la taille de la zone de dessin à la taille du widget.</li>
<li>QLabel permet d&rsquo;afficher un texte ou une image sans interaction avec l&rsquo;utilisateur. Même s&rsquo;il est possible de surcharger les fonctions de gestion des évènements souris, utiliser cette classe pour créer noter widget revient à ne pas respecter la conception de QLabel.</li>
<li>QpushButton permet également d&rsquo;afficher des images mais gère aussi les évènements souris. Mais cette classe n&rsquo;est pas destiner à gérer la position précise des évènements souris et de réagir différemment en fonction de celle-ci.</li>
</ol>
<p>Au final, même si certaine classe présentent des fonctionnalités qui seraient intéressantes (affichage d&rsquo;images, gestion des clics), seule l&rsquo;utilisation de QWidget respecte la conception des objets Qt.</p>
<p>En partant de QWidget, quels seront les fonctions qu&rsquo;il faudra implémenter ?</p>
<ol>
<li>L&rsquo;affichage des nuances de gris sera réalisée dans la fonction paintEvent(), qui prend en charge le dessin du widget. Il suffit de dessiner les nuances de gris puis la position de la couleur sélectionnée par un petit cercle.</li>
<li>La création des nuances de gris peut être réalisée directement dans paintEvent(). Cependant, cela implique de redessiner l&rsquo;image à chaque déplacement de la souris, ce qui peut rendre le widget non fluide. De plus, la récupération de la couleur sélectionnée utilisera la fonction QImage::pixel(). Au final, il est donc préférable de dessiner les nuances de gris dans une QImage et d&rsquo;afficher cette QImage dans la fonction paintEvent(). Cette image sera recalculée que lors d&rsquo;un changement de la couleur principale ou lors du redimensionnement du widget.</li>
<li>La fonction resizeEvent() sera utilisée pour mettre à jour l&rsquo;image contenant les nuances de gris.<br />
Les évènements souris seront gérés par les fonctions mousePressEvent(), mouseMoveEvent() et mouseReleaseEvent().</li>
<li>Le changement de couleur principale et la sélection d&rsquo;une couleur utilisera le système de signaux et slots, pour faciliter l&rsquo;interaction entre les widgets.</li>
</ol>
<p>Pour dessiner les différentes nuances de gris d&rsquo;un couleur, deux méthodes sont envisageables :</p>
<ol>
<li>Dessiner deux gradients superposés : un premier gradient linéaire, horizontal, allant du blanc (à gauche) à la couleur principale (à droite) ; un second gradient linéaire, horizontal, allant du transparent (en haut) au noir (en bas).</li>
<li>Faire varier les composantes S (saturation) et V (valeur) d&rsquo;une couleur en fonction des coordonnées (x, y) d&rsquo;un pixel. La saturation permet d&rsquo;aller du blanc (S=0) à la couleur (S=255) et correspond donc à l&rsquo;axe horizontal. La valeur permet d&rsquo;aller du noir (V=0) à la couleur (V=255) et correspond donc à l&rsquo;axe vertical.</li>
</ol>
<p>Ces deux méthodes sont identique visuellement (mais présentent en réalité des petites différences) et permettent toute deux d&rsquo;afficher toutes les nuances de gris d&rsquo;une couleur.</p>
<h1>Interface</h1>
<p>Pour créer l&rsquo;interface de notre classe, on crée une classe GradientWidget héritant de Qwidget, avec son constructeur :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">class</span> GradientWidget <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> QWidget<br />
<span style="color: #008000;">&#123;</span><br />
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">explicit</span> GradientWidget<span style="color: #008000;">&#40;</span>QWidget <span style="color: #000040;">*</span>parent <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Les fonctions de gestion des évènements surchargent les fonctions de même nom de QWidget :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">protected</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;mouseMoveEvent<span style="color: #008000;">&#40;</span>QMouseEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;mousePressEvent<span style="color: #008000;">&#40;</span>QMouseEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;mouseReleaseEvent<span style="color: #008000;">&#40;</span>QMouseEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;paintEvent<span style="color: #008000;">&#40;</span>QPaintEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;resizeEvent<span style="color: #008000;">&#40;</span>QResizeEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>La connexion avec les autres widgets est assurée par le signal envoyé lors de la sélection d&rsquo;une couleur et le slot reçu lors du changement de couleur principale :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">signals<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;colorSelected<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> QColor <span style="color: #000040;">&amp;</span>color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #0000ff;">public</span> slots<span style="color: #008080;">:</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;setMainColor<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> QColor <span style="color: #000040;">&amp;</span>color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Les variables privées permettent de conserver les couleurs principale et sélectionnée, l&rsquo;image dans laquelle on dessine les nuances de gris, la position du curseur et une variable boolean indiquant si la mouvement de la souris correspond à un clic ou non :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">private</span><span style="color: #008080;">:</span><br />
&nbsp; &nbsp; QColor &nbsp;m_main_color<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QColor &nbsp;m_selected_color<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QImage &nbsp;m_gradient_image<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QPen &nbsp; &nbsp;m_cursor_pen<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">int</span> &nbsp; &nbsp; m_cursor_diameter<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QPoint &nbsp;m_cursor_position<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">bool</span> &nbsp; &nbsp;m_tracking<span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Les variab Pour finir, deux fonctions privées, permettant de redessiner les nuances de gris et de mettre à jour la couleur sélectionnée :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #666666;">// private membres</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;updateGradientImage<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">void</span> &nbsp; &nbsp;updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<h1>Implémentation</h1>
<p>Commençons par l&rsquo;implémentation de la fonction updateSelectedColor(). Celle-ci récupère simplement la couleur du pixel de m_gradient_image à la position m_cursor_position, émet le signal colorSelected() puis appelle la fonction update() pour mettre ajour le widget :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">updateSelectedColor</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; m_selected_color <span style="color: #000080;">=</span> m_gradient_image.<span style="color: #007788;">pixel</span><span style="color: #008000;">&#40;</span>m_cursor_position<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; emit colorSelected<span style="color: #008000;">&#40;</span>m_selected_color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; update<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Création des nuances avec QLinearGradient</h2>
<p>La première version de la fonction updateGradientImage() utilise l&rsquo;approche à deux gradients :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">updateGradientImage</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span></div></td></tr></tbody></table></div>
<p>On crée une QPixmap de la taille du widget puis un QPainter pour dessiner dedans. Par défaut, nous n&rsquo;avons pas besoin de dessiner des traits donc on supprime le QPen :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; QPixmap pixmap<span style="color: #008000;">&#40;</span>size<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; QPainter painter<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span>pixmap<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">setPen</span><span style="color: #008000;">&#40;</span>QPen<span style="color: #008000;">&#40;</span>Qt<span style="color: #008080;">::</span><span style="color: #007788;">NoPen</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Pour dessiner des gradients linéaires, Qt propose une classe QLinearGradient. Il suffit donc de préciser les positions des points du gradient et les couleurs de ces points. Pour le premier gradient, les couleurs doivent aller du blanc (Qt::white) à la position (0, 0) à la couleur m_main_color à la position (with, 0) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; QLinearGradient h_gradient<span style="color: #008000;">&#40;</span>QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">0.0</span>, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span>, QPointF<span style="color: #008000;">&#40;</span>width<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; h_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">white</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; h_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span>, m_main_color<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Puis on dessine un rectangle de la taille du widget :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; painter.<span style="color: #007788;">setBrush</span><span style="color: #008000;">&#40;</span>QBrush<span style="color: #008000;">&#40;</span>h_gradient<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">drawRect</span><span style="color: #008000;">&#40;</span>rect<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Pour le second gradient, les couleurs doivent aller du transparent (Qt::transparent) à la position (0, 0) au noir (Qt::black) couleur m_main_color à la position (0, height) :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; QLinearGradient v_gradient<span style="color: #008000;">&#40;</span>QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">0.0</span>, <span style="color:#800080;">0.0</span><span style="color: #008000;">&#41;</span>, QPointF<span style="color: #008000;">&#40;</span><span style="color:#800080;">0.0</span>, height<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; v_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">transparent</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; v_gradient.<span style="color: #007788;">setColorAt</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span>, Qt<span style="color: #008080;">::</span><span style="color: #007788;">black</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">setBrush</span><span style="color: #008000;">&#40;</span>QBrush<span style="color: #008000;">&#40;</span>v_gradient<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">drawRect</span><span style="color: #008000;">&#40;</span>rect<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>On convertit ensuite la Qpixmap en Qimage et on la conserve dans m_gradient_image :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; m_gradient_image <span style="color: #000080;">=</span> pixmap.<span style="color: #007788;">toImage</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Pour finir, puisse que le gradient à été mis à jour, on met également à jour la couleur sélectionnée :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h2>Création des nuances avec HSV</h2>
<p>La seconde version de updateGradientImage() utilise deux boucles imbriquées qui parcourent l&rsquo;ensemble des pixels de l&rsquo;image et qui calcul la couleur en faisant varier la saturation et la valeur en fonction de la position (x, y). Pour commencer, il faut redimensionner l&rsquo;image de destination si celle-ci n&rsquo;est pas identique aux dimensions du widget :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">updateGradientImage</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>m_gradient_image.<span style="color: #007788;">rect</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">!</span><span style="color: #000080;">=</span> rect<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; m_gradient_image <span style="color: #000080;">=</span> QImage<span style="color: #008000;">&#40;</span>size<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, Qimage<span style="color: #008080;">::</span><span style="color: #007788;">Format_RGB32</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>La teinte est obtenue à partir de la couleur principale :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #0000ff;">float</span> h <span style="color: #000080;">=</span> m_main_color.<span style="color: #007788;">hsvHueF</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>On parcourt l&rsquo;ensemble des pixels de l&rsquo;image à l&rsquo;aide de deux boucles imbriquées :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> s<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> s<span style="color: #000080;">&lt;</span>width<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #000040;">++</span>s<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> v<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> v<span style="color: #008000;">&#40;</span>height<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">/</span><span style="color: #0000dd;">3</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008080;">?</span> Qt<span style="color: #008080;">::</span><span style="color: #007788;">white</span> <span style="color: #008080;">:</span> Qt<span style="color: #008080;">::</span><span style="color: #007788;">black</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Il reste plus qu&rsquo;a dessiner un cercle à la position m_cursor_position :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; painter.<span style="color: #007788;">setPen</span><span style="color: #008000;">&#40;</span>m_cursor_pen<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">setBrush</span><span style="color: #008000;">&#40;</span>QBrush<span style="color: #008000;">&#40;</span>Qt<span style="color: #008080;">::</span><span style="color: #007788;">NoBrush</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; painter.<span style="color: #007788;">drawEllipse</span><span style="color: #008000;">&#40;</span>m_cursor_position, defaut_diameter, defaut_diameter<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Le diamètre du cercle est une constante définie en début du fichier d&rsquo;implémentation :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> defaut_diameter <span style="color: #000080;">=</span> <span style="color: #0000dd;">5</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Lors d&rsquo;un évènement souris de type mousePressEvent, on teste si l&rsquo;utilisateur a cliqué sur le bouton gauche :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">mousePressEvent</span><span style="color: #008000;">&#40;</span>QMouseEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>event<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>button<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">==</span> Qt<span style="color: #008080;">::</span><span style="color: #007788;">LeftButton</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span></div></td></tr></tbody></table></div>
<p>Si c&rsquo;est le cas, on active le suivi des mouvements de la souris, on récupère la positon de la souris dans m_cursor_position puis on met à jour la couleur sélectionnée :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; m_tracking <span style="color: #000080;">=</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; m_cursor_position <span style="color: #000080;">=</span> event<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>pos<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Lors d&rsquo;un évènement de type mouseReleaseEvent, on déssactive le suivi de la souris et on met à jour la couleur sélectionnée :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">mouseReleaseEvent</span><span style="color: #008000;">&#40;</span>QMouseEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>event<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>button<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">==</span> Qt<span style="color: #008080;">::</span><span style="color: #007788;">LeftButton</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; m_tracking <span style="color: #000080;">=</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; m_cursor_position <span style="color: #000080;">=</span> event<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>pos<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Les mouvements de la souris sont détectés par l’évènement mouseMoveEvent. Pour que ces événements soient pris en compte pour le widget, il faut les activer en utilisant la fonction setMouseTracking(true) dans le constructeur. Ces évènements mouseMoveEvent sont activés même lorsqu&rsquo;aucun bouton n&rsquo;est appuyé. C&rsquo;est la fonction de la variable m_tracking d&rsquo;indiquer si on déplace la souris en conservant un bouton cliqué ou non.<br />
Lorsque l&rsquo;on maintient appuyé un bouton de la souris et que l&rsquo;on déplace celle-ci en dehors du widget, des évènements mouseMoveEvent continuent d&rsquo;être envoyé. Si on prend en compte ces évènements, on risque de demander la couleur de pixels en dehors de la taille de m_gradient_image, ce qui provoquera des erreurs. Il faut donc tester si la position de la souris est dans le widget. Au final, le code de mouseMoveEvent sera :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">mouseMoveEvent</span><span style="color: #008000;">&#40;</span>QMouseEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>m_tracking <span style="color: #000040;">&amp;&amp;</span> rect<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">contains</span><span style="color: #008000;">&#40;</span>event<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>pos<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; m_cursor_position <span style="color: #000080;">=</span> event<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>pos<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; updateSelectedColor<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">&#125;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Lors d&rsquo;un changement de taille du widget, il suffit de remettre à jour le gradient :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">resizeEvent</span><span style="color: #008000;">&#40;</span>QResizeEvent <span style="color: #000040;">*</span>event<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; Q_UNUSED<span style="color: #008000;">&#40;</span>event<span style="color: #008000;">&#41;</span><br />
&nbsp; &nbsp; m_cursor_position <span style="color: #000080;">=</span> rect<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">center</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; updateGradientImage<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Il ne reste plus qu&rsquo;a implémenter le constructeur :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">GradientWidget</span><span style="color: #008000;">&#40;</span>QWidget <span style="color: #000040;">*</span>parent<span style="color: #008000;">&#41;</span> <span style="color: #008080;">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; QWidget<span style="color: #008000;">&#40;</span>parent<span style="color: #008000;">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; m_cursor_position<span style="color: #008000;">&#40;</span>rect<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">center</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; m_tracking<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">false</span><span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; setAttribute<span style="color: #008000;">&#40;</span>Qt<span style="color: #008080;">::</span><span style="color: #007788;">WA_OpaquePaintEvent</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; setMouseTracking<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">true</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
&nbsp; &nbsp; updateGradientImage<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<p>Ainsi que le slot setMainColor() :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">void</span> GradientWidget<span style="color: #008080;">::</span><span style="color: #007788;">setMainColor</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> QColor <span style="color: #000040;">&amp;</span>color<span style="color: #008000;">&#41;</span><br />
<span style="color: #008000;">&#123;</span><br />
&nbsp; &nbsp; m_main_color <span style="color: #000080;">=</span> color<span style="color: #008080;">;</span><br />
&nbsp; &nbsp; updateGradientImage<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><br />
<span style="color: #008000;">&#125;</span></div></td></tr></tbody></table></div>
<h1>Le résultat final</h1>
<p>Voici le rendu obtenu :</p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/1-degrade/gbdivers/gbdivers_capture_ecran.png" alt="null" /></p>
<p><a href="http://www.developpez.net/forums/attachments/p66806d1283512715/c-cpp/bibliotheques/qt/exercice-qt-color-picker-deuxieme-partie-lancee/colorpicker.zip/">Télécharger les sources de cet article.</a></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Un ColorPicker avec Qt &#8211; Enoncé de l&#8217;exercice</title>
		<link>https://blog.developpez.com/gpu/?p=340</link>
		<comments>https://blog.developpez.com/gpu/?p=340#comments</comments>
		<pubDate>Fri, 15 Feb 2013 17:49:30 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Facile]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[QtQuick]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=340</guid>
		<description><![CDATA[Cet article est une reprise des sujets écris par johnlamericain et des documents que j&#8217;avais rédigé pour un exercice Qt. Cette première partie est un rappel (copié-collé) du sujet de l&#8217;exercice, posté sur le forum. Première partie : créer une &#8230; <a href="https://blog.developpez.com/gpu/?p=340">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Cet article est une reprise des sujets écris par <a href="http://www.developpez.net/forums/u44506/johnlamericain/">johnlamericain</a> et des documents que j&rsquo;avais rédigé pour un <a href="http://www.developpez.net/forums/d965869/c-cpp/bibliotheques/qt/exercice-qt-color-picker-deuxieme-partie-lancee/">exercice Qt</a>. Cette première partie est un rappel (copié-collé) du sujet de l&rsquo;exercice, posté sur le forum.</p>
<p><span id="more-340"></span></p>
<h1>Première partie : créer une zone de sélection avec gradient</h1>
<p>Bonjour à tous,</p>
<p>Nous savons que vous attendez avec impatience le prochain défi Qt, cependant un défi de qualité prends du temps à organiser et mettre en place. Pour palier à votre envie de développer et de vous faire les dents sur un nouveau projet, nous vous proposons un nouveau concept : les exercices Qt.</p>
<h2>Le principe</h2>
<p>Des petits sujets réguliers pour lesquels vous pouvez apporter une solution en détaillant quelques étapes clés imposés. La durée de l&rsquo;exercice dépendra de la difficulté de l&rsquo;énoncé. Il est possible qu&rsquo;un sujet conséquent en travail soit proposé et ainsi découpé en plusieurs parties représentant une suite d&rsquo;exercices sur le même thème.</p>
<p>Tout le monde peut ainsi proposer une solution pour répondre à l&rsquo;exercice posé. Dans le cas d&rsquo;un exercice en plusieurs parties, la meilleure solution pourra être utilisée par les autres participants pour la suite de l&rsquo;exercice.</p>
<p>Il est impossible pour l&rsquo;équipe de réunir un jury et réaliser un barème aussi poussé que celui des défis Qt. La meilleure solution sera ainsi désignée d&rsquo;un point de vue plus subjectif que pour les défis Qt par les responsables de la rubrique Qt, à savoir dourouc05 et moi-même et éventuellement d&rsquo;autres membres de l&rsquo;équipe Qt en fonction des disponibilités de chacun.</p>
<p>Les exercices seront ainsi proposés le vendredi (vous permettant d&rsquo;y travailler dès le week end après avoir découvert le nouveau sujet). La fin du dépôt des propositions aura lieu le vendredi soir à 23h59, un certain nombre de semaines (dépendant de la difficulté de celui-ci) après la présentation du sujet et le vainqueur sera désigné dans la semaine suivante avant la proposition du nouvel exercice.</p>
<p>Nous essayerons de faire attention à ce qu&rsquo;il n&rsquo;y ait pas de copie entre les codes des participants en appliquant la règle que le premier code publié sur le forum &laquo;&nbsp;appartient&nbsp;&raquo; au membre l&rsquo;ayant posté. Il existe bien sur des réponses à ces solutions sur internet, mais quel est l&rsquo;intérêt pour vous de les utiliser ?</p>
<p>Le premier sujet !</p>
<h2>Le thème</h2>
<p>Ce premier exercice fait parti d&rsquo;un thème qui est la réalisation d&rsquo;un color picker. Ci-dessous, quelques color pickers connus mais libre à vous de faire travailler votre imagination pour nous donner une meilleure solution au final :</p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/images/ColorPicker1.png" alt="null" /></p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/images/ColorPicker2.png" alt="null" /></p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/images/ColorPicker3.png" alt="null" /></p>
<p>Ce thème est découpé en trois parties qui toutes durent deux semaines. Un nouvel énoncé d&rsquo;exercice sera donc publié toutes les trois semaines (en comptant la semaine de correction).</p>
<h2>Première partie : widget personnalisé pour affiche et sélectionner la nuance de gris d&rsquo;une couleur</h2>
<p>Création d&rsquo;un widget permettant de sélectionner la nuance de gris à partir d&rsquo;une couleur. Seul le widget affichant le gradient de la nuance de gris sur une couleur sera évalué mais votre fenêtre peut afficher la couleur en cours et les valeurs de celle-ci pour démontrer le bon fonctionnement de celui-ci.</p>
<p>Le widget peut être carré, rectangulaire, sphérique, en cercle, libre à votre imagination.</p>
<p>Les étapes d&rsquo;explications imposées :</p>
<ol>
<li>Mise en place de la structure du widget (héritage, composition&#8230;) ;</li>
<li>Réalisation du gradient ;</li>
<li> Sélection et récupération de la couleur à partir du gradient.</li>
</ol>
<p>Vous avez donc jusqu&rsquo;au vendredi 3 septembre 23h59 pour nous fournir une solution. Le dépôt des solutions ce fait sur le forum à la suite de ce message.</p>
<p>J&rsquo;espère que l&rsquo;idée vous plait, n&rsquo;hésitez pas également à nous faire part de votre participation à la suite de ce message, de présenter votre solution en avant première et je finirai par dire : à vos claviers !</p>
<h1>Seconde partie : accompagner le widget d&rsquo;un sélecteur de couleur et amélioration de celui-ci</h1>
<p>L&rsquo;objectif est de continuer le widget en l&rsquo;améliorant et ajoutant d&rsquo;autres fonctionnalités. Pour ceux qui n&rsquo;auraient pas un widget fonctionnel, voici une archive contenant la classe du widget du vainqueur de la première partie à savoir gbdivers, améliorée par mes soins pour prendre en compte les malus qui lui ont été mis à la correction !</p>
<p>La première amélioration requise pour la partie deux est la création d&rsquo;un slider permettant la création de la couleur principale (teinte ou hue en anglais). L&rsquo;image ci-jointe vous montre de quoi je parle :</p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/images/partie2_hue.png" alt="null" /></p>
<p>Ce slider peut faire partie du widget ou être un widget indépendant. Je vous conseille de regarder la classe QSlider et les paramètres pour la personnalisée.</p>
<p>De plus un ensemble de champ QLineEdit respectant un certain format QIntValidator devra permettre d&rsquo;afficher et de modifier la valeur de la teinte, saturation ou valeur. En cas de modification, le changement se répercutera sur le widget par le déplacement du point de sélection. Il existe donc une connexion dans les deux sens entre ces différents éléments.</p>
<p>Par la suite, il faudra modifier le widget existant pour permettre également d&rsquo;afficher les dégradés de la saturation et de la valeur (et non plus la teinte, hue en anglais).</p>
<p>Exemples : </p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/images/partie2_saturation.png" alt="null" /></p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/images/partie2_value.png" alt="null" /></p>
<p>C&rsquo;est tout pour les étapes imposées dans cette seconde partie qui durera deux semaines.</p>
<p>Pour les personnes qui ont de l&rsquo;avance, vous pouvez réfléchir à n&rsquo;afficher que les couleurs disponibles pour le web et la représentation des dégradés dans les autres espaces colorimétriques.</p>
<p style="text-align: center"><img src="http://qt.developpez.com/exercices/01-colorpicker/images/partie2_web.png" alt="null" /></p>
<p>Si vous avez besoin d&rsquo;explications supplémentaires, n&rsquo;hésitez pas à poser des questions à la suite de ce message. De plus la correction et le code source de l&rsquo;ensemble des candidats pour la première partie est disponible dans ce sujet.</p>
<p>Bon courage !</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Pourquoi le C++ est un langage plus adapté pour les débutants que le C ?</title>
		<link>https://blog.developpez.com/gpu/?p=155</link>
		<comments>https://blog.developpez.com/gpu/?p=155#comments</comments>
		<pubDate>Mon, 21 Jan 2013 11:15:10 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Facile]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/gpu/?p=155</guid>
		<description><![CDATA[Un adage bien connu dit qu&#8217;enseigner, c&#8217;est répéter. Ceux qui fréquentent depuis quelque temps le forum C++ de Developpez le savent très bien : on revoit les mêmes discussions revenir régulièrement. Ce billet de blog va tenter d&#8217;analyser un peu &#8230; <a href="https://blog.developpez.com/gpu/?p=155">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Un adage bien connu dit qu&rsquo;enseigner, c&rsquo;est répéter. Ceux qui fréquentent depuis quelque temps le <a href="http://cpp.developpez.com" title="forum C++">forum C++</a> de Developpez le savent très bien : on revoit les mêmes discussions revenir régulièrement. Ce billet de blog va tenter d&rsquo;analyser un peu les arguments concernant l&rsquo;apprentissage du C++,  en se focalisant plus particulièrement sur les difficultés d&rsquo;utilisation. En particulier le raisonnement suivant, que l&rsquo;on entend souvent : &laquo;&nbsp;il est préférable d&rsquo;apprendre le C avec le C++&nbsp;&raquo;, ainsi que l&rsquo;affirmation suivante, souvent pas comprise : &laquo;&nbsp;le C++ est un meilleur langage pour débuter que le C&nbsp;&raquo;.<br />
<span id="more-155"></span></p>
<p><a href="http://www.developpez.net/forums/d1299991/c-cpp/cpp/pourquoi-cpp-langage-plus-adapte-debutants-c/">Vous pouvez commenter cet article sur le forum.</a></p>
<h1>Complexité du langage ou de l&rsquo;apprentissage</h1>
<p><em>Cette partie a été écrite par <a href="http://www.developpez.net/forums/u44106/jolyloic/">JolyLoic</a>.</em></p>
<p>Lorsque l&rsquo;on compare différents langages, il existe plusieurs notions de complexité, dont la confusion est une des causes qui fait que ces comparaisons s&rsquo;enflamment.</p>
<p>Il y a au moins :</p>
<ul>
<li>la complexité intrinsèque d&rsquo;un langage, celle qui correspond à l&rsquo;effort à réaliser pour y devenir un gourou qui connait tous les détails les plus arcanes. Cette complexité peut quasiment se mesurer au nombre de pages d&rsquo;un document décrivant le langage. Sur ce plan, le C++ est beaucoup plus complexe que le C, c&rsquo;est indéniable ;</li>
<li>la complexité au jour le jour d&rsquo;un langage, qui est liée au niveau de discipline mentale nécessaire pour coder, ainsi qu&rsquo;à la puissance des abstractions que l&rsquo;utilisateur peut mettre en œuvre pour l&rsquo;aider. Je suis convaincu que sur ce plan, le C++ est beaucoup plus simple que le C ;</li>
<li>la complexité d&rsquo;apprentissage. Elle correspond à la difficulté du parcours d&rsquo;un étudiant ne connaissant rien et devant apprendre assez du langage pour en devenir professionnel. Je pense là aussi que le C++ et plus simple que le C, mais la réponse est moins tranchée.</li>
</ul>
<p>La complexité intrinsèque et la complexité au jour le jour sont bien évidemment en opposition, puisque si l&rsquo;on complexifie un langage, ce n&rsquo;est pas par pur plaisir masochiste, mais bien pour le rendre plus puissant, donc plus efficace au jour le jour. La complexité d&rsquo;apprentissage est liée aux deux autres, mais aussi à la notion de progressivité. </p>
<p>Cette notion de progressivité de l&rsquo;apprentissage me semble importante. À part à un haut niveau post-bac, où l&rsquo;on suppose les étudiants comme des experts dans l&rsquo;art d&rsquo;ingurgiter des connaissances (et où à mon avis, on se trompe de méthode, mais c&rsquo;est un autre sujet), l&rsquo;ordre d&rsquo;apprentissage n&rsquo;a rien à voir avec un ordre logique, qui irait du plus théorique au plus pratique (ce qui consisterait pour notre langue maternelle à faire de la linguistique, de la grammaire et à apprendre le dictionnaire dans l&rsquo;ordre, avant de dire papa-maman, et dans le programmation à préconiser l&rsquo;apprentissage du théorème du point fixe, du lambda calculus et autres modélisations mathématiques des langages avant de faire un hello world), ou encore, pour parler d&rsquo;informatique du plus proche du matériel au plus éloigné (qui consisterait à faire de l&rsquo;électronique, puis de l&rsquo;assembleur, puis du C puis du C++).</p>
<p>Non, l&rsquo;apprentissage se fait de manière pragmatique, en parcourant le sujet à enseigner de manière en apparence erratique, mais avec pour objectifs :</p>
<ul>
<li>que chaque pas ne soit pas trop gros, afin de ne perdre personne ;</li>
<li>que le parcours soit à chaque étape intéressant, afin de motiver les gens.</li>
</ul>
<p>Un des points de passage de plus haute complexité du C comme du C++ est la notion de pointeurs, avec ses corollaires d&rsquo;allocation dynamique de la mémoire (mais même sans ça, la notion de pointeur et de valeur pointée pose bien des problèmes). Or, en C, impossible d&rsquo;écrire un programme un minimum intéressant sans utiliser explicitement des pointeurs. En C++ (ou des d&rsquo;autres langages comme java ou C#), l&rsquo;utilisation de ces pointeurs peut être cachée pendant assez longtemps et on a donc bien plus de liberté pour choisir un parcours le long duquel la courbe d&rsquo;apprentissage ne présente pas de murs trop importants.</p>
<h1>Apprentissage d&rsquo;un langage ou de la programmation</h1>
<p>Avant d&rsquo;expliquer et critiquer les phrases indiquées dans l&rsquo;introduction, il convient de préciser quel est l&rsquo;objectif recherché dans l&rsquo;apprentissage. Si l&rsquo;on est dans le cadre de l&rsquo;apprentissage d&rsquo;un langage précis, par exemple dans le cadre d&rsquo;un cours scolaire, dans lequel le langage à utiliser est imposé, alors bien évidemment, la question de savoir si le C++ est plus adapté pour les débutants n&rsquo;a pas lieu d&rsquo;être. Nous nous placeront donc dans le cas où la personne qui souhaite apprendre le C ou le C++ est libre de son choix.</p>
<p>Quelle est la différence entre apprendre un langage et apprendre la programmation ? Dans le premier cas, l&rsquo;attention se porte sur l&rsquo;apprentissage de la syntaxe du langage, ce que l&rsquo;on fait de ce langage compte peu finalement. Dans le second cas, l&rsquo;objectif est de savoir concevoir efficacement un programme permettant de répondre à une problématique donnée. Que ce soit dans le cadre amateur ou professionnel, ce dernier objectif est beaucoup plus intéressant et c&rsquo;est dans cette optique que se place ce billet de blog.</p>
<p>Précisons ce que signifie ce que l&rsquo;on entend par &laquo;&nbsp;efficacement&nbsp;&raquo;. Dans la création d&rsquo;un programme, on pense en général à la phase de codage et l&rsquo;on comprend facilement qu&rsquo;entre deux langages, si le premier permet d&rsquo;obtenir le même résultat que le second, mais en utilisant 10 fois moins de lignes de code, alors il sera beaucoup plus efficace d&rsquo;écrire un programme dans ce langage. Cependant, la création d&rsquo;une application ne se limite pas à écrire du code et l&rsquo;expérience montre que l&rsquo;on passe beaucoup de temps sur d&rsquo;autres tâches : la conception du programme, la correction des bugs, la validation du programme (vérifier que le programme donne le résultat correct). Le terme &laquo;&nbsp;efficacement&nbsp;&raquo; doit donc être considéré en analysant le temps global de développement et pas simplement la phase de codage.</p>
<h1>Conception impérative et objet</h1>
<p>La comparaison entre les différents paradigmes de programmation, leurs intérêts et inconvénients respectifs, leurs efficacités, n&rsquo;est pas le propos de cet article, je ne vais donc pas m&rsquo;attarder sur ce point. Signalons simplement que la raison même de la création de la programmation objet est de faciliter la création d&rsquo;application, on peut s&rsquo;attendre à ce que la conception en C++ utilisant des objets (correctement&#8230;) sera plus efficace qu&rsquo;en C.</p>
<h1>Erreurs à la compilation et l&rsquo;exécution</h1>
<h2>Problématique</h2>
<p>Probablement tous les développeurs expérimentés ont déjà eu le problème suivant, d&rsquo;un bug dont l&rsquo;origine était indéterminée et qui a nécessité plusieurs heures de recherche pour le corriger. Partant de ce constat, on peut tirer la règle suivante : plus la détection des erreurs est précoce, plus il sera facile de les corriger. Autrement dit, les erreurs survenant à la compilation seront beaucoup plus simples à corriger que les erreurs survenant lors de l&rsquo;exécution (et l&rsquo;une des forces des templates en C++ est de pouvoir justement réaliser beaucoup de vérifications à la compilation, sans surcoût à l&rsquo;exécution, mais cela sort du sujet de ce blog).</p>
<p>Pour être plus concret, voyons un exemple avec la vérification de la constance des variables (&laquo;&nbsp;const-correctness&nbsp;&raquo;). Le mot-clé const, utilisé pour le passage de paramètres dans les fonctions, est un contrat que passe l&rsquo;utilisateur d&rsquo;une fonction avec le créateur de cette fonction. Il permet de dire : cette variable ne sera pas modifiée dans cette fonction.</p>
<p>En théorie&#8230;</p>
<p>Voyons ce que cela donne en pratique. Imaginons une fonction foo() prenant une chaîne de caractères constante et pouvant modifier cette chaîne. Le respect de cette constance implique donc que la fonction ne doit pas travailler sur la chaîne passée en argument de la fonction (potentiellement non modifiable), mais sur une copie modifiable de cette chaîne.</p>
<h2>Cas du C</h2>
<p>Voyons par exemple le code suivant, qui convertit un simple const char* en char* (donc qui supprime le mot-clé const) :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void foo(const char* cstr) {<br />
&nbsp; &nbsp; char* str = cstr;<br />
&nbsp; &nbsp; bar(str); // modifie potentiellement str<br />
}<br />
&nbsp;<br />
int main()<br />
{ &nbsp;<br />
&nbsp; &nbsp;foo(&quot;une chaîne de caractères&quot;);<br />
&nbsp; &nbsp;return 0;<br />
}</div></td></tr></tbody></table></div>
<p>Lorsque l&rsquo;on compile avec un compilateur C, celui-ci ne donne qu&rsquo;un simple avertissement sur le fait que l&rsquo;on modifie une variable constante en non constante. Malheureusement, les warnings sont souvent ignorés par les débutants et le code ne sera pas corrigé. C&rsquo;est une mauvaise pratique largement répandue et c&rsquo;est souvent une bonne chose d&rsquo;activer l&rsquo;option -Werror dans gcc, pour que les warnings deviennent des erreurs). On va pouvoir observer des comportements différents en fonction du code de bar(). Si la fonction bar() ne modifie pas la chaîne, par exemple avec le code suivant, alors il n&rsquo;y aura pas de problème, le programme s&rsquo;exécutera sans erreur :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void bar(char* str) {<br />
&nbsp; &nbsp; printf(&quot;%s&quot; , str);<br />
}</div></td></tr></tbody></table></div>
<p>En revanche, si la fonction bar() modifie la chaîne, on va obtenir une erreur à l&rsquo;exécution. Par exemple, le code suivant :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void bar(char* str) {<br />
&nbsp; &nbsp; str[0] = 'X'; // on suppose que str existe et contient au moins un caractère<br />
&nbsp; &nbsp; printf(&quot;%s&quot; , str);<br />
}</div></td></tr></tbody></table></div>
<p>Ce code va générer l&rsquo;erreur suivante :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">result: runtime error &nbsp;time: 0.02s &nbsp;memory: 1676 kB &nbsp;signal: 11 (SIGSEGV)</div></td></tr></tbody></table></div>
<p>Ce message d&rsquo;erreur ne nous apprend pas grand-chose. Il indique simplement qu&rsquo;il y a un problème d&rsquo;accès sur une mémoire invalide, mais n&rsquo;indique pas que l&rsquo;on ne respecte pas le const-correctness et ne précise pas la ligne de code où se situe le problème. On imagine bien alors que dans un programme contenant plusieurs milliers de lignes de code, il sera difficile de trouver l&rsquo;erreur.</p>
<p>Même si le premier code de bar() ne provoque pas d&rsquo;erreur à l&rsquo;exécution, il n&rsquo;en reste pas moins que le code de foo() est la source du problème. Cependant, il n&rsquo;y a pas de validation réalisée par le compilateur et il est facilement possible dans ces conditions d&rsquo;avoir du code potentiellement problématique qui fonctionne&#8230; quelques temps. Et à l&rsquo;occasion d&rsquo;une modification mineure, ce code problématique va déclencher une erreur d&rsquo;exécution et la recherche de l&rsquo;erreur sera fastidieuse. On dit souvent, de façon humoristique, qu&rsquo;un code problématique et qui fonctionne quand même &laquo;&nbsp;que le programme tombe en marche&nbsp;&raquo;.</p>
<p>Sans cette vérification à la compilation, il est facile de se tromper, par exemple en écrivant le nom des fonctions. Supposons que l&rsquo;on a deux fonctions, une version qui ne modifie pas la chaîne et utilise donc directement la chaîne passée en argument ; une version qui modifie la chaîne, par exemple en appelant realloc(). Normalement, il faudrait récupérer le pointeur retourné par la fonction, mais comme on appelle la mauvaise fonction, on oublie de le faire :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">// version constante<br />
// s'utilise de la façon suivante : c_bar(str);<br />
void c_bar(const char* str) { <br />
&nbsp; &nbsp; do_something(str); <br />
}<br />
<br />
// version non constante<br />
// s'utilise de la façon suivante : str = c_bar(str);<br />
char* bar(char* str) { <br />
&nbsp; &nbsp; char* str2 = realloc(str, strlen(str)*2); <br />
&nbsp; &nbsp; if (str2) { str = str2; } else { printf(&quot;Error de réallocation&quot;); }<br />
&nbsp; &nbsp; do_something(str); <br />
&nbsp; &nbsp; return str; <br />
}<br />
<br />
void foo(const char* cstr) {<br />
&nbsp; &nbsp; // allocation et copie<br />
&nbsp; &nbsp; size_t len = strlen(cstr) + 1;<br />
&nbsp; &nbsp; char* str = malloc(len);<br />
&nbsp; &nbsp; strcpy(str, cstr);<br />
<br />
&nbsp; &nbsp; // appel de bar() au lieu de c_bar()<br />
&nbsp; &nbsp; bar(str); // problème !<br />
<br />
&nbsp; &nbsp; // libération<br />
&nbsp; &nbsp; free(str);<br />
}</div></td></tr></tbody></table></div>
<p>En C, lors de la compilation, il y aura un simple warning, qui est facilement manqué. À l&rsquo;exécution, le message d&rsquo;erreur n&rsquo;indique pas l&rsquo;origine du problème. Plus que le fait que le code à écrire est plus long qu&rsquo;en C++, il y aura une perte de temps très importante, plusieurs heures voir plusieurs jours, pour trouver l&rsquo;origine des erreurs.</p>
<h2>Cas du C++</h2>
<p>En C++, la situation sera beaucoup plus simple : ce code génère une erreur suivante, indiquant que l&rsquo;on essaie de convertir une variable constante en variable non constante :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">prog.cpp: In function ‘void foo(const char*)’:<br />
prog.cpp:5: error: invalid conversion from ‘const char*’ to ‘char*’</div></td></tr></tbody></table></div>
<p>L&rsquo;erreur est simple (profitons-en, ce n&rsquo;est pas toujours le cas en C++) et parfaitement localisée, la correction du code sera directe et rapide.</p>
<h1>Détection des fuites mémoires</h1>
<h2>Problématique</h2>
<p>Commençons par un rappel : qu&rsquo;est-ce qu&rsquo;une fuite mémoire ? Lorsque l&rsquo;on alloue une variable dynamique sur le Tas, le bloc mémoire alloué doit être détruit à un moment donné, quand on en a plus besoin, pour éviter de saturer la mémoire disponible. L&rsquo;adresse mémoire est sauvegardée dans un pointeur et il suffit d&rsquo;appeler la fonction adéquate pour libérer la mémoire, en fonction de la fonction utilisée pour l&rsquo;allouer : free() avec malloc ou calloc, delete avec new, delete[] avec new[]. Une fuite mémoire survient lorsque cette libération n&rsquo;est pas réalisée, par exemple si on perd le pointeur, si on n&rsquo;appelle pas la fonction de libération adéquate, voir qu&rsquo;on appelle pas du tout cette fonction.</p>
<p>La difficulté avec les fuites mémoires est que c&rsquo;est un problème qui survient exclusivement à l&rsquo;exécution, on retombe sur le souci décrit dans le chapitre précédent : la localisation pourra être complexe et longue. Il existe des outils d&rsquo;analyse des allocations, tel que valgrind, qui permet de rechercher ce type de problème (et il est recommandé de l&rsquo;utiliser régulièrement), mais s&rsquo;il est possible de minimiser les risques lors de la phase de codage, cela pourra améliorer l&rsquo;efficacité.</p>
<h2>Cas du C</h2>
<p>Revenons sur le code précédent de foo() et corrigeons le problème de const-correctness en copiant la chaîne constante dans une nouvelle chaîne non constante. Pour simplifier le code, il est possible d&rsquo;écrire deux fonctions d&rsquo;aide pour gérer la mémoire : createString(), pour allouer la mémoire et copier la chaîne, et destroyString(), pour libérer la mémoire. Une implémentation de ces fonctions peut-être la suivante :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">char* createString(const char* cstr) {<br />
&nbsp; &nbsp; size_t len = strlen(cstr) + 1;<br />
&nbsp; &nbsp; char* str = malloc(len);<br />
&nbsp; &nbsp; strcpy(str, cstr);<br />
&nbsp; &nbsp; return str;<br />
}<br />
<br />
void destroyString(char* str) {<br />
&nbsp; &nbsp; free(str)<br />
}</div></td></tr></tbody></table></div>
<p>Le code de la fonction foo() est modifié de la façon suivante :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void foo(const char* cstr) {<br />
&nbsp; &nbsp; char* str = createString(cstr);<br />
&nbsp; &nbsp; bar(str); // modifie potentiellement str<br />
&nbsp; &nbsp; destroyString(str);<br />
}<br />
<br />
int main()<br />
{ &nbsp;<br />
&nbsp; &nbsp;foo(&quot;une chaîne de caractères&quot;);<br />
&nbsp; &nbsp;return 0;<br />
}</div></td></tr></tbody></table></div>
<p>Dans un code aussi simple, il est peu probable que le développeur oublie de libérer la mémoire à la fin de la fonction foo(). Par contre, dans une situation plus complexe, dans lequel la création de la chaîne est réalisée à un endroit du programme, l&rsquo;utilisation à d&rsquo;autres endroits et la libération encore ailleurs, il peut être difficile de savoir si la mémoire est correctement libérée.</p>
<p>De plus, le code précédent n&rsquo;apporte pas de garanties fortes sur la libération. Par exemple, si le code entre createString() et destroyString() contient des return, la fonction destroy() ne sera pas appelée. Il est de la responsabilité du développeur de penser à appeler destroy() avant chaque return, ce qui peut être facilement oublié. Si la fonction manipule plusieurs objets dynamiques, il faut savoir à tout moment quelles sont les variables allouées et celles qui ne le sont pas.</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void foo(const char* cstr) {<br />
&nbsp; &nbsp; char* str = createString(cstr);<br />
&nbsp; &nbsp; if (test) <br />
&nbsp; &nbsp; &nbsp; &nbsp; return; // problème !<br />
&nbsp; &nbsp; destroyString(str);<br />
}</div></td></tr></tbody></table></div>
<p>Plus pernicieux, le code peut allouer un nouveau bloc mémoire et attribuer l’adresse dans un pointeur, ce qui invalidera celui-ci et provoquera une fuite.</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void foo(const char* cstr) {<br />
&nbsp; &nbsp; char* str = createString(cstr);<br />
&nbsp; &nbsp; if (test) <br />
&nbsp; &nbsp; &nbsp; &nbsp; str = createString(&quot;un nouvelle chaîne&quot;); // problème !<br />
&nbsp; &nbsp; destroyString(str);<br />
}</div></td></tr></tbody></table></div>
<p>En général, on trouve deux profils sur le forum : ceux qui croient qui ne feront jamais d&rsquo;erreurs aussi grossières et ceux qui savent qu&rsquo;il peut arriver de faire de telles erreurs. En général aussi, les premiers viennent sur le forum parce qu&rsquo;ils ont des problèmes avec leur code, les seconds viennent pour aider les premiers.<br />
Choisissez votre camp. Et si vous faites partie du premier groupe, posez-vous peut être des questions&#8230;</p>
<h2>Cas du C++</h2>
<p>La situation pourrait sembler plus compliquée en C++. En effet, le C++ ajoute un système de gestion d&rsquo;erreurs, absent du C : les exceptions. Ce système peut briser l&rsquo;ordre séquentiel d&rsquo;exécution du code et il peut être difficile de garantir la robustesse du code.</p>
<p>Cependant, le C++ propose également un système qui permet de transférer la responsabilité de la libération de la mémoire. En effet, grâce au <a href="http://cpp.developpez.com/faq/cpp/?page=pointeurs#POINTEURS_raii">RAII</a> (acquisition de ressources à l&rsquo;initialisation), la libération est sous la responsabilité d&rsquo;une classe au lieu d&rsquo;être sous celle de l&rsquo;utilisateur. Le RAII est un principe qui peut être implémenté par les concepteurs d&rsquo;une classe utilisant des ressources dynamiques, mais pour simplifier le travail, la bibliothèque standard STL fournit déjà de nombreuses classes encapsulant le RAII : std::string pour les chaînes, std::vector pour les tableaux, std::shared_ptr pour les pointeurs, etc. </p>
<p>Voyons par exemple une fonction utilisant une chaîne de caractères en C++ :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void foo(const char* cstr) {<br />
&nbsp; &nbsp; std::string str(cstr);<br />
&nbsp; &nbsp; // code quelconque pouvant modifier str<br />
}</div></td></tr></tbody></table></div>
<p>Ce code, en plus du fait d&rsquo;être beaucoup plus simple, nous garantit que quelles que soient les lignes de code contenues dans la fonction, la chaîne de caractères str sera correctement libérée lorsque l&rsquo;on sort de la fonction et quelle que soit la façon dont on sort de cette fonction (par return, par exception). L&rsquo;attention du développeur peut se focaliser sur le problème à résoudre et plus sur les détails de gestion de la mémoire.</p>
<h1>Conclusion</h1>
<p>D&rsquo;autres arguments peuvent être abordés pour justifier l&rsquo;apprentissage du C avant le C++ (on parle de l&rsquo;approche historique de l&rsquo;apprentissage du C++), mais une analyse peut facilement les mettre à mal. Par exemple, l&rsquo;argument selon lequel l&rsquo;apprentissage bas-niveau en C permet de mieux comprendre ensuite la programmation plus haut-niveau en C++. Outre le fait que l&rsquo;apprentissage de la gestion manuelle de la mémoire n&rsquo;est pas un prérequis pour comprendre les notions d&rsquo;allocation dynamique, de Pile et de Tas, la pratique montre que ceux qui abordent l&rsquo;apprentissage par cette approche se perdent dans les détails et le débogage et ont plus de mal à assimiler les concepts plus haut-niveau (conception objet, encapsulation, principes de programmations objet, etc.)</p>
<p>On pourrait croire que mes propos sont exagérés, qu&rsquo;il est rare de faire des erreurs aussi grossières, que cela n&rsquo;empêche pas de comprendre les concepts de programmation objet. En pratique, on voit régulièrement sur le forum les étudiants faire les mêmes erreurs d&rsquo;apprentissage (<a href="http://www.developpez.net/forums/d1298957/c-cpp/cpp/debuter/instanciation-statique-classe/#post7076206">un exemple récent</a>), mais ce n&rsquo;est pas une fatalité et il est possible de corriger notre façon d&rsquo;enseigner la programmation en C++.</p>
<h1>Remerciements</h1>
<p>Merci à <a href="http://www.developpez.net/forums/u44106/jolyloic/">JolyLoic</a> pour la partie qu&rsquo;il a rédigée et ses remarques, à <a href="http://www.developpez.net/forums/u329028/winjerome/">Winjerome</a>, <a href="http://www.developpez.net/forums/u169075/kalith/">Kalith</a> et <a href="http://www.developpez.net/forums/u240267/littlewhite/">LittleWhite</a> pour leur relecture et leurs remarques, à <a href="http://www.developpez.net/forums/u329028/winjerome/">Winjerome</a> pour ses corrections orthographiques.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Les accesseurs et les détails d&#8217;implémentation</title>
		<link>https://blog.developpez.com/gpu/?p=7</link>
		<comments>https://blog.developpez.com/gpu/?p=7#comments</comments>
		<pubDate>Thu, 21 Jun 2012 08:40:13 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Facile]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[C&#8217;est une discussion qui revient régulièrement sur le chat de Developpez.com. Une personne demande comment fait-on pour accéder aux variables membres privées d&#8217;une classe et on lui répond de créer des getter et setter. Viens alors un C++ien moyen (c&#8217;est-à-dire &#8230; <a href="https://blog.developpez.com/gpu/?p=7">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>C&rsquo;est une discussion qui revient régulièrement sur le <a href="http://chat.developpez.com/">chat de Developpez.com</a>. Une personne demande comment fait-on pour accéder aux variables membres privées d&rsquo;une classe et on lui répond de créer des getter et setter. Viens alors un C++ien moyen (c&rsquo;est-à-dire un casse-pied, en général moi) qui hurle au scandale et sort l&rsquo;adage bien connu : &laquo;&nbsp;les accesseurs, c&rsquo;est le mal&nbsp;&raquo;. S&rsquo;en suit une discussion sur pourquoi les accesseurs sont à éviter, quand j&rsquo;ai le temps et l&rsquo;humeur. <img src="https://blog.developpez.com/gpu/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /><br />
Dans cet article, je vais présenter les problèmes que posent les accesseurs concernant l&rsquo;exposition des détails d&rsquo;implémentation.<br />
<span id="more-7"></span><br />
<strong><em>Un exemple simple</em></strong></p>
<p>Imaginons que l&rsquo;on souhaite créer une classe représentant un point en 3D. On écrit alors le code suivant :</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">class</span> Point3D <span style="color: #008000;">&#123;</span> &nbsp;<br />
&nbsp; &nbsp; <span style="color: #0000ff;">float</span> x, y, z<span style="color: #008080;">;</span> &nbsp;<br />
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> <br />
&nbsp; &nbsp; <span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">float</span> x<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">const</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000ff;">return</span> x<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span> <br />
&nbsp; &nbsp; <span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">float</span> y<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">const</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000ff;">return</span> y<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span> <br />
&nbsp; &nbsp; <span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">float</span> z<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">const</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000ff;">return</span> z<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span> <br />
&nbsp;<br />
&nbsp; &nbsp; <span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span> setX<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span> v<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> x <span style="color: #000080;">=</span> v<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span> <br />
&nbsp; &nbsp; <span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span> setY<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span> v<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> y <span style="color: #000080;">=</span> v<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span> <br />
&nbsp; &nbsp; <span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span> setZ<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span> v<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> z <span style="color: #000080;">=</span> v<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span> <br />
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>On utilise ensuite cette classe à plusieurs endroits dans notre code, par exemple dans une fonction <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">glVertex</a> (remarque : pour ceux, qui comme <a href="http://www.developpez.net/forums/member.php?u=240267">LittleWhite</a> bloquent sur l&rsquo;utilisation de glVertex, je précise que ce n&rsquo;est qu&rsquo;un code d&rsquo;exemple ; glVertex est une fonction dépréciée et ne doit plus être utilisée) :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Point3D p { 1.0f, 1.0f, 1.0f }; <br />
glVertex3f(p.x(), p.y(), p.z());</div></td></tr></tbody></table></div>
<p>Jusque là, tout va bien. </p>
<p>(Ou presque. Si on modifie les coordonnées d&rsquo;un point, on obtient un nouveau point, bien distinct du premier. Les setters sont donc contraire au respect de la <a href="http://cpp.developpez.com/faq/cpp/?page=classes#CLASS_valeur">sémantique de valeur</a> et doivent être évités.)</p>
<p><strong><em>Là où les choses se gâtent</em></strong></p>
<p>Nos besoins évoluent. Au lieu de simplement vouloir faire de la représentation 3D sur nos points, on doit également faire des calculs pour de la simulation physique. On a la chance d&rsquo;avoir une carte graphique prenant en charge l&rsquo;extension <a href="http://www.opengl.org/registry/specs/ARB/gpu_shader_fp64.txt">fp64</a> et on décide donc d&rsquo;utiliser <em>double</em> au lieu de <em>float</em> :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class Point3D { &nbsp;<br />
&nbsp; &nbsp; double x, y, z; &nbsp;<br />
public: <br />
&nbsp; &nbsp; inline double x() const { return x; } <br />
&nbsp; &nbsp; inline double y() const { return y; } <br />
&nbsp; &nbsp; inline double z() const { return z; } <br />
&nbsp;<br />
&nbsp; &nbsp; inline void setX(double v) { x = v; } <br />
&nbsp; &nbsp; inline void setY(double v) { y = v; } <br />
&nbsp; &nbsp; inline void setZ(double v) { z = v; } <br />
};</div></td></tr></tbody></table></div>
<p>Malheureusement, le code qui utilise cette classe <em>Point3D</em> doit également être modifié pour pouvoir compiler. En effet, <em>glVertex3<strong>f</strong></em> prend comme paramètres des <em>float</em>, il faut maintenant utiliser <em>glVertex3<strong>d</strong></em> qui prend comme paramètres des <em>double</em> :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Point3D p { 1.0, 1.0, 1.0 }; <br />
glVertex3d(p.x(), p.y(), p.z());</div></td></tr></tbody></table></div>
<p>La modification est relativement facile. Maintenant imaginons que cette classe Point3D est utilisée dans des centaines de ligne de code. Imaginons aussi que toutes vos classes présentent le même problème d&rsquo;exposer des détails d&rsquo;implémentation interne. À chaque fois que l&rsquo;on doit modifier une classe, on se retrouve avec plein de bugs à la compilation et on doit perdre du temps à corriger toutes les lignes de code utilisant notre classe. C&rsquo;est un problème que connaissent beaucoup de débutants (et d&rsquo;autres développeurs plus anciens&#8230; mais aiment faire des erreurs de débutants). Quelle perte de temps ! On parle de couplage fort entre deux classes quand la modification de l&rsquo;une de classe implique la modification de l&rsquo;autre classe. Sinon, on parle de couplage faible.</p>
<p>En utilisant les templates, on améliore un peu la situation. On laisse la responsabilité de choisir le type utilisé en interne à l&rsquo;utilisateur de notre classe. Il sait donc quelle fonction appeler selon le contexte :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">template&lt;class T = float&gt; <br />
class Point3D { &nbsp;<br />
&nbsp; &nbsp; T x, y, z; &nbsp;<br />
public: <br />
&nbsp; &nbsp; inline T x() const { return x; } <br />
&nbsp; &nbsp; inline T y() const { return y; } <br />
&nbsp; &nbsp; inline T z() const { return z; } <br />
&nbsp;<br />
&nbsp; &nbsp; inline void setX(T v) { x = v; } <br />
&nbsp; &nbsp; inline void setY(T v) { y = v; } <br />
&nbsp; &nbsp; inline void setZ(T v) { z = v; } <br />
}; <br />
&nbsp;<br />
Point3D p { 1.0, 1.0, 1.0 }; <br />
glVertex3f(p.x(), p.y(), p.z()); <br />
Point3D&lt;double&gt; p2 { 1.0, 1.0, 1.0 }; <br />
glVertex3d(p2.x(), p2.y(), p2.z());</div></td></tr></tbody></table></div>
<p>Même si le code est meilleur que le précédent, il est encore améliorable. L&rsquo;idéal serait de ne plus avoir à choisir manuellement la fonction à appeler et laisser le compilateur faire le travail pour nous.</p>
<p><strong><em>Comment corriger ce problème ?</em></strong></p>
<p>Le premier principe qui n&rsquo;est pas respecté dans ce cas est le principe de ségrégation des interfaces. Ce principe dit &laquo;&nbsp;Une classe ou une fonction cliente ne doit pas dépendre d’interfaces dont elle n’a pas l’utilité&nbsp;&raquo; (<a href="http://blog.emmanueldeloget.com/index.php?post/2006/10/19/24-le-principe-de-segregation-des-interfaces">source</a>). Dit autrement, cela veut dire que si une classe A utilise une classe B et que cette classe B utilise une classe C, A n&rsquo;a pas à connaître C. Dans notre exemple, il faudrait que le code qui utilise la classe <em>Point3D</em> n&rsquo;a pas à connaître le type utilisé en interne (<em>float</em> ou <em>double</em>).</p>
<p>Mais le problème est plus profond (et critique) que cela. Le problème vient en fait d&rsquo;une mauvaise compréhension de ce qu&rsquo;est l&rsquo;encapsulation. La règle est la suivante : &laquo;&nbsp;On encapsule un comportement, pas des propriétés&nbsp;&raquo; (<a href="http://blog.emmanueldeloget.com/index.php?post/2006/08/30/11-le-principe-d-encapsulation">source</a>). Voyons ce que cela implique en pratique pour notre classe représentant un point en 3D. Si on pense en termes de propriétés, comme on l&rsquo;a fait au début, un point dans un espace 3D est effectivement un objet représenté par ses trois composantes réelles x, y et z. Si on pense en terme de comportement, l&rsquo;implémentation sera différente. Quels sont les comportements attendus pour notre point ? En suivant notre code d&rsquo;exemple précédant, le seul comportement que l&rsquo;on souhaite implémenter est de pouvoir l&rsquo;afficher. On écrit donc simplement le code suivant :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class Point3D { &nbsp;<br />
&nbsp; &nbsp; float x, y, z; &nbsp;<br />
public: <br />
&nbsp; &nbsp; void draw() const { glVertex3f(x, y, z); } <br />
}; <br />
&nbsp;<br />
Point3D p { 1.0f, 1.0f, 1.0f }; <br />
p.draw();</div></td></tr></tbody></table></div>
<p>La différence par rapport au code précédant est ridicule en terme de travail à fournir pour l&rsquo;implémentation. On a simplement refactorisé l&rsquo;appel à glVertex dans une fonction membre de Point3D. Par contre, en terme de sémantique, la différence est énorme : le code client n&rsquo;a plus besoin de connaître les détails d&rsquo;implémentation, notre code est plus facilement évolutif et donc efficace. Les modifications à apporter à notre code en cas de changement est localisé : on sait que si l&rsquo;on modifie une variable membre d&rsquo;une classe, on n&rsquo;a que les fonctions membres de la classe à modifier et rien d&rsquo;autre. </p>
<p>Si on est paresseux (et donc intelligent), on va utiliser la version template pour plus de souplesse, par exemple avec des spécialisations :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">template&lt;class T = float&gt; <br />
class Point3D { &nbsp;<br />
&nbsp; &nbsp; T x, y, z; &nbsp;<br />
public: <br />
&nbsp; &nbsp; void draw() const; <br />
}; <br />
&nbsp;<br />
template&lt;&gt; &nbsp;<br />
void Point3D&lt;float&gt;::draw() const { glVertex3f(x, y, z); } <br />
&nbsp;<br />
template&lt;&gt; &nbsp;<br />
void Point3D&lt;double&gt;::draw() const { glVertex3d(x, y, z); } <br />
&nbsp;<br />
Point3D p { 1.0, 1.0, 1.0 }; <br />
p.draw(); <br />
Point3D&lt;double&gt; p2 { 1.0, 1.0, 1.0 }; <br />
p2.draw();</div></td></tr></tbody></table></div>
<p>La version template demande un peu plus de ligne de code que la version non template et peu donc demander un peu plus de travail pour le développeur. Pour autant, elle est préférable puisqu&rsquo;il ne sera plus nécessaire de modifier le code en fonction des besoins du code client (respect du principe ouvert-fermé). Si on a plusieurs fonctions qui dépendent du type utilisé en interne, on peut également utiliser une classe de traits et polices :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">template&lt;class T = float&gt; <br />
struct gl_trait { <br />
&nbsp; &nbsp; typdef T internal; <br />
&nbsp; &nbsp; // static inline glVertex &nbsp;(internal x, internal y, internal z) const {} <br />
&nbsp; &nbsp; // static inline glNormal &nbsp;(internal x, internal y, internal z) const {} <br />
&nbsp; &nbsp; // static inline glTexCoord(internal x, internal y, internal z) const {} <br />
}; <br />
&nbsp;<br />
&lt;code&gt;template&lt;&gt; <br />
struct gl_trait&lt;float&gt; { <br />
&nbsp; &nbsp; static inline glVertex &nbsp;(internal x, internal y, internal z) const { glVertex3f(x, y, z); } <br />
&nbsp; &nbsp; static inline glNormal &nbsp;(internal x, internal y, internal z) const { glNormal3f(x, y, z); } <br />
&nbsp; &nbsp; static inline glTexCoord(internal x, internal y, internal z) const { glTexCoord3f(x, y, z); } <br />
}; <br />
&nbsp;<br />
&lt;code&gt;template&lt;&gt; <br />
struct gl_trait&lt;double&gt; { <br />
&nbsp; &nbsp; static inline glVertex &nbsp;(internal x, internal y, internal z) const { glVertex3d(x, y, z); } <br />
&nbsp; &nbsp; static inline glNormal &nbsp;(internal x, internal y, internal z) const { glNormal3d(x, y, z); } <br />
&nbsp; &nbsp; static inline glTexCoord(internal x, internal y, internal z) const { glTexCoord3d(x, y, z); } <br />
}; <br />
&nbsp;<br />
template&lt;class T&gt; <br />
class Point3D { &nbsp;<br />
&nbsp; &nbsp; gl_trait&lt;T&gt;::internal x, y, z; &nbsp;<br />
public: <br />
&nbsp; &nbsp; void draw() const { gl_trait&lt;T&gt;::glVertex(x, y, z); } <br />
}; <br />
&nbsp;<br />
Point3D p { 1.0, 1.0, 1.0 }; <br />
p.draw(); <br />
Point3D&lt;double&gt; p2 { 1.0, 1.0, 1.0 }; <br />
p2.draw();</div></td></tr></tbody></table></div>
<p>Avec ce code, la liste des fonctions à appeler en fonction du type utilisé en interne est localisé dans une même classe de traits. Et si on souhaite ajouter un nouveau type, il suffit d&rsquo;ajouter une nouvelle spécialisation pour la classe de traits, <em>sans rien modifier au code existant</em>.</p>
<p><em><strong>Pour terminer, un peu de lecture</strong></em><br />
Le respect de ces principes est une méthode pour éviter les couplages trop forts entre les classes. Il existe d&rsquo;autres méthodes pour découpler des classes (ie diminuer la force du couplage). Tout le monde connait par exemple la séparation du code des classes dans un fichier d&rsquo;en-tête (partie la moins susceptible d&rsquo;être modifiée) et un fichier d&rsquo;implémentation (partie plus facilement modifiable). On peut également citer l&rsquo;idiome Pimpl (Pointer To Implementation) ou l&rsquo;utilisation des signaux et slots, deux techniques très utilisée dans Qt.</p>
<p>En complément, Emmanuel Deloget a publié quelques articles intéressants sur les principes de programmation objet :</p>
<ul>
<li>valider et corriger une architecture objet <a href="http://blog.emmanueldeloget.com/index.php?post/2008/02/01/112-valider-et-corriger-une-architecture-objet-premiere-partie">première partie</a> et <a href="http://blog.emmanueldeloget.com/index.php?post/2008/02/29/114-valider-et-corriger-une-architecture-objet-seconde-partie">seconde partie</a> par Emmanuel Deloget ;</li>
<li><a href="http://blog.emmanueldeloget.com/index.php?post/2006/08/30/11-le-principe-d-encapsulation">le principe d&rsquo;encapsulation</a>, <a href="http://blog.emmanueldeloget.com/index.php?post/2006/10/19/24-le-principe-de-segregation-des-interfaces">le principe de ségrégation des interfaces</a> et <a href="http://blog.emmanueldeloget.com/index.php?post/2006/09/21/15-le-principe-ouvert-ferme">le principe ouvert-fermé</a> par Emmanuel Deloget ;</li>
<li><a href="http://blog.emmanueldeloget.com/index.php?post/2006/09/14/12-la-guerre-des-accesseurs">la guerre des accesseurs</a> par Emmanuel Deloget ;</li>
<li><a href="http://alp.developpez.com/tutoriels/traitspolicies/">présentation des classes de Traits et de Politiques en C++</a> par Alp Mestan ;</li>
<li><a href="http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Handle_Body#Pointer_To_Implementation_.28pImpl.29">l&rsquo;idome pImpl</a> (WikiBooks) ;</li>
<li><a href="http://www.boost.org/doc/libs/1_49_0/doc/html/signals.html">Boost.Signals</a> (documentation de Boost) ;</li>
<li><a href="http://qt-project.org/doc/qt-4.8/signalsandslots.html">les signaux et slots dans Qt</a> (documentation de Qt 4.8).</li>
</ul>
<p><em><strong>Commentaires</strong></em><br />
Vous pouvez réagir à ce billet sur le forum dans cette <a href="http://www.developpez.net/forums/d1234432/c-cpp/cpp/accesseurs-details-dimplementation/">discussion</a>. Merci.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Les signaux et slots dans Qt5</title>
		<link>https://blog.developpez.com/gpu/?p=12</link>
		<comments>https://blog.developpez.com/gpu/?p=12#comments</comments>
		<pubDate>Wed, 25 Apr 2012 15:19:18 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[Qt5]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[La nouvelle version de Qt vient de sortir en version 5 alpha. Cette version améliore la prise en charge de la nouvelle norme du C++, le C++11, et modifie ainsi le fonctionnement des signaux et slots de Qt. Cet article &#8230; <a href="https://blog.developpez.com/gpu/?p=12">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>La nouvelle version de Qt vient de sortir <a href="http://www.developpez.net/forums/d1209764/c-cpp/bibliotheques/qt/sortie-qt-5-alpha/">en version 5 alpha</a>. Cette version améliore la prise en charge de la nouvelle norme du C++, le C++11, et modifie ainsi le fonctionnement des signaux et slots de Qt. Cet article fait un rappel sur l&rsquo;utilisation des signaux et slots et présente les nouvelles fonctionnalités offertes par Qt5.<br />
<span id="more-12"></span><br />
<strong><em>Couplage entre classes et intérêt des signaux et slots</em></strong></p>
<p>Lorsque l&rsquo;on souhaite faire communiquer des objets entre eux, il nécessaire en général que les objets se connaissent mutuellement. Par exemple</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class Receiver { <br />
public: <br />
&nbsp; &nbsp; void slot() { cout &amp;lt;&amp;lt; &amp;quot;slot exécuté&amp;quot; &amp;lt;slot(); } <br />
private: <br />
&nbsp; &nbsp; Receiver* receiver; <br />
}; <br />
&nbsp;<br />
int main() { <br />
&nbsp; &nbsp; // on crée nos objets <br />
&nbsp; &nbsp; Receiver r; <br />
&nbsp; &nbsp; Sender s; <br />
&nbsp;<br />
&nbsp; &nbsp; // on connecte le sender et le receiver <br />
&nbsp; &nbsp; s.connect(&amp;amp;r); <br />
&nbsp;<br />
&nbsp; &nbsp; // on émet le signal <br />
&nbsp; &nbsp; s.signal(); <br />
}</div></td></tr></tbody></table></div>
<p>Ce code présente cependant plusieurs problèmes :</p>
<ul>
<li>il faut que la classe Sender connaisse la classe Receiver et cela implique d&rsquo;ajouter une dépendance (include) entre ces classes ;</li>
<li>il est nécessaire de créer une fonction spécifique dans Sender pour chaque type de classe Receiver et pour chaque slot possible ;</li>
<li>il ne permet pas de gérer des connexions vers plusieurs objets Receiver et il faut modifier le code pour gérer une liste de Receiver.</li>
</ul>
<p>Ces contraintes s&rsquo;accumulent dans un framework complexe comme Qt et cela alourdit fortement le code en ajoutant un nombre important de dépendances inutiles. Le code devient très vite ingérable (*).</p>
<p>Le système des signaux et slots permet de faire de s&rsquo;affranchir de ces contraintes. Il est ainsi possible de faire communiquer des objets entre eux sans qu&rsquo;il soit nécessaire que ces objets se connaissent mutuellement. On peut également choisir lors de la connexion le slot que l&rsquo;on souhaite appeler lorsque le signal est émis. On passe ainsi d&rsquo;un couplage fort (nécessité d&rsquo;avoir une dépendance) à un couplage faible (plus de dépendance nécessaire) et l&rsquo;on parle de découplage des classes.</p>
<p><em>(*) Il est possible d&rsquo;utiliser d&rsquo;autres approches que celle présentée ici. En particulier, on peut utiliser les pointeurs de fonctions ou équivalents (callback). Le lecteur intéressé par la question pourra par exemple étudier l&rsquo;approche utilisée dans <a href="http://www.boost.org/doc/libs/1_49_0/doc/html/signals.html">Boost.Signals</a></em></p>
<p><em><strong>Le système des signaux et slots dans Qt</strong></em></p>
<p>Le système de signaux et slots de Qt est relativement simple. Lorsqu&rsquo;un événement se produit, un signal est émis. Tous les slots qui sont connectés à ce signal sont alors exécutés. La fonction QObject::connect permet de créer une telle connexion. La forme la plus classique de cette fonction prend en paramètres un pointeur vers l&rsquo;objet émetteur, le nom du signal (ainsi que la liste des types des arguments du signal), un pointeur vers l&rsquo;objet recepteur et pour terminer le nom du slot (ainsi que la liste des types des arguments du slot). Il est possible de connecter plusieurs signaux à un même slot, un signal à plusieurs slots ou un signal avec un signal. Le compatibilité entre les classes et les signaux et slots est vérifiés lors de la compilation.</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">QAction a; <br />
QWidget w; <br />
QObject::connect( <br />
&nbsp; &nbsp; &amp;amp;a, SIGNAL(triggered()), // connecte le signal triggered() de QAction <br />
&nbsp; &nbsp; &nbsp; &amp;amp;w, SLOT(show())); &nbsp; &nbsp; &nbsp; // au slot show() de QWidget <br />
// ce code permet donc d'afficher le QWidget lorsque l'utilisateur active la QAction</div></td></tr></tbody></table></div>
<p><img src="http://doc-snapshot.qt-project.org/5.0/images/abstract-connections.png" alt="Exemple de connexions dans Qt" title="Exemples de connexions dans Qt" /></p>
<p>Les classes de Qt fournissent de nombreux signaux et slots par défaut. Ces signaux et slots seront disponibles dans les classes créées par les utilisateurs et dérivant des classes de Qt. Il est également possible de créer ses propres signaux et slots et de les connecter aux signaux et slots par défaut de Qt :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#include &nbsp;<br />
&nbsp;<br />
class Counter : public QObject // on hérite de QObject pour bénéficier des méta-informations de Qt <br />
{ <br />
&nbsp; &nbsp;Q_OBJECT // cette macro permet de générer les signaux et slots lors de la compilation <br />
&nbsp;<br />
public slots: <br />
&nbsp; &nbsp;void setValue(int value) <br />
&nbsp; &nbsp;{ <br />
&nbsp; &nbsp; &nbsp; if (value != m_value) // lorsque la valeur est changée <br />
&nbsp; &nbsp; &nbsp; { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;m_value = value; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;emit valueChanged(value); // on émet un signal valueChanged <br />
&nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp;} <br />
&nbsp;<br />
signals: <br />
&nbsp; &nbsp; void valueChanged(int newValue); // signal émis lorsque la valeur est changée <br />
&nbsp;<br />
private: <br />
&nbsp; &nbsp; int m_value; <br />
}; <br />
&nbsp;<br />
Counter a, b; <br />
&nbsp;<br />
// on connecte valueChanged de a à setValue de b <br />
QObject::connect(&amp;amp;a, &amp;amp;Counter::valueChanged, &amp;amp;b, &amp;amp;Counter::setValue); <br />
&nbsp;<br />
a.setValue(12); &nbsp;<br />
// a émet un signal valueChanged qui active le slot setValue de b <br />
// a.value() == 12, b.value() == 12 <br />
&nbsp;<br />
b.setValue(48); &nbsp; &nbsp; &nbsp;<br />
// b émet un signal valueChanged mais ce signal n'est pas connecté à un slot <br />
// a.value() == 12, b.value() == 48</div></td></tr></tbody></table></div>
<p><em><strong>Créer une connexion dans Qt 5</strong></em></p>
<p>Dans Qt 4, il est possible de connecter uniquement les fonctions déclarées comme signaux et<br />
slots dans la classes, comme indiqué dans le code d&rsquo;exemple précédant. Dans Qt 5, il est<br />
maintenant possible de connecter directement des pointeurs de fonctions ou d&rsquo;utiliser des<br />
fonctions lambdas.</p>
<p>La connexion de pointeurs de fonctions est similaire à une connexion classique, en donnant un pointeur sur les objets et sur les fonctions. Les classes émettrices et réceptrices doivent dériver de QObject mais il n&rsquo;est pas nécessaire de déclarer les fonctions slots avec le mot clé &laquo;&nbsp;slots&nbsp;&raquo;.</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class Sender : public QObject { <br />
&nbsp; &nbsp; Q_OBJECT <br />
&nbsp;<br />
signals: <br />
&nbsp; &nbsp; void send(int i = 0); <br />
}; <br />
&nbsp;<br />
class Receiver : public QObject { <br />
&nbsp;<br />
public: <br />
&nbsp; &nbsp; void receive(int i = 0) { std::cout &amp;lt;&amp;lt; &amp;quot;receive:&amp;quot; &amp;lt;&amp;lt; i &amp;lt;send(123);</div></td></tr></tbody></table></div>
<p>L&rsquo;avantage de cette écriture est que la compatibilité des paramètres est effectuée lors de la compilation et non lors de l’exécution. </p>
<p>Pour les fonctions lambdas :</p>
<div class="codecolorer-container text blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">// connexion avec les lambdas <br />
QObject::connect(s, &amp;amp;Sender::send, [r](int i = 0){ r-&amp;gt;receive(i); }); <br />
emit s-&amp;gt;send(456);</div></td></tr></tbody></table></div>
<p>Dans ce code, on capture le pointeur vers l&rsquo;objet recepteur et on récupère le paramètre passé par la fonction send() puis on appelle dans le corps de la lambda le fonction receive(). Le résultat obtenu est identique au code précédant, mais il est possible de faire beaucoup d&rsquo;autres choses dans la lambda (par exemple déconnecter tous le signaux ou parcourir tous les enfants de l&rsquo;objet récepteur).</p>
<p>Si le compilateur utilisé ne support pas les variadic template, les signaux et slots doivent avoir moins de 6 paramètres. </p>
<p><em><strong>Remarques</strong></em><br />
Vous pouvez télécharger un projet d&rsquo;exemple montrant ces nouvelles fonctionnalités en action : <a href="http://cpp.developpez.com/telecharger/detail/id/2744/-Qt-5-Les-nouvelles-possibilites-de-connexions-signaux-slots-dans-Qt-5">la page de téléchargement</a>.</p>
<p>Les images et codes d&rsquo;exemple sont issus de la documentation de Qt5 disponible à cette page : <a href="http://qt-project.org/doc/qt-5.0/qtcore/signalsandslots.html#signals-and-slots">Qt 5.0: Signals &amp; Slots</a>.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Introduction aux geometry shaders</title>
		<link>https://blog.developpez.com/gpu/?p=1</link>
		<comments>https://blog.developpez.com/gpu/?p=1#comments</comments>
		<pubDate>Thu, 12 Apr 2012 08:14:02 +0000</pubDate>
		<dc:creator><![CDATA[gbdivers]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Facile]]></category>
		<category><![CDATA[Geometry shaders]]></category>
		<category><![CDATA[OpenGL]]></category>
		<category><![CDATA[Qt]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[A la demande de LittleWhite, mon premier bloc technique est consacré à l&#8217;utilisation des geometry shader. Il n&#8217;y a rien de tres compliqué donc je présente un exemple simple apres quelques rappels. Pour ceux qui ne connaissent pas du tout &#8230; <a href="https://blog.developpez.com/gpu/?p=1">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>A la demande de LittleWhite, mon premier bloc technique est consacré à l&rsquo;utilisation des geometry shader. Il n&rsquo;y a rien de tres compliqué donc je présente un exemple simple apres quelques rappels. Pour ceux qui ne connaissent pas du tout les geometry shaders, je ferais un article plus detaille sur ce sujet.<br />
Pour faire au plus simple, j&rsquo;utilise Qt et en particulier QShaderProgram, qui gère les geometry shaders depuis Qt 4.7.<br />
<span id="more-1"></span></p>
<p><em><strong>Quelques rappels</strong></em><br />
Les geometry shaders sont optionnels et s&rsquo;intercalent entre les vertex shaders et les fragment shaders. Ils travaillent sur des primitives (triangle, ligne, points, avec un maximum de 6 vertices) et permettent de générer d&rsquo;autres primitives. Ils sont disponibles dans le Core depuis OpenGL 3.2 / GLSL 1.5 ou avant avec l&rsquo;extension GL_ARB_geometry_shader4 (en ajoutant le code #extension GL_EXT_geometry_shader4 : enable dans le geometry shader).</p>
<div class="codecolorer-container cpp blackboard" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br /></div></td><td><div class="cpp codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; <span style="color: #666666;">// Rechercher la présence de GL_EXT_geometry_shader4 pour &nbsp; </span><br />
&nbsp; &nbsp; <span style="color: #666666;">// tester si les geometry shaderssont pris en charge </span><br />
&nbsp; &nbsp; glGetString<span style="color: #008000;">&#40;</span>GL_EXTENSIONS<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> &nbsp;<br />
&nbsp; &nbsp; glGetStringi<span style="color: #008000;">&#40;</span>GL_EXTENSIONS, i<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// GL 3.2 </span><br />
&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; <span style="color: #666666;">// Créer un geometry shader </span><br />
&nbsp; &nbsp; glCreateShader<span style="color: #008000;">&#40;</span>GL_GEOMETRY_SHADER<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <br />
&nbsp; &nbsp; QGLShader shader<span style="color: #008000;">&#40;</span>QGLShader<span style="color: #008080;">::</span><span style="color: #007788;">Geometry</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// Qt 4.7 </span><br />
&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; <span style="color: #666666;">// Gérer les informations de position </span><br />
&nbsp; &nbsp; gl_PositionIn<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// position du vertex i en entrée </span><br />
&nbsp; &nbsp; gl_Position<span style="color: #008080;">;</span> <span style="color: #666666;">// position du nouveau vertex émis </span><br />
&nbsp; &nbsp; gl_in<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span>.<span style="color: #007788;">gl_Position</span><span style="color: #008080;">;</span> <span style="color: #666666;">// GLSL &gt; 330 </span><br />
&nbsp; &nbsp; gl_in.<span style="color: #007788;">length</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// GLSL &gt; 330, nombre de vertices en entrée </span><br />
&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; <span style="color: #666666;">// Générer des vertices et primitives (GLSL) </span><br />
&nbsp; &nbsp; EmitVertex<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <br />
&nbsp; &nbsp; EndPrimitive<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <br />
&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; <span style="color: #666666;">// Spécifier le type de primitives en entrée et sortie </span><br />
&nbsp; &nbsp; <span style="color: #666666;">// (GLSL) </span><br />
&nbsp; &nbsp; layout <span style="color: #008000;">&#40;</span>triangles<span style="color: #008000;">&#41;</span> in<span style="color: #008080;">;</span> <span style="color: #666666;">// points, lines, triangles, lines_adjacency ou triangles_adjacency </span><br />
&nbsp; &nbsp; layout <span style="color: #008000;">&#40;</span>triangle_strip<span style="color: #008000;">&#41;</span> out<span style="color: #008080;">;</span> <span style="color: #666666;">// points, line_strip ou triangle_strip </span><br />
&nbsp; &nbsp; layout <span style="color: #008000;">&#40;</span>max_vertices <span style="color: #000080;">=</span> <span style="color: #0000dd;">3</span><span style="color: #008000;">&#41;</span> out<span style="color: #008080;">;</span> <span style="color: #666666;">// maximum = GL_MAX_GEOMETRY_OUTPUT_VERTICES </span><br />
&nbsp;<br />
&nbsp; &nbsp; <span style="color: #666666;">// (Qt) </span><br />
&nbsp; &nbsp; program_shader.<span style="color: #007788;">setGeometryInputType</span><span style="color: #008000;">&#40;</span>GL_TRIANGLES<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <br />
&nbsp; &nbsp; program_shader.<span style="color: #007788;">setGeometryOutputType</span><span style="color: #008000;">&#40;</span>GL_LINE_STRIP<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <br />
&nbsp; &nbsp; program_shader.<span style="color: #007788;">setGeometryOutputVertexCount</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">6</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></div></td></tr></tbody></table></div>
<p>Lors de l&rsquo;appel à glDrawArray ou glDrawElement, il faut que le type spécifié corresponde au type d&rsquo;entrée du geometry shader. Les correspondances autorisées sont :</p>
<ol>
<li>points : GL_POINTS</li>
<li>lines : GL_LINES, GL_LINE_LOOP ou GL_LINE_STRIP</li>
<li>triangles : GL_TRIANGLES, GL_TRIANGLE_FAN ou GL_TRIANGLE_STRIP</li>
<li>lines_adjacency : GL_LINES_ADJACENCY</li>
<li>triangles_adjacency : GL_TRIANGLES_ADJACENCY</li>
</ol>
<p>Les nouveaux types ADJACENCY (GL_LINES_ADJACENCY, GL_LINE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY et GL_TRIANGLE_STRIP_ADJACENCY) permettent d&rsquo;accéder aux vertices des primitives voisines de la primitive en cours de traitement.</p>
<p><img src="http://gbelz.developpez.com/blog/geometry-shader.png" alt="Exemple d'affichage des vecteurs normaux" title="Exemple d'affichage des vecteurs normaux" /></p>
<p><strong><em>Un exemple simple</em></strong><br />
L&rsquo;exemple choisit est tres tres classique. Il permet d&rsquo;afficher le mailage d&rsquo;un mesh, les vecteurs normaux des faces et de calculer les vecteurs normaux de chaque face (flat mode). Cet exemple me permet également d&rsquo;introduire quelques classes simples, pour lire un fichier .obj (version simplifiee), calculer les vecteurs normaux, initialiser un contexte GL avec Qt.</p>
<p>Pour l&rsquo;affichage des vecteurs normaux et des triangles, on a besoin des positions et des normales, que l&rsquo;on passent donc aux shaders en attributs. </p>
<p>Pour afficher les vecteurs normaux dans le geometry shader, on prend un seul vertex en entree (input = points) et l&rsquo;on retourne une ligne (output = lignes, size = 2), dont le premier point est la position du vertex et le second point est la position du vertex auquel on ajoute le vecteur normal (multiplie par une constante, passee comme uniform, pour modifier la longueur du vecteur)</p>
<p>Pour afficher les triangles, on recupere un triangle en entree (input = triangles) et l&rsquo;on retourne 3 lignes (output = lignes, size = 6). Chaque points correspond a la position d&rsquo;un vertex decale legerement par le vecteur normal (pour que les lignes soient bien visibles et ne soient pas cachees par les triangles).</p>
<p><a href="http://gbelz.developpez.com/blog/geometry-shader.zip">Télécharger le projet d&rsquo;exemple</a></p>
<p><strong><em>A suivre&#8230;</em></strong></p>
<ol>
<li><a href="http://www.google.fr/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=4&amp;cts=1331312136494&amp;ved=0CEwQFjAD&amp;url=http%3A%2F%2Fweb.engr.oregonstate.edu%2F~mjb%2Fcs519%2FHandouts%2Fgeometry_shaders.1pp.pdf&amp;ei=ATZaT6zoFIiz8QPy3IXPDg&amp;usg=AFQjCNG9d-4Lpr0TbpuvK0iQrvmRVXKaMw&amp;sig2=zf5Oc9N-aIUMtgqDR-FXqw">Shrink, explosion, subdivision, silhouette, hedgehog</a></li>
<li><a href="http://www.twodee.org/blog/?p=805">Explosion, cheveux</a></li>
<li><a href="http://bat710.univ-lyon1.fr/~jpfarrug/Geometry.pdf">Instanciation et culling</a></li>
<li><a href="http://bat710.univ-lyon1.fr/~jciehl/Public/educ/M2PROIMA/2011/opengl_cm4.pdf">Instanciation et culling</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
