/* * display.c * * Copyright (C) 1993-1999 Evan Harris * * Permission is granted to freely redistribute and modify this code, * providing the author(s) get credit for having written it. * */ /* $Id: display.c,v 1.10 1999/11/07 05:54:35 evan Exp $ */ #ifndef lint static char vcid[] = "$Id: display.c,v 1.10 1999/11/07 05:54:35 evan Exp $"; #endif /* lint */ /* * (Evan) PIX_ALIGN changes inspired by Michael Weller, who patched against * version 1.1: * * Patched to not depend on illegal assumptions (xbytes==bytesperpixel*xdim), * to obey logical_width a multiple of 8 pixels (not bytes) * to let logical_width be an even higher multiple of a power of two * for spike free video signal on Mach32 * 16. 3. 94 Michael Weller (eowmob@exp-math.uni-essen.de or * eowmob@pollux.exp-math.uni-essen.de or mat42b@vm.hrz.uni-essen.de or * mat42b@de0hrz1a.bitnet) (svgalib, mach32-driver) */ #include "seejpeg.h" #include #include #include #include #include #include #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define ABS(a) (((a) < 0) ? -(a) : (a)) static int best_mode_256[] = { G320x200x256, #ifndef ONLY_1_8_MODES G320x240x256V, G400x300x256, G512x384x256, G640x400x256, #endif G640x480x256, #ifndef ONLY_1_8_MODES G720x540x256, #endif G800x600x256, #ifndef ONLY_1_8_MODES G848x480x256, G960x720x256, #endif G1024x768x256, #ifndef ONLY_1_8_MODES G1072x600x256, #endif G1152x864x256, #ifndef ONLY_1_8_MODES G1280x720x256, #endif G1280x1024x256, #ifndef ONLY_1_8_MODES G1360x768x256, #endif G1600x1200x256, #ifndef ONLY_1_8_MODES G1800x1012x256, G1920x1440x256, G2048x1152x256, G2048x1536x256, #endif TEXT }; static int best_mode_32k16m[] = { G320x200x16M32, G320x200x16M, G320x200x32K, #ifndef ONLY_1_8_MODES G320x240x16M32, G320x240x16M, G320x240x32K, G400x300x16M32, G400x300x16M, G400x300x32K, G512x384x16M32, G512x384x16M, G512x384x32K, G640x400x16M32, G640x400x16M, G640x400x32K, #endif G640x480x16M32, G640x480x16M, G640x480x32K, #ifndef ONLY_1_8_MODES G720x540x16M32, G720x540x16M, G720x540x32K, #endif G800x600x16M32, G800x600x16M, G800x600x32K, #ifndef ONLY_1_8_MODES G848x480x16M32, G848x480x16M, G848x480x32K, G960x720x16M32, G960x720x16M, G960x720x32K, #endif G1024x768x16M32, G1024x768x16M, G1024x768x32K, #ifndef ONLY_1_8_MODES G1072x600x16M32, G1072x600x16M, G1072x600x32K, #endif G1152x864x16M32, G1152x864x16M, G1152x864x32K, #ifndef ONLY_1_8_MODES G1280x720x16M32, G1280x720x16M, G1280x720x32K, #endif G1280x1024x16M32, G1280x1024x16M, G1280x1024x32K, #ifndef ONLY_1_8_MODES G1360x768x16M32, G1360x768x16M, G1360x768x32K, #endif G1600x1200x16M32, G1600x1200x16M, G1600x1200x32K, #ifndef ONLY_1_8_MODES G1800x1012x16M32, G1800x1012x16M, G1800x1012x32K, G1920x1440x16M32, G1920x1440x16M, G1920x1440x32K, G2048x1152x16M32, G2048x1152x16M, G2048x1152x32K, G2048x1536x16M32, G2048x1536x16M, G2048x1536x32K, #endif TEXT }; static int PIX_ALIGN; static int logical_width, logical_height; static int logical_byte_width, bytesperpixel; static int mode_width, mode_height, mode_depth, start_row, mode_linewidth; bool is_supported_256_mode(int mode) { int i = 0; do { if (best_mode_256[i] == mode) { return TRUE; } } while (best_mode_256[++i] != TEXT); return FALSE; } bool is_supported_32k16m_mode(int mode) { int i = 0; do { if (best_mode_32k16m[i] == mode) { return TRUE; } } while (best_mode_32k16m[++i] != TEXT); return FALSE; } /* * Finds the given mode in the table of modes, and sets the width, height and * depth parameters. * * Returns true if the mode was found, and false if it wasn't. */ static bool find_mode_in_table(int mode, int* table, int* width, int* height, int* depth) { vga_modeinfo* vgainfo; int i = 0; do { if (table[i] == mode) { vgainfo = vga_getmodeinfo(mode); *width = vgainfo->width; *height = vgainfo->height; *depth = vgainfo->colors; return TRUE; } } while (table[++i] != TEXT); return FALSE; } static bool mode_enabled(int width, int height) { bool enabled; /* Mode is one of: 5x4, 4x3, 8x5, 16x9 */ if (width * 4 == height * 5) { enabled = (opt_enable_5x4 ? TRUE : FALSE); } else if (width * 3 == height * 4) { enabled = (opt_enable_4x3 ? TRUE : FALSE); } else if (width * 5 == height * 8) { enabled = (opt_enable_8x5 ? TRUE : FALSE); } else if (ABS(width * 9 - height * 16) <= 48) { enabled = (opt_enable_16x9 ? TRUE : FALSE); } else { fprintf(stderr, "Unknown aspect ratio: %dx%d\n", width, height); enabled = FALSE; } return enabled; } static int select_best_256_mode(int width, int height) { vga_modeinfo* vgainfo; int mode = TEXT; int last_usable_mode = TEXT; int i = 0; do { if (vga_hasmode(best_mode_256[i])) { vgainfo = vga_getmodeinfo(best_mode_256[i]); if (mode_enabled(vgainfo->width, vgainfo->height)) { if (vgainfo->width >= opt_fuzz * width && (opt_widthonly || vgainfo->height >= opt_fuzz * height) && (opt_minwidth == 0 || vgainfo->width >= opt_minwidth) && (opt_maxwidth == 0 || vgainfo->width <= opt_maxwidth)) { mode = best_mode_256[i]; mode_width = vgainfo->width; mode_height = vgainfo->height; mode_depth = vgainfo->colors; } last_usable_mode = best_mode_256[i]; } } } while (mode == TEXT && best_mode_256[++i] != TEXT); if (mode == TEXT && last_usable_mode != TEXT) { vgainfo = vga_getmodeinfo(last_usable_mode); mode = last_usable_mode; mode_width = vgainfo->width; mode_height = vgainfo->height; mode_depth = vgainfo->colors; } return mode; } static int select_best_colour_mode(int width, int height) { #ifndef NO_32K_CASCADE int bytesperpixel, logical_byte_width, logical_width, logical_height; #endif vga_modeinfo *vgainfo; int mode = TEXT; int last_usable_mode = TEXT; int i = 0; do { if (vga_hasmode(best_mode_32k16m[i])) { vgainfo = vga_getmodeinfo(best_mode_32k16m[i]); if (mode_enabled(vgainfo->width, vgainfo->height)) { if (vgainfo->width >= opt_fuzz * width && (opt_widthonly || vgainfo->height >= opt_fuzz * height) && (opt_minwidth == 0 || vgainfo->width >= opt_minwidth) && (opt_maxwidth == 0 || vgainfo->width <= opt_maxwidth)) { mode = best_mode_32k16m[i]; mode_width = vgainfo->width; mode_height = vgainfo->height; mode_depth = vgainfo->colors; #ifndef NO_32K_CASCADE bytesperpixel = vgainfo->bytesperpixel; mode_linewidth = vgainfo->linewidth; logical_byte_width = MAX(mode_linewidth, width * bytesperpixel); if (logical_byte_width % PIX_ALIGN != 0) { logical_byte_width += PIX_ALIGN - logical_byte_width % PIX_ALIGN; } if (logical_byte_width % (bytesperpixel * PIX_ALIGN) != 0) { logical_byte_width += ((bytesperpixel * PIX_ALIGN) - logical_byte_width % (bytesperpixel * PIX_ALIGN)); } logical_width = logical_byte_width / bytesperpixel; logical_height = MIN(vgainfo->maxpixels * bytesperpixel / logical_byte_width, height); if (height > logical_height && mode_depth != 32768) { mode = TEXT; /* assume a 32k mode will be available */ } #endif } last_usable_mode = best_mode_32k16m[i]; } } } while (mode == TEXT && best_mode_32k16m[++i] != TEXT); if (mode == TEXT && last_usable_mode != TEXT) { vgainfo = vga_getmodeinfo(last_usable_mode); mode = last_usable_mode; mode_width = vgainfo->width; mode_height = vgainfo->height; mode_depth = vgainfo->colors; } return mode; } /* * Select the best video mode for the current video card. * * components: 3 for RGB, 1 for grayscale or colourmapped */ int best_mode(int width, int height, int components) { int mode = TEXT; if (opt_forcemode != TEXT) { if (vga_hasmode(opt_forcemode)) { if (components == 1) { if (find_mode_in_table(opt_forcemode, &best_mode_256[0], &mode_width, &mode_height, &mode_depth)) { return opt_forcemode; } else { error_exit("Selected image requires a 256 colour mode"); } } else if (components == 3) { if (find_mode_in_table(opt_forcemode, &best_mode_32k16m[0], &mode_width, &mode_height, &mode_depth)) { return opt_forcemode; } else { error_exit("Selected image requires a 32K or 16M colour mode (otherwise use -q or -1 or -g)"); } } else { error_exit("Selected mode not supported for this image type"); } } else { error_exit("Selected mode not supported by chipset and/or video card"); } } if (components == 1) { mode = select_best_256_mode(width, height); /* selecting also sets mode_width, mode_height, mode_depth */ if (mode == TEXT) { error_exit("Cannot find a 256 colour video mode"); } } else if (components == 3) { mode = select_best_colour_mode(width, height); /* selecting also sets mode_width, mode_height, mode_depth */ if (mode == TEXT) { error_exit("Cannot find a 32K/16M colour video mode"); } } else { error_exit("Cannot cope with number of output components"); } return mode; } void clear_screen(int mode_width, int mode_height, int logical_width, int logical_height) { int line, lines; vga_screenoff(); /* it's done by vga_setmode() but it can't hurt */ vga_clear(); vga_setpalette(0, 0, 0, 0); vga_setcolor(0); logical_height = MAX(logical_height, mode_height); /* fillblit is much better, but it's not general */ if ((logical_width * logical_height + mode_width - 1) / mode_width > mode_height) { lines = ((logical_width * logical_height + mode_width - 1) / mode_width - mode_height); for (line = mode_height; line < mode_height + lines; line++) { vga_drawline(0, line, mode_width, line); } } vga_screenon(); } /* ----------------------------------------------------------------- */ /* * For gamma corrected output: * * output_val = val ^ 1/gamma * * where 0 <= val <= 1 */ #define MAX_GAMMA_INDEX (GAMMA_TABLE_SIZE - 1) unsigned int gamma_table[GAMMA_TABLE_SIZE]; unsigned int gamma_table_shift2[GAMMA_TABLE_SIZE]; unsigned int gamma_table_shift3[GAMMA_TABLE_SIZE]; void gamma_table_init(double gamma) { int i; double val, output_val; double inverse_gamma = 1.0 / gamma; for (i = 0; i < GAMMA_TABLE_SIZE; i++ ) { /* * type conversion and pow() is slow, but this is only called * during startup */ val = (double)i / (double)MAX_GAMMA_INDEX; output_val = pow(val, inverse_gamma); gamma_table[i] = (int)(0.5 + MAX_GAMMA_INDEX * output_val); gamma_table_shift2[i] = gamma_table[i] >> 2; gamma_table_shift3[i] = gamma_table[i] >> 3; } } /* ----------------------------------------------------------------- */ static int trident = 0; void chiptype_init(void) { /* * Set PIX_ALIGN to be 64 because it seems to be a nice safe value. * Some cards can handle values much lower (which is desirable). * If your card can I would recommend trying values of 8, 16 or 32 * before 64. */ switch (vga_getcurrentchipset()) { case TVGA8900: PIX_ALIGN = 8; /* 8 is safe */ trident = 1; /* due to the 800x600x256 bug */ break; case CIRRUS: PIX_ALIGN = 8; /* the minimum is 8 */ break; case MACH32: PIX_ALIGN = 64; /* the minimum is 64 */ break; case NV3: PIX_ALIGN = 8; /* the minimum is 1 */ break; default: PIX_ALIGN = 64; /* a safe default */ break; } } /* ----------------------------------------------------------------- */ void display_init(int image_width, int image_height, int components) { int mode; /* our video mode */ int width, height; vga_modeinfo *vgainfo; width = (opt_doublex ? 2 * image_width : image_width); height = (opt_doubley ? 2 * image_height : image_height); mode = best_mode(width, height, components); vgainfo = vga_getmodeinfo(mode); bytesperpixel = vgainfo->bytesperpixel; mode_linewidth = vgainfo->linewidth; logical_byte_width = MAX(mode_linewidth, width * bytesperpixel); if (logical_byte_width % PIX_ALIGN != 0) { logical_byte_width += PIX_ALIGN - logical_byte_width % PIX_ALIGN; } /* * I don't really understand why we need this. * I wonder if it's documented somewhere... * * (Michael, Mach32): well some cards want multiples of 8pixels in a row.. * it is that easy.. */ if (logical_byte_width % (bytesperpixel * PIX_ALIGN) != 0) { logical_byte_width += (bytesperpixel * PIX_ALIGN) - logical_byte_width % (bytesperpixel * PIX_ALIGN); } while (logical_byte_width / bytesperpixel > vgainfo->maxpixels / mode_height) { logical_byte_width -= bytesperpixel * PIX_ALIGN; } logical_width = logical_byte_width / bytesperpixel; logical_height = MIN(vgainfo->maxpixels * bytesperpixel / logical_byte_width, height); /* * There is a problem with Trident's 800x600 mode, it does not correctly * support logical video modes larger than then the visible video mode. */ if (trident && mode == G800x600x256) { logical_width = 800; logical_byte_width = 800; } if (opt_verbose) { char s[20]; int s_pos; sprintf(s, "%4dx%4dx", image_width, image_height); s_pos = strlen(s); if (components == 1) { sprintf(&s[s_pos], "256"); } else if (components == 3) { sprintf(&s[s_pos], "16M"); } printf("%-14s %-16s ", s, vga_getmodename(mode)); printf("%4dx%4d\n", logical_width, logical_height); } #ifdef BUG_WORKAROUND vga_setmode(TEXT); #endif if (vga_getcurrentmode() != mode) { vga_setmode(mode); } vga_setdisplaystart(0); if (!trident || mode != G800x600x256) { vga_setlogicalwidth(logical_byte_width); } start_row = 0; clear_screen(mode_width, mode_height, logical_width, logical_height); } void display_set_palette(int num_colors, JSAMPARRAY colormap, int components) { int i, r, g, b; for (i = 0; i < num_colors; i++) { /* we should put these in an array so there's only one syscall */ if (components == 1) { r = g = b = gamma_table_shift2[GETJSAMPLE(colormap[0][i])]; } else if (components == 3) { r = gamma_table_shift2[GETJSAMPLE(colormap[0][i])]; g = gamma_table_shift2[GETJSAMPLE(colormap[1][i])]; b = gamma_table_shift2[GETJSAMPLE(colormap[2][i])]; } else { error_exit("Cannot cope with number of colour components"); /* cannot reach here, but keep the compiler happy */ r = g = b = 0; } vga_setpalette(i, r, g, b); } } void display_set_greyscale_palette(void) { int i, c; for (i = 0; i < 256; i++) { c = gamma_table_shift2[i]; vga_setpalette(i, c, c, c); } } void display_rows(int num_rows, JSAMPARRAY pixel_data, int image_width, int components) { JSAMPROW ptr; int row, col; int width, height; int mode_row, mode_col; int centre_row, centre_col; int r, g, b; if (opt_doublex) { width = MIN(image_width, logical_width / 2); } else { width = MIN(image_width, logical_width); } if (start_row < logical_height) { if (logical_height < start_row + num_rows) { height = logical_height - start_row; } else { height = num_rows; } } else { start_row += num_rows; return; } if (opt_centre && logical_height < mode_height) { centre_row = (mode_height - logical_height) / 2; } else { centre_row = 0; } centre_col = 0; if (opt_centre) { int w = (opt_doublex ? image_width * 2 : image_width); if (w < mode_width) { centre_col = (mode_width - w) / 2; } } if (components == 1) { for (row = 0; row < height; row++) { int start_pos = (start_row + row + centre_row) * logical_byte_width; mode_row = start_pos / mode_linewidth; mode_col = ((start_pos % mode_linewidth) / bytesperpixel) + centre_col; ptr = pixel_data[row]; /* * Duplicated code so we only do an unnecessary test per line * rather than per pixel. */ if (opt_doublex) { for (col = 0; col < width; col++) { vga_setcolor(GETJSAMPLE(*ptr++)); vga_drawpixel(mode_col++, mode_row); vga_drawpixel(mode_col++, mode_row); if (mode_col == mode_width) { mode_col = 0; mode_row++; } } } else { for (col = 0; col < width; col++) { vga_setcolor(GETJSAMPLE(*ptr++)); vga_drawpixel(mode_col++, mode_row); if (mode_col == mode_width) { mode_col = 0; mode_row++; } } } } } else { unsigned int* gamma; unsigned int r_shift, g_shift; if (mode_depth == 32768) { gamma = gamma_table_shift3; r_shift = 10; g_shift = 5; } else { gamma = gamma_table; r_shift = 16; g_shift = 8; } for (row = 0; row < height; row++) { int start_pos = (start_row + row + centre_row) * logical_byte_width; mode_row = start_pos / mode_linewidth; mode_col = ((start_pos % mode_linewidth) / bytesperpixel) + centre_col; ptr = pixel_data[row]; /* * Duplicated code so we only do an unnecessary test per line * rather than per pixel. */ if (opt_doublex) { for (col = 0; col < width; col++) { r = gamma[GETJSAMPLE(*ptr++)]; g = gamma[GETJSAMPLE(*ptr++)]; b = gamma[GETJSAMPLE(*ptr++)]; vga_setcolor(r << r_shift | g << g_shift | b); vga_drawpixel(mode_col++, mode_row); vga_drawpixel(mode_col++, mode_row); if (mode_col == mode_width) { mode_col = 0; mode_row++; } } } else { for (col = 0; col < width; col++) { r = gamma[GETJSAMPLE(*ptr++)]; g = gamma[GETJSAMPLE(*ptr++)]; b = gamma[GETJSAMPLE(*ptr++)]; vga_setcolor(r << r_shift | g << g_shift | b); vga_drawpixel(mode_col++, mode_row); if (mode_col == mode_width) { mode_col = 0; mode_row++; } } } } } start_row += num_rows; } #define OFFSET_ROWS 8 #define OFFSET_COLS PIX_ALIGN /* a high PIX_ALIGN is a pain here */ void scroll_until_end(void) { int c = 1, offset = 0; bool done = FALSE; int max_linestart_offset; long sec_diff, usec_diff; struct timeval tv_start, tv_end; max_linestart_offset = (logical_height - mode_height) * logical_byte_width; if (opt_slideshow >= 0) { gettimeofday(&tv_start, NULL); } while (!done) { if (c == 0) { struct timeval tv = { 0, 10000 }; select(0, NULL, NULL, NULL, &tv); } c = vga_getkey(); switch (c) { case 0: break; case 'q': case 'Q': opt_cycle = 0; next_image = NO_IMAGE; done = TRUE; break; case 0x1b: /* ESC */ c = vga_getkey(); if (c == '[' || c == 'O') { c = vga_getch(); switch (c) { case 0: break; case 'A': /* up */ if (offset > 0) { offset -= OFFSET_ROWS * logical_byte_width; while (offset < 0) { offset += logical_byte_width; } vga_setdisplaystart(offset); } break; case 'B': /* down */ if (offset < max_linestart_offset) { offset += OFFSET_ROWS * logical_byte_width; while (offset >= (max_linestart_offset + logical_byte_width)) { offset -= logical_byte_width; } #if 0 printf("offset = %d, row = %d\n", offset, offset / logical_byte_width); #endif vga_setdisplaystart(offset); } break; case 'C': /* right */ if (((offset % logical_byte_width) / bytesperpixel + mode_width) < logical_width) { offset += MIN(OFFSET_COLS * bytesperpixel, ((logical_width - mode_width) * bytesperpixel) - offset % logical_byte_width); vga_setdisplaystart(offset); } break; case 'D': /* left */ if (offset % logical_byte_width > 0) { offset -= MIN(OFFSET_COLS * bytesperpixel, offset % logical_byte_width); vga_setdisplaystart(offset); } break; default: done = TRUE; break; } } else if (c != 0) { done = TRUE; } break; case 0x7f: /* BACKSPACE */ done = TRUE; next_image = PREVIOUS_IMAGE; break; case 'r': case 'R': done = TRUE; next_image = SAME_IMAGE; break; default: done = TRUE; break; } if (opt_slideshow >= 0) { gettimeofday(&tv_end, NULL); sec_diff = tv_end.tv_sec - tv_start.tv_sec; usec_diff = tv_end.tv_usec - tv_start.tv_usec; done = ((sec_diff > opt_slideshow || (sec_diff == opt_slideshow && usec_diff >= 0)) ? TRUE : FALSE); } } } void display_shutdown(void) { vga_setmode(TEXT); }