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 "glk/picture.h"
24 #include "glk/glk.h"
25 #include "glk/raw_decoder.h"
26 #include "glk/screen.h"
27 #include "common/file.h"
28 #include "image/jpeg.h"
29 #include "image/png.h"
30 
31 namespace Glk {
32 
Pictures()33 Pictures::Pictures() : _refCount(0) {
34 	Common::File f;
35 	if (f.open("apal")) {
36 		while (f.pos() < f.size())
37 			_adaptivePics.push_back(
38 				Common::String::format("%u", f.readUint32BE()));
39 	}
40 }
41 
clear()42 void Pictures::clear() {
43 	for (uint idx = 0; idx < _store.size(); ++idx) {
44 		if (_store[idx]._picture)
45 			_store[idx]._picture->decrement();
46 		if (_store[idx]._scaled)
47 			_store[idx]._scaled->decrement();
48 	}
49 
50 	_store.clear();
51 }
52 
increment()53 void Pictures::increment() {
54 	++_refCount;
55 }
56 
decrement()57 void Pictures::decrement() {
58 	if (_refCount > 0 && --_refCount == 0)
59 		clear();
60 }
61 
search(const Common::String & name)62 PictureEntry *Pictures::search(const Common::String &name) {
63 	Picture *pic;
64 
65 	for (uint idx = 0; idx < _store.size(); ++idx) {
66 		pic = _store[idx]._picture;
67 
68 		if (pic && pic->_name.equalsIgnoreCase(name))
69 			return &_store[idx];
70 	}
71 
72 	return nullptr;
73 }
74 
storeOriginal(Picture * pic)75 void Pictures::storeOriginal(Picture *pic) {
76 	PictureEntry newPic;
77 	newPic._picture = pic;
78 
79 	_store.push_back(newPic);
80 }
81 
storeScaled(Picture * pic)82 void Pictures::storeScaled(Picture *pic) {
83 	PictureEntry *entry = search(pic->_name);
84 	if (!entry)
85 		return;
86 
87 	delete entry->_scaled;
88 	entry->_scaled = pic;
89 }
90 
store(Picture * pic)91 void Pictures::store(Picture *pic) {
92 	if (!pic)
93 		return;
94 
95 	if (!pic->_scaled)
96 		storeOriginal(pic);
97 	else
98 		storeScaled(pic);
99 }
100 
retrieve(const Common::String & name,bool scaled)101 Picture *Pictures::retrieve(const Common::String &name, bool scaled) {
102 	Picture *pic;
103 
104 	for (uint idx = 0; idx < _store.size(); ++idx) {
105 		pic = scaled ? _store[idx]._scaled : _store[idx]._picture;
106 
107 		if (pic && pic->_name.equalsIgnoreCase(name))
108 			return pic;
109 	}
110 
111 	return nullptr;
112 }
113 
load(const Common::String & name)114 Picture *Pictures::load(const Common::String &name) {
115 	::Image::PNGDecoder png;
116 	::Image::JPEGDecoder jpg;
117 	Graphics::Surface rectImg;
118 	RawDecoder raw;
119 	const Graphics::Surface *img;
120 	const byte *palette = nullptr;
121 	uint palCount = 0;
122 	int transColor = -1;
123 	Picture *pic;
124 
125 	// Check if the picture is already in the store
126 	pic = retrieve(name, false);
127 	if (pic)
128 		return pic;
129 
130 	Common::File f;
131 	if ((name.hasSuffixIgnoreCase(".png") && f.open(name))
132 		|| f.open(Common::String::format("pic%s.png", name.c_str()))
133 		|| f.open(Common::String::format("%s.png", name.c_str()))
134 	) {
135 		png.setKeepTransparencyPaletted(true);
136 		png.loadStream(f);
137 		img = png.getSurface();
138 		palette = png.getPalette();
139 		palCount = png.getPaletteColorCount();
140 		transColor = png.getTransparentColor();
141 	} else if (
142 		((name.hasSuffixIgnoreCase(".jpg") || name.hasSuffixIgnoreCase(".jpeg")) && f.open(name))
143 		|| f.open(Common::String::format("pic%s.jpg", name.c_str()))
144 		|| f.open(Common::String::format("pic%s.jpeg", name.c_str()))
145 		|| f.open(Common::String::format("%s.jpg", name.c_str()))
146 	) {
147 		jpg.setOutputPixelFormat(g_system->getScreenFormat());
148 		jpg.loadStream(f);
149 		img = jpg.getSurface();
150 	} else if ((name.hasSuffixIgnoreCase(".raw") && f.open(name)) ||
151 			f.open(Common::String::format("pic%s.raw", name.c_str()))) {
152 		raw.loadStream(f);
153 		img = raw.getSurface();
154 		palette = raw.getPalette();
155 		palCount = raw.getPaletteColorCount();
156 		transColor = raw.getTransparentColor();
157 	} else if (f.open(Common::String::format("pic%s.rect", name.c_str()))) {
158 		rectImg.w = f.readUint32BE();
159 		rectImg.h = f.readUint32BE();
160 		img = &rectImg;
161 	} else {
162 		// No such picture
163 		return nullptr;
164 	}
165 
166 	// Also check if it's going to be an adaptive pic
167 	bool isAdaptive = false;
168 	for (uint idx = 0; idx < _adaptivePics.size() && !isAdaptive; ++idx)
169 		isAdaptive = _adaptivePics[idx].equalsIgnoreCase(name);
170 
171 	if (isAdaptive) {
172 		// It is, so used previously saved palette
173 		assert(!_savedPalette.empty());
174 		palette = &_savedPalette[0];
175 		palCount = _savedPalette.size() / 3;
176 	} else if (palette) {
177 		// It's a picture with a valid palette, so save a copy of it for later
178 		_savedPalette.resize(palCount * 3);
179 		Common::copy(palette, palette + palCount * 3, &_savedPalette[0]);
180 	}
181 
182 	// Create new picture based on the image
183 	pic = new Picture(img->w, img->h, g_system->getScreenFormat());
184 	pic->_refCount = 1;
185 	pic->_name = name;
186 	pic->_scaled = false;
187 	if (transColor != -1 || (!palette && img->format.aBits() > 0))
188 		pic->clear(pic->getTransparentColor());
189 
190 	if (!img->getPixels()) {
191 		// Area definition without any content
192 	} else if (!palette) {
193 		pic->blitFrom(*img);
194 	} else {
195 		uint pal[256];
196 		for (uint idx = 0; idx < palCount; ++idx)
197 			pal[idx] = pic->format.RGBToColor(palette[idx * 3],
198 				palette[idx * 3 + 1], palette[idx * 3 + 2]);
199 
200 		const byte *srcP = (const byte *)img->getPixels();
201 		byte *destP = (byte *)pic->getPixels();
202 		for (int idx = 0; idx < img->w * img->h; ++idx, srcP++, destP += pic->format.bytesPerPixel) {
203 			if ((int)*srcP != transColor) {
204 				uint val = (*srcP >= palCount) ? 0 : pal[*srcP];
205 				if (pic->format.bytesPerPixel == 2)
206 					WRITE_LE_UINT16(destP, val);
207 				else
208 					WRITE_LE_UINT32(destP, val);
209 			}
210 		}
211 	}
212 
213 	store(pic);
214 	return pic;
215 }
216 
scale(Picture * src,size_t sx,size_t sy)217 Picture *Pictures::scale(Picture *src, size_t sx, size_t sy) {
218 	// Check for the presence of an already scaled version of that size
219 	Picture *dst = retrieve(src->_name, true);
220 	if (dst && (size_t)dst->w == sx && (size_t)dst->h == sy)
221 		return dst;
222 
223 	// Create a new picture of the destination size and rescale the source picture
224 	dst = new Picture(sx, sy, src->format);
225 	dst->_name = src->_name;
226 	dst->_scaled = true;
227 	dst->transBlitFrom(*src, src->getBounds(), dst->getBounds(), (uint)0x8888);
228 
229 	storeScaled(dst);
230 	return dst;
231 }
232 
233 /*--------------------------------------------------------------------------*/
234 
Picture(int width,int height,const Graphics::PixelFormat & fmt)235 Picture::Picture(int width, int height, const Graphics::PixelFormat &fmt) :
236 		Graphics::ManagedSurface(width, height, fmt), _refCount(0), _scaled(false) {
237 
238 	// Default transparent color chosen at random
239 	_transColor = format.RGBToColor(0x77, 0x77, 0x77);
240 }
241 
increment()242 void Picture::increment() {
243 	++_refCount;
244 }
245 
decrement()246 void Picture::decrement() {
247 	if (_refCount > 0 && --_refCount == 0) {
248 		// No longer any references to this picture, so remove it
249 		delete this;
250 	}
251 }
252 
drawPicture(const Common::Point & destPos,const Common::Rect & box)253 void Picture::drawPicture(const Common::Point &destPos, const Common::Rect &box) {
254 	Graphics::ManagedSurface s(*g_vm->_screen, box);
255 	Common::Point pt(destPos.x - box.left, destPos.y - box.top);
256 
257 	s.transBlitFrom(*this, pt, _transColor);
258 }
259 
260 } // End of namespace Glk
261