6359 lines
192 KiB
C
Executable File
6359 lines
192 KiB
C
Executable File
/* ---------------------------------------------------------------------
|
||
Bmp2DHR (C) Copyright Bill Buckels 2014.
|
||
All Rights Reserved.
|
||
|
||
Module Name - Description
|
||
-------------------------
|
||
|
||
b2d.c - main program
|
||
|
||
Licence Agreement
|
||
-----------------
|
||
|
||
You have a royalty-free right to use, modify, reproduce and distribute this
|
||
source code in any way you find useful, provided that you agree that Bill
|
||
Buckels has no warranty obligations or liability resulting from said
|
||
distribution in any way whatsoever. If you don't agree, remove this source code
|
||
and related files from your computer now.
|
||
|
||
Written by: Bill Buckels
|
||
Email: bbuckels@mts.net
|
||
|
||
Version 1.0
|
||
Developed between Aug 2014 and December 2014 with "standard parts".
|
||
|
||
Bmp2DHR reads a monochrome, 16 color, 256 color, or 24 bit BMP and writes Apple
|
||
II color or monochrome HGR or DHGR files.
|
||
|
||
Functional Summary of Bmp2DHR Version 1.0
|
||
-----------------------------------------
|
||
|
||
Input File Size
|
||
---------------
|
||
|
||
Full Screen Color Output - DHGR (default) and HGR (option "hgr")
|
||
|
||
Size, Nominal Resolution Etc
|
||
|
||
140 x 192 - verbatim
|
||
280 x 192 - lossy or merged
|
||
320 x 200 - 280 x 192
|
||
560 x 384 - lossy or merged
|
||
640 x 400 - 560 x 384
|
||
640 x 480 - 560 x 384
|
||
|
||
Full Screen HGR Monochrome Output - Option "mono"
|
||
|
||
280 x 192
|
||
|
||
Full Screen DHGR Monochrome Output - Option "mono"
|
||
|
||
560 x 192 Monochrome Only - verbatim conversion
|
||
560 x 384 Color Only - dithered output
|
||
|
||
|
||
Full Screen and Mixed Screen LGR (option "L") and DLGR (option "DL") Output
|
||
|
||
40 x 48 and 40 x 40 - LGR verbatim
|
||
80 x 48 and 80 x 40 - DLGR verbatim and LGR 2:1 merged scaling
|
||
160 x 96 and 160 x 80 - DLGR 2:2 and LGR 4:2 merged scaling
|
||
88 x 52 and 176 x 104 - Windowboxed Mini-Pix BMPs - Nominal Output Sizes same as above
|
||
320 x 192, 320 x 160, and 320 x 200 - DLGR 4:4 and LGR 8:4 merged scaling
|
||
560 x 384 and 560 x 320 - DLGR 7:8 and LGR 14:8 merged scaling
|
||
640 x 480 and 640 x 400 - DLGR 8:10 and LGR 16:10 merged scaling
|
||
|
||
|
||
Image Fragment DHGR Color "Sprite" Output - Option "F"
|
||
|
||
1 x 1 to 140 x 192 - verbatim - results in double-wide output appearance
|
||
1 x 1 to 280 x 192 - scaled - results in proportional output appearance
|
||
|
||
When scale is not set the maximum BMP input resolution for "Sprites" is 140 x
|
||
192, and when scale is set the maximum is 280 x 192.
|
||
|
||
Overlay File Size - 256 color BMP - verbatim sizes
|
||
|
||
HGR and DHGR Color Output - 140 x 192
|
||
HGR Mono Output - 280 x 192
|
||
DHGR Mono Output - 560 x 192
|
||
|
||
Additional Input Files - Text Format
|
||
------------------------------------
|
||
|
||
- Palette Files (various formats)
|
||
- User Definable Dither Files (see documentation and read source below)
|
||
- Overlay Titling Text (uses built-in TomThumb font)
|
||
|
||
Note: The Overlay Option for either a BMP or Text overlay does not apply to
|
||
image fragment output.
|
||
|
||
Output Summary
|
||
--------------
|
||
|
||
Bmp2DHR provides 4 primary types of Apple II output:
|
||
|
||
Default Color and Option "mono" - Full-Screen DHGR files - A2FC and A2FM single-file format.
|
||
Option "F"- Image Fragments in color DHGR Format - DHR scanline oriented single file format.
|
||
Option "hgr" - Full-Screen HGR files - single file BIN format.
|
||
Option "L" and "DL" - Full Screen and Mixed Screen Lo-Res and Double Lo-Res scanline oriented single file
|
||
|
||
DHGR Optional Alternate Output - Option "A" - AUX,BIN File Pairs instead of A2FC or A2FM files.
|
||
LGR and DLGR Optional Alternate Output - Option "A" - SL2 or DL1,DL2 File Pairs instead of SLO or DLO single files
|
||
|
||
AUX,BIN File Pairs, SL2 files and DL1,DL2 File Pairs are for AppleSoft BASIC programs.
|
||
|
||
Additional Optional Output includes:
|
||
|
||
Option "v" - Preview Output in BMP format.
|
||
Option "debug" - "debug" output of work files if any in BMP format.
|
||
Option "vbmp" - VBMP compatible BMP output (not available as LGR and DLGR output).
|
||
|
||
Additional Notes
|
||
----------------
|
||
|
||
For primary input Bmp2DHR accepts BMP files in Version 3 format only in a
|
||
specific range of input sizes and formats. The size and format of the input
|
||
file depends on the type of desired primary output and the rendering options
|
||
that have been selected.
|
||
|
||
Rendering options fall into several categories and where considered practically
|
||
possible and where it makes sense given the constraints and scope of Bmp2DHR,
|
||
all rendering options are available for all output.
|
||
|
||
Specific rendering options for specific output are also available.
|
||
|
||
Constraints are also also enforced by both BMP sizes and formats, and whether
|
||
output is color or monochrome, and also if external rendering is being used
|
||
like dithering in editors like The GIMP.
|
||
|
||
In the case of externally rendered input files, Bmp2DHR is only used as a
|
||
direct pixel converter to "pass-through" the BMP input file "verbatim". In this
|
||
case, the color palette needs to exactly match Bmp2DHR's color palette, and the
|
||
resolution needs to exactly match the Apple II output resolution.
|
||
|
||
560 x 384 and 560 x 192 BMPs are used as input files for DHGR monochrome output, and
|
||
280 x 192 BMPs are used as input files for HGR monochrome output.
|
||
|
||
Monochrome BMP files of 280 x 192 or 560 x 192 are required for verbatim
|
||
"pass-through" and output to Apple II HGR or DHGR files respectively. Palette
|
||
matched 140 x 192 color BMP files are required to pass-through properly to
|
||
color HGR or DHGR output. Color palettes that are used in external editors to
|
||
prepare pass-through input must either be imported into Bmp2DHR, or Bmp2DHR's
|
||
palettes must be imported into external editors.
|
||
|
||
Preview Output from Bmp2DHR can also generally be re-edited (carefully) and
|
||
reprocessed using direct pixel "passthrough" which is essentially the same
|
||
process as using an external editor to render and dither.
|
||
|
||
Secondary Input Files
|
||
---------------------
|
||
|
||
Bmp2DHR also accepts several secondary input files.
|
||
|
||
- Text Files for titling using a built-in font (HGR and DHGR full-screen conversion only)
|
||
- Palette Files in several text-based formats
|
||
- External Error Diffusion user-defined Dither Patterns in text format
|
||
- 256 color BMP files for overlaying the input image with verbatim text and simple pixel graphics
|
||
(HGR and DHGR full-screen conversion only)
|
||
|
||
For DHGR sprite output external Palette files and Dither Pattern files can be
|
||
used, but titling and overlaying is targeted at full-screen output.
|
||
|
||
Apple II Output Format Specification Summary
|
||
--------------------------------------------
|
||
|
||
DHGR output (default)
|
||
|
||
For DHGR default output, the A2FM and A2FC file extensions are just a naming
|
||
convention so the user can tell the difference between a monochrome and color
|
||
Apple II file; they are both binary DHGR files with a raw Auxiliary DHGR Memory
|
||
"DUMP" of 8192 bytes, followed by a raw Main DHGR Memory "DUMP" of 8192 bytes,
|
||
totalling 16384 bytes. These are stored in ProDOS as Binary FileType $06 with
|
||
an Auxiliary Type of either $2000 or $4000, which is the load address of the
|
||
DHGR screen.
|
||
|
||
Alternate Default Output (option "A")
|
||
|
||
Alternate output of a split version of the A2FC format is optionally available
|
||
using option "A". Sometimes called AUX,BIN file pairs, these are easier to load
|
||
in an AppleSoft BASIC program. They are also stored in ProDOS as Binary
|
||
FileType $06 with an Auxiliary Type of either $2000 or $4000 and are 8192 bytes
|
||
each.
|
||
|
||
For LGR and DLGR conversion the equivalent Alternate Output is also in "BSAVED" file
|
||
format. LGR and DLGR Apple II files are stored in ProDOS as Binary FileType $06 with
|
||
an Auxiliary Type of $0400.
|
||
|
||
HGR output (option "hgr")
|
||
|
||
For HGR output, the "BIN" file extension is used. These are indistinguishable
|
||
from DHGR BIN files in an AUX,BIN file pair which are also the same ProDOS file
|
||
type $06 and length of 8192 bytes, so a loader must be aware of the specific
|
||
files to load these properly.
|
||
|
||
Image Fragment ("Sprite") output (option "F")
|
||
|
||
Sprite (image fragment) format Output is an option. Sprite Output and normal
|
||
HGR and DHGR full-screen output are mutually exclusive (to some degree). If you
|
||
provide Bmp2DHR with a BMP image fragment but you don't specify Option "F" a
|
||
full-screen Apple II A2FC File will be produced with the Sprite in the top left
|
||
corner. This is so you can conveniently look at the sprite on an Apple II
|
||
display. The latest version of my cc65 dhishow slideshow also loads Sprites so
|
||
you can use that for the same purpose too.
|
||
|
||
About Sprites
|
||
|
||
The Sprites produced by this utility are in XPACK's DHR format, but XPACK only
|
||
produces Full Screen DHGR images so this is something new.
|
||
|
||
The DHR (Double Hi-Res Raster) Image Format
|
||
|
||
The image fragments produced by Bmp2DHR have an extension of DHR. Like A2FC and
|
||
AUX,BIN file pairs, they are stored on an Apple II Disk as ProDOS FileType $06
|
||
with an Auxiliary Type of $2000 or $4000 by default. On a DOS 3.3 disk they are
|
||
stored with header information required by DOS 3.3.
|
||
|
||
The Header of a DHR is in two parts;
|
||
|
||
3 bytes of ID data with the letters 'D', 'H', 'R' in upper-case
|
||
1 byte - width in bytes (multiples of 4 bytes - 7 pixels)
|
||
1 byte - height in rasters
|
||
|
||
The DHR is a raster based image with scanlines of raw DHGR data alternating
|
||
between auxiliary and main memory. Therefore a simple BASIC program cannot
|
||
easily load these since the DHGR screen is interleaved the same way that the
|
||
HGR screen is interleaved and not linear. Bank switching between auxiliary and
|
||
main memory banks 0 (main board) and 1 (language card) is also not easy in a
|
||
BASIC program.
|
||
|
||
For a full-screen DHR, there are 192 pairs of rasters, each of 40 bytes of
|
||
auxiliary memory data followed by 40 bytes of main memory data. This keeps bank
|
||
switching to a minimum and allows for linear reading from disk or buffer.
|
||
|
||
The full screen DHR loads raster by raster and displays as quickly as a
|
||
buffered read can display on the Apple II. At 15365 bytes per screen this
|
||
format provides a modest disk space saving over the 16384 bytes of the A2FC or
|
||
AUX,BIN equivalent.
|
||
|
||
A caveat for any file in DHR raster format is the 4 byte / 7 pixel pattern of
|
||
the DHGR display. The width descriptor in the header is given in byte width
|
||
rather than pixel width. Image fragments in DHGR must necessarily be aligned on
|
||
4 byte boundaries to display properly. This utility pads DHR formats as
|
||
required in an optional background color if desired.
|
||
|
||
By comparison, HGR image fragments (not produced by Bmp2DHR) are aligned on 2
|
||
byte boundaries for proper display but they are still somewhat recognizable if
|
||
not aligned properly.If DHGR image fragments are not aligned on 4 byte
|
||
boundaries they are a mess.
|
||
|
||
If a programmer wanted to load these according to a specific position on the
|
||
DHGR it would be possible to give the starting scanline and starting byte to
|
||
the desired position on the screen, and store that as the Auxiliary Type
|
||
instead:
|
||
|
||
1. The program would read the header and perform a file integrity check to
|
||
ensure that the file size was as expected.
|
||
|
||
2. Part of the verification would also be to determine if the Auxiliary Type
|
||
fell within the DHGR visible screen boundaries and if the file itself would
|
||
fit.
|
||
|
||
3. Having satisfied this requirement the image fragment could be positioned at
|
||
that point by the program.
|
||
|
||
Doing so would save disk-space and load time when constructing a pre-planned
|
||
screen in a DHGR program, since full-screens are generally larger by comparison
|
||
to creating full-screens from fragments.
|
||
|
||
Additional Remarks
|
||
------------------
|
||
|
||
This program has many more options. The source code comments and the
|
||
documentation can be reviewed for additional information.
|
||
|
||
------------------------------------------------------------------------ */
|
||
|
||
/* ***************************************************************** */
|
||
/* ========================== includes ============================= */
|
||
/* ***************************************************************** */
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <fcntl.h>
|
||
#include <math.h>
|
||
|
||
#include "b2d.h"
|
||
|
||
/* ***************************************************************** */
|
||
/* ======================= string data ============================= */
|
||
/* ***************************************************************** */
|
||
|
||
char *title = "Bmp2DHR Version 1.1 (c) Copyright Bill Buckels 2015.\nAll Rights Reserved.";
|
||
|
||
char *usage[] = {
|
||
"Usage: \"b2d input.bmp options\"",
|
||
"Input format: mono, 16 color, 256 color, or 24-bit Version 3 uncompressed BMP",
|
||
"Default DHGR Colored Output: Full Screen Apple II A2FC file",
|
||
"Optional Usage: \"b2d input.bmp hgr options\"",
|
||
" For HGR Colored Output: Full Screen Apple II BIN file",
|
||
"Optional Usage: \"b2d input.bmp mono options\"",
|
||
" For Mono Output: Full Screen Apple II DHGR A2FM or HGR BIN file",
|
||
"Free Scaled Input Sizes: Full Screen (default) or DHGR Sprite (option F) output",
|
||
" Full Scale: from 1 x 1 to 140 x 192 (default) - HGR and DHGR",
|
||
" Half Scale: from 1 x 1 to 280 x 192 (scaling option S2) - HGR and DHGR",
|
||
"HGR and DHGR Fixed Scaled Input Sizes: Full Screen Output (default)",
|
||
" 140 x 192 - Full Scale (for LGR use 40 x 48, for DLGR use 80 x 48)",
|
||
" 280 x 192 - Double Width Scale (for LGR and DLGR use 160 x 96)",
|
||
" 320 x 200 - Classic Size (also used for LGR and DLGR windowboxed output)",
|
||
" 560 x 384 - Quadruple Width, Double Height Scale (also for LGR and DLGR)",
|
||
" 640 x 400 - Classic Size (also used for LGR and DLGR mixed screen output)",
|
||
" 640 x 480 - Classic Size (also used for LGR and DLGR full screen output)",
|
||
"Full Screen Dithered Output (optional): Option D (D1 to D9)",
|
||
"Optional Usage: \"b2d input.bmp L (or DL) options\"",
|
||
" For Color LGR or DLGR Full Screen or Mixed Screen (option \"TOP\") Output",
|
||
"See documentation for more information including additional input size info",
|
||
NULL};
|
||
|
||
char *dithertext[] = {
|
||
"Floyd-Steinberg",
|
||
"Jarvis",
|
||
"Stucki",
|
||
"Atkinson",
|
||
"Burkes",
|
||
"Sierra",
|
||
"Sierra Two",
|
||
"Sierra Lite",
|
||
"Buckels",
|
||
"Custom"};
|
||
|
||
char *palname[] = {
|
||
"Kegs32 RGB",
|
||
"CiderPress RGB",
|
||
"Old AppleWin NTSC",
|
||
"New AppleWin NTSC",
|
||
"Wikipedia NTSC",
|
||
"tohgr NTSC DHGR",
|
||
"Imported",
|
||
"Legacy Canvas",
|
||
"Legacy Win16",
|
||
"Legacy Win32",
|
||
"Legacy VGA BIOS",
|
||
"Legacy VGA PCX",
|
||
"Super Convert RGB",
|
||
"Jace NTSC",
|
||
"Cybernesto-Munafo NTSC",
|
||
"Pseudo Palette",
|
||
"tohgr NTSC HGR"};
|
||
|
||
/* ***************************************************************** */
|
||
/* ========================== code ================================= */
|
||
/* ***************************************************************** */
|
||
|
||
int cmpstr(char *str, char *cmp)
|
||
{
|
||
int i;
|
||
|
||
if (strlen(cmp) != strlen(str)) return INVALID;
|
||
for (i=0;str[i] != 0;i++) {
|
||
if (toupper(cmp[i]) != toupper(str[i])) return INVALID;
|
||
}
|
||
return SUCCESS;
|
||
}
|
||
|
||
/* returns 255 if color number or color name are invalid */
|
||
uchar PaintByNumbers(char *str)
|
||
{
|
||
int idx;
|
||
uchar c = toupper(str[0]);
|
||
|
||
if (str[1] == (char) 0) {
|
||
/* alpha mnemonic */
|
||
if (c > 64 && c < 81) return (uchar) (c - 65);
|
||
}
|
||
|
||
/* non numeric argument so check for color names */
|
||
/* add additional color names here if required */
|
||
if (c > 57) {
|
||
c = 255;
|
||
if (cmpstr("black", str) == SUCCESS)c = 0;
|
||
else if (cmpstr("red", str) == SUCCESS)c = 1;
|
||
else if (cmpstr("dblue", str) == SUCCESS)c = 2;
|
||
else if (cmpstr("purple", str) == SUCCESS)c = 3;
|
||
else if (cmpstr("dgreen", str) == SUCCESS)c = 4;
|
||
else if (cmpstr("dgray", str) == SUCCESS)c = 5;
|
||
else if (cmpstr("dgrey", str) == SUCCESS)c = 5;
|
||
else if (cmpstr("mblue", str) == SUCCESS)c = 6;
|
||
else if (cmpstr("lblue", str) == SUCCESS)c = 7;
|
||
else if (cmpstr("brown", str) == SUCCESS)c = 8;
|
||
else if (cmpstr("orange", str) == SUCCESS)c = 9;
|
||
else if (cmpstr("lgray", str) == SUCCESS)c = 10;
|
||
else if (cmpstr("lgrey", str) == SUCCESS)c = 10;
|
||
else if (cmpstr("pink", str) == SUCCESS)c = 11;
|
||
else if (cmpstr("lgreen", str) == SUCCESS)c = 12;
|
||
else if (cmpstr("yellow", str) == SUCCESS)c = 13;
|
||
else if (cmpstr("aqua", str) == SUCCESS)c = 14;
|
||
else if (cmpstr("white", str) == SUCCESS)c = 15;
|
||
}
|
||
else {
|
||
if (c == '0' && str[1] == (char)0) {
|
||
c = 0;
|
||
}
|
||
else {
|
||
c = 255;
|
||
idx = atoi(str);
|
||
if (idx > -1 && idx < 16) c = (uchar) idx;
|
||
}
|
||
}
|
||
|
||
return c;
|
||
}
|
||
|
||
|
||
ushort Motorola16(ushort val)
|
||
{
|
||
uchar buf[2];
|
||
ushort *ptr;
|
||
|
||
/* msb in smallest address */
|
||
buf[0] = (uchar) (val % 256); val = val/256;
|
||
buf[1] = (uchar) (val % 256);
|
||
|
||
ptr = (ushort *)&buf[0];
|
||
val = ptr[0];
|
||
|
||
return val;
|
||
}
|
||
|
||
void WriteDosHeader(FILE *fp, ushort fl, ushort fa)
|
||
{
|
||
|
||
/* if CiderPress tags are turned-on I assume that the header is not required
|
||
since presumably the tags will be used to place the file properly and ciderpress
|
||
will create the DOS 3.3 header based on the file attribute preservation tag.
|
||
*/
|
||
if (dosheader == 1 && tags == 0) {
|
||
|
||
fa = Motorola16(fa);/* file bload address - not including this header */
|
||
fl = Motorola16(fl);/* file length - not including this header */
|
||
|
||
fwrite((char *)&fa,sizeof(ushort),1,fp);
|
||
fwrite((char *)&fl,sizeof(ushort),1,fp);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
|
||
Photoshop Luminosity Average
|
||
|
||
Formula for the Luminosity Average:
|
||
|
||
AvgLuma = 0.299<EFBFBD>AvgRed + 0.587<EFBFBD>AvgGreen + 0.114<EFBFBD>AvgBlue
|
||
|
||
|
||
*/
|
||
|
||
|
||
/* set luma to different values for closest color */
|
||
int lumaREQ = 601, lumaRED = 299, lumaGREEN = 587, lumaBLUE = 114;
|
||
double dlumaRED, dlumaGREEN, dlumaBLUE;
|
||
|
||
void setluma()
|
||
{
|
||
switch(lumaREQ)
|
||
{
|
||
case 240: /* SMPTE 240M transitional coefficients */
|
||
lumaRED = 212; lumaGREEN = 701; lumaBLUE = 87;
|
||
dlumaRED = 0.212; dlumaGREEN = 0.701; dlumaBLUE = 0.087;
|
||
break;
|
||
|
||
case 911: /* Sheldon Simms - tohgr */
|
||
lumaRED = 77; lumaGREEN = 151; lumaBLUE = 28;
|
||
dlumaRED = 0.077;dlumaGREEN = 0.151; dlumaBLUE = 0.028;
|
||
break;
|
||
|
||
case 411: /* The GIMP color managed */
|
||
lumaRED = 223; lumaGREEN = 717; lumaBLUE = 61;
|
||
dlumaRED = 0.2225; dlumaGREEN = 0.7169; dlumaBLUE = 0.0606;
|
||
break;
|
||
|
||
case 709: /* CCIR 709 - modern */
|
||
/* ImageMagick non-color managed */
|
||
lumaRED = 213; lumaGREEN = 715; lumaBLUE = 72;
|
||
dlumaRED = 0.212656;dlumaGREEN = 0.715158; dlumaBLUE = 0.072186;
|
||
break;
|
||
|
||
case 601: /* CCIR 601 - most digital standard definition formats */
|
||
default: lumaRED = 299; lumaGREEN = 587; lumaBLUE = 114;
|
||
dlumaRED = 0.299; dlumaGREEN = 0.587; dlumaBLUE = 0.114;
|
||
break;
|
||
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/* intialize the values for the current palette */
|
||
void InitDoubleArrays()
|
||
{
|
||
int i;
|
||
double dr, dg, db, dthreshold;
|
||
unsigned r, g, b;
|
||
|
||
/* array for matching closest color in palette */
|
||
for (i=0;i<16;i++) {
|
||
rgbDouble[i][0] = dr = (double) rgbArray[i][0];
|
||
rgbDouble[i][1] = dg = (double) rgbArray[i][1];
|
||
rgbDouble[i][2] = db = (double) rgbArray[i][2];
|
||
rgbLuma[i] = (dr*lumaRED + dg*lumaGREEN + db*lumaBLUE) / (255.0*1000);
|
||
}
|
||
|
||
/* array for matching closest color in palette
|
||
threshold reduced by 25% */
|
||
|
||
if (threshold == 0) {
|
||
dthreshold = 0.75;
|
||
}
|
||
else {
|
||
dthreshold = (double) threshold;
|
||
if (xmatrix != 2) dthreshold *= 0.5;
|
||
dthreshold = (double) (100.0 - dthreshold) / 100;
|
||
|
||
}
|
||
|
||
for (i=0;i<16;i++) {
|
||
dr = (double) rgbArray[i][0];
|
||
dg = (double) rgbArray[i][1];
|
||
db = (double) rgbArray[i][2];
|
||
|
||
dr *= dthreshold;
|
||
dg *= dthreshold;
|
||
db *= dthreshold;
|
||
|
||
rgbDoubleBrighten[i][0] = dr;
|
||
rgbDoubleBrighten[i][1] = dg;
|
||
rgbDoubleBrighten[i][2] = db;
|
||
rgbLumaBrighten[i] = (dr*lumaRED + dg*lumaGREEN + db*lumaBLUE) / (255.0*1000);
|
||
}
|
||
|
||
if (threshold == 0) {
|
||
dthreshold = 1.25;
|
||
}
|
||
else {
|
||
dthreshold = (double) threshold;
|
||
if (xmatrix != 2) dthreshold *= 0.5;
|
||
dthreshold = (double) (100.0 + dthreshold) / 100;
|
||
}
|
||
|
||
for (i=0;i<16;i++) {
|
||
dr = (double) rgbArray[i][0];
|
||
dg = (double) rgbArray[i][1];
|
||
db = (double) rgbArray[i][2];
|
||
|
||
dr *= dthreshold;
|
||
if (dr > 255.0) dr = 255.0;
|
||
dg *= dthreshold;
|
||
if (dg > 255.0) dg = 255.0;
|
||
db *= dthreshold;
|
||
if (db > 255.0) db = 255.0;
|
||
|
||
rgbDoubleDarken[i][0] = dr;
|
||
rgbDoubleDarken[i][1] = dg;
|
||
rgbDoubleDarken[i][2] = db;
|
||
rgbLumaDarken[i] = (dr*lumaRED + dg*lumaGREEN + db*lumaBLUE) / (255.0*1000);
|
||
}
|
||
}
|
||
|
||
|
||
/* select current palette */
|
||
void GetBuiltinPalette(sshort palidx, sshort previewidx, sshort pseudo)
|
||
{
|
||
sshort i,j;
|
||
uchar r,g,b;
|
||
|
||
/* set conversion colors */
|
||
switch(palidx) {
|
||
case 16:/* optional NTSC palette from tohgr - used for HGR conversion */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = hgrpal[i][0];
|
||
rgbArray[i][1] = hgrpal[i][1];
|
||
rgbArray[i][2] = hgrpal[i][2];
|
||
}
|
||
break;
|
||
case 15:
|
||
/* the infamous pseudo palette */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = PseudoPalette[i][0];
|
||
rgbArray[i][1] = PseudoPalette[i][1];
|
||
rgbArray[i][2] = PseudoPalette[i][2];
|
||
}
|
||
break;
|
||
case 14: /* Robert Munafo - http://mrob.com/pub/xapple2/colors.html */
|
||
/* NTSC Palette used by Cybernesto in VBMP GIMP tutorial */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = Cybernesto[i][0];
|
||
rgbArray[i][1] = Cybernesto[i][1];
|
||
rgbArray[i][2] = Cybernesto[i][2];
|
||
}
|
||
break;
|
||
case 13: /* Jace emulator NTSC palette */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = Jace[i][0];
|
||
rgbArray[i][1] = Jace[i][1];
|
||
rgbArray[i][2] = Jace[i][2];
|
||
}
|
||
break;
|
||
case 12: /* Super Convert HGR and DHGR conversion colors */
|
||
/* same as kegs32 colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = SuperConvert[i][0];
|
||
rgbArray[i][1] = SuperConvert[i][1];
|
||
rgbArray[i][2] = SuperConvert[i][2];
|
||
}
|
||
break;
|
||
/* 5 IBM-PC legacy palettes from BMPA2FC */
|
||
/* used for color substitution - not Apple II colors */
|
||
case 11: for (i=0;i<16;i++) {
|
||
/* default colors from some old ZSoft 16 color PCX */
|
||
rgbArray[i][0] = rgbPcxArray[i][0];
|
||
rgbArray[i][1] = rgbPcxArray[i][1];
|
||
rgbArray[i][2] = rgbPcxArray[i][2];
|
||
}
|
||
break;
|
||
case 10: for (i=0;i<16;i++) {
|
||
/* colors from VGA bios */
|
||
rgbArray[i][0] = rgbVgaArray[i][0];
|
||
rgbArray[i][1] = rgbVgaArray[i][1];
|
||
rgbArray[i][2] = rgbVgaArray[i][2];
|
||
}
|
||
break;
|
||
case 9: for (i=0;i<16;i++) {
|
||
/* colors from Windows Paint XP - 16 color BMP */
|
||
rgbArray[i][0] = rgbXmpArray[i][0];
|
||
rgbArray[i][1] = rgbXmpArray[i][1];
|
||
rgbArray[i][2] = rgbXmpArray[i][2];
|
||
}
|
||
break;
|
||
case 8: for (i=0;i<16;i++) {
|
||
/* colors from MSPaint Windows 3.1 - 16 color BMP */
|
||
rgbArray[i][0] = rgbBmpArray[i][0];
|
||
rgbArray[i][1] = rgbBmpArray[i][1];
|
||
rgbArray[i][2] = rgbBmpArray[i][2];
|
||
}
|
||
break;
|
||
case 7: for (i=0;i<16;i++) {
|
||
/* "canvas" colors from BmpA2FC */
|
||
rgbArray[i][0] = rgbCanvasArray[i][0];
|
||
rgbArray[i][1] = rgbCanvasArray[i][1];
|
||
rgbArray[i][2] = rgbCanvasArray[i][2];
|
||
}
|
||
break;
|
||
case 6: /* user definable imported palette file */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = rgbUser[i][0];
|
||
rgbArray[i][1] = rgbUser[i][1];
|
||
rgbArray[i][2] = rgbUser[i][2];
|
||
}
|
||
break;
|
||
case 4: /* wikipedia Apple II NTSC colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = wikipedia[i][0];
|
||
rgbArray[i][1] = wikipedia[i][1];
|
||
rgbArray[i][2] = wikipedia[i][2];
|
||
}
|
||
break;
|
||
case 3: /* Current AppleWin Version's sort-of NTSC colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = awinnewcolors[i][0];
|
||
rgbArray[i][1] = awinnewcolors[i][1];
|
||
rgbArray[i][2] = awinnewcolors[i][2];
|
||
}
|
||
break;
|
||
case 2: /* Previous AppleWin Version's sort-of NTSC colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = awinoldcolors[i][0];
|
||
rgbArray[i][1] = awinoldcolors[i][1];
|
||
rgbArray[i][2] = awinoldcolors[i][2];
|
||
}
|
||
break;
|
||
case 1: /* CiderPress RGB File Viewer colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = ciderpresscolors[i][0];
|
||
rgbArray[i][1] = ciderpresscolors[i][1];
|
||
rgbArray[i][2] = ciderpresscolors[i][2];
|
||
}
|
||
break;
|
||
case 0: /* kegs32 RGB colors - same as Super Convert */
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = kegs32colors[i][0];
|
||
rgbArray[i][1] = kegs32colors[i][1];
|
||
rgbArray[i][2] = kegs32colors[i][2];
|
||
}
|
||
break;
|
||
case 5: /* NTSC palette from tohgr - used for default HGR and DHGR conversion */
|
||
default:
|
||
for (i=0;i<16;i++) {
|
||
rgbArray[i][0] = grpal[i][0];
|
||
rgbArray[i][1] = grpal[i][1];
|
||
rgbArray[i][2] = grpal[i][2];
|
||
}
|
||
palidx = 5;
|
||
break;
|
||
}
|
||
|
||
/* set preview colors */
|
||
switch(previewidx) {
|
||
case 16:/* HGR conversion - optional palette from tohgr */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = hgrpal[i][0];
|
||
rgbPreview[i][1] = hgrpal[i][1];
|
||
rgbPreview[i][2] = hgrpal[i][2];
|
||
}
|
||
break;
|
||
case 15: /* the infamous pseudo palette */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = PseudoPalette[i][0];
|
||
rgbPreview[i][1] = PseudoPalette[i][1];
|
||
rgbPreview[i][2] = PseudoPalette[i][2];
|
||
}
|
||
break;
|
||
case 14: /* Robert Munafo - http://mrob.com/pub/xapple2/colors.html */
|
||
/* NTSC Palette used by Cybernesto in VBMP GIMP tutorial */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = Cybernesto[i][0];
|
||
rgbPreview[i][1] = Cybernesto[i][1];
|
||
rgbPreview[i][2] = Cybernesto[i][2];
|
||
}
|
||
break;
|
||
case 13: /* Jace emulator NTSC palette */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = Jace[i][0];
|
||
rgbPreview[i][1] = Jace[i][1];
|
||
rgbPreview[i][2] = Jace[i][2];
|
||
}
|
||
break;
|
||
case 12: /* Super Convert HGR and DHGR conversion colors */
|
||
/* same as kegs32 colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = SuperConvert[i][0];
|
||
rgbPreview[i][1] = SuperConvert[i][1];
|
||
rgbPreview[i][2] = SuperConvert[i][2];
|
||
}
|
||
break;
|
||
/* 5 IBM-PC VGA legacy palettes from BMPA2FC */
|
||
/* used for color substitution - not Apple II colors */
|
||
case 11: for (i=0;i<16;i++) {
|
||
/* default colors from some old ZSoft 16 color PCX */
|
||
rgbPreview[i][0] = rgbPcxArray[i][0];
|
||
rgbPreview[i][1] = rgbPcxArray[i][1];
|
||
rgbPreview[i][2] = rgbPcxArray[i][2];
|
||
}
|
||
break;
|
||
case 10: for (i=0;i<16;i++) {
|
||
/* colors from VGA bios */
|
||
rgbPreview[i][0] = rgbVgaArray[i][0];
|
||
rgbPreview[i][1] = rgbVgaArray[i][1];
|
||
rgbPreview[i][2] = rgbVgaArray[i][2];
|
||
}
|
||
break;
|
||
case 9: for (i=0;i<16;i++) {
|
||
/* colors from Windows Paint XP - 16 color BMP */
|
||
rgbPreview[i][0] = rgbXmpArray[i][0];
|
||
rgbPreview[i][1] = rgbXmpArray[i][1];
|
||
rgbPreview[i][2] = rgbXmpArray[i][2];
|
||
}
|
||
break;
|
||
case 8: for (i=0;i<16;i++) {
|
||
/* colors from MSPaint Windows 3.1 - 16 color BMP */
|
||
rgbPreview[i][0] = rgbBmpArray[i][0];
|
||
rgbPreview[i][1] = rgbBmpArray[i][1];
|
||
rgbPreview[i][2] = rgbBmpArray[i][2];
|
||
}
|
||
break;
|
||
case 7: for (i=0;i<16;i++) {
|
||
/* "canvas" colors from BmpA2FC */
|
||
rgbPreview[i][0] = rgbCanvasArray[i][0];
|
||
rgbPreview[i][1] = rgbCanvasArray[i][1];
|
||
rgbPreview[i][2] = rgbCanvasArray[i][2];
|
||
}
|
||
break;
|
||
case 6: /* user definable imported palette file */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = rgbUser[i][0];
|
||
rgbPreview[i][1] = rgbUser[i][1];
|
||
rgbPreview[i][2] = rgbUser[i][2];
|
||
}
|
||
break;
|
||
case 4: /* wikipedia Apple II NTSC colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = wikipedia[i][0];
|
||
rgbPreview[i][1] = wikipedia[i][1];
|
||
rgbPreview[i][2] = wikipedia[i][2];
|
||
}
|
||
break;
|
||
case 3: /* Current AppleWin Version's sort-of NTSC colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = awinnewcolors[i][0];
|
||
rgbPreview[i][1] = awinnewcolors[i][1];
|
||
rgbPreview[i][2] = awinnewcolors[i][2];
|
||
}
|
||
break;
|
||
case 2: /* Previous AppleWin Version's sort-of NTSC colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = awinoldcolors[i][0];
|
||
rgbPreview[i][1] = awinoldcolors[i][1];
|
||
rgbPreview[i][2] = awinoldcolors[i][2];
|
||
}
|
||
break;
|
||
case 1: /* CiderPress RGB File Viewer colors */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = ciderpresscolors[i][0];
|
||
rgbPreview[i][1] = ciderpresscolors[i][1];
|
||
rgbPreview[i][2] = ciderpresscolors[i][2];
|
||
}
|
||
break;
|
||
case 0: /* kegs32 RGB colors - same as Super Convert */
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = kegs32colors[i][0];
|
||
rgbPreview[i][1] = kegs32colors[i][1];
|
||
rgbPreview[i][2] = kegs32colors[i][2];
|
||
}
|
||
break;
|
||
case 5: /* NTSC palette from tohgr - used for default HGR and DHGR conversion */
|
||
default:
|
||
for (i=0;i<16;i++) {
|
||
rgbPreview[i][0] = grpal[i][0];
|
||
rgbPreview[i][1] = grpal[i][1];
|
||
rgbPreview[i][2] = grpal[i][2];
|
||
}
|
||
previewidx = 5;
|
||
break;
|
||
}
|
||
|
||
/* set-up the HGR conversion palette based-on the colors that were removed from palette 5 */
|
||
/* 3 options are available - 6 colors, 4 color Orange-Blue, or 4 color Green-Violet */
|
||
if (hgroutput == 1) {
|
||
for (i=0;i<16;i++) {
|
||
if (grpal[i][0] == 0 && grpal[i][1] == 0 && grpal[i][2] == 0) {
|
||
rgbPreview[i][0] = rgbArray[i][0] = 0;
|
||
rgbPreview[i][1] = rgbArray[i][1] = 0;
|
||
rgbPreview[i][2] = rgbArray[i][2] = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (i=0;i<16;i++) {
|
||
/* verbatim match - 4-bits deep not 8 */
|
||
rgbAppleArray[i][0] = rgbArray[i][0] >> 4;
|
||
rgbAppleArray[i][1] = rgbArray[i][1] >> 4;
|
||
rgbAppleArray[i][2] = rgbArray[i][2] >> 4;
|
||
/* match VBMP color palette to the current conversion palette */
|
||
rgbVBMP[i][0] = rgbArray[i][0];
|
||
rgbVBMP[i][1] = rgbArray[i][1];
|
||
rgbVBMP[i][2] = rgbArray[i][2];
|
||
}
|
||
|
||
/* no need to clip mono - the mono palette has only 2 colors */
|
||
if (paletteclip == 1 && mono == 0) {
|
||
/* command options "CV" or "CP" (clip view or clip palette) */
|
||
/* not implemented for preview or for verbatim match */
|
||
/* note that verbatim match is only 4-bits deep so already clips */
|
||
|
||
/* clipping filter for dirty blacks and whites */
|
||
/* borrowed from Sheldon Simms */
|
||
/* but this may have other adverse effects so it is optional */
|
||
rgbArray[0][RED] = 1;
|
||
rgbArray[0][GREEN] = 4;
|
||
rgbArray[0][BLUE] = 8;
|
||
|
||
rgbArray[15][RED] = 248;
|
||
rgbArray[15][GREEN] = 250;
|
||
rgbArray[15][BLUE] = 244;
|
||
}
|
||
|
||
if(pseudo != 1) {
|
||
if (quietmode == 1) {
|
||
if (mono == 1) puts("Black and White Monochrome Palette");
|
||
else printf("Palette %d: %s Colors\nPreview Palette %d: %s Colors\n",palidx,palname[palidx],previewidx,palname[previewidx]);
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
/* build pseudo-palettes by using the average rgb values of two or more palettes into one */
|
||
/* called from main() before setting the palettes
|
||
and after an external user definable palette has been set (if any) */
|
||
void BuildPseudoPalette(sshort palidx)
|
||
{
|
||
|
||
sshort i,j,k,idx;
|
||
ushort gun;
|
||
|
||
/* a pseudopalette cannot be used if palette 15 is selected as a conversion palette */
|
||
if ((palidx < 0 || palidx > 16) || palidx == 15) return;
|
||
|
||
|
||
/* get the initial values */
|
||
/* call the palette routine before it is actually used to select the
|
||
conversion and preview palette to avoid doing so much duplicate code */
|
||
GetBuiltinPalette(palidx,palidx,1);
|
||
for (i=0;i<16;i++) {
|
||
for (j=0;j<3;j++) {
|
||
pseudowork[i][j] = (ushort)rgbArray[i][j];
|
||
}
|
||
}
|
||
|
||
/* merge the values from the work buffers into the pseudo-palette */
|
||
/* accumulate the additional values */
|
||
for (k = 0; k < pseudocount;k++) {
|
||
idx = pseudolist[k];
|
||
GetBuiltinPalette(idx,idx,1);
|
||
for (i=0;i<16;i++) {
|
||
for (j=0;j<3;j++) {
|
||
pseudowork[i][j] += rgbArray[i][j];
|
||
}
|
||
}
|
||
}
|
||
|
||
pseudocount++;
|
||
for (i=0;i<16;i++) {
|
||
for (j=0;j<3;j++) {
|
||
/* basic linear color distance */
|
||
/* use the average rgb values */
|
||
/* no attempt to avoid rounding */
|
||
gun = pseudowork[i][j]/pseudocount;
|
||
PseudoPalette[i][j] = (uchar)gun;
|
||
}
|
||
}
|
||
pseudocount--;
|
||
|
||
/* if quiet mode is set print the final values */
|
||
if (outputtype != SPRITE_OUTPUT) {
|
||
if (quietmode == 0){
|
||
/* rgb values can be redirected to a text file and used as an external palette
|
||
for subsequnet conversions and/or whatever else this is useful for */
|
||
for (i=0;i<16;i++)
|
||
printf("%d,%d,%d\n",PseudoPalette[i][0],PseudoPalette[i][1],PseudoPalette[i][2]);
|
||
}
|
||
}
|
||
|
||
/* for normal output print the palette list */
|
||
if (quietmode == 1) {
|
||
printf("Pseudo Palette: %d (%s)",palidx,palname[palidx]);
|
||
for (k = 0; k < pseudocount;k++) {
|
||
idx = pseudolist[k];
|
||
printf(" + %d (%s)",idx,palname[idx]);
|
||
}
|
||
printf("\n");
|
||
}
|
||
}
|
||
|
||
/* use CCIR 601 luminosity to get closest color in current palette */
|
||
/* based on palette that has been selected for conversion */
|
||
uchar GetMedColor(uchar r, uchar g, uchar b, double *paldistance)
|
||
{
|
||
uchar drawcolor;
|
||
double dr, dg, db, diffR, diffG, diffB, luma, lumadiff, distance, prevdistance;
|
||
int i;
|
||
|
||
dr = (double)r;
|
||
dg = (double)g;
|
||
db = (double)b;
|
||
luma = (dr*lumaRED + dg*lumaGREEN + db*lumaBLUE) / (255.0*1000);
|
||
lumadiff = rgbLuma[0]-luma;
|
||
|
||
/* Compare the difference of RGB values, weigh by CCIR 601 luminosity */
|
||
/* set palette index to color with shortest distance */
|
||
|
||
/* get color distance to first palette color */
|
||
diffR = (rgbDouble[0][0]-dr)/255.0;
|
||
diffG = (rgbDouble[0][1]-dg)/255.0;
|
||
diffB = (rgbDouble[0][2]-db)/255.0;
|
||
|
||
prevdistance = (diffR*diffR*dlumaRED + diffG*diffG*dlumaGREEN + diffB*diffB*dlumaGREEN)*0.75
|
||
+ lumadiff*lumadiff;
|
||
/* set palette index to first color */
|
||
drawcolor = 0;
|
||
paldistance[0] = prevdistance;
|
||
|
||
/* get color distance to rest of palette colors */
|
||
for (i=1;i<16;i++) {
|
||
|
||
/* error test for doing dithered HGR */
|
||
/* test with a 4 color palette */
|
||
if (dither7 != (uchar) 0) {
|
||
/* dither7 is set in FloydSteinberg() function */
|
||
if (dither7 == 'O') {
|
||
/* 'O' - orange-blue palette */
|
||
if (i != LOMEDBLUE && i!= LOORANGE && i!= LOWHITE) continue;
|
||
}
|
||
else {
|
||
/* 'G' - green-violet palette */
|
||
if (i != LOPURPLE && i!= LOLTGREEN && i!= LOWHITE) continue;
|
||
}
|
||
}
|
||
|
||
/* get color distance of this index */
|
||
lumadiff = rgbLuma[i]-luma;
|
||
diffR = (rgbDouble[i][0]-dr)/255.0;
|
||
diffG = (rgbDouble[i][1]-dg)/255.0;
|
||
diffB = (rgbDouble[i][2]-db)/255.0;
|
||
distance = (diffR*diffR*dlumaRED + diffG*diffG*dlumaGREEN + diffB*diffB*dlumaGREEN)*0.75
|
||
+ lumadiff*lumadiff;
|
||
|
||
/* if distance is smaller use this index */
|
||
if (distance < prevdistance) {
|
||
prevdistance = distance;
|
||
paldistance[0] = prevdistance;
|
||
drawcolor = (uchar)i;
|
||
}
|
||
|
||
}
|
||
return drawcolor;
|
||
}
|
||
|
||
|
||
/* use CCIR 601 luminosity to get closest color in current palette */
|
||
/* match values have been decreased by user-defined threshold */
|
||
/* brightens darker colors by promoting them */
|
||
uchar GetHighColor(uchar r, uchar g, uchar b, double *paldistance)
|
||
{
|
||
uchar drawcolor;
|
||
double dr, dg, db, diffR, diffG, diffB, luma, lumadiff, distance, prevdistance;
|
||
int i;
|
||
|
||
dr = (double)r;
|
||
dg = (double)g;
|
||
db = (double)b;
|
||
luma = (dr*lumaRED + dg*lumaGREEN + db*lumaBLUE) / (255.0*1000);
|
||
lumadiff = rgbLumaBrighten[0]-luma;
|
||
|
||
/* Compare the difference of RGB values, weigh by CCIR 601 luminosity */
|
||
/* set palette index to color with shortest distance */
|
||
|
||
/* get color distance to first palette color */
|
||
diffR = (rgbDoubleBrighten[0][0]-dr)/255.0;
|
||
diffG = (rgbDoubleBrighten[0][1]-dg)/255.0;
|
||
diffB = (rgbDoubleBrighten[0][2]-db)/255.0;
|
||
|
||
prevdistance = (diffR*diffR*dlumaRED + diffG*diffG*dlumaGREEN + diffB*diffB*dlumaGREEN)*0.75
|
||
+ lumadiff*lumadiff;
|
||
/* set palette index to first color */
|
||
drawcolor = 0;
|
||
paldistance[0] = prevdistance;
|
||
|
||
/* get color distance to rest of palette colors */
|
||
for (i=1;i<16;i++) {
|
||
|
||
/* error test for doing dithered HGR */
|
||
/* test with a 4 color palette */
|
||
if (dither7 != (uchar) 0) {
|
||
/* dither7 is set in FloydSteinberg() function */
|
||
if (dither7 == 'O') {
|
||
/* 'O' - orange-blue palette */
|
||
if (i != LOMEDBLUE && i!= LOORANGE && i!= LOWHITE) continue;
|
||
}
|
||
else {
|
||
/* 'G' - green-violet palette */
|
||
if (i != LOPURPLE && i!= LOLTGREEN && i!= LOWHITE) continue;
|
||
}
|
||
}
|
||
|
||
/* get color distance of to this index */
|
||
lumadiff = rgbLumaBrighten[i]-luma;
|
||
diffR = (rgbDoubleBrighten[i][0]-dr)/255.0;
|
||
diffG = (rgbDoubleBrighten[i][1]-dg)/255.0;
|
||
diffB = (rgbDoubleBrighten[i][2]-db)/255.0;
|
||
distance = (diffR*diffR*dlumaRED + diffG*diffG*dlumaGREEN + diffB*diffB*dlumaGREEN)*0.75
|
||
+ lumadiff*lumadiff;
|
||
|
||
/* if distance is smaller use this index */
|
||
if (distance < prevdistance) {
|
||
prevdistance = distance;
|
||
paldistance[0] = prevdistance;
|
||
drawcolor = (uchar)i;
|
||
}
|
||
|
||
}
|
||
return drawcolor;
|
||
}
|
||
|
||
/* use CCIR 601 luminosity to get closest color in current palette */
|
||
/* match values have been increased by user-defined threshold */
|
||
/* darkens lighter colors by demoting them */
|
||
uchar GetLowColor(uchar r, uchar g, uchar b, double *paldistance)
|
||
{
|
||
uchar drawcolor;
|
||
double dr, dg, db, diffR, diffG, diffB, luma, lumadiff, distance, prevdistance;
|
||
int i;
|
||
|
||
dr = (double)r;
|
||
dg = (double)g;
|
||
db = (double)b;
|
||
luma = (dr*lumaRED + dg*lumaGREEN + db*lumaBLUE) / (255.0*1000);
|
||
lumadiff = rgbLumaDarken[0]-luma;
|
||
|
||
/* Compare the difference of RGB values, weigh by CCIR 601 luminosity */
|
||
/* set palette index to color with shortest distance */
|
||
|
||
/* get color distance to first palette color */
|
||
diffR = (rgbDoubleDarken[0][0]-dr)/255.0;
|
||
diffG = (rgbDoubleDarken[0][1]-dg)/255.0;
|
||
diffB = (rgbDoubleDarken[0][2]-db)/255.0;
|
||
|
||
prevdistance = (diffR*diffR*dlumaRED + diffG*diffG*dlumaGREEN + diffB*diffB*dlumaGREEN)*0.75
|
||
+ lumadiff*lumadiff;
|
||
/* set palette index to first color */
|
||
drawcolor = 0;
|
||
paldistance[0] = prevdistance;
|
||
|
||
/* get color distance to rest of palette colors */
|
||
for (i=1;i<16;i++) {
|
||
|
||
/* error test for doing dithered HGR */
|
||
/* test with a 4 color palette */
|
||
if (dither7 != (uchar) 0) {
|
||
/* dither7 is set in FloydSteinberg() function */
|
||
if (dither7 == 'O') {
|
||
/* 'O' - orange-blue palette */
|
||
if (i != LOMEDBLUE && i!= LOORANGE && i!= LOWHITE) continue;
|
||
}
|
||
else {
|
||
/* 'G' - green-violet palette */
|
||
if (i != LOPURPLE && i!= LOLTGREEN && i!= LOWHITE) continue;
|
||
}
|
||
}
|
||
|
||
/* get color distance of to this index */
|
||
lumadiff = rgbLumaDarken[i]-luma;
|
||
diffR = (rgbDoubleDarken[i][0]-dr)/255.0;
|
||
diffG = (rgbDoubleDarken[i][1]-dg)/255.0;
|
||
diffB = (rgbDoubleDarken[i][2]-db)/255.0;
|
||
distance = (diffR*diffR*dlumaRED + diffG*diffG*dlumaGREEN + diffB*diffB*dlumaGREEN)*0.75
|
||
+ lumadiff*lumadiff;
|
||
|
||
/* if distance is smaller use this index */
|
||
if (distance < prevdistance) {
|
||
prevdistance = distance;
|
||
paldistance[0] = prevdistance;
|
||
drawcolor = (uchar)i;
|
||
}
|
||
|
||
}
|
||
return drawcolor;
|
||
}
|
||
|
||
/* switchboard function to handle cross-hatched and non-cross-hatched output */
|
||
/* keeps the conditionals out of the main loop */
|
||
uchar GetDrawColor(uchar r, uchar g, uchar b, int x, int y)
|
||
{
|
||
|
||
/* additional vars for future */
|
||
double distance, lowdistance, highdistance;
|
||
uchar drawcolor, lowcolor, highcolor;
|
||
uchar red = (uchar)(r >> 4),
|
||
green = (uchar)(g >> 4),
|
||
blue = (uchar)(b >> 4);
|
||
int i;
|
||
|
||
/* quick check for verbatim match */
|
||
for (i = 0; i < 16; i++) {
|
||
|
||
/* error test for doing dithered HGR */
|
||
/* test with a 4 color palette */
|
||
if (i!= 0 && dither7 != (uchar) 0) {
|
||
/* dither7 is set in FloydSteinberg() function */
|
||
if (dither7 == 'O') {
|
||
/* 'O' - orange-blue palette */
|
||
if (i != LOMEDBLUE && i!= LOORANGE && i!= LOWHITE) continue;
|
||
}
|
||
else {
|
||
/* 'G' - green-violet palette */
|
||
if (i != LOPURPLE && i!= LOLTGREEN && i!= LOWHITE) continue;
|
||
}
|
||
}
|
||
|
||
if (rgbAppleArray[i][0] == red &&
|
||
rgbAppleArray[i][1] == green &&
|
||
rgbAppleArray[i][2] == blue) return (uchar)i;
|
||
|
||
}
|
||
|
||
/* non-cross-hatched output */
|
||
if (threshold == 0 && ymatrix == 0) return GetMedColor(r,g,b,&distance);
|
||
|
||
if (ymatrix != 0) {
|
||
switch(ymatrix) {
|
||
case 1: return GetLowColor(r,g,b,&lowdistance);
|
||
case 3: return GetHighColor(r,g,b,&highdistance);
|
||
case 2:
|
||
default:return GetMedColor(r,g,b,&distance);
|
||
}
|
||
}
|
||
|
||
/* patterned cross-hatching */
|
||
/* the thresholds are percentage based */
|
||
/* with a user definable threshold */
|
||
|
||
switch(xmatrix)
|
||
{
|
||
/* patterns 1, 2, 3 - 2 x 2 patterned cross-hatching */
|
||
case 1:
|
||
/* low, med
|
||
med, low
|
||
*/
|
||
if (y % 2 == 0) {
|
||
if (x%2 == 1) return GetMedColor(r,g,b,&distance);
|
||
return GetLowColor(r,g,b,&lowdistance);
|
||
}
|
||
if (x%2 == 0) return GetMedColor(r,g,b,&distance);
|
||
return GetLowColor(r,g,b,&lowdistance);
|
||
|
||
case 3:
|
||
/* high, med
|
||
med, high
|
||
*/
|
||
if (y % 2 == 0) {
|
||
if (x%2 == 1) return GetMedColor(r,g,b,&distance);
|
||
return GetHighColor(r,g,b,&highdistance);
|
||
}
|
||
if (x%2 == 0) return GetMedColor(r,g,b,&distance);
|
||
return GetHighColor(r,g,b,&highdistance);
|
||
|
||
case 2:
|
||
default:
|
||
/* high, low
|
||
low, high
|
||
*/
|
||
if (y % 2 == 0) {
|
||
if (x%2 == 1) return GetLowColor(r,g,b,&lowdistance);
|
||
return GetHighColor(r,g,b,&highdistance);
|
||
}
|
||
if (x%2 == 0) return GetLowColor(r,g,b,&lowdistance);
|
||
return GetHighColor(r,g,b,&highdistance);
|
||
|
||
}
|
||
|
||
#ifndef TURBOC
|
||
/* never gets to here */
|
||
return GetMedColor(r,g,b,&distance);
|
||
#endif
|
||
|
||
}
|
||
|
||
/* routines to save to Apple 2 Double Hires Format */
|
||
/* a double hi-res pixel can occur at any one of 7 positions */
|
||
/* in a 4 byte block which spans aux and main screen memory */
|
||
/* the horizontal resolution is 140 pixels */
|
||
void dhrplot(int x,int y,uchar drawcolor)
|
||
{
|
||
int xoff, pattern;
|
||
uchar *ptraux, *ptrmain;
|
||
|
||
pattern = (x%7);
|
||
xoff = HB[y] + ((x/7) * 2);
|
||
ptraux = (uchar *) &dhrbuf[xoff-0x2000];
|
||
ptrmain = (uchar *) &dhrbuf[xoff];
|
||
|
||
|
||
switch(pattern)
|
||
{
|
||
/* left this here for reference
|
||
|
||
uchar dhrpattern[7][4] = {
|
||
0,0,0,0,
|
||
0,0,0,1,
|
||
1,1,1,1,
|
||
1,1,2,2,
|
||
2,2,2,2,
|
||
2,3,3,3,
|
||
3,3,3,3};
|
||
*/
|
||
|
||
case 0: ptraux[0] &= 0x70;
|
||
ptraux[0] |= dhrbytes[drawcolor][0] &0x0f;
|
||
break;
|
||
case 1: ptraux[0] &= 0x0f;
|
||
ptraux[0] |= dhrbytes[drawcolor][0] & 0x70;
|
||
ptrmain[0] &= 0x7e;
|
||
ptrmain[0] |= dhrbytes[drawcolor][1] & 0x01;
|
||
break;
|
||
case 2: ptrmain[0] &= 0x61;
|
||
ptrmain[0] |= dhrbytes[drawcolor][1] & 0x1e;
|
||
break;
|
||
case 3: ptrmain[0] &= 0x1f;
|
||
ptrmain[0] |= dhrbytes[drawcolor][1] & 0x60;
|
||
ptraux[1] &= 0x7c;
|
||
ptraux[1] |= dhrbytes[drawcolor][2] & 0x03;
|
||
break;
|
||
case 4: ptraux[1] &= 0x43;
|
||
ptraux[1] |= dhrbytes[drawcolor][2] & 0x3c;
|
||
break;
|
||
case 5: ptraux[1] &= 0x3f;
|
||
ptraux[1] |= dhrbytes[drawcolor][2] & 0x40;
|
||
ptrmain[1] &= 0x78;
|
||
ptrmain[1] |= dhrbytes[drawcolor][3] & 0x07;
|
||
break;
|
||
case 6: ptrmain[1] &= 0x07;
|
||
ptrmain[1] |= dhrbytes[drawcolor][3] & 0x78;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/* monochrome DHGR - 560 x 192 */
|
||
unsigned char dhbmono[] = {0x7e,0x7d,0x7b,0x77,0x6f,0x5f,0x3f};
|
||
unsigned char dhwmono[] = {0x1,0x2,0x4,0x8,0x10,0x20,0x40};
|
||
|
||
void dhrmonoplot(int x, int y, uchar drawcolor)
|
||
{
|
||
|
||
int xoff, pixel;
|
||
uchar *ptr;
|
||
|
||
if (x > 559) return;
|
||
|
||
xoff = HB[y] + (x/14);
|
||
pixel = (x%14);
|
||
if (pixel > 6) {
|
||
/* main memory */
|
||
pixel -= 7;
|
||
ptr = (uchar *) &dhrbuf[xoff];
|
||
}
|
||
else {
|
||
/* auxiliary memory */
|
||
ptr = (uchar *) &dhrbuf[xoff-0x2000];
|
||
}
|
||
|
||
if (drawcolor != 0) {
|
||
/* white */
|
||
ptr[0] |= dhwmono[pixel]; /* inclusive OR */
|
||
}
|
||
else {
|
||
/* black */
|
||
ptr[0] &= dhbmono[pixel]; /* bitwise AND */
|
||
}
|
||
|
||
}
|
||
|
||
/* monochrome HGR 280 x 192 */
|
||
void hrmonoplot(int x, int y, uchar drawcolor)
|
||
{
|
||
|
||
int xoff, pixel;
|
||
uchar *ptr;
|
||
|
||
if (x > 279) return;
|
||
|
||
xoff = HB[y] + (x/7);
|
||
pixel = (x%7);
|
||
/* main memory */
|
||
ptr = (uchar *) &dhrbuf[xoff-0x2000];
|
||
|
||
if (drawcolor != 0) {
|
||
/* white */
|
||
ptr[0] |= dhwmono[pixel]; /* inclusive OR */
|
||
}
|
||
else {
|
||
/* black */
|
||
ptr[0] &= dhbmono[pixel]; /* bitwise AND */
|
||
}
|
||
|
||
}
|
||
|
||
void dhrfill(int y,uchar drawcolor)
|
||
{
|
||
int xoff, x;
|
||
uchar *ptraux, *ptrmain;
|
||
|
||
xoff = HB[y];
|
||
|
||
ptraux = (uchar *) &dhrbuf[xoff-0x2000];
|
||
ptrmain = (uchar *) &dhrbuf[xoff];
|
||
|
||
for (x = 0,xoff=0; x < 20; x++) {
|
||
ptraux[xoff] = dhrbytes[drawcolor][0];
|
||
ptrmain[xoff] = dhrbytes[drawcolor][1]; xoff++;
|
||
ptraux[xoff] = dhrbytes[drawcolor][2];
|
||
ptrmain[xoff] = dhrbytes[drawcolor][3]; xoff++;
|
||
}
|
||
}
|
||
|
||
|
||
/* initialize the scanlines in the write buffer
|
||
to the background color
|
||
|
||
this doesn't matter for a full-screen image
|
||
|
||
*/
|
||
void dhrclear()
|
||
{
|
||
int y;
|
||
uchar drawcolor;
|
||
memset(dhrbuf,0,16384);
|
||
if (backgroundcolor == LOBLACK) return;
|
||
drawcolor = (uchar)backgroundcolor;
|
||
for (y=0;y<192;y++) dhrfill(y,drawcolor);
|
||
}
|
||
|
||
/* mono-spaced "tom thumb" 4 x 6 font */
|
||
/* using a byte map to gain a little speed at the expense of memory */
|
||
/* a bitmap could have been encoded into nibbles of 3 bytes per character
|
||
rather than the 18 bytes per character that I am using
|
||
but the trade-off in the speed in unmasking would have slowed this down */
|
||
void plotthumbDHGR(unsigned char ch, unsigned x, unsigned y,
|
||
unsigned char fg, unsigned char bg)
|
||
{
|
||
unsigned offset, x1, x2=x+3, y2=y+6, xmono;
|
||
unsigned char byte;
|
||
|
||
if (ch < 33 || ch > 127) ch = 0;
|
||
else ch -=32;
|
||
|
||
if (ch == 0 && bg > 15) return;
|
||
|
||
/* each of the 96 characters is encoded into 18 bytes */
|
||
offset = (18 * ch);
|
||
|
||
while (y < y2) {
|
||
xmono = x * 2;
|
||
for (x1 = x; x1 < x2; x1++,xmono+=2) {
|
||
if (x1 > 139) {
|
||
offset++;
|
||
continue;
|
||
}
|
||
|
||
byte = tomthumb[offset++];
|
||
|
||
if (byte == 0) {
|
||
if (bg > 15) continue;
|
||
if (hgroutput == 1 && mono == 1) {
|
||
hrmonoplot(xmono,y,bg);
|
||
hrmonoplot(xmono+1,y,bg);
|
||
}
|
||
else {
|
||
dhrplot(x1,y,bg);
|
||
}
|
||
}
|
||
else {
|
||
if (fg > 15) continue;
|
||
if (hgroutput == 1 && mono == 1) {
|
||
hrmonoplot(xmono,y,fg);
|
||
hrmonoplot(xmono+1,y,fg);
|
||
}
|
||
else {
|
||
dhrplot(x1,y,fg);
|
||
}
|
||
}
|
||
}
|
||
/* if background color is being used then a trailing pixel is required
|
||
between characters */
|
||
if (bg < 16 && x2 < 140) {
|
||
if (hgroutput == 1 && mono == 1) {
|
||
hrmonoplot(xmono,y,bg);
|
||
hrmonoplot(xmono+1,y,bg);
|
||
}
|
||
else {
|
||
dhrplot(x2,y,bg);
|
||
}
|
||
}
|
||
|
||
if (y++ > 191) break;
|
||
}
|
||
|
||
}
|
||
|
||
/* normally spaced 4 x 6 font */
|
||
/* using character plotting function plotthumb() (above) */
|
||
void thumbDHGR(char *str,unsigned x, unsigned y,
|
||
unsigned char fg,unsigned char bg, unsigned char justify)
|
||
{
|
||
int target;
|
||
unsigned char ch;
|
||
|
||
if (justify == 'M' || justify == 'm') {
|
||
target = strlen(str);
|
||
x-= ((target * 4) /2);
|
||
}
|
||
|
||
while ((ch = *str++) != 0) {
|
||
plotthumbDHGR(ch,x,y,fg,bg);
|
||
x+=4;
|
||
}
|
||
}
|
||
|
||
|
||
/* VBMP output routines */
|
||
/* VBMP requires a palette in the DHGR color order
|
||
|
||
My Apple II routines whether in cc65, Aztec C65, or in my converters
|
||
always use the LORES color order. LORES, Double LORES, and DHGR all use
|
||
the same colors so it seems rather silly to use a different index value
|
||
for DHGR in re-usable program code.
|
||
|
||
However, since VBMP doesn't really care about the color palette in a BMP
|
||
and just the order of the palette, we need to remap the palette and the
|
||
scanline palette indices to DHGR color order.
|
||
|
||
|
||
*/
|
||
|
||
ushort WriteVbmpHeader(FILE *fp)
|
||
{
|
||
ushort outpacket;
|
||
int c, i, j;
|
||
|
||
/* BMP scanlines are padded to a multiple of 4 bytes (DWORD) */
|
||
outpacket = (ushort)72;
|
||
|
||
if (mono != 0 || hgroutput == 1) {
|
||
if (hgroutput == 1) {
|
||
outpacket = 36;
|
||
c = fwrite(mono280,1,sizeof(mono192),fp);
|
||
}
|
||
else {
|
||
c = fwrite(mono192,1,sizeof(mono192),fp);
|
||
}
|
||
if (c!= sizeof(mono192))return 0;
|
||
return outpacket;
|
||
}
|
||
|
||
memset((char *)&mybmp.bfi.bfType[0],0,sizeof(BMPHEADER));
|
||
|
||
/* create the info header */
|
||
mybmp.bmi.biSize = (ulong)40;
|
||
mybmp.bmi.biWidth = (ulong)140;
|
||
mybmp.bmi.biHeight = (ulong)192;
|
||
mybmp.bmi.biPlanes = 1;
|
||
mybmp.bmi.biBitCount = 4;
|
||
mybmp.bmi.biCompression = (ulong) BI_RGB;
|
||
|
||
mybmp.bmi.biSizeImage = (ulong)outpacket;
|
||
mybmp.bmi.biSizeImage *= mybmp.bmi.biHeight;
|
||
|
||
/* create the file header */
|
||
mybmp.bfi.bfType[0] = 'B';
|
||
mybmp.bfi.bfType[1] = 'M';
|
||
mybmp.bfi.bfOffBits = (ulong) sizeof(BMPHEADER) + sizeof(RGBQUAD) * 16;
|
||
mybmp.bfi.bfSize = mybmp.bmi.biSizeImage + mybmp.bfi.bfOffBits;
|
||
|
||
/* write the header for the output BMP */
|
||
c = fwrite((char *)&mybmp.bfi.bfType[0],sizeof(BMPHEADER),1,fp);
|
||
|
||
if (c!= 1)return 0;
|
||
|
||
/* use the current conversion palette for the VBMP palette */
|
||
/* rather than the preview palette */
|
||
for (i=0;i<16;i++) {
|
||
j = RemapLoToHi[i];
|
||
sbmp[i].rgbRed = rgbVBMP[j][RED];
|
||
sbmp[i].rgbGreen = rgbVBMP[j][GREEN];
|
||
sbmp[i].rgbBlue = rgbVBMP[j][BLUE];
|
||
|
||
}
|
||
|
||
/* write the palette for the output bmp */
|
||
c = fwrite((char *)&sbmp[0].rgbBlue, sizeof(RGBQUAD)*16,1,fp);
|
||
if (c!= 1)return 0;
|
||
|
||
return outpacket;
|
||
}
|
||
|
||
/* decodes scanlines from hgr or dhgr monochrome buffer */
|
||
void applemonobites(int y, int doubleres)
|
||
{
|
||
int xoff,idx;
|
||
unsigned char *ptraux, *ptrmain, ch;
|
||
|
||
xoff = HB[y];
|
||
ptraux = (unsigned char *) &dhrbuf[xoff-0x2000];
|
||
ptrmain = (unsigned char *) &dhrbuf[xoff];
|
||
|
||
xoff = 0;
|
||
for (idx = 0; idx < 40; idx++) {
|
||
|
||
ch = ptraux[idx];
|
||
|
||
buf280[xoff] = ((ch) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 1) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 2) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 3) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 4) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 5) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 6) &1); xoff++;
|
||
|
||
if (doubleres == 0) continue;
|
||
|
||
ch = ptrmain[idx];
|
||
|
||
buf280[xoff] = ((ch) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 1) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 2) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 3) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 4) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 5) &1); xoff++;
|
||
buf280[xoff] = ((ch >> 6) &1); xoff++;
|
||
|
||
}
|
||
}
|
||
|
||
/* encodes monochrome bmp scanline */
|
||
void ibmmonobites()
|
||
{
|
||
int i,j,k;
|
||
unsigned char bits[8];
|
||
|
||
j=0;
|
||
for(i=0;i<35;i++)
|
||
{
|
||
for(k=0;k<8;k++)
|
||
{
|
||
bits[k] = buf280[j]; j++;
|
||
}
|
||
bmpscanline[i] = (bits[0]<<7|bits[1]<<6|bits[2]<<5|bits[3]<<4|
|
||
bits[4]<<3|bits[5]<<2|bits[6]<<1|bits[7]);
|
||
}
|
||
}
|
||
|
||
|
||
/* writes VBMP compatible 140 x 192 x 16 color bmp or VBMP monochrome bmp in 2 sizes */
|
||
int WriteVBMPFile()
|
||
{
|
||
|
||
FILE *fp;
|
||
uchar ch;
|
||
int x,x1,y,y2,idx,j,packet=72;
|
||
|
||
if (hgroutput == 1) packet = 36;
|
||
|
||
fp = fopen(vbmpfile,"wb");
|
||
|
||
if (fp == NULL) {
|
||
printf("Error opening %s for writing!\n",vbmpfile);
|
||
return INVALID;
|
||
}
|
||
|
||
if (WriteVbmpHeader(fp) == 0) {
|
||
fclose(fp);
|
||
remove(vbmpfile);
|
||
printf("Error writing header to %s!\n",vbmpfile);
|
||
return INVALID;
|
||
}
|
||
memset(&bmpscanline[0],0,packet);
|
||
|
||
/* write 4 bit packed scanlines */
|
||
/* remap from LORES color order to DHGR color order */
|
||
/* VBMP does not use the colors in the palette, just the color order */
|
||
|
||
y2 = 191;
|
||
for (y = 0; y< 192; y++) {
|
||
if (hgroutput == 1) {
|
||
applemonobites(y2,0);
|
||
ibmmonobites();
|
||
}
|
||
else {
|
||
for (x = 0, x1=0; x < 140; x++) {
|
||
if (x%2 == 0) {
|
||
idx = dhrgetpixel(x,y2);
|
||
/* range check */
|
||
if (idx < 0 || idx > 15)idx = 0; /* default black */
|
||
j = RemapLoToHi[idx];
|
||
ch = (uchar)j << 4;
|
||
}
|
||
else {
|
||
idx = dhrgetpixel(x,y2);
|
||
/* range check */
|
||
if (idx < 0 || idx > 15)idx = 0; /* default black */
|
||
j = RemapLoToHi[idx];
|
||
bmpscanline[x1] = ch | (uchar)j; x1++;
|
||
}
|
||
}
|
||
}
|
||
|
||
fwrite((char *)&bmpscanline[0],1,packet,fp);
|
||
y2 -= 1;
|
||
}
|
||
|
||
fclose(fp);
|
||
if (quietmode == 1)printf("%s created!\n",vbmpfile);
|
||
return SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
/* plain old HGR transformation routines for DHGR 6-color HGR pseudo-output */
|
||
/* encodes apple II hgr scanline into buffer */
|
||
void hgrbits(int y)
|
||
{
|
||
int xoff,idx,jdx;
|
||
unsigned char *ptr, bits[7], x1, palettebit;
|
||
|
||
xoff = HB[y]-0x2000;
|
||
ptr = (unsigned char *) &hgrbuf[xoff];
|
||
|
||
xoff = 0;
|
||
for (idx = 0; idx < 40; idx++) {
|
||
|
||
for (jdx = 0; jdx < 7; jdx++) {
|
||
bits[jdx] = buf280[xoff]; xoff++;
|
||
}
|
||
palettebit = palettebits[idx];
|
||
|
||
x1 = (palettebit | bits[6]<<6|bits[5]<<5|bits[4]<<4|
|
||
bits[3]<<3|bits[2]<<2|bits[1]<<1|bits[0]);
|
||
|
||
ptr[idx] = x1;
|
||
}
|
||
}
|
||
|
||
void buildhgr()
|
||
{
|
||
int i, j;
|
||
|
||
/* create bit pattern from pixelized values */
|
||
/* i is even and j is odd */
|
||
for (i= 0, j = 1; i < 280; i+=2, j+=2) {
|
||
buf280[i] = 0; /* assume everything is black */
|
||
buf280[j] = 0;
|
||
|
||
/* add the white bits - this also accounts for the half shift of
|
||
the color pixels which applewin renders as white to represent
|
||
aliasing of the color anomalies */
|
||
|
||
if (doublewhite == 1) {
|
||
/* if double white is on, set the white pixels in pairs */
|
||
if (work280[i] == HWHITE || work280[j] == HWHITE) {
|
||
buf280[i] = buf280[j] = 1;
|
||
}
|
||
}
|
||
else {
|
||
/* otherwise set white pixels individually */
|
||
if (work280[i] == HWHITE) buf280[i] = 1;
|
||
if (work280[j] == HWHITE) buf280[j] = 1;
|
||
}
|
||
|
||
/* if double colors is on, set the color pixels in pairs */
|
||
if (doublecolors == 1) {
|
||
/* add the violet or blue bits - the 2-bit value will be 2 */
|
||
if (work280[i] == HBLUE || work280[i] == HVIOLET ||
|
||
work280[j] == HBLUE || work280[j] == HVIOLET ) {
|
||
buf280[i] = 1;
|
||
buf280[j] = 0;
|
||
}
|
||
|
||
/* add the green or orange bits - the 2-bit value will be 1 */
|
||
if (work280[i] == HORANGE || work280[i] == HGREEN ||
|
||
work280[j] == HORANGE || work280[j] == HGREEN ) {
|
||
buf280[i] = 0;
|
||
buf280[j] = 1;
|
||
}
|
||
|
||
}
|
||
else {
|
||
/* otherwise set the colors individually if double colors is off */
|
||
/* add the violet or blue bits - the 2-bit value will be 2 */
|
||
if (work280[i] == HBLUE || work280[i] == HVIOLET )buf280[i] = 1;
|
||
if (work280[j] == HBLUE || work280[j] == HVIOLET )buf280[j] = 0;
|
||
|
||
/* add the green or orange bits - the 2-bit value will be 1 */
|
||
if (work280[i] == HORANGE || work280[i] == HGREEN )buf280[i] = 0;
|
||
if (work280[j] == HORANGE || work280[j] == HGREEN )buf280[j] = 1;
|
||
}
|
||
|
||
if (doubleblack == 1) {
|
||
/* be careful here - this can foul the colors */
|
||
if (work280[i] == HBLACK || work280[j]==HBLACK) {
|
||
buf280[i] = 0;
|
||
buf280[j] = 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
void hgrline(int y)
|
||
{
|
||
int x,i,j,k,l, green, orange;
|
||
unsigned char c, p;
|
||
|
||
/* read the 6-color DHGR buffer and translate to HGR */
|
||
/* since DHGR is 140 pixels in width and HGR is 280, double each pixel */
|
||
/* HGR is only 140 color pixels in width so effective color resolution is identical */
|
||
/* this is a really slack method of converting to HGR because it ignores the half-pixel shift for black and white
|
||
pixels so technically half the available detail is lost on images with large areas of black and white but
|
||
generally on complex images with lots of colors artifacting is minimized by doubling-up
|
||
|
||
some of my other converters like Bmp2RAG offer more options but they don't provided dithering.
|
||
|
||
*/
|
||
|
||
/* double colors */
|
||
for (x=0,i=0,j=1;x<140;x++,i+=2,j+=2) {
|
||
/* get the DHGR color */
|
||
k = dhrgetpixel(x,y);
|
||
/* remap to the HGR color indices */
|
||
work280[i] = work280[j] = dhgr2hgr[k];
|
||
}
|
||
|
||
/* single colors - shift image right by one nominal pixel */
|
||
/* otherwise this setting will have no effect */
|
||
if (doublecolors == 0) {
|
||
for (x = 279;x > 0;x--) {
|
||
work280[x] = work280[x-1];
|
||
}
|
||
}
|
||
|
||
buildhgr();
|
||
|
||
/* set the HGR palette based on groups of seven HGR pixels */
|
||
if (hgrpaltype == 0 || hgrpaltype == 0x80) {
|
||
/* single palette over-ride... 4 color output. all non-black and
|
||
non-white pixels will be converted to either Green-Violet or
|
||
Orange-Blue */
|
||
for (i = 0; i < 40; i++) palettebits[i] = hgrpaltype;
|
||
}
|
||
else {
|
||
/* seed palette hi-bit with some value */
|
||
if (hgrcolortype == 'G' || hgrcolortype == 'V') p = 0;
|
||
else p = 0x80;
|
||
|
||
/* go through the 280 pixel scanline and determine precedence of colors
|
||
for the palette bit settings based on the command option selected. */
|
||
|
||
for (i = 0, k=0; i < 40; i++ ) {
|
||
orange = 0;
|
||
green = 0;
|
||
for (j = 0; j < 7; j++) {
|
||
/* count in groups of 7 pixels (really 3.5 color pixels) */
|
||
if (work280[k] == HORANGE || work280[k] == HBLUE) orange++;
|
||
if (work280[k] == HGREEN || work280[k] == HVIOLET) green++;
|
||
k++;
|
||
}
|
||
|
||
if (hgrcolortype == 'O') {
|
||
/* big orange - one orange pixel sets the palette */
|
||
/* orange blue */
|
||
if (orange > 0) p = 0x80;
|
||
else {
|
||
if (green > 0) p = 0;
|
||
}
|
||
}
|
||
else if (hgrcolortype == 'G') {
|
||
/* big green - one green pixel sets the palette */
|
||
/* green violet */
|
||
if (green > 0) p = 0;
|
||
else {
|
||
if (orange > 0) p = 0x80;
|
||
}
|
||
}
|
||
else {
|
||
/* normal precedence - the dominant color group sets the palette */
|
||
if (green > orange) p = 0;
|
||
else if (orange > green) p = 0x80;
|
||
else {
|
||
/* but if both groups are equal then 3 - options for behaviour */
|
||
/* little green - equal green and orange sets the palette to green */
|
||
if (hgrcolortype == 'V' && green == orange) p = 0;
|
||
/* little orange - equal green and orange sets the palette to orange */
|
||
else if (hgrcolortype == 'B' && orange == green) p = 0x80;
|
||
else if (orange > 0) {
|
||
/* it was either do this or carry the previous palette bit setting forward */
|
||
if (hgrcolortype == 'G' || hgrcolortype == 'V') p = 0;
|
||
else p = 0x80;
|
||
}
|
||
}
|
||
}
|
||
palettebits[i] = p;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/* routines to save to Apple 2 Lores Format */
|
||
|
||
#define LORAGWIDTH 80
|
||
#define LORAGHEIGHT 48
|
||
#define LORAGSIZE 1920
|
||
#define LOTOPSIZE 1600
|
||
#define LOBINSIZE 1016
|
||
|
||
|
||
/* Lo-Res and Double Lo-Res Routines */
|
||
|
||
/* The Lo-Res Display has a resolution of 40 x 48 x 16 colors.
|
||
|
||
The Double Lo-Res display has a resolution of 80 x 48 x 16 colors.
|
||
|
||
In either case, these are the same 16 colors used by the DHGR display.
|
||
|
||
The nominal resolution for a Double Lo-Res Screen is 320 x 192
|
||
|
||
/* routines to save to Apple 2 Lores Format */
|
||
|
||
/* sets the pixels in the lores buffer (hgrbuf) */
|
||
void setlopixel(unsigned char color,int x, int y,int ragflag)
|
||
{
|
||
unsigned char *crt, c1, c2;
|
||
int y1, offset;
|
||
|
||
y1 = y / 2;
|
||
|
||
c2 = (unsigned char ) (color & 15);
|
||
|
||
if (y%2 == 0) {
|
||
/* even rows in low nibble */
|
||
/* mask value to preserve high nibble */
|
||
c1 = 240;
|
||
}
|
||
else {
|
||
/* odd rows in high nibble */
|
||
/* mask value to preserve low nibble */
|
||
c1 = 15;
|
||
c2 = c2 * 16;
|
||
}
|
||
|
||
if (ragflag)
|
||
offset = (y1 * 80) + x;
|
||
else
|
||
offset = (textbase[y1]-1024)+x;
|
||
|
||
crt = (unsigned char *)&hgrbuf[offset];
|
||
crt[0] &= c1;
|
||
crt[0] |= c2;
|
||
}
|
||
|
||
|
||
/* save LGR or DLGR output files in either raster-oriented or BSAVE format */
|
||
/* only full-screen (48 line) or mixed-screen (40 line) files are supported for raster-oriented files */
|
||
/* only full-sceen format is supported for BSAVE files */
|
||
/* image fragments are not supported */
|
||
int savelofragment()
|
||
{
|
||
|
||
FILE *fp;
|
||
unsigned char outfile[MAXF], temp, remap;
|
||
int x,y,x2,y2, offset;
|
||
ushort fl = 1016; /* default LGR or DLGR file size in bytes - BSAVE format */
|
||
|
||
/* raster files - single file output */
|
||
if (applesoft == 0) {
|
||
/* save single lo-res and double lo-res */
|
||
/* save raster images of 48 or 40 scanlines
|
||
(full graphics or mixed text and graphics) */
|
||
|
||
if (lores == 1) {
|
||
if (appletop == 1) {
|
||
fl = 802;
|
||
sprintf(outfile,"%s.STO",hgrwork);
|
||
}
|
||
else {
|
||
fl = 962;
|
||
sprintf(outfile,"%s.SLO",hgrwork);
|
||
}
|
||
}
|
||
else {
|
||
if (appletop == 1) {
|
||
fl = 1602;
|
||
sprintf(outfile,"%s.DTO",hgrwork);
|
||
}
|
||
else {
|
||
fl = 1922;
|
||
sprintf(outfile,"%s.DLO",hgrwork);
|
||
}
|
||
}
|
||
if (tags == 1) {
|
||
strcat(outfile,"#060400");
|
||
}
|
||
fp = fopen(outfile,"wb");
|
||
if (NULL == fp)return INVALID;
|
||
WriteDosHeader(fp,fl,1024);
|
||
|
||
/* On the double lo res display each byte in
|
||
high memory is interleaved with a byte in low memory
|
||
in the interests of efficiency I am saving and loading
|
||
the interleaf on a scanline by scanline basis.
|
||
*/
|
||
memset(hgrbuf,0,LORAGSIZE);
|
||
for (y = 0; y< 48; y++) {
|
||
if (appletop == 1 && y > 39)break;
|
||
y2 = y;
|
||
/* first 40 bytes goes to auxiliary memory (even pixels) */
|
||
for (x = 0; x < 40; x++) {
|
||
x2 = (x*2);
|
||
remap = dhrgetpixel(x2,y2);
|
||
temp = dloauxcolor[remap];
|
||
setlopixel(temp,x,y,1);
|
||
}
|
||
/* followed by the interleaf (odd pixels)
|
||
next 40 bytes goes to main memory */
|
||
for (x = 0; x < 40; x++) {
|
||
x2 = (x*2) + 1;
|
||
temp = dhrgetpixel(x2,y2);
|
||
setlopixel(temp,x+40,y,1);
|
||
}
|
||
}
|
||
if (lores == 1) {
|
||
fputc(40,fp); /* bytes */
|
||
if (appletop == 1) fputc(20,fp);
|
||
else fputc(24,fp);
|
||
|
||
for (y = 0; y < 24; y++) {
|
||
if (appletop == 1 && y > 19)break;
|
||
offset = (y * 80)+40;
|
||
fwrite((unsigned char *)&hgrbuf[offset],1,40,fp);
|
||
}
|
||
}
|
||
else {
|
||
fputc(80,fp); /* bytes */
|
||
if (appletop == 1) {
|
||
fputc(20,fp); /* bytes (rasters / 2) */
|
||
fwrite(hgrbuf,1,LOTOPSIZE,fp);
|
||
}
|
||
else {
|
||
fputc(24,fp); /* bytes (rasters / 2) */
|
||
fwrite(hgrbuf,1,LORAGSIZE,fp);
|
||
}
|
||
}
|
||
fclose(fp);
|
||
printf("%s Saved!",outfile);
|
||
}
|
||
else {
|
||
|
||
/* bsaved images */
|
||
/* 2 files for double lo-res */
|
||
/* one file for lo-res */
|
||
|
||
/* these cannot be loaded in a ProDOS BASIC program and should
|
||
probably not be loaded in a C program */
|
||
/* they are arguably unsafe to load even in DOS 3.3
|
||
since they clobber the text screen holes */
|
||
|
||
/* for double lo-res the bsaved images are split into two files
|
||
the first file is loaded into aux mem
|
||
*/
|
||
if (lores == 0) {
|
||
sprintf(outfile,"%s.DL1",hgrwork);
|
||
if (tags == 1) {
|
||
strcat(outfile,"#060400");
|
||
}
|
||
fp = fopen(outfile,"wb");
|
||
if (NULL == fp)return INVALID;
|
||
WriteDosHeader(fp,fl,1024);
|
||
|
||
memset(hgrbuf,0,LOBINSIZE);
|
||
for (y = 0; y< 48; y++) {
|
||
y2 = y;
|
||
for (x = 0; x < 40; x++) {
|
||
x2 = (x*2);
|
||
remap = dhrgetpixel(x2,y2);
|
||
temp = dloauxcolor[remap];
|
||
setlopixel(temp,x,y,0);
|
||
}
|
||
}
|
||
fwrite(hgrbuf,1,LOBINSIZE,fp);
|
||
fclose(fp);
|
||
printf("%s Saved!",outfile);
|
||
}
|
||
|
||
/*
|
||
for single lo res only 1 file is needed
|
||
for double lo res the second file is loaded into main mem
|
||
*/
|
||
if (lores == 1)
|
||
sprintf(outfile,"%s.SL2",hgrwork);
|
||
else
|
||
sprintf(outfile,"%s.DL2",hgrwork);
|
||
if (tags == 1) {
|
||
strcat(outfile,"#060400");
|
||
}
|
||
fp = fopen(outfile,"wb");
|
||
if (NULL == fp)return INVALID;
|
||
WriteDosHeader(fp,fl,1024);
|
||
memset(hgrbuf,0,LOBINSIZE);
|
||
for (y = 0; y< 48; y++) {
|
||
y2 = y;
|
||
for (x = 0; x < 40; x++) {
|
||
x2 = (x*2) + 1;
|
||
temp = dhrgetpixel(x2,y2);
|
||
setlopixel(temp,x,y,0);
|
||
}
|
||
}
|
||
fwrite(hgrbuf,1,LOBINSIZE,fp);
|
||
fclose(fp);
|
||
printf("%s Saved!",outfile);
|
||
}
|
||
|
||
return SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
/* save both raw output file formats */
|
||
int savedhr()
|
||
{
|
||
|
||
FILE *fp;
|
||
int c,y;
|
||
|
||
if (outputtype != BIN_OUTPUT) return SUCCESS;
|
||
|
||
if (loresoutput == 1) {
|
||
savelofragment();
|
||
return SUCCESS;
|
||
}
|
||
|
||
/* titling from text files if found */
|
||
GetUserTextFile();
|
||
|
||
|
||
if (hgroutput == 1) {
|
||
/* just using the BIN file extension as always */
|
||
if (mono == 0) {
|
||
strcpy(mainfile,hgrcolor);
|
||
memset(hgrbuf,0,8192);
|
||
for (y = 0; y < 192; y++) {
|
||
hgrline(y); /* translate from DHGR and format the HGR line */
|
||
hgrbits(y); /* put the HGR line into the HGR file buffer */
|
||
}
|
||
}
|
||
else {
|
||
strcpy(mainfile,hgrmono);
|
||
}
|
||
fp = fopen(mainfile,"wb");
|
||
if (NULL == fp) {
|
||
if (quietmode == 1)printf("Error Opening %s for writing!\n",mainfile);
|
||
return INVALID;
|
||
}
|
||
|
||
WriteDosHeader(fp,8192,8192);
|
||
|
||
if (mono == 1) c = fwrite(dhrbuf,1,8192,fp);
|
||
else c = fwrite(&hgrbuf[0],1,8192,fp);
|
||
fclose(fp);
|
||
if (c != 8192) {
|
||
remove(mainfile);
|
||
if (quietmode == 1)printf("Error Writing %s!\n",mainfile);
|
||
return INVALID;
|
||
}
|
||
|
||
if (quietmode == 1) printf("%s created!\n",mainfile);
|
||
if (vbmp != 0) {
|
||
/* additional BMP file for Cybernesto's VBMP */
|
||
if (mono == 0) memcpy(&dhrbuf[0],&hgrbuf[0],8192);
|
||
WriteVBMPFile();
|
||
}
|
||
return SUCCESS;
|
||
}
|
||
|
||
if (applesoft == 0) {
|
||
|
||
fp = fopen(a2fcfile,"wb");
|
||
if (NULL == fp) {
|
||
if (quietmode == 1)printf("Error Opening %s for writing!\n",a2fcfile);
|
||
return INVALID;
|
||
}
|
||
|
||
WriteDosHeader(fp,16384,8192);
|
||
|
||
c = fwrite(dhrbuf,1,16384,fp);
|
||
fclose(fp);
|
||
|
||
if (c != 16384) {
|
||
remove(a2fcfile);
|
||
if (quietmode == 1)printf("Error Writing %s!\n",a2fcfile);
|
||
return INVALID;
|
||
}
|
||
if (quietmode == 1)printf("%s created!\n",a2fcfile);
|
||
if (vbmp != 0) {
|
||
/* additional BMP file for Cybernesto's VBMP */
|
||
WriteVBMPFile();
|
||
}
|
||
return SUCCESS;
|
||
}
|
||
|
||
|
||
/* the bsaved images are split into two files
|
||
the first file is loaded into aux mem */
|
||
fp = fopen(auxfile,"wb");
|
||
if (NULL == fp) {
|
||
if (quietmode == 1)printf("Error Opening %s for writing!\n",auxfile);
|
||
return INVALID;
|
||
}
|
||
WriteDosHeader(fp,8192,8192);
|
||
c = fwrite(dhrbuf,1,8192,fp);
|
||
fclose(fp);
|
||
if (c != 8192) {
|
||
remove(auxfile);
|
||
if (quietmode == 1)printf("Error Writing %s!\n",auxfile);
|
||
return INVALID;
|
||
}
|
||
|
||
/* the second file is loaded into main mem */
|
||
fp = fopen(mainfile,"wb");
|
||
if (NULL == fp) {
|
||
remove(auxfile);
|
||
if (quietmode == 1)printf("Error Opening %s for writing!\n",mainfile);
|
||
return INVALID;
|
||
}
|
||
WriteDosHeader(fp,8192,8192);
|
||
c = fwrite(&dhrbuf[8192],1,8192,fp);
|
||
fclose(fp);
|
||
if (c != 8192) {
|
||
/* remove both files */
|
||
remove(auxfile);
|
||
remove(mainfile);
|
||
if (quietmode == 1)printf("Error Writing %s!\n",mainfile);
|
||
return INVALID;
|
||
}
|
||
|
||
if (quietmode == 1) {
|
||
printf("%s created!\n",auxfile);
|
||
printf("%s created!\n",mainfile);
|
||
}
|
||
|
||
if (vbmp != 0) {
|
||
/* additional BMP file for Cybernesto's VBMP */
|
||
WriteVBMPFile();
|
||
}
|
||
|
||
return SUCCESS;
|
||
}
|
||
|
||
|
||
int saverag()
|
||
{
|
||
FILE *fp;
|
||
/* make an Rasterized Apple II Graphic (RAG) */
|
||
int c, x, y, xoff, width;
|
||
unsigned char *ptr;
|
||
|
||
if (scale == 1) spritewidth = bmpwidth;
|
||
else spritewidth = bmpwidth * 2;
|
||
|
||
if (spritewidth < 1) {
|
||
printf("Width is too small for %s!\n",spritefile);
|
||
return INVALID;
|
||
}
|
||
|
||
memset(hgrbuf,0,8192);
|
||
for (y = 0; y < 192; y++) {
|
||
hgrline(y); /* translate from DHGR and format the HGR line */
|
||
hgrbits(y); /* put the HGR line into the HGR file buffer */
|
||
}
|
||
|
||
width = spritewidth;
|
||
while (width%7 != 0)width++; /* multiples of 7 pixels */
|
||
/* if we have an orphan pixel hanging at the edge of an even byte
|
||
increase the width to the next 7 pixels */
|
||
if (width == spritewidth && (width % 14) != 0) width += 7;
|
||
width /= 7;
|
||
if (width > 40)width = 40; /* likely not necessary */
|
||
|
||
/* over-ride for default .RAG file extension */
|
||
/* use .BOT extension for full-screen */
|
||
/* use .TOP extension for mixed-screen */
|
||
if (width == 40 && (bmpheight == 160 || bmpheight == 192)) {
|
||
x = 999;
|
||
for (y=0;spritefile[y] != (char)0;y++) {
|
||
if (spritefile[y] == '.') x = y;
|
||
}
|
||
if (x != 999) {
|
||
spritefile[x+2] = 'O';
|
||
if (bmpheight == 160) {
|
||
spritefile[x+1] = 'T'; spritefile[x+3] = 'P';
|
||
}
|
||
else {
|
||
spritefile[x+1] = 'B'; spritefile[x+3] = 'T';
|
||
}
|
||
}
|
||
}
|
||
|
||
fp = fopen(spritefile,"wb");
|
||
if (NULL == fp) {
|
||
printf("Error Opening %s for writing!\n",spritefile);
|
||
return INVALID;
|
||
}
|
||
|
||
/* write 2 byte header */
|
||
fputc((uchar)width,fp); /* width in bytes */
|
||
fputc((uchar)bmpheight,fp); /* height in scanlines */
|
||
|
||
for (y = 0; y < bmpheight; y++) {
|
||
xoff = HB[y] - 0x2000;
|
||
ptr = (unsigned char *) &hgrbuf[xoff];
|
||
c = fwrite(ptr,1,width,fp);
|
||
if (c!=width) break;
|
||
|
||
}
|
||
fclose(fp);
|
||
|
||
if (c!=width) {
|
||
remove(spritefile);
|
||
printf("Error Writing %s!\n",spritefile);
|
||
return INVALID;
|
||
}
|
||
|
||
printf("%s created!\n",spritefile);
|
||
return SUCCESS;
|
||
}
|
||
|
||
/* save raster oriented DHGR image fragment
|
||
|
||
file format is 5 byte header
|
||
|
||
3 - upper case ID bytes 'D' 'H' 'R' for a sprite
|
||
3 - upper case ID bytes 'D' 'H' 'M' for a sprite-mask
|
||
|
||
1 byte - width in bytes (multiples of 4 bytes - 7 pixels)
|
||
1 byte - height in rasters
|
||
|
||
followed by interleaved raster data
|
||
|
||
aux raster, main raster = (width in bytes)
|
||
aux raster, main raster = (width in bytes)
|
||
aux raster, main raster = (width in bytes)
|
||
etc...
|
||
|
||
*/
|
||
int savesprite()
|
||
{
|
||
|
||
FILE *fp;
|
||
int i, c, width, packet, x, y, xoff, cnt;
|
||
ushort fl;
|
||
uchar *ptraux, *ptrmain, ch;
|
||
|
||
if (outputtype != SPRITE_OUTPUT) return SUCCESS;
|
||
|
||
if (hgroutput == 1) return saverag();
|
||
|
||
/* if scaling is turned-on the sprite matrix is 280 x 192 so for every 2-pixels
|
||
in the BMP only 1-pixel will be in the sprite. BMPs over 140 x 192 implictly
|
||
and automatically turn-on scaling whether sprite mode is selected (option "F")
|
||
or not.
|
||
*/
|
||
if (scale == 1) spritewidth = bmpwidth / 2;
|
||
else spritewidth = bmpwidth;
|
||
|
||
if (spritewidth < 1) {
|
||
if (quietmode == 1)printf("Width is too small for %s!\n",spritefile);
|
||
return INVALID;
|
||
}
|
||
while (spritewidth%7 != 0) spritewidth++;
|
||
|
||
width = (int)((spritewidth / 7) * 4); /* 4 bytes = 7 pixels */
|
||
packet = (int)width / 2;
|
||
|
||
/* prepare either an image fragment or a mask for the image fragment */
|
||
/* the idea for a mask is to provide a background mixing map for the image fragment */
|
||
if (spritemask != 1) {
|
||
fp = fopen(spritefile,"wb");
|
||
if (NULL == fp) {
|
||
if (quietmode == 1)printf("Error Opening %s for writing!\n",spritefile);
|
||
return INVALID;
|
||
}
|
||
}
|
||
else {
|
||
fp = fopen(fmask,"wb");
|
||
if (NULL == fp) {
|
||
if (quietmode == 1)printf("Error Opening %s for writing!\n",fmask);
|
||
return INVALID;
|
||
}
|
||
/* transform the buffer to a black and white mask for the sprite */
|
||
/* the background is black and the foreground is white */
|
||
/* this allows a rendered sprite to be prepared independently of the mask */
|
||
/* and to contain the background color in any rendering or dithering that goes-on */
|
||
for (y = 0; y < bmpheight; y ++) {
|
||
for (x = 0; x < spritewidth; x++) {
|
||
if (dhrgetpixel(x,y) == backgroundcolor) ch = 0;
|
||
else ch = 15;
|
||