Mon, 03 Aug 2009 14:21:23 +0100
added keepme for ptdecode obj directory
philpem@5 | 1 | /* |
philpem@5 | 2 | # |
philpem@5 | 3 | # File : draw_gradient.h |
philpem@5 | 4 | # ( C++ header file - CImg plug-in ) |
philpem@5 | 5 | # |
philpem@5 | 6 | # Description : Plugin that can be used to draw color gradient on images. |
philpem@5 | 7 | # This file is a part of the CImg Library project. |
philpem@5 | 8 | # ( http://cimg.sourceforge.net ) |
philpem@5 | 9 | # |
philpem@5 | 10 | # Copyright : Jerome Boulanger |
philpem@5 | 11 | # ( http://www.ricam.oeaw.ac.at/people/page.cgi?firstn=Jerome;lastn=Boulanger ) |
philpem@5 | 12 | # |
philpem@5 | 13 | # License : CeCILL v2.0 |
philpem@5 | 14 | # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) |
philpem@5 | 15 | # |
philpem@5 | 16 | # This software is governed by the CeCILL license under French law and |
philpem@5 | 17 | # abiding by the rules of distribution of free software. You can use, |
philpem@5 | 18 | # modify and/ or redistribute the software under the terms of the CeCILL |
philpem@5 | 19 | # license as circulated by CEA, CNRS and INRIA at the following URL |
philpem@5 | 20 | # "http://www.cecill.info". |
philpem@5 | 21 | # |
philpem@5 | 22 | # As a counterpart to the access to the source code and rights to copy, |
philpem@5 | 23 | # modify and redistribute granted by the license, users are provided only |
philpem@5 | 24 | # with a limited warranty and the software's author, the holder of the |
philpem@5 | 25 | # economic rights, and the successive licensors have only limited |
philpem@5 | 26 | # liability. |
philpem@5 | 27 | # |
philpem@5 | 28 | # In this respect, the user's attention is drawn to the risks associated |
philpem@5 | 29 | # with loading, using, modifying and/or developing or reproducing the |
philpem@5 | 30 | # software by the user in light of its specific status of free software, |
philpem@5 | 31 | # that may mean that it is complicated to manipulate, and that also |
philpem@5 | 32 | # therefore means that it is reserved for developers and experienced |
philpem@5 | 33 | # professionals having in-depth computer knowledge. Users are therefore |
philpem@5 | 34 | # encouraged to load and test the software's suitability as regards their |
philpem@5 | 35 | # requirements in conditions enabling the security of their systems and/or |
philpem@5 | 36 | # data to be ensured and, more generally, to use and operate it in the |
philpem@5 | 37 | # same conditions as regards security. |
philpem@5 | 38 | # |
philpem@5 | 39 | # The fact that you are presently reading this means that you have had |
philpem@5 | 40 | # knowledge of the CeCILL license and that you accept its terms. |
philpem@5 | 41 | # |
philpem@5 | 42 | */ |
philpem@5 | 43 | |
philpem@5 | 44 | #ifndef cimg_plugin_draw_gradient |
philpem@5 | 45 | #define cimg_plugin_draw_gradient |
philpem@5 | 46 | |
philpem@5 | 47 | // Convert the couple (shape,profile) into a description string |
philpem@5 | 48 | static inline const char *get_gradient_str(const int shape, const int profile) { |
philpem@5 | 49 | static char buf[128]; |
philpem@5 | 50 | switch(shape) { |
philpem@5 | 51 | case 0: std::sprintf(buf,"linear shape and");break; |
philpem@5 | 52 | case 1: std::sprintf(buf,"spheric shape and");break; |
philpem@5 | 53 | case 2: std::sprintf(buf,"conic shape and");break; |
philpem@5 | 54 | case 3: std::sprintf(buf,"square shape and");break; |
philpem@5 | 55 | case 4: std::sprintf(buf,"rectangle (L1) shape and");break; |
philpem@5 | 56 | case 5: std::sprintf(buf,"rectangle (Linf) shape and");break; |
philpem@5 | 57 | case 6: std::sprintf(buf,"Gaussian shape and");break; |
philpem@5 | 58 | default: std::sprintf(buf,"undefined shape and");break; |
philpem@5 | 59 | } |
philpem@5 | 60 | switch(profile) { |
philpem@5 | 61 | case 0: std::strcat(buf," linear profile");break; |
philpem@5 | 62 | case 1: std::strcat(buf," wave profile");break; |
philpem@5 | 63 | case 2: std::strcat(buf," ring/bar profile");break; |
philpem@5 | 64 | case 3: std::strcat(buf," exponential");break; |
philpem@5 | 65 | case 4: std::strcat(buf," vanishing wave profile");break; |
philpem@5 | 66 | case 5: std::strcat(buf," vanishing ring/bar profile");break; |
philpem@5 | 67 | case 6: std::strcat(buf," circ diffraction (Airy) profile");break; |
philpem@5 | 68 | case 7: std::strcat(buf," rect diffraction (sinc2) profile");break; |
philpem@5 | 69 | default: std::strcat(buf," undefined profile");break; |
philpem@5 | 70 | } |
philpem@5 | 71 | return buf; |
philpem@5 | 72 | } |
philpem@5 | 73 | |
philpem@5 | 74 | template<typename tc> |
philpem@5 | 75 | void _draw_gradient_profile(T *const ptr, const float opacity, const float r, |
philpem@5 | 76 | const tc *const color0, const tc *const color1, |
philpem@5 | 77 | const int profile) { |
philpem@5 | 78 | const unsigned int id = (color0?1:0) + (color1?2:0); |
philpem@5 | 79 | const tc col0 = color0?*color0:0, col1 = color1?*color1:0; |
philpem@5 | 80 | switch(profile) { |
philpem@5 | 81 | case 0: { // linear |
philpem@5 | 82 | switch(id) { // map the 3 cases |
philpem@5 | 83 | case 3: *ptr = (T)((1-opacity)**ptr + opacity*(col0*(1.f-r)+col1*r)); break; |
philpem@5 | 84 | case 1: if (r<1) *ptr = (T)((1-opacity*(1-r))**ptr + col0*opacity*(1-r)); break; |
philpem@5 | 85 | case 2: if (r>0) *ptr = (T)((1-opacity*r)**ptr + col1*opacity*r); break; |
philpem@5 | 86 | default: break; |
philpem@5 | 87 | } break; |
philpem@5 | 88 | } |
philpem@5 | 89 | case 1: { // waves |
philpem@5 | 90 | const float f = (1 - (float)std::cos(4.5f*r*2.f*cimg::valuePI))/2; |
philpem@5 | 91 | switch(id) { // map the 3 cases |
philpem@5 | 92 | case 3: *ptr = (T)((1-opacity)**ptr + opacity*(col0*(1.f-f)+col1*f)); break; |
philpem@5 | 93 | case 1: if (f<1) *ptr = (T)((1-opacity*(1-f))**ptr + col0*opacity*(1-f)); break; |
philpem@5 | 94 | case 2: if (f>0) *ptr = (T)((1-opacity*f)**ptr + col1*opacity*f); break; |
philpem@5 | 95 | default: break; |
philpem@5 | 96 | } break; |
philpem@5 | 97 | } |
philpem@5 | 98 | case 2:{ // ring/bar |
philpem@5 | 99 | const float f = (1 + (float)std::cos(r*2.f*cimg::valuePI))/2; |
philpem@5 | 100 | switch(id) { // map the 3 cases |
philpem@5 | 101 | case 3: *ptr = (T)((1-opacity)**ptr + opacity*(col0*(1.f-f)+col1*f)); break; |
philpem@5 | 102 | case 1: if (f<1) *ptr = (T)((1-opacity*(1-f))**ptr + col0*opacity*(1-f)); break; |
philpem@5 | 103 | case 2: if (f>0) *ptr = (T)((1-opacity*f)**ptr + col1*opacity*f); break; |
philpem@5 | 104 | default: break; |
philpem@5 | 105 | } break; |
philpem@5 | 106 | } |
philpem@5 | 107 | case 3: { // exponential |
philpem@5 | 108 | const float f = 1 - (float)std::exp(-r); |
philpem@5 | 109 | switch(id) { // map the 3 cases |
philpem@5 | 110 | case 3: *ptr = (T)((1-opacity)**ptr + opacity*(col0*(1.f-f)+col1*f)); break; |
philpem@5 | 111 | case 1: if (f<1) *ptr = (T)((1-opacity*(1-f))**ptr + col0*opacity*(1-f)); break; |
philpem@5 | 112 | case 2: if (f>0) *ptr = (T)((1-opacity*f)**ptr + col1*opacity*f); break; |
philpem@5 | 113 | default: break; |
philpem@5 | 114 | } break; |
philpem@5 | 115 | } |
philpem@5 | 116 | case 4: { // vanishing wave |
philpem@5 | 117 | const float f = (1 - (float)std::cos(4.5f*r*2.f*cimg::valuePI))/2, o = r<.9f?(float)std::exp(-.5*r*r*12.f):0; |
philpem@5 | 118 | switch(id) { // map the 3 cases |
philpem@5 | 119 | case 3: if (o>0) *ptr = (T)((1-o)**ptr + o*(col0*(1.f-f)+col1*f)); break; |
philpem@5 | 120 | case 1: if (f<1) *ptr = (T)((1-o*(1-f))**ptr + col0*o*(1-f)); break; |
philpem@5 | 121 | case 2: if (f>0) *ptr = (T)((1-o*f)**ptr + col1*o*f); break; |
philpem@5 | 122 | default: break; |
philpem@5 | 123 | } break; |
philpem@5 | 124 | } |
philpem@5 | 125 | case 5: { // vanishing ring/bar |
philpem@5 | 126 | const float f = (1 + (float)std::cos(r*2.f*cimg::valuePI))/2, o = r<.9?(float)std::exp(-.5*r*r*12.f):0; |
philpem@5 | 127 | switch(id) { // map the 3 cases |
philpem@5 | 128 | case 3: if (o>0) *ptr = (T)((1-o)**ptr + o*(col0*(1.f-f)+col1*f)); break; |
philpem@5 | 129 | case 1: if (f<1) *ptr = (T)((1-o*(1-f))**ptr + col0*o*(1-f)); break; |
philpem@5 | 130 | case 2: if (f>0) *ptr = (T)((1-o*f)**ptr + col1*o*f); break; |
philpem@5 | 131 | default: break; |
philpem@5 | 132 | } break; |
philpem@5 | 133 | } |
philpem@5 | 134 | case 6: { // diffraction pattern of a circular aperture (Airy function) |
philpem@5 | 135 | #define myj1(x) (std::sin((x)<3?(x)*2.2/3:(x)-0.8)*std::exp(-std::pow((x)/5.0,1/3.0))) |
philpem@5 | 136 | const float a = 10*(float)cimg::valuePI*r, tmp = a<0.2?.5f:((float)myj1(a)/a), f = 1-4*tmp*tmp; |
philpem@5 | 137 | #undef myj1 |
philpem@5 | 138 | switch(id) { // map the 3 cases |
philpem@5 | 139 | case 3: *ptr = (T)((1-opacity)**ptr + opacity*(col0*(1.f-f)+col1*f)); break; |
philpem@5 | 140 | case 1: if (f<1) *ptr = (T)((1-opacity*(1-f))**ptr + col0*opacity*(1-f)); break; |
philpem@5 | 141 | case 2: if (f>0) *ptr = (T)((1-opacity*f)**ptr + col1*opacity*f); break; |
philpem@5 | 142 | default: break; |
philpem@5 | 143 | } |
philpem@5 | 144 | break; |
philpem@5 | 145 | } |
philpem@5 | 146 | case 7: { // diffraction pattern of a rectangular function (sinc function) |
philpem@5 | 147 | const float a = 10*(float)cimg::valuePI*r, tmp = a==0?1:(float)std::sin(a)/a, f = 1-tmp*tmp; |
philpem@5 | 148 | switch(id) { // map the 3 cases |
philpem@5 | 149 | case 3: *ptr = (T)((1-opacity)**ptr + opacity*(col0*(1.f-f)+col1*f)); break; |
philpem@5 | 150 | case 1: if (f<1) *ptr = (T)((1-opacity*(1-f))**ptr + col0*opacity*(1-f)); break; |
philpem@5 | 151 | case 2: if (f>0) *ptr = (T)((1-opacity*f)**ptr + col1*opacity*f); break; |
philpem@5 | 152 | default: break; |
philpem@5 | 153 | } break; |
philpem@5 | 154 | } |
philpem@5 | 155 | default: |
philpem@5 | 156 | CImgArgumentException("CImg<%s>::draw_gradient : unknown profile parameter",pixel_type()); break; |
philpem@5 | 157 | } |
philpem@5 | 158 | } |
philpem@5 | 159 | |
philpem@5 | 160 | //! Draw a gradient with various shape and profile |
philpem@5 | 161 | /** |
philpem@5 | 162 | \param x0 X-coordinate of the 1st control point |
philpem@5 | 163 | \param y0 Y-coordinate of the 1st control point |
philpem@5 | 164 | \param x1 X-coordinate of the 2nd control point |
philpem@5 | 165 | \param y1 Y-coordinate of the 2nd control point |
philpem@5 | 166 | \param color0 Array of dimv() values of type \c T, defining the 1st color. |
philpem@5 | 167 | \param color1 Array of dimv() values of type \c T, defining the 2nd color. |
philpem@5 | 168 | \param shape shape of the gradient (0,3) |
philpem@5 | 169 | \param profile select a profile function (0,7) |
philpem@5 | 170 | \param opacity Drawing opacity. |
philpem@5 | 171 | \note |
philpem@5 | 172 | - if one color is NULL then the gradient is done to transparency |
philpem@5 | 173 | **/ |
philpem@5 | 174 | template<typename tc> |
philpem@5 | 175 | CImg<T>& draw_gradient(const int x0, const int y0, const int x1, const int y1, |
philpem@5 | 176 | const tc *const color0, const tc *const color1, |
philpem@5 | 177 | const int shape=0, const int profile=0, const float opacity=1.0f){ |
philpem@5 | 178 | if (is_empty()) return *this; |
philpem@5 | 179 | if (!color0 && !color1) |
philpem@5 | 180 | throw CImgArgumentException("CImg<%s>::draw_gradient : The two specified colors are (null).", |
philpem@5 | 181 | pixel_type()); |
philpem@5 | 182 | if (profile<0 || profile>7) { // catch this case before entering in the for loop |
philpem@5 | 183 | CImgArgumentException("CImg<%s>::draw_gradient : unknown profile parameter",pixel_type()); |
philpem@5 | 184 | return *this; |
philpem@5 | 185 | } |
philpem@5 | 186 | const float abx = (float)x1-x0, aby = (float)y1-y0, ab2 = abx*abx + aby*aby; // pt A=(x0,y0), B=(x1,y1) |
philpem@5 | 187 | const tc *pcol0 = color0, *pcol1 = color1; |
philpem@5 | 188 | T *ptr = data; |
philpem@5 | 189 | |
philpem@5 | 190 | switch(shape) { |
philpem@5 | 191 | case 0: { // linear |
philpem@5 | 192 | cimg_forV(*this,v) { cimg_forXYZ(*this,x,y,z) { // point M=(x,z) |
philpem@5 | 193 | const float amx = (float)x-x0, amy = (float)y-y0, r = cimg::max(0.f,cimg::min(1.f,(amx*abx+amy*aby)/ab2)); |
philpem@5 | 194 | _draw_gradient_profile(ptr++,opacity,r,pcol0,pcol1,profile); |
philpem@5 | 195 | } if (pcol0) ++pcol0; if (pcol1) ++pcol1; }} break; |
philpem@5 | 196 | case 1:{ // radial |
philpem@5 | 197 | cimg_forV(*this,v) { cimg_forXYZ(*this,x,y,z) { |
philpem@5 | 198 | const float amx = (float)x-x0, amy = (float)y-y0, r = cimg::max(0.f,cimg::min(1.f,(amx*amx+amy*amy)/ab2)); |
philpem@5 | 199 | _draw_gradient_profile(ptr++,opacity,r,pcol0,pcol1,profile); |
philpem@5 | 200 | } if (pcol0) ++pcol0; if (pcol1) ++pcol1; }} break; |
philpem@5 | 201 | case 2:{ // radial cone |
philpem@5 | 202 | cimg_forV(*this,v) { cimg_forXYZ(*this,x,y,z) { |
philpem@5 | 203 | const float amx = (float)x-x0, amy = (float)y-y0, r = cimg::max(0.f,cimg::min(1.f,(float)std::sqrt((amx*amx+amy*amy)/ab2))); |
philpem@5 | 204 | _draw_gradient_profile(ptr++,opacity,r,pcol0,pcol1,profile); |
philpem@5 | 205 | } if (pcol0) ++pcol0; if (pcol1) ++pcol1; }} break; |
philpem@5 | 206 | case 3:{ // square |
philpem@5 | 207 | cimg_forV(*this,v) { cimg_forXYZ(*this,x,y,z) { |
philpem@5 | 208 | const float amx = (float)x-x0, amy = (float)y-y0, r=cimg::max(0.f,cimg::min(1.f,(cimg::abs(amx*abx+amy*aby)+cimg::abs(amx*aby-amy*abx))/ab2)); |
philpem@5 | 209 | _draw_gradient_profile(ptr++,opacity,r,pcol0,pcol1,profile); |
philpem@5 | 210 | } if (pcol0) ++pcol0; if (pcol1) ++pcol1; }} break; |
philpem@5 | 211 | case 4:{ // rectangle (L1) |
philpem@5 | 212 | cimg_forV(*this,v) { cimg_forXYZ(*this,x,y,z) { |
philpem@5 | 213 | const float amx = (float)x-x0, amy = (float)y-y0, |
philpem@5 | 214 | r = cimg::max(0.f,cimg::min(1.f,(cimg::abs(amx/abx)+cimg::abs(amy/aby)))); |
philpem@5 | 215 | _draw_gradient_profile(ptr++,opacity,r,pcol0,pcol1,profile); |
philpem@5 | 216 | } if (pcol0) ++pcol0; if (pcol1) ++pcol1; }} break; |
philpem@5 | 217 | case 5:{ // rectangle (Linf) |
philpem@5 | 218 | cimg_forV(*this,v) { cimg_forXYZ(*this,x,y,z) { |
philpem@5 | 219 | const float amx = (float)x-x0, amy = (float)y-y0, |
philpem@5 | 220 | r=cimg::max(0.f,cimg::min(1.f,cimg::max(cimg::abs(amx/abx),cimg::abs(amy/aby)))); |
philpem@5 | 221 | _draw_gradient_profile(ptr++,opacity,r,pcol0,pcol1,profile); |
philpem@5 | 222 | } if (pcol0) ++pcol0; if (pcol1) ++pcol1; }} break; |
philpem@5 | 223 | case 6:{ // gaussian |
philpem@5 | 224 | cimg_forV(*this,v) { cimg_forXYZ(*this,x,y,z) { |
philpem@5 | 225 | const float amx = (float)x-x0, amy = (float)y-y0, r = cimg::max(0.f,cimg::min(1.f,1-(float)std::exp(-(amx*amx+amy*amy)/ab2))); |
philpem@5 | 226 | _draw_gradient_profile(ptr++,opacity,r,pcol0,pcol1,profile); |
philpem@5 | 227 | } if (pcol0) ++pcol0; if (pcol1) ++pcol1; }} break; |
philpem@5 | 228 | default: |
philpem@5 | 229 | CImgArgumentException("CImg<%s>::draw_gradient : unknown shape parameter",pixel_type()); break; |
philpem@5 | 230 | } |
philpem@5 | 231 | return *this; |
philpem@5 | 232 | } |
philpem@5 | 233 | |
philpem@5 | 234 | template<typename tc> |
philpem@5 | 235 | CImg<T>& draw_gradient(const int x0, const int y0, const int x1, const int y1, |
philpem@5 | 236 | const tc *const color0, const int color1, |
philpem@5 | 237 | const int shape=0, const int profile=0, const float opacity=1.0f) { |
philpem@5 | 238 | return (*this).draw_gradient(x0,y0,x1,y1,color0,(tc*)color1,shape,profile,opacity); |
philpem@5 | 239 | } |
philpem@5 | 240 | |
philpem@5 | 241 | template<typename tc> |
philpem@5 | 242 | CImg<T>& draw_gradient(const int x0, const int y0, const int x1, const int y1, |
philpem@5 | 243 | const int color0, const tc *const color1, |
philpem@5 | 244 | const int shape=0, const int profile=0, const float opacity=1.0f) { |
philpem@5 | 245 | return (*this).draw_gradient(x0,y0,x1,y1,(tc*)color0,color1,shape,profile,opacity); |
philpem@5 | 246 | } |
philpem@5 | 247 | |
philpem@5 | 248 | #endif |