1 /* === S I N F G =========================================================== */
3 ** \brief Blur Implementation File
5 ** $Id: blur.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $
8 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
10 ** This software and associated documentation
11 ** are CONFIDENTIAL and PROPRIETARY property of
12 ** the above-mentioned copyright holder.
14 ** You may not copy, print, publish, or in any
15 ** other way distribute this software without
16 ** a prior written agreement with
17 ** the copyright holder.
20 /* ========================================================================= */
22 /* === H E A D E R S ======================================================= */
31 #include <sinfg/general.h>
32 #include <sinfg/surface.h>
37 #include <ETL/stringf>
40 #include <ETL/gaussian>
41 #include <ETL/boxblur>
45 /* === U S I N G =========================================================== */
49 using namespace sinfg;
51 /* === M A C R O S ========================================================= */
53 /* === G L O B A L S ======================================================= */
55 /* === P R O C E D U R E S ================================================= */
57 /* === M E T H O D S ======================================================= */
58 Point Blur::operator ()(const Point &pos) const
68 blurpos[0]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[0];
73 blurpos[1]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[1];
79 Angle theta=Angle::rotations((float)rand()/(float)RAND_MAX);
80 Vector::value_type mag=(float)rand()/(float)RAND_MAX;
81 Vector vect((float)Angle::cos(theta).get()*mag,(float)Angle::sin(theta).get()*mag);
83 blurpos[0]+=vect[0]*size[0];
84 blurpos[1]+=vect[1]*size[1];
90 // Not quite a true gaussian blur,
91 // but the results are close enough for me.
94 blurpos[0]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[0]*3/4;
95 blurpos[0]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[0]*3/4;
99 blurpos[1]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[1]*3/4;
100 blurpos[1]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[1]*3/4;
107 blurpos[0]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[0];
109 blurpos[1]+=(Vector::value_type)( (signed)(RAND_MAX/2)-(signed)rand() )/(Vector::value_type)(RAND_MAX) * size[1];
116 Point Blur::operator ()(sinfg::Real x, sinfg::Real y) const
118 return (*this)(Point(x,y));
121 //blur functions to make my life easier
123 template <typename T>
124 static inline T zero()
130 static inline Color zero<Color>()
132 return Color::alpha();
135 template <typename T,typename AT,class VP>
136 static void GuassianBlur_2x2(etl::surface<T,AT,VP> &surface)
141 T *SC0=new T[surface.get_w()];
143 memcpy(SC0,surface[0],surface.get_w()*sizeof(T));
145 for(y=0;y<surface.get_h();y++)
148 for(x=0;x<surface.get_w();x++)
153 surface[y][x]=(SC0[x]+Tmp2)/4;
160 template <typename T,typename AT,class VP>
161 static void GuassianBlur_3x3(etl::surface<T,AT,VP> &surface)
172 // Setup the row bufers
173 for(x=0;x<w;x++)SC0[x]=surface[0][x]*4;
174 // memcpy(SC1,surface[0],w*sizeof(T));
183 SR0=SR1=surface[y][0];
202 surface[y-1][x-1]=(SC1[x]+Tmp2)/16;
211 template <typename T,typename AT,class VP>
212 inline static void GaussianBlur_5x5_(etl::surface<T,AT,VP> &surface,T *SC0,T *SC1,T *SC2,T *SC3)
215 T Tmp1,Tmp2,SR0,SR1,SR2,SR3;
220 // Setup the row bufers
221 for(x=0;x<w;x++)SC0[x+2]=surface[0][x]*24;
222 // memset(SC0,0,(w+2)*sizeof(T));
223 memset(SC1,0,(w+2)*sizeof(T));
224 memset(SC2,0,(w+2)*sizeof(T));
225 memset(SC3,0,(w+2)*sizeof(T));
235 SR0=surface[v][0]*1.5;
262 surface[y-2][x-2]=(SC3[x]+Tmp2)/256;
269 template <typename T,typename AT,class VP>
270 inline static void GaussianBlur_5x5(etl::surface<T,AT,VP> &surface)
272 int w=surface.get_w();
279 GaussianBlur_5x5_(surface,SC0,SC1,SC2,SC3);
287 template <typename T,typename AT,class VP>
288 static void GuassianBlur_nxn(etl::surface<T,AT,VP> &surface,int n)
292 T inv_divisor=pow(2.0,(n-1));
294 inv_divisor=1.0/(inv_divisor*inv_divisor);
304 SC[i]=new T[w+half_n];
307 throw(runtime_error(strprintf(__FILE__":%d:Malloc failure",__LINE__)));
310 memset(SC[i],0,(w+half_n)*sizeof(T));
313 // Setup the first row
314 // for(x=0;x<w;x++)SC[0][x+half_n]=surface[0][x]*550.0;//*pow(2.0,(n-1))*(2.0/n);
316 for(y=0;y<h+half_n;y++)
323 memset(SR,0,(n-1)*sizeof(T));
325 // SR[0]=surface[v][0]*(2.0-1.9/n);
327 for(x=0;x<w+half_n;x++)
336 for(i=0;i<half_n;i++)
345 for(i=0;i<half_n-1;i++)
347 Tmp2=SC[i*2][x]+Tmp1;
349 Tmp1=SC[i*2+1][x]+Tmp2;
352 Tmp2=SC[n-3][x]+Tmp1;
354 if(y>=half_n&&x>=half_n)
355 surface[y-half_n][x-half_n]=(SC[n-2][x]+Tmp2)*inv_divisor;
364 template <typename T,typename AT,class VP>
365 static void GuassianBlur_2x1(etl::surface<T,AT,VP> &surface)
370 for(y=0;y<surface.get_h();y++)
373 for(x=0;x<surface.get_w();x++)
378 surface[y][x]=(Tmp2)/2;
383 template <typename T,typename AT,class VP>
384 static void GuassianBlur_3x1(etl::surface<T,AT,VP> &surface)
387 AT Tmp1,Tmp2,SR0,SR1;
389 for(y=0;y<surface.get_h();y++)
391 SR0=SR1=surface[y][0];
392 for(x=0;x<surface.get_w();x++)
402 surface[y][x-1]=(Tmp1)/4;
407 template <typename T,typename AT,class VP>
408 static void GuassianBlur_1x2(etl::surface<T,AT,VP> &surface)
413 for(x=0;x<surface.get_w();x++)
416 for(y=0;y<surface.get_h();y++)
421 surface[y][x]=(Tmp2)/2;
426 template <typename T,typename AT,class VP>
427 static void GuassianBlur_1x3(etl::surface<T,AT,VP> &surface)
430 AT Tmp1,Tmp2,SR0,SR1;
432 for(x=0;x<surface.get_w();x++)
434 SR0=SR1=surface[0][x];
435 for(y=0;y<surface.get_h();y++)
445 surface[y-1][x]=(Tmp1)/4;
450 //THE GOOD ONE!!!!!!!!!
451 bool Blur::operator ()(const Surface &surface,
452 const Vector &resolution,
455 int w = surface.get_w(),
458 if(w == 0 || h == 0 || resolution[0] == 0 || resolution[1] == 0) return false;
460 const Real pw = resolution[0]/w,
461 ph = resolution[1]/h;
463 int halfsizex = (int) (abs(size[0]*.5/pw) + 1),
464 halfsizey = (int) (abs(size[1]*.5/ph) + 1);
468 SuperCallback blurcall(cb,0,5000,5000);
470 Surface worksurface(w,h);
472 //sinfg::info("Blur: check surface = %s", surface_valid(surface)?"true":"false");
474 // Premultiply the alpha
479 Color a = surface[y][x];
480 a.set_r(a.get_r()*a.get_a());
481 a.set_g(a.get_g()*a.get_a());
482 a.set_b(a.get_b()*a.get_a());
483 worksurface[y][x] = a;
489 case Blur::DISC: // D I S C ----------------------------------------------------------
494 if(size[0] && size[1] && w*h>2)
497 Surface tmp_surface(worksurface);
503 //accumulate all the pixels in an ellipse of w,h about the current pixel
504 Color color=Color::alpha();
507 for(y2=-bh;y2<=bh;y2++)
509 for(x2=-bw;x2<=bw;x2++)
511 //get the floating point distance away from the origin pixel in relative coords
512 float tmp_x=(float)x2/bw;
513 float tmp_y=(float)y2/bh;
517 //ignore if it's outside of the disc
521 //cap the pixel indices to inside the surface
526 if( u >= w ) u = w-1;
529 if( v >= h ) v = h-1;
531 //accumulate the color, and # of pixels added in
532 color += tmp_surface[v][u];
537 //blend the color with the original color
538 //if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
539 worksurface[y][x]=color/total;
541 // worksurface[y][x]=Color::blend(color/total,tmp_surface[y][x],get_amount(),get_blend_method());
543 if(!blurcall.amount_complete(y,h))
545 //if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
552 //if we don't qualify for disc blur just use box blur
555 case Blur::BOX: // B O X -------------------------------------------------------
558 //sinfg::info("Blur: Starting Box blur (surface valid %d)", (int)surface_valid(worksurface));
560 Surface temp_surface;
561 temp_surface.set_wh(w,h);
565 int length = halfsizex;
566 length=std::max(1,length);
568 //sinfg::info("Blur: hbox blur work -> temp %d", length);
569 etl::hbox_blur(worksurface.begin(),worksurface.end(),length,temp_surface.begin());
571 else temp_surface = worksurface;
572 //sinfg::info("Blur: hbox finished");
575 //Surface temp_surface2;
576 //temp_surface2.set_wh(w,h);
580 int length = halfsizey;
581 length = std::max(1,length);
583 //sinfg::info("Blur: vbox blur temp -> work %d",length);
584 etl::vbox_blur(temp_surface.begin(),temp_surface.end(),length,worksurface.begin());
586 else worksurface = temp_surface;
587 //sinfg::info("Blur: vbox finished");
589 //blend with the original surface
595 worksurface[y][x]=temp_surface2[y][x];//Color::blend(temp_surface2[y][x],worksurface[y][x],get_amount(),get_blend_method());
601 case Blur::FASTGAUSSIAN: // F A S T G A U S S I A N ----------------------------------------------
603 //fast gaussian is treated as a 3x3 type of thing, except expanded to work with the length
610 Surface temp_surface;
611 temp_surface.set_wh(w,h);
613 //Surface temp_surface2;
614 //temp_surface2.set_wh(w,h);
619 int length=(int)(abs((float)w/(resolution[0]))*size[0]*0.5+1);
620 length=std::max(1,length);
622 //two box blurs produces: 1 2 1
623 etl::hbox_blur(worksurface.begin(),w,h,length*3/4,temp_surface.begin());
624 etl::hbox_blur(temp_surface.begin(),w,h,length*3/4,worksurface.begin());
626 //else temp_surface2=worksurface;
631 int length=(int)(abs((float)h/(resolution[1]))*size[1]*0.5+1);
632 length=std::max(1,length);
634 //two box blurs produces: 1 2 1 on the horizontal 1 2 1
635 etl::vbox_blur(worksurface.begin(),w,h,length*3/4,temp_surface.begin());
636 etl::vbox_blur(temp_surface.begin(),w,h,length*3/4,worksurface.begin());
638 //else temp_surface2=temp_surface2;
645 worksurface[y][x]=temp_surface2[y][x];//Color::blend(temp_surface2[y][x],worksurface[y][x],get_amount(),get_blend_method());
651 case Blur::CROSS: // C R O S S -------------------------------------------------------
654 Surface temp_surface;
655 temp_surface.set_wh(worksurface.get_w(),worksurface.get_h());
659 int length = halfsizex;
660 length = std::max(1,length);
662 etl::hbox_blur(worksurface.begin(),worksurface.end(),length,temp_surface.begin());
664 else temp_surface = worksurface;
667 Surface temp_surface2;
668 temp_surface2.set_wh(worksurface.get_w(),worksurface.get_h());
672 int length = halfsizey;
673 length = std::max(1,length);
675 etl::vbox_blur(worksurface.begin(),worksurface.end(),length,temp_surface2.begin());
677 else temp_surface2 = worksurface;
679 //blend the two together
686 worksurface[y][x] = (temp_surface[y][x]+temp_surface2[y][x])/2;//Color::blend((temp_surface[y][x]+temp_surface2[y][x])/2,worksurface[y][x],get_amount(),get_blend_method());
693 case Blur::GAUSSIAN: // G A U S S I A N ----------------------------------------------
695 #ifndef GAUSSIAN_ADJUSTMENT
696 #define GAUSSIAN_ADJUSTMENT (0.05)
699 Real pw = (Real)w/(resolution[0]);
700 Real ph = (Real)h/(resolution[1]);
702 Surface temp_surface;
703 Surface *gauss_surface;
705 //sinfg::warning("Didn't crash yet b1");
707 //if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
708 gauss_surface = &worksurface;
711 temp_surface = worksurface;
712 gauss_surface = &temp_surface;
715 /* Squaring the pw and ph values
716 is necessary to insure consistant
717 results when rendered to different
719 Unfortunately, this automaticly
720 squares our rendertime.
721 There has got to be a faster way...
726 int bw = (int)(abs(pw)*size[0]*GAUSSIAN_ADJUSTMENT+0.5);
727 int bh = (int)(abs(ph)*size[1]*GAUSSIAN_ADJUSTMENT+0.5);
730 Color *SC0=new class Color[w+2];
731 Color *SC1=new class Color[w+2];
732 Color *SC2=new class Color[w+2];
733 Color *SC3=new class Color[w+2];
734 memset(SC0,0,(w+2)*sizeof(Color));
735 memset(SC0,0,(w+2)*sizeof(Color));
736 memset(SC0,0,(w+2)*sizeof(Color));
737 memset(SC0,0,(w+2)*sizeof(Color));
739 //sinfg::warning("Didn't crash yet b2");
744 if(!blurcall.amount_complete(max-(bw+bh),max))return false;
748 etl::gaussian_blur_5x5_(gauss_surface->begin(),gauss_surface->get_w(),gauss_surface->get_h(),SC0,SC1,SC2,SC3);
754 etl::gaussian_blur_3x3(gauss_surface->begin(),gauss_surface->end());
760 GuassianBlur_2x2(*gauss_surface);
764 //sinfg::warning("Didn't crash yet bi - %d",i++);
768 if(!blurcall.amount_complete(max-(bw+bh),max))return false;
771 GuassianBlur_3x1(*gauss_surface);
777 GuassianBlur_2x1(*gauss_surface);
780 //sinfg::warning("Didn't crash yet bi - %d",i++);
784 if(!blurcall.amount_complete(max-(bw+bh),max))return false;
787 GuassianBlur_1x3(*gauss_surface);
793 GuassianBlur_1x2(*gauss_surface);
796 //sinfg::warning("Didn't crash yet bi - %d",i++);
804 /*if(get_amount()!=1.0 || get_blend_method()!=Color::BLEND_STRAIGHT)
807 for(y=0;y<renddesc.get_h();y++)
808 for(x=0;x<renddesc.get_w();x++)
809 worksurface[y][x]=Color::blend(temp_surface[y][x],worksurface[y][x],get_amount(),get_blend_method());
811 //sinfg::warning("Didn't crash yet b end",i++);
819 // Scale up the alpha
821 //be sure the surface is of the correct size
822 //surface->set_wh(renddesc.get_w(),renddesc.get_h());
825 //divide out the alpha
830 Color a = worksurface[y][x];
833 a.set_r(a.get_r()/a.get_a());
834 a.set_g(a.get_g()/a.get_a());
835 a.set_b(a.get_b()/a.get_a());
838 else out[y][x]=Color::alpha();
842 //we are FRIGGGIN done....
843 blurcall.amount_complete(100,100);
848 bool Blur::operator ()(const surface<float> &surface,
849 const Vector &resolution,
850 surface<float> &out) const
852 int w = surface.get_w(),
855 if(w == 0 || h == 0 || resolution[0] == 0 || resolution[1] == 0) return false;
857 const Real pw = resolution[0]/w,
858 ph = resolution[1]/h;
860 int halfsizex = (int) (abs(size[0]*.5/pw) + 1),
861 halfsizey = (int) (abs(size[1]*.5/ph) + 1);
864 SuperCallback blurcall(cb,0,5000,5000);
866 etl::surface<float> worksurface(surface);
868 //don't need to premultiply because we are dealing with ONLY alpha
872 case Blur::DISC: // D I S C ----------------------------------------------------------
877 if(size[0] && size[1] && w*h>2)
880 etl::surface<float> tmp_surface(worksurface);
886 //accumulate all the pixels in an ellipse of w,h about the current pixel
890 for(y2=-bh;y2<=bh;y2++)
892 for(x2=-bw;x2<=bw;x2++)
894 //get the floating point distance away from the origin pixel in relative coords
895 float tmp_x=(float)x2/bw;
896 float tmp_y=(float)y2/bh;
900 //ignore if it's outside of the disc
904 //cap the pixel indices to inside the surface
909 if( u >= w ) u = w-1;
912 if( v >= h ) v = h-1;
914 //accumulate the color, and # of pixels added in
915 a += tmp_surface[v][u];
920 //blend the color with the original color
921 //if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
922 worksurface[y][x]=a/total;
924 // worksurface[y][x]=Color::blend(color/total,tmp_surface[y][x],get_amount(),get_blend_method());
926 if(!blurcall.amount_complete(y,h))
928 //if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
935 //if we don't qualify for disc blur just use box blur
938 case Blur::BOX: // B O X -------------------------------------------------------
941 etl::surface<float> temp_surface;
942 temp_surface.set_wh(w,h);
946 int length = halfsizex;
947 length=std::max(1,length);
949 etl::hbox_blur(worksurface.begin(),worksurface.end(),length,temp_surface.begin());
951 else temp_surface = worksurface;
954 //etl::surface<float> temp_surface2;
955 //temp_surface2.set_wh(w,h);
959 int length = halfsizey;
960 length = std::max(1,length);
961 etl::vbox_blur(temp_surface.begin(),temp_surface.end(),length,worksurface.begin());
963 else worksurface = temp_surface;
965 //blend with the original surface
971 worksurface[y][x]=temp_surface2[y][x];//Color::blend(temp_surface2[y][x],worksurface[y][x],get_amount(),get_blend_method());
977 case Blur::FASTGAUSSIAN: // F A S T G A U S S I A N ----------------------------------------------
979 //fast gaussian is treated as a 3x3 type of thing, except expanded to work with the length
986 etl::surface<float> temp_surface;
987 temp_surface.set_wh(w,h);
989 //etl::surface<float> temp_surface2;
990 //temp_surface2.set_wh(w,h);
995 int length=(int)(abs((float)w/(resolution[0]))*size[0]*0.5+1);
996 length=std::max(1,length);
998 //two box blurs produces: 1 2 1
999 etl::hbox_blur(worksurface.begin(),w,h,length*3/4,temp_surface.begin());
1000 etl::hbox_blur(temp_surface.begin(),w,h,length*3/4,worksurface.begin());
1002 //else temp_surface2=worksurface;
1007 int length=(int)(abs((float)h/(resolution[1]))*size[1]*0.5+1);
1008 length=std::max(1,length);
1010 //two box blurs produces: 1 2 1 on the horizontal 1 2 1
1011 etl::vbox_blur(worksurface.begin(),w,h,length*3/4,temp_surface.begin());
1012 etl::vbox_blur(temp_surface.begin(),w,h,length*3/4,worksurface.begin());
1014 //else temp_surface2=temp_surface2;
1021 worksurface[y][x]=temp_surface2[y][x];//Color::blend(temp_surface2[y][x],worksurface[y][x],get_amount(),get_blend_method());
1027 case Blur::CROSS: // C R O S S -------------------------------------------------------
1030 etl::surface<float> temp_surface;
1031 temp_surface.set_wh(worksurface.get_w(),worksurface.get_h());
1035 int length = halfsizex;
1036 length = std::max(1,length);
1038 etl::hbox_blur(worksurface.begin(),worksurface.end(),length,temp_surface.begin());
1040 else temp_surface = worksurface;
1043 etl::surface<float> temp_surface2;
1044 temp_surface2.set_wh(worksurface.get_w(),worksurface.get_h());
1048 int length = halfsizey;
1049 length = std::max(1,length);
1051 etl::vbox_blur(worksurface.begin(),worksurface.end(),length,temp_surface2.begin());
1053 else temp_surface2 = worksurface;
1055 //blend the two together
1062 worksurface[y][x] = (temp_surface[y][x]+temp_surface2[y][x])/2;//Color::blend((temp_surface[y][x]+temp_surface2[y][x])/2,worksurface[y][x],get_amount(),get_blend_method());
1069 case Blur::GAUSSIAN: // G A U S S I A N ----------------------------------------------
1071 #ifndef GAUSSIAN_ADJUSTMENT
1072 #define GAUSSIAN_ADJUSTMENT (0.05)
1075 Real pw = (Real)w/(resolution[0]);
1076 Real ph = (Real)h/(resolution[1]);
1078 //etl::surface<float> temp_surface;
1079 etl::surface<float> *gauss_surface;
1081 //if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
1082 gauss_surface = &worksurface;
1085 temp_surface = worksurface;
1086 gauss_surface = &temp_surface;
1089 /* Squaring the pw and ph values
1090 is necessary to insure consistant
1091 results when rendered to different
1093 Unfortunately, this automaticly
1094 squares our rendertime.
1095 There has got to be a faster way...
1100 int bw = (int)(abs(pw)*size[0]*GAUSSIAN_ADJUSTMENT+0.5);
1101 int bh = (int)(abs(ph)*size[1]*GAUSSIAN_ADJUSTMENT+0.5);
1104 float *SC0=new float[w+2];
1105 float *SC1=new float[w+2];
1106 float *SC2=new float[w+2];
1107 float *SC3=new float[w+2];
1109 memset(SC0,0,(w+2)*sizeof(float));
1110 memset(SC0,0,(w+2)*sizeof(float));
1111 memset(SC0,0,(w+2)*sizeof(float));
1112 memset(SC0,0,(w+2)*sizeof(float));
1118 if(!blurcall.amount_complete(max-(bw+bh),max))return false;
1122 etl::gaussian_blur_5x5_(gauss_surface->begin(),gauss_surface->get_w(),gauss_surface->get_h(),SC0,SC1,SC2,SC3);
1128 etl::gaussian_blur_3x3(gauss_surface->begin(),gauss_surface->end());
1134 GuassianBlur_2x2(*gauss_surface);
1141 if(!blurcall.amount_complete(max-(bw+bh),max))return false;
1144 GuassianBlur_3x1(*gauss_surface);
1150 GuassianBlur_2x1(*gauss_surface);
1157 if(!blurcall.amount_complete(max-(bw+bh),max))return false;
1160 GuassianBlur_1x3(*gauss_surface);
1166 GuassianBlur_1x2(*gauss_surface);
1176 /*if(get_amount()!=1.0 || get_blend_method()!=Color::BLEND_STRAIGHT)
1179 for(y=0;y<renddesc.get_h();y++)
1180 for(x=0;x<renddesc.get_w();x++)
1181 worksurface[y][x]=Color::blend(temp_surface[y][x],worksurface[y][x],get_amount(),get_blend_method());
1190 //be sure the surface is of the correct size
1191 //surface->set_wh(renddesc.get_w(),renddesc.get_h());
1194 //divide out the alpha - don't need to cause we rock
1197 //we are FRIGGGIN done....
1198 blurcall.amount_complete(100,100);
1203 /* === E N T R Y P O I N T ================================================= */