www.pudn.com > flash.rar > displaylist.cpp


//////////////////////////////////////////////////////////// 
// Flash Plugin and Player 
// Copyright (C) 1998,1999 Olivier Debon 
//  
// This program is free software; you can redistribute it and/or 
// modify it under the terms of the GNU General Public License 
// as published by the Free Software Foundation; either version 2 
// of the License, or (at your option) any later version. 
//  
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License 
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
//  
/////////////////////////////////////////////////////////////// 
//  Author : Olivier Debon   
//   
 
#include "swf.h" 
 
#ifdef RCSID 
static char *rcsid = "$Id: displaylist.cc,v 1.4 1999/09/03 15:17:40 ode Exp $"; 
#endif 
 
#define PRINT 0 
 
void deleteButton(FlashMovie *movie, DisplayListEntry *e) 
{ 
    /* save the focus */ 
    if (movie->mouse_active == 0 && e->renderState == stateOver) { 
        movie->lost_over = (Button *)e->character; 
        movie->cur_focus = NULL; 
    } 
 
    if (e == movie->cur_focus) { 
        movie->cur_focus = NULL; 
    } 
} 
 
void addButton(FlashMovie *movie, DisplayListEntry *e) 
{ 
    if (movie->mouse_active == 0 &&  
        movie->cur_focus == NULL && 
        movie->lost_over == (Button *)e->character) { 
        /* restore the lost focus */ 
        e->renderState = stateOver; 
        e->oldState = stateOver; 
	((Button *)e->character)->updateButtonState(e); 
        movie->lost_over = NULL; 
        movie->cur_focus = e; 
    } 
} 
 
DisplayList::DisplayList(FlashMovie *movie) 
{ 
	list = NULL; 
        this->movie = movie; 
	bbox.reset(); 
	isSprite = 0; 
} 
 
DisplayList::~DisplayList() 
{ 
	clearList(); 
} 
 
void 
DisplayList::clearList() 
{ 
	DisplayListEntry *del, *e; 
 
	for(e = list; e;) 
	{ 
		updateBoundingBox(e); 
                if (e->character->isButton()) { 
                    deleteButton(movie,e); 
                } 
		del = e; 
		e = e->next; 
		delete del; 
	} 
	list = 0; 
} 
 
DisplayListEntry * 
DisplayList::getList() 
{ 
	return list; 
} 
 
static void bbox(Rect *rect, Matrix *m, long x1, long y1) 
{ 
    long x,y; 
 
    x = m->getX(x1,y1); 
    y = m->getY(x1,y1); 
    if (x < rect->xmin) rect->xmin = x; 
    if (x > rect->xmax) rect->xmax = x; 
    if (y < rect->ymin) rect->ymin = y; 
    if (y > rect->ymax) rect->ymax = y; 
} 
 
// Update bb to include boundary, optional reset of bb 
void transformBoundingBox(Rect *bb, Matrix *matrix, Rect *boundary, int reset) 
{ 
    if (reset) { 
        bb->reset(); 
    } 
     
    if (boundary->xmin != LONG_MAX) { 
        bbox(bb, matrix, boundary->xmin, boundary->ymin); 
        bbox(bb, matrix, boundary->xmax, boundary->ymin); 
        bbox(bb, matrix, boundary->xmin, boundary->ymax); 
        bbox(bb, matrix, boundary->xmax, boundary->ymax); 
    } 
} 
 
void 
DisplayList::placeObject(GraphicDevice *gd,Character *character, long depth, Matrix *matrix, Cxform *cxform, char *name) 
{ 
	DisplayListEntry *n,*e,*prev; 
 
	n = new DisplayListEntry; 
	if (n == NULL) return; 
 
	n->depth = depth; 
	n->matrix = matrix; 
	n->cxform = cxform; 
	n->character = character; 
	n->instanceName = name; 
	n->owner = this; 
 
#if 0 
        printf("Dl %lx: placeObject: depth=%d character=%d cxform=%p\n", 
               this, n->depth,n->character ? n->character->getTagId() : 0, cxform); 
#endif 
 
	if (character == 0 || matrix == 0 || cxform == 0) { 
		for (e = list; e; prev = e, e = e->next) { 
			if (e->depth == n->depth) { 
				if (character == 0) { 
					n->character = e->character; 
				} 
				if (matrix == 0) { 
					n->matrix = e->matrix; 
				} 
				if (cxform == 0) { 
					n->cxform = e->cxform; 
				} 
				break; 
			} 
		} 
	} 
 
	if (n->character == 0) { 
		// Not found !!!    Should not happen 
            //		printf("PlaceObject cannot find character at depth %ld\n", n->depth); 
		delete n; 
		return; 
	} 
 
	prev = 0; 
	for (e = list; e; prev = e, e = e->next) 
	{ 
		if (e->depth == n->depth) { 
                        if (e->character->isButton()) { 
                            deleteButton(movie, e); 
                        } 
 
			// Do update, object has moved or been resized 
		        updateBoundingBox(e); 
 
			// Replace object 
                        e->depth = n->depth; 
                        e->matrix = n->matrix; 
                        e->cxform = n->cxform; 
                        e->character = n->character; 
                        /* if it is a button, we must update its state */ 
                        if (e->character->isButton()) { 
                            movie->buttons_updated = 1; 
                            addButton(movie, e); 
                        } 
 
		        updateBoundingBox(e); 
 
                        delete n; 
                        return; 
		} 
		if (e->depth > n->depth) break; 
	} 
        /* new object */ 
 
        /* button instantiation */ 
        if (n->character->isButton()) { 
            n->renderState = stateUp; 
            n->oldState = stateUp; 
	    ((Button *)n->character)->updateButtonState(n); 
            addButton(movie,n); 
        } 
 
	updateBoundingBox(n); 
 
	if (prev == 0) { 
		// Object comes at first place 
		n->next = list; 
		list = n; 
	} else { 
		// Insert object 
		n->next = prev->next; 
		prev->next = n; 
	} 
} 
 
 
Character * 
DisplayList::removeObject(GraphicDevice *gd,Character *character, long depth) 
{ 
    DisplayListEntry *e,*prev; 
     
    // List should not be empty 
    if (list == 0) return 0; 
     
#if 0 
    printf("removeObject: depth=%d character=%d\n", 
           depth,character ? character->getTagId() : 0); 
#endif 
     
    prev = 0; 
    for (e = list; e; prev = e, e = e->next) { 
        if (e->depth == depth) { 
            if (prev) { 
                prev->next = e->next; 
            } else { 
                list = e->next; 
            } 
            if (character == 0) { 
                character = e->character; 
            } 
            if (e->character->isButton()) { 
                deleteButton(movie, e); 
            } 
            if (e->character->isSprite()) { 
	    	((Sprite*)e->character)->reset(); 
	    } 
                 
	    updateBoundingBox(e); 
 
            delete e; 
            return character; 
        } 
    } 
    return 0;	// Should not happen 
} 
 
void 
DisplayList::updateBoundingBox(DisplayListEntry *e) 
{ 
	Rect	 rect; 
 
	//rect.reset(); 
	e->character->getBoundingBox(&rect,e); 
	transformBoundingBox(&this->bbox, e->matrix, &rect, 0); 
} 
 
int 
DisplayList::updateSprites() 
{ 
    Sprite *sprite; 
    DisplayListEntry *e; 
    int refresh = 0; 
 
    for (e = this->list; e != NULL; e = e->next) { 
        if (e->character->isButton() && e->buttonCharacter) { 
		if (e->buttonCharacter->isSprite()) { 
			Matrix mat; 
 
			sprite = (Sprite *)e->buttonCharacter; 
			refresh |= sprite->program->dl->updateSprites(); 
			refresh |= sprite->program->nestedMovie(this->movie->gd,this->movie->sm, e->matrix, e->cxform); 
			mat = (*e->matrix) * e->buttonMatrix; 
			transformBoundingBox(&this->bbox, &mat, 
					&(sprite->program->dl->bbox), 
					0); 
		} 
	} 
        if (e->character->isSprite()) { 
		sprite = (Sprite *)e->character; 
		refresh |= sprite->program->dl->updateSprites(); 
		refresh |= sprite->program->nestedMovie(this->movie->gd,this->movie->sm, e->matrix, e->cxform); 
		transformBoundingBox(&this->bbox, e->matrix, 
				&(sprite->program->dl->bbox), 
				0); 
        } 
    } 
    return refresh; 
} 
 
/* Function can return either 0,1 or 2 
   0:  Nothing match, continue 
   1:  Something matches, but continue searching 
   2:  Something matches, but stop searching 
*/ 
 
static int exploreButtons1(Program *prg, void *opaque,  
                           ExploreButtonFunc func) 
{ 
    DisplayListEntry *e; 
    int ret, ret2 = 0; 
 
    for(e=prg->dl->list; e != NULL; e = e->next) { 
	if (e->character == NULL) continue; 
        if (e->character->isButton()) { 
            ret = func(opaque,prg,e); 
	    if (ret == 2) return ret;	// Func asks to return at once !!! 
            if (ret) ret2 = 1; 
        } 
        if (e->character->isSprite()) { 
            ret = exploreButtons1(((Sprite *)e->character)->program, 
                                  opaque,func); 
	    if (ret == 2) return ret;	// Func asks to return at once !!! 
            if (ret) ret2 = 1; 
        } 
    } 
    return ret2; 
} 
 
int exploreButtons(FlashMovie *movie, void *opaque, ExploreButtonFunc func) 
{ 
    CInputScript *script; 
    int ret; 
 
    script = movie->main; 
    while (script != NULL) { 
	if (script->program) { 
		ret = exploreButtons1(script->program, opaque, func); 
		if (ret) return ret; 
	} 
        script = script->next; 
    } 
    return 0; 
} 
 
typedef struct { 
    long x,y; 
    int hit; 
    DisplayListEntry *bhit; 
} HitTable; 
 
static void button_hit_func(void *id, long y, long start, long end) 
{ 
    HitTable *h = (HitTable *) id; 
    if ( y == h->y && (h->x >= start && h->x < end) ) 
        h->hit = 1; 
} 
 
typedef struct { 
    FlashMovie *movie; 
    DisplayListEntry *bhit; 
} ButtonHit; 
 
static int button_hit(void *opaque, Program *prg, DisplayListEntry *e) 
{ 
    ButtonHit *h = (ButtonHit *) opaque; 
    HitTable hit_table; 
    FlashMovie *movie = h->movie; 
    Rect bb,boundary; 
    Matrix mat; 
    ButtonState save; 
 
    hit_table.x = movie->mouse_x; 
    hit_table.y = movie->mouse_y / FRAC; 
    hit_table.hit = 0; 
     
    // Compute the bounding box in screen coordinates 
    save = e->renderState; 
    e->renderState = stateHitTest; 
    e->character->getBoundingBox(&boundary,e); 
    e->renderState = save; 
    mat = (*movie->gd->adjust) * e->renderMatrix; 
    transformBoundingBox(&bb, &mat, &boundary, 1); 
    // Check if mouse is within bb 
    if (movie->mouse_x < bb.xmin) return 0; 
    if (movie->mouse_x > bb.xmax) return 0; 
    if (movie->mouse_y < bb.ymin) return 0; 
    if (movie->mouse_y > bb.ymax) return 0; 
 
    e->character->getRegion(movie->gd, &e->renderMatrix,  
                            &hit_table, button_hit_func); 
                 
    if (hit_table.hit) { 
        h->bhit = e; 
        return 1; 
    } else { 
        return 0; 
    } 
} 
 
static int button_reset(void *opaque, Program *prg, DisplayListEntry *e) 
{ 
    if (e->renderState != stateUp) { 
	    e->owner->updateBoundingBox(e); 
	    e->oldState = e->renderState; 
	    e->renderState = stateUp; 
	    ((Button *)e->character)->updateButtonState(e); 
	    e->owner->updateBoundingBox(e); 
    } 
    return 0; 
} 
 
/* update the button states according to the current mouse state & return the list of actions */ 
void 
DisplayList::updateButtons(FlashMovie *movie) 
{ 
    DisplayListEntry *bhit; 
    ButtonHit h; 
 
    if (movie->mouse_active) { 
 
        h.bhit = NULL; 
        h.movie = movie; 
 
        exploreButtons(movie, &h, button_hit); 
 
        bhit = h.bhit; 
 
        /* set every button to not hit */ 
        exploreButtons(movie, NULL, button_reset); 
 
        if (bhit) { 
	    ButtonState state; 
 
            if (movie->button_pressed) { 
                state = stateDown; 
            } else { 
                state = stateOver; 
            } 
	    if (state != bhit->renderState) { 
		    bhit->owner->updateBoundingBox(bhit); 
		    bhit->renderState = state; 
		    ((Button *)bhit->character)->updateButtonState(bhit); 
		    bhit->owner->updateBoundingBox(bhit); 
		    movie->cur_focus = bhit; 
		    if (movie->cursorOnOff) 
			    movie->cursorOnOff(1,movie->cursorOnOffClientData); 
	    } 
        } else { 
	    if (movie->cursorOnOff) 
		    movie->cursorOnOff(0,movie->cursorOnOffClientData); 
	} 
    } 
} 
 
typedef struct { 
    ActionRecord *action;	// Action to do 
    Program	 *prg;		// Context program 
} ButtonAction; 
 
static int button_action(void *opaque, Program *prg, DisplayListEntry *e) 
{ 
    ButtonAction *h = (ButtonAction *)opaque; 
    static ActionRecord actionRefresh; 
    static ActionRecord soundFx; 
    Button *b; 
    ActionRecord **paction; 
    int n; 
 
    actionRefresh.action = ActionRefresh; 
    actionRefresh.next = 0; 
     
    soundFx.action = ActionPlaySound; 
    soundFx.next = &actionRefresh; 
 
    b = (Button *)e->character; 
 
    if (e->oldState != e->renderState) { 
         
        paction = &actionRefresh.next; 
         
        if (b->conditionList) { 
            *paction = b->getActionFromTransition(e->renderState, e->oldState); 
        } else if (e->renderState == stateDown) { 
            /* if the button is pressed and  
               no condition list is defined*/ 
            *paction = b->actionRecords; 
        } 
         
        switch(e->renderState) { 
        case stateUp: 
            n = 0; 
            break; 
        case stateOver: 
            n = 1; 
            break; 
        default: 
            /* case stateDown: */ 
            n = 2; 
            break; 
        } 
         
        if (b->sound[n]) { 
            soundFx.sound = b->sound[n]; 
            h->action = &soundFx; 
        } else { 
            h->action = &actionRefresh; 
        } 
         
        e->oldState = e->renderState; 
 
        h->prg = prg; 
        return 2; 
    } 
    h->action = 0;	// Nothing to do about this 
    return 0; 
} 
 
int computeActions(FlashMovie *movie, Program **prg, ActionRecord **ar) 
{ 
    ButtonAction h; 
 
    h.action = NULL; 
    exploreButtons(movie, &h, button_action); 
    if (h.action) { 
    	*prg = h.prg; 
	*ar = h.action; 
	return 1; 
    } 
    return 0; 
} 
 
#define FOCUS_ZOOM       1.5 
/* in pixels */ 
#define FOCUS_SIZE_MIN   50 
#define FOCUS_TRANSLATE  15 
 
int 
DisplayList::render(GraphicDevice *gd, Matrix *render_matrix, Cxform *cxform) 
{ 
	DisplayListEntry *e,*cur_focus; 
	int sprite = 0; 
	long n = 0; 
        Cxform cxf,*cxf1; 
	Rect bb,boundary; 
 
        cur_focus = NULL; 
 
	/* 
	if (isSprite == 0) { 
		if (this->bbox.xmin == LONG_MAX) return 0; 
		gd->updateClippingRegion(&this->bbox, render_matrix); 
		gd->clearCanvas(); 
	} 
	*/ 
 
	for (e = list; e; e = e->next) 
	{ 
#if PRINT 
		printf("Character %3d @ %3d\n", e->character ? e->character->getTagId() : 0, e->depth); 
#endif 
		if (e->character) { 
			Matrix mat; 
 
			if (render_matrix) { 
				mat = *render_matrix; 
			} 
 
			if (e->matrix) { 
				mat = mat * (*e->matrix); 
			} 
 
                        /* fast clipping */ 
			// If object boundaries are outside current clip region give up with rendering 
                        e->character->getBoundingBox(&boundary,e); 
                        if (boundary.xmin != LONG_MAX) { 
                            Matrix tmat; 
 
                            tmat = (*gd->adjust) * mat; 
                            transformBoundingBox(&bb, &tmat, &boundary, 1); 
 
                            bb.xmin = bb.xmin >> FRAC_BITS; 
                            bb.ymin = bb.ymin >> FRAC_BITS; 
                            bb.xmax = (bb.xmax + FRAC - 1) >> FRAC_BITS; 
                            bb.ymax = (bb.ymax + FRAC - 1) >> FRAC_BITS; 
 
                            if (bb.xmin >= gd->clip_rect.xmax || 
                                bb.xmax <= gd->clip_rect.xmin || 
                                bb.ymin >= gd->clip_rect.ymax || 
                                bb.ymax <= gd->clip_rect.ymin) { 
                                continue; 
                            } 
                        } 
 
                        if (cxform == NULL) { 
                            cxf1 = e->cxform; 
                        } 
			else if (e->cxform == NULL) { 
                            cxf1 = cxform; 
                        } 
			else { 
                            cxf1 = &cxf; 
                            cxf.ra = cxform->ra * e->cxform->ra; 
                            cxf.ga = cxform->ga * e->cxform->ga; 
                            cxf.ba = cxform->ba * e->cxform->ba; 
                            cxf.aa = cxform->aa * e->cxform->aa; 
                             
                            cxf.rb = (long)(cxform->ra * e->cxform->rb + cxform->rb); 
                            cxf.gb = (long)(cxform->ga * e->cxform->gb + cxform->gb); 
                            cxf.bb = (long)(cxform->ba * e->cxform->bb + cxform->bb); 
                            cxf.ab = (long)(cxform->aa * e->cxform->ab + cxform->ab); 
                        } 
 
                        if (e->character->isButton()) { 
                            Button *b = (Button *) e->character; 
 
                            e->renderMatrix = mat; 
 
                            if (e->renderState != stateUp && movie->mouse_active == 0) { 
                                cur_focus = e; 
				((Button *)e->character)->updateButtonState(e); 
                            } 
 
                            if (b->execute(gd, &mat, cxf1, e->renderState)) { 
				sprite = 1; 
                            } 
                        } else { 
                            if (e->character->execute(gd, &mat, cxf1)) { 
				sprite = 1; 
                            } 
                        } 
 
			n++; 
		} 
	} 
 
#if 0 
    { 
	/* display the bounding box (debug) */ 
	Matrix tmat; 
	long x1,x2,y1,y2; 
	Color white; 
 
	white.red = 255; 
	white.green = white.blue = 0; 
	gd->setForegroundColor(white); 
 
	if (render_matrix) { 
		tmat = (*gd->adjust) * (*render_matrix); 
	} else { 
		tmat = *gd->adjust; 
	} 
	x1 = bbox.xmin; 
	y1 = bbox.ymin; 
	x2 = bbox.xmax; 
	y2 = bbox.ymax; 
	gd->drawLine(tmat.getX(x1,y1),tmat.getY(x1,y1),tmat.getX(x2,y1),tmat.getY(x2,y1),10*FRAC); 
	gd->drawLine(tmat.getX(x2,y1),tmat.getY(x2,y1),tmat.getX(x2,y2),tmat.getY(x2,y2),10*FRAC); 
	gd->drawLine(tmat.getX(x2,y2),tmat.getY(x2,y2),tmat.getX(x1,y2),tmat.getY(x1,y2),10*FRAC); 
	gd->drawLine(tmat.getX(x1,y2),tmat.getY(x1,y2),tmat.getX(x1,y1),tmat.getY(x1,y1),10*FRAC); 
	bbox.print(); 
    } 
#endif 
         
	// Reset clipping zone 
        bbox.reset(); 
 
	return sprite; 
} 
 
void 
DisplayList::getBoundary(Rect *bb) 
{ 
	DisplayListEntry *e; 
	Rect boundary; 
 
	bb->reset(); 
	for (e = list; e; e = e->next) 
	{ 
		if (e->character) { 
			e->character->getBoundingBox(&boundary,e); 
			transformBoundingBox(bb, e->matrix, &boundary, 0); 
		} 
	} 
} 
 
extern "C" { 
 
void dump_buttons(FlashHandle flashHandle) 
{ 
#if 0 
    Rect rect; 
    DisplayListEntry *e; 
    FlashMovie *movie; 
 
    movie = (FlashMovie *)flashHandle; 
 
    for (e = movie->first_button; e; e = e->next_button) { 
        computeBBox(movie,&rect,e); 
        printf("button: id=%d pos=%d %d %d %d\n", 
               e->character->getTagId(), 
               rect.xmin, rect.ymin, rect.xmax, rect.ymax); 
    } 
#endif 
} 
 
}