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