ivl 679
|
00001 /* This file is part of the ivl C++ library <http://image.ntua.gr/ivl>. 00002 A C++ template library extending syntax towards mathematical notation. 00003 00004 Copyright (C) 2012 Yannis Avrithis <iavr@image.ntua.gr> 00005 Copyright (C) 2012 Kimon Kontosis <kimonas@image.ntua.gr> 00006 00007 ivl is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU Lesser General Public License 00009 version 3 as published by the Free Software Foundation. 00010 00011 Alternatively, you can redistribute it and/or modify it under the terms 00012 of the GNU General Public License version 2 as published by the Free 00013 Software Foundation. 00014 00015 ivl is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00018 See the GNU General Public License for more details. 00019 00020 You should have received a copy of the GNU General Public License 00021 and a copy of the GNU Lesser General Public License along 00022 with ivl. If not, see <http://www.gnu.org/licenses/>. */ 00023 00024 #ifndef IVL_IMAGE_DETAILS_FUNCTIONS_AA_DRAWING_FUNCTIONS_HPP 00025 #define IVL_IMAGE_DETAILS_FUNCTIONS_AA_DRAWING_FUNCTIONS_HPP 00026 00027 namespace ivl { 00028 00029 namespace image_details { 00030 00031 inline bool cw(double_point<2> a, double_point<2> b, double_point<2> p) 00032 // returns true if a, b, c are clockwise 00033 { 00034 return (b.y - a.y) * (p.x - a.x) >= (b.x - a.x) * (p.y - a.y); 00035 } 00036 00037 inline double cwd(double_point<2> a, double_point<2> b, double_point<2> p) 00038 // returns > 0 if a, b, c are clockwise, 0 if they are in line, < 0 counter clockwise 00039 { 00040 return (b.y - a.y) * (p.x - a.x) - (b.x - a.x) * (p.y - a.y); 00041 } 00042 00043 inline bool intrig(double_point<2> a, double_point<2> b, double_point<2> c, double_point<2> p) 00044 // returns true if the point p lies in the triangle a, b, c (demands: a, b, c clockwise) 00045 { 00046 return cw(a, b, p) && cw(b, c, p) && cw(c, a, p); 00047 } 00048 00049 inline bool inquad(double_point<2> a, double_point<2> b, double_point<2> c, double_point<2> d, double_point<2> p) 00050 // returns true if the point p is in the quad, and demands a, b, c, d clockwise 00051 { 00052 return cw(a, b, p) && cw(b, c, p) && cw(c, d, p) && cw(d, a, p); 00053 } 00054 00055 inline bool crossinter(double_point<2> a1, double_point<2> a2, double_point<2> b1, double_point<2> b2) 00056 // returns true if the line segments (a1, a2) and (b1, b2) intersect 00057 { 00058 return (cwd(a1, a2, b1) * cwd(a1, a2, b2) < 0 ) && (cwd(b1, b2, a1) * cwd(b1, b2, a2) < 0 ); 00059 } 00060 00061 inline bool incirc(double_point<2> a, double sqrradius, double_point<2> p) 00062 // returns true if the point is in the circle (a, square_root(sqrradius) ) 00063 { 00064 double dy = a.y - p.y; 00065 double dx = a.x - p.x; 00066 return dx * dx + dy * dy <= sqrradius; 00067 } 00068 00069 inline bool inellipse(double_point<2>::base_class a, double_point<2>::base_class b, 00070 double sqrradius, double_point<2>::base_class p) 00071 // returns true if the point is in the circle (a, square_root(sqrradius) ) 00072 { 00073 double dy1 = a.y - p.y; 00074 double dx1 = a.x - p.x; 00075 double dy2 = b.y - p.y; 00076 double dx2 = b.x - p.x; 00077 00078 return dx1 * dx1 + dy1 * dy1 + dx2 * dx2 + dy2 * dy2 + 00079 2 * std::sqrt((dx1 * dx1 + dy1 * dy1)*(dx2 * dx2 + dy2 * dy2)) <= sqrradius; 00080 } 00081 00082 inline bool inellipse2(double_point<2>::base_class a, double_point<2>::base_class b, 00083 double sqrradius, double thickness, 00084 double_point<2>::base_class p) 00085 // returns true if the point is in the circle (a, square_root(sqrradius) ) 00086 { 00087 if(thickness < 0 && inellipse(a, b, sqrradius, p)) return true; 00088 if(thickness > 0) { 00089 double dy = p.y - (a.y + b.y) / 2.; 00090 double dx = p.x - (a.x + b.x) / 2.; 00091 00092 double theta = std::atan2(dy, dx); 00093 double_point<2> thickvec(std::sin(theta) * thickness, 00094 std::cos(theta) * thickness); 00095 00096 if(!inellipse(a, b, sqrradius, p + thickvec)) return false; 00097 } 00098 double dy1 = p.y - a.y; 00099 double dx1 = p.x - a.x; 00100 double dy2 = p.y - b.y; 00101 double dx2 = p.x - b.x; 00102 00103 double theta1 = std::atan2(dy1, dx1); 00104 double theta2 = std::atan2(dy2, dx2); 00105 00106 double_point<2> normvec((std::sin(theta1) + std::sin(theta2)), 00107 (std::cos(theta1) + std::cos(theta2))); 00108 00109 theta1 = std::atan2(normvec.y, normvec.x); 00110 00111 double_point<2> thickvec(std::sin(theta1) * thickness, 00112 std::cos(theta1) * thickness); 00113 00114 /* double prevdy1 = dy1; 00115 double prevdy2 = dy2; 00116 double prevdx1 = dx1; 00117 double prevdx2 = dx2; */ 00118 00119 p = p + thickvec; 00120 00121 return inellipse(a, b, sqrradius, p); 00122 00123 /* TODO: check if overextending inner thickness goes out of boundary of ellipse 00124 dy1 += normvec.y; 00125 dy2 += normvec.y; 00126 dx1 += normvec.x; 00127 dx2 += normvec.x; 00128 00129 if(dy1 * prevdy1 < 0) dy1 = 0; 00130 if(dy2 * prevdy2 < 0) dy2 = 0; 00131 if(dx1 * prevdx1 < 0) dx1 = 0; 00132 if(dx2 * prevdx2 < 0) dx2 = 0; 00133 */ 00134 00135 } 00136 00137 00138 00139 template <class T, class D> 00140 void AA_trig_artsy_impl(image<T, D>& im, double_point<2> a, double_point<2> b, double_point<2> c, color<T, D::Ch> col, int multisample = 32, double opacity = 1.) 00141 { 00142 if(!cw(a, b, c)) std::swap(b, c); 00143 00144 int y0 = int(min(arr<double>(a.y, b.y, c.y))) - 1; 00145 int x0 = int(min(arr<double>(a.x, b.x, c.x))) - 1; 00146 int y1 = int(ivl::max(arr<double>(a.y, b.y, c.y))) + 1; 00147 int x1 = int(max(arr<double>(a.x, b.x, c.x))) + 1; 00148 00149 y0 = std::max(y0, 0); 00150 x0 = std::max(x0, 0); 00151 y1 = std::min(y1, int(im.rows() - 1)); 00152 x1 = std::min(x1, int(im.cols() - 1)); 00153 00154 double coveradd = opacity / double(multisample * multisample); 00155 double pixeladd = 1. / double(multisample - 1); 00156 array<double> colord = cast<double>(col); 00157 00158 int y, x, i, j; 00159 00160 for(y = y0; y < y1; y++) { 00161 00162 for(x = x0; x < x1; x++) { 00163 00164 double_point<2> px1(double(y) - .5, double(x) - .5); 00165 double_point<2> px2(double(y) + .5, double(x) - .5); 00166 double_point<2> px3(double(y) + .5, double(x) + .5); 00167 double_point<2> px4(double(y) - .5, double(x) + .5); 00168 00169 bool pin1 = intrig(a, b, c, px1); 00170 bool pin2 = intrig(a, b, c, px2); 00171 bool pin3 = intrig(a, b, c, px3); 00172 bool pin4 = intrig(a, b, c, px4); 00173 00174 bool allin = pin1 && pin2 && pin3 && pin4; 00175 bool partin = pin1 || pin2 || pin3 || pin4; 00176 00177 if(!partin) partin = 00178 crossinter(px1, px2, a, b) || crossinter(px1, px2, b, c) || crossinter(px1, px2, c, a) || 00179 crossinter(px2, px3, a, b) || crossinter(px2, px3, b, c) || crossinter(px2, px3, c, a) || 00180 crossinter(px3, px4, a, b) || crossinter(px3, px4, b, c) || crossinter(px3, px4, c, a) || 00181 crossinter(px4, px1, a, b) || crossinter(px4, px1, b, c) || crossinter(px4, px1, c, a); 00182 00183 array<double> interp; 00184 double cover = 0.0; 00185 00186 if(0 && allin) im.col(y, x) = col; 00187 else if(1 || partin) { // AA 00188 interp = cast<double>(im.col(y, x)); 00189 00190 for(i = 0; i < multisample; i++) { 00191 00192 for(j = 0; j < multisample; j++) { 00193 00194 if(intrig(a, b, c, px1)) cover += coveradd; 00195 00196 px1.x += pixeladd; 00197 } 00198 px1.y += pixeladd; 00199 } 00200 00201 interp *= (double(1.) - cover); 00202 interp += colord * cover; 00203 00204 im.col(y, x) = cast<T>(interp); 00205 } 00206 } 00207 } 00208 } 00209 00210 00211 template <class T, class D> 00212 void AA_trig_impl(image<T, D>& im, double_point<2> a, double_point<2> b, double_point<2> c, color<T, D::Ch> col, int multisample = 8, double opacity = 1.) 00213 { 00214 // ensures that points in triangle are given in clockwise order 00215 if(!cw(a, b, c)) std::swap(b, c); 00216 00217 // bounding box 00218 int y0 = int(min(arr<double>(a.y, b.y, c.y))) - 1; 00219 int x0 = int(min(arr<double>(a.x, b.x, c.x))) - 1; 00220 int y1 = int(max(arr<double>(a.y, b.y, c.y))) + 1; 00221 int x1 = int(max(arr<double>(a.x, b.x, c.x))) + 1; 00222 00223 // clipping! 00224 y0 = std::max(y0, 0); 00225 x0 = std::max(x0, 0); 00226 y1 = std::min(y1, int(im.rows() - 1)); 00227 x1 = std::min(x1, int(im.cols() - 1)); 00228 00229 // for multisampling 00230 double coveradd = opacity / double(multisample * multisample); 00231 double pixeladd = 1. / double(multisample + 1); 00232 00233 // the color, in array format due to incompatibility issues. TODO: fix 00234 array<double> colord = cast<double>(col); 00235 00236 int y, x, i, j; 00237 00238 for(y = y0; y < y1; y++) { 00239 00240 for(x = x0; x < x1; x++) { 00241 00242 // the four corners that enclose the pixel 00243 double_point<2> px1(double(y) - .5, double(x) - .5); 00244 double_point<2> px2(double(y) + .5, double(x) - .5); 00245 double_point<2> px3(double(y) + .5, double(x) + .5); 00246 double_point<2> px4(double(y) - .5, double(x) + .5); 00247 00248 // checks if each of the four corners are in the shape 00249 bool pin1 = intrig(a, b, c, px1); 00250 bool pin2 = intrig(a, b, c, px2); 00251 bool pin3 = intrig(a, b, c, px3); 00252 bool pin4 = intrig(a, b, c, px4); 00253 00254 bool allin = pin1 && pin2 && pin3 && pin4; 00255 bool partin = pin1 || pin2 || pin3 || pin4; 00256 00257 // if no point is in the shape we need to assure that 00258 // also the shape is not in the pixel box 00259 // the test for a tiny shape that lies fully in the pixel is omitted for speed (not very useful) 00260 if(!partin) partin = 00261 crossinter(px1, px2, a, b) || crossinter(px1, px2, b, c) || crossinter(px1, px2, c, a) || 00262 crossinter(px2, px3, a, b) || crossinter(px2, px3, b, c) || crossinter(px2, px3, c, a) || 00263 crossinter(px3, px4, a, b) || crossinter(px3, px4, b, c) || crossinter(px3, px4, c, a) || 00264 crossinter(px4, px1, a, b) || crossinter(px4, px1, b, c) || crossinter(px4, px1, c, a); 00265 00266 array<double> interp; 00267 double cover = 0.0; 00268 00269 if(allin) { 00270 if(opacity == 1.) im.col(y, x) = col; 00271 else { 00272 interp = cast<double>(im.col(y, x)); 00273 interp *= (double(1.) - opacity); 00274 interp += colord * opacity; 00275 00276 im.col(y, x) = cast<T>(interp); 00277 } 00278 } else if(partin) { // AA 00279 interp = cast<double>(im.col(y, x)); 00280 00281 px1.x += pixeladd; 00282 px1.y += pixeladd; 00283 00284 for(i = 0; i < multisample; i++) { 00285 00286 for(j = 0; j < multisample; j++) { 00287 00288 // tests a grid of (multisample x multisample) points 00289 // in the pixel box and counts how many are in the shape 00290 if(intrig(a, b, c, px1)) cover += coveradd; 00291 00292 px1.x += pixeladd; 00293 } 00294 px1.x = px2.x + pixeladd; 00295 px1.y += pixeladd; 00296 } 00297 00298 // perform transparency with cover as a parameter 00299 interp *= (double(1.) - cover); 00300 interp += colord * cover; 00301 00302 im.col(y, x) = cast<T>(interp); 00303 } 00304 } 00305 } 00306 } 00307 00308 00309 template <class T, class D> 00310 void AA_quad_impl(image<T, D>& im, double_point<2> a, double_point<2> b, double_point<2> c, double_point<2> d, 00311 color<T, D::Ch> col, int multisample = 8, double opacity = 1.) 00312 { 00313 // tries to put the points in the quad in clockwise order 00314 if(!cw(a, b, c)) std::swap(b, d); 00315 00316 CHECK(cw(a, b, c), eshape()); // TODO: needs egeometry, but dont do it in this branch 00317 CHECK(cw(b, c, d), eshape()); // TODO: needs egeometry, but dont do it in this branch 00318 CHECK(cw(c, d, a), eshape()); // TODO: needs egeometry, but dont do it in this branch 00319 // "Quad is not cw" 00320 00321 // bounding box 00322 int y0 = int(min(arr<double>(a.y, b.y, c.y, d.y))) - 1; 00323 int x0 = int(min(arr<double>(a.x, b.x, c.x, d.x))) - 1; 00324 int y1 = int(max(arr<double>(a.y, b.y, c.y, d.y))) + 1; 00325 int x1 = int(max(arr<double>(a.x, b.x, c.x, d.x))) + 1; 00326 00327 // clipping! 00328 y0 = std::max(y0, 0); 00329 x0 = std::max(x0, 0); 00330 y1 = std::min(y1, int(im.rows() - 1)); 00331 x1 = std::min(x1, int(im.cols() - 1)); 00332 00333 // for multisampling 00334 double coveradd = opacity / double(multisample * multisample); 00335 double pixeladd = 1. / double(multisample + 1); 00336 00337 // the color, in array format due to incompatibility issues. TODO: fix 00338 array<double> colord = ivl::cast<double>(col); 00339 00340 int y, x, i, j; 00341 00342 for(y = y0; y < y1; y++) { 00343 00344 for(x = x0; x < x1; x++) { 00345 00346 // the four corners that enclose the pixel 00347 double_point<2> px1(double(y) - .5, double(x) - .5); 00348 double_point<2> px2(double(y) + .5, double(x) - .5); 00349 double_point<2> px3(double(y) + .5, double(x) + .5); 00350 double_point<2> px4(double(y) - .5, double(x) + .5); 00351 00352 // checks if each of the four corners are in the shape 00353 bool pin1 = inquad(a, b, c, d, px1); 00354 bool pin2 = inquad(a, b, c, d, px2); 00355 bool pin3 = inquad(a, b, c, d, px3); 00356 bool pin4 = inquad(a, b, c, d, px4); 00357 00358 bool allin = pin1 && pin2 && pin3 && pin4; 00359 bool partin = pin1 || pin2 || pin3 || pin4; 00360 00361 // if no point is in the shape we need to assure that 00362 // also the shape is not in the pixel box 00363 // the test for a tiny shape that lies fully in the pixel is omitted for speed (not very useful) 00364 if(!partin) partin = 00365 crossinter(px1, px2, a, b) || crossinter(px1, px2, b, c) || crossinter(px1, px2, c, d) || 00366 crossinter(px2, px3, a, b) || crossinter(px2, px3, b, c) || crossinter(px2, px3, c, d) || 00367 crossinter(px3, px4, a, b) || crossinter(px3, px4, b, c) || crossinter(px3, px4, c, d) || 00368 crossinter(px4, px1, a, b) || crossinter(px4, px1, b, c) || crossinter(px4, px1, c, d); 00369 00370 // interpolated color for opacity 00371 array<double> interp; 00372 // pixel coverage accumulator 0.0 - 1.0 00373 double cover = 0.0; 00374 00375 if(allin) { 00376 if(opacity == 1.) im.col(y, x) = col; 00377 else { 00378 interp = cast<double>(im.col(y, x)); 00379 interp *= (double(1.) - opacity); 00380 interp += colord * opacity; 00381 00382 im.col(y, x) = cast<T>(interp); 00383 } 00384 } else if(partin) { // AA 00385 interp = cast<double>(im.col(y, x)); 00386 00387 px1.x += pixeladd; 00388 px1.y += pixeladd; 00389 00390 for(i = 0; i < multisample; i++) { 00391 00392 for(j = 0; j < multisample; j++) { 00393 // tests a grid of (multisample x multisample) points 00394 // in the pixel box and counts how many are in the shape 00395 if(inquad(a, b, c, d, px1)) cover += coveradd; 00396 00397 px1.x += pixeladd; 00398 } 00399 px1.x = px2.x + pixeladd; 00400 px1.y += pixeladd; 00401 } 00402 00403 // perform transparency with cover as a parameter 00404 interp *= (double(1.) - cover); 00405 interp += colord * cover; 00406 00407 im.col(y, x) = cast<T>(interp); 00408 } 00409 } 00410 } 00411 } 00412 00413 00414 template <class T, class D> 00415 void AA_circ_impl(image<T, D>& im, double_point<2> a, double radius, color<T, D::Ch> col, int multisample = 8, double opacity = 1.) 00416 { 00417 // bounding box 00418 int y0 = int(a.y - radius) - 1; 00419 int x0 = int(a.x - radius) - 1; 00420 int y1 = int(a.y + radius) + 1; 00421 int x1 = int(a.x + radius) + 1; 00422 00423 // clipping! 00424 y0 = std::max(y0, 0); 00425 x0 = std::max(x0, 0); 00426 y1 = std::min(y1, int(im.rows() - 1)); 00427 x1 = std::min(x1, int(im.cols() - 1)); 00428 00429 double sqrradius = radius * radius; 00430 00431 // for multisampling 00432 double coveradd = opacity / double(multisample * multisample); 00433 double pixeladd = 1. / double(multisample + 1); 00434 00435 // the color, in array format due to incompatibility issues. TODO: fix 00436 array<double> colord = cast<double>(col); 00437 00438 int y, x, i, j; 00439 00440 for(y = y0; y < y1; y++) { 00441 00442 for(x = x0; x < x1; x++) { 00443 00444 // the four corners that enclose the pixel 00445 double_point<2> px1(double(y) - .5, double(x) - .5); 00446 double_point<2> px2(double(y) + .5, double(x) - .5); 00447 double_point<2> px3(double(y) + .5, double(x) + .5); 00448 double_point<2> px4(double(y) - .5, double(x) + .5); 00449 00450 // checks if each of the four corners are in the shape 00451 bool pin1 = incirc(a, sqrradius, px1); 00452 bool pin2 = incirc(a, sqrradius, px2); 00453 bool pin3 = incirc(a, sqrradius, px3); 00454 bool pin4 = incirc(a, sqrradius, px4); 00455 00456 bool allin = pin1 && pin2 && pin3 && pin4; 00457 bool partin = pin1 || pin2 || pin3 || pin4; 00458 00459 // if no point is in the shape we need to assure that 00460 // also the shape is not in the pixel box 00461 // the test for a circle that intersects the box is omitted for speed (very little quality loss) 00462 if(!partin) partin = a.x > px1.x && a.x < px3.x && a.y > px1.y && a.y < px3.y; 00463 00464 // interpolated color for opacity 00465 array<double> interp; 00466 // pixel coverage accumulator 0.0 - 1.0 00467 double cover = 0.0; 00468 00469 if(allin) { 00470 if(opacity == 1.) im.col(y, x) = col; 00471 else { 00472 interp = cast<double>(im.col(y, x)); 00473 interp *= (double(1.) - opacity); 00474 interp += colord * opacity; 00475 00476 im.col(y, x) = cast<T>(interp); 00477 } 00478 } else if(partin) { // AA 00479 interp = cast<double>(im.col(y, x)); 00480 00481 px1.x += pixeladd; 00482 px1.y += pixeladd; 00483 00484 for(i = 0; i < multisample; i++) { 00485 00486 for(j = 0; j < multisample; j++) { 00487 // tests a grid of (multisample x multisample) points 00488 // in the pixel box and counts how many are in the shape 00489 if(incirc(a, sqrradius, px1)) cover += coveradd; 00490 00491 px1.x += pixeladd; 00492 } 00493 px1.x = px2.x + pixeladd; 00494 px1.y += pixeladd; 00495 } 00496 00497 // perform transparency with cover as a parameter 00498 interp *= (double(1.) - cover); 00499 interp += colord * cover; 00500 00501 im.col(y, x) = cast<T>(interp); 00502 } 00503 } 00504 } 00505 } 00506 00507 00508 template <class T, class D> 00509 void AA_circle_impl(image<T, D>& im, double_point<2> a, double outradius, double inradius, color<T, D::Ch> col, int multisample = 8, double opacity = 1.) 00510 { 00511 // bounding box 00512 int y0 = int(a.y - outradius) - 1; 00513 int x0 = int(a.x - outradius) - 1; 00514 int y1 = int(a.y + outradius) + 1; 00515 int x1 = int(a.x + outradius) + 1; 00516 00517 // clipping! 00518 y0 = std::max(y0, 0); 00519 x0 = std::max(x0, 0); 00520 y1 = std::min(y1, int(im.rows() - 1)); 00521 x1 = std::min(x1, int(im.cols() - 1)); 00522 00523 double outsqrradius = outradius * outradius; 00524 double insqrradius = inradius * inradius; 00525 00526 // for multisampling 00527 double coveradd = opacity / double(multisample * multisample); 00528 double pixeladd = 1. / double(multisample + 1); 00529 00530 // the color, in array format due to incompatibility issues. TODO: fix 00531 array<double> colord = cast<double>(col); 00532 00533 int y, x, i, j; 00534 00535 for(y = y0; y < y1; y++) { 00536 00537 for(x = x0; x < x1; x++) { 00538 00539 // the four corners that enclose the pixel 00540 double_point<2> px1(double(y) - .5, double(x) - .5); 00541 double_point<2> px2(double(y) + .5, double(x) - .5); 00542 double_point<2> px3(double(y) + .5, double(x) + .5); 00543 double_point<2> px4(double(y) - .5, double(x) + .5); 00544 00545 // checks if each of the four corners are in the shape 00546 bool pin1 = incirc(a, outsqrradius, px1); 00547 bool pin2 = incirc(a, outsqrradius, px2); 00548 bool pin3 = incirc(a, outsqrradius, px3); 00549 bool pin4 = incirc(a, outsqrradius, px4); 00550 00551 bool allin = pin1 && pin2 && pin3 && pin4; 00552 bool partin = pin1 || pin2 || pin3 || pin4; 00553 00554 // if no point is in the shape we need to assure that 00555 // also the shape is not in the pixel box 00556 // the test for a circle that intersects the box is omitted for speed (very little quality loss) 00557 if(!partin) partin = a.x > px1.x && a.x < px3.x && a.y > px1.y && a.y < px3.y; 00558 else { 00559 pin1 = incirc(a, insqrradius, px1); 00560 pin2 = incirc(a, insqrradius, px2); 00561 pin3 = incirc(a, insqrradius, px3); 00562 pin4 = incirc(a, insqrradius, px4); 00563 if(pin1 || pin2 || pin3 || pin4) allin = false; 00564 if(pin1 && pin2 && pin3 && pin4) partin = false; 00565 } 00566 00567 // interpolated color for opacity 00568 array<double> interp; 00569 // pixel coverage accumulator 0.0 - 1.0 00570 double cover = 0.0; 00571 00572 if(allin) { 00573 if(opacity == 1.) im.col(y, x) = col; 00574 else { 00575 interp = cast<double>(im.col(y, x)); 00576 interp *= (double(1.) - opacity); 00577 interp += colord * opacity; 00578 00579 im.col(y, x) = cast<T>(interp); 00580 } 00581 } else if(partin) { // AA 00582 interp = cast<double>(im.col(y, x)); 00583 00584 px1.x += pixeladd; 00585 px1.y += pixeladd; 00586 00587 for(i = 0; i < multisample; i++) { 00588 00589 for(j = 0; j < multisample; j++) { 00590 // tests a grid of (multisample x multisample) points 00591 // in the pixel box and counts how many are in the shape 00592 if(incirc(a, outsqrradius, px1) && !incirc(a, insqrradius, px1)) cover += coveradd; 00593 00594 px1.x += pixeladd; 00595 } 00596 px1.x = px2.x + pixeladd; 00597 px1.y += pixeladd; 00598 } 00599 00600 // perform transparency with cover as a parameter 00601 interp *= (double(1.) - cover); 00602 interp += colord * cover; 00603 00604 im.col(y, x) = cast<T>(interp); 00605 } 00606 } 00607 } 00608 } 00609 00610 template <class T, class D> 00611 void AA_fillellipse_impl(image<T, D>& im, double_point<2> a, double_point<2> b, double radius, color<T, D::Ch> col, int multisample = 8, double opacity = 1.) 00612 { 00613 // bounding box 00614 int y0 = int(std::min(a.y, b.y) - radius) - 1; 00615 int x0 = int(std::min(a.x, b.x) - radius) - 1; 00616 int y1 = int(std::max(a.y, b.y) + radius) + 1; 00617 int x1 = int(std::max(a.x, b.x) + radius) + 1; 00618 00619 // clipping! 00620 y0 = std::max(y0, 0); 00621 x0 = std::max(x0, 0); 00622 y1 = std::min(y1, int(im.rows() - 1)); 00623 x1 = std::min(x1, int(im.cols() - 1)); 00624 00625 double sqrradius = radius * radius; 00626 00627 // for multisampling 00628 double coveradd = opacity / double(multisample * multisample); 00629 double pixeladd = 1. / double(multisample + 1); 00630 00631 // the color, in array format due to incompatibility issues. TODO: fix 00632 array<double> colord = cast<double>(col); 00633 00634 int y, x, i, j; 00635 00636 for(y = y0; y < y1; y++) { 00637 00638 for(x = x0; x < x1; x++) { 00639 00640 // the four corners that enclose the pixel 00641 double_point<2> px1(double(y) - .5, double(x) - .5); 00642 double_point<2> px2(double(y) + .5, double(x) - .5); 00643 double_point<2> px3(double(y) + .5, double(x) + .5); 00644 double_point<2> px4(double(y) - .5, double(x) + .5); 00645 00646 // checks if each of the four corners are in the shape 00647 bool pin1 = inellipse(a, b, sqrradius, px1); 00648 bool pin2 = inellipse(a, b, sqrradius, px2); 00649 bool pin3 = inellipse(a, b, sqrradius, px3); 00650 bool pin4 = inellipse(a, b, sqrradius, px4); 00651 00652 bool allin = pin1 && pin2 && pin3 && pin4; 00653 bool partin = pin1 || pin2 || pin3 || pin4; 00654 00655 // if no point is in the shape we need to assure that 00656 // also the shape is not in the pixel box 00657 // the test for a circle that intersects the box is omitted for speed (very little quality loss) 00658 if(!partin) partin = a.x > px1.x && a.x < px3.x && a.y > px1.y && a.y < px3.y; 00659 00660 // interpolated color for opacity 00661 array<double> interp; 00662 // pixel coverage accumulator 0.0 - 1.0 00663 double cover = 0.0; 00664 00665 if(allin) { 00666 if(opacity == 1.) im.col(y, x) = col; 00667 else { 00668 interp = cast<double>(im.col(y, x)); 00669 interp *= (double(1.) - opacity); 00670 interp += colord * opacity; 00671 00672 im.col(y, x) = cast<T>(interp); 00673 } 00674 } else if(partin) { // AA 00675 interp = cast<double>(im.col(y, x)); 00676 00677 px1.x += pixeladd; 00678 px1.y += pixeladd; 00679 00680 for(i = 0; i < multisample; i++) { 00681 00682 for(j = 0; j < multisample; j++) { 00683 // tests a grid of (multisample x multisample) points 00684 // in the pixel box and counts how many are in the shape 00685 if(inellipse(a, b, sqrradius, px1)) cover += coveradd; 00686 00687 px1.x += pixeladd; 00688 } 00689 px1.x = px2.x + pixeladd; 00690 px1.y += pixeladd; 00691 } 00692 00693 // perform transparency with cover as a parameter 00694 interp *= (double(1.) - cover); 00695 interp += colord * cover; 00696 00697 im.col(y, x) = cast<T>(interp); 00698 } 00699 } 00700 } 00701 } 00702 00703 00704 template <class T, class D> 00705 void AA_ellipse_impl(image<T, D>& im, double_point<2> a, double_point<2> b, double radius, double thickness, color<T, D::Ch> col, int multisample = 8, double opacity = 1.) 00706 { 00707 // bounding box 00708 int y0 = int(std::min(a.y, b.y) - radius) - 1; 00709 int x0 = int(std::min(a.x, b.x) - radius) - 1; 00710 int y1 = int(std::max(a.y, b.y) + radius) + 1; 00711 int x1 = int(std::max(a.x, b.x) + radius) + 1; 00712 00713 double sqrradius = radius * radius; 00714 00715 // clipping! 00716 y0 = std::max(y0, 0); 00717 x0 = std::max(x0, 0); 00718 y1 = std::min(y1, int(im.rows() - 1)); 00719 x1 = std::min(x1, int(im.cols() - 1)); 00720 00721 // for multisampling 00722 double coveradd = opacity / double(multisample * multisample); 00723 double pixeladd = 1. / double(multisample + 1); 00724 00725 // the color, in array format due to incompatibility issues. TODO: fix 00726 array<double> colord = cast<double>(col); 00727 00728 int y, x, i, j; 00729 00730 for(y = y0; y < y1; y++) { 00731 00732 for(x = x0; x < x1; x++) { 00733 00734 // the four corners that enclose the pixel 00735 double_point<2> px1(double(y) - .5, double(x) - .5); 00736 double_point<2> px2(double(y) + .5, double(x) - .5); 00737 double_point<2> px3(double(y) + .5, double(x) + .5); 00738 double_point<2> px4(double(y) - .5, double(x) + .5); 00739 00740 // checks if each of the four corners are in the shape 00741 bool pin1 = inellipse2(a, b, sqrradius, -thickness, px1); 00742 bool pin2 = inellipse2(a, b, sqrradius, -thickness, px2); 00743 bool pin3 = inellipse2(a, b, sqrradius, -thickness, px3); 00744 bool pin4 = inellipse2(a, b, sqrradius, -thickness, px4); 00745 00746 bool allin = pin1 && pin2 && pin3 && pin4; 00747 bool partin = pin1 || pin2 || pin3 || pin4; 00748 00749 // if no point is in the shape we need to assure that 00750 // also the shape is not in the pixel box 00751 // the test for a circle that intersects the box is omitted for speed (very little quality loss) 00752 if(!partin) partin = a.x > px1.x && a.x < px3.x && a.y > px1.y && a.y < px3.y; 00753 else { 00754 pin1 = inellipse2(a, b, sqrradius, thickness, px1); 00755 pin2 = inellipse2(a, b, sqrradius, thickness, px2); 00756 pin3 = inellipse2(a, b, sqrradius, thickness, px3); 00757 pin4 = inellipse2(a, b, sqrradius, thickness, px4); 00758 if(pin1 || pin2 || pin3 || pin4) allin = false; 00759 if(pin1 && pin2 && pin3 && pin4) partin = false; 00760 } 00761 00762 // interpolated color for opacity 00763 array<double> interp; 00764 // pixel coverage accumulator 0.0 - 1.0 00765 double cover = 0.0; 00766 00767 if(allin) { 00768 if(opacity == 1.) im.col(y, x) = col; 00769 else { 00770 interp = cast<double>(im.col(y, x)); 00771 interp *= (double(1.) - opacity); 00772 interp += colord * opacity; 00773 00774 im.col(y, x) = cast<T>(interp); 00775 } 00776 } else if(partin) { // AA 00777 interp = cast<double>(im.col(y, x)); 00778 00779 px1.x += pixeladd; 00780 px1.y += pixeladd; 00781 00782 for(i = 0; i < multisample; i++) { 00783 00784 for(j = 0; j < multisample; j++) { 00785 // tests a grid of (multisample x multisample) points 00786 // in the pixel box and counts how many are in the shape 00787 if(inellipse2(a, b, sqrradius, -thickness, px1) && !inellipse2(a, b, sqrradius, thickness, px1)) cover += coveradd; 00788 00789 px1.x += pixeladd; 00790 } 00791 px1.x = px2.x + pixeladd; 00792 px1.y += pixeladd; 00793 } 00794 00795 // perform transparency with cover as a parameter 00796 interp *= (double(1.) - cover); 00797 interp += colord * cover; 00798 00799 im.col(y, x) = cast<T>(interp); 00800 } 00801 } 00802 } 00803 } 00804 00805 00806 };//namespace image_details 00807 00808 template <class T, class D> 00809 void AA_trig_artsy(image<T, D>& im, double_point<2> a, double_point<2> b, double_point<2> c, color<T, D::Ch> col, int multisample, double opacity) 00810 { 00811 image_details::AA_trig_artsy_impl(im, a, b, c, col, multisample, opacity); 00812 } 00813 00814 template <class T, class D> 00815 void AA_trig(image<T, D>& im, double_point<2> a, double_point<2> b, double_point<2> c, color<T, D::Ch> col, int multisample, double opacity) 00816 { 00817 image_details::AA_trig_impl(im, a, b, c, col, multisample, opacity); 00818 } 00819 00820 template <class T, class D> 00821 void AA_quad(image<T, D>& im, double_point<2> a, double_point<2> b, double_point<2> c, double_point<2> d, 00822 color<T, D::Ch> col, int multisample, double opacity) 00823 { 00824 image_details::AA_quad_impl(im, a, b, c, d, col, multisample, opacity); 00825 } 00826 00827 template <class T, class D> 00828 void AA_circ(image<T, D>& im, double_point<2> a, double radius, color<T, D::Ch> col, int multisample, double opacity) 00829 { 00830 image_details::AA_circ_impl(im, a, radius, col, multisample, opacity); 00831 } 00832 00833 template <class T, class D> 00834 void AA_circle(image<T, D>& im, double_point<2> a, double outradius, double inradius, color<T, D::Ch> col, int multisample, double opacity) 00835 { 00836 image_details::AA_circle_impl(im, a, outradius, inradius, col, multisample, opacity); 00837 } 00838 00839 template <class T, class D> 00840 void AA_fillellipse(image<T, D>& im, double_point<2> a, double_point<2> b, double radius, color<T, D::Ch> col, int multisample, double opacity) 00841 { 00842 image_details::AA_fillellipse_impl(im, a, b, radius, col, multisample, opacity); 00843 } 00844 00845 template <class T, class D> 00846 void AA_ellipse(image<T, D>& im, double_point<2> a, double_point<2> b, double radius, double thickness, color<T, D::Ch> col, int multisample, double opacity) 00847 { 00848 image_details::AA_ellipse_impl(im, a, b, radius, thickness, col, multisample, opacity); 00849 } 00850 00851 };//namespace ivl 00852 00853 #endif // IVL_IMAGE_DETAILS_FUNCTIONS_AA_DRAWING_FUNCTIONS_HPP