/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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 3 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, see . * */ #include "common/debug.h" #include "common/file.h" #include "backends/imgui/IconsMaterialSymbols.h" #include "qdengine/qdengine.h" #include "qdengine/qd_fwd.h" #include "qdengine/system/graphics/gr_dispatcher.h" #include "qdengine/system/graphics/gr_tile_animation.h" #include "qdengine/parser/qdscr_parser.h" #include "qdengine/parser/xml_tag_buffer.h" #include "qdengine/qdcore/qd_animation_info.h" #include "qdengine/qdcore/qd_named_object_reference.h" #include "qdengine/qdcore/qd_game_dispatcher.h" #include "qdengine/qdcore/qd_animation.h" #include "qdengine/qdcore/qd_file_manager.h" namespace QDEngine { qdAnimation::qdAnimation() : _parent(NULL) { _tileAnimation = 0; _num_frames = 0; _length = _cur_time = 0.0f; _status = QD_ANIMATION_STOPPED; _sx = _sy = 0; _is_finished = false; _playback_speed = 1.0f; _frames_ptr = &_frames; _scaled_frames_ptr = &_scaled_frames; } qdAnimation::qdAnimation(const qdAnimation &anm) : qdNamedObject(anm), qdResource(anm), _parent(anm._parent), _length(anm._length), _cur_time(anm._cur_time), _status(anm._status), _is_finished(anm._is_finished), _sx(anm._sx), _sy(anm._sy), _num_frames(anm._num_frames), _playback_speed(1.0f), _tileAnimation(0) { copy_frames(anm); if (anm._tileAnimation) _tileAnimation = new grTileAnimation(*anm._tileAnimation); } qdAnimation::~qdAnimation() { clear_frames(); _qda_file.clear(); delete _tileAnimation; } qdAnimation &qdAnimation::operator = (const qdAnimation &anm) { if (this == &anm) return *this; *static_cast(this) = anm; *static_cast(this) = anm; _parent = anm._parent; _length = anm._length; _cur_time = anm._cur_time; _status = QD_ANIMATION_STOPPED; _is_finished = false; _playback_speed = anm._playback_speed; _sx = anm._sx; _sy = anm._sy; copy_frames(anm); _num_frames = anm._num_frames; delete _tileAnimation; _tileAnimation = 0; if (anm._tileAnimation) _tileAnimation = new grTileAnimation(*anm._tileAnimation); return *this; } void qdAnimation::quant(float dt) { if (_status == QD_ANIMATION_PLAYING) { if (need_stop()) { stop(); return; } _cur_time += dt * _playback_speed; if (_cur_time >= length()) { if (length() > 0.01f) { if (!check_flag(QD_ANIMATION_FLAG_LOOP)) { _cur_time = length() - 0.01f; _status = QD_ANIMATION_END_PLAYING; } else { _cur_time = fmodf(_cur_time, length()); } } else _cur_time = 0.0f; _is_finished = true; } } } void qdAnimation::redraw(int x, int y, int z, int mode) const { debugC(2, kDebugGraphics, "qdAnimation::redraw([%d, %d, %d], mode: %d), name: '%s'", x, y, z, mode, transCyrillic(_parent ? _parent->name() : name())); if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (check_flag(QD_ANIMATION_FLAG_BLACK_FON)) mode |= GR_BLACK_FON; if (tileAnimation()) { tileAnimation()->drawFrame(Vect2i(x, y), get_cur_frame_number(), mode, -1); } else if (const qdAnimationFrame *p = get_cur_frame()) p->redraw(x, y, z, mode); } void qdAnimation::redraw(int x, int y, int z, float scale, int mode) const { debugC(2, kDebugGraphics, "qdAnimation::redraw([%d, %d, %d], scale: %f, mode: %d), name: '%s'", x, y, z, scale, mode, transCyrillic(_parent ? _parent->name() : name())); if (fabs(scale - 1.0f) < 0.01f) { redraw(x, y, z, mode); return; } if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (check_flag(QD_ANIMATION_FLAG_BLACK_FON)) mode |= GR_BLACK_FON; if (tileAnimation()) { tileAnimation()->drawFrame_scale(Vect2i(x, y), get_cur_frame_number(), scale, mode); } else { const qdAnimationFrame *scaled_frame; int scale_index = get_scale_index(scale); if (scale_index == -1) scaled_frame = get_cur_frame(); else scaled_frame = get_scaled_frame(get_cur_frame_number(), scale_index); if (scaled_frame) scaled_frame->redraw(x, y, z, scale, mode); } } void qdAnimation::redraw_rot(int x, int y, int z, float angle, int mode) const { debugC(2, kDebugGraphics, "qdAnimation::redraw_rot([%d, %d, %d], angle: %f, mode: %d), name: '%s'", x, y, z, angle, mode, transCyrillic(_parent ? _parent->name() : name())); if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (tileAnimation()) { tileAnimation()->drawFrame(Vect2i(x, y), get_cur_frame_number(), angle, mode); } else if (const qdAnimationFrame *p = get_cur_frame()) p->redraw_rot(x, y, z, angle, mode); } void qdAnimation::redraw_rot(int x, int y, int z, float angle, const Vect2f &scale, int mode) const { debugC(2, kDebugGraphics, "qdAnimation::redraw_rot([%d, %d, %d], angle: %f, scale: [%f, %f], mode: %d), name: '%s'", x, y, z, angle, scale.x, scale.y, mode, transCyrillic(_parent ? _parent->name() : name())); if (fabs(scale.x - 1.0f) < 0.01f && fabs(scale.y - 1.0f) < 0.01f) { redraw_rot(x, y, z, angle, mode); return; } if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (tileAnimation()) { tileAnimation()->drawFrame(Vect2i(x, y), get_cur_frame_number(), angle, scale, mode); } else if (fabs(scale.x - scale.y) >= 0.01f) { if (const qdAnimationFrame *p = get_cur_frame()) p->redraw_rot(x, y, z, angle, scale, mode); } else { const qdAnimationFrame *scaled_frame; float newScale = scale.x; int scale_index = get_scale_index(newScale); if (scale_index == -1) scaled_frame = get_cur_frame(); else scaled_frame = get_scaled_frame(get_cur_frame_number(), scale_index); if (scaled_frame) { if (fabs(newScale - 1.0) >= 0.01f) scaled_frame->redraw_rot(x, y, z, angle, Vect2f(newScale, newScale), mode); else scaled_frame->redraw_rot(x, y, z, angle, mode); } } } void qdAnimation::draw_mask(int x, int y, int z, uint32 mask_color, int mask_alpha, int mode) const { if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (check_flag(QD_ANIMATION_FLAG_BLACK_FON)) mode |= GR_BLACK_FON; if (tileAnimation()) tileAnimation()->drawMask(Vect2i(x, y), get_cur_frame_number(), mask_color, mask_alpha, mode, -1); else if (const qdAnimationFrame *p = get_cur_frame()) p->draw_mask(x, y, z, mask_color, mask_alpha, mode); } void qdAnimation::draw_mask(int x, int y, int z, uint32 mask_color, int mask_alpha, float scale, int mode) const { if (fabs(scale - 1.0f) < 0.01f) { draw_mask(x, y, z, mask_color, mask_alpha, mode); return; } if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (check_flag(QD_ANIMATION_FLAG_BLACK_FON)) mode |= GR_BLACK_FON; if (tileAnimation()) { tileAnimation()->drawMask_scale(Vect2i(x, y), get_cur_frame_number(), mask_color, mask_alpha, scale, mode); } else { int scale_index = get_scale_index(scale); const qdAnimationFrame *scaled_frame; if (scale_index == -1) scaled_frame = get_cur_frame(); else scaled_frame = get_scaled_frame(get_cur_frame_number(), scale_index); if (scaled_frame) scaled_frame->draw_mask(x, y, z, mask_color, mask_alpha, scale, mode); } } void qdAnimation::draw_mask_rot(int x, int y, int z, float angle, uint32 mask_color, int mask_alpha, int mode) const { if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (tileAnimation()) { tileAnimation()->drawMask_rot(Vect2i(x, y), get_cur_frame_number(), mask_color, mask_alpha, angle, mode); } else if (const qdAnimationFrame *p = get_cur_frame()) p->draw_mask_rot(x, y, z, angle, mask_color, mask_alpha, mode); } void qdAnimation::draw_mask_rot(int x, int y, int z, float angle, uint32 mask_color, int mask_alpha, const Vect2f &scale, int mode) const { if (fabs(scale.x - 1.0f) < 0.01f && fabs(scale.y - 1.0f) < 0.01f) { draw_mask_rot(x, y, z, angle, mask_color, mask_alpha, mode); return; } if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (tileAnimation()) { tileAnimation()->drawMask_rot(Vect2i(x, y), get_cur_frame_number(), mask_color, mask_alpha, angle, scale, mode); } else if (fabs(scale.x - scale.y) >= 0.01f) { if (const qdAnimationFrame *p = get_cur_frame()) p->draw_mask_rot(x, y, z, angle, mask_color, mask_alpha, scale, mode); } else { const qdAnimationFrame *scaled_frame; float newScale = scale.x; int scale_index = get_scale_index(newScale); if (scale_index == -1) { scaled_frame = get_cur_frame(); } else { scaled_frame = get_scaled_frame(get_cur_frame_number(), scale_index); } if (scaled_frame) { if (fabs(newScale - 1.0) >= 0.01) { scaled_frame->draw_mask_rot(x, y, z, angle, mask_color, mask_alpha, Vect2f(newScale, newScale), mode); } else { scaled_frame->draw_mask_rot(x, y, z, angle, mask_color, mask_alpha, mode); } } } } void qdAnimation::draw_contour(int x, int y, uint32 color) const { int mode = 0; if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (check_flag(QD_ANIMATION_FLAG_BLACK_FON)) mode |= GR_BLACK_FON; if (tileAnimation()) { tileAnimation()->drawContour(Vect2i(x, y), get_cur_frame_number(), color, mode, -1); } else { const qdAnimationFrame *p = get_cur_frame(); if (p) p->draw_contour(x, y, color, mode); } } void qdAnimation::draw_contour(int x, int y, uint32 color, float scale) const { int mode = 0; if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; if (check_flag(QD_ANIMATION_FLAG_BLACK_FON)) mode |= GR_BLACK_FON; if (tileAnimation()) { if (fabs(scale - 1.0) >= 0.01f) tileAnimation()->drawContour(Vect2i(x, y), get_cur_frame_number(), color, scale, mode); else tileAnimation()->drawContour(Vect2i(x, y), get_cur_frame_number(), color, mode, -1); } else { const qdAnimationFrame *p = get_cur_frame(); if (p) p->draw_contour(x, y, color, scale, mode); } } qdAnimationFrame *qdAnimation::get_cur_frame() { for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf) { if ((*iaf)->end_time() >= cur_time()) return *iaf; } return NULL; } const qdAnimationFrame *qdAnimation::get_cur_frame() const { for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf) { if ((*iaf)->end_time() >= cur_time()) return *iaf; } return NULL; } const qdAnimationFrame *qdAnimation::get_cur_frame(float &scale) const { int index = get_scale_index(scale); if (index == -1) return get_cur_frame(); return get_scaled_frame(get_cur_frame_number(), index); } bool qdAnimation::add_frame(qdAnimationFrame *p, qdAnimationFrame *insert_pos, bool insert_after) { debugC(1, kDebugTemp, "qdAnimation::add_frame()"); if (check_flag(QD_ANIMATION_FLAG_REFERENCE)) return false; if (insert_pos) { for (auto iaf = _frames.begin(); iaf != _frames.end(); iaf++) { if (*iaf == insert_pos) { if (insert_after) ++iaf; _frames.insert(iaf, p); _num_frames = _frames.size(); return true; } } } else { if (insert_after) _frames.push_back(p); else _frames.insert(_frames.end(), p); debugC(1, kDebugTemp, "qdAnimation::add_frame(): inserted, is_empty: %d", is_empty()); return true; } return false; } void qdAnimation::init_size() { _length = 0.0f; if (!tileAnimation()) { _sx = _sy = 0; for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf) { qdAnimationFrame *p = *iaf; p->set_start_time(_length); if (p->size_x() > _sx) _sx = p->size_x(); if (p->size_y() > _sy) _sy = p->size_y(); _length += p->length(); } } else { for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf) { qdAnimationFrame *p = *iaf; p->set_start_time(_length); p->set_size(tileAnimation()->frameSize()); p->set_picture_offset(Vect2i(0, 0)); p->set_picture_size(tileAnimation()->frameSize()); _length += p->length(); } } if (_cur_time >= _length) _cur_time = _length - 0.01f; _num_frames = _frames_ptr->size(); } void qdAnimation::load_script(const xml::tag *p) { for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) { switch (it->ID()) { case QDSCR_NAME: set_name(it->data()); break; case QDSCR_ANIMATION_FILE: qda_set_file(Common::Path(it->data(), '\\')); break; case QDSCR_FLAG: set_flag(xml::tag_buffer(*it).get_int()); break; } } init_size(); } bool qdAnimation::save_script(Common::WriteStream &fh, int indent) const { for (int i = 0; i < indent; i++) { fh.writeString("\t"); } fh.writeString("\r\n"); return true; } bool qdAnimation::load_resources() { debugC(3, kDebugLoad, "qdAnimation::load_resources(): '%s' name: %s", transCyrillic(qda_file().toString()), transCyrillic(name())); if (check_flag(QD_ANIMATION_FLAG_REFERENCE)) return false; if (qda_file().empty()) { qdAnimationFrameList::iterator iaf; for (iaf = _frames.begin(); iaf != _frames.end(); ++iaf) { (*iaf)->load_resources(); } init_size(); return true; } else return qda_load(qda_file()); return false; } void qdAnimation::free_resources() { toggle_resource_status(false); if (check_flag(QD_ANIMATION_FLAG_REFERENCE)) return; for (qdAnimationFrameList::iterator iaf = _frames.begin(); iaf != _frames.end(); ++iaf) (*iaf)->free_resources(); for (qdAnimationFrameList::iterator iaf = _scaled_frames.begin(); iaf != _scaled_frames.end(); ++iaf) (*iaf)->free_resources(); } void qdAnimation::create_reference(qdAnimation *p, const qdAnimationInfo *inf) const { p->_frames_ptr = &_frames; p->_scaled_frames_ptr = &_scaled_frames; p->clear_flags(); p->set_flag(flags() | QD_ANIMATION_FLAG_REFERENCE); p->_length = _length; p->_cur_time = 0.0f; p->_sx = _sx; p->_sy = _sy; p->_num_frames = _num_frames; debugC(1, kDebugTemp, "num_frames_: %d empty?: %d, is_empty()?: %d", _num_frames, _frames.empty(), is_empty()); if (inf) { if (inf->check_flag(QD_ANIMATION_FLAG_LOOP)) p->set_flag(QD_ANIMATION_FLAG_LOOP); if (inf->check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) p->set_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL); if (inf->check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) p->set_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL); p->_playback_speed = inf->animation_speed(); } p->_parent = this; } bool qdAnimation::hit(int x, int y) const { int xx = x; int yy = y; if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) xx = -x; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) yy = -y; if (tileAnimation()) { Vect2i pos(xx, yy); return tileAnimation()->hit(get_cur_frame_number(), pos); } else { const qdAnimationFrame *p = get_cur_frame(); if (p) return p->hit(xx, yy); } return false; } bool qdAnimation::hit(int x, int y, float scale) const { int xx = x; int yy = y; if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) xx = -x; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) yy = -y; if (tileAnimation()) { Vect2i pos(xx, yy); return tileAnimation()->hit(get_cur_frame_number(), pos); // Weirdly, but there is no _scale variant } else { const qdAnimationFrame *p = get_cur_frame(); if (p) return p->hit(xx, yy, scale); } return false; } bool qdAnimation::qda_load(const Common::Path &fpath) { clear_frames(); debugC(3, kDebugLoad, "qdAnimation::qda_load(%s)", transCyrillic(fpath.toString())); Common::SeekableReadStream *fh; if (!qdFileManager::instance().open_file(&fh, fpath.toString().c_str())) { return false; } int32 version = fh->readSint32LE(); _sx = fh->readSint32LE(); _sy = fh->readSint32LE(); _length = fh->readFloatLE(); int32 fl = fh->readSint32LE(); int32 num_fr = fh->readSint32LE(); debugC(3, kDebugLoad, "qdAnimation::qda_load(): vers: %d sx: %d x %d len: %f fl: %d num_fr: %d", version, _sx, _sy, _length, fl, num_fr); int num_scales = 0; if (version >= 103) { num_scales = fh->readSint32LE(); } char tile_flag = 0; if (version >= 104) { tile_flag = fh->readByte(); } if (!tile_flag) { if (num_scales) { _scales.resize(num_scales); for (int i = 0; i < num_scales; i++) _scales[i] = fh->readFloatLE(); } else _scales.clear(); set_flag(fl & (QD_ANIMATION_FLAG_CROP | QD_ANIMATION_FLAG_COMPRESS)); for (int i = 0; i < num_fr; i++) { qdAnimationFrame *p = new qdAnimationFrame; p->qda_load(fh, version); add_frame(p); } for (int i = 0; i < num_fr * num_scales; i++) { qdAnimationFrame *p = new qdAnimationFrame; p->qda_load(fh, version); _scaled_frames.push_back(p); } } else { set_flag(fl); _sx = fh->readSint32LE(); _sy = fh->readSint32LE(); for (int i = 0; i < num_fr; i++) { float start_time, length; start_time = fh->readFloatLE(); length = fh->readFloatLE(); qdAnimationFrame *p = new qdAnimationFrame; p->set_start_time(start_time); p->set_length(length); add_frame(p); } debugC(1, kDebugLoad, "qdAnimation::qda_load() tileAnimation %s", transCyrillic(fpath.toString())); if (_tileAnimation) _tileAnimation->clear(); _tileAnimation = new grTileAnimation; _tileAnimation->load(fh, version); //_tileAnimation->dumpTiles(fpath, 50); } delete fh; init_size(); return true; } void qdAnimation::qda_set_file(const Common::Path &fname) { _qda_file = fname; } bool qdAnimation::crop() { for (qdAnimationFrameList::iterator it = _frames.begin(); it != _frames.end(); ++it) (*it)->crop(); for (qdAnimationFrameList::iterator it = _scaled_frames.begin(); it != _scaled_frames.end(); ++it) (*it)->crop(); return true; } bool qdAnimation::undo_crop() { for (qdAnimationFrameList::iterator it = _frames.begin(); it != _frames.end(); ++it) (*it)->undo_crop(); for (qdAnimationFrameList::iterator it = _scaled_frames.begin(); it != _scaled_frames.end(); ++it) (*it)->undo_crop(); return true; } bool qdAnimation::compress() { if (check_flag(QD_ANIMATION_FLAG_COMPRESS)) return false; bool result = true; for (qdAnimationFrameList::iterator it = _frames.begin(); it != _frames.end(); ++it) { if (!(*it)->compress()) result = false; } for (qdAnimationFrameList::iterator it = _scaled_frames.begin(); it != _scaled_frames.end(); ++it) { if (!(*it)->compress()) result = false; } set_flag(QD_ANIMATION_FLAG_COMPRESS); return result; } bool qdAnimation::tileCompress(grTileCompressionMethod method, int tolerance) { if (!_num_frames || check_flag(QD_ANIMATION_FLAG_TILE_COMPRESS)) return false; uncompress(); undo_crop(); grTileSprite::setComprasionTolerance(tolerance); _tileAnimation = new grTileAnimation; _tileAnimation->init(_num_frames, Vect2i(_sx, _sy), _frames.front()->check_flag(qdSprite::ALPHA_FLAG)); for (int i = 0; i < _num_frames; i++) _tileAnimation->addFrame((const uint32 *)get_frame(i)->data()); if (method != TILE_UNCOMPRESSED) _tileAnimation->compress(method); _tileAnimation->compact(); set_flag(QD_ANIMATION_FLAG_TILE_COMPRESS); return true; } bool qdAnimation::uncompress() { if (!check_flag(QD_ANIMATION_FLAG_COMPRESS) || check_flag(QD_ANIMATION_FLAG_TILE_COMPRESS)) return false; bool result = true; qdAnimationFrameList::iterator iaf; for (qdAnimationFrameList::iterator it = _frames.begin(); it != _frames.end(); ++it) { if (!(*it)->uncompress()) result = false; } for (qdAnimationFrameList::iterator it = _scaled_frames.begin(); it != _scaled_frames.end(); ++it) { if (!(*it)->uncompress()) result = false; } drop_flag(QD_ANIMATION_FLAG_COMPRESS); return result; } int qdAnimation::get_cur_frame_number() const { int num = 0; for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf) { if ((*iaf)->end_time() >= cur_time()) { return num; } num++; } return -1; } void qdAnimation::set_cur_frame(int number) { int num = 0; for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf) { if (num++ == number) { set_time((*iaf)->start_time() + (*iaf)->length() / 2.0f); return; } } } bool qdAnimation::remove_frame(int number) { int num = 0; for (qdAnimationFrameList::iterator iaf = _frames.begin(); iaf != _frames.end(); ++iaf) { if (num++ == number) { delete *iaf; _frames.erase(iaf); init_size(); return true; } } return false; } bool qdAnimation::remove_frame_range(int number0, int number1) { int num = 0; qdAnimationFrameList::iterator iaf, iaf0, iaf1; iaf0 = iaf1 = _frames.end(); for (iaf = _frames.begin(); iaf != _frames.end(); ++iaf) { if (num == number0) iaf0 = iaf; if (num == number1) { iaf1 = iaf; break; } num++; } if (iaf0 != _frames.end() && iaf1 != _frames.end()) { for (iaf = iaf0; iaf != iaf1; ++iaf) delete *iaf; _frames.erase(iaf0, iaf1); init_size(); return true; } return false; } bool qdAnimation::reverse_frame_range(int number0, int number1) { int num = 0; qdAnimationFrameList::iterator iaf0 = _frames.end(); qdAnimationFrameList::iterator iaf1 = _frames.end(); for (qdAnimationFrameList::iterator iaf = _frames.begin(); iaf != _frames.end(); ++iaf) { if (num == number0) iaf0 = iaf; if (num == number1) { iaf1 = iaf; break; } num++; } if (iaf0 != _frames.end() && iaf1 != _frames.end()) { iaf1++; Common::reverse(iaf0, iaf1); init_size(); return true; } return false; } qdAnimationFrame *qdAnimation::get_frame(int number) { int num = 0; for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf) { if (num == number) return *iaf; num++; } return 0; } bool qdAnimation::load_resource() { toggle_resource_status(); return load_resources(); } bool qdAnimation::free_resource() { toggle_resource_status(false); free_resources(); return true; } void qdAnimation::advance_time(float tm) { if (_length <= 0.01f) return; tm *= _playback_speed; if (_cur_time + tm >= length()) { if (check_flag(QD_ANIMATION_FLAG_LOOP)) { tm -= length() - _cur_time; while (tm >= length()) tm -= length(); _cur_time = tm; } else _cur_time = length() - 0.01f; } else _cur_time += tm; } int qdAnimation::picture_size_x() const { int i = 0; int sx = 0; for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf, i++) { sx += (*iaf)->picture_size_x(); } if (i) return sx / i; return 0; } int qdAnimation::picture_size_y() const { int i = 0; int sy = 0; for (qdAnimationFrameList::const_iterator iaf = _frames_ptr->begin(); iaf != _frames_ptr->end(); ++iaf, i++) { sy += (*iaf)->picture_size_y(); } if (i) return sy / i; return 0; } bool qdAnimation::scale(float coeff_x, float coeff_y) { bool res = true; qdAnimationFrameList::iterator iaf; for (iaf = _frames.begin(); iaf != _frames.end(); ++iaf) { if (!(*iaf)->scale(coeff_x, coeff_y)) res = false; } init_size(); return res; } Vect2i qdAnimation::remove_edges() { if (_frames.empty()) return Vect2i(0, 0); bool crop_flag = false; bool compress_flag = false; if (check_flag(QD_ANIMATION_FLAG_COMPRESS)) { uncompress(); compress_flag = true; } if (check_flag(QD_ANIMATION_FLAG_CROP)) { undo_crop(); crop_flag = true; } int left, top, right, bottom; if (!(*_frames.begin())->get_edges_width(left, top, right, bottom)) return Vect2i(0, 0); qdAnimationFrameList::iterator iaf; for (iaf = _frames.begin(); iaf != _frames.end(); ++iaf) { int l, t, r, b; if ((*iaf)->get_edges_width(l, t, r, b)) { if (l < left) left = l; if (t < top) top = t; if (r < right) right = r; if (b < bottom) bottom = b; } else return Vect2i(0, 0); } for (iaf = _frames.begin(); iaf != _frames.end(); ++iaf) { if (!(*iaf)->crop(left, top, right, bottom, false)) return Vect2i(0, 0); } _sx -= left + right; _sy -= top + bottom; if (crop_flag) crop(); if (compress_flag) compress(); return Vect2i(left, top); } bool qdAnimation::load_data(Common::SeekableReadStream &fh, int save_version) { debugC(3, kDebugSave, " qdAnimation::load_data(): before: %d", (int)fh.pos()); if (!qdNamedObject::load_data(fh, save_version)) return false; char fl; fl = fh.readByte(); if (fl) { qdNamedObjectReference ref; if (!ref.load_data(fh, save_version)) return false; if (qdGameDispatcher *p = qd_get_game_dispatcher()) { if (qdNamedObject *obj = p->get_named_object(&ref)) { if (obj->named_object_type() != QD_NAMED_OBJECT_ANIMATION) return false; int fl1 = flags(); static_cast(obj)->create_reference(this); clear_flags(); set_flag(fl1); } } } else clear(); char st, finished; st = fh.readByte(); finished = fh.readByte(); _cur_time = fh.readFloatLE(); _length = fh.readFloatLE(); _status = st; _is_finished = (finished) ? true : false; debugC(2, kDebugSave, " qdAnimation::load_data(): after: %d", (int)fh.pos()); return true; } bool qdAnimation::save_data(Common::WriteStream &fh) const { debugC(3, kDebugSave, " qdAnimation::save_data(): before: %d", (int)fh.pos()); if (!qdNamedObject::save_data(fh)) return false; if (check_flag(QD_ANIMATION_FLAG_REFERENCE) && _parent) { fh.writeByte(1); qdNamedObjectReference ref(_parent); if (!ref.save_data(fh)) return false; } else fh.writeByte(0); fh.writeByte(_status); fh.writeByte(_is_finished); fh.writeFloatLE(_cur_time); fh.writeFloatLE(_length); debugC(3, kDebugSave, " qdAnimation::save_data(): after: %d", (int)fh.pos()); return true; } grScreenRegion qdAnimation::screen_region(int mode, float scale) const { if (const qdAnimationFrame *p = get_cur_frame()) { if (check_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL)) mode |= GR_FLIP_HORIZONTAL; if (check_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL)) mode |= GR_FLIP_VERTICAL; return p->screen_region(mode, scale); } else return grScreenRegion_EMPTY; } bool qdAnimation::copy_frames(const qdAnimation &anm) { if (!check_flag(QD_ANIMATION_FLAG_REFERENCE)) { clear_frames(); _frames_ptr = &_frames; for (auto &it : anm._frames) { _frames.push_back(it->clone()); } _scaled_frames_ptr = &_scaled_frames; for (auto &it : anm._scaled_frames) { _scaled_frames.push_back(it->clone()); } } else { _frames_ptr = anm._frames_ptr; _scaled_frames_ptr = anm._scaled_frames_ptr; } return true; } void qdAnimation::clear_frames() { for (qdAnimationFrameList::iterator it = _frames.begin(); it != _frames.end(); ++it) delete *it; for (qdAnimationFrameList::iterator it = _scaled_frames.begin(); it != _scaled_frames.end(); ++it) delete *it; _frames.clear(); _scaled_frames.clear(); } bool qdAnimation::add_scale(float value) { if (fabs(value - 1.0f) <= 0.01f || value <= 0.01f) return false; Std::vector::const_iterator it = Common::find(_scales.begin(), _scales.end(), value); if (it != _scales.end()) return false; _scales.push_back(value); Common::sort(_scales.begin(), _scales.end()); return true; } bool qdAnimation::create_scaled_frames() { if (check_flag(QD_ANIMATION_FLAG_REFERENCE)) return false; for (qdAnimationFrameList::iterator it = _scaled_frames.begin(); it != _scaled_frames.end(); ++it) delete *it; _scaled_frames.clear(); for (uint i = 0; i < _scales.size(); i++) { for (qdAnimationFrameList::iterator it = _frames.begin(); it != _frames.end(); ++it) { _scaled_frames.push_back((*it)->clone()); _scaled_frames.back()->scale(_scales[i], _scales[i]); } } return true; } int qdAnimation::get_scale_index(float &scale_value) const { int index = -1; float scl = 1.0f; const Std::vector &scales_vect = (check_flag(QD_ANIMATION_FLAG_REFERENCE) && _parent) ? _parent->_scales : _scales; for (uint i = 0; i < scales_vect.size(); i++) { if (fabs(scale_value - scl) > fabs(scale_value - scales_vect[i])) { scl = scales_vect[i]; index = i; } } if (index != -1) scale_value /= scl; return index; } const qdAnimationFrame *qdAnimation::get_scaled_frame(int number, int scale_index) const { int num = 0; number += scale_index * _num_frames; for (qdAnimationFrameList::const_iterator it = _scaled_frames_ptr->begin(); it != _scaled_frames_ptr->end(); ++it) { if (num++ == number) return *it; } return NULL; } #ifdef __QD_DEBUG_ENABLE__ uint32 qdAnimation::resource_data_size() const { uint32 size = 0; for (qdAnimationFrameList::const_iterator it = _frames.begin(); it != _frames.end(); ++it) size += (*it)->resource_data_size(); for (qdAnimationFrameList::const_iterator it = _scaled_frames.begin(); it != _scaled_frames.end(); ++it) size += (*it)->resource_data_size(); return size; } #endif #define defFlag(x, i) { x, #x, i } struct FlagsList { int f; const char *s; const char *i; } static flagList[] = { defFlag(QD_ANIMATION_FLAG_REFERENCE, ICON_MS_ARTICLE_SHORTCUT), defFlag(QD_ANIMATION_FLAG_LOOP, ICON_MS_REPEAT), defFlag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL, ICON_MS_ARROWS_OUTWARD), defFlag(QD_ANIMATION_FLAG_FLIP_VERTICAL, ICON_MS_HEIGHT), defFlag(QD_ANIMATION_FLAG_BLACK_FON, ICON_MS_BACKGROUND_REPLACE), defFlag(QD_ANIMATION_FLAG_SUPPRESS_ALPHA, ICON_MS_IMAGE), defFlag(QD_ANIMATION_FLAG_CROP, ICON_MS_CROP), defFlag(QD_ANIMATION_FLAG_COMPRESS, ICON_MS_COMPRESS), defFlag(QD_ANIMATION_FLAG_TILE_COMPRESS, ICON_MS_GRID_VIEW), }; Common::String qdAnimation::flag2str(int fl, bool truncate, bool icon) { Common::String res; for (int i = 0; i < ARRAYSIZE(flagList); i++) { if (fl & flagList[i].f) { if (!icon) { if (!res.empty()) res += " | "; res += &flagList[i].s[truncate ? 18 : 0]; } else { res += flagList[i].i; } fl &= ~flagList[i].f; } } if (fl) res += Common::String::format(" | %x", fl); return res; } #define defEnum(x) #x static const char *statusList[] = { defEnum(QD_ANIMATION_STOPPED), defEnum(QD_ANIMATION_PLAYING), defEnum(QD_ANIMATION_PAUSED), defEnum(QD_ANIMATION_END_PLAYING), }; Common::String qdAnimation::status2str(int fl, bool truncate) { if (fl >= ARRAYSIZE(statusList) || fl < 0) return Common::String::format("<%d>", fl); return Common::String(&statusList[fl][truncate ? 13 : 0]); } } // namespace QDEngine