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