--- grabber-meteor.cc.orig Fri Jun 26 11:25:55 1998 +++ grabber-meteor.cc Fri Jun 26 11:18:53 1998 @@ -43,7 +43,6 @@ * */ -/*#define FRAME_CNTS /* print frame counts and fps when device stops -- debug */ #include #include #include @@ -51,16 +50,16 @@ #include #include #include -#ifdef FRAME_CNTS -#include -#endif #include "grabber.h" +#include "crdef.h" #include "Tcl.h" #include "device-input.h" #include "module.h" +#include "bsd-endian.h" #include +#include /*XXX*/ #define NTSC_WIDTH 320 @@ -72,62 +71,78 @@ class MeteorGrabber : public Grabber { - public: - MeteorGrabber(const char* name, const char* format); + public: + MeteorGrabber(const char* name); virtual ~MeteorGrabber(); + virtual int command(int argc, const char*const* argv); + virtual void fps(int); virtual void start(); virtual void stop(); - virtual void fps(int); - protected: - virtual int command(int argc, const char*const* argv); - virtual int capture(); virtual int grab(); + protected: void format(); - void setsize(); + virtual void setsize() = 0; + void suppress(const u_char* in, int istride); + virtual void saveblks(const u_char* in, int istride) = 0; + void set_size_meteor(int w, int h); int video_format_; /* video input format: NTSC or PAL */ int dev_; /* device fd */ int port_; /* video input port */ - int coder_format_; /* 411, 422, or cif */ u_int basewidth_; /* Height of frame to be captured */ u_int baseheight_; /* Width of frame to be captured */ u_int decimate_; /* division of base sizes */ volatile u_int* pyuv_; /* pointer to yuv data */ -#ifdef FRAME_CNTS - struct meteor_counts cnts_; /* pointer to counters */ - double start_time_; -#endif + int tuner_ ; /* tuner device... */ }; -static const int f_411 = 0; /* coder_format_s */ -static const int f_422 = 1; -static const int f_cif = 2; +class Meteor422Grabber : public MeteorGrabber { + public: + Meteor422Grabber(const char* name); + protected: + void setsize(); + void saveblk(const u_char* in, u_char* yp, u_char* up, u_char* vp, + int stride, int istride); + void saveblks(const u_char* in, int istride); +}; + +class MeteorCIFGrabber : public MeteorGrabber { + public: + MeteorCIFGrabber(const char* name); + protected: + void setsize(); + void saveblk(const u_char* in, u_char* yp, u_char* up, u_char* vp, + int stride, int istride); + void saveblks(const u_char* in, int istride); +}; class MeteorDevice : public InputDevice { - public: + public: MeteorDevice(const char* nickname, const char* devname, int free); virtual int command(int argc, const char*const* argv); - protected: + protected: const char* name_; }; class MeteorScanner { - public: + public: MeteorScanner(const int n); }; + static MeteorScanner find_meteor_devices(4); MeteorScanner::MeteorScanner(const int n) { - char* devname_template = "/dev/meteor%d"; - char* nickname_template = "Matrox Meteor %d"; + static char *d[] = { "/dev/bktr%d", "/dev/meteor%d", NULL }; + char *nickname_template = "meteor-%d"; for(int i = 0; i < n; i++) { - char *devname = new char[strlen(devname_template) + 3]; + for (int j = 0 ; d[j] != NULL ; j++) { + char *devname = new char[strlen(d[j]) + 3]; char *nickname = new char[strlen(nickname_template) + 3]; + sprintf(devname, d[j], i); sprintf(nickname, nickname_template, i + 1); - sprintf(devname, devname_template, i); if(access(devname, R_OK) == 0) { int fd = open(devname, O_RDONLY); if(fd < 0) { @@ -140,8 +155,9 @@ delete nickname; delete devname; } + } + } } -} MeteorDevice::MeteorDevice(const char* nickname, const char *devname, int free): InputDevice(nickname), name_(devname) @@ -149,6 +165,7 @@ if(free) attributes_ = "\ format {422 411} \ +type {pal ntsc secam auto} \ size {large normal small cif} \ port {RCA Port-1 Port-2 Port-3 S-Video RGB}"; else @@ -160,7 +177,10 @@ Tcl& tcl = Tcl::instance(); if ((argc == 3) && (strcmp(argv[1], "open") == 0)) { TclObject* o = 0; - o = new MeteorGrabber(name_, argv[2]); + if (strcmp(argv[2], "422") == 0) + o = new Meteor422Grabber(name_); + else if (strcmp(argv[2], "cif") == 0) + o = new MeteorCIFGrabber(name_); if (o != 0) tcl.result(o->name()); return (TCL_OK); @@ -168,19 +188,15 @@ return (InputDevice::command(argc, argv)); } -MeteorGrabber::MeteorGrabber(const char* name, const char* format) +MeteorGrabber::MeteorGrabber(const char* name) { - coder_format_ = -1; - if(!strcmp(format, "411")) coder_format_ = f_411; - if(!strcmp(format, "422")) coder_format_ = f_422; - if(!strcmp(format, "cif")) coder_format_ = f_cif; - if(coder_format_ == -1) { - fprintf(stderr, - "vic: MeteorGrabber: unsupported format: %s\n", - format); - abort(); - } - + int devnum; + if (sscanf(name, "/dev/bktr%d", &devnum) == 1) { + char *tunerdev = new char[strlen(name) + 3]; + sprintf(tunerdev, "/dev/tuner%d", devnum); + tuner_ = open(tunerdev, O_RDONLY); + } else + tuner_ = -1; dev_ = open(name, O_RDONLY); if (dev_ == -1) { status_ = -1; @@ -203,52 +219,33 @@ if (dev_ != -1) { close(dev_); } + if (tuner_ != -1) + close(tuner_); } -void MeteorGrabber::setsize() +void MeteorGrabber::set_size_meteor(int w, int h) { struct meteor_geomet geom; - geom.rows = (baseheight_ / decimate_) &~0xf; /* 0xf, ugh! */ - geom.columns = (basewidth_ / decimate_) &~0xf; + geom.rows = h &~0xf; /* 0xf, ugh! */ + geom.columns = w &~0xf; geom.frames = 1; - geom.oformat = METEOR_GEO_UNSIGNED; - geom.oformat |= METEOR_GEO_YUV_422; + geom.oformat = METEOR_GEO_UNSIGNED | METEOR_GEO_YUV_PACKED; /* * If we can get by with only reading even fields, then by all * means do so. */ unsigned short status; - ioctl(dev_, METEORSTATUS, &status); - if(status & METEOR_STATUS_HCLK) { /* do we have a source? */ - /* No source, assume ntsc*/ + // ioctl(dev_, METEORSTATUS, &status); + if ( video_format_ == METEOR_FMT_NTSC ) { if(geom.rows <= NTSC_HEIGHT && geom.columns <= NTSC_WIDTH) geom.oformat |= METEOR_GEO_EVEN_ONLY; } else { - if(status & METEOR_STATUS_FIDT) { /* is it pal or ntsc? */ - /* 60 hz */ - if(geom.rows<=NTSC_HEIGHT && geom.columns<=NTSC_WIDTH) - geom.oformat |= METEOR_GEO_EVEN_ONLY; - } else { /* 50 hz */ if(geom.rows<=PAL_HEIGHT && geom.columns<=PAL_WIDTH) geom.oformat |= METEOR_GEO_EVEN_ONLY; } - } - if(ioctl(dev_, METEORSETGEO, &geom) < 0) perror("vic: METERSETGEO: "); - - switch(coder_format_) { - case f_422: - set_size_422(geom.columns, geom.rows); - break; - case f_cif: - case f_411: - set_size_411(geom.columns, geom.rows); - break; - } - - allocref(); /* allocate reference frame */ } void MeteorGrabber::format() @@ -285,11 +282,6 @@ baseheight_ = PAL_HEIGHT * 2; basewidth_ = PAL_WIDTH * 2; } - - if(coder_format_ == f_cif) { - baseheight_ = CIF_HEIGHT * 2; - basewidth_ = CIF_WIDTH * 2; - } setsize(); } @@ -299,15 +291,6 @@ format(); int cmd = METEOR_CAP_SINGLE; ioctl(dev_, METEORCAPTUR, (char*)&cmd); -#ifdef FRAME_CNTS - cnts_.fifo_errors = 0; - cnts_.dma_errors = 0; - cnts_.frames_captured = 0; - cnts_.even_fields_captured = 0; - cnts_.odd_fields_captured = 0; - ioctl(dev_, METEORSCOUNT, &cnts_); - start_time_ = gettimeofday(); -#endif cmd = METEOR_CAP_CONTINOUS; ioctl(dev_, METEORCAPTUR, (char*)&cmd); @@ -319,19 +302,6 @@ int cmd = METEOR_CAP_STOP_CONT; ioctl(dev_, METEORCAPTUR, (char*)&cmd); -#ifdef FRAME_CNTS - double endtime = gettimeofday() ; - ioctl(dev_, METEORGCOUNT, &cnts_); - int diff = (int)((endtime-start_time_) * 1e-6 + 0.5); - printf("frames = %d, even fields = %d, odd fields = %d,\n\ -fifo errors = %d, dma errors = %d, seconds = %d", - cnts_.frames_captured, cnts_.even_fields_captured, - cnts_.odd_fields_captured, cnts_.fifo_errors, cnts_.dma_errors, - diff); - if(diff) - printf(",fps = %d", cnts_.frames_captured/diff); - printf("\n"); -#endif Grabber::stop(); } @@ -345,15 +315,15 @@ int MeteorGrabber::command(int argc, const char*const* argv) { + Tcl& tcl = Tcl::instance(); if (argc == 3) { if (strcmp(argv[1], "decimate") == 0) { int dec = atoi(argv[2]); - Tcl& tcl = Tcl::instance(); if (dec <= 0) { tcl.resultf("%s: divide by zero", argv[0]); return (TCL_ERROR); } - if (dec != decimate_) { + if ((u_int)dec != decimate_) { decimate_ = dec; if(running_) { stop(); @@ -362,7 +332,8 @@ } } return (TCL_OK); - } else if (strcmp(argv[1], "port") == 0) { + } + if (strcmp(argv[1], "port") == 0) { int p = port_; if(!strcmp(argv[2], "RCA")) p = METEOR_INPUT_DEV0; if(!strcmp(argv[2], "Port-1")) p = METEOR_INPUT_DEV1; @@ -377,7 +348,30 @@ ioctl(dev_, METEORSINPUT, &port_); } return (TCL_OK); - } else if (strcmp(argv[1], "format") == 0 || + } + if (strcmp(argv[1], "freeze") == 0) { + int cmd = METEOR_CAP_CONTINOUS ; + if ( atoi(argv[2]) != 0 ) + cmd = METEOR_CAP_STOP_CONT; + ioctl(dev_, METEORCAPTUR, (char*)&cmd); + return (TCL_OK); + } + if (strcmp(argv[1], "chan") == 0) { + int p = port_; + int c = atoi(argv[2]); + if (c > 0 && c < 199) + p = METEOR_INPUT_DEV1 ; + else + p = METEOR_INPUT_DEV0 ; + if (p != port_) { + port_ = p; + ioctl(dev_, METEORSINPUT, &port_); + } + if (p == METEOR_INPUT_DEV1) + ioctl(tuner_, TVTUNER_SETCHNL, &c); + return (TCL_OK); + } + if (strcmp(argv[1], "format") == 0 || strcmp(argv[1], "type") == 0) { if (strcmp(argv[2], "auto") == 0) video_format_ = METEOR_FMT_AUTOMODE; @@ -390,14 +384,35 @@ if (running_) format(); return (TCL_OK); - } else if (strcmp(argv[1], "contrast") == 0) { - contrast(atof(argv[2])); - return (TCL_OK); + } + if (strcmp(argv[1], "brightness") == 0) { + u_char val = atoi(argv[2]); + ioctl(dev_, METEORSBRIG, &val); + return (TCL_OK); + } + if (strcmp(argv[1], "contrast") == 0) { + u_char val = atoi(argv[2]); + ioctl(dev_, METEORSCONT, &val); + return (TCL_OK); + } + if (strcmp(argv[1], "hue") == 0) { + char val = atoi(argv[2]); + ioctl(dev_, METEORSHUE, &val); + return (TCL_OK); + } + if (strcmp(argv[1], "saturation") == 0) { + u_char val = atoi(argv[2]); + ioctl(dev_, METEORSCSAT, &val); + return (TCL_OK); + } + if (strcmp(argv[1], "uvgain") == 0) { + u_char val = atoi(argv[2]); + ioctl(dev_, METEORSCHCV, &val); + return (TCL_OK); } } else if (argc == 2) { if (strcmp(argv[1], "format") == 0 || strcmp(argv[1], "type") == 0) { - Tcl& tcl = Tcl::instance(); switch (video_format_) { case METEOR_FMT_AUTOMODE: @@ -423,54 +438,316 @@ return (TCL_OK); } + if (strcmp(argv[1], "brightness") == 0) { + u_char val; + ioctl(dev_, METEORGBRIG, &val); + tcl.resultf("%d", (unsigned int)val); + return (TCL_OK); + } + if (strcmp(argv[1], "contrast") == 0) { + u_char val; + ioctl(dev_, METEORGCONT, &val); + tcl.resultf("%d", (int)val); + return (TCL_OK); + } + if (strcmp(argv[1], "hue") == 0) { + char val; + ioctl(dev_, METEORGHUE, &val); + tcl.resultf("%d", (int)val); + return (TCL_OK); + } + if (strcmp(argv[1], "saturation") == 0) { + u_char val; + ioctl(dev_, METEORGCSAT, &val); + tcl.resultf("%d", (int)val); + return (TCL_OK); + } + if (strcmp(argv[1], "uvgain") == 0) { + u_char val; + ioctl(dev_, METEORGCHCV, &val); + tcl.resultf("%d", (int)val); + return (TCL_OK); + } } return (Grabber::command(argc, argv)); } -int MeteorGrabber::capture() -{ - if(pyuv_ == 0) return 0; - - volatile u_int* py = pyuv_; - volatile u_int* pu = (u_int *)((u_int)py + (u_int)framesize_); - volatile u_int* pv = (u_int *)((u_int)pu + (framesize_ >> 1)); - u_int* lum = (u_int *)frame_; - u_int* uoff = (u_int *)((u_int)lum + (u_int)framesize_); - int f422 = coder_format_ == f_422; - u_int* voff = (u_int *)((u_int)uoff + - (u_int)(framesize_>>(f422?1:2))); - int numc = ((basewidth_/decimate_) &~0xf) >> 3; - - for (int row = 0; row < (((baseheight_/decimate_)&~0xf) >> 1); row++) { - for(int col = 0; col < numc; col++) { - *lum++ = *py++; - *lum++ = *py++; - *uoff++ = *pu++; - *voff++ = *pv++; - } - for(col = 0; col < numc; col++) { - *lum++ = *py++; - *lum++ = *py++; - if(f422) { /* only copy odd in 4:2:2 format */ - *uoff++ = *pu++; - *voff++ = *pv++; - - } - } - if(!f422) { /* skip odd if 4:1:1 or cif format */ - pu += numc; - pv += numc; - } - } - return 1; +#define U 0 +#define Y0 1 +#define V 2 +#define Y1 3 + +/* + * define these for REPLENISH macro used below + */ +#define DIFF4(in, frm, v) \ + v += (in)[Y0] - (frm)[0]; \ + v += (in)[Y1] - (frm)[1]; \ + v += (in)[Y0+4] - (frm)[2]; \ + v += (in)[Y1+4] - (frm)[3]; + +#define DIFFLINE(in, frm, left, center, right) \ + DIFF4(in + 0*8, frm + 0*4, left); \ + DIFF4(in + 1*8, frm + 1*4, center); \ + DIFF4(in + 2*8, frm + 2*4, center); \ + DIFF4(in + 3*8, frm + 3*4, right); \ + if (right < 0) \ + right = -right; \ + if (left < 0) \ + left = -left; \ + if (center < 0) \ + center = -center; + +void MeteorGrabber::suppress(const u_char* devbuf, int is) +{ + const u_char* start = frame_ + 16 * vstart_ * outw_ + 16 * hstart_; + REPLENISH(devbuf, start, is, 2, + hstart_, hstop_, vstart_, vstop_); } int MeteorGrabber::grab() { - if (capture() == 0) - return (0); - suppress(frame_); - saveblks(frame_); - YuvFrame f(media_ts(), frame_, crvec_, outw_, outh_); + if (pyuv_ == 0) + return 0; + + int istride = inw_ * 2; + suppress((u_char*)pyuv_, istride); + saveblks((u_char*)pyuv_, istride); + u_int32_t ts = media_ts(); + YuvFrame f(ts, frame_, crvec_, outw_, outh_); return (target_->consume(&f)); +} + +Meteor422Grabber::Meteor422Grabber(const char* name) + : MeteorGrabber(name) +{ +} + +MeteorCIFGrabber::MeteorCIFGrabber(const char* name) + : MeteorGrabber(name) +{ +} + +void Meteor422Grabber::setsize() +{ + int w = basewidth_ / decimate_; + int h = baseheight_ / decimate_; + set_size_meteor(w, h); + set_size_422(w, h); +} + +inline void +Meteor422Grabber::saveblk(const u_char* in, + u_char* yp, u_char* up, u_char* vp, int stride, int istride) +{ + for (int i = 16; --i >= 0; ) { + /* + * Each iteration of this loop grabs 16 Ys & 8 U/Vs. + */ + register u_int y0, y1, u, v; + + u = in[U + 0*4] << SHIFT(24) | + in[U + 1*4] << SHIFT(16) | + in[U + 2*4] << SHIFT(8) | + in[U + 3*4] << SHIFT(0); + v = in[V + 0*4] << SHIFT(24) | + in[V + 1*4] << SHIFT(16) | + in[V + 2*4] << SHIFT(8) | + in[V + 3*4] << SHIFT(0); + y0 = in[Y0 + 0*4] << SHIFT(24) | + in[Y1 + 0*4] << SHIFT(16) | + in[Y0 + 1*4] << SHIFT(8) | + in[Y1 + 1*4] << SHIFT(0); + y1 = in[Y0 + 2*4] << SHIFT(24) | + in[Y1 + 2*4] << SHIFT(16) | + in[Y0 + 3*4] << SHIFT(8) | + in[Y1 + 3*4] << SHIFT(0); + + ((u_int*)yp)[0] = y0; + ((u_int*)yp)[1] = y1; + ((u_int*)up)[0] = u; + ((u_int*)vp)[0] = v; + + u = in[U + 4*4] << SHIFT(24) | + in[U + 5*4] << SHIFT(16) | + in[U + 6*4] << SHIFT(8) | + in[U + 7*4] << SHIFT(0); + v = in[V + 4*4] << SHIFT(24) | + in[V + 5*4] << SHIFT(16) | + in[V + 6*4] << SHIFT(8) | + in[V + 7*4] << SHIFT(0); + y0 = in[Y0 + 4*4] << SHIFT(24) | + in[Y1 + 4*4] << SHIFT(16) | + in[Y0 + 5*4] << SHIFT(8) | + in[Y1 + 5*4] << SHIFT(0); + y1 = in[Y0 + 6*4] << SHIFT(24) | + in[Y1 + 6*4] << SHIFT(16) | + in[Y0 + 7*4] << SHIFT(8) | + in[Y1 + 7*4] << SHIFT(0); + + ((u_int*)yp)[2] = y0; + ((u_int*)yp)[3] = y1; + ((u_int*)up)[1] = u; + ((u_int*)vp)[1] = v; + + in += istride; + yp += stride; + up += stride >> 1; + vp += stride >> 1; + } +} + +void Meteor422Grabber::saveblks(const u_char* devbuf, int is) +{ + u_char* crv = crvec_; + int off = framesize_; + u_char* lum = frame_; + u_char* chm = lum + off; + off >>= 1; + int stride = 15 * outw_; + int istride = is * 15; + for (int y = 0; y < blkh_; ++y) { + for (int x = 0; x < blkw_; ++x) { + int s = *crv++; + if ((s & CR_SEND) != 0) + saveblk(devbuf, lum, chm, chm + off, outw_, is); + + devbuf += 32; + lum += 16; + chm += 8; + } + lum += stride; + chm += stride >> 1; + devbuf += istride; + } +} + +void MeteorCIFGrabber::setsize() +{ + int w = basewidth_ / decimate_; + int h = baseheight_ / decimate_; + set_size_meteor(w, h); + set_size_cif(w, h); +} + +inline void +MeteorCIFGrabber::saveblk(const u_char* in, + u_char* yp, u_char* up, u_char* vp, int stride, int istride) +{ + for (int i = 8; --i >= 0; ) { + /* + * Each iteration of this loop grabs 32 Ys & 16 U/Vs. + */ + register u_int y0, y1, u, v; + + u = in[U + 0*4] << SHIFT(24) | + in[U + 1*4] << SHIFT(16) | + in[U + 2*4] << SHIFT(8) | + in[U + 3*4] << SHIFT(0); + v = in[V + 0*4] << SHIFT(24) | + in[V + 1*4] << SHIFT(16) | + in[V + 2*4] << SHIFT(8) | + in[V + 3*4] << SHIFT(0); + y0 = in[Y0 + 0*4] << SHIFT(24) | + in[Y1 + 0*4] << SHIFT(16) | + in[Y0 + 1*4] << SHIFT(8) | + in[Y1 + 1*4] << SHIFT(0); + y1 = in[Y0 + 2*4] << SHIFT(24) | + in[Y1 + 2*4] << SHIFT(16) | + in[Y0 + 3*4] << SHIFT(8) | + in[Y1 + 3*4] << SHIFT(0); + + ((u_int*)yp)[0] = y0; + ((u_int*)yp)[1] = y1; + ((u_int*)up)[0] = u; + ((u_int*)vp)[0] = v; + + u = in[U + 4*4] << SHIFT(24) | + in[U + 5*4] << SHIFT(16) | + in[U + 6*4] << SHIFT(8) | + in[U + 7*4] << SHIFT(0); + v = in[V + 4*4] << SHIFT(24) | + in[V + 5*4] << SHIFT(16) | + in[V + 6*4] << SHIFT(8) | + in[V + 7*4] << SHIFT(0); + y0 = in[Y0 + 4*4] << SHIFT(24) | + in[Y1 + 4*4] << SHIFT(16) | + in[Y0 + 5*4] << SHIFT(8) | + in[Y1 + 5*4] << SHIFT(0); + y1 = in[Y0 + 6*4] << SHIFT(24) | + in[Y1 + 6*4] << SHIFT(16) | + in[Y0 + 7*4] << SHIFT(8) | + in[Y1 + 7*4] << SHIFT(0); + + ((u_int*)yp)[2] = y0; + ((u_int*)yp)[3] = y1; + ((u_int*)up)[1] = u; + ((u_int*)vp)[1] = v; + + in += istride; + yp += stride; + up += stride >> 1; + vp += stride >> 1; + + /* do the 2nd (y only instead of yuv) line */ + + y0 = in[Y0 + 0*4] << SHIFT(24) | + in[Y1 + 0*4] << SHIFT(16) | + in[Y0 + 1*4] << SHIFT(8) | + in[Y1 + 1*4] << SHIFT(0); + y1 = in[Y0 + 2*4] << SHIFT(24) | + in[Y1 + 2*4] << SHIFT(16) | + in[Y0 + 3*4] << SHIFT(8) | + in[Y1 + 3*4] << SHIFT(0); + + ((u_int*)yp)[0] = y0; + ((u_int*)yp)[1] = y1; + + y0 = in[Y0 + 4*4] << SHIFT(24) | + in[Y1 + 4*4] << SHIFT(16) | + in[Y0 + 5*4] << SHIFT(8) | + in[Y1 + 5*4] << SHIFT(0); + y1 = in[Y0 + 6*4] << SHIFT(24) | + in[Y1 + 6*4] << SHIFT(16) | + in[Y0 + 7*4] << SHIFT(8) | + in[Y1 + 7*4] << SHIFT(0); + + ((u_int*)yp)[2] = y0; + ((u_int*)yp)[3] = y1; + + in += istride; + yp += stride; + } +} + +void MeteorCIFGrabber::saveblks(const u_char* in, int is) +{ + u_char* crv = crvec_; + int off = framesize_; + u_char* lum = frame_; + u_char* chm = lum + off; + off >>= 2; + + crv += vstart_ * blkw_ + hstart_; + lum += vstart_ * outw_ * 16 + hstart_ * 16; + chm += vstart_ * (outw_ >> 1) * 8 + hstart_ * 8; + + int skip = hstart_ + (blkw_ - hstop_); + + for (int y = vstart_; y < vstop_; ++y) { + const u_char* nin = in; + for (int x = hstart_; x < hstop_; ++x) { + int s = *crv++; + if ((s & CR_SEND) != 0) + saveblk(in, lum, chm, chm + off, outw_, is); + + in += 32; + lum += 16; + chm += 8; + } + crv += skip; + lum += 15 * outw_ + skip * 16; + chm += 7 * (outw_ >> 1) + skip * 8; + in = nin + 16 * is; + } }