1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/ultima4/gfx/image.h"
24 #include "ultima/ultima4/gfx/imageloader.h"
25 #include "ultima/ultima4/gfx/imagemgr.h"
26 #include "ultima/ultima4/controllers/intro_controller.h"
27 #include "ultima/ultima4/core/config.h"
28 #include "ultima/ultima4/core/settings.h"
29 #include "ultima/ultima4/ultima4.h"
30 
31 namespace Ultima {
32 namespace Ultima4 {
33 
hasBlackBackground()34 bool ImageInfo::hasBlackBackground() {
35 	return this->_filetype == "image/x-u4raw";
36 }
37 
38 
39 class ImageSet {
40 public:
41 	~ImageSet();
42 
43 	Common::String _name;
44 	Common::String _location;
45 	Common::String _extends;
46 	Std::map<Common::String, ImageInfo *> _info;
47 };
48 
49 ImageMgr *ImageMgr::_instance = nullptr;
50 
getInstance()51 ImageMgr *ImageMgr::getInstance() {
52 	if (_instance == nullptr) {
53 		_instance = new ImageMgr();
54 		_instance->init();
55 	}
56 	return _instance;
57 }
58 
destroy()59 void ImageMgr::destroy() {
60 	if (_instance != nullptr) {
61 		delete _instance;
62 		_instance = nullptr;
63 	}
64 }
65 
ImageMgr()66 ImageMgr::ImageMgr() : _baseSet(nullptr), _abyssData(nullptr) {
67 	settings.addObserver(this);
68 }
69 
~ImageMgr()70 ImageMgr::~ImageMgr() {
71 	settings.deleteObserver(this);
72 
73 	for (Std::map<Common::String, ImageSet *>::iterator i = _imageSets.begin(); i != _imageSets.end(); i++)
74 		delete i->_value;
75 
76 	delete[] _abyssData;
77 }
78 
init()79 void ImageMgr::init() {
80 	/*
81 	 * register the "screen" image representing the entire screen
82 	 */
83 	Image *screen = Image::createScreenImage();
84 
85 	_screenInfo._name = "screen";
86 	_screenInfo._filename = "";
87 	_screenInfo._width = screen->width();
88 	_screenInfo._height = screen->height();
89 	_screenInfo._depth = 0;
90 	_screenInfo._prescale = 0;
91 	_screenInfo._filetype = "";
92 	_screenInfo._tiles = 0;
93 	_screenInfo._introOnly = false;
94 	_screenInfo._transparentIndex = -1;
95 	_screenInfo._xu4Graphic = false;
96 	_screenInfo._fixup = FIXUP_NONE;
97 	_screenInfo._image = screen;
98 
99 	/*
100 	 * register all the images declared in the config files
101 	 */
102 	const Config *config = Config::getInstance();
103 	Std::vector<ConfigElement> graphicsConf = config->getElement("graphics").getChildren();
104 	for (Std::vector<ConfigElement>::iterator conf = graphicsConf.begin(); conf != graphicsConf.end(); conf++) {
105 		if (conf->getName() == "imageset") {
106 			ImageSet *set = loadImageSetFromConf(*conf);
107 			_imageSets[set->_name] = set;
108 
109 			// all image sets include the "screen" image
110 			set->_info[_screenInfo._name] = &_screenInfo;
111 		}
112 	}
113 
114 	_imageSetNames.clear();
115 	for (Std::map<Common::String, ImageSet *>::const_iterator set = _imageSets.begin(); set != _imageSets.end(); set++)
116 		_imageSetNames.push_back(set->_key);
117 
118 	update(&settings);
119 }
120 
loadImageSetFromConf(const ConfigElement & conf)121 ImageSet *ImageMgr::loadImageSetFromConf(const ConfigElement &conf) {
122 	ImageSet *set;
123 
124 	set = new ImageSet();
125 	set->_name = conf.getString("name");
126 	set->_location = conf.getString("location");
127 	set->_extends = conf.getString("extends");
128 
129 	Std::vector<ConfigElement> children = conf.getChildren();
130 	for (Std::vector<ConfigElement>::iterator i = children.begin(); i != children.end(); i++) {
131 		if (i->getName() == "image") {
132 			ImageInfo *info = loadImageInfoFromConf(*i);
133 			if (set->_info.contains(info->_name))
134 				delete set->_info[info->_name];
135 			set->_info[info->_name] = info;
136 		}
137 	}
138 
139 	return set;
140 }
141 
loadImageInfoFromConf(const ConfigElement & conf)142 ImageInfo *ImageMgr::loadImageInfoFromConf(const ConfigElement &conf) {
143 	ImageInfo *info;
144 	static const char *fixupEnumStrings[] = { "none", "intro", "abyss", "abacus", "dungns", "blackTransparencyHack", "fmtownsscreen", nullptr };
145 
146 	info = new ImageInfo();
147 	info->_name = conf.getString("name");
148 	info->_filename = conf.getString("filename");
149 	info->_width = conf.getInt("width", -1);
150 	info->_height = conf.getInt("height", -1);
151 	info->_depth = conf.getInt("depth", -1);
152 	info->_prescale = conf.getInt("prescale");
153 	info->_filetype = conf.getString("filetype");
154 	info->_tiles = conf.getInt("tiles");
155 	info->_introOnly = conf.getBool("introOnly");
156 	info->_transparentIndex = conf.getInt("transparentIndex", -1);
157 
158 	info->_xu4Graphic = conf.getBool("xu4Graphic");
159 	info->_fixup = static_cast<ImageFixup>(conf.getEnum("fixup", fixupEnumStrings));
160 	info->_image = nullptr;
161 
162 	Std::vector<ConfigElement> children = conf.getChildren();
163 	for (Std::vector<ConfigElement>::iterator i = children.begin(); i != children.end(); i++) {
164 		if (i->getName() == "subimage") {
165 			SubImage *subimage = loadSubImageFromConf(info, *i);
166 			info->_subImages[subimage->_name] = subimage;
167 		}
168 	}
169 
170 	return info;
171 }
172 
loadSubImageFromConf(const ImageInfo * info,const ConfigElement & conf)173 SubImage *ImageMgr::loadSubImageFromConf(const ImageInfo *info, const ConfigElement &conf) {
174 	SubImage *subimage;
175 	static int x = 0,
176 	           y = 0,
177 	           last_width = 0,
178 	           last_height = 0;
179 
180 	subimage = new SubImage();
181 	subimage->_name = conf.getString("name");
182 	subimage->setWidth(conf.getInt("width"));
183 	subimage->setHeight(conf.getInt("height"));
184 	subimage->_srcImageName = info->_name;
185 	if (conf.exists("x") && conf.exists("y")) {
186 		x = conf.getInt("x");
187 		y = conf.getInt("y");
188 		subimage->moveTo(x, y);
189 	} else {
190 		// Automatically increment our position through the base image
191 		x += last_width;
192 		if (x >= last_width) {
193 			x = 0;
194 			y += last_height;
195 		}
196 
197 		subimage->moveTo(x, y);
198 	}
199 
200 	// "remember" the width and height of this subimage
201 	last_width = subimage->width();
202 	last_height = subimage->height();
203 
204 	return subimage;
205 }
206 
fixupIntro(Image * im,int prescale)207 void ImageMgr::fixupIntro(Image *im, int prescale) {
208 	const byte *sigData;
209 	int i, x, y;
210 	bool alpha = im->isAlphaOn();
211 	RGBA color;
212 
213 	sigData = g_intro->getSigData();
214 	im->alphaOff();
215 	if (settings._videoType != "VGA-ALLPNG" && settings._videoType != "new") {
216 		/* ----------------------------
217 		 * update the position of "and"
218 		 * ---------------------------- */
219 		im->drawSubRectOn(im, 148 * prescale, 17 * prescale,
220 		                  153 * prescale,
221 		                  17 * prescale,
222 		                  11 * prescale,
223 		                  4 * prescale);
224 		im->drawSubRectOn(im, 159 * prescale, 17 * prescale,
225 		                  165 * prescale,
226 		                  18 * prescale,
227 		                  1 * prescale,
228 		                  4 * prescale);
229 		im->drawSubRectOn(im, 160 * prescale, 17 * prescale,
230 		                  164 * prescale,
231 		                  17 * prescale,
232 		                  16 * prescale,
233 		                  4 * prescale);
234 		/* ---------------------------------------------
235 		 * update the position of "Origin Systems, Inc."
236 		 * --------------------------------------------- */
237 		im->drawSubRectOn(im, 86 * prescale, 21 * prescale,
238 		                  88 * prescale,
239 		                  21 * prescale,
240 		                  114 * prescale,
241 		                  9 * prescale);
242 		im->drawSubRectOn(im, 199 * prescale, 21 * prescale,
243 		                  202 * prescale,
244 		                  21 * prescale,
245 		                  6 * prescale,
246 		                  9 * prescale);
247 		im->drawSubRectOn(im, 207 * prescale, 21 * prescale,
248 		                  208 * prescale,
249 		                  21 * prescale,
250 		                  28 * prescale,
251 		                  9 * prescale);
252 		/* ---------------------------------------------
253 		 * update the position of "Ultima IV"
254 		 * --------------------------------------------- */
255 		// move this *prior* to moving "present"
256 		im->drawSubRectOn(im, 59 * prescale, 33 * prescale,
257 		                  61 * prescale,
258 		                  33 * prescale,
259 		                  204 * prescale,
260 		                  46 * prescale);
261 		/* ---------------------------------------------
262 		 * update the position of "Quest of the Avatar"
263 		 * --------------------------------------------- */
264 		im->drawSubRectOn(im, 69 * prescale, 80 * prescale,     // quEst
265 		                  70 * prescale,
266 		                  80 * prescale,
267 		                  11 * prescale,
268 		                  13 * prescale);
269 		im->drawSubRectOn(im, 82 * prescale, 80 * prescale,     // queST
270 		                  84 * prescale,
271 		                  80 * prescale,
272 		                  27 * prescale,
273 		                  13 * prescale);
274 		im->drawSubRectOn(im, 131 * prescale, 80 * prescale,    // oF
275 		                  132 * prescale,
276 		                  80 * prescale,
277 		                  11 * prescale,
278 		                  13 * prescale);
279 		im->drawSubRectOn(im, 150 * prescale, 80 * prescale,    // THE
280 		                  149 * prescale,
281 		                  80 * prescale,
282 		                  40 * prescale,
283 		                  13 * prescale);
284 		im->drawSubRectOn(im, 166 * prescale, 80 * prescale,    // tHe
285 		                  165 * prescale,
286 		                  80 * prescale,
287 		                  11 * prescale,
288 		                  13 * prescale);
289 		im->drawSubRectOn(im, 200 * prescale, 80 * prescale,    // AVATAR
290 		                  201 * prescale,
291 		                  80 * prescale,
292 		                  81 * prescale,
293 		                  13 * prescale);
294 		im->drawSubRectOn(im, 227 * prescale, 80 * prescale,    // avAtar
295 		                  228 * prescale,
296 		                  80 * prescale,
297 		                  11 * prescale,
298 		                  13 * prescale);
299 	}
300 	/* -----------------------------------------------------------------------------
301 	 * copy "present" to new location between "Origin Systems, Inc." and "Ultima IV"
302 	 * ----------------------------------------------------------------------------- */
303 	// do this *after* moving "Ultima IV"
304 	im->drawSubRectOn(im, 132 * prescale, 33 * prescale,
305 	                  135 * prescale,
306 	                  0 * prescale,
307 	                  56 * prescale,
308 	                  5 * prescale);
309 
310 	if (alpha) {
311 		im->alphaOn();
312 	}
313 
314 	/* ----------------------------
315 	 * erase the original "present"
316 	 * ---------------------------- */
317 	im->fillRect(135 * prescale, 0 * prescale, 56 * prescale, 5 * prescale, 0, 0, 0);
318 
319 	/* -------------------------
320 	 * update the colors for VGA
321 	 * ------------------------- */
322 	if (settings._videoType == "VGA") {
323 		ImageInfo *borderInfo = imageMgr->get(BKGD_BORDERS, true);
324 //        ImageInfo *charsetInfo = imageMgr->get(BKGD_CHARSET);
325 		if (!borderInfo)
326 			error("ERROR 1001: Unable to load the \"%s\" data file", BKGD_BORDERS);
327 
328 		delete borderInfo->_image;
329 		borderInfo->_image = nullptr;
330 		borderInfo = imageMgr->get(BKGD_BORDERS, true);
331 
332 		im->setPaletteFromImage(borderInfo->_image);
333 
334 		// update the color of "and" and "present"
335 		(void)im->setPaletteIndex(15, im->setColor(226, 226, 255));
336 
337 		// update the color of "Origin Systems, Inc."
338 		(void)im->setPaletteIndex(9, im->setColor(129, 129, 255));
339 
340 #ifdef TODO
341 		borderInfo->_image->save("border.png");
342 #endif
343 		// update the border appearance
344 		borderInfo->_image->alphaOff();
345 		borderInfo->_image->drawSubRectOn(im, 0, 96, 0, 0, 16, 56);
346 		for (i = 0; i < 9; i++) {
347 			borderInfo->_image->drawSubRectOn(im, 16 + (i * 32), 96, 144, 0, 48, 48);
348 		}
349 		im->drawSubRectInvertedOn(im, 0, 144, 0, 104, 320, 40);
350 		im->drawSubRectOn(im, 0, 184, 0, 96, 320, 8);
351 		borderInfo->_image->alphaOn();
352 
353 		delete borderInfo->_image;
354 		borderInfo->_image = nullptr;
355 	}
356 
357 	/* -----------------------------
358 	 * draw "Lord British" signature
359 	 * ----------------------------- */
360 	color = im->setColor(0, 255, 255);  // cyan for EGA
361 	int blue[16] = {255, 250, 226, 226, 210, 194, 161, 161,
362 	                129,  97,  97,  64,  64,  32,  32,   0
363 	               };
364 	i = 0;
365 	while (sigData[i] != 0) {
366 		/* (x/y) are unscaled coordinates, i.e. in 320x200 */
367 		x = sigData[i] + 0x14;
368 		y = 0xBF - sigData[i + 1];
369 
370 		if (settings._videoType != "EGA") {
371 			// yellow gradient
372 			color = im->setColor(255, (y == 1 ? 250 : 255), blue[y]);
373 		}
374 
375 		im->fillRect(x * prescale, y * prescale,
376 		             2 * prescale, prescale,
377 		             color.r, color.g, color.b);
378 		i += 2;
379 	}
380 
381 	/* --------------------------------------------------------------
382 	 * draw the red line between "Origin Systems, Inc." and "present"
383 	 * -------------------------------------------------------------- */
384 	/* we're still working with an unscaled surface */
385 	if (settings._videoType != "EGA") {
386 		color = im->setColor(0, 0, 161);    // dark blue
387 	} else {
388 		color = im->setColor(128, 0, 0);    // dark red for EGA
389 	}
390 	for (i = 84; i < 236; i++)  // 152 px wide
391 		im->fillRect(i * prescale, 31 * prescale,
392 		             prescale, prescale,
393 		             color.r, color.g, color.b);
394 }
395 
fixupAbyssVision(Image * im,int prescale)396 void ImageMgr::fixupAbyssVision(Image *im, int prescale) {
397 	// Ignore fixups for xu4 PNG images - they're already correct
398 	if (im->isIndexed())
399 		return;
400 
401 	/*
402 	 * Each VGA vision components must be XORed with all the previous
403 	 * vision components to get the actual image.
404 	 */
405 	if (_abyssData) {
406 		for (int y = 0; y < im->height(); y++) {
407 			for (int x = 0; x < im->width(); x++) {
408 				uint index;
409 				im->getPixelIndex(x, y, index);
410 				index ^= _abyssData[y * im->width() + x];
411 				im->putPixelIndex(x, y, index);
412 			}
413 		}
414 	} else {
415 		_abyssData = new uint[im->width() * im->height()];
416 	}
417 
418 	for (int y = 0; y < im->height(); y++) {
419 		for (int x = 0; x < im->width(); x++) {
420 			uint index;
421 			im->getPixelIndex(x, y, index);
422 			_abyssData[y * im->width() + x] = index;
423 		}
424 	}
425 }
426 
fixupAbacus(Image * im,int prescale)427 void ImageMgr::fixupAbacus(Image *im, int prescale) {
428 
429 	/*
430 	 * surround each bead with a row green pixels to avoid artifacts
431 	 * when scaling
432 	 */
433 
434 	im->fillRect(7 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
435 	im->fillRect(16 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
436 	im->fillRect(8 * prescale, 186 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
437 	im->fillRect(8 * prescale, 199 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
438 
439 	im->fillRect(23 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
440 	im->fillRect(32 * prescale, 186 * prescale, prescale, 14 * prescale, 0, 255, 80); /* green */
441 	im->fillRect(24 * prescale, 186 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
442 	im->fillRect(24 * prescale, 199 * prescale, prescale * 8, prescale, 0, 255, 80); /* green */
443 }
444 
fixupDungNS(Image * im,int prescale)445 void ImageMgr::fixupDungNS(Image *im, int prescale) {
446 	for (int y = 0; y < im->height(); y++) {
447 		for (int x = 0; x < im->width(); x++) {
448 			uint index;
449 			im->getPixelIndex(x, y, index);
450 			if (index == 1)
451 				im->putPixelIndex(x, y, 2);
452 			else if (index == 2)
453 				im->putPixelIndex(x, y, 1);
454 		}
455 	}
456 }
457 
fixupFMTowns(Image * im,int prescale)458 void ImageMgr::fixupFMTowns(Image *im, int prescale) {
459 	for (int y = 20; y < im->height(); y++) {
460 		for (int x = 0; x < im->width(); x++) {
461 			uint index;
462 			im->getPixelIndex(x, y, index);
463 			im->putPixelIndex(x, y - 20, index);
464 		}
465 	}
466 }
467 
getSet(const Common::String & setname)468 ImageSet *ImageMgr::getSet(const Common::String &setname) {
469 	Std::map<Common::String, ImageSet *>::iterator i = _imageSets.find(setname);
470 	if (i != _imageSets.end())
471 		return i->_value;
472 	else
473 		return nullptr;
474 }
475 
getInfo(const Common::String & name)476 ImageInfo *ImageMgr::getInfo(const Common::String &name) {
477 	return getInfoFromSet(name, _baseSet);
478 }
479 
getInfoFromSet(const Common::String & name,ImageSet * imageset)480 ImageInfo *ImageMgr::getInfoFromSet(const Common::String &name, ImageSet *imageset) {
481 	if (!imageset)
482 		return nullptr;
483 
484 	/* if the image set contains the image we want, AND IT EXISTS we are done */
485 	Std::map<Common::String, ImageInfo *>::iterator i = imageset->_info.find(name);
486 	if (i != imageset->_info.end())
487 		if (imageExists(i->_value))
488 			return i->_value;
489 
490 	/* otherwise if this image set extends another, check the base image set */
491 	while (imageset->_extends != "") {
492 		imageset = getSet(imageset->_extends);
493 		return getInfoFromSet(name, imageset);
494 	}
495 
496 	//warning("Searched recursively from imageset %s through to %s and couldn't find %s", baseSet->name.c_str(), imageset->name.c_str(), name.c_str());
497 	return nullptr;
498 }
499 
guessFileType(const Common::String & filename)500 Common::String ImageMgr::guessFileType(const Common::String &filename) {
501 	if (filename.size() >= 4 && filename.hasSuffixIgnoreCase(".png")) {
502 		return "image/png";
503 	} else {
504 		return "";
505 	}
506 }
507 
imageExists(ImageInfo * info)508 bool ImageMgr::imageExists(ImageInfo *info) {
509 	if (info->_filename == "") //If it is an abstract image like "screen"
510 		return true;
511 	Common::File *file = getImageFile(info);
512 	if (file) {
513 		delete file;
514 		return true;
515 	}
516 	return false;
517 }
518 
519 
getImageFile(ImageInfo * info)520 Common::File *ImageMgr::getImageFile(ImageInfo *info) {
521 	Common::String filename = info->_filename;
522 
523 	if (filename.empty())
524 		return nullptr;
525 
526 	Common::File *file = new Common::File();
527 	if (!info->_xu4Graphic) {
528 		// It's a file in the game folder
529 		if (file->open(filename))
530 			return file;
531 	}
532 
533 	if (file->open("data/graphics/" + filename))
534 		return file;
535 
536 	delete file;
537 	return nullptr;
538 }
539 
get(const Common::String & name,bool returnUnscaled)540 ImageInfo *ImageMgr::get(const Common::String &name, bool returnUnscaled) {
541 	ImageInfo *info = getInfo(name);
542 	if (!info)
543 		return nullptr;
544 
545 	/* return if already loaded */
546 	if (info->_image != nullptr)
547 		return info;
548 
549 	Common::File *file = getImageFile(info);
550 	Image *unscaled = nullptr;
551 	if (file) {
552 		if (info->_filetype.empty())
553 			info->_filetype = guessFileType(info->_filename);
554 		Common::String filetype = info->_filetype;
555 		ImageLoader *loader = g_ultima->_imageLoaders->getLoader(filetype);
556 		if (loader == nullptr) {
557 			warning("can't find loader to load image \"%s\" with type \"%s\"", info->_filename.c_str(), filetype.c_str());
558 		} else {
559 			unscaled = loader->load(*file, info->_width, info->_height, info->_depth);
560 			if (info->_width == -1) {
561 				// Write in the values for later use.
562 				info->_width = unscaled->width();
563 				info->_height = unscaled->height();
564 				// ###            info->depth = ???
565 			}
566 		}
567 
568 		delete file;
569 	} else {
570 		warning("Failed to open file %s for reading.", info->_filename.c_str());
571 		return nullptr;
572 	}
573 
574 	if (unscaled == nullptr)
575 		return nullptr;
576 
577 	if (info->_transparentIndex != -1)
578 		unscaled->setTransparentIndex(info->_transparentIndex);
579 
580 	if (info->_prescale == 0)
581 		info->_prescale = 1;
582 
583 	/*
584 	 * fixup the image before scaling it
585 	 */
586 	switch (info->_fixup) {
587 	case FIXUP_NONE:
588 		break;
589 	case FIXUP_INTRO:
590 		fixupIntro(unscaled, info->_prescale);
591 		break;
592 	case FIXUP_ABYSS:
593 		fixupAbyssVision(unscaled, info->_prescale);
594 		break;
595 	case FIXUP_ABACUS:
596 		fixupAbacus(unscaled, info->_prescale);
597 		break;
598 	case FIXUP_DUNGNS:
599 		fixupDungNS(unscaled, info->_prescale);
600 		break;
601 	case FIXUP_FMTOWNSSCREEN:
602 		fixupFMTowns(unscaled, info->_prescale);
603 		break;
604 	case FIXUP_BLACKTRANSPARENCYHACK:
605 		//Apply transparency shadow hack to ultima4 ega and vga upgrade classic graphics.
606 		Image *unscaled_original = unscaled;
607 		unscaled = Image::duplicate(unscaled);
608 		delete unscaled_original;
609 		if (Settings::getInstance()._enhancements && Settings::getInstance()._enhancementsOptions._u4TileTransparencyHack) {
610 			int transparency_shadow_size = Settings::getInstance()._enhancementsOptions._u4TrileTransparencyHackShadowBreadth;
611 			int black_index = 0;
612 			int opacity = Settings::getInstance()._enhancementsOptions._u4TileTransparencyHackPixelShadowOpacity;
613 
614 			int frames = info->_tiles;
615 			for (int f = 0; f < frames; ++f)
616 				unscaled->performTransparencyHack(black_index, frames, f, transparency_shadow_size, opacity);
617 		}
618 		break;
619 	}
620 
621 	if (returnUnscaled) {
622 		info->_image = unscaled;
623 		return info;
624 	}
625 
626 	int imageScale = settings._scale;
627 	if ((settings._scale % info->_prescale) != 0) {
628 		int orig_scale = settings._scale;
629 		settings._scale = info->_prescale;
630 		settings.write();
631 		error("image %s is prescaled to an incompatible size: %d\nResetting the scale to %d. Sorry about the inconvenience, please restart.", info->_filename.c_str(), orig_scale, settings._scale);
632 	}
633 	imageScale /= info->_prescale;
634 
635 	info->_image = g_screen->screenScale(unscaled, imageScale, info->_tiles, 1);
636 
637 	delete unscaled;
638 	return info;
639 }
640 
getSubImage(const Common::String & name)641 SubImage *ImageMgr::getSubImage(const Common::String &name) {
642 	Common::String setname;
643 
644 	ImageSet *set = _baseSet;
645 
646 	while (set != nullptr) {
647 		for (Std::map<Common::String, ImageInfo *>::iterator i = set->_info.begin(); i != set->_info.end(); i++) {
648 			ImageInfo *info = (ImageInfo *) i->_value;
649 			Std::map<Common::String, SubImage *>::iterator j = info->_subImages.find(name);
650 			if (j != info->_subImages.end())
651 				return j->_value;
652 		}
653 
654 		set = getSet(set->_extends);
655 	}
656 
657 	return nullptr;
658 }
659 
freeIntroBackgrounds()660 void ImageMgr::freeIntroBackgrounds() {
661 	for (Std::map<Common::String, ImageSet *>::iterator i = _imageSets.begin(); i != _imageSets.end(); i++) {
662 		ImageSet *set = i->_value;
663 		for (Std::map<Common::String, ImageInfo *>::iterator j = set->_info.begin(); j != set->_info.end(); j++) {
664 			ImageInfo *info = j->_value;
665 			if (info->_image != nullptr && info->_introOnly) {
666 				delete info->_image;
667 				info->_image = nullptr;
668 			}
669 		}
670 	}
671 }
672 
getSetNames()673 const Std::vector<Common::String> &ImageMgr::getSetNames() {
674 	return _imageSetNames;
675 }
676 
update(Settings * newSettings)677 void ImageMgr::update(Settings *newSettings) {
678 	Common::String setname;
679 
680 	setname = newSettings->_videoType;
681 
682 	_baseSet = getSet(setname);
683 }
684 
~ImageSet()685 ImageSet::~ImageSet() {
686 	for (Std::map<Common::String, ImageInfo *>::iterator i = _info.begin(); i != _info.end(); i++) {
687 		ImageInfo *imageInfo = i->_value;
688 		if (imageInfo->_name != "screen")
689 			delete imageInfo;
690 	}
691 }
692 
~ImageInfo()693 ImageInfo::~ImageInfo() {
694 	for (Std::map<Common::String, SubImage *>::iterator i = _subImages.begin(); i != _subImages.end(); i++)
695 		delete i->_value;
696 	if (_image != nullptr)
697 		delete _image;
698 }
699 
700 } // End of namespace Ultima4
701 } // End of namespace Ultima
702