gray: finalize the gray engine API

* Define dgray() to replace gray_start() and gray_stop()
* Introduce a mechanism to override the d*() functions rather than using
  another set of functions, namely g*(). Gray rendering should now be
  done with d*() (a compatibility macro for g*() is available until v2.1).
* Gray engine now reserves TMU0 at the start of the add-in to prevent
  surprises if timers are exhausted, so it nevers fails to start
* Replace other gray engine functions with dgray_*()
* More general rendering functions (in render/) to lessen the burden of
  porting them to the gray engine. As a consequence, dtext_opt(),
  dprint_opt() and drect_border() are now available in the gray engine,
  which was an omission from 230b796.
* Allow C_NONE in more functions, mainly on fx-CG 50
* Remove the now-unused dupdate_noint()
This commit is contained in:
Lephe 2020-07-13 13:49:07 +02:00
parent 411bbb9568
commit 94fb300e72
Signed by untrusted user: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
35 changed files with 557 additions and 481 deletions

3
TODO
View File

@ -1,14 +1,15 @@
For the 2.1.0 release:
* bopti: remove the deprecated image_t definition
* project: remove the compat branch
* project: remove the gray aliases
Issues:
* #10 support fx-CG 20
Extensions on existing code:
* build: move the private headers to the src/ folder
* tmu: make interrupt handlers more elegant
* bopti: try to display fullscreen images with TLB access + DMA on fxcg50
* gray: double-buffer gray settings and unify d* with g*
* topti: support unicode fonts
* dma: fx9860g support (need to switch it on and update the Makefile)
* core: try to leave add-in without reset in case of panic

View File

@ -8,7 +8,7 @@
#include <gint/defs/types.h>
#include <gint/display.h>
/* masks() - compute the vram masks for a given rectangle
/* masks(): Compute the vram masks for a given rectangle
Since the VRAM is line-based with four uin32_t elements per row, we can
execute any operation on a rectangle by running it on each set of four
@ -24,7 +24,7 @@
@masks Stores the result of the function (four uint32_t values) */
void masks(int x1, int x2, uint32_t *masks);
/* bopti_render_clip() - render a bopti image with clipping
/* bopti_render_clip(): Render a bopti image with clipping
@x @y Location of the top-left corner
@img Image encoded by [fxconv]
@left @top @w @h Bounding box to render
@ -33,7 +33,7 @@ void masks(int x1, int x2, uint32_t *masks);
void bopti_render_clip(int x, int y, bopti_image_t const *img, int left,
int top, int w, int h, uint32_t *v1, uint32_t *v2, void *bopti_asm);
/* bopti_render_noclip() - render a bopti image without clipping
/* bopti_render_noclip(): Render a bopti image without clipping
This function is only ever slightly faster than bopti_render_clip(),
eliminating two types of coordinate checks:
1. The bounding box does not overflow from the image
@ -47,4 +47,59 @@ void bopti_render_clip(int x, int y, bopti_image_t const *img, int left,
void bopti_render_noclip(int x, int y, bopti_image_t const *img, int left,
int top, int w, int h, uint32_t *v1, uint32_t *v2, void *bopti_asm);
//---
// Alternate rendering modes
//---
/* The gray engine overrides the rendering functions by specifying a set of
alternate primitives that are suited to work with two VRAMs. To avoid
linking with them when the gray engine is not used, the display module
exposes a global state in the form of a struct rendering_mode and the gray
engine modifies that state when it runs. */
struct rendering_mode
{
/* Because the gray engine still has business to do after the call to
dgray(DGRAY_OFF), the original dupdate() is made to execute after
the replacement one if the replacement one returns 1. */
int (*dupdate)(void);
/* Area rendering */
void (*dclear)(color_t color);
void (*drect)(int x1, int y1, int x2, int y2, color_t color);
/* Point rendering */
void (*dpixel)(int x, int y, color_t color);
void (*gint_dhline)(int x1, int x2, int y, int color);
void (*gint_dvline)(int y1, int y2, int x, int color);
/* Text and image rendering */
void (*dtext_opt)
(int x, int y, int fg, int bg, int halign, int valign,
char const *str);
void (*dsubimage)
(int x, int y, bopti_image_t const *image, int left, int top,
int width, int height, int flags);
};
/* The alternate rendering mode pointer (initially NULL)*/
extern struct rendering_mode const *dmode;
/* These are the corresponding gray rendering functions */
int gupdate(void);
void gclear(color_t color);
void grect(int x1, int y1, int x2, int y2, color_t color);
void gpixel(int x, int y, color_t color);
void gint_ghline(int x1, int x2, int y, int color);
void gint_gvline(int y1, int y2, int x, int color);
void gtext_opt
(int x, int y, int fg, int bg, int halign, int valign,
char const *str);
void gsubimage
(int x, int y, bopti_image_t const *image, int left, int top,
int width, int height, int flags);
/* Short macro to call the alternate rendering function when available */
#define DMODE_OVERRIDE(func, ...) \
if(dmode && dmode->func) { \
dmode->func(__VA_ARGS__); \
return; \
}
#endif /* DISPLAY_FX */

View File

@ -29,12 +29,33 @@
//---
/* dupdate(): Push the video RAM to the display driver
This function makes the contents of the VRAM visible on the screen. It is
the direct equivalent of Bdisp_PutDisp_DD().
On fxcg50, if triple buffering is enabled (which is the default and disabled
only by calling dvram()), it also swaps buffers. Due to the DMA being used,
always waits for the previous call to dupdate() call() to finish. */
This function makes the contents of the VRAM visible on the screen. It is
the equivalent of Bdisp_PutDisp_DD() in most situations.
On fx-9860G, this function also manages the gray engine settings. When the
gray engine is stopped, it pushes the contents of the VRAM to screen, and
when it is on, it swaps buffer and lets the engine's timer push the VRAMs to
screen when suitable. To make the transition between the two modes smooth,
dgray() does not enable the gray engine immediately; instead the first call
to update() after dgray() switches the gray engine on and off.
On fx-CG 50, because rendering is slow and sending data to screen is also
slow, a special mechanism known as triple buffering is implemented. Two
VRAMs are allocated, and frames are alternately rendered on each VRAM. When
dupdate() is called to push one of the VRAMs to screen, it starts the DMA
(which performs the push in the background) and immediately returns after
switching to the other VRAM so that the user can draw during the push.
The transfer from the DMA to the screen lasts about 10 ms if memory is
available every cycle. Each access to the memory delays the transfer by a
bit, so to fully use the available time, try to run AIs, physics or other
calculation-intensive code rather than using the VRAM.
Typical running time without overclock:
fx-9860G: 1 ms (gray engine off)
fx-9860G: ~30% of CPU time (gray engine on)
fx-CG 50: 11 ms */
void dupdate(void);
//---
@ -42,88 +63,108 @@ void dupdate(void);
//---
/* dclear(): Fill the screen with a single color
This function clears the screen by painting all the pixels in a single
color. It is optimized for opaque colors.
color. It is optimized for opaque colors; on fx-CG 50, it uses dma_memset()
to fill in the VRAM.
On fx9860g, use drect() if you need complex operators such as invert.
Typical running time without overclock:
fx-9860G SH3: 70 µs
fx-9860G SH4: 15 µs
fx-CG 50: 2.5 ms (full-screen drect() is about 6 ms)
@color fx9860g: white, black
fxcg50: Any R5G6B5 color */
On fx9860g, use drect() if you need operators that modify existing pixels
instead of replacing them such as invert.
@color fx-9860G: white light* dark* black
fx-CG 50: Any R5G6B5 color
*: When the gray engine is on, see dgray(). */
void dclear(color_t color);
/* drect(): Fill a rectangle of the screen
This functions applies a color or an operator to a rectangle defined by two
points (x1 y1) and (x2 y2). Both are included in the rectangle.
@x1 @y1 @x2 @y2 Bounding rectangle (drawn area).
@color fx9860g: white, black, none, invert
fxcg50: Any R5G6B5 color */
void drect(int x1, int y1, int x2, int y2, color_t color);
@x1 @y1 Top-left rectangle corner (included)
@x2 @y2 Bottom-right rectangle corner (included)
@color fx-9860G: white light* dark* black none invert lighten* darken*
fx-CG 50: Any R5G6B5 color or C_NONE
*: When the gray engine is on, see dgray(). */
void drect(int x1, int y1, int x2, int y2, int color);
/* drect_border(): Rectangle with border
This function draws a rectangle with an inner border. The border width must
be smaller than half the width and half the height. */
void drect_border(int x1, int y1, int x2, int y2, int fill_color,
int border_width, int border_color);
be smaller than half the width and half the height.
@x1 @y1 @x2 @y2 Top-left and bottom-right rectangle corners (included)
@fill_color Center color (same values as drect() are allowed)
@border_width Amount of pixels reserved for the border on each side
@border_color Border color (same values as drect() are allowed) */
void drect_border(int x1, int y1, int x2, int y2,
int fill_color, int border_width, int border_color);
//---
// Point drawing functions
//---
/* dpixel(): Change a pixel's color
Paints the selected pixel with an opaque color. Setting pixels individually
is a slow method for rendering. Other functions that draw lines, rectangles,
images or text will take advantage of possible optimizations to make the
rendering faster: check them out first.
On fx9860g, if an operator such as invert is used, the result will depend
Paints the selected pixel with an opaque color or applies an operator to a
pixel. Setting pixels individually is a slow method for rendering. Other
functions that draw lines, rectangles, images or text will take advantage of
possible optimizations to make the rendering faster, so prefer using them
when they apply.
On fx-9860G, if an operator such as invert is used, the result will depend
on the current color of the pixel.
@x @y Coordinates of the pixel to repaint
@color fx9860g: white, black, none, invert
fxcg50: Any R5G6B5 color */
void dpixel(int x, int y, color_t color);
@color fx-9860G: white light* dark* black none invert lighten* darken*
fx-CG 50: Any R5G6B5 color or C_NONE
*: When the gray engine is on, see dgray(). */
void dpixel(int x, int y, int color);
/* dline(): Render a straight line
This function draws a line using a Bresenham-style algorithm. Please note
that dline() may not render lines exactly like Bdisp_DrawLineVRAM().
dline() has optimization facilities for horizontal and vertical lines. The
speedup for horizontal lines is about x2 on fxcg50 and probably about x30
on fx9860g. Vertical lines have a smaller speedup.
dline() has optimizations for horizontal and vertical lines. The speedup for
horizontal lines is about x2 on fx-CG 50 and probably about x30 on fx-9860G.
Vertical lines have a smaller speedup.
dline() is currently not able to clip arbitrary lines without calculating
all the pixels, so drawing a line from (-1e6,0) to (1e6,395) will work but
will be veeery slow.
will be very slow.
@x1 @y1 @x2 @y2 End points of the line (both included).
@color fx9860g: white, black, none, invert
fxcg50: Any R5G6B5 color */
void dline(int x1, int y1, int x2, int y2, color_t color);
@x1 @y1 @x2 @y2 Endpoints of the line (both included).
@color Line color (same values as dpixel() are allowed) */
void dline(int x1, int y1, int x2, int y2, int color);
/* dhline(): Full-width horizontal line
This function draws a horizontal line from the left end of the screen to the
right end, much like the Basic command "Horizontal".
@y Line number
@color fx9860g: white, black, none, invert
fxcg50: Any R5G6B5 color */
void dhline(int y, color_t color);
@color Line color (same values as dline() are allowed) */
void dhline(int y, int color);
/* dvline(): Full-height vertical line
This function draws a vertical line from the top end of the screen to the
bottom end, much like the Basic command "Vertical".
@x Column number
@color fx9860g: white, black, none, invert
fxcg50: Any R5G6B5 color */
void dvline(int x, color_t color);
@color Line color (same values as dline() are allowed) */
void dvline(int x, int color);
//---
// Text rendering (topti)
//---
/* font_t - font data encoded for topti */
/* font_t: Font data encoded for topti */
typedef struct
{
/* Length of font name (not necessarily NUL-terminated!) */
@ -172,17 +213,28 @@ typedef struct
} GPACKED(4) font_t;
/* dfont(): Set the default font for text rendering
This font will be used by dtext() and sister functions. If [font = NULL],
gint's default font is used.
On fx9860g, the default font is a 5x7 font very close to the system's.
On fxcg50, the default font is an original 8x9 font.
This function changes the default font for text rendering; this affects
dtext_opt(), dtext() and the related functions. If the specified font is
NULL, gint's default font is used instead.
On fx-9860G, the default font is a 5x7 font very close to the system's.
On fx-CG 50, the default font is an original, proportional 8x9 font.
This function returns the previously configured font. Normally you want to
restore it after you're done so as to not affect ambiant calls to text
rendering functions. It would look like this:
font_t const *old_font = dfont(new_font);
// Do the text rendering...
dfont(old_font);
@font Font to use for subsequent text rendering calls
Returns the previously configured font. */
font_t const *dfont(font_t const * font);
font_t const *dfont(font_t const *font);
/* dsize(): Get the width and height of rendered text
This function computes the size that the given string would take up if
rendered with a certain font. If you specify a NULL font, the currently
configured font will be used; this is different from dfont(), which uses
@ -191,15 +243,15 @@ font_t const *dfont(font_t const * font);
Note that the height of each glyph is not stored in the font, only the
maximum. Usually this is what you want because vertically-centered strings
must have the same baseline regardless of their contents. So the height
returned by dsize() is the same for all strings, only depends on the font.
set by dsize() is the same for all strings and only depends on the font.
The height is computed in constant time, and the width in linear time. If
[w = NULL], this function returns in constant time.
the third argument is NULL, this function returns in constant time.
@str String whose size must be evaluated
@font Font to use; if NULL, defaults to the current font
@w @h Set to the width and height of the rendered text, may be NULL */
void dsize(char const *str, font_t const * font, int *w, int *h);
void dsize(char const *str, font_t const *font, int *w, int *h);
/* Alignment settings for dtext_opt() and dprint_opt(). Combining a vertical
and a horizontal alignment option specifies where a given point (x,y) should
@ -221,61 +273,66 @@ enum {
default if no such font was set). This function has a lot of parameters,
see dtext() for a simpler version.
On fx9860g, due to the particular design of topti, this function performs
drastic rendering optimizations using the line structure of the VRAM and is
able to render several characters at once.
The alignment options specify where (x y) should be relative to the rendered
string. The default is halign=DTEXT_LEFT and valign=DTEXT_TOP, which means
that (x y) is the top-left corder of the rendered string. The different
combinations of settings provide 9 positions of (x y) to choose from.
This is not a printf()-family function so [str] cannot contain formats like
On fx-9860G, due to the particular design of topti, this function performs
drastic rendering optimizations using the line structure of the VRAM and
renders 5 or 6 characters simultaneously.
This is not a printf()-family function so str cannot contain formats like
"%d" and you cannot pass additional arguments. See dprint_opt() and dprint()
for that.
@x @y Coordinates of top-left corner of the rendered string
@fg Text color
fx9860g: white, black, none, invert
fxcg50: Any R5G6B6 color, or C_NONE
@bg Background color
fx9860g: white, black, none, invert
fxcg50: Any R5G6B5 color, or C_NONE
@halign Where x should be relative to the rendered string (see above enum)
@valign Where y should be relative to the rendered string (see above enum)
@x @y Coordinates of the anchor of the rendered string
@fg @bg Text color and background color
fx-9860G: white light* dark* black none invert lighten* darken*
fx-CG 50: Any R5G6B6 color, or C_NONE
@halign Where x should be relative to the rendered string
@valign Where y should be relative to the rendered string
@str String to display */
void dtext_opt(int x, int y, int fg, int bg, int halign, int valign,
char const *str);
/* dtext(): Simple version of dtext_opt() with defaults
This is exactly dtext_opt() with bg=C_NONE, halign=DTEXT_LEFT and
valign=DTEXT_TOP. */
Calls dtext_opt() with bg=C_NONE, halign=DTEXT_LEFT and valign=DTEXT_TOP. */
void dtext(int x, int y, int fg, char const *str);
/* dprint_opt(): Display a formatted string
Much like dtext_opt(), but accepts printf-like formats with arguments. See
<gint/std/stdio.h> for a detailed view of what this format supports. */
This function is exactly like dtext_opt(), but accepts printf-like formats
with arguments. See <gint/std/stdio.h> for a detailed view of what this
format supports. For example:
dprint_opt(x, y, fg, bg, halign, valign, "A=%d B=%d", A, B); */
void dprint_opt(int x, int y, int fg, int bg, int halign, int valign,
char const *format, ...);
/* dprint(): Simple version of dprint_op() with defaults
Like dtext() with formatted printing. */
Calls dprint_opt() with bg=C_NONE, halign=DTEXT_LEFT and valign=DTEXT_TOP */
void dprint(int x, int y, int fg, char const *format, ...);
//---
// Image rendering (bopti)
//---
/* The bopti_image_t structure is platform-dependent. */
/* The bopti_image_t structure is platform-dependent, see <gint/display-fx.h>
and <gint/display-cg.h> if you're curious. */
/* dimage(): Render a full image
This function blits an image on the VRAM using gint's special format. It is
a special case of dsubimage() where the full image is drawn with clipping.
@x @y Coordinates of the top-left corner of the image
@image Pointer to image encoded with [fxconv] */
@image Pointer to image encoded with fxconv for bopti */
void dimage(int x, int y, bopti_image_t const *image);
/* Option values for dsubimage() */
enum {
/* No option */
DIMAGE_NONE = 0x00,
/* Disable clipping, ie. adjustments to the specified subrectangle and
screen location such that any part that overflows from the image or
the screen is ignored. Slightly faster. */
@ -287,23 +344,11 @@ enum {
the VRAM. It is more general than dimage() and also provides a few options.
@x @y Coordinates on screen of the rendered subrectangle
@image Pointer to image encoded with [fxconv]
@left @top Top-left coordinates of the subrectangle within [image]
@image Pointer to image encoded with fxconv for bopti
@left @top Top-left coordinates of the subrectangle within the image
@width @height Subrectangle dimensions
@flags OR-combination of DIMAGE_* flags */
void dsubimage(int x, int y, bopti_image_t const *image, int left, int top,
int width, int height, int flags);
//---
// Advanced functions
//---
/* dupdate_noint(): Push VRAM to the display without interrupts
This function does exactly as dupdate(), but does not use interrupts and
always returns when the transfer is finished. It actively waits for the end
of the transfer and is thus bad for any general-purpose use. In fact, it is
only needed to display panic messages in exception handlers. Don't use it
unless you know precisely why you're doing it. */
void dupdate_noint(void);
#endif /* GINT_DISPLAY */

View File

@ -1,5 +1,5 @@
//---
// gint:gray - Gray engine and rendering functions
// gint:gray - Gray engine control
//---
#ifndef GINT_GRAY
@ -8,34 +8,71 @@
#include <gint/defs/types.h>
#include <gint/display.h>
//---
// Engine control
//---
/* Commands for the gray engine */
enum {
/* Start or stop the engine */
DGRAY_ON,
DGRAY_OFF,
/* Start or stop the engine, but remember previous state */
DGRAY_PUSH_ON,
DGRAY_PUSH_OFF,
/* Restore previous state remembered by DGRAY_PUSH_* */
DGRAY_POP,
};
/* gray_start(): Start the gray engine
/* dgray(): Start or stop the gray engine at the next dupdate()
The control of the screen is transferred to the engine; you should not use
dupdate() after this function, only gupdate().
This function configures the display module to work with or without the gray
engine. When the mode is set to DGRAY_ON, all rendering functions are
replaced to use gray rendering, and gray frames will start being displayed
after the next dupdate(). To transition from monochrome rendering to gray
rendering, you must first call dgray(DGRAY_ON), then draw your first gray
frame from scratch, then call dupdate().
The engine uses timer number GRAY_TIMER (equal to 0 by default) to run. If
the timer is unavailable, this function will fail. Otherwise, the gray
engine is started and any attempt to use timer 0 while it is running will be
denied by the timer module. */
void gray_start(void);
Similarly, when the mode is DGRAY_OFF, all rendering functions are replaced
to use monochrome rendering, and the next frame sent with dupdate() will
stop the gray engine on the display. To transition back to monochrome
rendering, call dgray(DGRAY_OFF), draw your first monochrome frame from
scratch, then call dupdate().
/* gray_stop(): Stop the gray engine
The gray engine uses the timer id GRAY_TIMER (which is 0) to run and needs 3
additional VRAMs (totaling 4), obtained on the heap by default. Timer 0 is
chosen for its precision and high priority. If GRAY_TIMER is unavailable or
VRAMs cannot be obtained with malloc(), the gray engine will fail to start
and this function will return a non-zero error code.
Safe to call if the engine was not running. The gray engine returns the
control of the screen and dupdate() is safe to use again.
When functions that use the gray engine call functions that don't and vice-
versa, it can be tedious to know exacly when the gray engine should be
running. This function provides a stack of gray engine states to solve this
problem. When entering a function that uses/doesn't use the gray engine, you
can call dgray(DGRAY_PUSH_ON)/dgray(DGRAY_PUSH_OFF) to start/stop the
engine; then call dgray(DGRAY_POP) at the end of the function.
dgray(DGRAY_POP) will restore the state of the engine as it was before the
previous DGRAY_PUSH_*. This allows each function to set its own state
without altering the state of their callers.
This function will leave the screen in whatever state it is, which is most
probably one of the gray buffers being displayed. You should dupdate()
quickly after this call to avoid visual artifacts. If the next monochrome
frame is slow to render, consider rendering it before stopping the gray
engine, and calling dupdate() immediately after. */
void gray_stop(void);
This function returns non-zero if the engine has failed to initialize at
startup (because it failed to obtain either GRAY_TIMER or some of its VRAM
memory). Because changes to the gray engine only apply after a call to
dupdate(), you can check if the engine is successfully initialized with:
/* gray_delays(): Set the gray engine delays
int engine_successfully_initialized = dgray(DGRAY_ON);
dgray(DGRAY_OFF);
@mode DGRAY_ON or DGRAY_PUSH_ON to turn the engine on;
DGRAY_OFF or DGRAY_PUSH_OFF to turn the engine off;
DGRAY_POP to restore the state as before the last DGRAY_PUSH_*.
Returns 0 on success and non-zero if the engine has failed to initialize. */
int dgray(int mode);
/* dgray_enabled(): Check whether gray mode is enabled
Returns non-zero if gray mode is enabled, that is if the gray engine is
planning to run at the next call to dupdate(). (This is true between the
calls to dgray(DGRAY_ON) and dgray(DGRAY_OFF).) */
int dgray_enabled(void);
/* dgray_setdelays(): Set the gray engine delays
The gray engine works by swapping two images at a fast pace. Pixels that are
white on both or black or both will appear as such, but pixels that are
@ -59,164 +96,83 @@ void gray_stop(void);
and speed; but overall they're the *one* thing you want to avoid.
* And the color of the shades themselves.
Here are values from an older version of gint, which may not look good now
but provide bases to search for good settings:
Here are values from the old fx-9860G-like family, which is every monochrome
calculator but the Graph 35+E II:
LIGHT DARK BLINKING STRIPES COLORS
--------------------------------------------------
860 1298 none terrible decent
912 1343 heavy none good
993 1609 medium light decent
1325 1607 heavy light excellent
898 1350 none common good
1075 1444 low common too close
609 884 medium terrible variable
937 1333 low common decent
923 1742 none scanline good [default]
--------------------------------------------------
Here are values for this version of gint (for the Graph 35+E II only):
Here are values for the Graph 35+E II only:
LIGHT DARK BLINKING STRIPES COLORS
--------------------------------------------------
680 1078
762 1311 low some too light [default]
869 1097 medium some excellent
680 1078 low common good
762 1311 low some good [default]
869 1097 medium some too close
869 1311 medium none good
937 1425 medium none good
937 1425 none bad good
--------------------------------------------------
This function sets the delays of the gray engine. It is safe to call while
the engine is running, although for best visual effects it is better to call
it prior to dgray(GRAY_ON).
@light New light delay
@dark New dark delay */
void gray_delays(uint32_t light, uint32_t dark);
void dgray_setdelays(uint32_t light, uint32_t dark);
/* gray_config(): Get the current configuration of the engine
/* dgray_getdelays(): Get the gray engine delays
Provides the value of the current light and dark delays, measured in timer
ticks of prescaler P_phi/64. See <gint/clock.h> on how to obtain this value.
Both pointers may be NULL.
@light Set to the current light delay setting
@dark Set to the current dark delay setting
Returns non-zero if the engine is currently running, 0 otherwise. */
int gray_config(uint32_t *light, uint32_t *dark);
//---
// Area rendering functions
//---
/* gclear(): Fill the screen with a single color
Clears the VRAM and paints all the pixels in the same color. Optimized for
opaque colors; use grect() for other colors.
@color white, light, dark, black */
void gclear(color_t color);
/* grect(): Fill a rectangle on the screen
Applies a color or an operator to the rectangle enclosed by (x1 y1) and
(x2 y2), both included.
@x1 @x2 @y1 @y2 Bounding rectangle
@color white, light, dark, black, none, invert, lighten, darken */
void grect(int x1, int y1, int x2, int y2, color_t color);
//---
// Point drawing functions
//---
/* gpixel(): Change a pixel's color
Paints the specified pixel. Use lines or area rendering when possible
because painting pixels individually is slow.
@x @y Coordinates of the pixel to paint
@color white, light, dark, black, none, invert, lighten, darken */
void gpixel(int x, int y, color_t color);
/* gline(): Render a straight line
Draws a line without anti-aliasing, using a Bresenham-style algorithm. Much
like dline(), has optimizations for vertical and horizontal lines but is not
able to handle clipping.
@x1 @y1 @x2 @y2 End points of the line (both included)
@color white, light, dark, black, none, invert, lighten, darken */
void gline(int x1, int y1, int x2, int y2, color_t color);
/* ghline(): Full-width horizontal line
@y Line number
@color white, light, dark, black, none, invert, lighten, darken */
void ghline(int y, color_t color);
/* gvline(): Full-height vertical line
@x Column number
@color white, light, dark, black, none, invert, lighten, darken */
void gvline(int x, color_t color);
//---
// Text rendering
//---
/* dfont() and dsize() still work with the gray engine. */
/* gtext(): Display a string of text
Exactly like dtext(), except that the rendering is done on the two gray
VRAMs and all colors are supported.
@x @y Coordinates of top-left corner of the rendered string
@str String to display
@fg Foreground: white, light, dark, black, none, invert, lighten, darken
@bg Background, same colors as fg */
void gtext(int x, int y, const char *str, int fg, int bg);
//---
// Image rendering
//---
/* gimage(): Render a full image
This function is exactly like dimage(), but it draws gray image instead.
@x @y Coordinates of the top-left corner of the image
@image Pointer to gray image encoded with [fxconv] */
void gimage(int x, int y, bopti_image_t const *image);
/* gsubimage(): Render a section of an image
Like dsubimage() for gray images. Same options apply.
@x @y Coordinates on screen of the rendered subrectangle
@image Pointer to image encoded with [fxconv]
@left @top Top-left coordinates of the subrectangle within [image]
@width @height Subrectangle dimensions
@flags OR-combination of DIMAGE_* flags */
void gsubimage(int x, int y, bopti_image_t const *image, int left, int top,
int width, int height, int flags);
@dark Set to the current dark delay setting */
void dgray_getdelays(uint32_t *light, uint32_t *dark);
//---
// VRAM management
//---
/* gupdate(): Push the current VRAMs to the screen
/* dgray_getvram(): Get the current VRAM pointers
These pointers can be used for custom rendering functions. */
void dgray_getvram(uint32_t **light, uint32_t **dark);
This function swaps pairs of VRAM buffers in the gray engine so that the
gray and light VRAMs that were being drawn to are now available to be
displayed on the screen at the next refresh, while the VRAMs that were
previously being displayed are now available for drawing.
//---
// Aliases for older programs
//---
Unlike dupdate(), gupdate() does not interact with the screen as this
interaction is done very frequently by the engine itself. It performs an
atomic operation to swap buffers, but nothing on the screen will change
until the engine's timer actually fires. */
void gupdate(void);
/* Functions for the gray engine used to be called g*() to mirror the d*()
functions of the display module. They are now bundled together, so these
aliases are for older programs that still use the g*() naming scheme */
#ifdef GINT_GRAY_ALIASES
/* gvram(): Get the current VRAM pointers
#define gclear dclear
#define grect drect
#define gpixel dpixel
#define gline dline
#define ghline dhline
#define gvline dvline
#define gimage dimage
#define gsubimage dsubimage
#define gtext dtext
#define gtext_opt dtext_opt
#define gprint dprint
#define gprint_opt dprint_opt
This function stores the current VRAM pointers to the specified locations.
This operation needs to be done atomically because the engine might kick in
and sway buffers at any time; calling gvraml() then gvramd() is unsafe.
#define gupdate dupdate
#define gray_delays dgray_setdelays
#define gray_config dgray_getdelays
#define gvram dgray_getvram
@light Set to the current light VRAM pointer
@dark Set to the current dark VRAM pointer */
void gvram(uint32_t **light, uint32_t **dark);
/* gvraml(): Shorthand to retrieve the current light VRAM pointer */
uint32_t *gvraml(void);
/* gvramd(): Shorthand to retrieve the current dark VRAM pointer */
uint32_t *gvramd(void);
#endif /* GINT_GRAY_ALIASES */
#endif /* GINT_GRAY */

View File

@ -10,6 +10,8 @@
#include <gint/display.h>
#include <gint/timer.h>
#include <display/fx.h>
/* Three additional video RAMS, allocated statically if --static-gray was set
at configure time, or with malloc() otherwise. */
#ifdef GINT_STATIC_GRAY
@ -17,27 +19,63 @@ GBSS static uint32_t gvrams[3][256];
#endif
/* Four VRAMs: two to draw and two to display */
GBSS static uint32_t *vrams[4];
static uint32_t *vrams[4] = { NULL, NULL, NULL, NULL };
/* Current VRAM pair used for drawing; the value can either be 0 (draws to
VRAMs 0 and 1) or 2 (draws to VRAMs 2 and 3). */
static volatile int st = 0;
/* Whether the engine is running. Delays of light and dark frames. */
static int volatile st = 0;
/* Timer ID, always equal to GRAY_TIMER except if initialization fails */
static int timer = -1;
/* Whether the engine is scheduled to run at the next frame */
static int runs = 0;
/* Underlying timer, set to count at P_phi/64 */
#define GRAY_TIMER 0
#define GRAY_CLOCK TIMER_Pphi_64
/* Delays of the light and dark frames for the above setting */
GBSS static int delays[2];
/* Underlying timer */
#define GRAY_TIMER 0
/* The alternate rendering mode structure used to override d*() */
static struct rendering_mode const gray_mode = {
.dupdate = gupdate,
.dclear = gclear,
.drect = grect,
.dpixel = gpixel,
.gint_dhline = gint_ghline,
.gint_dvline = gint_gvline,
.dtext_opt = gtext_opt,
.dsubimage = gsubimage,
};
static struct rendering_mode const gray_exit_mode = {
.dupdate = gupdate,
.dclear = NULL,
.drect = NULL,
.dpixel = NULL,
.gint_dhline = NULL,
.gint_dvline = NULL,
.dtext_opt = NULL,
.dsubimage = NULL,
};
//---
// Engine control
// Engine control (init/quit and start/stop)
//---
/* gray_init(): Engine setup */
static int gray_int(void);
static void gray_quit(void);
/* gray_isinit(): Check whether the engine is initialized and ready to run */
static int gray_isinit(void)
{
return (vrams[0] && vrams[1] && vrams[2] && vrams[3] && timer >= 0);
}
/* gray_init(): Initialize the engine
This is done at startup so that memory can be reserved very early from the
heap (because not having enough memory is unrecoverable for the engine). */
GCONSTRUCTOR static void gray_init(void)
{
/* Here [gint_vram] refers to the standard, monochrome VRAM */
/* We need four VRAMs. First use the standard monochrome one */
vrams[0] = gint_vram;
#ifdef GINT_STATIC_GRAY
@ -61,16 +99,74 @@ GCONSTRUCTOR static void gray_init(void)
delays[0] = 923;
delays[1] = 1742;
}
/* Try to obtain the timer right away */
timer = timer_setup(GRAY_TIMER | GRAY_CLOCK, 1000, gray_int);
/* On failure, release the resources that we obtained */
if(!gray_isinit()) gray_quit();
}
/* gray_quit(): Engine deinitialization() */
/* gray_quit(): Free engine resources */
GDESTRUCTOR static void gray_quit(void)
{
#ifndef GINT_STATIC_GRAY
free(vrams[1]);
free(vrams[2]);
free(vrams[3]);
if(vrams[1]) free(vrams[1]);
if(vrams[2]) free(vrams[2]);
if(vrams[3]) free(vrams[3]);
vrams[1] = NULL;
vrams[2] = NULL;
vrams[3] = NULL;
#endif /* GINT_STATIC_GRAY */
if(timer >= 0) timer_stop(timer);
timer = -1;
}
/* gray_start(): Start the gray engine */
static void gray_start(void)
{
st = 2;
timer_reload(GRAY_TIMER, delays[0]);
timer_start(GRAY_TIMER);
runs = 1;
}
/* gray_stop(): Stop the gray engine */
static void gray_stop(void)
{
timer_pause(GRAY_TIMER);
runs = 0;
st = 0;
}
//---
// Dynamic udpate and rendering mode
//---
/* dgray(): Start or stop the gray engine at the next dupdate() */
int dgray(int mode)
{
if(mode == DGRAY_ON)
{
if(!gray_isinit()) return 1;
/* Set the display module's alternate rendering mode to
override rendering functions to use their g*() variant */
dmode = &gray_mode;
return 0;
}
else if(mode == DGRAY_OFF)
{
/* Set the mode to a temporary one that only overrides
dupdate() so that we can stop the engine next frame */
if(dmode == &gray_mode) dmode = &gray_exit_mode;
return 0;
}
/* TODO: DGRAY_PUSH_* and DGRAY_POP */
return 2;
}
/* gray_int(): Interrupt handler */
@ -83,69 +179,57 @@ int gray_int(void)
return TIMER_CONTINUE;
}
/* gray_start(): Start the gray engine */
void gray_start(void)
/* gupdate(): Push the current VRAMs to the screen */
int gupdate(void)
{
#ifndef GINT_STATIC_GRAY
if(!vrams[1] || !vrams[2] || !vrams[3]) return;
#endif
/* At the first gupdate(), start the engine */
if(dmode == &gray_mode && !runs)
{
gray_start();
return 0;
}
/* At the last gupdate(), stop the engine */
if(dmode == &gray_exit_mode)
{
gray_stop();
dmode = NULL;
return 1;
}
if(runs) return;
int timer = timer_setup(GRAY_TIMER|TIMER_Pphi_64, delays[0], gray_int);
if(timer != GRAY_TIMER) return;
timer_start(GRAY_TIMER);
st = 0;
runs = 1;
/* When the engine is running, swap frames */
st ^= 2;
return 0;
}
/* gray_stop(): Stop the gray engine */
void gray_stop(void)
//---
// Query and configuration functions
//---
/* dgray_enabled(): Check whether gray mode is enabled */
int dgray_enabled(void)
{
timer_stop(GRAY_TIMER);
runs = 0;
return (dmode == &gray_mode);
}
/* gray_delays(): Set the gray engine delays */
void gray_delays(uint32_t light, uint32_t dark)
/* dgray_setdelays(): Set the gray engine delays */
void dgray_setdelays(uint32_t light, uint32_t dark)
{
delays[0] = light;
delays[1] = dark;
}
/* gray_config(): Get the current configuration of the engine */
int gray_config(uint32_t *light, uint32_t *dark)
/* dgray_getdelays(): Get the gray engine delays */
void dgray_getdelays(uint32_t *light, uint32_t *dark)
{
if(light) *light = delays[0];
if(dark) *dark = delays[1];
return runs;
}
/* gupdate(): Push the current VRAMs to the screen */
void gupdate(void)
{
st ^= 2;
}
/* gvram(): Get the current VRAM pointers */
void gvram(uint32_t **light, uint32_t **dark)
/* dgray_getvram(): Get the current VRAM pointers */
void dgray_getvram(uint32_t **light, uint32_t **dark)
{
int base = st;
if(light) *light = vrams[base & 2];
if(dark) *dark = vrams[base | 1];
}
/* gvraml(): Shorthand to retrieve the current light VRAM pointer */
uint32_t *gvraml(void)
{
return vrams[st & 2];
}
/* gvramd(): Shorthand to retrieve the current dark VRAM pointer */
uint32_t *gvramd(void)
{
return vrams[st | 1];
}

View File

@ -4,7 +4,7 @@
void gclear(color_t color)
{
uint32_t *light, *dark;
gvram(&light, &dark);
dgray_getvram(&light, &dark);
uint32_t l = -(color == C_LIGHT || color == C_BLACK);
uint32_t d = -(color == C_DARK || color == C_BLACK);

View File

@ -1,7 +0,0 @@
#include <gint/gray.h>
/* ghline(): Full-width horizontal line */
void ghline(int y, color_t color)
{
gline(0, y, 127, y, color);
}

16
src/gray/gint_gline.c Normal file
View File

@ -0,0 +1,16 @@
#include <gint/display.h>
#include <gint/defs/util.h>
/* gint_ghline(): Optimized horizontal line, but not actually optimized */
void gint_ghline(int x1, int x2, int y, int color)
{
if(x1 > x2) swap(x1, x2);
for(int x = x1; x <= x2; x++) dpixel(x, y, color);
}
/* gint_gvline(): Optimized horizontal line, but not actually optimized */
void gint_gvline(int y1, int y2, int x, int color)
{
if(y1 > y2) swap(y1, y2);
for(int y = y1; y <= y2; y++) dpixel(x, y, color);
}

View File

@ -1,55 +0,0 @@
#include <gint/gray.h>
#include <gint/defs/util.h>
#include <display/common.h>
/* gline(): Bresenham line drawing algorithm
Remotely adapted from MonochromeLib code by Pierre "PerriotLL" Le Gall.
Relies on grect() for optimized situations.
@x1 @y1 @x2 @y2 Coordinates of endpoints of line (included)
@color Any color */
void gline(int x1, int y1, int x2, int y2, color_t color)
{
/* Trivial optimizations */
if(x1 == x2 || y1 == y2)
{
grect(x1, y1, x2, y2, color);
return;
}
/* Brensenham line drawing algorithm */
int i, x = x1, y = y1, cumul;
int dx = x2 - x1, dy = y2 - y1;
int sx = sgn(dx), sy = sgn(dy);
dx = abs(dx), dy = abs(dy);
gpixel(x1, y1, color);
if(dx >= dy)
{
/* Start with a non-zero cumul to even the overdue between the
two ends of the line (for more regularity) */
cumul = dx >> 1;
for(i = 1; i < dx; i++)
{
x += sx;
cumul += dy;
if(cumul > dx) cumul -= dx, y += sy;
gpixel(x, y, color);
}
}
else
{
cumul = dy >> 1;
for(i = 1; i < dy; i++)
{
y += sy;
cumul += dx;
if(cumul > dy) cumul -= dy, x += sx;
gpixel(x, y, color);
}
}
gpixel(x2, y2, color);
}

View File

@ -4,10 +4,8 @@
/* gpixel(): Change a pixel's color */
void gpixel(int x, int y, color_t color)
{
if((uint)x >= 128 || (uint)y >= 64) return;
uint32_t *light, *dark;
gvram(&light, &dark);
dgray_getvram(&light, &dark);
int offset = (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31), tmp;

View File

@ -5,22 +5,12 @@
/* grect(): Fill a rectangle on the screen */
void grect(int x1, int y1, int x2, int y2, color_t color)
{
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
/* Argument checking */
if(x1 >= 128 || x2 < 0 || y1 >= 64 || y2 < 0) return;
if(x1 < 0) x1 = 0;
if(x2 >= 128) x2 = 127;
if(y1 < 0) y1 = 0;
if(y2 >= 64) y2 = 63;
/* Use masks to get the work done fast! */
uint32_t m[4];
masks(x1, x2, m);
uint32_t *light, *dark;
gvram(&light, &dark);
dgray_getvram(&light, &dark);
light += (y1 << 2);
dark += (y1 << 2);

View File

@ -10,22 +10,12 @@ static void *bopti_asm[] = {
bopti_gasm_gray_alpha,
};
/* gimage(): Render a full image */
void gimage(int x, int y, bopti_image_t const *img)
{
uint32_t *light, *dark;
gvram(&light, &dark);
bopti_render_clip(x, y, img, 0, 0, img->width, img->height, light,
dark, bopti_asm[img->profile]);
}
/* gsubimage(): Render a section of an image */
void gsubimage(int x, int y, bopti_image_t const *img, int left, int top,
int width, int height, int flags)
{
uint32_t *light, *dark;
gvram(&light, &dark);
dgray_getvram(&light, &dark);
if(flags & DIMAGE_NOCLIP)
{

View File

@ -2,13 +2,23 @@
#include <display/common.h>
#include "../render-fx/topti-asm.h"
/* gtext(): Display a string of text */
void gtext(int x, int y, const char *str, int fg, int bg)
/* gtext_opt(): Display a string of text */
void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
char const *str)
{
if((uint)fg >= 8 || (uint)bg >= 8) return;
uint32_t *light, *dark;
gvram(&light, &dark);
dgray_getvram(&light, &dark);
if(halign != DTEXT_LEFT || valign != DTEXT_TOP)
{
int w, h;
dsize(str, topti_font, &w, &h);
if(halign == DTEXT_RIGHT) x -= w;
if(halign == DTEXT_CENTER) x -= ((w+1) >> 1);
if(valign == DTEXT_BOTTOM) y -= h;
if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1);
}
topti_render(x, y, str, topti_font, topti_asm_text[fg],
topti_asm_text[bg], light, dark);

View File

@ -1,7 +0,0 @@
#include <gint/gray.h>
/* gvline(): Full-height vertical line */
void gvline(int x, color_t color)
{
gline(x, 0, x, 63, color);
}

View File

@ -1,7 +0,0 @@
#include <gint/display.h>
/* dhline(): Full-width horizontal line */
void dhline(int y, color_t color)
{
dline(0, y, 395, y, color);
}

View File

@ -1,10 +1,10 @@
#include <gint/display.h>
/* dpixel() - change a pixel's color */
void dpixel(int x, int y, uint16_t color)
void dpixel(int x, int y, int color)
{
/* Coordinate checks */
if((uint)x >= 396 || (uint)y >= 224) return;
if((uint)x >= 396 || (uint)y >= 224 || color == C_NONE) return;
gint_vram[396 * y + x] = color;
}

View File

@ -2,8 +2,10 @@
#include <gint/display.h>
/* drect() - fill a rectangle of the screen */
void drect(int x1, int y1, int x2, int y2, uint16_t color)
void drect(int x1, int y1, int x2, int y2, int color)
{
if(color == C_NONE) return;
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);

View File

@ -1,13 +1,7 @@
#include <gint/display.h>
#include "bopti-asm.h"
/* dimage() - render a full image */
void dimage(int x, int y, bopti_image_t const *img)
{
bopti_render_clip(x, y, img, 0, 0, img->width, img->height);
}
/* dsubimage() - render a section of an image */
/* dsubimage(): Render a section of an image */
void dsubimage(int x, int y, bopti_image_t const *img, int left, int top,
int width, int height, int flags)
{

View File

@ -11,9 +11,3 @@ void dupdate(void)
overwriting the data which is about to be sent. */
dvram_switch();
}
/* dupdate_noint() - Push VRAM to the display without interrupts */
void dupdate_noint(void)
{
r61524_display(gint_vram, 0, 224, R61524_DMA_WAIT);
}

View File

@ -133,9 +133,3 @@ void dtext_opt(int x, int y, int fg, int bg, int halign, int valign,
topti_render(x, y, str, strlen(str), topti_font, fg, bg);
}
/* dtext(): Simple version of dtext_opt() with defaults */
void dtext(int x, int y, int fg, char const *str)
{
dtext_opt(x, y, fg, C_NONE, DTEXT_LEFT, DTEXT_TOP, str);
}

View File

@ -1,8 +1,11 @@
#include <gint/display.h>
#include <display/fx.h>
/* dclear() - fill the screen with a single color */
void dclear(color_t color)
{
DMODE_OVERRIDE(dclear, color);
/* SuperH only supports a single write-move addressing mode, which is
pre-decrement write; the other similar mode is post-increment
read. So we'll use pre-decrement writes to improve performance. */

View File

@ -1,12 +1,15 @@
#include <gint/display.h>
#include <gint/defs/types.h>
#include <display/fx.h>
/* dpixel() - change a pixel's color */
void dpixel(int x, int y, color_t color)
void dpixel(int x, int y, int color)
{
/* Sanity checks */
if((uint)x >= 128 || (uint)y >= 64) return;
DMODE_OVERRIDE(dpixel, x, y, color);
uint32_t *lword = gint_vram + (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31);

View File

@ -3,7 +3,7 @@
#include <display/fx.h>
/* drect() - fill a rectangle of the screen */
void drect(int x1, int y1, int x2, int y2, color_t color)
void drect(int x1, int y1, int x2, int y2, int color)
{
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
@ -15,6 +15,8 @@ void drect(int x1, int y1, int x2, int y2, color_t color)
if(y1 < 0) y1 = 0;
if(y2 >= 64) y2 = 63;
DMODE_OVERRIDE(drect, x1, y1, x2, y2, color);
/* Use masks to get the work done fast! */
uint32_t m[4];
masks(x1, x2, m);

View File

@ -8,18 +8,12 @@ static void *bopti_asm[] = {
bopti_asm_mono_alpha,
};
/* dimage() - render a full image */
void dimage(int x, int y, bopti_image_t const *img)
{
if(img->gray) return;
bopti_render_clip(x, y, img, 0, 0, img->width, img->height, gint_vram,
NULL, bopti_asm[img->profile]);
}
/* dsubimage() - render a section of an image */
/* dsubimage(): Render a section of an image */
void dsubimage(int x, int y, bopti_image_t const *img, int left, int top,
int width, int height, int flags)
{
DMODE_OVERRIDE(dsubimage, x, y, img, left, top, width, height, flags);
if(img->gray) return;
if(flags & DIMAGE_NOCLIP)

View File

@ -1,30 +0,0 @@
#include <gint/display.h>
#include <display/common.h>
#include "topti-asm.h"
/* dtext_opt(): Display a string of text */
void dtext_opt(int x, int y, int fg, int bg, int halign, int valign,
char const *str)
{
if((uint)fg >= 8 || (uint)bg >= 8) return;
if(halign != DTEXT_LEFT || valign != DTEXT_TOP)
{
int w, h;
dsize(str, topti_font, &w, &h);
if(halign == DTEXT_RIGHT) x -= w;
if(halign == DTEXT_CENTER) x -= ((w+1) >> 1);
if(valign == DTEXT_BOTTOM) y -= h;
if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1);
}
topti_render(x, y, str, topti_font, topti_asm_text[fg],
topti_asm_text[bg], gint_vram, gint_vram);
}
/* dtext(): Simple version of dtext_opt() with defaults */
void dtext(int x, int y, int fg, char const *str)
{
dtext_opt(x, y, fg, C_NONE, DTEXT_LEFT, DTEXT_TOP, str);
}

View File

@ -1,5 +1,6 @@
#include <gint/display.h>
#include <gint/drivers/t6k11.h>
#include <display/fx.h>
/* Standard video RAM for fx9860g is 1 bit per pixel */
GSECTION(".bss") static uint32_t fx_vram[256];
@ -7,14 +8,19 @@ GSECTION(".bss") static uint32_t fx_vram[256];
/* Here is the definition of the VRAM pointer, exposed in <gint/display.h> */
uint32_t *gint_vram = fx_vram;
/* The current rendering mode */
struct rendering_mode const *dmode = NULL;
/* dupdate() - push the video RAM to the display driver */
void dupdate(void)
{
if(dmode && dmode->dupdate)
{
/* Call the overridden dupdate(), but continue if itreturns
non-zero (this is used when stopping the gray engine) */
int rc = dmode->dupdate();
if(rc == 0) return;
}
t6k11_display(gint_vram, 0, 64, 16);
}
/* dupdate_noint() - Push VRAM to the display without interrupts */
void dupdate_noint(void)
{
dupdate();
}

View File

@ -1,7 +0,0 @@
#include <gint/display.h>
/* dvline(): Full-height vertical line */
void dvline(int x, color_t color)
{
dline(x, 0, x, 63, color);
}

View File

@ -3,7 +3,7 @@
#include <display/fx.h>
/* gint_dhline(): Optimized horizontal line using a rectangle mask */
void gint_dhline(int x1, int x2, int y, color_t color)
void gint_dhline(int x1, int x2, int y, int color)
{
if((uint)y >= 64) return;
if(x1 > x2) swap(x1, x2);
@ -39,7 +39,7 @@ void gint_dhline(int x1, int x2, int y, color_t color)
}
/* gint_dvline(): Optimized vertical line */
void gint_dvline(int y1, int y2, int x, color_t color)
void gint_dvline(int y1, int y2, int x, int color)
{
if((uint)x >= 128) return;
if(y1 > y2) swap(y1, y2);

View File

@ -2,6 +2,7 @@
#include <gint/defs/attributes.h>
#include <gint/display.h>
#include <display/common.h>
#include <display/fx.h>
#include "topti-asm.h"
/* Default font */
@ -173,3 +174,26 @@ void topti_render(int x, int y, char const *str, font_t const *f,
asm_fg(v1, v2, operators + vdisp, height - vdisp);
}
}
/* dtext_opt(): Display a string of text */
void dtext_opt(int x, int y, int fg, int bg, int halign, int valign,
char const *str)
{
if((uint)fg >= 8 || (uint)bg >= 8) return;
DMODE_OVERRIDE(dtext_opt, x, y, fg, bg, halign, valign, str);
if(halign != DTEXT_LEFT || valign != DTEXT_TOP)
{
int w, h;
dsize(str, topti_font, &w, &h);
if(halign == DTEXT_RIGHT) x -= w;
if(halign == DTEXT_CENTER) x -= ((w+1) >> 1);
if(valign == DTEXT_BOTTOM) y -= h;
if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1);
}
topti_render(x, y, str, topti_font, topti_asm_text[fg],
topti_asm_text[bg], gint_vram, gint_vram);
}

View File

@ -1,7 +1,7 @@
#include <gint/display.h>
/* dhline(): Full-width horizontal line */
void dhline(int y, color_t color)
void dhline(int y, int color)
{
dline(0, y, 127, y, color);
dline(0, y, DWIDTH - 1, y, color);
}

7
src/render/dimage.c Normal file
View File

@ -0,0 +1,7 @@
#include <gint/display.h>
/* dimage(): Render a full image */
void dimage(int x, int y, bopti_image_t const *img)
{
dsubimage(x, y, img, 0, 0, img->width, img->height, DIMAGE_NONE);
}

View File

@ -2,21 +2,35 @@
#include <gint/defs/util.h>
#include <display/common.h>
#ifdef FX9860G
#include <display/fx.h>
#endif
/* dline(): Bresenham line drawing algorithm
Remotely adapted from MonochromeLib code by Pierre "PerriotLL" Le Gall.
Relies on platform-dependent dhline() and dvline() for optimized situations.
@x1 @y1 @x2 @y2 Coordinates of endpoints of line (included)
@color Any color accepted by dpixel() on the platform */
void dline(int x1, int y1, int x2, int y2, color_t color)
void dline(int x1, int y1, int x2, int y2, int color)
{
if(color == C_NONE) return;
/* Possible optimizations */
if(y1 == y2)
{
#ifdef FX9860G
DMODE_OVERRIDE(gint_dhline, x1, x2, y1, color);
#endif
gint_dhline(x1, x2, y1, color);
return;
}
if(x1 == x2)
{
#ifdef FX9860G
DMODE_OVERRIDE(gint_dvline, y1, y2, x1, color);
#endif
gint_dvline(y1, y2, x1, color);
return;
}

7
src/render/dtext.c Normal file
View File

@ -0,0 +1,7 @@
#include <gint/display.h>
/* dtext(): Simple version of dtext_opt() with defaults */
void dtext(int x, int y, int fg, char const *str)
{
dtext_opt(x, y, fg, C_NONE, DTEXT_LEFT, DTEXT_TOP, str);
}

View File

@ -1,7 +1,7 @@
#include <gint/display.h>
/* dvline(): Full-height vertical line */
void dvline(int x, color_t color)
void dvline(int x, int color)
{
dline(x, 0, x, 223, color);
dline(x, 0, x, DHEIGHT - 1, color);
}