ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

image.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/stark/resources/image.h"
00024 
00025 #include "common/debug.h"
00026 #include "image/png.h"
00027 
00028 #include "engines/stark/debug.h"
00029 #include "engines/stark/formats/xrc.h"
00030 #include "engines/stark/resources/location.h"
00031 #include "engines/stark/services/archiveloader.h"
00032 #include "engines/stark/services/settings.h"
00033 #include "engines/stark/services/services.h"
00034 #include "engines/stark/visual/effects/bubbles.h"
00035 #include "engines/stark/visual/effects/fireflies.h"
00036 #include "engines/stark/visual/effects/fish.h"
00037 #include "engines/stark/visual/image.h"
00038 #include "engines/stark/visual/text.h"
00039 
00040 #include "math/line2d.h"
00041 #include "math/vector2d.h"
00042 
00043 namespace Stark {
00044 namespace Resources {
00045 
00046 Object *Image::construct(Object *parent, byte subType, uint16 index, const Common::String &name) {
00047     switch (subType) {
00048     case kImageSub2:
00049     case kImageSub3:
00050         return new ImageStill(parent, subType, index, name);
00051     case kImageText:
00052         return new ImageText(parent, subType, index, name);
00053     default:
00054         error("Unknown image subtype %d", subType);
00055     }
00056 }
00057 
00058 Image::~Image() {
00059     delete _visual;
00060 }
00061 
00062 Image::Image(Object *parent, byte subType, uint16 index, const Common::String &name) :
00063         Object(parent, subType, index, name),
00064         _transparent(false),
00065         _transparentColor(0),
00066         _field_44_ADF(0),
00067         _field_48_ADF(30),
00068         _visual(nullptr) {
00069     _type = TYPE;
00070 }
00071 
00072 void Image::readData(Formats::XRCReadStream *stream) {
00073     _filename = stream->readString();
00074     _hotspot = stream->readPoint();
00075     _transparent = stream->readBool();
00076     _transparentColor = stream->readUint32LE();
00077 
00078     uint32 polygonCount = stream->readUint32LE();
00079     for (uint32 i = 0; i < polygonCount; i++) {
00080         Polygon polygon;
00081 
00082         uint32 pointCount = stream->readUint32LE();
00083         for (uint32 j = 0; j < pointCount; j++) {
00084             polygon.push_back(stream->readPoint());
00085         }
00086 
00087         _polygons.push_back(polygon);
00088     }
00089 
00090     _archiveName = stream->getArchiveName();
00091 }
00092 
00093 Visual *Image::getVisual() {
00094     initVisual();
00095     return _visual;
00096 }
00097 
00098 void Image::printData() {
00099     debug("filename: %s", _filename.c_str());
00100     debug("hotspot: x %d, y %d", _hotspot.x, _hotspot.y);
00101     debug("transparent: %d", _transparent);
00102     debug("transparentColor: %d", _transparentColor);
00103     debug("field_44: %d", _field_44_ADF);
00104     debug("field_48: %d", _field_48_ADF);
00105 
00106     for (uint32 i = 0; i < _polygons.size(); i++) {
00107         Common::String description;
00108         for (uint32 j = 0; j < _polygons[i].size(); j++) {
00109             description += Common::String::format("(x %d, y %d) ", _polygons[i][j].x, _polygons[i][j].y);
00110         }
00111         debug("polygon %d: %s", i, description.c_str());
00112     }
00113 }
00114 
00115 int Image::indexForPoint(const Common::Point &point) const {
00116     int index = -1;
00117     for (uint32 i = 0; i < _polygons.size(); i++) {
00118         if (isPointInPolygon(_polygons[i], point)) {
00119             index = i;
00120         }
00121     }
00122 
00123     return index;
00124 }
00125 
00126 bool Image::isPointInPolygon(const Polygon &polygon, const Common::Point &point) const {
00127     if (polygon.size() <= 1) {
00128         return false; // Empty polygon
00129     }
00130 
00131     // A ray cast from the point
00132     Math::Segment2d testLine(Math::Vector2d(point.x, point.y), Math::Vector2d(-100, -100));
00133 
00134     // Special case the line created between the last point and the first
00135     Math::Vector2d prevPoint = Math::Vector2d(polygon.back().x, polygon.back().y);
00136 
00137     // Count the intersections of the ray with the polygon's edges
00138     int intersectCount = 0;
00139     for (uint32 j = 0; j < polygon.size(); j++) {
00140         Math::Vector2d curPoint = Math::Vector2d(polygon[j].x, polygon[j].y);
00141 
00142         if (Math::Segment2d(prevPoint, curPoint).intersectsSegment(testLine, nullptr)) {
00143             intersectCount++;
00144         }
00145 
00146         prevPoint = curPoint;
00147     }
00148 
00149     // If the ray crosses the polygon an odd number of times, the point is inside the polygon
00150     return intersectCount % 2 != 0;
00151 }
00152 
00153 Common::Point Image::getHotspotPosition(uint index) const {
00154     if (index >= _polygons.size()) {
00155         return Common::Point(-1, -1);
00156     }
00157 
00158     Polygon polygon = _polygons[index];
00159 
00160     // Return the top-middle point as the hotspot
00161     int right = polygon[0].x, top = polygon[0].y;
00162 
00163     for (uint i = 1; i < polygon.size(); ++i) {
00164         right += polygon[i].x;
00165         if (polygon[i].y < top) {
00166             top = polygon[i].y;
00167         }
00168     }
00169 
00170     right /= polygon.size();
00171     
00172     if (top < 0) {
00173         top = 0;
00174     }
00175 
00176     return Common::Point(right, top);
00177 }
00178 
00179 ImageStill::~ImageStill() {
00180 }
00181 
00182 ImageStill::ImageStill(Object *parent, byte subType, uint16 index, const Common::String &name) :
00183         Image(parent, subType, index, name),
00184         _noName(false) {
00185 }
00186 
00187 void ImageStill::readData(Formats::XRCReadStream *stream) {
00188     Image::readData(stream);
00189 
00190     if (stream->isDataLeft()) {
00191         _field_44_ADF = stream->readUint32LE();
00192         _field_44_ADF /= 33;
00193     }
00194 
00195     if (stream->isDataLeft()) {
00196         _field_48_ADF = stream->readUint32LE();
00197     }
00198 
00199     _noName = _filename == "noname" || _filename == "noname.xmg";
00200 }
00201 
00202 void ImageStill::onPostRead() {
00203     initVisual();
00204 }
00205 
00206 void ImageStill::initVisual() {
00207     if (_visual) {
00208         return; // The visual is already there
00209     }
00210 
00211     if (_noName) {
00212         return; // No file to load
00213     }
00214 
00215     Common::ReadStream *xmgStream = StarkArchiveLoader->getFile(_filename, _archiveName);
00216 
00217     VisualImageXMG *visual = new VisualImageXMG(StarkGfx);
00218 
00219     if (StarkSettings->isAssetsModEnabled() && loadPNGOverride(visual)) {
00220         visual->readOriginalSize(xmgStream);
00221     } else {
00222         visual->load(xmgStream);
00223     }
00224 
00225     visual->setHotSpot(_hotspot);
00226 
00227     _visual = visual;
00228 
00229     delete xmgStream;
00230 }
00231 
00232 bool ImageStill::loadPNGOverride(VisualImageXMG *visual) const {
00233     if (!_filename.hasSuffixIgnoreCase(".xmg")) {
00234         return false;
00235     }
00236 
00237     Common::String pngFilename = Common::String(_filename.c_str(), _filename.size() - 4) + ".png";
00238     Common::String pngFilePath = StarkArchiveLoader->getExternalFilePath(pngFilename, _archiveName);
00239 
00240     debugC(kDebugModding, "Attempting to load %s", pngFilePath.c_str());
00241 
00242     Common::SeekableReadStream *pngStream = SearchMan.createReadStreamForMember(pngFilePath);
00243     if (!pngStream) {
00244         return false;
00245     }
00246 
00247     if (!visual->loadPNG(pngStream)) {
00248         warning("Failed to load %s. It is not a valid PNG file.", pngFilePath.c_str());
00249         delete pngStream;
00250         return false;
00251     }
00252 
00253     debugC(kDebugModding, "Loaded %s", pngFilePath.c_str());
00254 
00255     delete pngStream;
00256     return true;
00257 }
00258 
00259 void ImageStill::printData() {
00260     Image::printData();
00261 }
00262 
00263 ImageText::ImageText(Object *parent, byte subType, uint16 index, const Common::String &name) :
00264         Image(parent, subType, index, name),
00265         _color(Color(0, 0, 0)),
00266         _font(0) {
00267 }
00268 
00269 ImageText::~ImageText() {
00270 }
00271 
00272 void ImageText::readData(Formats::XRCReadStream *stream) {
00273     Image::readData(stream);
00274 
00275     _size = stream->readPoint();
00276     _text = stream->readString();
00277     _color.r = stream->readByte();
00278     _color.g = stream->readByte();
00279     _color.b = stream->readByte();
00280     _color.a = stream->readByte() | 0xFF;
00281     _font = stream->readUint32LE();
00282 
00283     // WORKAROUND: Give more space to text in the Archives' computer
00284     // So there are no line breaks in the French version of the game
00285     // when using a scaled font.
00286     Location *location = findParent<Location>();
00287     if (_name == "MAIN" && location && location->getName() == "Archive Database") {
00288         _size.x = 80;
00289     }
00290 }
00291 
00292 void ImageText::initVisual() {
00293     if (_visual) {
00294         return; // The visual is already there
00295     }
00296 
00297     if (_text.hasPrefix("GFX_Bubbles")) {
00298         VisualEffectBubbles *bubbles = new VisualEffectBubbles(StarkGfx, _size);
00299         bubbles->setParams(_text);
00300         _visual = bubbles;
00301     } else if (_text.hasPrefix("GFX_FireFlies")) {
00302         VisualEffectFireFlies *fireFlies = new VisualEffectFireFlies(StarkGfx, _size);
00303         fireFlies->setParams(_text);
00304         _visual = fireFlies;
00305     } else if (_text.hasPrefix("GFX_Fish")) {
00306         VisualEffectFish *fish = new VisualEffectFish(StarkGfx, _size);
00307         fish->setParams(_text);
00308         _visual = fish;
00309     } else if (_text.hasPrefix("GFX_")) {
00310         error("Unknown effect '%s'", _text.c_str());
00311     } else {
00312         VisualText *text = new VisualText(StarkGfx);
00313         text->setText(_text);
00314         text->setColor(_color);
00315         text->setTargetWidth(_size.x);
00316         text->setTargetHeight(_size.y);
00317         text->setFont(FontProvider::kCustomFont, _font);
00318 
00319         // WORKAROUND: Move the "White Cardinal" hotspot in the Archives'
00320         // computer so it matches the text. Fixes the hotspot being hard to
00321         // use with scaled fonts in the Spanish version of the game.
00322         if (_name == "The Church" && _polygons.size() == 2) {
00323             fixWhiteCardinalHotspot(text);
00324         }
00325 
00326         _visual = text;
00327     }
00328 }
00329 
00330 void ImageText::fixWhiteCardinalHotspot(VisualText *text) {
00331     Common::Rect textRect = text->getRect();
00332 
00333     Polygon &hotspotPoly = _polygons.back();
00334     if (hotspotPoly.size() != 4) {
00335         return;
00336     }
00337 
00338     Common::Point &topLeft     = hotspotPoly[0];
00339     Common::Point &topRight    = hotspotPoly[1];
00340     Common::Point &bottomRight = hotspotPoly[2];
00341     Common::Point &bottomLeft  = hotspotPoly[3];
00342 
00343     int16 hotspotHeight = bottomLeft.y - topLeft.y;
00344     if (hotspotHeight <= 0) {
00345         return;
00346     }
00347 
00348     bottomLeft .y = textRect.bottom;
00349     bottomRight.y = textRect.bottom;
00350     topLeft    .y = textRect.bottom - hotspotHeight;
00351     topRight   .y = textRect.bottom - hotspotHeight;
00352 }
00353 
00354 void ImageText::resetVisual() {
00355     if (!_visual) {
00356         return;
00357     }
00358 
00359     VisualText *text = _visual->get<VisualText>();
00360     if (text) {
00361         text->resetTexture();
00362     }
00363 }
00364 
00365 void ImageText::printData() {
00366     Image::printData();
00367 
00368     debug("size: x %d, y %d", _size.x, _size.y);
00369     debug("text: %s", _text.c_str());
00370     debug("color: (%d, %d, %d, %d)", _color.r, _color.g, _color.b, _color.a);
00371     debug("font: %d", _font);
00372 }
00373 
00374 } // End of namespace Resources
00375 } // End of namespace Stark


Generated on Sat Aug 1 2020 05:01:22 for ResidualVM by doxygen 1.7.1
curved edge   curved edge