apple2gs_shr_converter/src_b2d/b2d.c

6359 lines
192 KiB
C
Executable File
Raw Permalink Blame History

/* ---------------------------------------------------------------------
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;