From cef9b0c106244aea2a7a9bfca1e8d8e1576755ba Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Fri, 29 Jun 2018 11:21:42 -0400 Subject: [PATCH] b2d: add version of this that compiles under Linux --- bmp2dhr/Makefile | 13 + bmp2dhr/README | 6 + bmp2dhr/b2d.c | 6360 ++++++++++++++++++++++++++++++++++++++++++ bmp2dhr/b2d.h | 924 ++++++ bmp2dhr/tomthumb.h | 774 +++++ chiptune_debug/NOTES | 1 + 6 files changed, 8078 insertions(+) create mode 100644 bmp2dhr/Makefile create mode 100644 bmp2dhr/README create mode 100644 bmp2dhr/b2d.c create mode 100644 bmp2dhr/b2d.h create mode 100644 bmp2dhr/tomthumb.h diff --git a/bmp2dhr/Makefile b/bmp2dhr/Makefile new file mode 100644 index 00000000..9ff9d2dd --- /dev/null +++ b/bmp2dhr/Makefile @@ -0,0 +1,13 @@ +CC = gcc +CFLAGS = -Wall -O2 + +all: b2d + +b2d: b2d.o + $(CC) $(LFLAGS) -o b2d b2d.o + +b2d.o: b2d.c b2d.h + $(CC) $(CFLAGS) -c b2d.c + +clean: + rm -f *~ *.o b2d diff --git a/bmp2dhr/README b/bmp2dhr/README new file mode 100644 index 00000000..a637e895 --- /dev/null +++ b/bmp2dhr/README @@ -0,0 +1,6 @@ +Modified the source a bit, mostly so it compiles and runs +on 64-bit Linux. + +Most of it was assumptions about the size of unsigned long in +the BMP header. + diff --git a/bmp2dhr/b2d.c b/bmp2dhr/b2d.c new file mode 100644 index 00000000..cd6cc024 --- /dev/null +++ b/bmp2dhr/b2d.c @@ -0,0 +1,6360 @@ +/* --------------------------------------------------------------------- +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 +#include +#include +#include +#include +#include +#include + +#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 */ +uint8_t PaintByNumbers(char *str) +{ + int idx; + uint8_t c = toupper(str[0]); + + if (str[1] == (char) 0) { + /* alpha mnemonic */ + if (c > 64 && c < 81) return (uint8_t) (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 = (uint8_t) idx; + } + } + +return c; +} + + +uint16_t Motorola16(uint16_t val) +{ + uint8_t buf[2]; + uint16_t *ptr; + + /* msb in smallest address */ + buf[0] = (uint8_t) (val % 256); val = val/256; + buf[1] = (uint8_t) (val % 256); + + ptr = (uint16_t *)&buf[0]; + val = ptr[0]; + + return val; +} + +void WriteDosHeader(FILE *fp, uint16_t fl, uint16_t 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(uint16_t),1,fp); + fwrite((char *)&fl,sizeof(uint16_t),1,fp); + } +} + + +/* + +Photoshop Luminosity Average + +Formula for the Luminosity Average: + + AvgLuma = 0.299·AvgRed + 0.587·AvgGreen + 0.114·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(int16_t palidx, int16_t previewidx, int16_t pseudo) +{ + int16_t i,j; + uint8_t 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(int16_t palidx) +{ + + int16_t i,j,k,idx; + uint16_t 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] = (uint16_t)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] = (uint8_t)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 */ +uint8_t GetMedColor(uint8_t r, uint8_t g, uint8_t b, double *paldistance) +{ + uint8_t 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 != (uint8_t) 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 = (uint8_t)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 */ +uint8_t GetHighColor(uint8_t r, uint8_t g, uint8_t b, double *paldistance) +{ + uint8_t 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 != (uint8_t) 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 = (uint8_t)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 */ +uint8_t GetLowColor(uint8_t r, uint8_t g, uint8_t b, double *paldistance) +{ + uint8_t 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 != (uint8_t) 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 = (uint8_t)i; + } + + } + return drawcolor; +} + +/* switchboard function to handle cross-hatched and non-cross-hatched output */ +/* keeps the conditionals out of the main loop */ +uint8_t GetDrawColor(uint8_t r, uint8_t g, uint8_t b, int x, int y) +{ + + /* additional vars for future */ + double distance, lowdistance, highdistance; + uint8_t drawcolor, lowcolor, highcolor; + uint8_t red = (uint8_t)(r >> 4), + green = (uint8_t)(g >> 4), + blue = (uint8_t)(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 != (uint8_t) 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 (uint8_t)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,uint8_t drawcolor) +{ + int xoff, pattern; + uint8_t *ptraux, *ptrmain; + + pattern = (x%7); + xoff = HB[y] + ((x/7) * 2); + ptraux = (uint8_t *) &dhrbuf[xoff-0x2000]; + ptrmain = (uint8_t *) &dhrbuf[xoff]; + + + switch(pattern) + { + /* left this here for reference + + uint8_t 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, uint8_t drawcolor) +{ + + int xoff, pixel; + uint8_t *ptr; + + if (x > 559) return; + + xoff = HB[y] + (x/14); + pixel = (x%14); + if (pixel > 6) { + /* main memory */ + pixel -= 7; + ptr = (uint8_t *) &dhrbuf[xoff]; + } + else { + /* auxiliary memory */ + ptr = (uint8_t *) &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, uint8_t drawcolor) +{ + + int xoff, pixel; + uint8_t *ptr; + + if (x > 279) return; + + xoff = HB[y] + (x/7); + pixel = (x%7); + /* main memory */ + ptr = (uint8_t *) &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,uint8_t drawcolor) +{ + int xoff, x; + uint8_t *ptraux, *ptrmain; + + xoff = HB[y]; + + ptraux = (uint8_t *) &dhrbuf[xoff-0x2000]; + ptrmain = (uint8_t *) &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; + uint8_t drawcolor; + memset(dhrbuf,0,16384); + if (backgroundcolor == LOBLACK) return; + drawcolor = (uint8_t)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. + + +*/ + +uint16_t WriteVbmpHeader(FILE *fp) +{ + uint16_t outpacket; + int c, i, j; + + /* BMP scanlines are padded to a multiple of 4 bytes (DWORD) */ + outpacket = (uint16_t)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 = (uint32_t)40; + mybmp.bmi.biWidth = (uint32_t)140; + mybmp.bmi.biHeight = (uint32_t)192; + mybmp.bmi.biPlanes = 1; + mybmp.bmi.biBitCount = 4; + mybmp.bmi.biCompression = (uint32_t) BI_RGB; + + mybmp.bmi.biSizeImage = (uint32_t)outpacket; + mybmp.bmi.biSizeImage *= mybmp.bmi.biHeight; + + /* create the file header */ + mybmp.bfi.bfType[0] = 'B'; + mybmp.bfi.bfType[1] = 'M'; + mybmp.bfi.bfOffBits = (uint32_t) 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; + uint8_t 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 = (uint8_t)j << 4; + } + else { + idx = dhrgetpixel(x,y2); + /* range check */ + if (idx < 0 || idx > 15)idx = 0; /* default black */ + j = RemapLoToHi[idx]; + bmpscanline[x1] = ch | (uint8_t)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; + uint16_t 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((uint8_t)width,fp); /* width in bytes */ + fputc((uint8_t)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; + uint16_t fl; + uint8_t *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; + dhrplot(x,y,ch); + } + } + /* now that we have transformed the image into a mask for mixing the sprite + with a background image we save it in the same format as the sprite + but as a DHM file rather than a DHR file */ + + /* append M for mask to the array basename */ + if (quietmode == 0) strcat(fname,"M"); + + } + + if (dosheader == 1) { + fl = (uint16_t) width; + fl *=bmpheight; + fl += 5; + WriteDosHeader(fp,fl,8192); + } + + /* 5 byte header */ + /* some kind of identifier */ + fputc('D',fp); + fputc('H',fp); + if (spritemask != 1) fputc('R',fp); + else fputc('M',fp); + + fputc((uint8_t)width,fp); /* width in bytes */ + fputc((uint8_t)bmpheight,fp); /* height in scanlines */ + + /* write header values to stdout */ + if (quietmode == 0) { + printf("#define %sWIDTH %d\n",fname,width); + printf("#define %sHEIGHT %d\n",fname,bmpheight); + printf("#define %sSIZE %d\n\n",fname,width * bmpheight); + + /* if we are writing a mask, background color is irrelevant */ + /* the whole idea behind background color is the same as a mask */ + + if (spritemask != 1) printf("uint8_t %sBackgroundColor = %d;\n\n",fname,backgroundcolor); + + printf("/* Embedded DHGR Image Fragment created from %s */\n\n",bmpfile); + printf("uint8_t %sPixelData[] = {\n",fname); + } + + for (y = 0, cnt = 0; y < bmpheight; y++) { + xoff = HB[y]; + ptraux = (uint8_t *) &dhrbuf[xoff-0x2000]; + ptrmain = (uint8_t *) &dhrbuf[xoff]; + /* aux raster */ + c = fwrite((char *)&ptraux[0],1,packet,fp); + if (c!= packet) break; + /* main raster */ + c = fwrite((char *)&ptrmain[0],1,packet,fp); + if (c!= packet) break; + + if (quietmode == 0) { + for (i=0;i 255) value = 255; + } + buf[0] = value; + if (clip == 0) { + if (value < 0) value = 0; + else if (value > 255) value = 255; + } + return (uint8_t) value; +} + + + +/* helper function for ReadCustomDither */ +int InitCustomLine(char *ptr, int lidx) +{ + int cnt=0, i; + + customdither[lidx][cnt] = (int16_t) atoi(ptr); + + /* enforce 11 fields */ + for (i=0;ptr[i]!=0;i++) { + if (ptr[i]== ',') { + cnt++; + if (cnt < 11) customdither[lidx][cnt] = (int16_t) atoi((char*)&ptr[i+1]); + } + } + if (cnt != 10) return -1; +} + + +/* read a custom dither pattern from a comma delimited text file + + line 1 is the custom divisor + the next 3 lines are 11 fields in the following format: + + 0,0,0,0,0,*,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0 + + unused fields must be padded with zeros + + errata: + + - no range checking + - current pixel (asterisk) at subscript 5 is not "protected" + +*/ +int ReadCustomDither(char *name) +{ + FILE *fp; + char ch, buf[128]; + int i,j; + + /* clear 3-dimensional custom dither array */ + memset(&customdither[0][0],0,sizeof(int16_t)*33); + + fp = fopen(name,"r"); + if (NULL == fp) return -1; + + /* read divisor */ + for (;;) { + if (NULL == fgets(buf, 128, fp)) { + fclose(fp); + return -1; + } + /* ignore comment lines and blank lines */ + ch = buf[0]; + /* leading numeric characters only */ + if (ch < 48 || ch > 57) continue; + break; + } + customdivisor = (int16_t) atoi(buf); + if (customdivisor < 1) { + fclose(fp); + return -1; + } + + /* read up to 3 lines of dither pattern */ + for (i=0;;) { + if (NULL == fgets(buf, 128, fp)) { + fclose(fp); + return -1; + } + /* ignore comment lines and blank lines */ + ch = buf[0]; + /* leading numeric characters only */ + if (ch < 48 || ch > 57) continue; + /* condition line - remove trailing comments */ + for (j=0;buf[j]!=0;j++) { + ch = buf[j]; + /* numeric characters are ok */ + if (ch > 47 && ch < 58) continue; + /* commas and asterisks are ok */ + if (ch == ',' || ch == '*') continue; + buf[j] = 0; + break; + } + + /* parse fields - there must be 11 fields */ + if(InitCustomLine((char *)&buf[0],i)==-1) { + fclose(fp); + return -1; + } + i++; + if (i == 3) break; + } + fclose(fp); + if (i == 0) return -1; + + if (quietmode == 1) { + printf("Imported Dither from %s\n",name); + } + + dither = CUSTOM; + return SUCCESS; + +} + +/* http://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering */ +/* http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/ */ +/* http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT */ +int run0=0, run1=0, run2=0; + +void FloydSteinberg(int y, int width) +{ + + double paldistance; /* not used in this function */ + int16_t red, green, blue, red_error, green_error, blue_error; + int16_t pos, mult; + int dx,i, x,x1, total_difference, total_error, total_used; + int testrun, runs, temperror, z; + uint8_t drawcolor, r,g,b; + + if (ditherstart == 0) { + + /* for hgr color dithering cancel serpentine effect and go forward only + otherwise groups of 7 pixels for choosing between Orange and Green hgr + palettes becomes too complicated */ + + /* this solution may effect user definable dithering but it is up to the + user to make their own pattern work within the program's limitations + */ + if (hgrdither == 1) serpentine = 0; + + if (quietmode == 1) { + if (mono == 1) puts("Monochrome Dithered Output:"); + else puts("Color Dithered Output:"); + + if (colorbleed < 100) + printf("Dither = %d - %s, Color Bleed Increase: %d%%\n",dither,dithertext[dither-1],(colorbleed-100)*-1); + else if (colorbleed > 100) + printf("Dither = %d - %s, Color Bleed Reduction: %d%%\n",dither,dithertext[dither-1],(colorbleed-100)); + else + printf("Dither = %d - %s\n",dither,dithertext[dither-1]); + + if (serpentine == 1) puts("Serpentine effect is on!"); + + } + ditherstart = 1; + /* reduce or increase color bleed */ + switch(dither) { + case FLOYDSTEINBERG: bleed = (16 * colorbleed)/100; break; + case JARVIS: bleed = (48 * colorbleed)/100; break; + case STUCKI: bleed = (42 * colorbleed)/100; break; + case ATKINSON: bleed = (8 * colorbleed)/100; break; + case BURKES: + case SIERRA: bleed = (32 * colorbleed)/100; break; + case SIERRATWO: bleed = (16 * colorbleed)/100; break; + case SIERRALITE: bleed = (4 * colorbleed)/100; break; + case CUSTOM: bleed = (customdivisor * colorbleed)/100; break; + default: bleed = (8 * colorbleed)/100; break; /* same as atkinson */ + } + if (bleed < 1) bleed = 1; + } + + /* When converting to HGR do palette matching here between Green-Violet and + Orange-Blue palettes in groups of 7 pixels */ + + /* from left to right */ + /* if we are dithering HGR we need to decide if we are using the Orange-Blue palette or + the Green-Violet palette based on groups of 7 pixels */ + + if (hgrdither == 1) { + testrun = 0; + /* the idea here is to work on a copy while we make the first two passes + to determine the palette */ + + /* on the third (and final) pass, we dither using the 4 color choice + with the lowest cumulative error for each 7 pixel group */ + + /* this particular idea is based on how Sheldon Simms tohgr program + decides which palette to use but you probably wouldn't know that + by just looking at the code */ + + /* Clear the buffers */ + memset(&OrangeBlueError[0],0,640); + memset(&GreenVioletError[0],0,640); + memset(&HgrPixelPalette[0],0,320); + + /* save the original dither buffers */ + /* work on a copy for the first two passes */ + memcpy(&redSave[0],&redDither[0],640); + memcpy(&greenSave[0],&greenDither[0],640); + memcpy(&blueSave[0],&blueDither[0],640); + } + else { + testrun = 2; + } + + /* if we are dithering color hgr, for the first two test passes, we don't + bother to diffuse the error beyond the current scanline */ + + /* it is not necessary to do so because all we are concerned with is the + color of the transformed pixels on the current line, but it is necessary to + dither the line completely in either palette in order to tansform the pixels + in the current line */ + for (runs=testrun;runs<3;runs++) { + + /* big hgr color rigamorole here */ + if (hgrdither == 1) { + if (runs == 1 || runs == 2) { + /* restore dither buffer after both test runs for the final run */ + memcpy(&redDither[0],&redSave[0],640); + memcpy(&greenDither[0],&greenSave[0],640); + memcpy(&blueSave[0],&blueDither[0],640); + } + + /* for the first two runs, dither7 does not change */ + if (runs == 0) { + dither7 = 'O'; + } + else if (runs == 1) { + dither7 = 'G'; + } + else { + /* after the first two runs */ + /* determine hgr palette for each pixel based on the first two + runs here before beginning the 3rd and final run */ + for (x = 0; x < width; x+=7) { + red_error = green_error = 0; + for (z = 0; z < 7; z++) { + red_error += OrangeBlueError[x+z]; + green_error += GreenVioletError[x+z]; + } + /* if the Green-Violet palette has the closest colors for + this group then use it. otherwise use the Orange-Blue + palette */ + if (green_error < red_error) dither7 = 'G'; + else dither7 = 'O'; + /* set the hgr palette for 7 pixels */ + for (z = 0; z < 7; z++) { + HgrPixelPalette[x+z] = dither7; + } + } + } + + } + + for (x=0;x 0) AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)((color_error * 7)/bleed)+total_difference); + /* seed next line forward */ + /* for serpentine effect line 2 error is reversed */ + if (x>0)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)((color_error * 1)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)((color_error * 3)/bleed)); + + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error * 7)/bleed)+total_difference); + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next line forward */ + if (x>0)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)((color_error * 3)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)((color_error * 1)/bleed)); + } + + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)((color_error * 5)/bleed)); + break; + + /* J 2 */ + case JARVIS: + /* + * 7 5 + 3 5 7 5 3 + 1 3 5 3 1 (1/48) + */ + + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error * 7)/bleed)); + AdjustShortPixel(1,(int16_t *)&colorptr[x+2],(int16_t)((color_error * 5)/bleed)); + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next lines forward */ + if (x>0){ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)((color_error * 5)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x-1],(int16_t)((color_error * 3)/bleed)); + } + if (x>1){ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x-2],(int16_t)((color_error * 3)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x-2],(int16_t)(color_error/bleed)); + + } + + /* seed next line forward */ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)((color_error * 7)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)((color_error * 5)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+2],(int16_t)((color_error * 3)/bleed)); + + /* seed furthest line forward */ + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x],(int16_t)((color_error * 5)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x+1],(int16_t)((color_error * 3)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x+2],(int16_t)(color_error/bleed)); + break; + + /* S 3 */ + case STUCKI: + /* + * 8 4 + 2 4 8 4 2 + 1 2 4 2 1 (1/42) + */ + + /* for serpentine effect alternating scanlines run the error in reverse */ + if (serpentine == 1 && y%2 == 1) { + /* finish this line */ + if(x>0)AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)((color_error * 8)/bleed)); + if(x>1)AdjustShortPixel(1,(int16_t *)&colorptr[x-2],(int16_t)((color_error * 4)/bleed)); + + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error * 8)/bleed)); + AdjustShortPixel(1,(int16_t *)&colorptr[x+2],(int16_t)((color_error * 4)/bleed)); + } + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next lines forward */ + if (x>0){ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)((color_error * 4)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x-1],(int16_t)((color_error * 2)/bleed)); + } + if (x>1){ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x-2],(int16_t)((color_error * 2)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x-2],(int16_t)(color_error/bleed)); + + } + + /* seed next line forward */ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)((color_error * 8)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)((color_error * 4)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+2],(int16_t)((color_error * 2)/bleed)); + + /* seed furthest line forward */ + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x],(int16_t)((color_error * 4)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x+1],(int16_t)((color_error * 2)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x+2],(int16_t)(color_error/bleed)); + break; + + /* A 4 */ + case ATKINSON: + /* + * 1 1 + 1 1 1 + 1 (1/8) + + */ + + /* for serpentine effect alternating scanlines run the error in reverse */ + if (serpentine == 1 && y%2 == 1) { + /* finish this line */ + if (x>0)AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)(color_error/bleed)); + if (x>1)AdjustShortPixel(1,(int16_t *)&colorptr[x-2],(int16_t)(color_error/bleed)); + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)(color_error/bleed)); + AdjustShortPixel(1,(int16_t *)&colorptr[x+2],(int16_t)(color_error/bleed)); + } + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next line forward */ + if (x>0)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)(color_error/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)(color_error/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)(color_error/bleed)); + + /* seed furthest line forward */ + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x],(int16_t)(color_error/bleed)); + break; + + /* B 5 */ + case BURKES: + /* + * 8 4 + 2 4 8 4 2 (1/32) + */ + + /* for serpentine effect alternating scanlines run the error in reverse */ + if (serpentine == 1 && y%2 == 1) { + /* finish this line */ + if(x>0)AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)((color_error * 8) /bleed)); + if(x>1)AdjustShortPixel(1,(int16_t *)&colorptr[x-2],(int16_t)((color_error * 4) /bleed)); + + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error * 8) /bleed)); + AdjustShortPixel(1,(int16_t *)&colorptr[x+2],(int16_t)((color_error * 4) /bleed)); + + } + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next line forward */ + if (x>0)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)((color_error * 4) / bleed)); + if (x>1)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-2],(int16_t)((color_error * 2) / bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)((color_error * 8) /bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)((color_error * 4) /bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+2],(int16_t)((color_error * 2) /bleed)); + break; + + /* SI 6 */ + case SIERRA: + /* + * 5 3 + 2 4 5 4 2 + 2 3 2 (1/32) + */ + /* for serpentine effect alternating scanlines run the error in reverse */ + if (serpentine == 1 && y%2 == 1) { + /* finish this line */ + if(x>0)AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)((color_error * 5)/bleed)); + if(x>1)AdjustShortPixel(1,(int16_t *)&colorptr[x-2],(int16_t)((color_error * 3)/bleed)); + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error * 5)/bleed)); + AdjustShortPixel(1,(int16_t *)&colorptr[x+2],(int16_t)((color_error * 3)/bleed)); + } + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next lines forward */ + if (x>0){ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)((color_error * 4)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x-1],(int16_t)((color_error * 2)/bleed)); + } + if (x>1){ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x-2],(int16_t)((color_error * 2)/bleed)); + } + + /* seed next line forward */ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)((color_error * 5)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)((color_error * 4)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+2],(int16_t)((color_error * 2)/bleed)); + + /* seed furthest line forward */ + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x],(int16_t)((color_error * 3)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x+1],(int16_t)((color_error * 2)/bleed)); + break; + + /* S2 7 */ + case SIERRATWO: + /* + * 4 3 + 1 2 3 2 1 (1/16) + */ + + /* for serpentine effect alternating scanlines run the error in reverse */ + if (serpentine == 1 && y%2 == 1) { + /* finish this line */ + if(x>0)AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)((color_error*4)/bleed)); + if(x>1)AdjustShortPixel(1,(int16_t *)&colorptr[x-2],(int16_t)((color_error*3)/bleed)); + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error*4)/bleed)); + AdjustShortPixel(1,(int16_t *)&colorptr[x+2],(int16_t)((color_error*3)/bleed)); + } + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next line forward */ + if (x>0)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)((color_error*2)/bleed)); + if (x>1)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-2],(int16_t)(color_error/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)((color_error*3)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)((color_error*2)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+2],(int16_t)(color_error/bleed)); + break; + + /* SL 8 */ + case SIERRALITE: + /* + * 2 + 1 1 (1/4) + */ + + /* for serpentine effect alternating scanlines run the error in reverse */ + if (serpentine == 1 && y%2 == 1) { + /* finish this line */ + if (x>0)AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)((color_error * 2) /bleed)); + + /* seed next line forward */ + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)(color_error/bleed)); + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error * 2) /bleed)); + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next line forward */ + if (x>0)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)(color_error/bleed)); + } + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)(color_error/bleed)); + + break; + + + case CUSTOM: + + /* 0,0,0,0,0,*,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0 */ + + for (dx = 0,pos=x-5;dx < 11; dx++,pos++) { + /* finish this line */ + if (pos < 0) continue; + + mult = customdither[0][dx]; + if (mult > 0) { + AdjustShortPixel(1,(int16_t *)&colorptr[pos],(int16_t)((color_error * mult) /bleed)); + } + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) continue; + + /* seed next line forward */ + mult = customdither[1][dx]; + if (mult > 0) { + AdjustShortPixel(threshold,(int16_t *)&seedptr[pos],(int16_t)((color_error * mult) /bleed)); + } + /* seed furthest line forward */ + mult = customdither[2][dx]; + if (mult > 0) { + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[pos],(int16_t)((color_error * mult) /bleed)); + } + + } + break; + + default: /* buckels dither - d9 */ + /* + * 2 1 + 1 2 1 + 1 (1/8) + + Serpentine + + 1 2 * + 1 2 1 + 1 + + */ + + /* if error summing is turned-on add the accumulated rounding error + to the next pixel */ + if (errorsum == 0) { + total_difference = 0; + } + else { + total_error = (color_error * 8) / bleed; + total_used = (color_error * 2)/bleed; + total_used += (color_error * 2)/bleed; + total_used += (color_error /bleed); + total_used += (color_error /bleed); + total_used += (color_error /bleed); + total_used += (color_error /bleed); + total_difference = total_error - total_used; + } + + /* for serpentine effect alternating scanlines run the error in reverse */ + if (serpentine == 1 && y%2 == 1) { + /* finish this line */ + if (x>0)AdjustShortPixel(1,(int16_t *)&colorptr[x-1],(int16_t)((color_error*2)/bleed)+total_difference); + if (x>1)AdjustShortPixel(1,(int16_t *)&colorptr[x-2],(int16_t)(color_error/bleed)); + } + else { + /* finish this line */ + AdjustShortPixel(1,(int16_t *)&colorptr[x+1],(int16_t)((color_error*2)/bleed)+total_difference); + AdjustShortPixel(1,(int16_t *)&colorptr[x+2],(int16_t)(color_error/bleed)); + } + + /* if making hgr passes 0 and 1 dither first line only */ + if (runs < 2 || ditheroneline == 1) break; + + /* seed next line forward */ + if (x>0)AdjustShortPixel(threshold,(int16_t *)&seedptr[x-1],(int16_t)(color_error/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x],(int16_t)((color_error*2)/bleed)); + AdjustShortPixel(threshold,(int16_t *)&seedptr[x+1],(int16_t)(color_error/bleed)); + + /* seed furthest line forward */ + AdjustShortPixel(threshold,(int16_t *)&seed2ptr[x],(int16_t)(color_error/bleed)); + + + + } + } + } + } + + /* turn-off hgr color dither */ + dither7 = 0; + + /* get the mask line from the mask file if we are overlaying this image */ + /* the mask file is a 256 color BMP and is applied after rendering is complete and */ + /* immediately before Preview files are written to disk and the DHGR buffer is plotted */ + /* for monochrome masking the maskfile is either 280 x 192 or 560 x 192 */ + /* for color masking the maskfile is always 140 x 192 */ + + if (overlay == 1) { + ReadMaskLine(y); + } + + /* plot dithered scanline in DHGR buffer using selected conversion palette */ + /* plot dithered scanline in Preview buffer using selected preview palette */ + for (x=0,x1=0;x -1) { + if (jxoffset > 80) jxoffset = 80; + offset+= jxoffset * 3; + } + else { + offset += 120; + } + if (bmpheight == 480) { + if (jyoffset > -1) { + if (jyoffset > 96)jyoffset = 0; + else jyoffset = 96 - jyoffset; + offset += (1920L * jyoffset); + } + else { + offset += (1920L * 48); + } + } + if (bmpheight == 400) { + if (jyoffset > -1) { + if (jyoffset > 16)jyoffset = 0; + else jyoffset = 16 - jyoffset; + offset += (1920L * jyoffset); + } + else { + offset += (1920L * 8); + } + } + break; + + case 320: + if (jxoffset > -1) { + if (jxoffset > 40) jxoffset = 40; + offset+= jxoffset * 3; + } + else { + offset += 60; + } + if (jyoffset > -1) { + if (jyoffset > 8)jyoffset = 0; + else jyoffset = 8 - jyoffset; + offset += (960L * jyoffset); + } + else { + offset += (960L * 4); + } + break; + + } + } + else { + /* LGR and DLGR */ + offset += (jyoffset * packet); + offset += (jxoffset * 3); + } + + } + + /* seek past extraneous info in header if any */ + fseek(fp,bfi.bfOffBits+offset,SEEK_SET); + + if (justify == 1 && loresoutput == 0) { + for (y = 0;y< 192;y++) { + fread((char *)&dibscanline1[0],1,packet,fp); + if (bmpheight == 200) { + /* no merging at all on 320 x 200 */ + fwrite((char *)&dibscanline1[0],1,outpacket,fp2); + continue; + } + fread((char *)&dibscanline2[0],1,packet,fp); + for (x = 0,i=0,j=0;x<280;x++) { + b = (uint16_t)dibscanline1[i]; b+= dibscanline2[i]; i++; + g = (uint16_t)dibscanline1[i]; g+= dibscanline2[i]; i++; + r = (uint16_t)dibscanline1[i]; r+= dibscanline2[i]; i++; + /* half merge (merge vertically) unless merge is turned-on */ + if (merge == 0) { + i+=3;b*=2;g*=2;r*=2; + } + else { + b += dibscanline1[i]; b+= dibscanline2[i]; i++; + g += dibscanline1[i]; g+= dibscanline2[i]; i++; + r += dibscanline1[i]; r+= dibscanline2[i]; i++; + } + bmpscanline[j] = (uint8_t) (uint16_t)(b/4);j++; + bmpscanline[j] = (uint8_t) (uint16_t)(g/4);j++; + bmpscanline[j] = (uint8_t) (uint16_t)(r/4);j++; + } + fwrite((char *)&bmpscanline[0],1,outpacket,fp2); + } + } + else { + if (loresoutput == 1) { + /* LGR and DLGR input file */ + if (appletop == 1) chunks = 40; + else chunks = 48; + + for (y=0;y> 4; + dibscanline1[j] = ch; j++; + ch = bmpscanline[i] & 0xf; + dibscanline1[j] = ch; j++; + } + } + memset(&bmpscanline[0],0,1920); + for (i=0,j=0;i 280) { + status = INVALID; + switch(bmpwidth) { + case 640: if (bmpheight == 400 || bmpheight == 480) status = SUCCESS; break; + case 320: if (bmpheight == 200) status = SUCCESS; break; + case 560: if (bmpheight == 384) status = SUCCESS; break; + } + } + else { + if (bmpheight > 192) status = INVALID; + } + } + } + + if (status == INVALID) { + fclose(fp); + fp = NULL; + printf("%s is not a supported size!\n",bmpfile); + return fp; + } + + if (bmi.biBitCount == 8) + fread((char *)&sbmp[0].rgbBlue, sizeof(RGBQUAD)*256,1,fp); + else if (bmi.biBitCount == 4) + fread((char *)&sbmp[0].rgbBlue, sizeof(RGBQUAD)*16,1,fp); + else if (bmi.biBitCount == 1) + fread((char *)&sbmp[0].rgbBlue, sizeof(RGBQUAD)*2,1,fp); + + /* seek past extraneous info in header if any */ + fseek(fp,bfi.bfOffBits,SEEK_SET); + + /* align on 4 byte boundaries */ + if (bmi.biBitCount == 1) { + if (bmpwidth == 280) packet = 36; + else packet = 72; + } + else if (bmi.biBitCount == 8) { + packet = bmpwidth; + } + else { + packet = bmpwidth / 2; + if (bmpwidth%2 != 0)packet++; + } + while ((packet % 4)!=0)packet++; + + if((fp2=fopen(reformatfile,"wb"))==NULL) { + printf("Error Opening %s for writing!\n",reformatfile); + return fp; + } + if (bmi.biBitCount == 1) { + if (bmpwidth == 280) outpacket = WriteDIBHeader(fp2,bmpwidth,bmpheight); + else outpacket = WriteDIBHeader(fp2,bmpwidth,bmpheight*2); + } + else { + outpacket = WriteDIBHeader(fp2,bmpwidth,bmpheight); + } + if (outpacket < 1) { + fclose(fp2); + remove(reformatfile); + printf("Error writing header to %s!\n",reformatfile); + return fp; + } + + for (y=0;y -1 || jyoffset < 9)) { + jyoffset = 8 - jyoffset; + break; + } + jyoffset = 8; + justify = 1; + + } + else { + /* fullscreen */ + /* 40 x 48 and 80 x 48 */ + justify = jyoffset = 0; + + } + break; + + case 88: /* the next two input widths (88 and 176) are processed as over-scanned images */ + /* these originate from clipped BMP format "MiniPix" conversions */ + /* saved in Windows Paint as "old" and "new" printshop "pastes" from ClipShop in + small-copy (single-scaled) or regular copy (double-scaled) format. */ + + if (bmpheight != 52) break; + status = SUCCESS; /* verbatim for dlgr - nominal size 80 x 48 */ + if (jxoffset < 0 || jxoffset > 8) { + jxoffset = 4; /* centre */ + } + if (appletop == 1) { /* mixed text and graphics - dlgr windowbox 80 x 40 */ + if (justify == 1 && (jyoffset > -1 && jyoffset < 13)) jyoffset = 12 - jyoffset; + else jyoffset = 10; + justify = 1; + break; + } + justify = 1; + if (jyoffset < 0 || jyoffset > 4) { + jyoffset = 2; /* centre */ + } + break; + case 176: if (bmpheight != 104) break; + status = SUCCESS; /* dlgr double-scaled - nominal size 160 x 96 */ + if (jxoffset < 0 || jxoffset > 16) { + jxoffset = 8; /* centre */ + } + if (appletop == 1) { /* mixed text and graphics - dlgr windowbox 160 x 80 */ + if (justify == 1 && (jyoffset > -1 && jyoffset < 25)) jyoffset = 24 - jyoffset; + else jyoffset = 20; + justify = 1; + break; + } + justify = 1; + if (jyoffset < 0 || jyoffset > 8) { + jyoffset = 4; /* centre */ + } + break; + case 160: /* nominal size 160 x 96 */ + /* 2:2 scaled for dlgr, 4:2 scaled for lgr */ + if (bmpheight == 80 || bmpheight == 96) status = SUCCESS; + else break; + jxoffset = 0; + if (bmpheight == 80) { + /* 160 x 80 - mixed text and graphics */ + appletop = 1; + justify = jyoffset = 0; + break; + } + if (appletop == 1) { + /* mixed text and graphics */ + /* 160 x 96 - windowbox required */ + /* top justified if not otherwise specified */ + if (justify == 1 && (jyoffset > -1 || jyoffset < 17)) { + jyoffset = 16 - jyoffset; + break; + } + jyoffset = 16; + justify = 1; + + } + else { + /* fullscreen */ + /* 160 x 96 */ + justify = jyoffset = 0; + + } + break; + case 320: /* nominal size 320 x 192 */ + /* 4:4 scaled for dlgr, 8:4 scaled for lgr */ + if (bmpheight == 160 || bmpheight == 192 || bmpheight == 200) { + status = SUCCESS; + jxoffset = 0; /* vertical scaling of mixed text and graphics only */ + if (bmpheight == 160) { + appletop = 1; + justify = jyoffset = 0; + break; + } + if (bmpheight == 192) { + if (appletop == 1) { + if (justify == 1 && (jyoffset > -1 && jyoffset < 33)) jyoffset = 32 - jyoffset; + else jyoffset = 32; + justify = 1; + break; + } + justify = jyoffset = 0; + break; + } + /* 320 x 200 */ + /* centre in frame by default */ + if (appletop == 1) { + if (justify == 1 && (jyoffset > -1 && jyoffset < 41)) jyoffset = 40 - jyoffset; + else jyoffset = 36; + justify = 1; + break; + } + if (justify == 1 && (jyoffset > -1 && jyoffset < 9)) jyoffset = 8 - jyoffset; + else jyoffset = 4; + justify = 1; + } + break; + case 560: /* "Classic" full-screen conversion of pre-scaled 560 x 384 input */ + /* "Classic" mixed text and graphics screen conversion of pre-scaled + 560 x 384 full-screen or 560 x 320 cropped input */ + /* 7:8 scaled for dlgr, 14:8 scaled for lgr */ + if (bmpheight == 320 || bmpheight == 384) status = SUCCESS; + else break; + jxoffset = 0; + if (bmpheight == 320) { + /* mixed text and graphics */ + appletop = 1; + justify = jyoffset = 0; + break; + } + if (appletop == 1) { + /* mixed text and graphics */ + if (justify == 1 && (jyoffset > -1 && jyoffset < 65)) jyoffset = 64 - jyoffset; + else jyoffset = 64; + justify = 1; + } + else { + /* fullscreen */ + justify = jyoffset = 0; + } + break; + case 640: /* "Classic" full-screen conversion of 640 x 480 square pixeled input */ + /* "Classic" mixed text and graphics screen conversion of square pixeled + 640 x 480 full-screen or 640 x 400 cropped input */ + /* 8:10 scaled for dlgr, 16:10 scaled for dlgr */ + if (bmpheight == 480 || bmpheight == 400) status = SUCCESS; + else break; + jxoffset = 0; + if (bmpheight == 400) { + /* mixed text and graphics */ + appletop = 1; + justify = jyoffset = 0; + break; + } + if (appletop == 1) { + /* mixed text and graphics */ + if (justify == 1 && (jyoffset > -1 && jyoffset < 81)) jyoffset = 80 - jyoffset; + else jyoffset = 64; + justify = 1; + } + else { + /* fullscreen */ + justify = jyoffset = 0; + } + break; + } + } + + return status; +} + +/* for color DHGR */ +/* 1. reads a 24 bit BMP file in the range from 1 x 1 to 280 x 192 */ +/* 2. writes a DHGR screen image or optionally a DHGR image fragment */ +/* 3. also creates an optional preview file... + when preview is on... also leaves an optional error-diffused dib file + in place if error diffusion is also turned-on */ +/* Etcetera */ +int16_t Convert() +{ + + FILE *fp, *fpdib, *fpreview; + int16_t status = INVALID, resize = 0; + uint16_t x,x1,x2,y,yoff,i,packet, outpacket, width, dwidth, red, green, blue; + uint8_t r,g,b,drawcolor; + uint32_t pos, prepos; + + /* if using a mask file, open it now */ + /* leave it open throughout the conversion session */ + /* it will be closed in main before exiting */ + if (overlay == 1)OpenMaskFile(); + + if((fp=fopen(bmpfile,"rb"))==NULL) { + printf("Error Opening %s for reading!\n",bmpfile); + return status; + } + /* read the header stuff into the appropriate structures */ + fread((char *)&bfi.bfType[0], + sizeof(BITMAPFILEHEADER),1,fp); + fread((char *)&bmi.biSize, + sizeof(BITMAPINFOHEADER),1,fp); + + /* reformat to 24 bit */ + if (bmi.biCompression==BI_RGB && + bfi.bfType[0] == 'B' && bfi.bfType[1] == 'M' && bmi.biPlanes==1) { + + bmpwidth = (uint16_t) bmi.biWidth; + bmpheight = (uint16_t) bmi.biHeight; + + if (loresoutput == 1) { + /* LGR and DLGR */ + status = ValidLoResSizeRange(); + if (status == INVALID) { + fclose(fp); + printf("%s is in the wrong format!\n",bmpfile); + return status; + } + } + + if (bmi.biBitCount == 8 || bmi.biBitCount == 4) { + fp = ReformatBMP(fp); + if (fp == NULL) return INVALID; + } + } + + if (bmi.biCompression==BI_RGB && + bfi.bfType[0] == 'B' && bfi.bfType[1] == 'M' && + bmi.biPlanes==1 && bmi.biBitCount == 24) { + + bmpwidth = (uint16_t) bmi.biWidth; + bmpheight = (uint16_t) bmi.biHeight; + + if (loresoutput == 0) { + /* color HGR and DHGR */ + /* resize some classic screen sizes */ + if (bmpwidth == 320 && bmpheight == 200) + resize = 1; + else if (bmpwidth == 640 && bmpheight == 400) + resize = 2; + else if (bmpwidth == 640 && bmpheight == 480) + resize = 3; + else if (bmpwidth == 560 && bmpheight == 384) + resize = 4; + } + else { + /* color LGR and DLGR */ + /* all lo-res and double lo-res input is resized to one of 2 - + sizes of input file: + + 80 x 48 or 80 x 40 + + the input file is then processed as a "small" double hi-res + image. since the color palette is exactly the same, the same + rendering options are available. + + after processing the double hi-res output buffer is converted to + lo-res or double lo-res formatting and saved as Apple II native + LGR or DLGR output. + + */ + resize = 5; + } + + if (resize != 0) { + memset(&bmpscanline[0],0,1920); + memset(&dibscanline1[0],0,1920); + memset(&dibscanline2[0],0,1920); + memset(&dibscanline3[0],0,1920); + memset(&dibscanline4[0],0,1920); + fp = ResizeBMP(fp,resize); + if (fp == NULL) return INVALID; + bmpwidth = (uint16_t) bmi.biWidth; + bmpheight = (uint16_t) bmi.biHeight; + } + + if (loresoutput == 0) { + /* HGR and DHGR output */ + if (scale == 0) { + if (bmpwidth > 140) scale = 1; + } + + if (scale == 1) { + width = bmpwidth; + dwidth = (bmpwidth+1)/2; + if (bmpwidth > 0 && bmpwidth < 281 && + bmpheight > 0 && bmpheight < 193) status = SUCCESS; + } + else { + width = bmpwidth * 2; + dwidth = bmpwidth; + if (bmpwidth > 0 && bmpwidth < 141 && + bmpheight > 0 && bmpheight < 193) status = SUCCESS; + + } + } + else { + /* LGR and DLGR */ + width = bmpwidth; + if (lores == 1) { + scale = 1; + dwidth = 40; + } + else { + scale = 0; + dwidth = 80; + } + } + } + + if (status == INVALID) { + fclose(fp); + printf("%s is in the wrong format!\n",bmpfile); + return status; + } + + + packet = bmpwidth * 3; + /* BMP scanlines are padded to a multiple of 4 bytes (DWORD) */ + while ((packet % 4) != 0) packet++; + + /* error diffusion option */ + if (diffuse != 0) { + /* clear buffers */ + memset(&bmpscanline[0],0,960); + memset(&dibscanline1[0],0,960); + memset(&dibscanline2[0],0,960); + fp = ReadDIBFile(fp, packet); + if (fp == NULL) return INVALID; + } + + if (preview!=0) { + fpreview = fopen(previewfile,"wb+"); + + if (fpreview != NULL) { + outpacket = WriteDIBHeader(fpreview,width,bmpheight); + if (outpacket == 0) { + fclose(fpreview); + remove(previewfile); + printf("Error writing header to %s!\n",previewfile); + preview = 0; + } + else { + /* pad the preview file */ + memset(&dibscanline1[0],0,960); + for (y=0;y '9') && buf[idx] != ' ') + buf[idx] = ASCIIZ; + ptr[jdx] = buf[idx]; jdx++; + ptr[jdx] = ASCIIZ; + } + + /* remove trailing whitespace... + this occurrs during parsing of photostyler */ + len = strlen(ptr); + while (len > 0) { + len--; + if (ptr[len] != ' ') + break; + ptr[len] = ASCIIZ; + } +} + +/* split the RGB triple from a text line read-in from an + ascii palette file. */ +int ReadPaletteLine(unsigned char *ptr, unsigned char *palptr, unsigned int colordepth) +{ + int red, green, blue, idx, spaces = 0; + + red = atoi(ptr); + if (red < 0 || red > 255) return INVALID; + + /* there must be at least 3 fields */ + for (idx = 0; ptr[idx] != 0; idx++) { + if (ptr[idx] == ' ' && ptr[idx+1] >= '0' && ptr[idx+1] <= '9') { + spaces++; + switch(spaces) { + case 1: + green = atoi(&ptr[idx+1]); + if (green < 0 || green > 255) return INVALID; + break; + case 2: + blue = atoi(&ptr[idx+1]); + if (blue < 0 || blue > 255) return INVALID; + break; + } + } + } + + if (spaces<2) + return INVALID; + + if (colordepth == 6) { + palptr[0] = (uint8_t)red << 2; + palptr[1] = (uint8_t)green << 2; + palptr[2] = (uint8_t)blue << 2; + } + else { + palptr[0] = (uint8_t)red; + palptr[1] = (uint8_t)green; + palptr[2] = (uint8_t)blue; + } + return SUCCESS; + +} + +/* check version if Paintshop palette since JASC may change someday */ +/* also check Aldus version although that product is old... */ + +/* The Top Half of NeoPaint Windows Palettes are the same as their */ +/* DOS palettes so we use the 6 bit color values and handle both */ +/* file types the same way... so no worry about neopaint versions. */ + +char *Gimp = "GIMP Palette"; /* followed by RGB values and comments */ + +/* NeoPaint and PaintShop Pro headers + 3 lines followed by RGB values */ +char *NeoPaint = "NeoPaint Palette File"; +char *PaintShop = "JASC-PAL"; +char *PaintShopVersion = "0100"; + +/* Aldus photostyler + 3 lines followed by RGB values */ +char *AldusPal = "CWPAL"; +char *AldusClr = "CWCLR"; /* partial palettes */ +char *AldusVersion = "100"; + +#define GENERIC 1 +#define GIMP 2 +#define JASC 3 +#define NEO 4 +#define ALDUS 5 + +int16_t GetUserPalette(char *name) +{ + FILE *fp; + char buf[128]; + int cnt=16; + int16_t status = INVALID; + unsigned colordepth=8,userpaltype=GENERIC; + + fp = fopen(name,"r"); + if (fp == NULL) return status; + + for (;;) { + if (NULL == fgets(buf, 128, fp)) { + fclose(fp); + break; + } + nocr(buf); + SqueezeLine(buf); + + /* check for some known palette types */ + if (strcmp(Gimp, buf)==0) userpaltype = GIMP; + else if (strcmp(PaintShop, buf)==0) userpaltype = JASC; + else if (strcmp(NeoPaint, buf)==0) { + colordepth = 6; + userpaltype = NEO; + } + else if (strcmp(AldusPal, buf) == 0 || strcmp(AldusClr, buf) == 0) { + userpaltype = ALDUS; + } + /* if not a known type then assume it's just a simple csv */ + + status = SUCCESS; + switch(userpaltype) + { + case GENERIC: rewind(fp); break; + + case JASC: + case NEO: + case ALDUS: + /* check 2 remaining header lines */ + status = INVALID; + if (NULL == fgets(buf, 128, fp)) break; + nocr(buf); + SqueezeLine(buf); + if (userpaltype == JASC && strcmp(PaintShopVersion, buf)!=0)break; + if (userpaltype == ALDUS && strcmp(AldusVersion, buf) != 0)break; + if (NULL == fgets(buf, 128, fp)) break; + cnt = atoi(buf); + if (cnt < 16) break; + status = SUCCESS; + } + if (status == INVALID) break; + + memset(&rgbUser[0][0],0,48); + cnt = 0; + while (fgets(buf,128,fp) != NULL) { + if (buf[0] == '#') continue; + if (strlen(buf) < 5) continue; + nocr(buf); + SqueezeLine(buf); + if (INVALID == ReadPaletteLine(buf,(uint8_t *)&rgbUser[cnt][0],colordepth)) continue; + cnt++; + if (cnt > 15)break; + } + break; + } + fclose(fp); + + if (cnt < 15) { + printf("%s contains only %d colors!",name,cnt); + } + if (status == INVALID) { + printf("%s is not a valid palette file!",name); + } + return status; +} + +/* returns the Apple II Hires drawcolor 0-15 */ +/* 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 */ +int dhrgetpixel(int x,int y) +{ + int xoff, pattern, idx; + unsigned char *ptraux, *ptrmain,c1, c2, d1, d2; + + pattern = (x%7); + xoff = HB[y] + ((x/7) * 2); + ptraux = (unsigned char *) &dhrbuf[xoff-0x2000]; + ptrmain = (unsigned char *) &dhrbuf[xoff]; + + + switch(pattern) + { + /* left this here for reference + + unsigned char 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}; + */ + + /* compare colors in the input file to color patterns and return drawcolor */ + /* somewhat inelegant but lazy to read and debug if a problem */ + case 0: c1 = ptraux[0] &0x0f; + for (idx = 0; idx < 16; idx++) { + d1 = dhrbytes[idx][0] & 0x0f; + if (d1 == c1) return idx; + } + break; + case 1: c1 = ptraux[0] & 0x70; + c2 = ptrmain[0] & 0x01; + for (idx = 0; idx < 16; idx++) { + d1 = dhrbytes[idx][0] & 0x70; + d2 = dhrbytes[idx][1] & 0x01; + if (d1 == c1 && d2 == c2) return idx; + } + break; + case 2: c1 = ptrmain[0] & 0x1e; + for (idx = 0; idx < 16; idx++) { + d1 = dhrbytes[idx][1] & 0x1e; + if (d1 == c1) return idx; + } + break; + case 3: c1 = ptrmain[0] & 0x60; + c2 = ptraux[1] & 0x03; + for (idx = 0; idx < 16; idx++) { + d1 = dhrbytes[idx][1] & 0x60; + d2 = dhrbytes[idx][2] & 0x03; + if (d1 == c1 && d2 == c2) return idx; + } + break; + case 4: c1 = ptraux[1] & 0x3c; + for (idx = 0; idx < 16; idx++) { + d1 = dhrbytes[idx][2] & 0x3c; + if (d1 == c1) return idx; + } + break; + case 5: c1 = ptraux[1] & 0x40; + c2 = ptrmain[1] & 0x07; + for (idx = 0; idx < 16; idx++) { + d1 = dhrbytes[idx][2] & 0x40; + d2 = dhrbytes[idx][3] & 0x07; + if (d1 == c1 && d2 == c2) return idx; + } + break; + case 6: c1 = ptrmain[1] & 0x78; + for (idx = 0; idx < 16; idx++) { + d1 = dhrbytes[idx][3] & 0x78; + if (d1 == c1) return idx; + } + break; + } + + return INVALID; + +} + + +int save_to_bmp24(void) +{ + + FILE *fp; + uint8_t tempr, tempg, tempb; + int i,x,y, y2 = 191,idx = 1; + uint16_t width = 280, height = 192, outpacket; + + fp = fopen(previewfile,"wb"); + if (NULL == fp) { + printf("Error opening %s for writing!\n",previewfile); + preview = 0; + return INVALID; + } + + if (mono == 1 && hgroutput == 0) { + width = 560; + height = 384; + idx = 2; + } + + /* write header for 24 bit bmp */ + outpacket = WriteDIBHeader(fp,width,height); + if (outpacket == 0) { + fclose(fp); + remove(previewfile); + printf("Error writing header to %s!\n",previewfile); + preview = 0; + return INVALID; + } + + if (mono == 0) { + /* write rgb triples and double each pixel to preserve the aspect ratio */ + for (y = 0; y< 192; y++) { + + for (x = 0; x < 140; x++) { + idx = dhrgetpixel(x,y2); + + /* range check */ + if (idx < 0 || idx > 15)idx = 0; /* default black */ + + tempr = rgbPreview[idx][0]; + tempg = rgbPreview[idx][1]; + tempb = rgbPreview[idx][2]; + + /* reverse order */ + fputc(tempb, fp); + fputc(tempg, fp); + fputc(tempr, fp); + + /* double-up */ + fputc(tempb, fp); + fputc(tempg, fp); + fputc(tempr, fp); + } + y2 -= 1; + } + } + else { + for (y = 0;y< 192;y++,y2--) { + if (width == 560) applemonobites(y2,1); + else applemonobites(y2,0); + for (i=0;i < idx;i++) { + for (x = 0; x < width; x++) { + if (buf280[x] == 0) tempb = 0; + else tempb = 255; + /* any order - black and white */ + fputc(tempb, fp); + fputc(tempb, fp); + fputc(tempb, fp); + } + } + } + } + + fclose(fp); + return SUCCESS; + +} + +/* titling from a textfile */ +/* only available for full-screen raw output */ +/* no attempt at proportional spacing */ +/* 4 x 6 font titling */ +/* based on a 140 x 192 matrix */ +/* 140 / 4 = 35 characters across */ +/* 192 / 6 = 32 lines down */ +int16_t GetUserTextFile() +{ + FILE *fp; + char buf[128]; + int x,y,i,cnt=0; + + fp = fopen(usertextfile,"r"); + if (NULL == fp) { + /* for batch operations */ + fp = fopen("b2d.txt","r"); + if (NULL == fp) return INVALID; + } + + /* read up to 32 lines of text */ + for (i=0,y=0;i<32;i++,y+=6) { + if (NULL == fgets(buf, 128, fp)) { + break; + } + nocr(buf); + if (buf[0] == 0) continue; + buf[35] = 0; + for (x=0;x<3;x++) { + /* creating a black outline */ + thumbDHGR(buf,x,y,0,255,'L'); + /* skip the middle pixels, and fill-in color */ + if (x!=1)thumbDHGR(buf,x,y+1,0,255,'L'); + thumbDHGR(buf,x,y+2,0,255,'L'); + } + /* now do the middle pixels */ + if (mono == 1) { + /* for monochrome the letters are always white */ + thumbDHGR(buf,1,y+1,15,255,'L'); + } + else { + /* for color the letters can be any color at all except for black */ + if (backgroundcolor == 0)thumbDHGR(buf,1,y+1,15,255,'L'); + else thumbDHGR(buf,1,y+1,(unsigned char)backgroundcolor,255,'L'); + } + cnt++; + } + fclose(fp); + + /* if we have created titled output, and we have already written a preview image + we need to over-write it with a titled version */ + if (cnt!= 0 && preview != 0) save_to_bmp24(); + + /* at this point we just return */ + return SUCCESS; +} + + + + +int main(int argc, char **argv) +{ + int16_t idx,jdx,kdx,palidx=5,previewidx=5,hgrpalidx=5,pseudopal=0, + status,basename=0,plainname=0; + uint8_t c, ch, *wordptr, *ptr; + char hgroptions[20]; + + if (argc < 2) { + pusage(); + return (1); + } + + /* allocate our output buffers to support MS-DOS compilers + but does no harm for 32-bit compilers + */ + dhrbuf = hgrbuf = (uint8_t *)malloc(8192); + if (NULL != hgrbuf) { + dhrbuf = (uint8_t *)malloc(16384); + } + if (dhrbuf == NULL) { + puts("No memory..."); + return (1); + } + + /* initialize color space for color distance */ + setluma(); + + /* automatic naming is used for a number of reasons */ + /* I make no attempt to test for a legal ProDOS file name length - that's up to the user */ + /* but short names should be used when possible for a number of reasons */ + + /* HGR output - options accumulator for HGR long filename - not available in MS-DOS */ + /* this differentiates BIN files created for HGR from AUX,BIN file pairs used for alternate output of DHGR */ + /* and from each other so they can be compared */ + /* for HGR output the preview file is an approximation so far */ + hgroptions[0] = 0; + + usertextfile[0] = 0; + + /* getopts */ + if (argc > 2) { + for (idx = 2; idx < argc; idx++) { + /* switch character is optional */ + wordptr = (uint8_t *)&argv[idx][0]; + ch = toupper(wordptr[0]); + if (ch == '-') { + wordptr = (uint8_t *)&argv[idx][1]; + ch = toupper(wordptr[0]); + } + + if (cmpstr(wordptr,"debug") == SUCCESS) { + debug = 1; + continue; + } + + /* set different Luma for color distance */ + jdx = 0; + if (cmpstr(wordptr,"GIMP") == SUCCESS) jdx = 411; + else if (cmpstr(wordptr,"MAGICK") == SUCCESS) jdx = 709; + else if (cmpstr(wordptr,"HDMI") == SUCCESS) jdx = 240; + if (jdx != 0) { + lumaREQ = jdx; + printf("Using LumaREQ %d\n", lumaREQ); + setluma(); + continue; + } + + /* so-called "quick" commands */ + if (cmpstr(wordptr,"photo") == SUCCESS) { + dither = FLOYDSTEINBERG; + continue; + } + if (cmpstr(wordptr,"art") == SUCCESS) { + threshold = 25; + xmatrix = 2; + continue; + } + if (cmpstr(wordptr,"both") == SUCCESS) { + dither = FLOYDSTEINBERG; + threshold = 15; + xmatrix = 2; + continue; + } + if (cmpstr(wordptr,"sprite") == SUCCESS) { + outputtype = SPRITE_OUTPUT; + continue; + } + + if (cmpstr(wordptr,"BIN") == SUCCESS) { + applesoft = 1; + continue; + } + + if (cmpstr(wordptr,"sum") == SUCCESS) { + errorsum = 1; + continue; + } + + if (cmpstr(wordptr,"mono") == SUCCESS || cmpstr(wordptr,"reverse") == SUCCESS) { + mono = 1; + if (dither == 0) dither = FLOYDSTEINBERG; + if (cmpstr(wordptr,"reverse") == SUCCESS) reverse = 1; + continue; + } + + /* DOS 3.3 header will be appended to Apple II Output */ + if (cmpstr(wordptr,"dos") == SUCCESS) { + dosheader = 1; + continue; + } + + /* TGR long commands */ + /* to select alternate conversion palette 15 from tohgr, instead of HGR use TGR for HGR long commands */ + if (ch == 'T') { + c = toupper(wordptr[1]); + if (c == 'G') { + c = toupper(wordptr[2]); + if (c == 'R') { + wordptr[0] = ch = 'H'; + palidx = hgrpalidx = 16; + puts("HGR Option TGR: tohgr HGR color conversion palette"); + } + } + } + + + switch(ch) { + case 'A': /* output AUX,BIN - default is A2FC */ + applesoft = 1; + break; + case 'B': + c = wordptr[1]; + if (c == (uint8_t)0) break; + + if (cmpstr("basename", (char *)&wordptr[0]) == SUCCESS || + cmpstr("base", (char *)&wordptr[0]) == SUCCESS) { + basename = 1; + break; + } + + /* background color 1-15 (0 by default) */ + c = PaintByNumbers((char *)&wordptr[1]); + if (c!= (uint8_t)255) backgroundcolor = c; + break; + + case 'C': ch = toupper(wordptr[1]); + if (ch == 'P' || ch == 'V') { + paletteclip = 1; + break; + } + globalclip = 1; + break; + + case 'D': if (cmpstr("DL", (char *)&wordptr[0]) == SUCCESS) { + /* DLGR output */ + loresoutput = 1; + break; + } + + dither = FLOYDSTEINBERG; + + if (ReadCustomDither((char *)&wordptr[1]) == SUCCESS) { + break; + } + + ch = toupper(wordptr[1]); + if (ch == 'X') { + wordptr++; + serpentine = 1; + } + + jdx = atoi((char *)&wordptr[1]); + if (jdx > 0 && jdx < 10) dither = jdx; + else { + ch = toupper(wordptr[1]); + switch(ch) { + case 'F': dither = FLOYDSTEINBERG;break; + case 'J': dither = JARVIS;break; + case 'S': dither = STUCKI; + ch = toupper(wordptr[2]); + if (ch == 'I') dither = SIERRA; + else if (ch == '2') dither = SIERRATWO; + else if (ch == 'L') dither = SIERRALITE; + break; + case 'A': dither = ATKINSON;break; + case 'B': dither = BURKES; + ch = toupper(wordptr[2]); + if (ch == 'B') dither = 9; + break; + } + } + break; + + + case 'E': + /* error diffusion default = E2 */ + diffuse = 2; + jdx = atoi((char *)&wordptr[1]); + /* E4 */ + if (jdx == 4) diffuse = 4; + break; + + case 'F': /* image fragment - off by default */ + outputtype = SPRITE_OUTPUT; + ch = toupper(wordptr[1]); + if (ch == 'M') spritemask = 1; + break; + + case 'J': + /* scaling of larger sizes is pixel by pixel + when justification is selected */ + justify = 1; + ch = toupper(wordptr[1]); /* justify */ + switch(ch) { + case 'L': jxoffset = atoi((char *)&wordptr[2]); + break; + case 'T': jyoffset = atoi((char *)&wordptr[2]); + break; + + } + break; + + case 'H': /* HGR output - command option 'H' */ + /* some options follow */ + hgroutput = 1; + if (hgroptions[0] == (char)0){ + /* if we are using Sheldon's HGR palette we use the TC suffix */ + /* if we are using Sheldon's DHGR palette modified for HGR we use the C suffix */ + if (hgrpalidx == 16)strcat(hgroptions,"TC"); + else strcat(hgroptions,"C"); + clearcolor = 3; /* set overlay color to violet */ + } + /* default long file name for HGR color */ + /* HGR long commands - can be followed by separate HGR short commands to over-ride fixed settings */ + /* by default individual pixels are set */ + + /* hgrclean = X, hgrclip = Y, hgrsum = Z */ + for (;;) { + jdx = strlen((char *)&wordptr[0]); + if (jdx < 6 || jdx > 9) break; + if (jdx == 8 || jdx == 9) { + if (jdx == 8) ptr = (char *)&wordptr[3]; + else ptr = (char *)&wordptr[4]; + if (cmpstr("clean", (char *)&ptr[0]) == SUCCESS) { + printf("HGR Option X: %s\n",(char *)&ptr[0]); + globalclip = errorsum = 1; + ptr[0] = 0; + strcat(hgroptions,"X"); + jdx = 0; + } + } + if (jdx < 6 || jdx > 8) break; + jdx = strlen((char *)&wordptr[0]); + if (jdx == 7 || jdx == 8) { + if (jdx == 7) ptr = (char *)&wordptr[3]; + else ptr = (char *)&wordptr[4]; + if (cmpstr("clip", (char *)&ptr[0]) == SUCCESS) { + printf("HGR Option Y: %s\n",(char *)&ptr[0]); + globalclip = 1; + ptr[0] = 0; + strcat(hgroptions,"Y"); + jdx = 0; + } + } + if (jdx < 6 || jdx > 8) break; + jdx = strlen((char *)&wordptr[0]); + if (jdx == 6 || jdx == 7) { + if (jdx == 6) ptr = (char *)&wordptr[3]; + else ptr = (char *)&wordptr[4]; + if (cmpstr("sum", (char *)&ptr[0]) == SUCCESS) { + printf("HGR Option Z: %s\n",(char *)&ptr[0]); + errorsum = 1; + ptr[0] = 0; + strcat(hgroptions,"Z"); + } + } + break; + } + + jdx = strlen((char *)&wordptr[0]); + switch(jdx) { + /* long commands */ + case 3: + if (cmpstr("hgr", (char *)&wordptr[0]) == SUCCESS) { + if (hgrcolortype == (char)0) hgrcolortype = 'B'; + } + break; + case 4: + if (cmpstr("hgrs", (char *)&wordptr[0]) == SUCCESS) { + /* optionally single colored pixels are set */ + if (hgrcolortype == (char)0) hgrcolortype = 'B'; + doublecolors = 0; + puts("HGR Option S: single color pixels"); + strcat(hgroptions,"S"); + } + else if (cmpstr("hgrw", (char *)&wordptr[0]) == SUCCESS) { + /* optionally double colors are set with a double white overlay */ + if (hgrcolortype == (char)0) hgrcolortype = 'B'; + puts("HGR Option W: double color and white pixels"); + doublecolors = 1; + doublewhite = 1; + strcat(hgroptions,"W"); + } + else if (cmpstr("hgrb", (char *)&wordptr[0]) == SUCCESS) { + /* optionally double colors are set with a double black overlay */ + if (hgrcolortype == (char)0) hgrcolortype = 'B'; + puts("HGR Option B: double color and black pixels"); + doublecolors = 1; + doubleblack = 1; + strcat(hgroptions,"B"); + } + else if (cmpstr("hgro", (char *)&wordptr[0]) == SUCCESS) { + /* set HGR output for orange and blue only */ + /* color type is not needed */ + /* no pixel options - individual pixels only */ + puts("HGR Option O: Orange and Blue Palette Only"); + grpal[3][0] = grpal[3][1] = grpal[3][2] = 0; + grpal[12][0] = grpal[12][1] = grpal[12][2] = 0; + hgrpaltype = 0x80; + strcat(hgroptions,"O"); + hgrdither = 0; + + } + else if (cmpstr("hgrg", (char *)&wordptr[0]) == SUCCESS) { + /* set HGR output for green and violet only */ + /* color type is not needed */ + /* no pixel options - individual pixels only */ + clearcolor = 6; /* set overlay color to blue */ + puts("HGR Option G: Green and Violet Palette Only"); + grpal[6][0] = grpal[6][1] = grpal[6][2] = 0; + grpal[9][0] = grpal[9][1] = grpal[9][2] = 0; + hgrpaltype = 0; + strcat(hgroptions,"G"); + hgrdither = 0; + } + else if (cmpstr("hgr2",(char *)&wordptr[0]) == SUCCESS) { + puts("HGR alternate nearest color option"); + strcat(hgroptions,"A"); + hgrdither = 1; + } + break; + case 1: + case 2: + /* short commands */ + ch = toupper(wordptr[1]); + if (ch == 'O' || ch == 'G' || ch == 'B' || ch == 'V' || ch == (char)0) { + /* this command sets color precedence for the palette bit + the default is 'O' orange + + 'HO' and 'HG' set strong precedence + 'HB' and 'HV' set weak precedence + 'H' by itself sets equal precedence + + regardless of precedence both palettes will be used unless specifically disabled + by 'HGRO' or 'HGRG' which sets 4 color HGR output. + + */ + if (ch == (char)0) { + puts("HGR Precedence Over-ride: Equal"); + } + else if (ch == 'B' || ch == 'V') { + printf("HGR Precedence Over-ride: Weak %c\n",ch); + } + else { + printf("HGR Precedence Over-ride: Strong %c\n",ch); + } + wordptr[1] = hgrcolortype = ch; + wordptr[0] = toupper(wordptr[0]); + strcat(hgroptions,(char *)&wordptr[0]); + } + } + + + /* Low-resolution colors + 0 (black), + 3 (purple), + 6 (medium blue), + 9 (orange), + 12 (light green) and + 15 (white) are also available in high-resolution mode */ + /* + + /* disable the unused colors in the default palette */ + /* these will be propagated to any alternate palettes that are selected */ + grpal[1][0] = grpal[1][1] = grpal[1][2] = 0; + grpal[2][0] = grpal[2][1] = grpal[2][2] = 0; + grpal[4][0] = grpal[4][1] = grpal[4][2] = 0; + grpal[5][0] = grpal[5][1] = grpal[5][2] = 0; + grpal[7][0] = grpal[7][1] = grpal[7][2] = 0; + grpal[8][0] = grpal[8][1] = grpal[8][2] = 0; + grpal[10][0] = grpal[10][1] = grpal[10][2] = 0; + grpal[11][0] = grpal[11][1] = grpal[11][2] = 0; + grpal[13][0] = grpal[13][1] = grpal[13][2] = 0; + grpal[14][0] = grpal[14][1] = grpal[14][2] = 0; + break; + case 'L': + if (wordptr[1] == (char) 0 || cmpstr(wordptr,"lgr") == SUCCESS) { + /* LGR output */ + lores = loresoutput = 1; + break; + } + + /* Luma */ + jdx = atoi((char *)&wordptr[1]); + if (jdx == 601 || jdx == 709 || jdx == 240 || jdx == 911 || jdx == 411) { + lumaREQ = jdx; + printf("Using LumaREQ %d\n", lumaREQ); + setluma(); + break; + } + break; + + case 'M': /* M2 - horizontal merge - S2 mode only */ + /* by default every second pixel is skipped */ + merge = 1; + break; + + case 'O': /* use an 8 bit overlay file - must be 140 x 192 */ + /* transparent color must be set or default is 128,128,128 - color 5 */ + c = wordptr[1]; + if (c == (uint8_t)0) break; + c = PaintByNumbers((char *)&wordptr[1]); + if (c!= (uint8_t)255) { + clearcolor = c; break; + } + overlay = 1; + strcpy(maskfile,(char *)&wordptr[1]); + /* maskfile must have an extension or .bmp is assumed */ + /* this avoids typing extensions which I dislike doing */ + /* so I provide expected extensions. any questions? */ + kdx = 999; + for (jdx=0;maskfile[jdx]!=0;jdx++) { + if (maskfile[jdx] == '.')kdx = jdx; + } + if (kdx == 999)strcat(maskfile,".bmp"); + break; + + case 'V': /* create preview file */ + preview = 1; + if (wordptr[1] == 0) break; + if (cmpstr(wordptr,"vbmp") == SUCCESS) { + vbmp = 1; + break; + } + + case 'P': + if (cmpstr("plainname", (char *)&wordptr[0]) == SUCCESS || + cmpstr("plain", (char *)&wordptr[0]) == SUCCESS) { + plainname = 1; + break; + } + + /* palette settings */ + c = toupper(wordptr[1]); + /* check for palette names */ + if (c > 57) { + c = 255; + if (cmpstr("kegs32", (char *)&wordptr[1]) == SUCCESS || + cmpstr("kegs", (char *)&wordptr[1]) == SUCCESS)c = previewidx = palidx = 0; + else if (cmpstr("cider", (char *)&wordptr[1]) == SUCCESS || + cmpstr("ciderpress", (char *)&wordptr[1]) == SUCCESS)c = previewidx = palidx = 1; + else if (cmpstr("old", (char *)&wordptr[1]) == SUCCESS)c = previewidx = palidx = 2; + else if (cmpstr("new", (char *)&wordptr[1]) == SUCCESS || + cmpstr("applewin", (char *)&wordptr[1]) == SUCCESS)c = previewidx = palidx = 3; + else if (cmpstr("wikipedia", (char *)&wordptr[1]) == SUCCESS || + cmpstr("wiki", (char *)&wordptr[1]) == SUCCESS)c = previewidx = palidx = 4; + /* Sheldon Simms tohgr and AppleWin NTSC */ + else if (cmpstr("sheldon", (char *)&wordptr[1]) == SUCCESS || + cmpstr("todhr", (char *)&wordptr[1]) == SUCCESS || + cmpstr("ntsc", (char *)&wordptr[1]) == SUCCESS)c = previewidx = palidx = 5; + /* Jason Harper's Super Convert DHGR Palette */ + else if (cmpstr("rgb", (char *)&wordptr[1]) == SUCCESS || + cmpstr("super", (char *)&wordptr[1]) == SUCCESS || + cmpstr("gs", (char *)&wordptr[1]) == SUCCESS) c = previewidx = palidx = 12; + /* Jace DHGR Palette */ + else if (cmpstr("jace", (char *)&wordptr[1]) == SUCCESS || + cmpstr("blurry", (char *)&wordptr[1]) == SUCCESS) c = previewidx = palidx = 13; + /* Cybnernesto */ + else if (cmpstr("cybernesto", (char *)&wordptr[1]) == SUCCESS)c = previewidx = palidx = 14; + else if (cmpstr("canvas", (char *)&wordptr[1]) == SUCCESS)c = 7; + else if (cmpstr("bmp", (char *)&wordptr[1]) == SUCCESS)c = 8; + else if (cmpstr("win16", (char *)&wordptr[1]) == SUCCESS)c = 8; + else if (cmpstr("xmp", (char *)&wordptr[1]) == SUCCESS)c = 9; + else if (cmpstr("win32", (char *)&wordptr[1]) == SUCCESS)c = 9; + else if (cmpstr("vga", (char *)&wordptr[1]) == SUCCESS)c = 10; + else if (cmpstr("pcx", (char *)&wordptr[1]) == SUCCESS)c = 11; + if (c!= 255) { + if (ch == 'P') palidx = c; + else previewidx = c; + break; + } + } + + jdx = GetUserPalette((char *)&wordptr[1]); + if (jdx == SUCCESS) { + if (ch == 'P') palidx = 6; + else previewidx = 6; + } + else { + + + c = toupper(wordptr[1]); + + if (c == 'P') { + /* pseudo palette */ + if (wordptr[2] > (char)47 && wordptr[2] < (char)58) { + jdx = atoi((char *)&wordptr[2]); + if ((jdx < 0 || jdx > 16) || jdx == 15) break; + if (pseudocount < PSEUDOMAX) { + pseudolist[pseudocount] = jdx; + pseudocount++; + pseudopal = 1; + break; + } + } + + } + + if (c == 'K' || c == 'C' || c == 'O' || c == 'N' || c == 'W' || c == 'S'|| + c == 'R' || c == 'G' || c == 'E' || c == 'J' || c == 'V') { + jdx=0; /* Kegs */ + switch(c) { + case 'R': /* RGB */ + case 'G': jdx = 12; break; /* Apple II "G" (IIgs) - RGB display */ + case 'J': jdx = 13; break; /* Jace NTSC Palette */ + case 'V': jdx = 14; break; /* VBMP NTSC Palette */ + case 'E': /* Apple II "E" (IIe) - composite display */ + case 'S': jdx++;/* Sheldon Simms NTSC Palette */ + case 'W': jdx++;/* Wikipedia NTSC */ + case 'N': jdx++;/* New AppleWin */ + case 'O': jdx++;/* Old AppleWin */ + case 'C': jdx++;/* CiderPress */ + } + + } + else { + if (c < 48 || c > 59) break; + jdx = atoi((char *)&wordptr[1]); + } + /* palettes 0-5 are the original palettes */ + /* palette 6 is a user palette file */ + /* palettes 7-11 are legacy palettes */ + /* palette 12 is Super Convert RGB palette */ + /* palette 13 is Jace NTSC palette */ + /* palette 14 is Cybernesto's VBMP NTSC palette */ + /* palette 15 defaults to a Pseudo-Palette of the average RGB values + of Palette 5 (tohgr NTSC) and Palette 12 (Super Convert RGB) */ + /* palette 16 is tohgr's old NTSC colors which as of June 2014 are still used for HGR conversion */ + if (jdx > -1 && jdx < 17) { + if (ch == 'P') palidx = jdx; + else previewidx = jdx; + } + } + break; + case 'Q': quietmode = 0; + break; + + case 'R': /* reduced or increased color bleed + by percentage (for dithering only) */ + + jdx = atoi((char *)&wordptr[1]); + if ((jdx > 0 && jdx < 101) || (jdx < 0 && jdx > -101)) colorbleed = 100 + jdx; + break; + + case 'S': /* by default scaling is set to S1 - full scale (verbatim) */ + /* so choosing option S without the S1 numeric modifier sets scaling to half-scale */ + jdx = atoi((char *)&wordptr[1]); + + if (jdx == 1) scale = 0; /* S1 full scale (verbatim) */ + else scale = 1; /* S2 - double scaled */ + break; + case 'T': if (cmpstr("op", (char *)&wordptr[1]) == SUCCESS) { + /* LGR and DLGR mixed text and graphics */ + loresoutput = appletop = 1; + break; + } + /* use ciderpress tags - off by default */ + tags = 1; + break; + case 'X': /* pattern setting for general purpose 2 x 2 cross-hatching */ + xmatrix = 2; + if (threshold == 0) threshold = 25; + /* optional pattern setting for general purpose 2 x 2 cross-hatching */ + jdx = atoi((char *)&wordptr[1]); + if (jdx == 1 || jdx == 3) xmatrix = jdx; + break; + case 'Y': /* increase or decrease color - non-cross-hatched ouput */ + /* this can eventually be replaced by a saturation adjustment or + a hue correction or something else */ + ymatrix = 1; + jdx = atoi((char *)&wordptr[1]); + if (jdx == 2 || jdx == 3) ymatrix = jdx; + break; + + case 'Z': /* threshold setting for general purpose 2 x 2 cross-hatching */ + /* and for brightening and darkening of colors */ + threshold = 25; + if (xmatrix == 0)xmatrix = 2; + jdx = atoi((char *)&wordptr[1]); + /* allow up to 50% adjustment on RGB values */ + /* surely that's enough */ + if (jdx > 0 && jdx < 51) threshold = jdx; + break; + + } + } + } + + /* mutually exclusive commands are handled here */ + if (hgroutput == 1) { + if (loresoutput == 1) { + loresoutput = 0; + puts("HGR output and Lo-Res output are mutually exclusive.\nLo-Res output cancelled!"); + } + /* + if (outputtype == SPRITE_OUTPUT) { + outputtype = BIN_OUTPUT; + puts("HGR output and Image Fragment output are mutually exclusive.\nImage Fragment output cancelled!"); + } + */ + if (mono == 1) { + mono = 0; + puts("HGR output and Monochrome output are mutually exclusive.\nMonochrome output cancelled!"); + } + } + else { + hgrdither = 0; + } + + if (mono == 1) { + if (loresoutput == 1) { + loresoutput = 0; + puts("Monochrome output and Lo-Res output are mutually exclusive.\nLo-Res output cancelled!"); + } + if (outputtype == SPRITE_OUTPUT) { + mono = 0; + puts("Image Fragment output and Monochrome output are mutually exclusive.\nMonochrome output cancelled!"); + } + } + + if (loresoutput == 1) { + overlay = 0; + if (outputtype == SPRITE_OUTPUT) { + outputtype = BIN_OUTPUT; + puts("Lo-Res output and Image Fragment output are mutually exclusive.\nImage Fragment output cancelled!"); + } + } + + /* embedding of image fragments or palette output only */ + if (outputtype != SPRITE_OUTPUT) { + if (pseudopal == 0) quietmode = 1; + } + + jdx = 999; + strcpy(fname, argv[1]); + for (idx = 0; fname[idx] != (uint8_t)0; idx++) { + if (fname[idx] == '.') { + jdx = idx; + } + } + if (jdx != 999) fname[jdx] = (uint8_t)0; + + sprintf(bmpfile,"%s.bmp",fname); + sprintf(dibfile,"%s.dib",fname); +#ifdef MSDOS + tags = 0; + sprintf(previewfile,"%s.pmp",fname); + sprintf(scaledfile,"%s.smp",fname); + sprintf(reformatfile,"%s.rmp",fname); + sprintf(vbmpfile,"%s.vmp",fname); +#else + sprintf(previewfile,"%s_Preview.bmp",fname); + sprintf(scaledfile,"%s_Scaled.bmp",fname); + sprintf(reformatfile,"%s_Reformat.bmp",fname); + sprintf(vbmpfile,"%s_VBMP.bmp",fname); +#endif + /* user titling file */ + sprintf(usertextfile,"%s.txt",fname); + + /* upper case basename for Apple II Output */ + for (idx = 0; fname[idx] != (uint8_t)0; idx++) { + ch = toupper(fname[idx]); + fname[idx] = ch; + } + strcpy(hgrwork,fname); + + if (basename == 1) { + /* if they are using the same naming convention that I am */ + /* optionally strip the resolution nomenclature from the input file's base name */ + idx = strlen(hgrwork); + if (idx > 3) { + /* in order below: 384 - 560 x 384 + 280 - 280 x 192 + 640 - 640 x 480 + 400 - 640 x 400 + 320 - 320 x 200 + 140 - 140 x 192 + 560 - 560 x 192 + + LGR and DLGR only + + 176 - 176 x 104 + 160 - 160 x 80 and 160 x 96 + 88 - 88 x 52 + 80 - 80 x 40 and 80 x 48 + 48 - 80 x 48 and 40 x 48 + 40 - 80 x 40 and 40 x 40 + */ + if (hgrwork[idx-3] == '3' && hgrwork[idx-2] == '8' && hgrwork[idx-1] == '4') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '2' && hgrwork[idx-2] == '8' && hgrwork[idx-1] == '0') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '6' && hgrwork[idx-2] == '4' && hgrwork[idx-1] == '0') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '4' && hgrwork[idx-2] == '0' && hgrwork[idx-1] == '0') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '3' && hgrwork[idx-2] == '2' && hgrwork[idx-1] == '0') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '1' && hgrwork[idx-2] == '4' && hgrwork[idx-1] == '0') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '5' && hgrwork[idx-2] == '6' && hgrwork[idx-1] == '0') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '1' && hgrwork[idx-2] == '7' && hgrwork[idx-1] == '6') hgrwork[idx - 3] = 0; + else if (hgrwork[idx-3] == '1' && hgrwork[idx-2] == '6' && hgrwork[idx-1] == '0') hgrwork[idx - 3] = 0; + + if (hgrwork[idx - 3] != (char)0) { + /* LGR and DLGR only */ + if (hgrwork[idx-2] == '8' && hgrwork[idx-1] == '8') hgrwork[idx - 2] = 0; + else if (hgrwork[idx-2] == '8' && hgrwork[idx-1] == '0') hgrwork[idx - 2] = 0; + else if (hgrwork[idx-2] == '4' && hgrwork[idx-1] == '8') hgrwork[idx - 2] = 0; + else if (hgrwork[idx-2] == '4' && hgrwork[idx-1] == '0') hgrwork[idx - 2] = 0; + } + } + } + + /* CiderPress File Attribute Preservation Tags */ + if (tags == 1) { + if (hgroutput == 0) + sprintf(spritefile,"%s.DHR#062000",hgrwork); + else + sprintf(spritefile,"%s.RAG#062000",hgrwork); + + sprintf(fmask,"%s.DHM#062000",hgrwork); + sprintf(mainfile,"%s.BIN#062000",hgrwork); + sprintf(auxfile,"%s.AUX#062000",hgrwork); + sprintf(a2fcfile,"%s.A2FC#062000",hgrwork); + if (plainname == 0) { + sprintf(hgrcolor,"%s%s.BIN#062000",hgrwork,hgroptions); + sprintf(hgrmono,"%sM.BIN#062000",hgrwork); + if (mono == 1) { + sprintf(a2fcfile,"%s.A2FM#062000",hgrwork); + sprintf(mainfile,"%sM.BIN#062000",hgrwork); + sprintf(auxfile,"%sM.AUX#062000",hgrwork); + } + } + else { + sprintf(hgrcolor,"%s.BIN#062000",hgrwork); + sprintf(hgrmono,"%s.BIN#062000",hgrwork); + } + } + else { + /* tags are off by default */ + /* unadorned file names */ + if (hgroutput == 0) + sprintf(spritefile,"%s.DHR",hgrwork); + else + sprintf(spritefile,"%s.RAG",hgrwork); + + sprintf(fmask,"%s.DHM",hgrwork); + sprintf(mainfile,"%s.BIN",hgrwork); + sprintf(auxfile,"%s.AUX",hgrwork); +#ifdef MSDOS + if (plainname == 0 && mono == 1) + sprintf(a2fcfile,"%s.2FM",hgrwork); + else + sprintf(a2fcfile,"%s.2FC",hgrwork); + strcpy(hgrcolor,hgrwork); + strcpy(hgrmono,hgrwork); +#else + sprintf(a2fcfile,"%s.A2FC",hgrwork); + + if (plainname == 0) { + sprintf(hgrcolor,"%s%s.BIN",hgrwork,hgroptions); + sprintf(hgrmono,"%sM.BIN",hgrwork); + if (mono == 1) { + sprintf(a2fcfile,"%s.A2FM",hgrwork); + sprintf(mainfile,"%sM.BIN#062000",hgrwork); + sprintf(auxfile,"%sM.AUX#062000",hgrwork); + } + } + else { + sprintf(hgrcolor,"%s.BIN",hgrwork); + sprintf(hgrmono,"%s.BIN",hgrwork); + } +#endif + } + + if (mono == 1) { + palidx = previewidx = 4; + /* create a black and white palette */ + memset(&wikipedia[0][0],0,45); + } + else { + /* create pseudo-palette for conversion */ + /* preview using pseudopalette is optional - v15 */ + if (pseudopal != 0) { + BuildPseudoPalette(palidx); + palidx = 15; + } + } + + GetBuiltinPalette(palidx,previewidx,0); + InitDoubleArrays(); + + if (mono == 1) status = ConvertMono(); + else status = Convert(); + + /* close mask file if any before exiting */ + if (NULL != fpmask) fclose(fpmask); + + free(dhrbuf); + free(hgrbuf); + + if (status == INVALID) return (1); + + return SUCCESS; +} + + diff --git a/bmp2dhr/b2d.h b/bmp2dhr/b2d.h new file mode 100644 index 00000000..edab7708 --- /dev/null +++ b/bmp2dhr/b2d.h @@ -0,0 +1,924 @@ +/* --------------------------------------------------------------------- +Bmp2DHR (C) Copyright Bill Buckels 2014. +All Rights Reserved. + +Module Name - Description +------------------------- + +b2d.h - header file for 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. + +*/ + +#ifndef BMP2SHR_H +#define BMP2SHR_H 1 + +/* ***************************************************************** */ +/* ========================== includes ============================= */ +/* ***************************************************************** */ + +#include "tomthumb.h" + +/* ***************************************************************** */ +/* ========================== defines ============================== */ +/* ***************************************************************** */ + +/* file name length max */ +#define MAXF 256 + +#define SUCCESS 0 +#define INVALID -1 +#define RETRY 911 +#define BIN_OUTPUT 1 +#define SPRITE_OUTPUT 2 + +/* constants for the biCompression field in a BMP header */ +#define BI_RGB 0L +#define BI_RLE8 1L +#define BI_RLE4 2L + +/* DHGR, DLGR, LGR colors - LGR color order */ +#define LOBLACK 0 +#define LORED 1 +#define LODKBLUE 2 +#define LOPURPLE 3 +#define LODKGREEN 4 +#define LOGRAY 5 +#define LOMEDBLUE 6 +#define LOLTBLUE 7 +#define LOBROWN 8 +#define LOORANGE 9 +#define LOGREY 10 +#define LOPINK 11 +#define LOLTGREEN 12 +#define LOYELLOW 13 +#define LOAQUA 14 +#define LOWHITE 15 + +/* HGR color constants */ +#define HBLACK 0 +#define HGREEN 1 +#define HVIOLET 2 +#define HORANGE 3 +#define HBLUE 4 +#define HWHITE 5 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +#define FLOYDSTEINBERG 1 +#define JARVIS 2 +#define STUCKI 3 +#define ATKINSON 4 +#define BURKES 5 +#define SIERRA 6 +#define SIERRATWO 7 +#define SIERRALITE 8 +#define BUCKELS 9 +#define CUSTOM 10 + +#define ASCIIZ 0 +#define CRETURN 13 +#define LFEED 10 + +#define PSEUDOMAX 100 + +/* ***************************************************************** */ +/* ========================== typedefs ============================= */ +/* ***************************************************************** */ + +/* Bitmap Header structures */ +typedef struct __attribute__((__packed__)) tagBITMAPINFOHEADER { + uint32_t biSize; + uint32_t biWidth; + uint32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + uint32_t biXPelsPerMeter; + uint32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +} BITMAPINFOHEADER; + +typedef struct __attribute__((__packed__)) tagBITMAPFILEHEADER + +{ + uint8_t bfType[2]; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +} BITMAPFILEHEADER; + +typedef struct __attribute__((__packed__)) tagBMPHEADER { + BITMAPFILEHEADER bfi; + BITMAPINFOHEADER bmi; +} BMPHEADER; + +typedef struct __attribute__((__packed__)) tagRGBQUAD { + uint8_t rgbBlue; + uint8_t rgbGreen; + uint8_t rgbRed; + uint8_t rgbReserved; +} RGBQUAD; + + +/* ***************************************************************** */ +/* =================== prototypes as required ===================== */ +/* ***************************************************************** */ + +int16_t GetUserTextFile(void); +int dhrgetpixel(int x,int y); + +/* ***************************************************************** */ +/* ========================== globals ============================== */ +/* ***************************************************************** */ + +/* output buffers */ +uint8_t *dhrbuf; +uint8_t *hgrbuf; + +/* static structures for processing */ +BITMAPFILEHEADER bfi; +BITMAPINFOHEADER bmi; +BMPHEADER mybmp,maskbmp; +RGBQUAD sbmp[256], maskpalette[256]; /* super vga - applewin */ + +/* overlay file for screen titling and framing and "cleansing" */ +FILE *fpmask = NULL; +uint8_t remap[256]; + +char bmpfile[MAXF], dibfile[MAXF], scaledfile[MAXF], previewfile[MAXF], reformatfile[MAXF], maskfile[MAXF], fmask[MAXF]; +char spritefile[MAXF], mainfile[MAXF],auxfile[MAXF],a2fcfile[MAXF],usertextfile[MAXF],vbmpfile[MAXF],fname[MAXF]; + +/* HGR file designations for systems with long file names */ +/* these match the HGR output options selected */ +char hgrcolor[MAXF],hgrmono[MAXF],hgrwork[MAXF]; + +int mono = 0, dosheader = 0, spritemask = 0, tags=0; +int backgroundcolor = 0, quietmode = 1, diffuse = 0, merge = 0, scale = 0, applesoft = 0, outputtype = BIN_OUTPUT; +int reformat = 0, debug = 0; +int preview = 0, vbmp = 0, hgroutput = 0; +int overlay = 0, maskpixel=0, overcolor=0, clearcolor=5; + +/* 2 x 2 general purpose cross-hatching */ +int xmatrix = 0, ymatrix = 0, threshold = 0; + +uint16_t bmpwidth = 0, bmpheight = 0, spritewidth = 0; + +/* classic size alternate windowed output */ +/* applies to 320 x 200, 640 x 400, and 640 x 480 only */ +/* for 320 x 200 the window is 280 x 192 */ +/* for 640 x the window is 560 x 384 */ +/* output is always 280 x 192 */ +/* for 640 output is merged 2 x 2 */ +int16_t justify = 0, jxoffset = -1, jyoffset = -1; + +/* HGR setting - by default, double colors is off and pixels are set individually */ +int doubleblack = 0, doublewhite = 0, doublecolors = 1, ditheroneline = 0; + +/* error diffusion dithering settings */ +int globalclip = 0; +int ditherstart = 0; +int bleed = 16; +int paletteclip = 0; + +/* custom dither file support */ +int16_t customdivisor; +int16_t customdither[3][11]; + +uint8_t msk[]={0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1}; +int reverse = 0; + +/* the line buffers in this program are designed to hold a 320 pixel scanline... + for reusability in other modules and for other Apple II modes + including SHR 320 */ +/* for DHGR monochrome they must hold a scanline 560 pixels wide */ +uint8_t bmpscanline[1920], + bmpscanline2[1920], + dibscanline1[1920], + dibscanline2[1920], + dibscanline3[1920], + dibscanline4[1920], + previewline[1920], + maskline[560]; + +/* Floyd-Steinberg Etc. Dithering */ +uint8_t dither = 0, errorsum = 0, serpentine = 0; +/* color channel buffers for Floyd-Steinberg Etc. Dithering */ +/* note that these are arrays of short integers (2 bytes * 320) */ +/* current line contains current scanline values + + (plus) seed values (error values) from previous line + + these error values are the linear distance in a scale of 0-255 + between the real color and the nearest palette color. + + each of the r,g,b color channels is separate. + + a psychovisual palette match is done with the real color + to get the near color from the palette + + the dither error is then applied to neighboring pixels + using the dithering filter's distribution pattern + + later on when the color channels are finally reconstructed + a psychovisual palette match is done again to get the dither + color. + + if preview is turned-on the output can be viewed in + a BMP viewer. + +*/ +int16_t redDither[640],greenDither[640],blueDither[640]; +/* seed values from previous line */ +int16_t redSeed[640],greenSeed[640],blueSeed[640]; +int16_t redSeed2[640],greenSeed2[640],blueSeed2[640]; +int16_t *colorptr, *seedptr, *seed2ptr, color_error; + +int colorbleed = 100; + +/* color hgr dither routines */ +/* these buffers correspond to redDither, blueDither, greenDither */ +int16_t redSave[320], greenSave[320], blueSave[320]; +int16_t OrangeBlueError[320], GreenVioletError[320]; +uint8_t HgrPixelPalette[320]; +uint8_t dither7 = 0, hgrdither = 0; + +/* HGR output routines */ +uint8_t palettebits[40], hgrpaltype = 255; /* Both palettes are active by default */ +uint8_t hgrcolortype = 0; +uint8_t work280[280], buf280[560]; + +/* built-in palette options */ +/* based on DHGR, DLGR, LGR colors */ +/* LGR color order */ +uint8_t kegs32colors[16][3] = { + {0,0,0}, /* black */ + {221,0,51}, /* red */ + {0,0,153}, /* dk blue */ + {221,0,221}, /* purple */ + {0,119,0}, /* dk green */ + {85,85,85}, /* gray */ + {34,34,255}, /* med blue */ + {102,170,255}, /* lt blue */ + {136,85,34}, /* brown */ + {255,102,0}, /* orange */ + {170,170,170}, /* grey */ + {255,153,136}, /* pink */ + {0,221,0}, /* lt green */ + {255,255,0}, /* yellow */ + {0,255,153}, /* aqua */ + {255,255,255}/* white */ +}; +uint8_t ciderpresscolors[16][3] = { + {0,0,0}, /* black */ + {221,0,51}, /* red */ + {0,0,153}, /* dk blue */ + {221,34,221}, /* purple */ + {0,119,34}, /* dk green */ + {85,85,85}, /* gray */ + {34,34,255}, /* med blue */ + {102,170,255}, /* lt blue */ + {136,85,0}, /* brown */ + {255,102,0}, /* orange */ + {170,170,170}, /* grey */ + {255,153,136}, /* pink */ + {17,221,0}, /* lt green */ + {255,255,0}, /* yellow */ + {68,255,153}, /* aqua */ + {255,255,255}/* white */ +}; + +uint8_t awinoldcolors[16][3] = { + {0,0,0}, /* black */ + {208,0,48}, /* red */ + {0,0,128}, /* dk blue */ + {255,0,255}, /* purple */ + {0,128,0}, /* dk green */ + {128,128,128}, /* gray */ + {0,0,255}, /* med blue */ + {96,160,255}, /* lt blue */ + {128,80,0}, /* brown */ + {255,128,0}, /* orange */ + {192,192,192}, /* grey */ + {255,144,128}, /* pink */ + {0,255,0}, /* lt green */ + {255,255,0}, /* yellow */ + {64,255,144}, /* aqua */ + {255,255,255} /* white */ +}; + +uint8_t awinnewcolors[16][3] = { + {0,0,0}, /* black */ + {157,9,102}, /* red */ + {42,42,229}, /* dk blue */ + {199,52,255}, /* purple */ + {0,118,26}, /* dk green */ + {128,128,128}, /* gray */ + {13,161,255}, /* med blue */ + {170,170,255}, /* lt blue */ + {85,85,0}, /* brown */ + {242,94,0}, /* orange */ + {192,192,192}, /* grey */ + {255,137,229}, /* pink */ + {56,203,0}, /* lt green */ + {213,213,26}, /* yellow */ + {98,246,153}, /* aqua */ + {255,255,255} /* white */ +}; + +/* http://en.wikipedia.org/wiki/List_of_8-bit_computer_hardware_palettes */ +uint8_t wikipedia[16][3] = { + {0,0,0}, /* black */ + {114,38,64}, /* red */ + {64,51,127}, /* dk blue */ + {228,52,254}, /* purple */ + {14,89,64}, /* dk green */ + {128,128,128}, /* gray */ + {27,154,254}, /* med blue */ + {191,179,255}, /* lt blue */ + {64,76,0}, /* brown */ + {228,101,1}, /* orange */ + {128,128,128}, /* grey */ + {241,166,191}, /* pink */ + {27,203,1}, /* lt green */ + {191,204,128}, /* yellow */ + {141,217,191}, /* aqua */ + {255,255,255} /* white */ +}; + +/* http://wsxyz.net/tohgr.html */ +/* Sheldon Simm's palette from todhr */ +/* + +Sheldon is clipping the black and white ranges. + +The usual reason for doing this is for dirty images (caused by poor digitizing or +sampling or re-sampling color depth loss due to scaling). By using a clipping threshold +at either end of the rgb range, blacks that are not quite black don't match to some other +dark color and whites that are not quite white don't match to some other light color. + +I too put this in place as a clipping option when I wrote Bmp2SHR about a year ago in my +Brooks output routines but it worked a little differently. I didn't build it into a +palette for one thing. + +Sheldon's weighting favours clipping blue gun values at both ends of the range. + +Red is clipped more than green in the high range, and green is clipped more than +red in the low range. + + static Pixel pal[] = { + { 1, 4, 8}, // 0 black + { 32, 54, 212}, // 1 dk blue + { 51, 111, 0}, // 2 dk green + { 7, 168, 225}, // 3 med blue + { 99, 77, 0}, // 4 brown + { 126, 126, 126}, // 5 gray + { 67, 200, 0}, // 6 lt green + { 93, 248, 133}, // 7 aqua + { 148, 12, 125}, // 8 red + { 188, 55, 255}, // 9 purple + { 126, 126, 126}, // A grey + { 158, 172, 255}, // B lt blue + { 249, 86, 29}, // C orange + { 255, 129, 236}, // D pink + { 221, 206, 23}, // E yellow + { 248, 250, 244} // F white + + Also note that I use the array name grpal. This was used as a palette in + one of Sheldon's previous versions. Since I have propagated this array name + to my code and my code is working fine with it, I have no plans to change it + to maintain currency with Sheldon's code. + +*/ +uint8_t grpal[16][3] = { + {0,0,0}, /* black */ + {148,12,125}, /* red - hgr 0*/ + {32,54,212}, /* dk blue - hgr 0 */ + {188,55,255}, /* purple - default HGR overlay color */ + {51,111,0}, /* dk green - hgr 0 */ + {126,126,126}, /* gray - hgr 0 */ + {7,168,225}, /* med blue - alternate HGR overlay color */ + {158,172,255}, /* lt blue - hgr 0 */ + {99,77,0}, /* brown - hgr 0 */ + {249,86,29}, /* orange */ + {126,126,126}, /* grey - hgr 0 */ + {255,129,236}, /* pink - hgr 0 */ + {67,200,0}, /* lt green */ + {221,206,23}, /* yellow - hgr 0 */ + {93,248,133}, /* aqua - hgr 0 */ + {255,255,255} /* white */ +}; + +/* Sheldon still uses the RGB values from his old palette for HGR conversion */ +uint8_t hgrpal[16][3] = { + {0x00,0x00,0x00}, /* black */ + {0xad,0x18,0x28}, /* red */ + {0x55,0x1b,0xe1}, /* dk blue */ + {0xe8,0x2c,0xf8}, /* purple 232,44,248 - default hgr overlay color */ + {0x01,0x73,0x63}, /* dk green */ + {0x7e,0x82,0x7f}, /* gray */ + {0x34,0x85,0xfc}, /* med blue - 52,133,252 - alternate HGR overlay color */ + {0xd1,0x95,0xff}, /* lt blue */ + {0x33,0x6f,0x00}, /* brown */ + {0xd0,0x81,0x01}, /* orange */ + {0x7f,0x7e,0x77}, /* grey */ + {0xfe,0x93,0xa3}, /* pink */ + {0x1d,0xd6,0x09}, /* lt green */ + {0xae,0xea,0x22}, /* yellow */ + {0x5b,0xeb,0xd9}, /* aqua */ + {0xff,0xff,0xff} /* white */ +}; + +/* Kegs32 uses Super Convert's colors */ +uint8_t SuperConvert[16][3] = { + 0,0,0, /* black */ + 221,0,51, /* red - hgr 0*/ + 0,0,153, /* dk blue - hgr 0 */ + 221,0,221, /* purple */ + 0,119,0, /* dk green - hgr 0 */ + 85,85,85, /* gray - hgr 0 */ + 34,34,255, /* med blue */ + 102,170,255, /* lt blue - hgr 0 */ + 136,85,34, /* brown - hgr 0 */ + 255,102,0, /* orange */ + 170,170,170, /* grey - hgr 0 */ + 255,153,136, /* pink - hgr 0 */ + 0,221,0, /* lt green */ + 255,255,0, /* yellow - hgr 0 */ + 0,255,153, /* aqua - hgr 0 */ + 255,255,255};/* white */ + +uint8_t Jace[16][3] = { + 0,0,0, /* black */ + 177,0,93, /* red */ + 32,41,255, /* dk blue */ + 210,41,255, /* purple */ + 0,127,34, /* dk green */ + 127,127,127, /* gray */ + 0,168,255, /* med blue */ + 160,168,255, /* lt blue */ + 94,86,0, /* brown */ + 255,86,0, /* orange */ + 127,127,127, /* grey */ + 255,127,220, /* pink */ + 44,213,0, /* lt green */ + 222,213,0, /* yellow */ + 77,255,161, /* aqua */ + 255,255,255}; /* white */ + +/* https://github.com/cybernesto/VBMP/wiki/Converting-a-picture-into-a-DHGR-color-image-using-the-GIMP */ +/* Robert Munafo - http://mrob.com/pub/xapple2/colors.html */ +uint8_t Cybernesto[16][3] = { + 0, 0, 0, /* 0 Black */ + 227, 30, 96, /* 1 Magenta */ + 96, 78,189, /* 8 Dark Blue */ + 255, 68,253, /* 9 Violet */ + 0,163, 96, /* 4 Dark Green */ + 156,156,156, /* 5 Grey1 */ + 20,207,253, /* 12 Medium Blue */ + 208,195,255, /* 13 Light Blue */ + 96,114, 3, /* 2 Brown */ + 255,106, 60, /* 3 Orange */ + 156,156,156, /* 10 Grey2 */ + 255,160,208, /* 11 Pink */ + 20,245, 60, /* 6 Green */ + 208,221,141, /* 7 Yellow */ + 114,255,208, /* 14 Aqua */ + 255,255,255}; /* 15 White */ + +/* pseudopalette support */ +int16_t pseudocount = 0; +int16_t pseudolist[PSEUDOMAX]; +uint16_t pseudowork[16][3]; + +/* "merged" pseudo palette average RGB values*/ +/* initial values are the average values of p5 NTSC and p12 RGB */ +uint8_t PseudoPalette[16][3] = { + 0,0,0, + 184,6,88, + 16,27,182, + 204,27,238, + 25,115,0, + 105,105,105, + 20,101,240, + 130,171,255, + 117,81,17, + 252,94,14, + 148,148,148, + 255,141,186, + 33,210,0, + 238,230,11, + 46,251,143, + 255,255,255}; + + +/* ------------------------------------------------------------------------------ */ +/* skip this if you think that this program has been clean up to now. */ +/* this is more like an egg I laid than an easter egg. */ +/* some legacy stuff - unhacked skeletons in my closet */ +/* from the AppleX Aztec C65 distribution - not everything always goes as planned */ +/* ------------------------------------------------------------------------------ */ + +/* "designer" palettes - VGA style mapping colors from BMPA2FC */ +/* these are in the lo-res color order, same as above */ +/* in BMPA2FC these were in IBM-PC default BIOS order */ + +/* Serious doubt! */ +/* at this point, I wonder how well BMPA2FC mapped colors from BMP's */ +/* not very well I think - but it's an old MS-DOS utility and no point + in worrying about it now - new users who find this can play with it */ + +/* this first array was my first attempt at establishing something that looked like + 24 bit Lo-Res Colors - I am not even sure where I got the colors + they looked like what I wanted at the time before I knew a little more + + the rest of the palettes in here were just pasted forward from ClipShop + but with a couple of minor changes to accomodate the move to Windows Paint for Xp + and the 16 color BMP's which have been stable ever since. + + why the default palette changed between XP and Windows 3.1 I have no clue. + + */ + +/* If someone happens to use my canvas scheme from + my old utilities or whatever, having these here will at least save them + from ending-up with work that needs to be redone if they expected remapping */ + +/* goes with the canvas bmp that I put out there with AppleX */ +uint8_t rgbCanvasArray[16][3] = { +0 , 0 , 0 , +208, 0 , 48 , +0 , 0 , 128, +255, 0 , 255, +0 , 128, 0 , +128, 128, 128, +0 , 0 , 255, +96 , 160, 255, +128, 80 , 0 , +255, 128, 0 , +192, 192, 192, +255, 144, 128, +0 , 255, 0 , +255, 255, 0 , +64 , 255, 144, +255, 255, 255}; + +/* this might work with old Win16 16 color BMP's */ +uint8_t rgbBmpArray[16][3] = { +0 ,0 , 0 , +191,0 , 0 , +0 ,0 , 191, +191,0 , 191, +0 ,191, 0 , +128,128, 128, +0 ,191, 191, +0 ,0 , 255, +191,191, 0 , +255,0 , 0 , +192,192, 192, +255,0 , 255, +0 ,255, 0 , +255,255, 0 , +0 ,255, 255, +255,255, 255}; + +/* this might work with new Win32 16 color BMP's */ +uint8_t rgbXmpArray[16][3] = { +0 , 0 , 0 , +128, 0 , 0 , +0 , 0 , 128, +128, 0 , 128, +0 , 128, 0 , +128, 128, 128, +0 , 128, 128, +0 , 0 , 255, +128, 128, 0 , +255, 0 , 0 , +192, 192, 192, +255, 0 , 255, +0 , 255, 0 , +255, 255, 0 , +0 , 255, 255, +255, 255, 255}; + +/* from the bios in some PC I had */ +uint8_t rgbVgaArray[16][3] = { +0 , 0 , 0 , +255, 0 , 0 , +0 , 0 , 255, +255, 0 , 255, +0 , 255, 0 , +85 , 85 , 85 , +0 , 255, 255, +85 , 85 , 255, +255, 255, 0 , +255, 85 , 85 , +192, 192, 192, +255, 85 , 255, +85 , 255, 85 , +255, 255, 85 , +85 , 255, 255, +255, 255, 255}; + +/* some old ZSoft VGA Pcx Colors */ +uint8_t rgbPcxArray[16][3] = { +0 , 0 , 0 , +170, 0 , 0 , +0 , 0 , 170, +170, 0 , 170, +0 , 170, 0 , +85 , 85 , 85 , +0 , 170, 170, +85 , 85 , 255, +170, 170, 0 , +255, 85 , 85 , +170, 170, 170, +255, 85 , 255, +85 , 255, 85 , +255, 255, 85 , +85 , 255, 255, +255, 255, 255}; + +/* ------------------------------------------------------------------- */ +/* end of legacy unhacked hack, and back to something that makes sense */ +/* ------------------------------------------------------------------- */ + +/* palette work arrays */ +uint8_t rgbArray[16][3], rgbAppleArray[16][3], rgbPreview[16][3], rgbUser[16][3]; +double rgbLuma[16], rgbDouble[16][3]; +double rgbOrangeDouble[3][3], rgbGreenDouble[3][3]; +double rgbOrangeLuma[3], rgbGreenLuma[3]; + +double rgbLumaBrighten[16], rgbDoubleBrighten[16][3]; +double rgbLumaDarken[16], rgbDoubleDarken[16][3]; + +/* provides base address for page1 hires scanlines */ +unsigned HB[]={ +0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, +0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80, +0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00, +0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80, +0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00, +0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80, +0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00, +0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80, +0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28, +0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8, +0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28, +0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8, +0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28, +0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8, +0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28, +0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8, +0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50, +0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0, +0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50, +0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0, +0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50, +0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0, +0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50, +0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0}; + +/* + +The following is logically reordered to match the lores +color order... + + Repeated + Binary + Color aux1 main1 aux2 main2 Pattern + Black 00 00 00 00 0000 + Magenta 08 11 22 44 0001 + Dark Blue 11 22 44 08 1000 + Violet 19 33 66 4C 1001 + Dark Green 22 44 08 11 0100 + Grey1 2A 55 2A 55 0101 + Medium Blue 33 66 4C 19 1100 + Light Blue 3B 77 6E 5D 1101 + Brown 44 08 11 22 0010 + Orange 4C 19 33 66 0011 + Grey2 55 2A 55 2A 1010 + Pink 5D 3B 77 6E 1011 + Green 66 4C 19 33 0110 + Yellow 6E 5D 3B 77 0111 + Aqua 77 6E 5D 3B 1110 + White 7F 7F 7F 7F 1111 + +*/ + +/* the following array is based on the above */ +uint8_t dhrbytes[16][4] = { + 0x00,0x00,0x00,0x00, + 0x08,0x11,0x22,0x44, + 0x11,0x22,0x44,0x08, + 0x19,0x33,0x66,0x4C, + 0x22,0x44,0x08,0x11, + 0x2A,0x55,0x2A,0x55, + 0x33,0x66,0x4C,0x19, + 0x3B,0x77,0x6E,0x5D, + 0x44,0x08,0x11,0x22, + 0x4C,0x19,0x33,0x66, + 0x55,0x2A,0x55,0x2A, + 0x5D,0x3B,0x77,0x6E, + 0x66,0x4C,0x19,0x33, + 0x6E,0x5D,0x3B,0x77, + 0x77,0x6E,0x5D,0x3B, + 0x7F,0x7F,0x7F,0x7F}; + +int16_t lores = 0, loresoutput = 0, appletop = 0; + +/* base addresses for Apple II primary text page */ +/* also the base addresses for the 48 scanline pairs */ +/* for Apple II lores graphics mode 40 x 48 x 16 colors */ +unsigned textbase[24]={ + 0x0400, + 0x0480, + 0x0500, + 0x0580, + 0x0600, + 0x0680, + 0x0700, + 0x0780, + 0x0428, + 0x04A8, + 0x0528, + 0x05A8, + 0x0628, + 0x06A8, + 0x0728, + 0x07A8, + 0x0450, + 0x04D0, + 0x0550, + 0x05D0, + 0x0650, + 0x06D0, + 0x0750, + 0x07D0}; + +/* the following is used to remap + double lo res 4 bit colors + from bank 0 to bank 1 */ +uint8_t dloauxcolor[16] = { + 0,8,1,9,2,10,3,11,4,12,5,13,6,14,7,15}; + +/* the following is used to remap + double lo res 4 bit colors + from bank 1 to bank 0 */ + +/* +uint8_t dlomaincolor[16] = { + 0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}; + +*/ + +/* resizing tables */ + +/* use as-is to use for 320 x 200 */ +/* merge 4 pixels into one to use for 640 x 400 */ +uint8_t mix25to24[24][2] = { +24,1, +23,2, +22,3, +21,4, +20,5, +19,6, +18,7, +17,8, +16,9, +15,10, +14,11, +13,12, +12,13, +11,14, +10,15, +9,16, +8,17, +7,18, +6,19, +5,20, +4,21, +3,22, +2,23, +1,24}; + +/* + +To scale from 320 to 280, a 7 part mix is used from each pixel +in the following pattern with a ratio of 8:7 + +00000001 11111122 22222333 33334444 44455555 55666666 67777777 +00000000 11111111 22222222 33333333 44444444 55555555 66666666 + +*/ + +uint8_t pixel320to280[7][4] = { +7,1,0,1, +6,2,1,2, +5,3,2,3, +4,4,3,4, +3,5,4,5, +2,6,5,6, +1,7,6,7}; + + +/* http://en.wikipedia.org/wiki/List_of_8-bit_computer_hardware_palettes */ +uint8_t rgbVBMP[16][3] = { + 0,0,0, /* black */ + 114,38,64, /* red */ + 64,51,127, /* dk blue */ + 228,52,254, /* purple */ + 14,89,64, /* dk green */ + 128,128,128, /* gray */ + 27,154,254, /* med blue */ + 191,179,255, /* lt blue */ + 64,76,0, /* brown */ + 228,101,1, /* orange */ + 128,128,128, /* grey */ + 241,166,191, /* pink */ + 27,203,1, /* lt green */ + 191,204,128, /* yellow */ + 141,217,191, /* aqua */ + 255,255,255};/* white */ + +uint8_t RemapLoToHi[16] = { + LOBLACK, + LORED, + LOBROWN, + LOORANGE, + LODKGREEN, + LOGRAY, + LOLTGREEN, + LOYELLOW, + LODKBLUE, + LOPURPLE, + LOGREY, + LOPINK, + LOMEDBLUE, + LOLTBLUE, + LOAQUA, + LOWHITE}; + +/* 560 x 192 - verbatim DHGR mono output format */ +uint8_t mono192[62] ={ +0x42, 0x4D, 0x3E, 0x36, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, +0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0xC0, 0x00, +0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00}; + +/* 280 x 192 - verbatim HGR output format */ +uint8_t mono280[62] ={ +0x42, 0x4D, 0x3E, 0x1B, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, +0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0xC0, 0x00, +0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00}; + +uint8_t dhgr2hgr[16] = { + HBLACK, + HBLACK, + HBLACK, + HVIOLET, + HBLACK, + HBLACK, + HBLUE, + HBLACK, + HBLACK, + HORANGE, + HBLACK, + HBLACK, + HGREEN, + HBLACK, + HBLACK, + HWHITE}; + +#endif diff --git a/bmp2dhr/tomthumb.h b/bmp2dhr/tomthumb.h new file mode 100644 index 00000000..03ff9749 --- /dev/null +++ b/bmp2dhr/tomthumb.h @@ -0,0 +1,774 @@ +/* --------------------------------------------------------------------- +Bmp2DHR (C) Copyright Bill Buckels 2014. +All Rights Reserved. + +Module Name - Description +------------------------- + +tomthumb.h - header file for b2d.h (main program header) + +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. + + +*/ +/* ------------------------------------------------------------------------ +Written by : Bill Buckels + Based on a font by Brian Swetland and Robey Pointer + +Date Ported : July 2014 + 1.0 First Release - cc65 + cross-development environment for current cc65 snapshot + includes Windows, Linux, and Mac OSX +Licence : You may use this program for whatever you wish as long + as you agree that Bill Buckels has no warranty or + liability obligations whatsoever from said use. +------------------------------------------------------------------------ */ + +/* + +The "Tom Thumb" Font (below) was originally developed as a Palm Pilot +font by developer Brian Swetland, and "fine-tuned" as a derivative +work by Robey Pointer, robeypointer@gmail.com: + +http://robey.lag.net/2010/01/23/tiny-monospace-font.html */ + +/* + +The Tom Thumb Font is a derivative work partially covered under the +following Copyright and Conditions of use: + +** Copyright 1999 Brian J. Swetland +** Copyright 1999 Vassilii Khachaturov +** Portions (of vt100.c/vt100.h) copyright Dan Marks +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions, and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions, and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the authors may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* the following version of the Tom Thumb Font was entirely + hand-made by me in my programmer’s editor by simply looking at + a bitmap of the font characters and counting pixels. + + Licence: You may use this program for whatever you wish as long + as you agree that Bill Buckels has no warranty or + liability obligations whatsoever from said use. +*/ + +#ifndef TOMTHUMB_H +#define TOMTHUMB_H 1 + +/* unsigned char tomthumb[96][6][3] */ +unsigned char tomthumb[1728] = { +/* 32 */ +0,0,0, +0,0,0, +0,0,0, +0,0,0, +0,0,0, +0,0,0, +/* ! */ +0,1,0, +0,1,0, +0,1,0, +0,0,0, +0,1,0, +0,0,0, +/* " */ +1,0,1, +1,0,1, +0,0,0, +0,0,0, +0,0,0, +0,0,0, +/* # */ +1,0,1, +1,1,1, +1,0,1, +1,1,1, +1,0,1, +0,0,0, +/* $ */ +0,1,1, +1,1,0, +0,1,1, +1,1,0, +0,1,0, +0,0,0, +/* % */ +1,0,0, +0,0,1, +0,1,0, +1,0,0, +0,0,1, +0,0,0, +/* & */ +1,1,0, +1,1,0, +1,1,1, +1,0,1, +0,1,1, +0,0,0, +/* ' */ +0,1,0, +0,1,0, +0,0,0, +0,0,0, +0,0,0, +0,0,0, +/* ( */ +0,0,1, +0,1,0, +0,1,0, +0,1,0, +0,0,1, +0,0,0, +/* ) */ +1,0,0, +0,1,0, +0,1,0, +0,1,0, +1,0,0, +0,0,0, +/* * */ +1,0,1, +0,1,0, +1,0,1, +0,0,0, +0,0,0, +0,0,0, +/* + */ +0,0,0, +0,1,0, +1,1,1, +0,1,0, +0,0,0, +0,0,0, +/* , */ +0,0,0, +0,0,0, +0,0,0, +0,1,0, +1,0,0, +0,0,0, +/* - */ +0,0,0, +0,0,0, +1,1,1, +0,0,0, +0,0,0, +0,0,0, +/* . */ +0,0,0, +0,0,0, +0,0,0, +0,0,0, +0,1,0, +0,0,0, +/* / */ +0,0,1, +0,0,1, +0,1,0, +1,0,0, +1,0,0, +0,0,0, +/* 0 */ +0,1,1, +1,0,1, +1,0,1, +1,0,1, +1,1,0, +0,0,0, +/* 1 */ +0,1,0, +1,1,0, +0,1,0, +0,1,0, +0,1,0, +0,0,0, +/* 2 */ +1,1,0, +0,0,1, +0,1,0, +1,0,0, +1,1,1, +0,0,0, +/* 3 */ +1,1,0, +0,0,1, +0,1,0, +0,0,1, +1,1,0, +0,0,0, +/* 4 */ +1,0,1, +1,0,1, +1,1,1, +0,0,1, +0,0,1, +0,0,0, +/* 5 */ +1,1,1, +1,0,0, +1,1,0, +0,0,1, +1,1,0, +0,0,0, +/* 6 */ +0,1,1, +1,0,0, +1,1,1, +1,0,1, +1,1,1, +0,0,0, +/* 7 */ +1,1,1, +0,0,1, +0,1,0, +1,0,0, +1,0,0, +0,0,0, +/* 8 */ +1,1,1, +1,0,1, +1,1,1, +1,0,1, +1,1,1, +0,0,0, +/* 9 */ +1,1,1, +1,0,1, +1,1,1, +0,0,1, +1,1,0, +0,0,0, +/* : */ +0,0,0, +0,1,0, +0,0,0, +0,1,0, +0,0,0, +0,0,0, +/* ; */ +0,0,0, +0,1,0, +0,0,0, +0,1,0, +1,0,0, +0,0,0, +/* < */ +0,0,1, +0,1,0, +1,0,0, +0,1,0, +0,0,1, +0,0,0, +/* = */ +0,0,0, +1,1,1, +0,0,0, +1,1,1, +0,0,0, +0,0,0, +/* > */ +1,0,0, +0,1,0, +0,0,1, +0,1,0, +1,0,0, +0,0,0, +/* ? */ +1,1,1, +0,0,1, +0,1,0, +0,0,0, +0,1,0, +0,0,0, +/* @ */ +0,1,0, +1,0,1, +1,1,1, +1,0,0, +0,1,1, +0,0,0, +/* A */ +0,1,0, +1,0,1, +1,1,1, +1,0,1, +1,0,1, +0,0,0, +/* B */ +1,1,0, +1,0,1, +1,1,0, +1,0,1, +1,1,0, +0,0,0, +/* C */ +0,1,1, +1,0,0, +1,0,0, +1,0,0, +0,1,1, +0,0,0, +/* D */ +1,1,0, +1,0,1, +1,0,1, +1,0,1, +1,1,0, +0,0,0, +/* E */ +1,1,1, +1,0,0, +1,1,1, +1,0,0, +1,1,1, +0,0,0, +/* F */ +1,1,1, +1,0,0, +1,1,1, +1,0,0, +1,0,0, +0,0,0, +/* G */ +0,1,1, +1,0,0, +1,1,1, +1,0,1, +0,1,1, +0,0,0, +/* H */ +1,0,1, +1,0,1, +1,1,1, +1,0,1, +1,0,1, +0,0,0, +/* I */ +1,1,1, +0,1,0, +0,1,0, +0,1,0, +1,1,1, +0,0,0, +/* J */ +0,0,1, +0,0,1, +0,0,1, +1,0,1, +0,1,0, +0,0,0, +/* K */ +1,0,1, +1,0,1, +1,1,0, +1,0,1, +1,0,1, +0,0,0, +/* L */ +1,0,0, +1,0,0, +1,0,0, +1,0,0, +1,1,1, +0,0,0, +/* M */ +1,0,1, +1,1,1, +1,1,1, +1,0,1, +1,0,1, +0,0,0, +/* N */ +1,0,1, +1,1,1, +1,1,1, +1,1,1, +1,0,1, +0,0,0, +/* O */ +0,1,0, +1,0,1, +1,0,1, +1,0,1, +0,1,0, +0,0,0, +/* P */ +1,1,0, +1,0,1, +1,1,0, +1,0,0, +1,0,0, +0,0,0, +/* Q */ +0,1,0, +1,0,1, +1,0,1, +1,1,1, +0,1,1, +0,0,0, +/* R */ +1,1,0, +1,0,1, +1,1,1, +1,1,0, +1,0,1, +0,0,0, +/* S */ +0,1,1, +1,0,0, +0,1,0, +0,0,1, +1,1,0, +0,0,0, +/* T */ +1,1,1, +0,1,0, +0,1,0, +0,1,0, +0,1,0, +0,0,0, +/* U */ +1,0,1, +1,0,1, +1,0,1, +1,0,1, +0,1,1, +0,0,0, +/* V */ +1,0,1, +1,0,1, +1,0,1, +0,1,0, +0,1,0, +0,0,0, +/* W */ +1,0,1, +1,0,1, +1,1,1, +1,1,1, +1,0,1, +0,0,0, +/* X */ +1,0,1, +1,0,1, +0,1,0, +1,0,1, +1,0,1, +0,0,0, +/* Y */ +1,0,1, +1,0,1, +0,1,0, +0,1,0, +0,1,0, +0,0,0, +/* Z */ +1,1,1, +0,0,1, +0,1,0, +1,0,0, +1,1,1, +0,0,0, +/* [ */ +1,1,1, +1,0,0, +1,0,0, +1,0,0, +1,1,1, +0,0,0, +/* \ */ +0,0,0, +1,0,0, +0,1,0, +0,0,1, +0,0,0, +0,0,0, +/* ] */ +1,1,1, +0,0,1, +0,0,1, +0,0,1, +1,1,1, +0,0,0, +/* ^ */ +0,1,0, +1,0,1, +0,0,0, +0,0,0, +0,0,0, +0,0,0, +/* _ */ +0,0,0, +0,0,0, +0,0,0, +0,0,0, +1,1,1, +0,0,0, +/* ` */ +1,0,0, +0,1,0, +0,0,0, +0,0,0, +0,0,0, +0,0,0, +/* a */ +0,0,0, +1,1,0, +0,1,1, +1,0,1, +1,1,1, +0,0,0, +/* b */ +1,0,0, +1,1,0, +1,0,1, +1,0,1, +1,1,0, +0,0,0, +/* c */ +0,0,0, +0,1,1, +1,0,0, +1,0,0, +0,1,1, +0,0,0, +/* d */ +0,0,1, +0,1,1, +1,0,1, +1,0,1, +0,1,1, +0,0,0, +/* e */ +0,0,0, +0,1,1, +1,0,1, +1,1,0, +0,1,1, +0,0,0, +/* f */ +0,0,1, +0,1,0, +1,1,1, +0,1,0, +0,1,0, +0,0,0, +/* g */ +0,0,0, +0,1,1, +1,0,1, +1,1,1, +0,0,1, +0,1,0, +/* h */ +1,0,0, +1,1,0, +1,0,1, +1,0,1, +1,0,1, +0,0,0, +/* i */ +0,1,0, +0,0,0, +0,1,0, +0,1,0, +0,1,0, +0,0,0, +/* j */ +0,0,1, +0,0,0, +0,0,1, +0,0,1, +1,0,1, +0,1,0, +/* k */ +1,0,0, +1,0,1, +1,1,0, +1,1,0, +1,0,1, +0,0,0, +/* l */ +1,1,0, +0,1,0, +0,1,0, +0,1,0, +1,1,1, +0,0,0, +/* m */ +0,0,0, +1,1,1, +1,1,1, +1,1,1, +1,0,1, +0,0,0, +/* n */ +0,0,0, +1,1,0, +1,0,1, +1,0,1, +1,0,1, +0,0,0, +/* o */ +0,0,0, +0,1,0, +1,0,1, +1,0,1, +0,1,0, +0,0,0, +/* p */ +0,0,0, +1,1,0, +1,0,1, +1,0,1, +1,1,0, +1,0,0, +/* q */ +0,0,0, +0,1,1, +1,0,1, +1,0,1, +0,1,1, +0,0,1, +/* r */ +0,0,0, +0,1,1, +1,0,0, +1,0,0, +1,0,0, +0,0,0, +/* s */ +0,0,0, +0,1,1, +1,1,0, +0,1,1, +1,1,0, +0,0,0, +/* t */ +0,1,0, +1,1,1, +0,1,0, +0,1,0, +0,1,1, +0,0,0, +/* u */ +0,0,0, +1,0,1, +1,0,1, +1,0,1, +0,1,1, +0,0,0, +/* v */ +0,0,0, +1,0,1, +1,0,1, +1,0,1, +0,1,0, +0,0,0, +/* w */ +0,0,0, +1,0,1, +1,1,1, +1,1,1, +1,1,1, +0,0,0, +/* x */ +0,0,0, +1,0,1, +0,1,0, +0,1,0, +1,0,1, +0,0,0, +/* y */ +0,0,0, +1,0,1, +1,0,1, +0,1,1, +0,0,1, +0,1,0, +/* z */ +0,0,0, +1,1,1, +0,1,1, +1,1,0, +1,1,1, +0,0,0, +/* { */ +0,1,1, +0,1,0, +1,0,0, +0,1,0, +0,1,1, +0,0,0, +/* | */ +0,1,0, +0,1,0, +0,0,0, +0,1,0, +0,1,0, +0,0,0, +/* } */ +1,1,0, +0,1,0, +0,0,1, +0,1,0, +1,1,0, +0,0,0, +/* ~ */ +0,1,1, +1,1,0, +0,0,0, +0,0,0, +0,0,0, +0,0,0, +/* ¦ */ +1,1,1, +0,0,0, +1,1,1, +1,1,1, +1,1,1, +0,0,0}; +#endif + diff --git a/chiptune_debug/NOTES b/chiptune_debug/NOTES index b0ec533e..7c90ee91 100644 --- a/chiptune_debug/NOTES +++ b/chiptune_debug/NOTES @@ -91,3 +91,4 @@ Still shows the glitch, actually very quickly. Changes made to test this: + Try increasing the WAIT delay from 85 to 120 (still an issue) +