root/vic/trunk/render/vw.cpp @ 901

Revision 901, 14.1 KB (checked in by piers, 15 years ago)

New dir: render with sub directories for mkbv, mkcube, mkhuff, ppmtolut

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * Copyright (c) 1993-1994 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by the University of
16 *      California, Berkeley and the Network Research Group at
17 *      Lawrence Berkeley Laboratory.
18 * 4. Neither the name of the University nor of the Laboratory may be used
19 *    to endorse or promote products derived from this software without
20 *    specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34static const char rcsid[] =
35    "@(#) $Header$ (LBL)";
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40extern "C" {
41//#include <tk.h>
42
43#ifdef USE_SHM
44#include <sys/ipc.h>
45#include <sys/shm.h>
46#if defined(sun) && !defined(__svr4__)
47int shmget(key_t, int, int);
48char *shmat(int, char*, int);
49int shmdt(char*);
50int shmctl(int, int, struct shmid_ds*);
51#endif
52#ifdef __osf__
53int XShmGetEventBase(struct _XDisplay *);
54#else
55int XShmGetEventBase(Display *);
56#endif
57#ifdef sgi
58#define XShmAttach __XShmAttach__
59#define XShmDetach __XShmDetach__
60#define XShmPutImage __XShmPutImage__
61#endif
62#include <X11/extensions/XShm.h>
63#ifdef sgi
64#undef XShmAttach
65#undef XShmDetach
66#undef XShmPutImage
67int XShmAttach(Display*, XShmSegmentInfo*);
68int XShmDetach(Display*, XShmSegmentInfo*);
69int XShmPutImage(Display*, Drawable, GC, XImage*, int, int, int, int,
70                 int, int, int);
71#endif
72#endif
73//#include <X11/Xutil.h>
74}
75//undefed as windows unfortunately defines it
76//#undef Status
77#include "vw.h"
78//#undef Status
79#include "color.h"
80#include "rgb-converter.h"
81
82static class VideoCommand : public TclObject {
83public:
84        VideoCommand(const char* name) : TclObject(name) { }
85protected:
86        int command(int argc, const char*const* argv);
87} video_cmd("video");
88
89/*
90 * video $path $width $height
91 */
92int VideoCommand::command(int argc, const char*const* argv)
93{
94        Tcl& tcl = Tcl::instance();
95        if (argc != 4) {
96                tcl.result("video arg mismatch");
97                return (TCL_ERROR);
98        }
99        const char* name = argv[1];
100        int width = atoi(argv[2]);
101        int height = atoi(argv[3]);
102        VideoWindow* p = new VideoWindow(name);
103        p->setsize(width, height);
104        tcl.result(p->name());
105
106        return (TCL_OK);
107}
108
109class SlowVideoImage : public StandardVideoImage {
110public:
111        SlowVideoImage(Tk_Window, int width, int height);
112        ~SlowVideoImage();
113        void putimage(Display* dpy, Window window, GC gc,
114                      int sx, int sy, int x, int y,
115                      int w, int h) const;
116};
117
118VideoImage::VideoImage(Tk_Window tk, int w, int h)
119        : width_(w), height_(h)
120{
121        dpy_ = Tk_Display(tk);
122        int depth = Tk_Depth(tk);
123        /*XXX*/
124        bpp_ =  (depth == 24) ? 32 : depth;
125}
126
127VideoImage::~VideoImage()
128{
129}
130
131StandardVideoImage* StandardVideoImage::allocate(Tk_Window tk, int w, int h)
132{
133#ifdef USE_SHM
134        extern int use_shm;
135        if (use_shm) {
136                SharedVideoImage* p = new SharedVideoImage(tk, w, h);
137                if (p->valid())
138                        return (p);
139                delete p;
140        }
141#endif
142        return (new SlowVideoImage(tk, w, h));
143}
144
145StandardVideoImage::StandardVideoImage(Tk_Window tk, int w, int h)
146        : VideoImage(tk, w, h)
147{
148        image_ = XCreateImage(dpy_, Tk_Visual(tk), Tk_Depth(tk),
149                              ZPixmap, 0, (char*)0, w, h, 8, 0);
150}
151
152StandardVideoImage::~StandardVideoImage()
153{
154        /*XXX*/
155#ifndef WIN32
156        XSync(dpy_, 0);
157#endif
158        image_->data = 0;
159        image_->obdata = 0;
160        XDestroyImage(image_);
161}
162
163SlowVideoImage::SlowVideoImage(Tk_Window tk, int w, int h)
164        : StandardVideoImage(tk, w, h)
165{
166        int size = w * h;
167        if (bpp_ > 8)
168                size *= bpp_ / 8;
169        image_->data = new char[size];
170}
171
172SlowVideoImage::~SlowVideoImage()
173{
174        delete image_->data;
175}
176
177void SlowVideoImage::putimage(Display* dpy, Window window, GC gc,
178                              int sx, int sy, int x, int y,
179                              int w, int h) const
180{
181        XPutImage(dpy, window, gc, image_, sx, sy, x, y, w, h);
182}
183
184#ifdef USE_SHM
185SharedVideoImage::SharedVideoImage(Tk_Window tk, int w, int h)
186        : StandardVideoImage(tk, w, h)
187{
188        int size = w * h;
189        if (bpp_ > 8)
190                size *= bpp_ / 8;
191
192        shminfo_.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
193        if (shminfo_.shmid < 0) {
194                perror("vic: shmget");
195                fprintf(stderr, "\
196vic: reverting to non-shared memory; you should reconfigure your system\n\
197vic: with more a higher limit on shared memory segments.\n\
198vic: refer to the README that accompanies the vic distribution\n");
199                extern int use_shm;
200                use_shm = 0;
201                return;
202        }
203        shminfo_.shmaddr = (char*)shmat(shminfo_.shmid, 0, 0);
204        if (shminfo_.shmaddr == (char*)-1) {
205                perror("shmat");
206                exit(1);
207        }
208        init(tk);
209}
210
211/*
212 * side affect - shmid is detached from local addr space
213 */
214SharedVideoImage::SharedVideoImage(Tk_Window tk, int w, int h,
215                                   u_char* shmaddr, int shmid)
216        : StandardVideoImage(tk, w, h)
217{
218        shminfo_.shmid = shmid;
219        shminfo_.shmaddr = (char*)shmaddr;
220        init(tk);
221}
222
223SharedVideoImage::~SharedVideoImage()
224{
225        if (valid()) {
226                XShmDetach(dpy_, &shminfo_);
227                if (shmdt(shminfo_.shmaddr) < 0)
228                        perror("vic: shmdt");
229        }
230}
231
232void SharedVideoImage::init(Tk_Window tk)
233{
234/*XXX capture-windows need to be writeable */
235#ifdef notdef
236        shminfo_.readOnly = 1;
237#else
238        shminfo_.readOnly = 0;
239#endif
240        XShmAttach(dpy_, &shminfo_);
241        /*
242         * Once the X server has attached the shm segments,
243         * we rmid them so they will go away when we exit.
244         * The sync is to make the X server do the attach
245         * before we do the rmid.
246         */
247        XSync(dpy_, 0);
248        (void)shmctl(shminfo_.shmid, IPC_RMID, 0);
249
250        /*
251         * Wrap segment in an ximage
252         */
253        image_->obdata = (char*)&shminfo_;
254        image_->data = shminfo_.shmaddr;
255}
256
257void SharedVideoImage::putimage(Display* dpy, Window window, GC gc,
258                                int sx, int sy, int x, int y,
259                                int w, int h) const
260{
261        XShmPutImage(dpy, window, gc, image_, sx, sy, x, y, w, h, 0);
262}
263#endif
264
265BareWindow::BareWindow(const char* name, XVisualInfo* vinfo)
266        : TclObject(name), width_(0), height_(0)
267{
268        Tcl& tcl = Tcl::instance();
269        tk_ = Tk_CreateWindowFromPath(tcl.interp(), tcl.tkmain(),
270                                      (char*)name, 0);
271        if (tk_ == 0)
272                abort();
273        Tk_SetClass(tk_, "Vic");
274        Tk_CreateEventHandler(tk_, ExposureMask|StructureNotifyMask,
275                              handle, (ClientData)this);
276        dpy_ = Tk_Display(tk_);
277        if (vinfo != 0) {
278                /*XXX*/
279                Colormap cm = XCreateColormap(dpy_, Tk_WindowId(tcl.tkmain()),
280                                              vinfo->visual, AllocNone);
281                Tk_SetWindowVisual(tk_, vinfo->visual, vinfo->depth, cm);
282        }
283}
284
285BareWindow::~BareWindow()
286{
287        Tk_DeleteEventHandler(tk_, ExposureMask|StructureNotifyMask,
288                              handle, (ClientData)this);
289}
290
291int BareWindow::command(int argc, const char*const* argv)
292{
293        Tcl& tcl = Tcl::instance();
294        if (argc == 2) {
295                if (strcmp(argv[1], "width") == 0) {
296                        sprintf(tcl.buffer(), "%d", width_);
297                        tcl.result(tcl.buffer());
298                        return (TCL_OK);
299                }
300                if (strcmp(argv[1], "height") == 0) {
301                        sprintf(tcl.buffer(), "%d", height_);
302                        tcl.result(tcl.buffer());
303                        return (TCL_OK);
304                }
305        } else if (argc == 4) {
306                if (strcmp(argv[1], "resize") == 0) {
307                        setsize(atoi(argv[2]), atoi(argv[3]));
308                        return (TCL_OK);
309                }
310        }
311        return (TclObject::command(argc, argv));
312}
313
314void BareWindow::handle(ClientData cd, XEvent* ep)
315{
316        BareWindow* w = (BareWindow*)cd;
317       
318        switch (ep->type) {
319        case Expose:
320                if (ep->xexpose.count == 0)
321                        w->redraw();
322                break;
323
324        case DestroyNotify:
325                w->destroy();
326                break;
327
328#ifdef notyet
329        case ConfigureNotify:
330                if (w->width_ != ep->xconfigure.width ||
331                    w->height_ != ep->xconfigure.height)
332                        ;
333                break;
334#endif
335        }
336}
337
338void BareWindow::destroy()
339{
340}
341
342void BareWindow::setsize(int w, int h)
343{
344        width_ = w;
345        height_ = h;
346        Tk_GeometryRequest(tk_, w, h);
347}
348
349GC VideoWindow::gc_;
350
351VideoWindow::VideoWindow(const char* name, XVisualInfo* vinfo)
352        : BareWindow(name, vinfo),
353          vi_(0),
354          callback_pending_(0),
355          damage_(0),
356          voff_(0),
357          hoff_(0)
358{
359        if (gc_ == 0) {
360                /*XXX should use Vic.background */
361                XColor* c = Tk_GetColor(Tcl::instance().interp(), tk_,
362                                        Tk_GetUid("gray50"));
363                if (c == 0)
364                        abort();
365                XGCValues v;
366                v.background = c->pixel;
367                v.foreground = c->pixel;
368                const u_long mask = GCForeground|GCBackground;
369                gc_ = Tk_GetGC(tk_, mask, &v);
370        }
371}
372
373VideoWindow::~VideoWindow()
374{
375        if (callback_pending_)
376                Tk_CancelIdleCall(display, (ClientData)this);
377}
378
379int VideoWindow::command(int argc, const char*const* argv)
380{
381        if (argc == 2) {
382                if (strcmp(argv[1], "redraw") == 0) {
383                        redraw();
384                        return (TCL_OK);
385                }
386                if (strcmp(argv[1], "clear") == 0) {
387                        clear();
388                        return (TCL_OK);
389                }
390                if (strcmp(argv[1], "dim") == 0) {
391                        dim();
392                        return (TCL_OK);
393                }
394        } else if (argc == 3) {
395                if (strcmp(argv[1], "voff") == 0) {
396                        voff_ = atoi(argv[2]);
397                        return (TCL_OK);
398                }
399                if (strcmp(argv[1], "hoff") == 0) {
400                        hoff_ = atoi(argv[2]);
401                        return (TCL_OK);
402                }
403        }
404        return (BareWindow::command(argc, argv));
405}
406
407void VideoWindow::display(ClientData cd)
408{
409        VideoWindow* vw = (VideoWindow*)cd;
410        vw->callback_pending_ = 0;
411        int h = (vw->vi_ != 0) ? vw->vi_->height() : vw->height_;
412        vw->draw(0, h, 0, 0);
413}
414
415void VideoWindow::draw(int y0, int y1, int x0, int x1)
416{
417        if (callback_pending_) {
418                callback_pending_ = 0;
419                Tk_CancelIdleCall(display, (ClientData)this);
420        }
421        if (!Tk_IsMapped(tk_))
422                return;
423
424        Window window = Tk_WindowId(tk_);
425
426        if (vi_ == 0) {
427                XFillRectangle(dpy_, window, gc_, 0, 0, width_, height_);
428                return;
429        }
430        int hoff = (width_ - vi_->width()) >> 1;
431        int voff = (height_ - vi_->height()) >> 1;
432        hoff += hoff_;
433        voff += voff_;
434        /*XXX*/
435        if (damage_) {
436                damage_ = 0;
437                if (hoff > 0) {
438                        XFillRectangle(dpy_, window, gc_,
439                                       0, 0, hoff, height_ + 1);
440                        XFillRectangle(dpy_, window, gc_,
441                                       width_ - hoff, 0, hoff, height_ + 1);
442                }
443                if (voff > 0) {
444                        XFillRectangle(dpy_, window, gc_,
445                                       0, 0, width_, voff + 1);
446                        XFillRectangle(dpy_, window, gc_,
447                                       0, height_ - voff, width_, voff + 1);
448                }
449        }
450        int h = y1 - y0;
451        if (h == 0)
452                h = vi_->height();
453        else if (h > vi_->height())
454                h = vi_->height();
455        else if (h < 0)
456                return;
457        int w = x1 - x0;
458        if (w == 0)
459                w = vi_->width();
460        else if (w > vi_->width())
461                w = vi_->width();
462        else if (w < 0)
463                return;
464
465        vi_->putimage(dpy_, window, gc_, x0, y0, x0 + hoff, y0 + voff, w, h);
466}
467
468/*XXX*/
469void VideoWindow::redraw()
470{
471        damage_ = 1;
472        if (!callback_pending_) {
473                callback_pending_ = 1;
474                Tk_DoWhenIdle(display, (ClientData)this);
475        }
476}
477
478void VideoWindow::clear()
479{
480        if (!callback_pending_) {
481                callback_pending_ = 1;
482                Tk_DoWhenIdle(doclear, (ClientData)this);
483        }
484}
485
486int VideoWindow::bpp()
487{
488    // XXX
489    StandardVideoImage* vi = StandardVideoImage::allocate(tk_, 1, 1);
490    int bpp = vi->ximage()->bits_per_pixel;
491    delete vi;
492    return bpp;
493}
494
495void VideoWindow::dim()
496{
497        if (!callback_pending_) {
498                callback_pending_ = 1;
499                Tk_DoWhenIdle(dodim, (ClientData)this);
500        }
501}
502
503void VideoWindow::doclear(ClientData cd)
504{
505        VideoWindow* vw = (VideoWindow*)cd;
506        vw->callback_pending_ = 0;
507        if (Tk_IsMapped(vw->tk_)) {
508                Window window = Tk_WindowId(vw->tk_);
509                XFillRectangle(vw->dpy_, window, vw->gc_,
510                               0, 0, vw->width_, vw->height_);
511        }
512}
513
514void VideoWindow::dodim(ClientData cd)
515{
516        static Pixmap graypm; /*XXX*/
517        static GC graygc; /*XXX*/
518
519        VideoWindow* vw = (VideoWindow*)cd;
520        vw->callback_pending_ = 0;
521        if (Tk_IsMapped(vw->tk_)) {
522                Window window = Tk_WindowId(vw->tk_);
523                if (graypm == 0) {
524                        u_int32_t bm[32];
525                        for (int i = 0; i < 32; i += 2) {
526                                bm[i]   = 0x55555555;
527                                bm[i+1] = 0xaaaaaaaa;
528                        }
529                        graypm = XCreateBitmapFromData(vw->dpy_, window,
530                                                       (const char*)bm,
531                                                       32, 32);
532                        XColor* c = Tk_GetColor(Tcl::instance().interp(),
533                                                vw->tk_, Tk_GetUid("gray50"));
534                        if (c == 0)
535                                abort();
536                        XGCValues v;
537                        v.background = c->pixel;
538                        v.foreground = c->pixel;
539                        v.fill_style = FillStippled;
540                        v.stipple = graypm;
541                        graygc = Tk_GetGC(vw->tk_,
542                               GCForeground|GCBackground|GCFillStyle|GCStipple,
543                               &v);
544                }
545                XFillRectangle(vw->dpy_, window, graygc,
546                               0, 0, vw->width_, vw->height_);
547        }
548}
549
550void VideoWindow::render(const VideoImage* v, int miny, int maxy,
551                         int minx, int maxx)
552{
553        vi_ = v;
554        draw(miny, maxy, minx, maxx);
555}
556
557CaptureWindow::CaptureWindow(const char* name, XVisualInfo* vinfo)
558        : BareWindow(name, vinfo),
559          base_width_(0),
560          base_height_(0),
561          image_(0)
562{
563        gc_ = Tk_GetGC(tk_, 0, 0);
564}
565
566CaptureWindow::~CaptureWindow()
567{
568        Tk_FreeGC(dpy_, gc_);
569        delete image_;
570}
571
572void CaptureWindow::setsize(int w, int h)
573{
574        BareWindow::setsize(w, h);
575        delete image_;
576        image_ = StandardVideoImage::allocate(tk_, width_, height_);
577}
578
579void CaptureWindow::grab_image()
580{
581        XImage* image = image_->ximage();
582#ifdef USE_SHM
583        if (image->obdata != 0)
584                XShmGetImage(dpy_, Tk_WindowId(tk_), image,
585                             0, 0, AllPlanes);
586        else
587#endif
588                XGetSubImage(Tk_Display(tk_), Tk_WindowId(tk_),
589                             0, 0, image->width, image->height,
590                             AllPlanes, ZPixmap, image, 0, 0);
591}
592                       
593void CaptureWindow::capture(u_int8_t* frm)
594{
595        /*
596         * Xv requires that the window be unobscured in order
597         * to capture the video.  So we grab the server and
598         * raise the window.  This won't work if the window
599         * isn't mapped.  Also, we block signals while
600         * the server is grabbed.  Otherwise, the process
601         * could be stopped while the display is locked out.
602         */
603        if (Tk_IsMapped(tk_)) {
604                raise();
605                grab_image();
606                converter_->convert((u_int8_t*)image_->pixbuf(),
607                                    width_, height_, frm);
608        }
609}
Note: See TracBrowser for help on using the browser.