//////////////////////////////////////////////////////////// // 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.1.1.1 2002/01/25 22:14:58 kergoth 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 } }