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