1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id: screen.c 2268 2006-11-06 17:18:51Z roms $ */
3 
4 /*  TiEmu - Tiemu Is an EMUlator
5  *
6  *  Copyright (c) 2000-2001, Thomas Corvazier, Romain Lievin
7  *  Copyright (c) 2001-2003, Romain Lievin
8  *  Copyright (c) 2003, Julien Blache
9  *  Copyright (c) 2004, Romain Li�vin
10  *  Copyright (c) 2005, Romain Li�vin
11  *  Copyright (c) 2006, Kevin Kofler
12  *
13  *  This program is free software; you can redistribute it and/or modify
14  *  it under the terms of the GNU General Public License as published by
15  *  the Free Software Foundation; either version 2 of the License, or
16  *  (at your option) any later version.
17  *
18  *  This program is distributed in the hope that it will be useful,
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *  GNU General Public License for more details.
22  *
23  *  You should have received a copy of the GNU General Public License
24  *  along with this program; if not, write to the Free Software
25  *  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #  include <config.h>
30 #endif				/*  */
31 
32 #include <gtk/gtk.h>
33 #include <glade/glade.h>
34 #include <gdk-pixbuf/gdk-pixbuf.h>
35 #include <string.h>
36 
37 #include "intl.h"
38 #include "paths.h"
39 #include "skinops.h"
40 #include "support.h"
41 #include "ti68k_int.h"
42 #include "struct.h"
43 #include "tie_error.h"
44 #include "calc.h"
45 #include "gscales.h"
46 
47 /* Types */
48 
49 #define BPP 8               // 8 bits per pixel
50 #define NGS 16              // number of gray scales (contrast level)
51 
52 #define SCREEN_ON   (!0)
53 #define SCREEN_OFF	0
54 
55 /* Variables */
56 
57 extern GtkWidget *main_wnd;
58 extern GtkWidget *area;
59 
60 extern SKIN_INFOS skin_infos;
61 
62 GdkPixbuf *lcd_mem = NULL;
63 GdkPixbuf *lcd = NULL;
64 GdkPixmap *pixmap = NULL;
65 
66 uint32_t*	lcd_bytmap;				// LCD buffer (color-mapped as grayscale)
67 
68 LCD_INFOS	li;
69 float		sf;	// scaling factor
70 
71 LCD_RECT	ls;	// LCD rectangle in skin
72 LCD_RECT	lr;	// LCD rectangle in window
73 SKN_RECT	sr;	// skin rectangle
74 union {
75   WND_RECT wr;
76   GdkRectangle gr;
77 }	wr;	// window rectangle
78 
79 static uint32_t convtab[512];      	// planar to chunky conversion table
80 static RGB      grayscales[16];		// gray scales rgb values (colormap)
81 static int		lcd_state = -1;     // screen state
82 
83 static int contrast = NGS;          // current contrast level
84 static int old_contrast = 0;        // previous contrast level
85 static int new_contrast = NGS;		// new contrast level
86 
87 static int max_plane = 0;         	// number of grayscales to emulate
88 
89 int shot_cnt = 0;					// number of captures
90 int	skip_cnt = 0;					// number of frames to skip
91 
92 /* Compute conversion table */
compute_convtable(void)93 void compute_convtable(void)
94 {
95   	int i, j;
96   	uint8_t k;
97   	uint8_t *tab = (uint8_t *)convtab;
98 
99   	for(i=0, j=0; i<256; i++)
100     {
101       	for(k = 1<<7; k; k>>=1)
102 		{
103 			tab[j++] = (i & k) ? 1 : 0;
104 		}
105     }
106 }
107 
108 /* The value v is between l and h and can not be outside */
109 #define filter(v, l, h) (v<l ? l : (v>h ? h : v))
110 
111 /* Computes the 16 grays level colors */
compute_grayscale(void)112 void compute_grayscale(void)
113 {
114   	int i;
115   	int sr, sg, sb;
116   	int er, eg, eb;
117   	int r, g ,b;
118     uint32_t white = skin_infos.lcd_white;  // 0xcfe0ce
119     uint32_t black = skin_infos.lcd_black;  // 0x222e31
120 
121 	//printf("# planes: %i | contrast: %i\n", max_plane, contrast);
122 
123 	// Compute RBG bsaic values
124   	sr = (white & 0xff0000) >> 8;
125   	sg = (white & 0x00ff00);
126   	sb = (white & 0x0000ff) << 8;
127 
128   	er = (black & 0xff0000) >> 8;
129   	eg = (black & 0x00ff00);
130   	eb = (black & 0x0000ff) << 8;
131 
132 	// Compute RGB values tuned with contrast
133   	if(contrast < NGS)
134     {
135       	sr = sr - (sr-er) * (NGS - contrast)/NGS;
136       	sg = sg - (sg-eg) * (NGS - contrast)/NGS;
137       	sb = sb - (sb-eb) * (NGS - contrast)/NGS;
138     }
139   	else
140     {
141       	er = er - (er-sr)*(contrast - NGS)/NGS;
142       	eg = eg - (eg-sg)*(contrast - NGS)/NGS;
143       	eb = eb - (eb-sb)*(contrast - NGS)/NGS;
144     }
145 
146 	//printf("a: %02x%02x%02x | %02x%02x%02x\n", sr>>8, sg>>8, sb>>8, er>>8, eg>>8, eb>>8);
147 
148   	r = sr;
149   	g = sg;
150   	b = sb;
151 
152   	if(lcd_state)
153     {
154       	for(i = 0; i <= (max_plane+1); i++)
155 		{
156 	  		grayscales[i].r = filter(r, 0x0000, 0xfff0) >> 8;
157 	  		grayscales[i].g = filter(g, 0x0000, 0xff00) >> 8;
158 	  		grayscales[i].b = filter(b, 0x0000, 0xff00) >> 8;
159 
160 	  		r -= ((sr-er) / (max_plane+1));
161 	  		g -= ((sg-eg) / (max_plane+1));
162 	  		b -= ((sb-eb) / (max_plane+1));
163 		}
164     }
165 
166 	// Compute grayscale palette
167 	if(!max_plane)
168 		return;
169 
170 	for(i = 0; i <= max_plane; i++)
171 	{
172 		grayscales[i].r = ((sr-er)/max_plane * (max_plane-i) + er) >> 8;
173 		grayscales[i].g = ((sg-eg)/max_plane * (max_plane-i) + eg) >> 8;
174 		grayscales[i].b = ((sb-eb)/max_plane * (max_plane-i) + eb) >> 8;
175 	}
176 }
177 
178 /* Redraw the skin into window but don't reload skin file */
redraw_skin(void)179 void redraw_skin(void)
180 {
181 	// no skin ?
182   	if(!options.skin)
183     	return;
184 
185 	// scale image
186 	g_object_unref(skin_infos.image);
187 	skin_infos.image = gdk_pixbuf_scale_simple(skin_infos.raw, wr.wr.w, wr.wr.h, GDK_INTERP_NEAREST);
188 
189 	// and draw image into pixmap (next, into window on expose event)
190     gdk_draw_pixbuf(pixmap, main_wnd->style->fg_gc[GTK_WIDGET_STATE(main_wnd)],
191 		  skin_infos.image, 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
192 	gdk_window_invalidate_rect(main_wnd->window, &wr.gr, FALSE);
193 }
194 
195 /* Redraw the lcd only */
redraw_lcd(void)196 void redraw_lcd(void)
197 {
198 	if(!pixmap)
199 		return;
200 
201 	// scale image
202 	g_object_unref(skin_infos.image);
203 	skin_infos.image =
204 		gdk_pixbuf_scale_simple(skin_infos.raw, sr.w, sr.h, GDK_INTERP_NEAREST);
205 
206 	// and draw
207 	gdk_draw_pixbuf(pixmap, main_wnd->style->fg_gc[GTK_WIDGET_STATE(main_wnd)],
208 		  skin_infos.image, ls.x, ls.y, lr.x, lr.y, lr.w, lr.h, GDK_RGB_DITHER_NONE, 0, 0);
209 	gtk_widget_queue_draw_area(area, lr.x, lr.y, lr.w, lr.h);
210 }
211 
212 // gray plane sequences in relation with gscales.c
213 const int gp_seq[9][8] = {
214 	{ -1 },						// unused
215 	{ 0, -1 },					// b&w		(1 plane)
216 	{ -1 },						// unused
217 	{ 0, 0, 1, -1 },			// 4 colors (2 planes)
218 	{ -1 },						// unused
219 	{ -1 },						// unused
220 	{ -1 },						// unused
221 	{ 2, 0, 1, 0, 1, 0, -1 },	// 7 colors (3 planes)
222 	{ 1, 0, 2, 0, 0, 1, 0, -1 },// 8 colors (3 planes)
223 };
224 
225 /* Update LCD screen part */
hid_update_lcd(void)226 int hid_update_lcd(void)
227 {
228 	int i, j, k, l;
229 	uint8_t *lcd_bitmap = &tihw.ram[tihw.lcd_adr];
230 	uint8_t *lcd_buf = (uint8_t *)lcd_bytmap;
231 	GdkRect src;
232 	guchar *p;
233 
234 	tihw.lcd_ptr = (char *) &tihw.ram[tihw.lcd_adr];
235 
236     if(!pixmap || !lcd_mem || !tihw.lcd_ptr)
237         return 0;
238 
239 	// Check for LCD state change (from TI HW)
240 	if(lcd_state != tihw.on_off)
241 	{
242 		lcd_state = tihw.on_off;
243 		lcd_changed = 1;
244 
245 		if(!lcd_state)
246 			redraw_lcd();	// to clear LCD
247 	}
248 
249 	// Check for contrast change (from TI HW)
250 	if(contrast != tihw.contrast)
251 	{
252 		gint c = contrast = tihw.contrast;
253 
254 		new_contrast = (c + old_contrast) / 2;
255   		old_contrast = c;
256 
257 		compute_grayscale();
258 
259 		lcd_changed = 1;
260 	}
261 
262 	// Check for gray plane change (menu/hw)
263     if(max_plane != ngc)
264     {
265 		max_plane = ngc;
266 		compute_grayscale();
267     }
268 
269 	// LCD off or unchanged: don't refresh !
270 	if(!lcd_state || !lcd_changed)
271 		return 0;
272 
273 	// Reset LCD changed flag.
274 	lcd_changed = 0;
275 
276 	// Convert the bitmap screen to a bytemap screen and grayscalize
277 	memset(lcd_bytmap, 0, LCDMEM_H*LCDMEM_W);
278 	for(l = 0; l < 8; l++)
279 	{
280 		int pp = gp_seq[ngc][l];
281 		if(pp == -1) break;
282 
283 		lcd_bitmap = lcd_planebufs[pp];
284 
285 		for(j = 0, k = 0; k < LCDMEM_H; k++)
286 		{
287 			for(i = 0; i < LCDMEM_W/8; i++, lcd_bitmap++)
288 			{
289 				lcd_bytmap[j++] += convtab[(*lcd_bitmap << 1)  ];
290 				lcd_bytmap[j++] += convtab[(*lcd_bitmap << 1)+1];
291 			}
292 		}
293     }
294 
295     if(1)
296 	{
297 		// Copy LCD from buffer to pixbuf
298 		for(j = 0; j < LCDMEM_H; j++)
299 		{
300 			for (i = 0; i < LCDMEM_W; i++)
301 			{
302 				p = li.pixels + j * li.rowstride + i * li.n_channels;
303 
304 				p[0] = grayscales[*lcd_buf].r;
305 				p[1] = grayscales[*lcd_buf].g;
306 				p[2] = grayscales[*lcd_buf].b;
307 				lcd_buf++;
308 			}
309 		}
310 
311         // Set region to update
312         src.x = src.y = 0;
313 		src.w = (tihw.log_w > tihw.lcd_w) ? tihw.lcd_w : tihw.log_w;
314 		src.h = (tihw.log_h > tihw.lcd_h) ? tihw.lcd_h : tihw.log_h;
315 
316 		// Copy surface into window
317 		if(sf)
318 		{
319 			src.w = (int)(sf * src.w);
320 			src.h = (int)(sf * src.h);
321 
322 			// scale image
323 			g_object_unref(skin_infos.image);
324 			skin_infos.image = gdk_pixbuf_scale_simple(lcd, lr.w, lr.h, GDK_INTERP_NEAREST);
325 
326 			// and draw image into pixmap (next, into window on expose event)
327 			gdk_draw_pixbuf(pixmap, main_wnd->style->fg_gc[GTK_WIDGET_STATE(main_wnd)],
328 			 skin_infos.image, src.x, src.y, lr.x, lr.y, src.w, src.h,
329 			  GDK_RGB_DITHER_NONE, 0, 0);
330 			gtk_widget_queue_draw_area(area, lr.x, lr.y, src.w, src.h);
331 		}
332 		else
333 		{
334 			// and draw image into pixmap (next, into window on expose event)
335 			gdk_draw_pixbuf(pixmap, main_wnd->style->fg_gc[GTK_WIDGET_STATE(main_wnd)],
336 			  lcd_mem, src.x, src.y, lr.x, lr.y, src.w, src.h,
337 			  GDK_RGB_DITHER_NONE, 0, 0);
338 			gtk_widget_queue_draw_area(area, lr.x, lr.y, src.w, src.h);
339 		}
340     }
341 
342 	// Screen shot
343 	if(!--skip_cnt && shot_cnt > 0)
344 	{
345 		skip_cnt = options2.skips;
346 
347 		hid_screenshot_single();
348 		shot_cnt--;
349 	}
350 
351     return -1;
352 }
353 
354 // Copy LCD pixuf into B&W pixbuf
hid_copy_lcd(void)355 GdkPixbuf* hid_copy_lcd(void)
356 {
357 	int i, j, k;
358 	uint8_t *lcd_bitmap = &tihw.ram[tihw.lcd_adr];
359 	uint8_t *lcd_buf = (uint8_t *)lcd_bytmap;
360 	guchar *p;
361 
362 	tihw.lcd_ptr = (char *) &tihw.ram[tihw.lcd_adr];
363 
364 	// convert the bitmap screen to a bytemap screen and grayscalize
365 	memset(lcd_bytmap, 0, LCDMEM_H*LCDMEM_W);
366 	for(j = 0, k = 0; k < LCDMEM_H; k++)
367 	{
368 		for(i = 0; i < LCDMEM_W/8; i++, lcd_bitmap++)
369 		{
370 			lcd_bytmap[j++] = convtab[(*lcd_bitmap << 1)  ];
371 			lcd_bytmap[j++] = convtab[(*lcd_bitmap << 1)+1];
372 		}
373 	}
374 
375 	// and copy
376 	for(j = 0; j < LCDMEM_H; j++)
377 	{
378 		for (i = 0; i < LCDMEM_W; i++)
379 		{
380 			p = li.pixels + j * li.rowstride + i * li.n_channels;
381 
382 			p[0] = *lcd_buf ? 0x00: 0xff;
383 			p[1] = *lcd_buf ? 0x00: 0xff;
384 			p[2] = *lcd_buf ? 0x00: 0xff;
385 			lcd_buf++;
386 		}
387 	}
388 
389 	return gdk_pixbuf_new_subpixbuf(lcd_mem, 0, 0, tihw.lcd_w, tihw.lcd_h);
390 }
391