1/*
2 * ViewBuffer - Mimicing a frame buffer output - but using a BView.
3 * Based on frame_buffer_console.
4 *
5 * Copyright 2005 Michael Lotz. All rights reserved.
6 * Distributed under the MIT License.
7 *
8 * Copyright 2005, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
9 * Distributed under the terms of the MIT License.
10 */
11
12#include <string.h>
13
14#include "ViewBuffer.h"
15
16#define CHAR_WIDTH	7
17#define CHAR_HEIGHT	13
18
19// Palette is (black and white are swapt like in normal BeOS Terminal)
20//	0 - black,
21//	1 - blue,
22//	2 - green,
23//	3 - cyan,
24//	4 - red,
25//	5 - magenta,
26//	6 - yellow,
27//	7 - white
28//  8-15 - same but bright (we're ignoring those)
29
30static uint32 sPalette32[] = {
31	0xffffff,
32	0x0000ff,
33	0x00ff00,
34	0x00ffff,
35	0xff0000,
36	0xff00ff,
37	0xffff00,
38	0x000000,
39};
40
41
42ViewBuffer::ViewBuffer(BRect frame)
43	:	BView(frame, "ViewBuffer", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS),
44		fColumns(frame.IntegerWidth() / CHAR_WIDTH),
45		fRows(frame.IntegerHeight() / CHAR_HEIGHT),
46		fGlyphGrid(NULL),
47		fResizeCallback(NULL),
48		// initially, the cursor is hidden
49		fCursorX(-1),
50		fCursorY(-1)
51{
52	SetFont(be_fixed_font);
53
54	// initialize private palette
55	for (int i = 0; i < 8; i++) {
56		fPalette[i].red = (sPalette32[i] >> 16) & 0xff;
57		fPalette[i].green = (sPalette32[i] >> 8) & 0xff;
58		fPalette[i].blue = (sPalette32[i] >> 0) & 0xff;
59		fPalette[i].alpha = 0xff;
60	}
61
62	// initialize glyph grid
63	uint32 size = fRows * fColumns;
64	if (size > 0) {
65		fGlyphGrid = new uint16[size];
66		memset(fGlyphGrid, 0, size * sizeof(uint16));
67	}
68}
69
70
71ViewBuffer::~ViewBuffer()
72{
73	delete[] fGlyphGrid;
74}
75
76
77void
78ViewBuffer::FrameResized(float width, float height)
79{
80	int32 oldColumns = fColumns;
81	int32 oldRows = fRows;
82
83	fColumns = (int32)width / CHAR_WIDTH;
84	fRows = (int32)height / CHAR_HEIGHT;
85
86	// resize glyph grid
87	uint16* oldGlyphGrid = fGlyphGrid;
88	uint32 size = fRows * fColumns;
89	if (size > 0) {
90		fGlyphGrid = new uint16[size];
91		memset(fGlyphGrid, 0, size * sizeof(uint16));
92	} else
93		fGlyphGrid = NULL;
94	// transfer old glyph grid into new one
95	if (oldGlyphGrid && fGlyphGrid) {
96		int32 columns	= min_c(oldColumns, fColumns);
97		int32 rows		= min_c(oldRows, fRows);
98		for (int32 y = 0; y < rows; y++) {
99			for (int32 x = 0; x < columns; x++) {
100				fGlyphGrid[y * fColumns + x] = oldGlyphGrid[y * oldColumns + x];
101			}
102		}
103	}
104	delete[] oldGlyphGrid;
105
106	if (fResizeCallback)
107		fResizeCallback(fColumns, fRows, fResizeCallbackData);
108}
109
110
111status_t
112ViewBuffer::GetSize(int32 *width, int32 *height)
113{
114	*width = fColumns;
115	*height = fRows;
116	return B_OK;
117}
118
119
120void
121ViewBuffer::SetResizeCallback(resize_callback callback, void *data)
122{
123	fResizeCallback = callback;
124	fResizeCallbackData = data;
125}
126
127
128uint8
129ViewBuffer::ForegroundColor(uint8 attr)
130{
131	return attr & 0x7;
132}
133
134
135uint8
136ViewBuffer::BackgroundColor(uint8 attr)
137{
138	return (attr >> 4) & 0x7;
139}
140
141
142rgb_color
143ViewBuffer::GetPaletteEntry(uint8 index)
144{
145	return fPalette[index];
146}
147
148
149void
150ViewBuffer::PutGlyph(int32 x, int32 y, uint8 glyph, uint8 attr)
151{
152	if (x >= fColumns || y >= fRows)
153		return;
154
155	RenderGlyph(x, y, glyph, attr);
156}
157
158
159void
160ViewBuffer::FillGlyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph, uint8 attr)
161{
162	if (x >= fColumns || y >= fRows)
163		return;
164
165	int32 left = x + width;
166	if (left > fColumns)
167		left = fColumns;
168
169	int32 bottom = y + height;
170	if (bottom > fRows)
171		bottom = fRows;
172
173	for (; y < bottom; y++) {
174		for (int32 x2 = x; x2 < left; x2++) {
175			RenderGlyph(x2, y, glyph, attr);
176		}
177	}
178}
179
180
181void
182ViewBuffer::RenderGlyph(int32 x, int32 y, uint8 glyph, uint8 attr)
183{
184	char string[2];
185	string[0] = glyph;
186	string[1] = 0;
187
188	if (LockLooper()) {
189		_RenderGlyph(x, y, string, attr);
190		Sync();
191		UnlockLooper();
192	}
193	// remember the glyph in the grid
194	if (fGlyphGrid) {
195		fGlyphGrid[y * fColumns + x] = (glyph << 8) | attr;
196	}
197}
198
199
200void
201ViewBuffer::Draw(BRect updateRect)
202{
203	if (fGlyphGrid) {
204		int32 startX	= max_c(0, (int32)(updateRect.left / CHAR_WIDTH));
205		int32 endX		= min_c(fColumns - 1, (int32)(updateRect.right / CHAR_WIDTH) + 1);
206		int32 startY	= max_c(0, (int32)(updateRect.top / CHAR_HEIGHT));
207		int32 endY		= min_c(fRows - 1, (int32)(updateRect.bottom / CHAR_HEIGHT) + 1);
208
209		char string[2];
210		string[1] = 0;
211
212		for (int32 y = startY; y <= endY; y++) {
213			for (int32 x = startX; x <= endX; x++) {
214				uint16 grid = fGlyphGrid[y * fColumns + x];
215				uint8 glyph = grid >> 8;
216				uint8 attr = grid & 0x00ff;
217				string[0] = glyph;
218				_RenderGlyph(x, y, string, attr, false);
219			}
220		}
221	}
222
223	DrawCursor(fCursorX, fCursorY);
224}
225
226
227void
228ViewBuffer::DrawCursor(int32 x, int32 y)
229{
230	if (x < 0 || y < 0)
231		return;
232
233	x *= CHAR_WIDTH;
234	y *= CHAR_HEIGHT;
235
236	if (LockLooper()) {
237		InvertRect(BRect(x, y, x + CHAR_WIDTH, y + CHAR_HEIGHT));
238		Sync();
239		UnlockLooper();
240	}
241}
242
243
244void
245ViewBuffer::MoveCursor(int32 x, int32 y)
246{
247	DrawCursor(fCursorX, fCursorY);
248	DrawCursor(x, y);
249
250	fCursorX = x;
251	fCursorY = y;
252}
253
254
255void
256ViewBuffer::Blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx, int32 desty)
257{
258	// blit inside the glyph grid
259	if (fGlyphGrid) {
260		int32 xOffset = destx - srcx;
261		int32 yOffset = desty - srcy;
262
263		int32 xIncrement;
264		int32 yIncrement;
265
266		uint16* src = fGlyphGrid + srcy * fColumns + srcx;
267
268		if (xOffset > 0) {
269			// copy from right to left
270			xIncrement = -1;
271			src += width - 1;
272		} else {
273			// copy from left to right
274			xIncrement = 1;
275		}
276
277		if (yOffset > 0) {
278			// copy from bottom to top
279			yIncrement = -fColumns;
280			src += (height - 1) * fColumns;
281		} else {
282			// copy from top to bottom
283			yIncrement = fColumns;
284		}
285
286		uint16* dst = src + yOffset * fColumns + xOffset;
287
288		for (int32 y = 0; y < height; y++) {
289			uint16* srcHandle = src;
290			uint16* dstHandle = dst;
291			for (int32 x = 0; x < width; x++) {
292				*dstHandle = *srcHandle;
293				srcHandle += xIncrement;
294				dstHandle += xIncrement;
295			}
296			src += yIncrement;
297			dst += yIncrement;
298		}
299	}
300
301	height *= CHAR_HEIGHT;
302	width *= CHAR_WIDTH;
303
304	srcx *= CHAR_WIDTH;
305	srcy *= CHAR_HEIGHT;
306	BRect source(srcx, srcy, srcx + width, srcy + height);
307
308	destx *= CHAR_WIDTH;
309	desty *= CHAR_HEIGHT;
310	BRect dest(destx, desty, destx + width, desty + height);
311
312	if (LockLooper()) {
313		CopyBits(source, dest);
314		Sync();
315		UnlockLooper();
316	}
317}
318
319
320void
321ViewBuffer::Clear(uint8 attr)
322{
323	if (LockLooper()) {
324		SetLowColor(GetPaletteEntry(BackgroundColor(attr)));
325		SetViewColor(LowColor());
326		FillRect(Frame(), B_SOLID_LOW);
327		Sync();
328		UnlockLooper();
329	}
330
331	fCursorX = -1;
332	fCursorY = -1;
333
334	if (fGlyphGrid)
335		memset(fGlyphGrid, 0, fRows * fColumns * sizeof(uint16));
336}
337
338
339void
340ViewBuffer::_RenderGlyph(int32 x, int32 y, const char* string, uint8 attr, bool fill)
341{
342	BPoint where(x * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT - 3);
343
344	SetHighColor(GetPaletteEntry(ForegroundColor(attr)));
345	if (fill) {
346		SetLowColor(GetPaletteEntry(BackgroundColor(attr)));
347		FillRect(BRect(x * CHAR_WIDTH, y * CHAR_HEIGHT,
348					   (x + 1) * CHAR_WIDTH, (y + 1) * CHAR_HEIGHT),
349				 B_SOLID_LOW);
350	}
351	DrawString(string, where);
352}
353
354