File: walk_behind.cpp

package info (click to toggle)
scummvm 2.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 450,580 kB
  • sloc: cpp: 4,299,825; asm: 28,322; python: 12,901; sh: 11,302; java: 9,289; xml: 7,895; perl: 2,639; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (165 lines) | stat: -rw-r--r-- 5,749 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "common/std/algorithm.h"
#include "ags/engine/ac/walk_behind.h"
#include "ags/shared/ac/common.h"
#include "ags/shared/ac/common_defines.h"
#include "ags/engine/ac/room_status.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/gfx/graphics_driver.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/globals.h"

namespace AGS3 {

using namespace AGS::Shared;
using namespace AGS::Engine;

// Generates walk-behinds as separate sprites
void walkbehinds_generate_sprites() {
	const Bitmap *mask = _GP(thisroom).WalkBehindMask.get();
	const Bitmap *bg = _GP(thisroom).BgFrames[_GP(play).bg_frame].Graphic.get();

	const int coldepth = bg->GetColorDepth();
	Bitmap wbbmp; // temp buffer
	// Iterate through walk-behinds and generate a texture for each of them
	for (int wb = 1 /* 0 is "no area" */; wb < MAX_WALK_BEHINDS; ++wb) {
		const Rect pos = _G(walkBehindAABB)[wb];
		if (pos.Right > 0) {
			wbbmp.CreateTransparent(pos.GetWidth(), pos.GetHeight(), coldepth);
			// Copy over all solid pixels belonging to this WB area
			const int sx = pos.Left, ex = pos.Right, sy = pos.Top, ey = pos.Bottom;
			for (int y = sy; y <= ey; ++y) {
				const uint8_t *check_line = mask->GetScanLine(y);
				const uint8_t *src_line = bg->GetScanLine(y);
				uint8_t *dst_line = wbbmp.GetScanLineForWriting(y - sy);
				for (int x = sx; x <= ex; ++x) {
					if (check_line[x] != wb) continue;
					switch (coldepth) {
					case 8:
						dst_line[(x - sx)] = src_line[x];
						break;
					case 16:
						reinterpret_cast<uint16_t *>(dst_line)[(x - sx)] =
							reinterpret_cast<const uint16_t *>(src_line)[x];
						break;
					case 32:
						reinterpret_cast<uint32_t *>(dst_line)[(x - sx)] =
							reinterpret_cast<const uint32_t *>(src_line)[x];
						break;
					default: assert(0); break;
					}
				}
			}
			// Add to walk-behinds image list
			add_walkbehind_image(wb, &wbbmp, pos.Left, pos.Top);
		}
	}

	_G(walkBehindsCachedForBgNum) = _GP(play).bg_frame;
}

// Edits the given game object's sprite, cutting out pixels covered by walk-behinds;
// returns whether any pixels were updated;
bool walkbehinds_cropout(Bitmap *sprit, int sprx, int spry, int basel) {
	if (_G(noWalkBehindsAtAll))
		return false;

	const int maskcol = sprit->GetMaskColor();
	const int spcoldep = sprit->GetColorDepth();

	bool pixels_changed = false;
	// pass along the sprite's pixels, but skip those that lie outside the mask
	for (int x = MAX(0, 0 - sprx);
		(x < sprit->GetWidth()) && (x + sprx < _GP(thisroom).WalkBehindMask->GetWidth()); ++x) {
		// select the WB column at this x
		const auto &wbcol = _G(walkBehindCols)[x + sprx];
		// skip if no area, or sprite lies outside of all areas in this column
		if ((!wbcol.Exists) ||
			(wbcol.Y2 <= spry) ||
			(wbcol.Y1 > spry + sprit->GetHeight()))
			continue;

		// ensure we only check within the valid areas (between Y1 and Y2)
		// we assume that Y1 and Y2 are always within the mask
		for (int y = MAX(0, wbcol.Y1 - spry);
				(y < sprit->GetHeight()) && (y + spry < wbcol.Y2); ++y) {
			const int wb = _GP(thisroom).WalkBehindMask->GetScanLine(y + spry)[x + sprx];
			if (wb < 1) continue; // "no area"
			if (_G(croom)->walkbehind_base[wb] <= basel) continue;

			pixels_changed = true;
			uint8_t *dst_line = sprit->GetScanLineForWriting(y);
			switch (spcoldep) {
			case 8:
				dst_line[x] = maskcol;
				break;
			case 16:
				reinterpret_cast<uint16_t *>(dst_line)[x] = maskcol;
				break;
			case 32:
				reinterpret_cast<uint32_t *>(dst_line)[x] = maskcol;
				break;
			default:
				assert(0);
				break;
			}
		}
	}
	return pixels_changed;
}

void walkbehinds_recalc() {
	// Reset all data
	_G(walkBehindCols).clear();
	for (int wb = 0; wb < MAX_WALK_BEHINDS; ++wb) {
		_G(walkBehindAABB)[wb] = Rect(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN);
	}
	_G(noWalkBehindsAtAll) = true;

	// Recalculate everything; note that mask is always 8-bit
	const Bitmap *mask = _GP(thisroom).WalkBehindMask.get();
	_G(walkBehindCols).resize(mask->GetWidth());
	for (int col = 0; col < mask->GetWidth(); ++col) {
		auto &wbcol = _G(walkBehindCols)[col];
		for (int y = 0; y < mask->GetHeight(); ++y) {
			int wb = mask->GetScanLine(y)[col];
			// Valid areas start with index 1, 0 = no area
			if ((wb >= 1) && (wb < MAX_WALK_BEHINDS)) {
				if (!wbcol.Exists) {
					wbcol.Y1 = y;
					wbcol.Exists = true;
					_G(noWalkBehindsAtAll) = false;
				}
				wbcol.Y2 = y + 1; // +1 to allow bottom line of screen to work (CHECKME??)
				// resize the bounding rect
				_G(walkBehindAABB)[wb].Left = MIN(col, _G(walkBehindAABB)[wb].Left);
				_G(walkBehindAABB)[wb].Top = MIN(y, _G(walkBehindAABB)[wb].Top);
				_G(walkBehindAABB)[wb].Right = MAX(col, _G(walkBehindAABB)[wb].Right);
				_G(walkBehindAABB)[wb].Bottom = MAX(y, _G(walkBehindAABB)[wb].Bottom);
			}
		}
	}
	_G(walkBehindsCachedForBgNum) = -1;
}

} // namespace AGS3