00001
00022
00023
00024
00025
00026 #ifndef __SYNFIG_COLOR_H
00027 #define __SYNFIG_COLOR_H
00028
00029
00030
00031
00032
00033 #include <math.h>
00034 #include <cassert>
00035 #include "gamma.h"
00036 #include <synfig/string.h>
00037
00038 #ifdef USE_HALF_TYPE
00039 #include <OpenEXR/half.h>
00040 #endif
00041
00042 #ifndef SYNFIG_NO_ANGLE
00043 # include "angle.h"
00044 #endif
00045
00046
00047
00048 #define use_colorspace_gamma() App::use_colorspace_gamma
00049 #define colorspace_gamma() (2.2f)
00050 #define gamma_in(x) ((x>=0) ? pow((float)x,1.0f/colorspace_gamma()) : -pow((float)-x,1.0f/colorspace_gamma()))
00051 #define gamma_out(x) ((x>=0) ? pow((float)x, colorspace_gamma()) : -pow((float)-x, colorspace_gamma()))
00052
00053 #ifdef WIN32
00054 #include <float.h>
00055 #ifndef isnan
00056 extern "C" { int _isnan(double x); }
00057 #define isnan _isnan
00058 #endif
00059 #endif
00060
00061
00062
00063 #if defined(__APPLE__) && !defined(SYNFIG_ISNAN_FIX)
00064 #ifdef isnan
00065 #undef isnan
00066 #endif
00067 inline bool isnan(double x) { return x != x; }
00068 inline bool isnan(float x) { return x != x; }
00069 #define SYNFIG_ISNAN_FIX 1
00070 #endif
00071
00072 namespace synfig {
00073
00074 #ifdef USE_HALF_TYPE
00075 typedef half ColorReal;
00076 #else
00077 typedef float ColorReal;
00078 #endif
00079
00080 static const float EncodeYUV[3][3]=
00081 {
00082 { 0.299f, 0.587f, 0.114f },
00083 { -0.168736f, -0.331264f, 0.5f },
00084 { 0.5f, -0.418688f, -0.081312f }
00085 };
00086
00087 static const float DecodeYUV[3][3]=
00088 {
00089 { 1.0f, 0.0f, 1.402f },
00090 { 1.0f, -0.344136f, -0.714136f },
00091 { 1.0f, 1.772f, 0.0f }
00092 };
00093
00094
00095
00096
00097
00098 #ifdef USE_HALF_TYPE
00099 class ColorAccumulator;
00100 #endif
00101
00102
00103
00104
00109 class Color
00110 {
00111 public:
00112 typedef ColorReal value_type;
00113
00114 private:
00115 value_type a_, r_, g_, b_;
00116
00117 public:
00118
00119 const String get_string(void)const;
00120
00121 Color &
00122 operator+=(const Color &rhs)
00123 {
00124 r_+=rhs.r_;
00125 g_+=rhs.g_;
00126 b_+=rhs.b_;
00127 a_+=rhs.a_;
00128 return *this;
00129 }
00130
00131 Color &
00132 operator-=(const Color &rhs)
00133 {
00134 r_-=rhs.r_;
00135 g_-=rhs.g_;
00136 b_-=rhs.b_;
00137 a_-=rhs.a_;
00138 return *this;
00139 }
00140
00141 Color &
00142 operator*=(const float &rhs)
00143 {
00144 r_*=rhs;
00145 g_*=rhs;
00146 b_*=rhs;
00147 a_*=rhs;
00148 return *this;
00149 }
00150
00151 Color &
00152 operator/=(const float &rhs)
00153 {
00154 const float temp(value_type(1)/rhs);
00155 r_*=temp;
00156 g_*=temp;
00157 b_*=temp;
00158 a_*=temp;
00159 return *this;
00160 }
00161
00162 Color
00163 operator+(const Color &rhs)const
00164 { return Color(*this)+=rhs; }
00165
00166 Color
00167 operator-(const Color &rhs)const
00168 { return Color(*this)-=rhs; }
00169
00170 Color
00171 operator*(const float &rhs)const
00172 { return Color(*this)*=rhs; }
00173
00174 Color
00175 operator/(const float &rhs)const
00176 { return Color(*this)/=rhs; }
00177
00178 bool
00179 operator==(const Color &rhs)const
00180 { return r_==rhs.r_ && g_==rhs.g_ && b_==rhs.b_ && a_==rhs.a_; }
00181
00182 bool
00183 operator!=(const Color &rhs)const
00184 { return r_!=rhs.r_ || g_!=rhs.g_ || b_!=rhs.b_ || a_!=rhs.a_; }
00185
00186 Color
00187 operator-()const
00188 { return Color(-r_,-g_,-b_,-a_); }
00189
00191 Color
00192 operator~()const
00193 { return Color(1.0f-r_,1.0f-g_,1.0f-b_,a_); }
00194
00195 bool is_valid()const
00196 { return !isnan(r_) && !isnan(g_) && !isnan(b_) && !isnan(a_); }
00197
00198 Color premult_alpha() const
00199 {
00200 return Color (r_*a_, g_*a_, b_*a_, a_);
00201 }
00202
00203 Color demult_alpha() const
00204 {
00205 if(a_)
00206 {
00207 const value_type inva = 1/a_;
00208 return Color (r_*inva, g_*inva, b_*inva, a_);
00209 }else return alpha();
00210 }
00211
00212 public:
00213
00214
00215
00216
00217 Color() :a_(0), r_(0), g_(0), b_(0) { }
00218 Color(const value_type &f) :a_(f),r_(f), g_(f), b_(f) { }
00219 Color(int f) :a_(f),r_(f), g_(f), b_(f) { }
00220
00225 Color(const value_type& R, const value_type& G, const value_type& B, const value_type& A=1):
00226 a_(A),
00227 r_(R),
00228 g_(G),
00229 b_(B) { }
00230
00233 Color(const Color& c, const value_type& A):
00234 a_(A),
00235 r_(c.r_),
00236 g_(c.g_),
00237 b_(c.b_) { }
00238
00239
00241 Color(const Color& c):
00242 a_(c.a_),
00243 r_(c.r_),
00244 g_(c.g_),
00245 b_(c.b_) { }
00246
00247 #ifdef USE_HALF_TYPE
00248 friend class ColorAccumulator;
00250 Color(const ColorAccumulator& c);
00251 #endif
00252
00254
00255
00256
00257
00258
00259
00260
00261
00262
00264 const value_type& get_r()const { return r_; }
00265
00267 const value_type& get_g()const { return g_; }
00268
00270 const value_type& get_b()const { return b_; }
00271
00273 const value_type& get_a()const { return a_; }
00274
00276 const value_type& get_alpha()const { return get_a(); }
00277
00279 static ColorReal hex2real(String s);
00280
00282 static const String real2hex(ColorReal c);
00283
00285 const String get_hex()const { return String(real2hex(r_)+real2hex(g_)+real2hex(b_)); }
00286
00288 void set_hex(String& hex);
00289
00291 Color& set_r(const value_type& x) { r_ = x; return *this; }
00292
00294 Color& set_g(const value_type& x) { g_ = x; return *this; }
00295
00297 Color& set_b(const value_type& x) { b_ = x; return *this; }
00298
00300 Color& set_a(const value_type& x) { a_ = x; return *this; }
00301
00303 Color& set_alpha(const value_type& x) { return set_a(x); }
00304
00306 float
00307 get_y() const
00308 {
00309 return
00310 (float)get_r()*EncodeYUV[0][0]+
00311 (float)get_g()*EncodeYUV[0][1]+
00312 (float)get_b()*EncodeYUV[0][2];
00313 }
00314
00315
00317 float
00318 get_u() const
00319 {
00320 return
00321 (float)get_r()*EncodeYUV[1][0]+
00322 (float)get_g()*EncodeYUV[1][1]+
00323 (float)get_b()*EncodeYUV[1][2];
00324 }
00325
00326
00328 float
00329 get_v() const
00330 {
00331 return
00332 (float)get_r()*EncodeYUV[2][0]+
00333 (float)get_g()*EncodeYUV[2][1]+
00334 (float)get_b()*EncodeYUV[2][2];
00335 }
00336
00338
00340 float
00341 get_s() const
00342 {
00343 const float u(get_u()), v(get_v());
00344 return sqrt(u*u+v*v);
00345 }
00346
00348 Color&
00349 set_yuv(const float &y, const float &u, const float &v)
00350 {
00351 set_r(y*DecodeYUV[0][0]+u*DecodeYUV[0][1]+v*DecodeYUV[0][2]);
00352 set_g(y*DecodeYUV[1][0]+u*DecodeYUV[1][1]+v*DecodeYUV[1][2]);
00353 set_b(y*DecodeYUV[2][0]+u*DecodeYUV[2][1]+v*DecodeYUV[2][2]);
00354 return *this;
00355 }
00356
00358 Color& set_y(const float &y) { return set_yuv(y,get_u(),get_v()); }
00359
00361 Color& set_u(const float &u) { return set_yuv(get_y(),u,get_v()); }
00362
00364 Color& set_v(const float &v) { return set_yuv(get_y(),get_u(),v); }
00365
00367 Color& set_uv(const float& u, const float& v) { return set_yuv(get_y(),u,v); }
00368
00370
00371 Color&
00372 set_s(const float &x)
00373 {
00374 float u(get_u()), v(get_v());
00375 const float s(sqrt(u*u+v*v));
00376 if(s)
00377 {
00378 u=(u/s)*x;
00379 v=(v/s)*x;
00380 return set_uv(u,v);
00381 }
00382 return *this;
00383 }
00384
00386 static Color YUV(const float& y, const float& u, const float& v, const value_type& a=1)
00387 { return Color().set_yuv(y,u,v).set_a(a); }
00388
00389 #ifndef SYNFIG_NO_ANGLE
00391
00393 Angle
00394 get_hue() const
00395 { return Angle::tan(get_u(),get_v()); }
00396
00398 Angle get_uv_angle() const { return get_hue(); }
00399
00401
00402 Color&
00403 set_hue(const Angle& theta)
00404 {
00405 const float s(get_s());
00406 const float
00407 u(s*(float)Angle::sin(theta).get()),
00408 v(s*(float)Angle::cos(theta).get());
00409 return set_uv(u,v);
00410 }
00411
00413 Color& set_uv_angle(const Angle& theta) { return set_hue(theta); }
00414
00416 Color& rotate_uv(const Angle& theta)
00417 {
00418 const float a(Angle::sin(theta).get()), b(Angle::cos(theta).get());
00419 const float u(get_u()), v(get_v());
00420
00421 return set_uv(b*u-a*v,a*u+b*v);
00422 }
00423
00425
00428 Color& set_yuv(const float& y, const float& s, const Angle& theta)
00429 {
00430 return
00431 set_yuv(
00432 y,
00433 s*(float)Angle::sin(theta).get(),
00434 s*(float)Angle::cos(theta).get()
00435 );
00436 }
00437
00439
00443 static Color YUV(const float& y, const float& s, const Angle& theta, const value_type& a=1)
00444 { return Color().set_yuv(y,s,theta).set_a(a); }
00445
00446 #endif
00447
00449 Color clamped()const;
00450
00452 Color clamped_negative()const;
00453
00454
00455
00457
00458 #ifdef HAS_VIMAGE
00459 static inline Color alpha() { return Color(0,0,0,0.0000001f); }
00460 #else
00461 static inline Color alpha() { return Color(0,0,0,0); }
00462 #endif
00463 static inline Color black() { return Color(0,0,0); }
00464 static inline Color white() { return Color(1,1,1); }
00465 static inline Color gray() { return Color(0.5f,0.5f,0.5f); }
00466 static inline Color magenta() { return Color(1,0,1); }
00467 static inline Color red() { return Color(1,0,0); }
00468 static inline Color green() { return Color(0,1,0); }
00469 static inline Color blue() { return Color(0,0,1); }
00470 static inline Color cyan() { return Color(0,1,1); }
00471 static inline Color yellow() { return Color(1,1,0); }
00473
00475 enum BlendMethod
00476 {
00477 BLEND_COMPOSITE=0,
00478 BLEND_STRAIGHT=1,
00479 BLEND_ONTO=13,
00480 BLEND_STRAIGHT_ONTO=21,
00481 BLEND_BEHIND=12,
00482 BLEND_SCREEN=16,
00483 BLEND_OVERLAY=20,
00484 BLEND_HARD_LIGHT=17,
00485 BLEND_MULTIPLY=6,
00486 BLEND_DIVIDE=7,
00487 BLEND_ADD=4,
00488 BLEND_SUBTRACT=5,
00489 BLEND_DIFFERENCE=18,
00490 BLEND_BRIGHTEN=2,
00491 BLEND_DARKEN=3,
00492 BLEND_COLOR=8,
00493 BLEND_HUE=9,
00494 BLEND_SATURATION=10,
00495 BLEND_LUMINANCE=11,
00496
00497 BLEND_ALPHA_BRIGHTEN=14,
00498 BLEND_ALPHA_DARKEN=15,
00499 BLEND_ALPHA_OVER=19,
00500
00501 BLEND_END=22
00502 };
00503
00504
00505 static Color blend(Color a, Color b,float amount,BlendMethod type=BLEND_COMPOSITE);
00506
00507 static bool is_onto(BlendMethod x)
00508 {
00509 return x==BLEND_BRIGHTEN
00510 || x==BLEND_DARKEN
00511 || x==BLEND_ADD
00512 || x==BLEND_SUBTRACT
00513 || x==BLEND_MULTIPLY
00514 || x==BLEND_DIVIDE
00515 || x==BLEND_COLOR
00516 || x==BLEND_HUE
00517 || x==BLEND_SATURATION
00518 || x==BLEND_LUMINANCE
00519 || x==BLEND_ONTO
00520 || x==BLEND_STRAIGHT_ONTO
00521 || x==BLEND_SCREEN
00522 || x==BLEND_OVERLAY
00523 || x==BLEND_DIFFERENCE
00524 || x==BLEND_HARD_LIGHT
00525 ;
00526 }
00527
00529 static bool is_straight(BlendMethod x)
00530 {
00531 return x==BLEND_STRAIGHT
00532 || x==BLEND_STRAIGHT_ONTO
00533 || x==BLEND_ALPHA_BRIGHTEN
00534 ;
00535 }
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552 };
00553
00554 #ifndef USE_HALF_TYPE
00555 typedef Color ColorAccumulator;
00556 #else
00557 class ColorAccumulator
00558 {
00559 friend class Color;
00560 public:
00561 typedef float value_type;
00562
00563 private:
00564 value_type a_, r_, g_, b_;
00565
00566 public:
00567
00568 ColorAccumulator &
00569 operator+=(const ColorAccumulator &rhs)
00570 {
00571 r_+=rhs.r_;
00572 g_+=rhs.g_;
00573 b_+=rhs.b_;
00574 a_+=rhs.a_;
00575 return *this;
00576 }
00577
00578 ColorAccumulator &
00579 operator-=(const ColorAccumulator &rhs)
00580 {
00581 r_-=rhs.r_;
00582 g_-=rhs.g_;
00583 b_-=rhs.b_;
00584 a_-=rhs.a_;
00585 return *this;
00586 }
00587
00588 ColorAccumulator &
00589 operator*=(const float &rhs)
00590 {
00591 r_*=rhs;
00592 g_*=rhs;
00593 b_*=rhs;
00594 a_*=rhs;
00595 return *this;
00596 }
00597
00598 ColorAccumulator &
00599 operator/=(const float &rhs)
00600 {
00601 const float temp(value_type(1)/rhs);
00602 r_*=temp;
00603 g_*=temp;
00604 b_*=temp;
00605 a_*=temp;
00606 return *this;
00607 }
00608
00609 ColorAccumulator
00610 operator+(const ColorAccumulator &rhs)const
00611 { return Color(*this)+=rhs; }
00612
00613 ColorAccumulator
00614 operator-(const ColorAccumulator &rhs)const
00615 { return Color(*this)-=rhs; }
00616
00617 ColorAccumulator
00618 operator*(const float &rhs)const
00619 { return Color(*this)*=rhs; }
00620
00621 ColorAccumulator
00622 operator/(const float &rhs)const
00623 { return Color(*this)/=rhs; }
00624
00625 bool
00626 operator==(const ColorAccumulator &rhs)const
00627 { return r_==rhs.r_ && g_==rhs.g_ && b_==rhs.b_ && a_!=rhs.a_; }
00628
00629 bool
00630 operator!=(const ColorAccumulator &rhs)const
00631 { return r_!=rhs.r_ || g_!=rhs.g_ || b_!=rhs.b_ || a_!=rhs.a_; }
00632
00633 Color
00634 operator-()const
00635 { return ColorAccumulator(-r_,-g_,-b_,-a_); }
00636
00637 bool is_valid()const
00638 { return !isnan(r_) && !isnan(g_) && !isnan(b_) && !isnan(a_); }
00639
00640 public:
00641 ColorAccumulator() { }
00642
00647 ColorAccumulator(const value_type& R, const value_type& G, const value_type& B, const value_type& A=1):
00648 a_(A),
00649 r_(R),
00650 g_(G),
00651 b_(B) { }
00652
00654 ColorAccumulator(const ColorAccumulator& c):
00655 a_(c.a_),
00656 r_(c.r_),
00657 g_(c.g_),
00658 b_(c.b_) { }
00659
00661 ColorAccumulator(const Color& c):
00662 a_(c.a_),
00663 r_(c.r_),
00664 g_(c.g_),
00665 b_(c.b_) { }
00666
00668 ColorAccumulator(int c): a_(c),r_(c), g_(c), b_(c) { }
00669
00671 const value_type& get_r()const { return r_; }
00672
00674 const value_type& get_g()const { return g_; }
00675
00677 const value_type& get_b()const { return b_; }
00678
00680 const value_type& get_a()const { return a_; }
00681
00683 const value_type& get_alpha()const { return get_a(); }
00684
00686 ColorAccumulator& set_r(const value_type& x) { r_ = x; return *this; }
00687
00689 ColorAccumulator& set_g(const value_type& x) { g_ = x; return *this; }
00690
00692 ColorAccumulator& set_b(const value_type& x) { b_ = x; return *this; }
00693
00695 ColorAccumulator& set_a(const value_type& x) { a_ = x; return *this; }
00696
00698 ColorAccumulator& set_alpha(const value_type& x) { return set_a(x); }
00699 };
00700
00701 inline
00702 Color::Color(const ColorAccumulator& c):
00703 a_(c.a_),
00704 r_(c.r_),
00705 g_(c.g_),
00706 b_(c.b_) { }
00707
00708 #endif
00709
00710
00711
00712
00713
00714 enum PixelFormat
00715 {
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728 PF_RGB=0,
00729 PF_GRAY=(1<<0),
00730 PF_A=(1<<1),
00731 PF_Z=(1<<2),
00732 PF_BGR=(1<<3),
00733 PF_A_START=(1<<4),
00734 PF_Z_START=(1<<5),
00735 PF_ZA=(1<<6),
00736
00737 PF_A_INV=(1<<7),
00738 PF_Z_INV=(1<<8),
00739 PF_RAW_COLOR=(1<<9)+(1<<1)
00740 };
00741
00742 inline PixelFormat operator|(PixelFormat lhs, PixelFormat rhs)
00743 { return static_cast<PixelFormat>((int)lhs|(int)rhs); }
00744
00745 inline PixelFormat operator&(PixelFormat lhs, PixelFormat rhs)
00746 { return static_cast<PixelFormat>((int)lhs&(int)rhs); }
00747 #define FLAGS(x,y) (((x)&(y))==(y))
00748
00750 inline int
00751 channels(PixelFormat x)
00752 {
00753 int chan=0;
00754 if(FLAGS(x,PF_GRAY))
00755 ++chan;
00756 else
00757 chan+=3;
00758 if(FLAGS(x,PF_A))
00759 ++chan;
00760 if(FLAGS(x,PF_Z))
00761 ++chan;
00762 if(FLAGS(x,PF_RAW_COLOR))
00763 chan=sizeof(Color);
00764
00765 return chan;
00766 }
00767
00768 inline unsigned char *
00769 Color2PixelFormat(const Color &color, const PixelFormat &pf, unsigned char *out, const Gamma &gamma)
00770 {
00771 if(FLAGS(pf,PF_RAW_COLOR))
00772 {
00773 Color *outcol=reinterpret_cast<Color *>(out);
00774 *outcol=color;
00775 out+=sizeof(color);
00776 return out;
00777 }
00778
00779 int alpha=(int)((FLAGS(pf,PF_A_INV)?(-(float)color.get_a()+1):(float)color.get_a())*255);
00780 if(alpha<0)alpha=0;
00781 if(alpha>255)alpha=255;
00782
00783 if(FLAGS(pf,PF_ZA|PF_A_START|PF_Z_START))
00784 {
00785 if(FLAGS(pf,PF_Z_START))
00786 *out++;
00787 if(FLAGS(pf,PF_A_START))
00788 *out++=static_cast<unsigned char>(alpha);
00789 }
00790 else
00791 {
00792 if(FLAGS(pf,PF_A_START))
00793 *out++=static_cast<unsigned char>(alpha);
00794 if(FLAGS(pf,PF_Z_START))
00795 *out++;
00796
00797 }
00798
00799 if(FLAGS(pf,PF_GRAY))
00800 *out++=static_cast<unsigned char>(gamma.g_F32_to_U8(color.get_y()));
00801 else
00802 {
00803 if(FLAGS(pf,PF_BGR))
00804 {
00805 *out++=static_cast<unsigned char>(gamma.r_F32_to_U8(color.get_b()));
00806 *out++=static_cast<unsigned char>(gamma.g_F32_to_U8(color.get_g()));
00807 *out++=static_cast<unsigned char>(gamma.b_F32_to_U8(color.get_r()));
00808 }
00809 else
00810 {
00811 *out++=static_cast<unsigned char>(gamma.r_F32_to_U8(color.get_r()));
00812 *out++=static_cast<unsigned char>(gamma.g_F32_to_U8(color.get_g()));
00813 *out++=static_cast<unsigned char>(gamma.b_F32_to_U8(color.get_b()));
00814 }
00815 }
00816
00817 if(FLAGS(pf,PF_ZA))
00818 {
00819 if(!FLAGS(pf,PF_Z_START) && FLAGS(pf,PF_Z))
00820 out++;
00821 if(!FLAGS(pf,PF_A_START) && FLAGS(pf,PF_A))
00822 *out++=static_cast<unsigned char>(alpha);
00823 }
00824 else
00825 {
00826 if(!FLAGS(pf,PF_Z_START) && FLAGS(pf,PF_Z))
00827 out++;
00828 if(!FLAGS(pf,PF_A_START) && FLAGS(pf,PF_A))
00829 *out++=static_cast<unsigned char>(alpha);
00830 }
00831 return out;
00832 }
00833
00834 inline void
00835 convert_color_format(unsigned char *dest, const Color *src, int w, PixelFormat pf,const Gamma &gamma)
00836 {
00837 assert(w>=0);
00838 while(w--)
00839 dest=Color2PixelFormat((*(src++)).clamped(),pf,dest,gamma);
00840 }
00841
00842 inline const unsigned char *
00843 PixelFormat2Color(Color &color, const PixelFormat &pf,const unsigned char *out)
00844 {
00845 if(FLAGS(pf,PF_ZA|PF_A_START|PF_Z_START))
00846 {
00847 if(FLAGS(pf,PF_Z_START))
00848 out++;
00849 if(FLAGS(pf,PF_A_START))
00850 color.set_a((float)*out++/255);
00851 }
00852 else
00853 {
00854 if(FLAGS(pf,PF_A_START))
00855 color.set_a((float)*out++/255);
00856 if(FLAGS(pf,PF_Z_START))
00857 out++;
00858 }
00859
00860 if(FLAGS(pf,PF_GRAY))
00861 color.set_yuv((float)*out++/255,0,0);
00862 else
00863 {
00864 if(FLAGS(pf,PF_BGR))
00865 {
00866 color.set_b((float)*out++/255);
00867 color.set_g((float)*out++/255);
00868 color.set_r((float)*out++/255);
00869 }
00870 else
00871 {
00872 color.set_r((float)*out++/255);
00873 color.set_g((float)*out++/255);
00874 color.set_b((float)*out++/255);
00875 }
00876 }
00877
00878 if(FLAGS(pf,PF_ZA))
00879 {
00880 if(!FLAGS(pf,PF_Z_START) && FLAGS(pf,PF_Z))
00881 out++;
00882 if(!FLAGS(pf,PF_A_START) && FLAGS(pf,PF_A))
00883 color.set_a((float)*out++/255);
00884 }
00885 else
00886 {
00887 if(!FLAGS(pf,PF_A_START) && FLAGS(pf,PF_A))
00888 color.set_a((float)*out++/255);
00889 if(!FLAGS(pf,PF_Z_START) && FLAGS(pf,PF_Z))
00890 out++;
00891 }
00892 return out;
00893 }
00894
00895
00896
00897 };
00898
00899
00900
00901 #endif