- this implements blargg's nes ntsc filters using core options - an optional height doubling is also added but disabled for performance reasons (might make that optional as a core option) - since PS2 and PSP have their own blitter branches, these platforms do not have the ntsc filters since i dont have the means to test on those systems. - compile with HAVE_NTSC=1 to have these options, HAVE_NTSC=0 disabled filter including core options - HAVE_NTSC=1 is set as default, other than PS2 and PSP as stated above.
219 lines
10 KiB
Plaintext
219 lines
10 KiB
Plaintext
nes_ntsc 0.2.2: NES NTSC Video Filter
|
|
-------------------------------------
|
|
Author : Shay Green <gblargg@gmail.com>
|
|
Website : http://www.slack.net/~ant/
|
|
Forum : http://groups.google.com/group/blargg-sound-libs
|
|
License : GNU Lesser General Public License (LGPL)
|
|
Language: C or C++
|
|
|
|
|
|
Overview
|
|
--------
|
|
To perform NTSC filtering, first allocate memory for a nes_ntsc_t object
|
|
and call nes_ntsc_init(), then call nes_ntsc_blit() to perform
|
|
filtering. You can call nes_ntsc_init() at any time to change image
|
|
parameters.
|
|
|
|
nes_ntsc_blit() reads NES pixels and writes RGB pixels (16-bit by
|
|
default). The NES pixels are 6-bit raw palette values (0 to 0x3F). Edit
|
|
nes_ntsc_config.h to change this.
|
|
|
|
|
|
Color Emphasis Support
|
|
----------------------
|
|
Support for the three color emphasis bits in PPU register $2001 can be
|
|
enabled in nes_ntsc_config.h. By default, support is disabled and a
|
|
64-color palette is used. If enabled, a 512-color palette is used and
|
|
the three color emphasis bits from PPU $2001 are stored in bits 6-8 (bit
|
|
mask 0x1C0) of the input pixels to the blitter. Form the index as
|
|
follows:
|
|
|
|
index = (ppu_2001 << 1 & 0x1C0) | (palette_index & 0x3F).
|
|
|
|
|
|
RGB Palettes
|
|
------------
|
|
An RGB palette can be generated for use in a normal blitter. If color
|
|
emphasis is disabled, it's 64 colors (192 bytes); if enabled, it's 512
|
|
colors (1536 bytes). In your nes_ntsc_setup_t structure, point
|
|
palette_out to a buffer to hold the palette, then call nes_ntsc_init().
|
|
If you only need the palette and aren't going to be using the NTSC
|
|
filter, pass 0 for the first parameter.
|
|
|
|
A custom source RGB palette can also be used to replace the standard NES
|
|
color generation hardware. See the palette and base_palette members of
|
|
nes_ntsc_setup_t in nes_ntsc.h for more.
|
|
|
|
|
|
Image Parameters
|
|
----------------
|
|
Many image parameters can be adjusted and presets are provided for
|
|
composite video, S-video, RGB, and monochrome. Most are floating-point
|
|
values with a general range of -1.0 to 1.0, where 0 is normal. The
|
|
ranges are adjusted so that one parameter at an extreme (-1 or +1) and
|
|
the rest at zero shouldn't result in any internal overflow (garbage
|
|
pixels). Setting multiple parameters to their extreme can produce
|
|
garbage. Put another way, the state space defined by all parameters
|
|
within the range -1 to +1 is not fully usable, but some extreme corners
|
|
are very useful so I don't want to reduce the parameter ranges.
|
|
|
|
The sharpness and resolution parameters have similar effects. Resolution
|
|
affects how crisp pixels are. Sharpness merely enhances the edges by
|
|
increasing contrast, which makes things brighter at the edges. Artifacts
|
|
sets how much "junk" is around the edges where colors and brightness
|
|
change in the image, where -1 completely eliminates them. (Color) bleed
|
|
affects how much colors blend together and the artifact colors at the
|
|
edges of pixels surrounded by black. (Color) fringing affects how much
|
|
color fringing occurs around the edges of bright objects, especially
|
|
white text on a black background.
|
|
|
|
When using custom settings, initialize your nes_ntsc_setup_t using one
|
|
of the standard setups before customizing it. This will ensure that all
|
|
fields are properly initialized, including any added in future releases
|
|
of the library that your current code can't even know about.
|
|
|
|
nes_ntsc_setup_t setup;
|
|
setup = nes_ntsc_composite; /* do this first */
|
|
setup.sharpness = custom_sharpness;
|
|
nes_ntsc_init( ntsc, &setup );
|
|
|
|
|
|
Image Size
|
|
----------
|
|
For proper aspect ratio, the image generated by the library must be
|
|
doubled vertically.
|
|
|
|
Use the NES_NTSC_OUT_WIDTH() and NES_NTSC_IN_WIDTH() macros to convert
|
|
between input and output widths that the blitter uses. For example, if
|
|
you are blitting an image 256 pixels wide, use NES_NTSC_OUT_WIDTH( 256 )
|
|
to find out how many output pixels are written per row. Another example,
|
|
use NES_NTSC_IN_WIDTH( 640 ) to find how many input pixels will fit
|
|
within 640 output pixels. The blitter rounds the input width down in
|
|
some cases, so the requested width might not be possible. Use
|
|
NES_NTSC_IN_WIDTH( NES_NTSC_OUT_WIDTH( in_width ) ) to find what a given
|
|
in_width would be rounded down to.
|
|
|
|
|
|
Burst Phase
|
|
-----------
|
|
The burst_phase parameter to nes_ntsc_blit() should generally toggle
|
|
values between frames, i.e. 0 on first call to nes_ntsc_blit(), 1 on
|
|
second call, 0 on third call, 1 on fourth, etc. If merge_fields is
|
|
enabled (see below), you should always pass 0. Read further for more
|
|
detailed operation.
|
|
|
|
If you're using nes_ntsc_blit() to do partial screen updates,
|
|
burst_phase should be calculated as (burst_phase + row) % 3, where row
|
|
is the starting row (0 through 239). For example, if burst_phase is 1
|
|
for the current frame and you make two calls to nes_ntsc_blit() to blit
|
|
rows 0 to 100, then rows 101 to 239, for the first call you should pass
|
|
1 for burst_phase, and for the second call you should pass 0 for
|
|
burst_phase: (1 + 101) % 3 = 0. Do the same regardless of the
|
|
merge_fields setting.
|
|
|
|
For more accurate burst_phase operation, it should be adjusted at the
|
|
beginning of a frame based on the length of scanline 20: if 341 clocks
|
|
(normal), burst_phase = (burst_phase + 1) % 3, otherwise burst_phase =
|
|
(burst_phase + 2) % 3 (for shorter 340 clock scanline).
|
|
|
|
The included test ROMs verify proper burst_phase implementation. They
|
|
must pass in order; an earlier failing test means that later tests will
|
|
give meaningless results. The first two tests will pass with either
|
|
method of burst_phase handling described above; the third will only pass
|
|
with the more accurate handling. The tests flash sets of dots quickly
|
|
with the dot color being the only important aspect.
|
|
|
|
1.line_phase.nes - Tests for proper burst_phase on each scanline. All
|
|
dots on screen should be the same color.
|
|
|
|
2.frame_phase.nes - Tests for proper burst_phase toggling between
|
|
frames. Each row of dots should alternate between the same two colors
|
|
(if merge_fields is set to 1, they should all be the same color).
|
|
|
|
3.special_frame_phase.nes - Tests for proper burst_phase incrementing
|
|
between frames when $2001 rendering is enabled late in the frame. Each
|
|
rectangle of dots should be one color, and there should be three
|
|
different colors of rectangles (if merge_fields is set to 1, each
|
|
rectangle should be made of three colors of dots). There is a visual
|
|
glitch near the top of the screen for the first line of dots; this is
|
|
unrelated the test and should be ignored.
|
|
|
|
|
|
Flickering
|
|
----------
|
|
The displayed image toggles between two different pixel artifact
|
|
patterns at a steady rate, making it appear stable. For an emulator to
|
|
duplicate this effect, its frame rate must match the host monitor's
|
|
refresh rate, it must be synchronizing to the refresh (vsync), and it
|
|
must not be skipping any frames. If any of these don't hold, the image
|
|
will probably flicker much more than it would on a TV. It is important
|
|
that you play around with these factors to get a good feel for the
|
|
issue, and document it clearly for end-users, otherwise they will have
|
|
difficulty getting an authentic image.
|
|
|
|
The library includes a partial workaround for this issue, for the cases
|
|
where all the conditions can't be met. When merge_fields is set to 1,
|
|
nes_ntsc_blit() does the equivalent of blitting the image twice with the
|
|
two different phases and then mixes them together, but without any
|
|
performance impact. The result is similar to what you'd see if the
|
|
monitor's refresh rate were the same as the emulator's. It does reduce
|
|
the shimmer effect when scrolling, so it's not a complete solution to
|
|
the refresh rate issue.
|
|
|
|
The merge_fields option is also useful when taking a screenshot. If you
|
|
capture without merge_fields set to 1, you'll only get the even or odd
|
|
artifacts, which will make the image look more grainy than when the
|
|
emulator is running. Again, play around with this to get an idea of the
|
|
difference. It might be best to simply allow the user to choose when to
|
|
enable this option.
|
|
|
|
Note that when you have merge_fields set to 1, you should always pass 0
|
|
for the burst_phase parameter to nes_ntsc_blit() (unless doing partial
|
|
screen updates). If you don't, you'll still get some flicker.
|
|
|
|
|
|
Custom Blitter
|
|
--------------
|
|
You can write your own blitter, allowing customization of how input
|
|
pixels are obtained, the format of output pixels (15, 16, or 32-bit
|
|
RGB), optimizations for your platform, and additional effects like
|
|
efficient scanline doubling during blitting.
|
|
|
|
Macros are included in nes_ntsc.h for writing your blitter so that your
|
|
code can be carried over without changes to improved versions of the
|
|
library. The default blitter at the end of nes_ntsc.c shows how to use
|
|
the macros. Contact me for further assistance.
|
|
|
|
The NES_NTSC_BEGIN_ROW macro allows starting up to three pixels. The
|
|
first pixel is cut off; its use is in specifying a background color
|
|
other than black for the sliver on the left edge. The next two pixels
|
|
can be used to handle the extra one or two pixels not handled by the
|
|
main chunks of three pixels. For example if you want to blit 257 input
|
|
pixels on a row (for whatever odd reason), you would start the first two
|
|
with NES_NTSC_BEGIN_ROW( ... nes_ntsc_black, line_in [0], line_in [1] ),
|
|
then do the remaining 255 in chunks of three (255 is divisible by 3).
|
|
|
|
|
|
Limitations
|
|
-----------
|
|
The library's horizontal rescaling is too wide by about 3% in order to
|
|
allow a much more optimal implementation. This means that a 256 pixel
|
|
wide input image should appear as 581 output pixels, but with this
|
|
library appears as 602 output pixels. TV aspect ratios probably vary by
|
|
this much anyway. If you really need unscaled output, contact me and
|
|
I'll see about adding it.
|
|
|
|
|
|
Thanks
|
|
------
|
|
Thanks to NewRisingSun for his original code and explanations of NTSC,
|
|
which was a starting point for me learning about NTSC video and
|
|
decoding. Thanks to the Nesdev forum for feedback and encouragement.
|
|
Thanks to Martin Freij (Nestopia author) and Charles MacDonald (SMS Plus
|
|
author) for significant ongoing testing and feedback as the library has
|
|
improved. Thanks to byuu (bsnes author) and pagefault (ZSNES team) for
|
|
feedback about the SNES version.
|
|
|
|
--
|
|
Shay Green <gblargg@gmail.com>
|