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 "graphics/managed_surface.h"
24 #include "common/algorithm.h"
25 #include "common/textconsole.h"
26 
27 namespace Graphics {
28 
29 const int SCALE_THRESHOLD = 0x100;
30 
ManagedSurface()31 ManagedSurface::ManagedSurface() :
32 		w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
33 		_disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
34 }
35 
ManagedSurface(ManagedSurface & surf)36 ManagedSurface::ManagedSurface(ManagedSurface &surf) :
37 		w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
38 		_disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
39 	*this = surf;
40 }
41 
ManagedSurface(int width,int height)42 ManagedSurface::ManagedSurface(int width, int height) :
43 		w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
44 		_disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
45 	create(width, height);
46 }
47 
ManagedSurface(int width,int height,const Graphics::PixelFormat & pixelFormat)48 ManagedSurface::ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat) :
49 		w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
50 		_disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
51 	create(width, height, pixelFormat);
52 }
53 
ManagedSurface(ManagedSurface & surf,const Common::Rect & bounds)54 ManagedSurface::ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds) :
55 		w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format),
56 		_disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) {
57 	create(surf, bounds);
58 }
59 
~ManagedSurface()60 ManagedSurface::~ManagedSurface() {
61 	free();
62 }
63 
operator =(ManagedSurface & surf)64 ManagedSurface &ManagedSurface::operator=(ManagedSurface &surf) {
65 	// Free any current surface
66 	free();
67 
68 	if (surf._disposeAfterUse == DisposeAfterUse::YES) {
69 		// Create a new surface and copy the pixels from the source surface
70 		create(surf.w, surf.h, surf.format);
71 		Common::copy((const byte *)surf.getPixels(), (const byte *)surf.getPixels() +
72 			surf.w * surf.h * surf.format.bytesPerPixel, (byte *)this->getPixels());
73 	} else {
74 		// Source isn't managed, so simply copy its fields
75 		_owner = surf._owner;
76 		_offsetFromOwner = surf._offsetFromOwner;
77 		void *srcPixels = surf._innerSurface.getPixels();
78 		_innerSurface.setPixels(srcPixels);
79 		_innerSurface.w = surf.w;
80 		_innerSurface.h = surf.h;
81 		_innerSurface.pitch = surf.pitch;
82 		this->format = surf.format;
83 	}
84 
85 	return *this;
86 }
87 
setPixels(void * newPixels)88 void ManagedSurface::setPixels(void *newPixels) {
89 	free();
90 	_innerSurface.setPixels(newPixels);
91 }
92 
create(uint16 width,uint16 height)93 void ManagedSurface::create(uint16 width, uint16 height) {
94 	create(width, height, PixelFormat::createFormatCLUT8());
95 }
96 
create(uint16 width,uint16 height,const PixelFormat & pixelFormat)97 void ManagedSurface::create(uint16 width, uint16 height, const PixelFormat &pixelFormat) {
98 	free();
99 	_innerSurface.create(width, height, pixelFormat);
100 
101 	_disposeAfterUse = DisposeAfterUse::YES;
102 	markAllDirty();
103 }
104 
create(ManagedSurface & surf,const Common::Rect & bounds)105 void ManagedSurface::create(ManagedSurface &surf, const Common::Rect &bounds) {
106 	free();
107 
108 	_offsetFromOwner = Common::Point(bounds.left, bounds.top);
109 	_innerSurface.setPixels(surf.getBasePtr(bounds.left, bounds.top));
110 	_innerSurface.pitch = surf.pitch;
111 	_innerSurface.format = surf.format;
112 	_innerSurface.w = bounds.width();
113 	_innerSurface.h = bounds.height();
114 	_owner = &surf;
115 	_disposeAfterUse = DisposeAfterUse::NO;
116 }
117 
free()118 void ManagedSurface::free() {
119 	if (_disposeAfterUse == DisposeAfterUse::YES)
120 		_innerSurface.free();
121 
122 	_disposeAfterUse = DisposeAfterUse::NO;
123 	_owner = nullptr;
124 	_offsetFromOwner = Common::Point(0, 0);
125 }
126 
clip(Common::Rect & srcBounds,Common::Rect & destBounds)127 bool ManagedSurface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) {
128 	if (destBounds.left >= this->w || destBounds.top >= this->h ||
129 			destBounds.right <= 0 || destBounds.bottom <= 0)
130 		return false;
131 
132 	// Clip the bounds if necessary to fit on-screen
133 	if (destBounds.right > this->w) {
134 		srcBounds.right -= destBounds.right - this->w;
135 		destBounds.right = this->w;
136 	}
137 
138 	if (destBounds.bottom > this->h) {
139 		srcBounds.bottom -= destBounds.bottom - this->h;
140 		destBounds.bottom = this->h;
141 	}
142 
143 	if (destBounds.top < 0) {
144 		srcBounds.top += -destBounds.top;
145 		destBounds.top = 0;
146 	}
147 
148 	if (destBounds.left < 0) {
149 		srcBounds.left += -destBounds.left;
150 		destBounds.left = 0;
151 	}
152 
153 	return true;
154 }
155 
blitFrom(const Surface & src)156 void ManagedSurface::blitFrom(const Surface &src) {
157 	blitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0));
158 }
159 
blitFrom(const Surface & src,const Common::Point & destPos)160 void ManagedSurface::blitFrom(const Surface &src, const Common::Point &destPos) {
161 	blitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos);
162 }
163 
blitFrom(const Surface & src,const Common::Rect & srcRect,const Common::Point & destPos)164 void ManagedSurface::blitFrom(const Surface &src, const Common::Rect &srcRect,
165 		const Common::Point &destPos) {
166 	Common::Rect srcBounds = srcRect;
167 	Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(),
168 		destPos.y + srcRect.height());
169 	uint destPixel;
170 	byte rSrc, gSrc, bSrc, aSrc;
171 	byte rDest, gDest, bDest;
172 	double alpha;
173 
174 	if (!srcRect.isValidRect() || !clip(srcBounds, destBounds))
175 		return;
176 
177 	if (format != src.format) {
178 		// When the pixel format differs, both source an dest must be
179 		// 2 or 4 bytes per pixel
180 		assert(format.bytesPerPixel == 2 || format.bytesPerPixel == 4);
181 		assert(src.format.bytesPerPixel == 2 || src.format.bytesPerPixel == 4);
182 	}
183 
184 	for (int y = 0; y < srcBounds.height(); ++y) {
185 		const byte *srcP = (const byte *)src.getBasePtr(srcBounds.left, srcBounds.top + y);
186 		byte *destP = (byte *)getBasePtr(destBounds.left, destBounds.top + y);
187 
188 		if (src.format == format) {
189 			// Matching surface formats, so we can do a straight copy
190 			Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP);
191 		} else {
192 			for (int x = 0; x < srcBounds.width(); ++x,
193 					srcP += src.format.bytesPerPixel,
194 					destP += format.bytesPerPixel) {
195 				src.format.colorToARGB(src.format.bytesPerPixel == 2 ? *(const uint16 *)srcP : *(const uint32 *)srcP,
196 					aSrc, rSrc, gSrc, bSrc);
197 				format.colorToRGB(format.bytesPerPixel == 2 ? *(const uint16 *)destP : *(const uint32 *)destP,
198 					rDest, gDest, bDest);
199 
200 				if (aSrc == 0) {
201 					// Completely transparent, so skip
202 					continue;
203 				} else if (aSrc == 0xff) {
204 					// Completely opaque, so copy RGB values over
205 					rDest = rSrc;
206 					gDest = gSrc;
207 					bDest = bSrc;
208 				} else {
209 					// Partially transparent, so calculate new pixel colors
210 					alpha = (double)aSrc / 255.0;
211 					rDest = static_cast<byte>((rSrc * alpha) + (rDest * (1.0 - alpha)));
212 					gDest = static_cast<byte>((gSrc * alpha) + (gDest * (1.0 - alpha)));
213 					bDest = static_cast<byte>((bSrc * alpha) + (bDest * (1.0 - alpha)));
214 				}
215 
216 				destPixel = format.ARGBToColor(0xff, rDest, gDest, bDest);
217 				if (format.bytesPerPixel == 2)
218 					*(uint16 *)destP = destPixel;
219 				else
220 					*(uint32 *)destP = destPixel;
221 			}
222 		}
223 	}
224 
225 	addDirtyRect(Common::Rect(0, 0, this->w, this->h));
226 }
227 
transBlitFrom(const Surface & src,uint transColor,bool flipped,uint overrideColor)228 void ManagedSurface::transBlitFrom(const Surface &src, uint transColor, bool flipped, uint overrideColor) {
229 	transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(0, 0, this->w, this->h),
230 		transColor, false, overrideColor);
231 }
232 
transBlitFrom(const Surface & src,const Common::Point & destPos,uint transColor,bool flipped,uint overrideColor)233 void ManagedSurface::transBlitFrom(const Surface &src, const Common::Point &destPos,
234 		uint transColor, bool flipped, uint overrideColor) {
235 	transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(destPos.x, destPos.y,
236 		destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
237 }
238 
transBlitFrom(const Surface & src,const Common::Rect & srcRect,const Common::Point & destPos,uint transColor,bool flipped,uint overrideColor)239 void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
240 		const Common::Point &destPos, uint transColor, bool flipped, uint overrideColor) {
241 	transBlitFrom(src, srcRect, Common::Rect(destPos.x, destPos.y,
242 		destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor);
243 }
244 
245 template<typename TSRC, typename TDEST>
transBlit(const Surface & src,const Common::Rect & srcRect,Surface & dest,const Common::Rect & destRect,TSRC transColor,bool flipped,uint overrideColor)246 void transBlit(const Surface &src, const Common::Rect &srcRect, Surface &dest, const Common::Rect &destRect, TSRC transColor, bool flipped, uint overrideColor) {
247 	int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width();
248 	int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height();
249 	const Graphics::PixelFormat &srcFormat = src.format;
250 	const Graphics::PixelFormat &destFormat = dest.format;
251 	byte aSrc, rSrc, gSrc, bSrc;
252 	byte rDest, gDest, bDest;
253 	double alpha;
254 
255 	// Loop through drawing output lines
256 	for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) {
257 		if (destY < 0 || destY >= dest.h)
258 			continue;
259 		const TSRC *srcLine = (const TSRC *)src.getBasePtr(srcRect.left, scaleYCtr / SCALE_THRESHOLD + srcRect.top);
260 		TDEST *destLine = (TDEST *)dest.getBasePtr(destRect.left, destY);
261 
262 		// Loop through drawing the pixels of the row
263 		for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) {
264 			if (destX < 0 || destX >= dest.w)
265 				continue;
266 
267 			TSRC srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD];
268 			if (srcVal == transColor)
269 				continue;
270 
271 			if (srcFormat == destFormat) {
272 				// Matching formats, so we can do a straight copy
273 				destLine[xCtr] = overrideColor ? overrideColor : srcVal;
274 			} else {
275 				// Otherwise we have to manually decode and re-encode each pixel
276 				srcFormat.colorToARGB(*srcLine, aSrc, rSrc, gSrc, bSrc);
277 				destFormat.colorToRGB(destLine[xCtr], rDest, gDest, bDest);
278 
279 				if (aSrc == 0) {
280 					// Completely transparent, so skip
281 					continue;
282 				} else if (aSrc == 0xff) {
283 					// Completely opaque, so copy RGB values over
284 					rDest = rSrc;
285 					gDest = gSrc;
286 					bDest = bSrc;
287 				} else {
288 					// Partially transparent, so calculate new pixel colors
289 					alpha = (double)aSrc / 255.0;
290 					rDest = static_cast<byte>((rSrc * alpha) + (rDest * (1.0 - alpha)));
291 					gDest = static_cast<byte>((gSrc * alpha) + (gDest * (1.0 - alpha)));
292 					bDest = static_cast<byte>((bSrc * alpha) + (bDest * (1.0 - alpha)));
293 				}
294 
295 				destLine[xCtr] = destFormat.ARGBToColor(0xff, rDest, gDest, bDest);
296 			}
297 		}
298 	}
299 }
300 
301 #define HANDLE_BLIT(SRC_BYTES, DEST_BYTES, SRC_TYPE, DEST_TYPE) \
302 	if (src.format.bytesPerPixel == SRC_BYTES && format.bytesPerPixel == DEST_BYTES) \
303 		transBlit<SRC_TYPE, DEST_TYPE>(src, srcRect, _innerSurface, destRect, transColor, flipped, overrideColor); \
304 	else
305 
transBlitFrom(const Surface & src,const Common::Rect & srcRect,const Common::Rect & destRect,uint transColor,bool flipped,uint overrideColor)306 void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect,
307 	const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) {
308 	if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0)
309 		return;
310 
311 	HANDLE_BLIT(1, 1, byte, byte)
312 	HANDLE_BLIT(2, 2, uint16, uint16)
313 	HANDLE_BLIT(4, 4, uint32, uint32)
314 	HANDLE_BLIT(2, 4, uint16, uint32)
315 	HANDLE_BLIT(4, 2, uint32, uint16)
316 		error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4");
317 
318 	// Mark the affected area
319 	addDirtyRect(destRect);
320 }
321 
322 #undef HANDLE_BLIT
323 
markAllDirty()324 void ManagedSurface::markAllDirty() {
325 	addDirtyRect(Common::Rect(0, 0, this->w, this->h));
326 }
327 
addDirtyRect(const Common::Rect & r)328 void ManagedSurface::addDirtyRect(const Common::Rect &r) {
329 	if (_owner) {
330 		Common::Rect bounds = r;
331 		bounds.clip(Common::Rect(0, 0, this->w, this->h));
332 		bounds.translate(_offsetFromOwner.x, _offsetFromOwner.y);
333 		_owner->addDirtyRect(bounds);
334 	}
335 }
336 
clear(uint color)337 void ManagedSurface::clear(uint color) {
338 	fillRect(getBounds(), color);
339 }
340 
341 } // End of namespace Graphics
342